123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676 | // Copyright (C) 2021 The Qt Company Ltd.// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>// Copyright (C) 2022 Intel Corporation.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only#include"qlogging.h"#include"qlogging_p.h"#include"qbytearray.h"#include"qlist.h"#include"qcoreapplication.h"#include"private/qcoreapplication_p.h"#include"qdatetime.h"#include"qdebug.h"#include"qgettid_p.h"#include"private/qlocking_p.h"#include"qloggingcategory.h"#include"private/qloggingregistry_p.h"#include"qmutex.h"#include"qscopeguard.h"#include"qstring.h"#include"qtcore_tracepoints_p.h"#include"qthread.h"#include"qvarlengtharray.h"#ifdef Q_CC_MSVC#include <intrin.h>#endif#if QT_CONFIG(slog2)#include <sys/slog2.h>#endif#if __has_include(<paths.h>)#include <paths.h>#endif#ifdef Q_OS_ANDROID#include <android/log.h>#endif#ifdef Q_OS_DARWIN#include <QtCore/private/qcore_mac_p.h>#endif#if QT_CONFIG(journald)# define SD_JOURNAL_SUPPRESS_LOCATION# include <systemd/sd-journal.h># include <syslog.h>#endif#if QT_CONFIG(syslog)# include <syslog.h>#endif#ifdef Q_OS_UNIX# include <sys/types.h># include <sys/stat.h># include <unistd.h># include"private/qcore_unix_p.h"#endif#ifdef Q_OS_WASM#include <emscripten/emscripten.h>#endif#if QT_CONFIG(slog2)externchar*__progname;#endif#ifdef QLOGGING_HAVE_BACKTRACE# include <qregularexpression.h>#endif#ifdef QLOGGING_USE_EXECINFO_BACKTRACE# if QT_CONFIG(dladdr)# include <dlfcn.h># endif# include BACKTRACE_HEADER# include <cxxabi.h>#endif// QLOGGING_USE_EXECINFO_BACKTRACE#include <cstdlib>#include <algorithm>#include <chrono>#include <memory>#include <vector>#include <stdio.h>#ifdef Q_OS_WIN#include <qt_windows.h>#include <processthreadsapi.h>#include"qfunctionpointer.h"#endif QT_BEGIN_NAMESPACE using namespaceQt::StringLiterals;Q_TRACE_POINT(qtcore, qt_message_print,int type,const char*category,const char*function,const char*file,int line,const QString &message);/*! \headerfile <QtLogging> \inmodule QtCore \title Qt Logging Types \brief The <QtLogging> header file defines Qt logging types, functions and macros. The <QtLogging> header file contains several types, functions and macros for logging. The QtMsgType enum identifies the various messages that can be generated and sent to a Qt message handler; QtMessageHandler is a type definition for a pointer to a function with the signature \c {void myMessageHandler(QtMsgType, const QMessageLogContext &, const char *)}. qInstallMessageHandler() function can be used to install the given QtMessageHandler. QMessageLogContext class contains the line, file, and function the message was logged at. This information is created by the QMessageLogger class. <QtLogging> also contains functions that generate messages from the given string argument: qDebug(), qInfo(), qWarning(), qCritical(), and qFatal(). These functions call the message handler with the given message. Example: \snippet code/src_corelib_global_qglobal.cpp 4 \sa QLoggingCategory*/template<typename String>static voidqt_maybe_message_fatal(QtMsgType,const QMessageLogContext &context, String &&message);static voidqt_message_print(QtMsgType,const QMessageLogContext &context,const QString &message);static voidpreformattedMessageHandler(QtMsgType type,const QMessageLogContext &context,const QString &formattedMessage);static QString formatLogMessage(QtMsgType type,const QMessageLogContext &context,const QString &str);static intchecked_var_value(const char*varname){// qEnvironmentVariableIntValue returns 0 on both parsing failure and on// empty, but we need to distinguish between the two for backwards// compatibility reasons. QByteArray str =qgetenv(varname);if(str.isEmpty())return0;bool ok;int value = str.toInt(&ok,0);return(ok && value >=0) ? value :1;}static boolisFatalCountDown(const char*varname, QBasicAtomicInt &n){static const int Uninitialized =0;static const int NeverFatal =1;static const int ImmediatelyFatal =2;int v = n.loadRelaxed();if(v == Uninitialized) {// first, initialize from the environment// note that the atomic stores the env.var value plus 1, so adjustconst int env =checked_var_value(varname) +1;if(env == NeverFatal) {// not fatal, now or in the future, so use a fast path n.storeRelaxed(NeverFatal);return false;}else if(env == ImmediatelyFatal) {return true;}else if(n.testAndSetRelaxed(Uninitialized, env -1, v)) {return false;// not yet fatal, but decrement}else{// some other thread initialized before we did}}while(v > ImmediatelyFatal && !n.testAndSetRelaxed(v, v -1, v))qYieldCpu();// We exited the loop, so either v already was ImmediatelyFatal or we// succeeded to set n from v to v-1.return v == ImmediatelyFatal;} Q_CONSTINIT static QBasicAtomicInt fatalCriticalsCount = {}; Q_CONSTINIT static QBasicAtomicInt fatalWarningsCount = {};static boolisFatal(QtMsgType msgType){switch(msgType){case QtFatalMsg:return true;// always fatalcase QtCriticalMsg:returnisFatalCountDown("QT_FATAL_CRITICALS", fatalCriticalsCount);case QtWarningMsg:returnisFatalCountDown("QT_FATAL_WARNINGS", fatalWarningsCount);case QtDebugMsg:case QtInfoMsg:break;// never fatal}return false;}#if defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)static boolqt_append_thread_name_to(QString &message){std::array<char,16> name{};if(pthread_getname_np(pthread_self(), name.data(), name.size()) ==0) { QUtf8StringView threadName(name.data());if(!threadName.isEmpty()) { message.append(threadName);return true;}}return false;}#elif defined(Q_OS_WIN)typedefHRESULT(WINAPI *GetThreadDescriptionFunc)(HANDLE, PWSTR *);static boolqt_append_thread_name_to(QString &message){// Once MinGW 12.0 is required for Qt, we can call GetThreadDescription directly// instead of this runtime resolve:static GetThreadDescriptionFunc pGetThreadDescription = []() -> GetThreadDescriptionFunc { HMODULE hKernel =GetModuleHandleW(L"kernel32.dll");if(!hKernel)returnnullptr;auto funcPtr =reinterpret_cast<QFunctionPointer>(GetProcAddress(hKernel,"GetThreadDescription"));return reinterpret_cast<GetThreadDescriptionFunc>(funcPtr);} ();if(!pGetThreadDescription)return false;// Not available on this system PWSTR description =nullptr; HRESULT hr =pGetThreadDescription(GetCurrentThread(), &description);std::unique_ptr<WCHAR,decltype(&LocalFree)>descriptionOwner(description, &LocalFree);if(SUCCEEDED(hr)) { QStringView threadName(description);if(!threadName.isEmpty()) { message.append(threadName);return true;}}return false;}#elsestatic boolqt_append_thread_name_to(QString &message){Q_UNUSED(message)return false;}#endif#ifndef Q_OS_WASM/*! Returns true if writing to \c stderr is supported. \internal \sa stderrHasConsoleAttached()*/static boolsystemHasStderr(){return true;}/*! Returns true if writing to \c stderr will end up in a console/terminal visible to the user. This is typically the case if the application was started from the command line. If the application is started without a controlling console/terminal, but the parent process reads \c stderr and presents it to the user in some other way, the parent process may override the detection in this function by setting the QT_ASSUME_STDERR_HAS_CONSOLE environment variable to \c 1. \note Qt Creator does not implement a pseudo TTY, nor does it launch apps with the override environment variable set, but it will read stderr and print it to the user, so in effect this function cannot be used to conclude that stderr output will _not_ be visible to the user, as even if this function returns false, the output might still end up visible to the user. For this reason, we don't guard the stderr output in the default message handler with stderrHasConsoleAttached(). \internal \sa systemHasStderr()*/static boolstderrHasConsoleAttached(){static const bool stderrHasConsoleAttached = []() ->bool{if(!systemHasStderr())return false;if(qEnvironmentVariableIntValue("QT_LOGGING_TO_CONSOLE")) {fprintf(stderr,"warning: Environment variable QT_LOGGING_TO_CONSOLE is deprecated, use\n""QT_ASSUME_STDERR_HAS_CONSOLE and/or QT_FORCE_STDERR_LOGGING instead.\n");return true;}if(qEnvironmentVariableIntValue("QT_ASSUME_STDERR_HAS_CONSOLE"))return true;#if defined(Q_OS_WIN)returnGetConsoleWindow();#elif defined(Q_OS_UNIX)# ifndef _PATH_TTY# define _PATH_TTY"/dev/tty"# endif// If we can open /dev/tty, we have a controlling TTYint ttyDevice = -1;if((ttyDevice =qt_safe_open(_PATH_TTY, O_RDONLY)) >=0) {qt_safe_close(ttyDevice);return true;}else if(errno == ENOENT || errno == EPERM || errno == ENXIO) {// Fall back to isatty for some non-critical errorsreturnisatty(STDERR_FILENO);}else{return false;}#elsereturn false;// No way to detect if stderr has a console attached#endif}();return stderrHasConsoleAttached;}namespace QtPrivate {/*! Returns true if logging \c stderr should be ensured. This is normally the case if \c stderr has a console attached, but may be overridden by the user by setting the QT_FORCE_STDERR_LOGGING environment variable to \c 1. \internal \sa stderrHasConsoleAttached()*/boolshouldLogToStderr(){static bool forceStderrLogging =qEnvironmentVariableIntValue("QT_FORCE_STDERR_LOGGING");return forceStderrLogging ||stderrHasConsoleAttached();}}// QtPrivateusing namespace QtPrivate;#endif// ifndef Q_OS_WASM/*! \class QMessageLogContext \inmodule QtCore \brief The QMessageLogContext class provides additional information about a log message. \since 5.0 The class provides information about the source code location a qDebug(), qInfo(), qWarning(), qCritical() or qFatal() message was generated. \note By default, this information is recorded only in debug builds. You can overwrite this explicitly by defining \c QT_MESSAGELOGCONTEXT or \c{QT_NO_MESSAGELOGCONTEXT}. \sa QMessageLogger, QtMessageHandler, qInstallMessageHandler()*//*! \class QMessageLogger \inmodule QtCore \brief The QMessageLogger class generates log messages. \since 5.0 QMessageLogger is used to generate messages for the Qt logging framework. Usually one uses it through qDebug(), qInfo(), qWarning(), qCritical, or qFatal() functions, which are actually macros: For example qDebug() expands to QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).debug() for debug builds, and QMessageLogger(0, 0, 0).debug() for release builds. One example of direct use is to forward errors that stem from a scripting language, e.g. QML: \snippet code/qlogging/qlogging.cpp 1 \sa QMessageLogContext, qDebug(), qInfo(), qWarning(), qCritical(), qFatal()*/#if defined(Q_CC_MSVC_ONLY) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR)staticinlinevoidconvert_to_wchar_t_elided(wchar_t*d,size_t space,const char*s) noexcept {size_t len =qstrlen(s);if(len +1> space) {const size_t skip = len - space +4;// 4 for "..." + '\0' s += skip; len -= skip;for(int i =0; i <3; ++i)*d++ = L'.';}while(len--)*d++ = *s++;*d++ =0;}#endif/*! \internal*/ Q_NEVER_INLINE static voidqt_message(QtMsgType msgType,const QMessageLogContext &context,const char*msg,va_list ap){ QString buf =QString::vasprintf(msg, ap);qt_message_print(msgType, context, buf);qt_maybe_message_fatal(msgType, context, buf);}/*! Logs a debug message specified with format \a msg. Additional parameters, specified by \a msg, may be used. \sa qDebug()*/voidQMessageLogger::debug(const char*msg, ...)const{ QInternalMessageLogContext ctxt(context);va_list ap;va_start(ap, msg);// use variable arg listqt_message(QtDebugMsg, ctxt, msg, ap);va_end(ap);}/*! Logs an informational message specified with format \a msg. Additional parameters, specified by \a msg, may be used. \sa qInfo() \since 5.5*/voidQMessageLogger::info(const char*msg, ...)const{ QInternalMessageLogContext ctxt(context);va_list ap;va_start(ap, msg);// use variable arg listqt_message(QtInfoMsg, ctxt, msg, ap);va_end(ap);}/*! \typedef QMessageLogger::CategoryFunction This is a typedef for a pointer to a function with the following signature: \snippet code/qlogging/qlogging.cpp 2 The \c Q_DECLARE_LOGGING_CATEGORY macro generates a function declaration with this signature, and \c Q_LOGGING_CATEGORY generates its definition. \since 5.3 \sa QLoggingCategory*//*! Logs a debug message specified with format \a msg for the context \a cat. Additional parameters, specified by \a msg, may be used. \since 5.3 \sa qCDebug()*/voidQMessageLogger::debug(const QLoggingCategory &cat,const char*msg, ...)const{if(!cat.isDebugEnabled())return; QInternalMessageLogContext ctxt(context,cat());va_list ap;va_start(ap, msg);// use variable arg listqt_message(QtDebugMsg, ctxt, msg, ap);va_end(ap);}/*! Logs a debug message specified with format \a msg for the context returned by \a catFunc. Additional parameters, specified by \a msg, may be used. \since 5.3 \sa qCDebug()*/voidQMessageLogger::debug(QMessageLogger::CategoryFunction catFunc,const char*msg, ...)const{const QLoggingCategory &cat = (*catFunc)();if(!cat.isDebugEnabled())return; QInternalMessageLogContext ctxt(context,cat());va_list ap;va_start(ap, msg);// use variable arg listqt_message(QtDebugMsg, ctxt, msg, ap);va_end(ap);}#ifndef QT_NO_DEBUG_STREAM/*! Logs a debug message using a QDebug stream \sa qDebug(), QDebug*/ QDebug QMessageLogger::debug()const{ QDebug dbg =QDebug(QtDebugMsg); QMessageLogContext &ctxt = dbg.stream->context; ctxt.copyContextFrom(context);return dbg;}/*! Logs a debug message into category \a cat using a QDebug stream. \since 5.3 \sa qCDebug(), QDebug*/ QDebug QMessageLogger::debug(const QLoggingCategory &cat)const{ QDebug dbg =QDebug(QtDebugMsg);if(!cat.isDebugEnabled()) dbg.stream->message_output =false; QMessageLogContext &ctxt = dbg.stream->context; ctxt.copyContextFrom(context); ctxt.category = cat.categoryName();return dbg;}/*! Logs a debug message into category returned by \a catFunc using a QDebug stream. \since 5.3 \sa qCDebug(), QDebug*/ QDebug QMessageLogger::debug(QMessageLogger::CategoryFunction catFunc)const{returndebug((*catFunc)());}#endif/*! Logs an informational message specified with format \a msg for the context \a cat. Additional parameters, specified by \a msg, may be used. \since 5.5 \sa qCInfo()*/voidQMessageLogger::info(const QLoggingCategory &cat,const char*msg, ...)const{if(!cat.isInfoEnabled())return; QInternalMessageLogContext ctxt(context,cat());va_list ap;va_start(ap, msg);// use variable arg listqt_message(QtInfoMsg, ctxt, msg, ap);va_end(ap);}/*! Logs an informational message specified with format \a msg for the context returned by \a catFunc. Additional parameters, specified by \a msg, may be used. \since 5.5 \sa qCInfo()*/voidQMessageLogger::info(QMessageLogger::CategoryFunction catFunc,const char*msg, ...)const{const QLoggingCategory &cat = (*catFunc)();if(!cat.isInfoEnabled())return; QInternalMessageLogContext ctxt(context,cat());va_list ap;va_start(ap, msg);// use variable arg listqt_message(QtInfoMsg, ctxt, msg, ap);va_end(ap);}#ifndef QT_NO_DEBUG_STREAM/*! Logs an informational message using a QDebug stream. \since 5.5 \sa qInfo(), QDebug*/ QDebug QMessageLogger::info()const{ QDebug dbg =QDebug(QtInfoMsg); QMessageLogContext &ctxt = dbg.stream->context; ctxt.copyContextFrom(context);return dbg;}/*! Logs an informational message into the category \a cat using a QDebug stream. \since 5.5 \sa qCInfo(), QDebug*/ QDebug QMessageLogger::info(const QLoggingCategory &cat)const{ QDebug dbg =QDebug(QtInfoMsg);if(!cat.isInfoEnabled()) dbg.stream->message_output =false; QMessageLogContext &ctxt = dbg.stream->context; ctxt.copyContextFrom(context); ctxt.category = cat.categoryName();return dbg;}/*! Logs an informational message into category returned by \a catFunc using a QDebug stream. \since 5.5 \sa qCInfo(), QDebug*/ QDebug QMessageLogger::info(QMessageLogger::CategoryFunction catFunc)const{returninfo((*catFunc)());}#endif/*! Logs a warning message specified with format \a msg. Additional parameters, specified by \a msg, may be used. \sa qWarning()*/voidQMessageLogger::warning(const char*msg, ...)const{ QInternalMessageLogContext ctxt(context);va_list ap;va_start(ap, msg);// use variable arg listqt_message(QtWarningMsg, ctxt, msg, ap);va_end(ap);}/*! Logs a warning message specified with format \a msg for the context \a cat. Additional parameters, specified by \a msg, may be used. \since 5.3 \sa qCWarning()*/voidQMessageLogger::warning(const QLoggingCategory &cat,const char*msg, ...)const{if(!cat.isWarningEnabled())return; QInternalMessageLogContext ctxt(context,cat());va_list ap;va_start(ap, msg);// use variable arg listqt_message(QtWarningMsg, ctxt, msg, ap);va_end(ap);}/*! Logs a warning message specified with format \a msg for the context returned by \a catFunc. Additional parameters, specified by \a msg, may be used. \since 5.3 \sa qCWarning()*/voidQMessageLogger::warning(QMessageLogger::CategoryFunction catFunc,const char*msg, ...)const{const QLoggingCategory &cat = (*catFunc)();if(!cat.isWarningEnabled())return; QInternalMessageLogContext ctxt(context,cat());va_list ap;va_start(ap, msg);// use variable arg listqt_message(QtWarningMsg, ctxt, msg, ap);va_end(ap);}#ifndef QT_NO_DEBUG_STREAM/*! Logs a warning message using a QDebug stream \sa qWarning(), QDebug*/ QDebug QMessageLogger::warning()const{ QDebug dbg =QDebug(QtWarningMsg); QMessageLogContext &ctxt = dbg.stream->context; ctxt.copyContextFrom(context);return dbg;}/*! Logs a warning message into category \a cat using a QDebug stream. \sa qCWarning(), QDebug*/ QDebug QMessageLogger::warning(const QLoggingCategory &cat)const{ QDebug dbg =QDebug(QtWarningMsg);if(!cat.isWarningEnabled()) dbg.stream->message_output =false; QMessageLogContext &ctxt = dbg.stream->context; ctxt.copyContextFrom(context); ctxt.category = cat.categoryName();return dbg;}/*! Logs a warning message into category returned by \a catFunc using a QDebug stream. \since 5.3 \sa qCWarning(), QDebug*/ QDebug QMessageLogger::warning(QMessageLogger::CategoryFunction catFunc)const{returnwarning((*catFunc)());}#endif/*! Logs a critical message specified with format \a msg. Additional parameters, specified by \a msg, may be used. \sa qCritical()*/voidQMessageLogger::critical(const char*msg, ...)const{ QInternalMessageLogContext ctxt(context);va_list ap;va_start(ap, msg);// use variable arg listqt_message(QtCriticalMsg, ctxt, msg, ap);va_end(ap);}/*! Logs a critical message specified with format \a msg for the context \a cat. Additional parameters, specified by \a msg, may be used. \since 5.3 \sa qCCritical()*/voidQMessageLogger::critical(const QLoggingCategory &cat,const char*msg, ...)const{if(!cat.isCriticalEnabled())return; QInternalMessageLogContext ctxt(context,cat());va_list ap;va_start(ap, msg);// use variable arg listqt_message(QtCriticalMsg, ctxt, msg, ap);va_end(ap);}/*! Logs a critical message specified with format \a msg for the context returned by \a catFunc. Additional parameters, specified by \a msg, may be used. \since 5.3 \sa qCCritical()*/voidQMessageLogger::critical(QMessageLogger::CategoryFunction catFunc,const char*msg, ...)const{const QLoggingCategory &cat = (*catFunc)();if(!cat.isCriticalEnabled())return; QInternalMessageLogContext ctxt(context,cat());va_list ap;va_start(ap, msg);// use variable arg listqt_message(QtCriticalMsg, ctxt, msg, ap);va_end(ap);}#ifndef QT_NO_DEBUG_STREAM/*! Logs a critical message using a QDebug stream \sa qCritical(), QDebug*/ QDebug QMessageLogger::critical()const{ QDebug dbg =QDebug(QtCriticalMsg); QMessageLogContext &ctxt = dbg.stream->context; ctxt.copyContextFrom(context);return dbg;}/*! Logs a critical message into category \a cat using a QDebug stream. \since 5.3 \sa qCCritical(), QDebug*/ QDebug QMessageLogger::critical(const QLoggingCategory &cat)const{ QDebug dbg =QDebug(QtCriticalMsg);if(!cat.isCriticalEnabled()) dbg.stream->message_output =false; QMessageLogContext &ctxt = dbg.stream->context; ctxt.copyContextFrom(context); ctxt.category = cat.categoryName();return dbg;}/*! Logs a critical message into category returned by \a catFunc using a QDebug stream. \since 5.3 \sa qCCritical(), QDebug*/ QDebug QMessageLogger::critical(QMessageLogger::CategoryFunction catFunc)const{returncritical((*catFunc)());}#endif/*! Logs a fatal message specified with format \a msg for the context \a cat. Additional parameters, specified by \a msg, may be used. \since 6.5 \sa qCFatal()*/voidQMessageLogger::fatal(const QLoggingCategory &cat,const char*msg, ...)const noexcept { QInternalMessageLogContext ctxt(context,cat());va_list ap;va_start(ap, msg);// use variable arg listqt_message(QtFatalMsg, ctxt, msg, ap);va_end(ap);#ifndef Q_CC_MSVC_ONLYQ_UNREACHABLE();#endif}/*! Logs a fatal message specified with format \a msg for the context returned by \a catFunc. Additional parameters, specified by \a msg, may be used. \since 6.5 \sa qCFatal()*/voidQMessageLogger::fatal(QMessageLogger::CategoryFunction catFunc,const char*msg, ...)const noexcept {const QLoggingCategory &cat = (*catFunc)(); QInternalMessageLogContext ctxt(context,cat());va_list ap;va_start(ap, msg);// use variable arg listqt_message(QtFatalMsg, ctxt, msg, ap);va_end(ap);#ifndef Q_CC_MSVC_ONLYQ_UNREACHABLE();#endif}/*! Logs a fatal message specified with format \a msg. Additional parameters, specified by \a msg, may be used. \sa qFatal()*/voidQMessageLogger::fatal(const char*msg, ...)const noexcept { QInternalMessageLogContext ctxt(context);va_list ap;va_start(ap, msg);// use variable arg listqt_message(QtFatalMsg, ctxt, msg, ap);va_end(ap);#ifndef Q_CC_MSVC_ONLYQ_UNREACHABLE();#endif}#ifndef QT_NO_DEBUG_STREAM/*! Logs a fatal message using a QDebug stream. \since 6.5 \sa qFatal(), QDebug*/ QDebug QMessageLogger::fatal()const{ QDebug dbg =QDebug(QtFatalMsg); QMessageLogContext &ctxt = dbg.stream->context; ctxt.copyContextFrom(context);return dbg;}/*! Logs a fatal message into category \a cat using a QDebug stream. \since 6.5 \sa qCFatal(), QDebug*/ QDebug QMessageLogger::fatal(const QLoggingCategory &cat)const{ QDebug dbg =QDebug(QtFatalMsg); QMessageLogContext &ctxt = dbg.stream->context; ctxt.copyContextFrom(context); ctxt.category = cat.categoryName();return dbg;}/*! Logs a fatal message into category returned by \a catFunc using a QDebug stream. \since 6.5 \sa qCFatal(), QDebug*/ QDebug QMessageLogger::fatal(QMessageLogger::CategoryFunction catFunc)const{returnfatal((*catFunc)());}#endif// QT_NO_DEBUG_STREAMstatic boolisDefaultCategory(const char*category){return!category ||strcmp(category,QLoggingRegistry::defaultCategoryName) ==0;}/*! \internal*/ Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info){// Strip the function info down to the base function name// note that this throws away the template definitions,// the parameter types (overloads) and any const/volatile qualifiers.if(info.isEmpty())return info; qsizetype pos;// Skip trailing [with XXX] for templates (gcc), but make// sure to not affect Objective-C message names. pos = info.size() -1;if(info.endsWith(']') && !(info.startsWith('+') || info.startsWith('-'))) {while(--pos) {if(info.at(pos) =='[') { info.truncate(pos);break;}}if(info.endsWith(' ')) { info.chop(1);}}// operator names with '(', ')', '<', '>' in itstatic const char operator_call[] ="operator()";static const char operator_lessThan[] ="operator<";static const char operator_greaterThan[] ="operator>";static const char operator_lessThanEqual[] ="operator<=";static const char operator_greaterThanEqual[] ="operator>=";// canonize operator names info.replace("operator ","operator"); pos = -1;// remove argument list forever {int parencount =0; pos = info.lastIndexOf(')', pos);if(pos == -1) {// Don't know how to parse this function namereturn info;}if(info.indexOf('>', pos) != -1|| info.indexOf(':', pos) != -1) {// that wasn't the function argument list.--pos;continue;}// find the beginning of the argument list--pos;++parencount;while(pos && parencount) {if(info.at(pos) ==')')++parencount;else if(info.at(pos) =='(')--parencount;--pos;}if(parencount !=0)return info; info.truncate(++pos);if(info.at(pos -1) ==')') {if(info.indexOf(operator_call) == pos -qsizetype(strlen(operator_call)))break;// this function returns a pointer to a function// and we matched the arguments of the return type's parameter list// try again info.remove(0, info.indexOf('(')); info.chop(1);continue;}else{break;}}// find the beginning of the function nameint parencount =0;int templatecount =0;--pos;// make sure special characters in operator names are keptif(pos > -1) {switch(info.at(pos)) {case')':if(info.indexOf(operator_call) == pos -qsizetype(strlen(operator_call)) +1) pos -=2;break;case'<':if(info.indexOf(operator_lessThan) == pos -qsizetype(strlen(operator_lessThan)) +1)--pos;break;case'>':if(info.indexOf(operator_greaterThan) == pos -qsizetype(strlen(operator_greaterThan)) +1)--pos;break;case'=': {auto operatorLength =qsizetype(strlen(operator_lessThanEqual));if(info.indexOf(operator_lessThanEqual) == pos - operatorLength +1) pos -=2;else if(info.indexOf(operator_greaterThanEqual) == pos - operatorLength +1) pos -=2;break;}default:break;}}while(pos > -1) {if(parencount <0|| templatecount <0)return info;char c = info.at(pos);if(c ==')')++parencount;else if(c =='(')--parencount;else if(c =='>')++templatecount;else if(c =='<')--templatecount;else if(c ==' '&& templatecount ==0&& parencount ==0)break;--pos;} info = info.mid(pos +1);// remove trailing '*', '&' that are part of the return argumentwhile((info.at(0) =='*')|| (info.at(0) =='&')) info = info.mid(1);// we have the full function name now.// clean up the templateswhile((pos = info.lastIndexOf('>')) != -1) {if(!info.contains('<'))break;// find the matching close qsizetype end = pos; templatecount =1;--pos;while(pos && templatecount) {char c = info.at(pos);if(c =='>')++templatecount;else if(c =='<')--templatecount;--pos;}++pos; info.remove(pos, end - pos +1);}return info;}// tokens as recognized in QT_MESSAGE_PATTERNstatic const char categoryTokenC[] ="%{category}";static const char typeTokenC[] ="%{type}";static const char messageTokenC[] ="%{message}";static const char fileTokenC[] ="%{file}";static const char lineTokenC[] ="%{line}";static const char functionTokenC[] ="%{function}";static const char pidTokenC[] ="%{pid}";static const char appnameTokenC[] ="%{appname}";static const char threadidTokenC[] ="%{threadid}";static const char threadnameTokenC[] ="%{threadname}";static const char qthreadptrTokenC[] ="%{qthreadptr}";static const char timeTokenC[] ="%{time";//not a typo: this command has argumentsstatic const char backtraceTokenC[] ="%{backtrace";//dittostatic const char ifCategoryTokenC[] ="%{if-category}";static const char ifDebugTokenC[] ="%{if-debug}";static const char ifInfoTokenC[] ="%{if-info}";static const char ifWarningTokenC[] ="%{if-warning}";static const char ifCriticalTokenC[] ="%{if-critical}";static const char ifFatalTokenC[] ="%{if-fatal}";static const char endifTokenC[] ="%{endif}";static const char emptyTokenC[] ="";struct QMessagePattern {QMessagePattern();~QMessagePattern();voidsetPattern(const QString &pattern);voidsetDefaultPattern(){const char*const defaultTokens[] = {#ifndef Q_OS_ANDROID// "%{if-category}%{category}: %{endif}%{message}" ifCategoryTokenC, categoryTokenC,": ",// won't point to literals[] but that's ok endifTokenC,#endif messageTokenC,};// we don't attempt to free the pointers, so only call from the ctorQ_ASSERT(!tokens);Q_ASSERT(!literals);auto ptr =newconst char*[std::size(defaultTokens) +1];auto end =std::copy(std::begin(defaultTokens),std::end(defaultTokens), ptr);*end =nullptr; tokens.release(); tokens.reset(ptr);}// 0 terminated arrays of literal tokens / literal or placeholder tokensstd::unique_ptr<std::unique_ptr<const char[]>[]> literals;std::unique_ptr<const char*[]> tokens; QList<QString> timeArgs;// timeFormats in sequence of %{timestd::chrono::steady_clock::time_point appStartTime =std::chrono::steady_clock::now();struct BacktraceParams { QString backtraceSeparator;int backtraceDepth;};#ifdef QLOGGING_HAVE_BACKTRACE QList<BacktraceParams> backtraceArgs;// backtrace arguments in sequence of %{backtraceint maxBacktraceDepth =0;#endifbool fromEnvironment;static QBasicMutex mutex;#ifdef Q_OS_ANDROIDboolcontainsToken(const char*token)const{for(int i =0; tokens[i]; ++i) {if(tokens[i] == token)return true;}return false;}#endif};Q_DECLARE_TYPEINFO(QMessagePattern::BacktraceParams, Q_RELOCATABLE_TYPE); Q_CONSTINIT QBasicMutex QMessagePattern::mutex;QMessagePattern::QMessagePattern(){const QString envPattern =qEnvironmentVariable("QT_MESSAGE_PATTERN");if(envPattern.isEmpty()) {setDefaultPattern(); fromEnvironment =false;}else{setPattern(envPattern); fromEnvironment =true;}}QMessagePattern::~QMessagePattern() =default;voidQMessagePattern::setPattern(const QString &pattern){ timeArgs.clear();#ifdef QLOGGING_HAVE_BACKTRACE backtraceArgs.clear(); maxBacktraceDepth =0;#endif// scanner QList<QString> lexemes; QString lexeme;bool inPlaceholder =false;for(int i =0; i < pattern.size(); ++i) {const QChar c = pattern.at(i);if(c == u'%'&& !inPlaceholder) {if((i +1< pattern.size())&& pattern.at(i +1) == u'{') {// beginning of placeholderif(!lexeme.isEmpty()) { lexemes.append(lexeme); lexeme.clear();} inPlaceholder =true;}} lexeme.append(c);if(c == u'}'&& inPlaceholder) {// end of placeholder lexemes.append(lexeme); lexeme.clear(); inPlaceholder =false;}}if(!lexeme.isEmpty()) lexemes.append(lexeme);// tokenizerstd::vector<std::unique_ptr<const char[]>> literalsVar; tokens.reset(newconst char*[lexemes.size() +1]); tokens[lexemes.size()] =nullptr;bool nestedIfError =false;bool inIf =false; QString error;for(int i =0; i < lexemes.size(); ++i) {const QString lexeme = lexemes.at(i);if(lexeme.startsWith("%{"_L1) && lexeme.endsWith(u'}')) {// placeholderif(lexeme ==QLatin1StringView(typeTokenC)) { tokens[i] = typeTokenC;}else if(lexeme ==QLatin1StringView(categoryTokenC)) tokens[i] = categoryTokenC;else if(lexeme ==QLatin1StringView(messageTokenC)) tokens[i] = messageTokenC;else if(lexeme ==QLatin1StringView(fileTokenC)) tokens[i] = fileTokenC;else if(lexeme ==QLatin1StringView(lineTokenC)) tokens[i] = lineTokenC;else if(lexeme ==QLatin1StringView(functionTokenC)) tokens[i] = functionTokenC;else if(lexeme ==QLatin1StringView(pidTokenC)) tokens[i] = pidTokenC;else if(lexeme ==QLatin1StringView(appnameTokenC)) tokens[i] = appnameTokenC;else if(lexeme ==QLatin1StringView(threadidTokenC)) tokens[i] = threadidTokenC;else if(lexeme ==QLatin1StringView(threadnameTokenC)) tokens[i] = threadnameTokenC;else if(lexeme ==QLatin1StringView(qthreadptrTokenC)) tokens[i] = qthreadptrTokenC;else if(lexeme.startsWith(QLatin1StringView(timeTokenC))) { tokens[i] = timeTokenC; qsizetype spaceIdx = lexeme.indexOf(QChar::fromLatin1(' '));if(spaceIdx >0) timeArgs.append(lexeme.mid(spaceIdx +1, lexeme.size() - spaceIdx -2));else timeArgs.append(QString());}else if(lexeme.startsWith(QLatin1StringView(backtraceTokenC))) {#ifdef QLOGGING_HAVE_BACKTRACE tokens[i] = backtraceTokenC; QString backtraceSeparator =QStringLiteral("|");int backtraceDepth =5;static const QRegularExpression depthRx(QStringLiteral(" depth=(?|\"([^\"]*)\"|([^ }]*))"));static const QRegularExpression separatorRx(QStringLiteral(" separator=(?|\"([^\"]*)\"|([^ }]*))")); QRegularExpressionMatch m = depthRx.match(lexeme);if(m.hasMatch()) {int depth = m.capturedView(1).toInt();if(depth <=0) error +="QT_MESSAGE_PATTERN: %{backtrace} depth must be a number greater than 0\n"_L1;else backtraceDepth = depth;} m = separatorRx.match(lexeme);if(m.hasMatch()) backtraceSeparator = m.captured(1); BacktraceParams backtraceParams; backtraceParams.backtraceDepth = backtraceDepth; backtraceParams.backtraceSeparator = backtraceSeparator; backtraceArgs.append(backtraceParams); maxBacktraceDepth =qMax(maxBacktraceDepth, backtraceDepth);#else error +="QT_MESSAGE_PATTERN: %{backtrace} is not supported by this Qt build\n"_L1; tokens[i] ="";#endif}#define IF_TOKEN(LEVEL) \ else if (lexeme == QLatin1StringView(LEVEL)) { \ if (inIf) \ nestedIfError = true; \ tokens[i] = LEVEL; \ inIf = true; \ }IF_TOKEN(ifCategoryTokenC)IF_TOKEN(ifDebugTokenC)IF_TOKEN(ifInfoTokenC)IF_TOKEN(ifWarningTokenC)IF_TOKEN(ifCriticalTokenC)IF_TOKEN(ifFatalTokenC)#undef IF_TOKENelse if(lexeme ==QLatin1StringView(endifTokenC)) { tokens[i] = endifTokenC;if(!inIf && !nestedIfError) error +="QT_MESSAGE_PATTERN: %{endif} without an %{if-*}\n"_L1; inIf =false;}else{ tokens[i] = emptyTokenC; error +="QT_MESSAGE_PATTERN: Unknown placeholder "_L1 + lexeme +'\n'_L1;}}else{using UP =std::unique_ptr<char[]>; tokens[i] = literalsVar.emplace_back(UP(qstrdup(lexeme.toLatin1().constData()))).get();}}if(nestedIfError) error +="QT_MESSAGE_PATTERN: %{if-*} cannot be nested\n"_L1;else if(inIf) error +="QT_MESSAGE_PATTERN: missing %{endif}\n"_L1;if(!error.isEmpty()) {// remove the last '\n' because the sinks deal with that on their own error.chop(1); QMessageLogContext ctx(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE,"QMessagePattern::setPattern",nullptr);preformattedMessageHandler(QtWarningMsg, ctx, error);} literals.reset(newstd::unique_ptr<const char[]>[literalsVar.size() +1]);std::move(literalsVar.begin(), literalsVar.end(), &literals[0]);}#if defined(QLOGGING_HAVE_BACKTRACE)// make sure the function has "Message" in the name so the function is removed/* A typical backtrace in debug mode looks like: #0 QInternalMessageLogContext::populateBacktrace (this=0x7fffffffd660, frameCount=5) at qlogging.cpp:1342 #1 QInternalMessageLogContext::QInternalMessageLogContext (logContext=..., this=<optimized out>) at qlogging_p.h:42 #2 QDebug::~QDebug (this=0x7fffffffdac8, __in_chrg=<optimized out>) at qdebug.cpp:160 In release mode, the QInternalMessageLogContext constructor will be usually inlined. Empirical testing with GCC 13 and Clang 17 suggest they do obey the Q_ALWAYS_INLINE in that constructor even in debug mode and do inline it. Unfortunately, we can't know for sure if it has been.*/staticconstexprint TypicalBacktraceFrameCount =3;staticconstexprconst char*QtCoreLibraryName ="Qt"QT_STRINGIFY(QT_VERSION_MAJOR)"Core";#if defined(QLOGGING_USE_STD_BACKTRACE) Q_NEVER_INLINE voidQInternalMessageLogContext::populateBacktrace(int frameCount){assert(frameCount >=0); backtrace =std::stacktrace::current(0, TypicalBacktraceFrameCount + frameCount);}static QStringList backtraceFramesForLogMessage(int frameCount,constQInternalMessageLogContext::BacktraceStorage &buffer){ QStringList result; result.reserve(buffer.size());constauto shouldSkipFrame = [](QByteArrayView description){#if defined(_MSVC_STL_VERSION)constauto libraryNameEnd = description.indexOf('!');if(libraryNameEnd != -1) {constauto libraryName = description.first(libraryNameEnd);if(!libraryName.contains(QtCoreLibraryName))return false;}#endifif(description.contains("populateBacktrace"))return true;if(description.contains("QInternalMessageLogContext"))return true;if(description.contains("~QDebug"))return true;return false;};for(constauto&entry : buffer) {conststd::string description = entry.description();if(result.isEmpty() &&shouldSkipFrame(description))continue; result.append(QString::fromStdString(description));}return result;}#elif defined(QLOGGING_USE_EXECINFO_BACKTRACE) Q_NEVER_INLINE voidQInternalMessageLogContext::populateBacktrace(int frameCount){assert(frameCount >=0); BacktraceStorage &result = backtrace.emplace(TypicalBacktraceFrameCount + frameCount);int n = ::backtrace(result.data(), result.size());if(n <=0) result.clear();else result.resize(n);}static QStringList backtraceFramesForLogMessage(int frameCount,constQInternalMessageLogContext::BacktraceStorage &buffer){struct DecodedFrame { QString library; QString function;}; QStringList result;if(frameCount ==0)return result;auto shouldSkipFrame = [&result](constauto&library,constauto&function) {if(!result.isEmpty() || !library.contains(QLatin1StringView(QtCoreLibraryName)))return false;if(function.isEmpty())return true;if(function.contains("6QDebug"_L1))return true;if(function.contains("14QMessageLogger"_L1))return true;if(function.contains("17qt_message_output"_L1))return true;if(function.contains("26QInternalMessageLogContext"_L1))return true;return false;};auto demangled = [](auto&function) -> QString {if(!function.startsWith("_Z"_L1))return function;// we optimize for the case where __cxa_demangle succeedsauto fn = [&]() {ifconstexpr(sizeof(function.at(0)) ==1)return function.data();// -> const char *elsereturnstd::move(function).toUtf8();// -> QByteArray}(); QScopedPointer<char, QScopedPointerPodDeleter> demangled; demangled.reset(abi::__cxa_demangle(fn,nullptr,nullptr,nullptr));if(demangled)returnQString::fromUtf8(qCleanupFuncinfo(demangled.data()));elsereturnQString::fromUtf8(fn);// restore};# if QT_CONFIG(dladdr)// use dladdr() instead of backtrace_symbols() QString cachedLibrary;const char*cachedFname =nullptr;auto decodeFrame = [&](void*addr) -> DecodedFrame { Dl_info info;if(!dladdr(addr, &info))return{};// These are actually UTF-8, so we'll correct below QLatin1StringView fn(info.dli_sname); QLatin1StringView lib;if(const char*lastSlash =strrchr(info.dli_fname,'/')) lib =QLatin1StringView(lastSlash +1);else lib =QLatin1StringView(info.dli_fname);if(shouldSkipFrame(lib, fn))return{}; QString function =demangled(fn);if(lib.data() != cachedFname) { cachedFname = lib.data(); cachedLibrary =QString::fromUtf8(cachedFname, lib.size());}return{ cachedLibrary, function };};# else// The results of backtrace_symbols looks like this:// /lib/libc.so.6(__libc_start_main+0xf3) [0x4a937413]// The offset and function name are optional.// This regexp tries to extract the library name (without the path) and the function name.// This code is protected by QMessagePattern::mutex so it is thread safe on all compilersstatic const QRegularExpression rx(QStringLiteral("^(?:[^(]*/)?([^(/]+)\\(([^+]*)(?:[\\+[a-f0-9x]*)?\\)\\[[a-f0-9x]*\\]$"));auto decodeFrame = [&](void*&addr) -> DecodedFrame { QScopedPointer<char*, QScopedPointerPodDeleter>strings(backtrace_symbols(&addr,1)); QString trace =QString::fromUtf8(strings.data()[0]); QRegularExpressionMatch m = rx.match(trace);if(!m.hasMatch())return{}; QString library = m.captured(1); QString function = m.captured(2);// skip the trace from QtCore that are because of the qDebug itselfif(shouldSkipFrame(library, function))return{}; function =demangled(function);return{ library, function };};# endiffor(void*const&addr : buffer) { DecodedFrame frame =decodeFrame(addr);if(!frame.library.isEmpty()) {if(frame.function.isEmpty()) result.append(u'?'+ frame.library + u'?');else result.append(frame.function);}else{// innermost, unknown frames are usually the logging framework itselfif(!result.isEmpty()) result.append(QStringLiteral("???"));}if(result.size() == frameCount)break;}return result;}#else#error"Internal error: backtrace enabled, but no way to gather backtraces available"#endif// QLOGGING_USE_..._BACKTRACEstatic QString formatBacktraceForLogMessage(constQMessagePattern::BacktraceParams backtraceParams,const QMessageLogContext &ctx){// do we have a backtrace stored?if(ctx.version <=QMessageLogContext::CurrentVersion)returnQString();auto&fullctx =static_cast<const QInternalMessageLogContext &>(ctx);if(!fullctx.backtrace.has_value())returnQString(); QString backtraceSeparator = backtraceParams.backtraceSeparator;int backtraceDepth = backtraceParams.backtraceDepth; QStringList frames =backtraceFramesForLogMessage(backtraceDepth, *fullctx.backtrace);if(frames.isEmpty())returnQString();// if the first frame is unknown, replace it with the context functionif(ctx.function && frames.at(0).startsWith(u'?')) frames[0] =QString::fromUtf8(qCleanupFuncinfo(ctx.function));return frames.join(backtraceSeparator);}#elsevoidQInternalMessageLogContext::populateBacktrace(int){// initFrom() returns 0 to our caller, so we should never get hereQ_UNREACHABLE();}#endif// !QLOGGING_HAVE_BACKTRACEQ_GLOBAL_STATIC(QMessagePattern, qMessagePattern)/*! \relates <QtLogging> \since 5.4 Generates a formatted string out of the \a type, \a context, \a str arguments. qFormatLogMessage returns a QString that is formatted according to the current message pattern. It can be used by custom message handlers to format output similar to Qt's default message handler. The function is thread-safe. \sa qInstallMessageHandler(), qSetMessagePattern() */ QString qFormatLogMessage(QtMsgType type,const QMessageLogContext &context,const QString &str){returnformatLogMessage(type, context, str);}// Separate function so the default message handler can bypass the public,// exported function above. Static functions can't get added to the dynamic// symbol tables, so they never show up in backtrace_symbols() or equivalent.static QString formatLogMessage(QtMsgType type,const QMessageLogContext &context,const QString &str){ QString message;constauto locker =qt_scoped_lock(QMessagePattern::mutex); QMessagePattern *pattern =qMessagePattern();if(!pattern) {// after destruction of static QMessagePattern instance message.append(str);return message;}bool skip =false;int timeArgsIdx =0;#ifdef QLOGGING_HAVE_BACKTRACEint backtraceArgsIdx =0;#endif// we do not convert file, function, line literals to local encoding due to overheadfor(int i =0; pattern->tokens[i]; ++i) {const char*token = pattern->tokens[i];if(token == endifTokenC) { skip =false;}else if(skip) {// we skip adding messages, but we have to iterate over// timeArgsIdx and backtraceArgsIdx anywayif(token == timeTokenC) timeArgsIdx++;#ifdef QLOGGING_HAVE_BACKTRACEelse if(token == backtraceTokenC) backtraceArgsIdx++;#endif}else if(token == messageTokenC) { message.append(str);}else if(token == categoryTokenC) { message.append(QLatin1StringView(context.category));}else if(token == typeTokenC) {switch(type) {case QtDebugMsg: message.append("debug"_L1);break;case QtInfoMsg: message.append("info"_L1);break;case QtWarningMsg: message.append("warning"_L1);break;case QtCriticalMsg:message.append("critical"_L1);break;case QtFatalMsg: message.append("fatal"_L1);break;}}else if(token == fileTokenC) {if(context.file) message.append(QLatin1StringView(context.file));else message.append("unknown"_L1);}else if(token == lineTokenC) { message.append(QString::number(context.line));}else if(token == functionTokenC) {if(context.function) message.append(QString::fromLatin1(qCleanupFuncinfo(context.function)));else message.append("unknown"_L1);}else if(token == pidTokenC) { message.append(QString::number(QCoreApplication::applicationPid()));}else if(token == appnameTokenC) { message.append(QCoreApplication::applicationName());}else if(token == threadidTokenC) {// print the TID as decimal message.append(QString::number(qt_gettid()));}else if(token == threadnameTokenC) {if(!qt_append_thread_name_to(message)) message.append(QString::number(qt_gettid()));// fallback to the TID}else if(token == qthreadptrTokenC) { message.append("0x"_L1); message.append(QString::number(qlonglong(QThread::currentThread()->currentThread()),16));#ifdef QLOGGING_HAVE_BACKTRACE}else if(token == backtraceTokenC) {QMessagePattern::BacktraceParams backtraceParams = pattern->backtraceArgs.at(backtraceArgsIdx); backtraceArgsIdx++; message.append(formatBacktraceForLogMessage(backtraceParams, context));#endif}else if(token == timeTokenC) {using namespacestd::chrono;auto formatElapsedTime = [](steady_clock::duration time) {// we assume time > 0auto ms = duration_cast<milliseconds>(time);auto sec = duration_cast<seconds>(ms); ms -= sec;returnQString::asprintf("%6lld.%03u",qint64(sec.count()),uint(ms.count()));}; QString timeFormat = pattern->timeArgs.at(timeArgsIdx); timeArgsIdx++;if(timeFormat =="process"_L1) { message +=formatElapsedTime(steady_clock::now() - pattern->appStartTime);}else if(timeFormat =="boot"_L1) {// just print the milliseconds since the elapsed timer reference// like the Linux kernel does message +=formatElapsedTime(steady_clock::now().time_since_epoch());#if QT_CONFIG(datestring)}else if(timeFormat.isEmpty()) { message.append(QDateTime::currentDateTime().toString(Qt::ISODate));}else{ message.append(QDateTime::currentDateTime().toString(timeFormat));#endif// QT_CONFIG(datestring)}}else if(token == ifCategoryTokenC) {if(isDefaultCategory(context.category)) skip =true;#define HANDLE_IF_TOKEN(LEVEL) \ } else if (token == if##LEVEL##TokenC) { \ skip = type != Qt##LEVEL##Msg;HANDLE_IF_TOKEN(Debug)HANDLE_IF_TOKEN(Info)HANDLE_IF_TOKEN(Warning)HANDLE_IF_TOKEN(Critical)HANDLE_IF_TOKEN(Fatal)#undef HANDLE_IF_TOKEN}else{ message.append(QLatin1StringView(token));}}return message;}static voidqDefaultMessageHandler(QtMsgType type,const QMessageLogContext &context,const QString &buf);// pointer to QtMessageHandler debug handler (with context) Q_CONSTINIT static QBasicAtomicPointer<void(QtMsgType,const QMessageLogContext &,const QString &)> messageHandler =Q_BASIC_ATOMIC_INITIALIZER(nullptr);// ------------------------ Alternate logging sinks -------------------------#if QT_CONFIG(slog2)#ifndef QT_LOG_CODE#define QT_LOG_CODE 9000#endifstatic boolslog2_default_handler(QtMsgType type,const QMessageLogContext &,const QString &message){if(shouldLogToStderr())return false;// Leave logging up to stderr handler QString formattedMessage = message; formattedMessage.append(u'\n');if(slog2_set_default_buffer((slog2_buffer_t)-1) ==0) { slog2_buffer_set_config_t buffer_config; slog2_buffer_t buffer_handle; buffer_config.buffer_set_name = __progname; buffer_config.num_buffers =1; buffer_config.verbosity_level = SLOG2_DEBUG1; buffer_config.buffer_config[0].buffer_name ="default"; buffer_config.buffer_config[0].num_pages =8;if(slog2_register(&buffer_config, &buffer_handle,0) == -1) {fprintf(stderr,"Error registering slogger2 buffer!\n");fprintf(stderr,"%s", formattedMessage.toLocal8Bit().constData());fflush(stderr);return false;}// Set as the default bufferslog2_set_default_buffer(buffer_handle);}int severity = SLOG2_INFO;//Determines the severity levelswitch(type) {case QtDebugMsg: severity = SLOG2_DEBUG1;break;case QtInfoMsg: severity = SLOG2_INFO;break;case QtWarningMsg: severity = SLOG2_NOTICE;break;case QtCriticalMsg: severity = SLOG2_WARNING;break;case QtFatalMsg: severity = SLOG2_ERROR;break;}//writes to the slog2 bufferslog2c(NULL, QT_LOG_CODE, severity, formattedMessage.toLocal8Bit().constData());return true;// Prevent further output to stderr}#endif// slog2#if QT_CONFIG(journald)static boolsystemd_default_message_handler(QtMsgType type,const QMessageLogContext &context,const QString &message){if(shouldLogToStderr())return false;// Leave logging up to stderr handlerint priority = LOG_INFO;// Informationalswitch(type) {case QtDebugMsg: priority = LOG_DEBUG;// Debug-level messagesbreak;case QtInfoMsg: priority = LOG_INFO;// Informational conditionsbreak;case QtWarningMsg: priority = LOG_WARNING;// Warning conditionsbreak;case QtCriticalMsg: priority = LOG_CRIT;// Critical conditionsbreak;case QtFatalMsg: priority = LOG_ALERT;// Action must be taken immediatelybreak;}sd_journal_send("MESSAGE=%s", message.toUtf8().constData(),"PRIORITY=%i", priority,"CODE_FUNC=%s", context.function ? context.function :"unknown","CODE_LINE=%d", context.line,"CODE_FILE=%s", context.file ? context.file :"unknown","QT_CATEGORY=%s", context.category ? context.category :"unknown", NULL);return true;// Prevent further output to stderr}#endif#if QT_CONFIG(syslog)static boolsyslog_default_message_handler(QtMsgType type,const QMessageLogContext &context,const QString &formattedMessage){if(shouldLogToStderr())return false;// Leave logging up to stderr handlerint priority = LOG_INFO;// Informationalswitch(type) {case QtDebugMsg: priority = LOG_DEBUG;// Debug-level messagesbreak;case QtInfoMsg: priority = LOG_INFO;// Informational conditionsbreak;case QtWarningMsg: priority = LOG_WARNING;// Warning conditionsbreak;case QtCriticalMsg: priority = LOG_CRIT;// Critical conditionsbreak;case QtFatalMsg: priority = LOG_ALERT;// Action must be taken immediatelybreak;}syslog(priority,"%s", formattedMessage.toUtf8().constData());return true;// Prevent further output to stderr}#endif#ifdef Q_OS_ANDROIDstatic boolandroid_default_message_handler(QtMsgType type,const QMessageLogContext &context,const QString &formattedMessage){if(shouldLogToStderr())return false;// Leave logging up to stderr handler android_LogPriority priority = ANDROID_LOG_DEBUG;switch(type) {case QtDebugMsg: priority = ANDROID_LOG_DEBUG;break;case QtInfoMsg: priority = ANDROID_LOG_INFO;break;case QtWarningMsg: priority = ANDROID_LOG_WARN;break;case QtCriticalMsg: priority = ANDROID_LOG_ERROR;break;case QtFatalMsg: priority = ANDROID_LOG_FATAL;break;}; QMessagePattern *pattern =qMessagePattern();const QString tag = (pattern && pattern->containsToken(categoryTokenC))// If application name is a tag ensure it has no spaces?QCoreApplication::applicationName().replace(u' ', u'_'):QString::fromUtf8(context.category);__android_log_print(priority,qPrintable(tag),"%s\n",qPrintable(formattedMessage));return true;// Prevent further output to stderr}#endif//Q_OS_ANDROID#ifdef Q_OS_WINstatic voidwin_outputDebugString_helper(const QString &message){const qsizetype maxOutputStringLength =32766; Q_CONSTINIT static QBasicMutex m;auto locker =qt_unique_lock(m);// fast path: Avoid string copies if one output is enoughif(message.length() <= maxOutputStringLength) {OutputDebugString(reinterpret_cast<const wchar_t*>(message.utf16()));}else{wchar_t*messagePart =newwchar_t[maxOutputStringLength +1];for(qsizetype i =0; i < message.length(); i += maxOutputStringLength) {const qsizetype length =qMin(message.length() - i, maxOutputStringLength);const qsizetype len = QStringView{message}.mid(i, length).toWCharArray(messagePart);Q_ASSERT(len == length); messagePart[len] =0;OutputDebugString(messagePart);}delete[] messagePart;}}static boolwin_message_handler(QtMsgType,const QMessageLogContext &,const QString &formattedMessage){if(shouldLogToStderr())return false;// Leave logging up to stderr handlerwin_outputDebugString_helper(formattedMessage + u'\n');return true;// Prevent further output to stderr}#endif#ifdef Q_OS_WASMstatic boolwasm_default_message_handler(QtMsgType type,const QMessageLogContext &,const QString &formattedMessage){static bool forceStderrLogging =qEnvironmentVariableIntValue("QT_FORCE_STDERR_LOGGING");if(forceStderrLogging)return false;int emOutputFlags = EM_LOG_CONSOLE; QByteArray localMsg = formattedMessage.toLocal8Bit();switch(type) {case QtDebugMsg:break;case QtInfoMsg:break;case QtWarningMsg: emOutputFlags |= EM_LOG_WARN;break;case QtCriticalMsg: emOutputFlags |= EM_LOG_ERROR;break;case QtFatalMsg: emOutputFlags |= EM_LOG_ERROR;}emscripten_log(emOutputFlags,"%s\n",qPrintable(formattedMessage));return true;// Prevent further output to stderr}#endif// --------------------------------------------------------------------------static voidstderr_message_handler(QtMsgType type,const QMessageLogContext &context,const QString &formattedMessage){Q_UNUSED(type);Q_UNUSED(context);// print nothing if message pattern didn't apply / was empty.// (still print empty lines, e.g. because message itself was empty)if(formattedMessage.isNull())return;fprintf(stderr,"%s\n", formattedMessage.toLocal8Bit().constData());fflush(stderr);}namespace{struct SystemMessageSink {using Fn =bool(QtMsgType,const QMessageLogContext &,const QString &); Fn *sink;bool messageIsUnformatted =false;};}staticconstexpr SystemMessageSink systemMessageSink = {#if defined(Q_OS_WIN) win_message_handler #elif QT_CONFIG(slog2) slog2_default_handler #elif QT_CONFIG(journald) systemd_default_message_handler,true#elif QT_CONFIG(syslog) syslog_default_message_handler #elif defined(Q_OS_ANDROID) android_default_message_handler #elif defined(QT_USE_APPLE_UNIFIED_LOGGING)AppleUnifiedLogger::messageHandler,true#elif defined Q_OS_WASM wasm_default_message_handler #elsenullptr#endif};static voidpreformattedMessageHandler(QtMsgType type,const QMessageLogContext &context,const QString &formattedMessage){ QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Waddress")// "the address of ~~ will never be NULLif(systemMessageSink.sink && systemMessageSink.sink(type, context, formattedMessage))return; QT_WARNING_POP stderr_message_handler(type, context, formattedMessage);}/*! \internal*/static voidqDefaultMessageHandler(QtMsgType type,const QMessageLogContext &context,const QString &message){// A message sink logs the message to a structured or unstructured destination,// optionally formatting the message if the latter, and returns true if the sink// handled stderr output as well, which will shortcut our default stderr output.if(systemMessageSink.messageIsUnformatted) {if(systemMessageSink.sink(type, context, message))return;}preformattedMessageHandler(type, context,formatLogMessage(type, context, message));} Q_CONSTINIT static thread_local bool msgHandlerGrabbed =false;static boolgrabMessageHandler(){if(msgHandlerGrabbed)return false; msgHandlerGrabbed =true;return true;}static voidungrabMessageHandler(){ msgHandlerGrabbed =false;}static voidqt_message_print(QtMsgType msgType,const QMessageLogContext &context,const QString &message){Q_TRACE(qt_message_print, msgType, context.category, context.function, context.file, context.line, message);// qDebug, qWarning, ... macros do not check whether category is enabledgcif(msgType != QtFatalMsg &&isDefaultCategory(context.category)) {if(QLoggingCategory *defaultCategory =QLoggingCategory::defaultCategory()) {if(!defaultCategory->isEnabled(msgType))return;}}// prevent recursion in case the message handler generates messages// itself, e.g. by using Qt APIif(grabMessageHandler()) {constauto ungrab =qScopeGuard([]{ungrabMessageHandler(); });auto msgHandler = messageHandler.loadAcquire();(msgHandler ? msgHandler : qDefaultMessageHandler)(msgType, context, message);}else{stderr_message_handler(msgType, context, message);}}template<typename String>static voidqt_maybe_message_fatal(QtMsgType msgType,const QMessageLogContext &context, String &&message){if(!isFatal(msgType))return;#if defined(Q_CC_MSVC_ONLY) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR)wchar_t contextFileL[256];// we probably should let the compiler do this for us, by declaring QMessageLogContext::file to// be const wchar_t * in the first place, but the #ifdefery above is very complex and we// wouldn't be able to change it later on...convert_to_wchar_t_elided(contextFileL,sizeof contextFileL /sizeof*contextFileL, context.file);// get the current report modeint reportMode =_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW);_CrtSetReportMode(_CRT_ERROR, reportMode);int ret =_CrtDbgReportW(_CRT_ERROR, contextFileL, context.line,_CRT_WIDE(QT_VERSION_STR),reinterpret_cast<const wchar_t*>(message.utf16()));if((ret ==0) && (reportMode & _CRTDBG_MODE_WNDW))return;// ignoreelse if(ret ==1)_CrtDbgBreak();#elseQ_UNUSED(context);#endififconstexpr(std::is_class_v<String> && !std::is_const_v<String>) message.clear();elseQ_UNUSED(message);qAbort();}/*! \internal*/voidqt_message_output(QtMsgType msgType,const QMessageLogContext &context,const QString &message){ QInternalMessageLogContext ctx(context);qt_message_print(msgType, ctx, message);qt_maybe_message_fatal(msgType, ctx, message);}voidqErrnoWarning(const char*msg, ...){// qt_error_string() will allocate anyway, so we don't have// to be careful here (like we do in plain qWarning()) QString error_string =qt_error_string(-1);// before vasprintf changes errno/GetLastError()va_list ap;va_start(ap, msg); QString buf =QString::vasprintf(msg, ap);va_end(ap); buf +=" ("_L1 + error_string + u')'; QInternalMessageLogContext context{QMessageLogContext()};qt_message_output(QtWarningMsg, context, buf);}voidqErrnoWarning(int code,const char*msg, ...){// qt_error_string() will allocate anyway, so we don't have// to be careful here (like we do in plain qWarning())va_list ap;va_start(ap, msg); QString buf =QString::vasprintf(msg, ap);va_end(ap); buf +=" ("_L1 +qt_error_string(code) + u')'; QInternalMessageLogContext context{QMessageLogContext()};qt_message_output(QtWarningMsg, context, buf);}/*! \typedef QtMessageHandler \relates <QtLogging> \since 5.0 This is a typedef for a pointer to a function with the following signature: \snippet code/src_corelib_global_qglobal.cpp 49 \sa QtMsgType, qInstallMessageHandler()*//*! \fn QtMessageHandler qInstallMessageHandler(QtMessageHandler handler) \relates <QtLogging> \since 5.0 Installs a Qt message \a handler. Returns a pointer to the previously installed message handler. A message handler is a function that prints out debug, info, warning, critical, and fatal messages from Qt's logging infrastructure. By default, Qt uses a standard message handler that formats and prints messages to different sinks specific to the operating system and Qt configuration. Installing your own message handler allows you to assume full control, and for instance log messages to the file system. Note that Qt supports \l{QLoggingCategory}{logging categories} for grouping related messages in semantic categories. You can use these to enable or disable logging per category and \l{QtMsgType}{message type}. As the filtering for logging categories is done even before a message is created, messages for disabled types and categories will not reach the message handler. A message handler needs to be \l{Reentrancy and Thread-Safety}{reentrant}. That is, it might be called from different threads, in parallel. Therefore, writes to common sinks (like a database, or a file) often need to be synchronized. Qt allows to enrich logging messages with further meta-information by calling \l qSetMessagePattern(), or setting the \c QT_MESSAGE_PATTERN environment variable. To keep this formatting, a custom message handler can use \l qFormatLogMessage(). Try to keep the code in the message handler itself minimal, as expensive operations might block the application. Also, to avoid recursion, any logging messages generated in the message handler itself will be ignored. The message handler should always return. For \l{QtFatalMsg}{fatal messages}, the application aborts immediately after handling that message. Only one message handler can be installed at a time, for the whole application. If there was a previous custom message handler installed, the function will return a pointer to it. This handler can then be later reinstalled by another call to the method. Also, calling \c qInstallMessageHandler(nullptr) will restore the default message handler. Here is an example of a message handler that logs to a local file before calling the default handler: \snippet code/src_corelib_global_qglobal.cpp 23 Note that the C++ standard guarantees that \c{static FILE *f} is initialized in a thread-safe way. We can also expect \c{fprintf()} and \c{fflush()} to be thread-safe, so no further synchronization is necessary. \sa QtMessageHandler, QtMsgType, qDebug(), qInfo(), qWarning(), qCritical(), qFatal(), {Debugging Techniques}, qFormatLogMessage()*//*! \fn void qSetMessagePattern(const QString &pattern) \relates <QtLogging> \since 5.0 \brief Changes the output of the default message handler. Allows to tweak the output of qDebug(), qInfo(), qWarning(), qCritical(), and qFatal(). The category logging output of qCDebug(), qCInfo(), qCWarning(), and qCCritical() is formatted, too. Following placeholders are supported: \table \header \li Placeholder \li Description \row \li \c %{appname} \li QCoreApplication::applicationName() \row \li \c %{category} \li Logging category \row \li \c %{file} \li Path to source file \row \li \c %{function} \li Function \row \li \c %{line} \li Line in source file \row \li \c %{message} \li The actual message \row \li \c %{pid} \li QCoreApplication::applicationPid() \row \li \c %{threadid} \li The system-wide ID of current thread (if it can be obtained) \row \li \c %{threadname} \li The current thread name (if it can be obtained, or the thread ID, since Qt 6.10) \row \li \c %{qthreadptr} \li A pointer to the current QThread (result of QThread::currentThread()) \row \li \c %{type} \li "debug", "warning", "critical" or "fatal" \row \li \c %{time process} \li time of the message, in seconds since the process started (the token "process" is literal) \row \li \c %{time boot} \li the time of the message, in seconds since the system boot if that can be determined (the token "boot" is literal). If the time since boot could not be obtained, the output is indeterminate (see QElapsedTimer::msecsSinceReference()). \row \li \c %{time [format]} \li system time when the message occurred, formatted by passing the \c format to \l QDateTime::toString(). If the format is not specified, the format of Qt::ISODate is used. \row \li \c{%{backtrace [depth=N] [separator="..."]}} \li A backtrace with the number of frames specified by the optional \c depth parameter (defaults to 5), and separated by the optional \c separator parameter (defaults to "|"). This expansion is available only on some platforms: \list \li platforms using glibc; \li platforms shipping C++23's \c{<stacktrace>} header (requires compiling Qt in C++23 mode). \endlist Depending on the platform, there are some restrictions on the function names printed by this expansion. On some platforms, names are only known for exported functions. If you want to see the name of every function in your application, make sure your application is compiled and linked with \c{-rdynamic}, or an equivalent of it. When reading backtraces, take into account that frames might be missing due to inlining or tail call optimization. \endtable You can also use conditionals on the type of the message using \c %{if-debug}, \c %{if-info} \c %{if-warning}, \c %{if-critical} or \c %{if-fatal} followed by an \c %{endif}. What is inside the \c %{if-*} and \c %{endif} will only be printed if the type matches. Finally, text inside \c %{if-category} ... \c %{endif} is only printed if the category is not the default one. Example: \snippet code/src_corelib_global_qlogging.cpp 0 The default \a pattern is \c{%{if-category}%{category}: %{endif}%{message}}. \note On Android, the default \a pattern is \c{%{message}} because the category is used as \l{Android: log_print}{tag} since Android logcat has a dedicated field for the logging categories, see \l{Android: Log}{Android Logging}. If a custom \a pattern including the category is used, QCoreApplication::applicationName() is used as \l{Android: log_print}{tag}. The \a pattern can also be changed at runtime by setting the QT_MESSAGE_PATTERN environment variable; if both \l qSetMessagePattern() is called and QT_MESSAGE_PATTERN is set, the environment variable takes precedence. \note The information for the placeholders \c category, \c file, \c function and \c line is only recorded in debug builds. Alternatively, \c QT_MESSAGELOGCONTEXT can be defined explicitly. For more information refer to the QMessageLogContext documentation. \note The message pattern only applies to unstructured logging, such as the default \c stderr output. Structured logging such as systemd will record the message as is, along with as much structured information as can be captured. Custom message handlers can use qFormatLogMessage() to take \a pattern into account. \sa qInstallMessageHandler(), {Debugging Techniques}, {QLoggingCategory}, QMessageLogContext */ QtMessageHandler qInstallMessageHandler(QtMessageHandler h){constauto old = messageHandler.fetchAndStoreOrdered(h);if(old)return old;elsereturn qDefaultMessageHandler;}voidqSetMessagePattern(const QString &pattern){constauto locker =qt_scoped_lock(QMessagePattern::mutex);if(!qMessagePattern()->fromEnvironment)qMessagePattern()->setPattern(pattern);}static voidcopyInternalContext(QInternalMessageLogContext *self,const QMessageLogContext &logContext) noexcept {if(logContext.version == self->version) {auto other =static_cast<const QInternalMessageLogContext *>(&logContext); self->backtrace = other->backtrace;}}/*! \internal Copies context information from \a logContext into this QMessageLogContext. Returns the number of backtrace frames that are desired.*/intQInternalMessageLogContext::initFrom(const QMessageLogContext &logContext){ version = CurrentVersion +1;copyContextFrom(logContext);#ifdef QLOGGING_HAVE_BACKTRACEif(backtrace.has_value())return0;// we have a stored backtrace, no need to get it again// initializes the message pattern, if neededif(auto pattern =qMessagePattern())return pattern->maxBacktraceDepth;#endifreturn0;}/*! Copies context information from \a logContext into this QMessageLogContext. Returns a reference to this object. Note that the version is \b not copied, only the context information. \internal*/ QMessageLogContext &QMessageLogContext::copyContextFrom(const QMessageLogContext &logContext) noexcept {this->category = logContext.category;this->file = logContext.file;this->line = logContext.line;this->function = logContext.function;if(Q_UNLIKELY(version == CurrentVersion +1))copyInternalContext(static_cast<QInternalMessageLogContext *>(this), logContext);return*this;}/*! \fn QMessageLogger::QMessageLogger() Constructs a default QMessageLogger. See the other constructors to specify context information.*//*! \fn QMessageLogger::QMessageLogger(const char *file, int line, const char *function) Constructs a QMessageLogger to record log messages for \a file at \a line in \a function. The is equivalent to QMessageLogger(file, line, function, "default")*//*! \fn QMessageLogger::QMessageLogger(const char *file, int line, const char *function, const char *category) Constructs a QMessageLogger to record \a category messages for \a file at \a line in \a function. \sa QLoggingCategory*//*! \fn void QMessageLogger::noDebug(const char *, ...) const \internal Ignores logging output \sa QNoDebug, qDebug()*//*! \fn QMessageLogContext::QMessageLogContext() \internal Constructs a QMessageLogContext*//*! \fn QMessageLogContext::QMessageLogContext(const char *fileName, int lineNumber, const char *functionName, const char *categoryName) \internal Constructs a QMessageLogContext with for file \a fileName at line \a lineNumber, in function \a functionName, and category \a categoryName. \sa QLoggingCategory*//*! \macro qDebug(const char *message, ...) \relates <QtLogging> \threadsafe Calls the message handler with the debug message \a message. If no message handler has been installed, the message is printed to stderr. Under Windows the message is sent to the console, if it is a console application; otherwise, it is sent to the debugger. On QNX, the message is sent to slogger2. This function does nothing if \c QT_NO_DEBUG_OUTPUT was defined during compilation. If you pass the function a format string and a list of arguments, it works in similar way to the C printf() function. The format should be a Latin-1 string. Example: \snippet code/src_corelib_global_qglobal.cpp 24 If you include \c <QtDebug>, a more convenient syntax is also available: \snippet code/src_corelib_global_qglobal.cpp 25 With this syntax, the function returns a QDebug object that is configured to use the QtDebugMsg message type. It automatically puts a single space between each item, and outputs a newline at the end. It supports many C++ and Qt types. To suppress the output at runtime, install your own message handler with qInstallMessageHandler(). \sa qCDebug(), qInfo(), qWarning(), qCritical(), qFatal(), qInstallMessageHandler(), {Debugging Techniques}*//*! \macro qInfo(const char *message, ...) \relates <QtLogging> \threadsafe \since 5.5 Calls the message handler with the informational message \a message. If no message handler has been installed, the message is printed to stderr. Under Windows, the message is sent to the console, if it is a console application; otherwise, it is sent to the debugger. On QNX the message is sent to slogger2. This function does nothing if \c QT_NO_INFO_OUTPUT was defined during compilation. If you pass the function a format string and a list of arguments, it works in similar way to the C printf() function. The format should be a Latin-1 string. Example: \snippet code/src_corelib_global_qglobal.cpp qInfo_printf If you include \c <QtDebug>, a more convenient syntax is also available: \snippet code/src_corelib_global_qglobal.cpp qInfo_stream With this syntax, the function returns a QDebug object that is configured to use the QtInfoMsg message type. It automatically puts a single space between each item, and outputs a newline at the end. It supports many C++ and Qt types. To suppress the output at runtime, install your own message handler using qInstallMessageHandler(). \sa qCInfo(), qDebug(), qWarning(), qCritical(), qFatal(), qInstallMessageHandler(), {Debugging Techniques}*//*! \macro qWarning(const char *message, ...) \relates <QtLogging> \threadsafe Calls the message handler with the warning message \a message. If no message handler has been installed, the message is printed to stderr. Under Windows, the message is sent to the debugger. On QNX the message is sent to slogger2. This function takes a format string and a list of arguments, similar to the C printf() function. The format should be a Latin-1 string. Example: \snippet code/src_corelib_global_qglobal.cpp 26 If you include <QtDebug>, a more convenient syntax is also available: \snippet code/src_corelib_global_qglobal.cpp 27 This syntax inserts a space between each item, and appends a newline at the end. This function does nothing if \c QT_NO_WARNING_OUTPUT was defined during compilation. To suppress the output at runtime, you can set \l{QLoggingCategory}{logging rules} or register a custom \l{QLoggingCategory::installFilter()}{filter}. For debugging purposes, it is sometimes convenient to let the program abort for warning messages. This allows you then to inspect the core dump, or attach a debugger - see also \l{qFatal()}. To enable this, set the environment variable \c{QT_FATAL_WARNINGS} to a number \c n. The program terminates then for the n-th warning. That is, if the environment variable is set to 1, it will terminate on the first call; if it contains the value 10, it will exit on the 10th call. Any non-numeric value in the environment variable is equivalent to 1. \sa qCWarning(), qDebug(), qInfo(), qCritical(), qFatal(), qInstallMessageHandler(), {Debugging Techniques}*//*! \macro qCritical(const char *message, ...) \relates <QtLogging> \threadsafe Calls the message handler with the critical message \a message. If no message handler has been installed, the message is printed to stderr. Under Windows, the message is sent to the debugger. On QNX the message is sent to slogger2. This function takes a format string and a list of arguments, similar to the C printf() function. The format should be a Latin-1 string. Example: \snippet code/src_corelib_global_qglobal.cpp 28 If you include <QtDebug>, a more convenient syntax is also available: \snippet code/src_corelib_global_qglobal.cpp 29 A space is inserted between the items, and a newline is appended at the end. To suppress the output at runtime, you can define \l{QLoggingCategory}{logging rules} or register a custom \l{QLoggingCategory::installFilter()}{filter}. For debugging purposes, it is sometimes convenient to let the program abort for critical messages. This allows you then to inspect the core dump, or attach a debugger - see also \l{qFatal()}. To enable this, set the environment variable \c{QT_FATAL_CRITICALS} to a number \c n. The program terminates then for the n-th critical message. That is, if the environment variable is set to 1, it will terminate on the first call; if it contains the value 10, it will exit on the 10th call. Any non-numeric value in the environment variable is equivalent to 1. \sa qCCritical(), qDebug(), qInfo(), qWarning(), qFatal(), qInstallMessageHandler(), {Debugging Techniques}*//*! \macro qFatal(const char *message, ...) \relates <QtLogging> Calls the message handler with the fatal message \a message. If no message handler has been installed, the message is printed to stderr. Under Windows, the message is sent to the debugger. On QNX the message is sent to slogger2. If you are using the \b{default message handler} this function will abort to create a core dump. On Windows, for debug builds, this function will report a _CRT_ERROR enabling you to connect a debugger to the application. This function takes a format string and a list of arguments, similar to the C printf() function. Example: \snippet code/src_corelib_global_qglobal.cpp 30 To suppress the output at runtime, install your own message handler with qInstallMessageHandler(). \sa qCFatal(), qDebug(), qInfo(), qWarning(), qCritical(), qInstallMessageHandler(), {Debugging Techniques}*//*! \enum QtMsgType \relates <QtLogging> This enum describes the messages that can be sent to a message handler (QtMessageHandler). You can use the enum to identify and associate the various message types with the appropriate actions. Its values are, in order of increasing severity: \value QtDebugMsg A message generated by the qDebug() function. \value QtInfoMsg A message generated by the qInfo() function. \value QtWarningMsg A message generated by the qWarning() function. \value QtCriticalMsg A message generated by the qCritical() function. \value QtFatalMsg A message generated by the qFatal() function. \omitvalue QtSystemMsg \sa QtMessageHandler, qInstallMessageHandler(), QLoggingCategory*/ QT_END_NAMESPACE
|