Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qlogging.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
3// Copyright (C) 2022 Intel Corporation.
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5// Qt-Security score:significant reason:default
6
7#include "qlogging.h"
8#include "qlogging_p.h"
9
10#include "qbytearray.h"
11#include "qlist.h"
13#include "private/qcoreapplication_p.h"
14#include "qdatetime.h"
15#include "qdebug.h"
16#include "qgettid_p.h"
17#include "private/qlocking_p.h"
19#include "private/qloggingregistry_p.h"
20#include "qmutex.h"
21#include "qscopeguard.h"
22#include "qstring.h"
23#include "qtcore_tracepoints_p.h"
24#include "qthread.h"
26
27#ifdef Q_CC_MSVC
28#include <intrin.h>
29#endif
30#if QT_CONFIG(slog2)
31#include <sys/slog2.h>
32#endif
33#if __has_include(<paths.h>)
34#include <paths.h>
35#endif
36
37#ifdef Q_OS_ANDROID
38#include <android/log.h>
39#endif
40
41#ifdef Q_OS_DARWIN
42#include <QtCore/private/qcore_mac_p.h>
43#endif
44
45#if QT_CONFIG(journald)
46# define SD_JOURNAL_SUPPRESS_LOCATION
47# include <systemd/sd-journal.h>
48# include <syslog.h>
49#endif
50#if QT_CONFIG(syslog)
51# include <syslog.h>
52#endif
53#ifdef Q_OS_UNIX
54# include <sys/types.h>
55# include <sys/stat.h>
56# include <unistd.h>
57# include "private/qcore_unix_p.h"
58#endif
59
60#ifdef Q_OS_WASM
61#include <emscripten/emscripten.h>
62#endif
63
64#if QT_CONFIG(slog2)
65extern char *__progname;
66#endif
67
68#ifdef QLOGGING_HAVE_BACKTRACE
69# include <qregularexpression.h>
70#endif
71
72#ifdef QLOGGING_USE_EXECINFO_BACKTRACE
73# if QT_CONFIG(dladdr)
74# include <dlfcn.h>
75# endif
76# include BACKTRACE_HEADER
77# include <cxxabi.h>
78#endif // QLOGGING_USE_EXECINFO_BACKTRACE
79
80#include <cstdlib>
81#include <algorithm>
82#include <chrono>
83#include <memory>
84#include <vector>
85
86#include <stdio.h>
87
88#ifdef Q_OS_WIN
89#include <qt_windows.h>
90#include <processthreadsapi.h>
91#include "qfunctionpointer.h"
92#endif
93
95
96using namespace Qt::StringLiterals;
97
98Q_TRACE_POINT(qtcore, qt_message_print, int type, const char *category, const char *function, const char *file, int line, const QString &message);
99
100/*!
101 \headerfile <QtLogging>
102 \inmodule QtCore
103 \title Qt Logging Types
104
105 \brief The <QtLogging> header file defines Qt logging types, functions
106 and macros.
107
108 The <QtLogging> header file contains several types, functions and
109 macros for logging.
110
111 The QtMsgType enum identifies the various messages that can be generated
112 and sent to a Qt message handler; QtMessageHandler is a type definition for
113 a pointer to a function with the signature
114 \c {void myMessageHandler(QtMsgType, const QMessageLogContext &, const char *)}.
115 qInstallMessageHandler() function can be used to install the given
116 QtMessageHandler. QMessageLogContext class contains the line, file, and
117 function the message was logged at. This information is created by the
118 QMessageLogger class.
119
120 <QtLogging> also contains functions that generate messages from the
121 given string argument: qDebug(), qInfo(), qWarning(), qCritical(),
122 and qFatal(). These functions call the message handler
123 with the given message.
124
125 Example:
126
127 \snippet code/src_corelib_global_qglobal.cpp 4
128
129 \sa QLoggingCategory
130*/
131
132template <typename String>
133static void qt_maybe_message_fatal(QtMsgType, const QMessageLogContext &context, String &&message);
134static void qt_message_print(QtMsgType, const QMessageLogContext &context, const QString &message);
135static void preformattedMessageHandler(QtMsgType type, const QMessageLogContext &context,
136 const QString &formattedMessage);
137static QString formatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &str);
138
139static int checked_var_value(const char *varname)
140{
141 // qEnvironmentVariableIntValue returns 0 on both parsing failure and on
142 // empty, but we need to distinguish between the two for backwards
143 // compatibility reasons.
144 QByteArray str = qgetenv(varname);
145 if (str.isEmpty())
146 return 0;
147
148 bool ok;
149 int value = str.toInt(&ok, 0);
150 return (ok && value >= 0) ? value : 1;
151}
152
153static bool isFatalCountDown(const char *varname, QBasicAtomicInt &n)
154{
155 static const int Uninitialized = 0;
156 static const int NeverFatal = 1;
157 static const int ImmediatelyFatal = 2;
158
159 int v = n.loadRelaxed();
160 if (v == Uninitialized) {
161 // first, initialize from the environment
162 // note that the atomic stores the env.var value plus 1, so adjust
163 const int env = checked_var_value(varname) + 1;
164 if (env == NeverFatal) {
165 // not fatal, now or in the future, so use a fast path
166 n.storeRelaxed(NeverFatal);
167 return false;
168 } else if (env == ImmediatelyFatal) {
169 return true;
170 } else if (n.testAndSetRelaxed(Uninitialized, env - 1, v)) {
171 return false; // not yet fatal, but decrement
172 } else {
173 // some other thread initialized before we did
174 }
175 }
176
177 while (v > ImmediatelyFatal && !n.testAndSetRelaxed(v, v - 1, v))
178 qYieldCpu();
179
180 // We exited the loop, so either v already was ImmediatelyFatal or we
181 // succeeded to set n from v to v-1.
182 return v == ImmediatelyFatal;
183}
184
185Q_CONSTINIT static QBasicAtomicInt fatalCriticalsCount = Q_BASIC_ATOMIC_INITIALIZER(0);
186Q_CONSTINIT static QBasicAtomicInt fatalWarningsCount = Q_BASIC_ATOMIC_INITIALIZER(0);
187static bool isFatal(QtMsgType msgType)
188{
189 switch (msgType){
190 case QtFatalMsg:
191 return true; // always fatal
192
193 case QtCriticalMsg:
194 return isFatalCountDown("QT_FATAL_CRITICALS", fatalCriticalsCount);
195
196 case QtWarningMsg:
197 return isFatalCountDown("QT_FATAL_WARNINGS", fatalWarningsCount);
198
199 case QtDebugMsg:
200 case QtInfoMsg:
201 break; // never fatal
202 }
203
204 return false;
205}
206
207#if defined(Q_OS_LINUX) || defined(Q_OS_DARWIN) || defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
208static bool qt_append_thread_name_to(QString &message)
209{
210 std::array<char, 16> name{};
211 if (pthread_getname_np(pthread_self(), name.data(), name.size()) == 0) {
212 QUtf8StringView threadName(name.data());
213 if (!threadName.isEmpty()) {
214 message.append(threadName);
215 return true;
216 }
217 }
218 return false;
219}
220#elif defined(Q_OS_WIN)
221typedef HRESULT (WINAPI *GetThreadDescriptionFunc)(HANDLE, PWSTR *);
222static bool qt_append_thread_name_to(QString &message)
223{
224 // Once MinGW 12.0 is required for Qt, we can call GetThreadDescription directly
225 // instead of this runtime resolve:
226 static GetThreadDescriptionFunc pGetThreadDescription = []() -> GetThreadDescriptionFunc {
227 HMODULE hKernel = GetModuleHandleW(L"kernel32.dll");
228 if (!hKernel)
229 return nullptr;
230 auto funcPtr = reinterpret_cast<QFunctionPointer>(GetProcAddress(hKernel, "GetThreadDescription"));
231 return reinterpret_cast<GetThreadDescriptionFunc>(funcPtr);
232 } ();
233 if (!pGetThreadDescription)
234 return false; // Not available on this system
235 PWSTR description = nullptr;
236 HRESULT hr = pGetThreadDescription(GetCurrentThread(), &description);
237 std::unique_ptr<WCHAR, decltype(&LocalFree)> descriptionOwner(description, &LocalFree);
238 if (SUCCEEDED(hr)) {
239 QStringView threadName(description);
240 if (!threadName.isEmpty()) {
241 message.append(threadName);
242 return true;
243 }
244 }
245 return false;
246}
247#else
248static bool qt_append_thread_name_to(QString &message)
249{
250 Q_UNUSED(message)
251 return false;
252}
253#endif
254
255#ifndef Q_OS_WASM
256
257/*!
258 Returns true if writing to \c stderr is supported.
259
260 \internal
261 \sa stderrHasConsoleAttached()
262*/
263static bool systemHasStderr()
264{
265 return true;
266}
267
268/*!
269 Returns true if writing to \c stderr will end up in a console/terminal visible to the user.
270
271 This is typically the case if the application was started from the command line.
272
273 If the application is started without a controlling console/terminal, but the parent
274 process reads \c stderr and presents it to the user in some other way, the parent process
275 may override the detection in this function by setting the QT_ASSUME_STDERR_HAS_CONSOLE
276 environment variable to \c 1.
277
278 \note Qt Creator does not implement a pseudo TTY, nor does it launch apps with
279 the override environment variable set, but it will read stderr and print it to
280 the user, so in effect this function cannot be used to conclude that stderr
281 output will _not_ be visible to the user, as even if this function returns false,
282 the output might still end up visible to the user. For this reason, we don't guard
283 the stderr output in the default message handler with stderrHasConsoleAttached().
284
285 \internal
286 \sa systemHasStderr()
287*/
289{
290 static const bool stderrHasConsoleAttached = []() -> bool {
292 return false;
293
294 if (qEnvironmentVariableIntValue("QT_LOGGING_TO_CONSOLE")) {
295 fprintf(stderr, "warning: Environment variable QT_LOGGING_TO_CONSOLE is deprecated, use\n"
296 "QT_ASSUME_STDERR_HAS_CONSOLE and/or QT_FORCE_STDERR_LOGGING instead.\n");
297 return true;
298 }
299
300 if (qEnvironmentVariableIntValue("QT_ASSUME_STDERR_HAS_CONSOLE"))
301 return true;
302
303#if defined(Q_OS_WIN)
304 return GetConsoleWindow();
305#elif defined(Q_OS_UNIX)
306# ifndef _PATH_TTY
307# define _PATH_TTY "/dev/tty"
308# endif
309
310 // If we can open /dev/tty, we have a controlling TTY
311 int ttyDevice = -1;
312 if ((ttyDevice = qt_safe_open(_PATH_TTY, O_RDONLY)) >= 0) {
313 qt_safe_close(ttyDevice);
314 return true;
315 } else if (errno == ENOENT || errno == EPERM || errno == ENXIO) {
316 // Fall back to isatty for some non-critical errors
317 return isatty(STDERR_FILENO);
318 } else {
319 return false;
320 }
321#else
322 return false; // No way to detect if stderr has a console attached
323#endif
324 }();
325
326 return stderrHasConsoleAttached;
327}
328
329
330namespace QtPrivate {
331
332/*!
333 Returns true if logging \c stderr should be ensured.
334
335 This is normally the case if \c stderr has a console attached, but may be overridden
336 by the user by setting the QT_FORCE_STDERR_LOGGING environment variable to \c 1.
337
338 \internal
339 \sa stderrHasConsoleAttached()
340*/
342{
343 static bool forceStderrLogging = qEnvironmentVariableIntValue("QT_FORCE_STDERR_LOGGING");
344 return forceStderrLogging || stderrHasConsoleAttached();
345}
346
347
348} // QtPrivate
349
350using namespace QtPrivate;
351
352#endif // ifndef Q_OS_WASM
353
354/*!
355 \class QMessageLogContext
356 \inmodule QtCore
357 \brief The QMessageLogContext class provides additional information about a log message.
358 \since 5.0
359
360 The class provides information about the source code location a qDebug(), qInfo(), qWarning(),
361 qCritical() or qFatal() message was generated.
362
363 \note By default, this information is recorded only in debug builds. You can overwrite
364 this explicitly by defining \c QT_MESSAGELOGCONTEXT or \c{QT_NO_MESSAGELOGCONTEXT}.
365
366 \sa QMessageLogger, QtMessageHandler, qInstallMessageHandler()
367*/
368
369/*!
370 \class QMessageLogger
371 \inmodule QtCore
372 \brief The QMessageLogger class generates log messages.
373 \since 5.0
374
375 QMessageLogger is used to generate messages for the Qt logging framework. Usually one uses
376 it through qDebug(), qInfo(), qWarning(), qCritical, or qFatal() functions,
377 which are actually macros: For example qDebug() expands to
378 QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO).debug()
379 for debug builds, and QMessageLogger(0, 0, 0).debug() for release builds.
380
381 One example of direct use is to forward errors that stem from a scripting language, e.g. QML:
382
383 \snippet qlogging/qlogging.cpp 1
384
385 \sa QMessageLogContext, qDebug(), qInfo(), qWarning(), qCritical(), qFatal()
386*/
387
388#if defined(Q_CC_MSVC_ONLY) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR)
389static inline void convert_to_wchar_t_elided(wchar_t *d, size_t space, const char *s) noexcept
390{
391 size_t len = qstrlen(s);
392 if (len + 1 > space) {
393 const size_t skip = len - space + 4; // 4 for "..." + '\0'
394 s += skip;
395 len -= skip;
396 for (int i = 0; i < 3; ++i)
397 *d++ = L'.';
398 }
399 while (len--)
400 *d++ = *s++;
401 *d++ = 0;
402}
403#endif
404
405/*!
406 \internal
407*/
409static void qt_message(QtMsgType msgType, const QMessageLogContext &context, const char *msg, va_list ap)
410{
411 QString buf = QString::vasprintf(msg, ap);
412 qt_message_print(msgType, context, buf);
413 qt_maybe_message_fatal(msgType, context, buf);
414}
415
416/*!
417 Logs a debug message specified with format \a msg. Additional
418 parameters, specified by \a msg, may be used.
419
420 \sa qDebug()
421*/
422void QMessageLogger::debug(const char *msg, ...) const
423{
424 QInternalMessageLogContext ctxt(context);
425 va_list ap;
426 va_start(ap, msg); // use variable arg list
427 qt_message(QtDebugMsg, ctxt, msg, ap);
428 va_end(ap);
429}
430
431/*!
432 Logs an informational message specified with format \a msg. Additional
433 parameters, specified by \a msg, may be used.
434
435 \sa qInfo()
436 \since 5.5
437*/
438void QMessageLogger::info(const char *msg, ...) const
439{
440 QInternalMessageLogContext ctxt(context);
441 va_list ap;
442 va_start(ap, msg); // use variable arg list
443 qt_message(QtInfoMsg, ctxt, msg, ap);
444 va_end(ap);
445}
446
447/*!
448 \typedef QMessageLogger::CategoryFunction
449
450 This is a typedef for a pointer to a function with the following
451 signature:
452
453 \snippet qlogging/qlogging.cpp 2
454
455 The \c Q_DECLARE_LOGGING_CATEGORY macro generates a function declaration
456 with this signature, and \c Q_LOGGING_CATEGORY generates its definition.
457
458 \since 5.3
459
460 \sa QLoggingCategory
461*/
462
463/*!
464 Logs a debug message specified with format \a msg for the context \a cat.
465 Additional parameters, specified by \a msg, may be used.
466
467 \since 5.3
468 \sa qCDebug()
469*/
470void QMessageLogger::debug(const QLoggingCategory &cat, const char *msg, ...) const
471{
472 if (!cat.isDebugEnabled())
473 return;
474
475 QInternalMessageLogContext ctxt(context, cat());
476
477 va_list ap;
478 va_start(ap, msg); // use variable arg list
479 qt_message(QtDebugMsg, ctxt, msg, ap);
480 va_end(ap);
481}
482
483/*!
484 Logs a debug message specified with format \a msg for the context returned
485 by \a catFunc. Additional parameters, specified by \a msg, may be used.
486
487 \since 5.3
488 \sa qCDebug()
489*/
490void QMessageLogger::debug(QMessageLogger::CategoryFunction catFunc,
491 const char *msg, ...) const
492{
493 const QLoggingCategory &cat = (*catFunc)();
494 if (!cat.isDebugEnabled())
495 return;
496
497 QInternalMessageLogContext ctxt(context, cat());
498
499 va_list ap;
500 va_start(ap, msg); // use variable arg list
501 qt_message(QtDebugMsg, ctxt, msg, ap);
502 va_end(ap);
503}
504
505#ifndef QT_NO_DEBUG_STREAM
506
507/*!
508 Logs a debug message using a QDebug stream
509
510 \sa qDebug(), QDebug
511*/
512QDebug QMessageLogger::debug() const
513{
514 QDebug dbg = QDebug(QtDebugMsg);
515 QMessageLogContext &ctxt = dbg.stream->context;
516 ctxt.copyContextFrom(context);
517 return dbg;
518}
519
520/*!
521 Logs a debug message into category \a cat using a QDebug stream.
522
523 \since 5.3
524 \sa qCDebug(), QDebug
525*/
526QDebug QMessageLogger::debug(const QLoggingCategory &cat) const
527{
528 QDebug dbg = QDebug(QtDebugMsg);
529 if (!cat.isDebugEnabled())
530 dbg.stream->message_output = false;
531
532 QMessageLogContext &ctxt = dbg.stream->context;
533 ctxt.copyContextFrom(context);
534 ctxt.category = cat.categoryName();
535
536 return dbg;
537}
538
539/*!
540 Logs a debug message into category returned by \a catFunc using a QDebug stream.
541
542 \since 5.3
543 \sa qCDebug(), QDebug
544*/
545QDebug QMessageLogger::debug(QMessageLogger::CategoryFunction catFunc) const
546{
547 return debug((*catFunc)());
548}
549#endif
550
551/*!
552 Logs an informational message specified with format \a msg for the context \a cat.
553 Additional parameters, specified by \a msg, may be used.
554
555 \since 5.5
556 \sa qCInfo()
557*/
558void QMessageLogger::info(const QLoggingCategory &cat, const char *msg, ...) const
559{
560 if (!cat.isInfoEnabled())
561 return;
562
563 QInternalMessageLogContext ctxt(context, cat());
564
565 va_list ap;
566 va_start(ap, msg); // use variable arg list
567 qt_message(QtInfoMsg, ctxt, msg, ap);
568 va_end(ap);
569}
570
571/*!
572 Logs an informational message specified with format \a msg for the context returned
573 by \a catFunc. Additional parameters, specified by \a msg, may be used.
574
575 \since 5.5
576 \sa qCInfo()
577*/
578void QMessageLogger::info(QMessageLogger::CategoryFunction catFunc,
579 const char *msg, ...) const
580{
581 const QLoggingCategory &cat = (*catFunc)();
582 if (!cat.isInfoEnabled())
583 return;
584
585 QInternalMessageLogContext ctxt(context, cat());
586
587 va_list ap;
588 va_start(ap, msg); // use variable arg list
589 qt_message(QtInfoMsg, ctxt, msg, ap);
590 va_end(ap);
591}
592
593#ifndef QT_NO_DEBUG_STREAM
594
595/*!
596 Logs an informational message using a QDebug stream.
597
598 \since 5.5
599 \sa qInfo(), QDebug
600*/
601QDebug QMessageLogger::info() const
602{
603 QDebug dbg = QDebug(QtInfoMsg);
604 QMessageLogContext &ctxt = dbg.stream->context;
605 ctxt.copyContextFrom(context);
606 return dbg;
607}
608
609/*!
610 Logs an informational message into the category \a cat using a QDebug stream.
611
612 \since 5.5
613 \sa qCInfo(), QDebug
614*/
615QDebug QMessageLogger::info(const QLoggingCategory &cat) const
616{
617 QDebug dbg = QDebug(QtInfoMsg);
618 if (!cat.isInfoEnabled())
619 dbg.stream->message_output = false;
620
621 QMessageLogContext &ctxt = dbg.stream->context;
622 ctxt.copyContextFrom(context);
623 ctxt.category = cat.categoryName();
624
625 return dbg;
626}
627
628/*!
629 Logs an informational message into category returned by \a catFunc using a QDebug stream.
630
631 \since 5.5
632 \sa qCInfo(), QDebug
633*/
634QDebug QMessageLogger::info(QMessageLogger::CategoryFunction catFunc) const
635{
636 return info((*catFunc)());
637}
638
639#endif
640
641/*!
642 Logs a warning message specified with format \a msg. Additional
643 parameters, specified by \a msg, may be used.
644
645 \sa qWarning()
646*/
647void QMessageLogger::warning(const char *msg, ...) const
648{
649 QInternalMessageLogContext ctxt(context);
650 va_list ap;
651 va_start(ap, msg); // use variable arg list
652 qt_message(QtWarningMsg, ctxt, msg, ap);
653 va_end(ap);
654}
655
656/*!
657 Logs a warning message specified with format \a msg for the context \a cat.
658 Additional parameters, specified by \a msg, may be used.
659
660 \since 5.3
661 \sa qCWarning()
662*/
663void QMessageLogger::warning(const QLoggingCategory &cat, const char *msg, ...) const
664{
665 if (!cat.isWarningEnabled())
666 return;
667
668 QInternalMessageLogContext ctxt(context, cat());
669
670 va_list ap;
671 va_start(ap, msg); // use variable arg list
672 qt_message(QtWarningMsg, ctxt, msg, ap);
673 va_end(ap);
674}
675
676/*!
677 Logs a warning message specified with format \a msg for the context returned
678 by \a catFunc. Additional parameters, specified by \a msg, may be used.
679
680 \since 5.3
681 \sa qCWarning()
682*/
683void QMessageLogger::warning(QMessageLogger::CategoryFunction catFunc,
684 const char *msg, ...) const
685{
686 const QLoggingCategory &cat = (*catFunc)();
687 if (!cat.isWarningEnabled())
688 return;
689
690 QInternalMessageLogContext ctxt(context, cat());
691
692 va_list ap;
693 va_start(ap, msg); // use variable arg list
694 qt_message(QtWarningMsg, ctxt, msg, ap);
695 va_end(ap);
696}
697
698#ifndef QT_NO_DEBUG_STREAM
699/*!
700 Logs a warning message using a QDebug stream
701
702 \sa qWarning(), QDebug
703*/
704QDebug QMessageLogger::warning() const
705{
706 QDebug dbg = QDebug(QtWarningMsg);
707 QMessageLogContext &ctxt = dbg.stream->context;
708 ctxt.copyContextFrom(context);
709 return dbg;
710}
711
712/*!
713 Logs a warning message into category \a cat using a QDebug stream.
714
715 \sa qCWarning(), QDebug
716*/
717QDebug QMessageLogger::warning(const QLoggingCategory &cat) const
718{
719 QDebug dbg = QDebug(QtWarningMsg);
720 if (!cat.isWarningEnabled())
721 dbg.stream->message_output = false;
722
723 QMessageLogContext &ctxt = dbg.stream->context;
724 ctxt.copyContextFrom(context);
725 ctxt.category = cat.categoryName();
726
727 return dbg;
728}
729
730/*!
731 Logs a warning message into category returned by \a catFunc using a QDebug stream.
732
733 \since 5.3
734 \sa qCWarning(), QDebug
735*/
736QDebug QMessageLogger::warning(QMessageLogger::CategoryFunction catFunc) const
737{
738 return warning((*catFunc)());
739}
740
741#endif
742
743/*!
744 Logs a critical message specified with format \a msg. Additional
745 parameters, specified by \a msg, may be used.
746
747 \sa qCritical()
748*/
749void QMessageLogger::critical(const char *msg, ...) const
750{
751 QInternalMessageLogContext ctxt(context);
752 va_list ap;
753 va_start(ap, msg); // use variable arg list
754 qt_message(QtCriticalMsg, ctxt, msg, ap);
755 va_end(ap);
756}
757
758/*!
759 Logs a critical message specified with format \a msg for the context \a cat.
760 Additional parameters, specified by \a msg, may be used.
761
762 \since 5.3
763 \sa qCCritical()
764*/
765void QMessageLogger::critical(const QLoggingCategory &cat, const char *msg, ...) const
766{
767 if (!cat.isCriticalEnabled())
768 return;
769
770 QInternalMessageLogContext ctxt(context, cat());
771
772 va_list ap;
773 va_start(ap, msg); // use variable arg list
774 qt_message(QtCriticalMsg, ctxt, msg, ap);
775 va_end(ap);
776}
777
778/*!
779 Logs a critical message specified with format \a msg for the context returned
780 by \a catFunc. Additional parameters, specified by \a msg, may be used.
781
782 \since 5.3
783 \sa qCCritical()
784*/
785void QMessageLogger::critical(QMessageLogger::CategoryFunction catFunc,
786 const char *msg, ...) const
787{
788 const QLoggingCategory &cat = (*catFunc)();
789 if (!cat.isCriticalEnabled())
790 return;
791
792 QInternalMessageLogContext ctxt(context, cat());
793
794 va_list ap;
795 va_start(ap, msg); // use variable arg list
796 qt_message(QtCriticalMsg, ctxt, msg, ap);
797 va_end(ap);
798}
799
800#ifndef QT_NO_DEBUG_STREAM
801/*!
802 Logs a critical message using a QDebug stream
803
804 \sa qCritical(), QDebug
805*/
806QDebug QMessageLogger::critical() const
807{
808 QDebug dbg = QDebug(QtCriticalMsg);
809 QMessageLogContext &ctxt = dbg.stream->context;
810 ctxt.copyContextFrom(context);
811 return dbg;
812}
813
814/*!
815 Logs a critical message into category \a cat using a QDebug stream.
816
817 \since 5.3
818 \sa qCCritical(), QDebug
819*/
820QDebug QMessageLogger::critical(const QLoggingCategory &cat) const
821{
822 QDebug dbg = QDebug(QtCriticalMsg);
823 if (!cat.isCriticalEnabled())
824 dbg.stream->message_output = false;
825
826 QMessageLogContext &ctxt = dbg.stream->context;
827 ctxt.copyContextFrom(context);
828 ctxt.category = cat.categoryName();
829
830 return dbg;
831}
832
833/*!
834 Logs a critical message into category returned by \a catFunc using a QDebug stream.
835
836 \since 5.3
837 \sa qCCritical(), QDebug
838*/
839QDebug QMessageLogger::critical(QMessageLogger::CategoryFunction catFunc) const
840{
841 return critical((*catFunc)());
842}
843
844#endif
845
846/*!
847 Logs a fatal message specified with format \a msg for the context \a cat.
848 Additional parameters, specified by \a msg, may be used.
849
850 \since 6.5
851 \sa qCFatal()
852*/
853void QMessageLogger::fatal(const QLoggingCategory &cat, const char *msg, ...) const noexcept
854{
855 QInternalMessageLogContext ctxt(context, cat());
856
857 va_list ap;
858 va_start(ap, msg); // use variable arg list
859 qt_message(QtFatalMsg, ctxt, msg, ap);
860 va_end(ap);
861
862#ifndef Q_CC_MSVC_ONLY
863 Q_UNREACHABLE();
864#endif
865}
866
867/*!
868 Logs a fatal message specified with format \a msg for the context returned
869 by \a catFunc. Additional parameters, specified by \a msg, may be used.
870
871 \since 6.5
872 \sa qCFatal()
873*/
874void QMessageLogger::fatal(QMessageLogger::CategoryFunction catFunc,
875 const char *msg, ...) const noexcept
876{
877 const QLoggingCategory &cat = (*catFunc)();
878
879 QInternalMessageLogContext ctxt(context, cat());
880
881 va_list ap;
882 va_start(ap, msg); // use variable arg list
883 qt_message(QtFatalMsg, ctxt, msg, ap);
884 va_end(ap);
885
886#ifndef Q_CC_MSVC_ONLY
887 Q_UNREACHABLE();
888#endif
889}
890
891/*!
892 Logs a fatal message specified with format \a msg. Additional
893 parameters, specified by \a msg, may be used.
894
895 \sa qFatal()
896*/
897void QMessageLogger::fatal(const char *msg, ...) const noexcept
898{
899 QInternalMessageLogContext ctxt(context);
900 va_list ap;
901 va_start(ap, msg); // use variable arg list
902 qt_message(QtFatalMsg, ctxt, msg, ap);
903 va_end(ap);
904
905#ifndef Q_CC_MSVC_ONLY
906 Q_UNREACHABLE();
907#endif
908}
909
910#ifndef QT_NO_DEBUG_STREAM
911/*!
912 Logs a fatal message using a QDebug stream.
913
914 \since 6.5
915
916 \sa qFatal(), QDebug
917*/
918QDebug QMessageLogger::fatal() const
919{
920 QDebug dbg = QDebug(QtFatalMsg);
921 QMessageLogContext &ctxt = dbg.stream->context;
922 ctxt.copyContextFrom(context);
923 return dbg;
924}
925
926/*!
927 Logs a fatal message into category \a cat using a QDebug stream.
928
929 \since 6.5
930 \sa qCFatal(), QDebug
931*/
932QDebug QMessageLogger::fatal(const QLoggingCategory &cat) const
933{
934 QDebug dbg = QDebug(QtFatalMsg);
935
936 QMessageLogContext &ctxt = dbg.stream->context;
937 ctxt.copyContextFrom(context);
938 ctxt.category = cat.categoryName();
939
940 return dbg;
941}
942
943/*!
944 Logs a fatal message into category returned by \a catFunc using a QDebug stream.
945
946 \since 6.5
947 \sa qCFatal(), QDebug
948*/
949QDebug QMessageLogger::fatal(QMessageLogger::CategoryFunction catFunc) const
950{
951 return fatal((*catFunc)());
952}
953#endif // QT_NO_DEBUG_STREAM
954
955static bool isDefaultCategory(const char *category)
956{
957 return !category || strcmp(category, QLoggingRegistry::defaultCategoryName) == 0;
958}
959
960/*!
961 \internal
962*/
963Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info)
964{
965 // Strip the function info down to the base function name
966 // note that this throws away the template definitions,
967 // the parameter types (overloads) and any const/volatile qualifiers.
968
969 if (info.isEmpty())
970 return info;
971
972 qsizetype pos;
973
974 // Skip trailing [with XXX] for templates (gcc), but make
975 // sure to not affect Objective-C message names.
976 pos = info.size() - 1;
977 if (info.endsWith(']') && !(info.startsWith('+') || info.startsWith('-'))) {
978 while (--pos) {
979 if (info.at(pos) == '[') {
980 info.truncate(pos);
981 break;
982 }
983 }
984 if (info.endsWith(' ')) {
985 info.chop(1);
986 }
987 }
988
989 // operator names with '(', ')', '<', '>' in it
990 static const char operator_call[] = "operator()";
991 static const char operator_lessThan[] = "operator<";
992 static const char operator_greaterThan[] = "operator>";
993 static const char operator_lessThanEqual[] = "operator<=";
994 static const char operator_greaterThanEqual[] = "operator>=";
995
996 // canonize operator names
997 info.replace("operator ", "operator");
998
999 pos = -1;
1000 // remove argument list
1001 forever {
1002 int parencount = 0;
1003 pos = info.lastIndexOf(')', pos);
1004 if (pos == -1) {
1005 // Don't know how to parse this function name
1006 return info;
1007 }
1008 if (info.indexOf('>', pos) != -1
1009 || info.indexOf(':', pos) != -1) {
1010 // that wasn't the function argument list.
1011 --pos;
1012 continue;
1013 }
1014
1015 // find the beginning of the argument list
1016 --pos;
1017 ++parencount;
1018 while (pos && parencount) {
1019 if (info.at(pos) == ')')
1020 ++parencount;
1021 else if (info.at(pos) == '(')
1022 --parencount;
1023 --pos;
1024 }
1025 if (parencount != 0)
1026 return info;
1027
1028 info.truncate(++pos);
1029
1030 if (info.at(pos - 1) == ')') {
1031 if (info.indexOf(operator_call) == pos - qsizetype(strlen(operator_call)))
1032 break;
1033
1034 // this function returns a pointer to a function
1035 // and we matched the arguments of the return type's parameter list
1036 // try again
1037 info.remove(0, info.indexOf('('));
1038 info.chop(1);
1039 continue;
1040 } else {
1041 break;
1042 }
1043 }
1044
1045 // find the beginning of the function name
1046 int parencount = 0;
1047 int templatecount = 0;
1048 --pos;
1049
1050 // make sure special characters in operator names are kept
1051 if (pos > -1) {
1052 switch (info.at(pos)) {
1053 case ')':
1054 if (info.indexOf(operator_call) == pos - qsizetype(strlen(operator_call)) + 1)
1055 pos -= 2;
1056 break;
1057 case '<':
1058 if (info.indexOf(operator_lessThan) == pos - qsizetype(strlen(operator_lessThan)) + 1)
1059 --pos;
1060 break;
1061 case '>':
1062 if (info.indexOf(operator_greaterThan) == pos - qsizetype(strlen(operator_greaterThan)) + 1)
1063 --pos;
1064 break;
1065 case '=': {
1066 auto operatorLength = qsizetype(strlen(operator_lessThanEqual));
1067 if (info.indexOf(operator_lessThanEqual) == pos - operatorLength + 1)
1068 pos -= 2;
1069 else if (info.indexOf(operator_greaterThanEqual) == pos - operatorLength + 1)
1070 pos -= 2;
1071 break;
1072 }
1073 default:
1074 break;
1075 }
1076 }
1077
1078 while (pos > -1) {
1079 if (parencount < 0 || templatecount < 0)
1080 return info;
1081
1082 char c = info.at(pos);
1083 if (c == ')')
1084 ++parencount;
1085 else if (c == '(')
1086 --parencount;
1087 else if (c == '>')
1088 ++templatecount;
1089 else if (c == '<')
1090 --templatecount;
1091 else if (c == ' ' && templatecount == 0 && parencount == 0)
1092 break;
1093
1094 --pos;
1095 }
1096 info = info.mid(pos + 1);
1097
1098 // remove trailing '*', '&' that are part of the return argument
1099 while ((info.at(0) == '*')
1100 || (info.at(0) == '&'))
1101 info = info.mid(1);
1102
1103 // we have the full function name now.
1104 // clean up the templates
1105 while ((pos = info.lastIndexOf('>')) != -1) {
1106 if (!info.contains('<'))
1107 break;
1108
1109 // find the matching close
1110 qsizetype end = pos;
1111 templatecount = 1;
1112 --pos;
1113 while (pos && templatecount) {
1114 char c = info.at(pos);
1115 if (c == '>')
1116 ++templatecount;
1117 else if (c == '<')
1118 --templatecount;
1119 --pos;
1120 }
1121 ++pos;
1122 info.remove(pos, end - pos + 1);
1123 }
1124
1125 return info;
1126}
1127
1128// tokens as recognized in QT_MESSAGE_PATTERN
1129static const char categoryTokenC[] = "%{category}";
1130static const char typeTokenC[] = "%{type}";
1131static const char messageTokenC[] = "%{message}";
1132static const char fileTokenC[] = "%{file}";
1133static const char lineTokenC[] = "%{line}";
1134static const char functionTokenC[] = "%{function}";
1135static const char pidTokenC[] = "%{pid}";
1136static const char appnameTokenC[] = "%{appname}";
1137static const char threadidTokenC[] = "%{threadid}";
1138static const char threadnameTokenC[] = "%{threadname}";
1139static const char qthreadptrTokenC[] = "%{qthreadptr}";
1140static const char timeTokenC[] = "%{time"; //not a typo: this command has arguments
1141static const char backtraceTokenC[] = "%{backtrace"; //ditto
1142static const char ifCategoryTokenC[] = "%{if-category}";
1143static const char ifDebugTokenC[] = "%{if-debug}";
1144static const char ifInfoTokenC[] = "%{if-info}";
1145static const char ifWarningTokenC[] = "%{if-warning}";
1146static const char ifCriticalTokenC[] = "%{if-critical}";
1147static const char ifFatalTokenC[] = "%{if-fatal}";
1148static const char endifTokenC[] = "%{endif}";
1149static const char emptyTokenC[] = "";
1150
1152{
1155
1156 void setPattern(const QString &pattern);
1158 {
1159 const char *const defaultTokens[] = {
1160#ifndef Q_OS_ANDROID
1161 // "%{if-category}%{category}: %{endif}%{message}"
1164 ": ", // won't point to literals[] but that's ok
1166#endif
1168 };
1169
1170 // we don't attempt to free the pointers, so only call from the ctor
1171 Q_ASSERT(!tokens);
1172 Q_ASSERT(!literals);
1173
1174 auto ptr = new const char *[std::size(defaultTokens) + 1];
1175 auto end = std::copy(std::begin(defaultTokens), std::end(defaultTokens), ptr);
1176 *end = nullptr;
1177 tokens.release();
1178 tokens.reset(ptr);
1179 }
1180
1181 // 0 terminated arrays of literal tokens / literal or placeholder tokens
1183 std::unique_ptr<const char *[]> tokens;
1184 QList<QString> timeArgs; // timeFormats in sequence of %{time
1185 std::chrono::steady_clock::time_point appStartTime = std::chrono::steady_clock::now();
1191#ifdef QLOGGING_HAVE_BACKTRACE
1192 QList<BacktraceParams> backtraceArgs; // backtrace arguments in sequence of %{backtrace
1193 int maxBacktraceDepth = 0;
1194#endif
1195
1198
1199#ifdef Q_OS_ANDROID
1200 bool containsToken(const char *token) const
1201 {
1202 for (int i = 0; tokens[i]; ++i) {
1203 if (tokens[i] == token)
1204 return true;
1205 }
1206
1207 return false;
1208 }
1209#endif
1210};
1212
1213Q_CONSTINIT QBasicMutex QMessagePattern::mutex;
1214
1216{
1217 const QString envPattern = qEnvironmentVariable("QT_MESSAGE_PATTERN");
1218 if (envPattern.isEmpty()) {
1220 fromEnvironment = false;
1221 } else {
1222 setPattern(envPattern);
1223 fromEnvironment = true;
1224 }
1225}
1226
1227QMessagePattern::~QMessagePattern() = default;
1228
1229void QMessagePattern::setPattern(const QString &pattern)
1230{
1231 timeArgs.clear();
1232#ifdef QLOGGING_HAVE_BACKTRACE
1233 backtraceArgs.clear();
1234 maxBacktraceDepth = 0;
1235#endif
1236
1237 // scanner
1238 QList<QString> lexemes;
1239 QString lexeme;
1240 bool inPlaceholder = false;
1241 for (int i = 0; i < pattern.size(); ++i) {
1242 const QChar c = pattern.at(i);
1243 if (c == u'%' && !inPlaceholder) {
1244 if ((i + 1 < pattern.size())
1245 && pattern.at(i + 1) == u'{') {
1246 // beginning of placeholder
1247 if (!lexeme.isEmpty()) {
1248 lexemes.append(lexeme);
1249 lexeme.clear();
1250 }
1251 inPlaceholder = true;
1252 }
1253 }
1254
1255 lexeme.append(c);
1256
1257 if (c == u'}' && inPlaceholder) {
1258 // end of placeholder
1259 lexemes.append(lexeme);
1260 lexeme.clear();
1261 inPlaceholder = false;
1262 }
1263 }
1264 if (!lexeme.isEmpty())
1265 lexemes.append(lexeme);
1266
1267 // tokenizer
1268 std::vector<std::unique_ptr<const char[]>> literalsVar;
1269 tokens.reset(new const char *[lexemes.size() + 1]);
1270 tokens[lexemes.size()] = nullptr;
1271
1272 bool nestedIfError = false;
1273 bool inIf = false;
1274 QString error;
1275
1276 for (int i = 0; i < lexemes.size(); ++i) {
1277 const QString lexeme = lexemes.at(i);
1278 if (lexeme.startsWith("%{"_L1) && lexeme.endsWith(u'}')) {
1279 // placeholder
1280 if (lexeme == QLatin1StringView(typeTokenC)) {
1281 tokens[i] = typeTokenC;
1282 } else if (lexeme == QLatin1StringView(categoryTokenC))
1283 tokens[i] = categoryTokenC;
1284 else if (lexeme == QLatin1StringView(messageTokenC))
1285 tokens[i] = messageTokenC;
1286 else if (lexeme == QLatin1StringView(fileTokenC))
1287 tokens[i] = fileTokenC;
1288 else if (lexeme == QLatin1StringView(lineTokenC))
1289 tokens[i] = lineTokenC;
1290 else if (lexeme == QLatin1StringView(functionTokenC))
1291 tokens[i] = functionTokenC;
1292 else if (lexeme == QLatin1StringView(pidTokenC))
1293 tokens[i] = pidTokenC;
1294 else if (lexeme == QLatin1StringView(appnameTokenC))
1295 tokens[i] = appnameTokenC;
1296 else if (lexeme == QLatin1StringView(threadidTokenC))
1297 tokens[i] = threadidTokenC;
1298 else if (lexeme == QLatin1StringView(threadnameTokenC))
1299 tokens[i] = threadnameTokenC;
1300 else if (lexeme == QLatin1StringView(qthreadptrTokenC))
1301 tokens[i] = qthreadptrTokenC;
1302 else if (lexeme.startsWith(QLatin1StringView(timeTokenC))) {
1303 tokens[i] = timeTokenC;
1304 qsizetype spaceIdx = lexeme.indexOf(QChar::fromLatin1(' '));
1305 if (spaceIdx > 0)
1306 timeArgs.append(lexeme.mid(spaceIdx + 1, lexeme.size() - spaceIdx - 2));
1307 else
1308 timeArgs.append(QString());
1309 } else if (lexeme.startsWith(QLatin1StringView(backtraceTokenC))) {
1310#ifdef QLOGGING_HAVE_BACKTRACE
1311 tokens[i] = backtraceTokenC;
1312 QString backtraceSeparator = QStringLiteral("|");
1313 int backtraceDepth = 5;
1314 static const QRegularExpression depthRx(QStringLiteral(" depth=(?|\"([^\"]*)\"|([^ }]*))"));
1315 static const QRegularExpression separatorRx(QStringLiteral(" separator=(?|\"([^\"]*)\"|([^ }]*))"));
1316 QRegularExpressionMatch m = depthRx.match(lexeme);
1317 if (m.hasMatch()) {
1318 int depth = m.capturedView(1).toInt();
1319 if (depth <= 0)
1320 error += "QT_MESSAGE_PATTERN: %{backtrace} depth must be a number greater than 0\n"_L1;
1321 else
1322 backtraceDepth = depth;
1323 }
1324 m = separatorRx.match(lexeme);
1325 if (m.hasMatch())
1326 backtraceSeparator = m.captured(1);
1327 BacktraceParams backtraceParams;
1328 backtraceParams.backtraceDepth = backtraceDepth;
1329 backtraceParams.backtraceSeparator = backtraceSeparator;
1330 backtraceArgs.append(backtraceParams);
1331 maxBacktraceDepth = qMax(maxBacktraceDepth, backtraceDepth);
1332#else
1333 error += "QT_MESSAGE_PATTERN: %{backtrace} is not supported by this Qt build\n"_L1;
1334 tokens[i] = "";
1335#endif
1336 }
1337
1338#define IF_TOKEN(LEVEL)
1339 else if (lexeme == QLatin1StringView(LEVEL)) {
1340 if (inIf)
1341 nestedIfError = true;
1342 tokens[i] = LEVEL;
1343 inIf = true;
1344 }
1345 IF_TOKEN(ifCategoryTokenC)
1346 IF_TOKEN(ifDebugTokenC)
1347 IF_TOKEN(ifInfoTokenC)
1348 IF_TOKEN(ifWarningTokenC)
1349 IF_TOKEN(ifCriticalTokenC)
1350 IF_TOKEN(ifFatalTokenC)
1351#undef IF_TOKEN
1352 else if (lexeme == QLatin1StringView(endifTokenC)) {
1353 tokens[i] = endifTokenC;
1354 if (!inIf && !nestedIfError)
1355 error += "QT_MESSAGE_PATTERN: %{endif} without an %{if-*}\n"_L1;
1356 inIf = false;
1357 } else {
1358 tokens[i] = emptyTokenC;
1359 error += "QT_MESSAGE_PATTERN: Unknown placeholder "_L1 + lexeme + '\n'_L1;
1360 }
1361 } else {
1362 using UP = std::unique_ptr<char[]>;
1363 tokens[i] = literalsVar.emplace_back(UP(qstrdup(lexeme.toLatin1().constData()))).get();
1364 }
1365 }
1366 if (nestedIfError)
1367 error += "QT_MESSAGE_PATTERN: %{if-*} cannot be nested\n"_L1;
1368 else if (inIf)
1369 error += "QT_MESSAGE_PATTERN: missing %{endif}\n"_L1;
1370
1371 if (!error.isEmpty()) {
1372 // remove the last '\n' because the sinks deal with that on their own
1373 error.chop(1);
1374
1376 "QMessagePattern::setPattern", nullptr);
1377 preformattedMessageHandler(QtWarningMsg, ctx, error);
1378 }
1379
1380 literals.reset(new std::unique_ptr<const char[]>[literalsVar.size() + 1]);
1381 std::move(literalsVar.begin(), literalsVar.end(), &literals[0]);
1382}
1383
1384#if defined(QLOGGING_HAVE_BACKTRACE)
1385// make sure the function has "Message" in the name so the function is removed
1386/*
1387 A typical backtrace in debug mode looks like:
1388 #0 QInternalMessageLogContext::populateBacktrace (this=0x7fffffffd660, frameCount=5) at qlogging.cpp:1342
1389 #1 QInternalMessageLogContext::QInternalMessageLogContext (logContext=..., this=<optimized out>) at qlogging_p.h:42
1390 #2 QDebug::~QDebug (this=0x7fffffffdac8, __in_chrg=<optimized out>) at qdebug.cpp:160
1391
1392 In release mode, the QInternalMessageLogContext constructor will be usually
1393 inlined. Empirical testing with GCC 13 and Clang 17 suggest they do obey the
1394 Q_ALWAYS_INLINE in that constructor even in debug mode and do inline it.
1395 Unfortunately, we can't know for sure if it has been.
1396*/
1397static constexpr int TypicalBacktraceFrameCount = 3;
1398static constexpr const char *QtCoreLibraryName = "Qt" QT_STRINGIFY(QT_VERSION_MAJOR) "Core";
1399
1400#if defined(QLOGGING_USE_STD_BACKTRACE)
1401Q_NEVER_INLINE void QInternalMessageLogContext::populateBacktrace(int frameCount)
1402{
1403 assert(frameCount >= 0);
1404 backtrace = std::stacktrace::current(0, TypicalBacktraceFrameCount + frameCount);
1405}
1406
1407static QStringList
1408backtraceFramesForLogMessage(int frameCount,
1409 const QInternalMessageLogContext::BacktraceStorage &buffer)
1410{
1411 QStringList result;
1412 result.reserve(buffer.size());
1413
1414 const auto shouldSkipFrame = [](QByteArrayView description)
1415 {
1416#if defined(_MSVC_STL_VERSION)
1417 const auto libraryNameEnd = description.indexOf('!');
1418 if (libraryNameEnd != -1) {
1419 const auto libraryName = description.first(libraryNameEnd);
1420 if (!libraryName.contains(QtCoreLibraryName))
1421 return false;
1422 }
1423#endif
1424 if (description.contains("populateBacktrace"))
1425 return true;
1426 if (description.contains("QInternalMessageLogContext"))
1427 return true;
1428 if (description.contains("~QDebug"))
1429 return true;
1430 return false;
1431 };
1432
1433 for (const auto &entry : buffer) {
1434 const std::string description = entry.description();
1435 if (result.isEmpty() && shouldSkipFrame(description))
1436 continue;
1437 result.append(QString::fromStdString(description));
1438 }
1439
1440 return result;
1441}
1442
1443#elif defined(QLOGGING_USE_EXECINFO_BACKTRACE)
1444
1445Q_NEVER_INLINE void QInternalMessageLogContext::populateBacktrace(int frameCount)
1446{
1447 assert(frameCount >= 0);
1448 BacktraceStorage &result = backtrace.emplace(TypicalBacktraceFrameCount + frameCount);
1449 Q_ASSERT(result.size() == int(result.size()));
1450 int n = ::backtrace(result.data(), int(result.size()));
1451 if (n <= 0)
1452 result.clear();
1453 else
1454 result.resize(n);
1455}
1456
1457static QStringList
1458backtraceFramesForLogMessage(int frameCount,
1459 const QInternalMessageLogContext::BacktraceStorage &buffer)
1460{
1461 struct DecodedFrame {
1462 QString library;
1463 QString function;
1464 };
1465
1466 QStringList result;
1467 if (frameCount == 0)
1468 return result;
1469
1470 auto shouldSkipFrame = [&result](const auto &library, const auto &function) {
1471 if (!result.isEmpty() || !library.contains(QLatin1StringView(QtCoreLibraryName)))
1472 return false;
1473 if (function.isEmpty())
1474 return true;
1475 if (function.contains("6QDebug"_L1))
1476 return true;
1477 if (function.contains("14QMessageLogger"_L1))
1478 return true;
1479 if (function.contains("17qt_message_output"_L1))
1480 return true;
1481 if (function.contains("26QInternalMessageLogContext"_L1))
1482 return true;
1483 return false;
1484 };
1485
1486 auto demangled = [](auto &function) -> QString {
1487 if (!function.startsWith("_Z"_L1))
1488 return function;
1489
1490 // we optimize for the case where __cxa_demangle succeeds
1491 auto fn = [&]() {
1492 if constexpr (sizeof(function.at(0)) == 1)
1493 return function.data(); // -> const char *
1494 else
1495 return std::move(function).toUtf8(); // -> QByteArray
1496 }();
1497 auto cleanup = [](auto *p) { free(p); };
1498 using Ptr = std::unique_ptr<char, decltype(cleanup)>;
1499 auto demangled = Ptr(abi::__cxa_demangle(fn, nullptr, nullptr, nullptr), cleanup);
1500
1501 if (demangled)
1502 return QString::fromUtf8(qCleanupFuncinfo(demangled.get()));
1503 else
1504 return QString::fromUtf8(fn); // restore
1505 };
1506
1507# if QT_CONFIG(dladdr)
1508 // use dladdr() instead of backtrace_symbols()
1509 QString cachedLibrary;
1510 const char *cachedFname = nullptr;
1511 auto decodeFrame = [&](void *addr) -> DecodedFrame {
1512 Dl_info info;
1513 if (!dladdr(addr, &info))
1514 return {};
1515
1516 // These are actually UTF-8, so we'll correct below
1517 QLatin1StringView fn(info.dli_sname);
1518 QLatin1StringView lib;
1519 if (const char *lastSlash = strrchr(info.dli_fname, '/'))
1520 lib = QLatin1StringView(lastSlash + 1);
1521 else
1522 lib = QLatin1StringView(info.dli_fname);
1523
1524 if (shouldSkipFrame(lib, fn))
1525 return {};
1526
1527 QString function = demangled(fn);
1528 if (lib.data() != cachedFname) {
1529 cachedFname = lib.data();
1530 cachedLibrary = QString::fromUtf8(cachedFname, lib.size());
1531 }
1532 return { cachedLibrary, function };
1533 };
1534# else
1535 // The results of backtrace_symbols looks like this:
1536 // /lib/libc.so.6(__libc_start_main+0xf3) [0x4a937413]
1537 // The offset and function name are optional.
1538 // This regexp tries to extract the library name (without the path) and the function name.
1539 // This code is protected by QMessagePattern::mutex so it is thread safe on all compilers
1540 static const QRegularExpression rx(QStringLiteral("^(?:[^(]*/)?([^(/]+)\\‍(([^+]*)(?:[\\+[a-f0-9x]*)?\\‍) \\‍[[a-f0-9x]*\\‍]$"));
1541
1542 auto decodeFrame = [&](void *&addr) -> DecodedFrame {
1543 auto cleanup = [](auto *p) { free(p); };
1544 auto strings =
1545 std::unique_ptr<char *, decltype(cleanup)>(backtrace_symbols(&addr, 1), cleanup);
1546 QString trace = QString::fromUtf8(strings.get()[0]);
1547 QRegularExpressionMatch m = rx.match(trace);
1548 if (!m.hasMatch())
1549 return {};
1550
1551 QString library = m.captured(1);
1552 QString function = m.captured(2);
1553
1554 // skip the trace from QtCore that are because of the qDebug itself
1555 if (shouldSkipFrame(library, function))
1556 return {};
1557
1558 function = demangled(function);
1559 return { library, function };
1560 };
1561# endif
1562
1563 for (void *const &addr : buffer) {
1564 DecodedFrame frame = decodeFrame(addr);
1565 if (!frame.library.isEmpty()) {
1566 if (frame.function.isEmpty())
1567 result.append(u'?' + frame.library + u'?');
1568 else
1569 result.append(frame.function);
1570 } else {
1571 // innermost, unknown frames are usually the logging framework itself
1572 if (!result.isEmpty())
1573 result.append(QStringLiteral("???"));
1574 }
1575
1576 if (result.size() == frameCount)
1577 break;
1578 }
1579 return result;
1580}
1581#else
1582#error "Internal error: backtrace enabled, but no way to gather backtraces available"
1583#endif // QLOGGING_USE_..._BACKTRACE
1584
1585static QString formatBacktraceForLogMessage(const QMessagePattern::BacktraceParams backtraceParams,
1586 const QMessageLogContext &ctx)
1587{
1588 // do we have a backtrace stored?
1589 if (ctx.version <= QMessageLogContext::CurrentVersion)
1590 return QString();
1591
1592 auto &fullctx = static_cast<const QInternalMessageLogContext &>(ctx);
1593 if (!fullctx.backtrace.has_value())
1594 return QString();
1595
1596 QString backtraceSeparator = backtraceParams.backtraceSeparator;
1597 int backtraceDepth = backtraceParams.backtraceDepth;
1598
1599 QStringList frames = backtraceFramesForLogMessage(backtraceDepth, *fullctx.backtrace);
1600 if (frames.isEmpty())
1601 return QString();
1602
1603 // if the first frame is unknown, replace it with the context function
1604 if (ctx.function && frames.at(0).startsWith(u'?'))
1605 frames[0] = QString::fromUtf8(qCleanupFuncinfo(ctx.function));
1606
1607 return frames.join(backtraceSeparator);
1608}
1609#else
1611{
1612 // initFrom() returns 0 to our caller, so we should never get here
1613 Q_UNREACHABLE();
1614}
1615#endif // !QLOGGING_HAVE_BACKTRACE
1616
1617Q_GLOBAL_STATIC(QMessagePattern, qMessagePattern)
1618
1619/*!
1620 \relates <QtLogging>
1621 \since 5.4
1622
1623 Generates a formatted string out of the \a type, \a context, \a str arguments.
1624
1625 qFormatLogMessage returns a QString that is formatted according to the current message pattern.
1626 It can be used by custom message handlers to format output similar to Qt's default message
1627 handler.
1628
1629 The function is thread-safe.
1630
1631 \sa qInstallMessageHandler(), qSetMessagePattern()
1632 */
1633QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &str)
1634{
1635 return formatLogMessage(type, context, str);
1636}
1637
1638// Separate function so the default message handler can bypass the public,
1639// exported function above. Static functions can't get added to the dynamic
1640// symbol tables, so they never show up in backtrace_symbols() or equivalent.
1641static QString formatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &str)
1642{
1643 QString message;
1644
1645 const auto locker = qt_scoped_lock(QMessagePattern::mutex);
1646
1647 QMessagePattern *pattern = qMessagePattern();
1648 if (!pattern) {
1649 // after destruction of static QMessagePattern instance
1650 message.append(str);
1651 return message;
1652 }
1653
1654 bool skip = false;
1655
1656 int timeArgsIdx = 0;
1657#ifdef QLOGGING_HAVE_BACKTRACE
1658 int backtraceArgsIdx = 0;
1659#endif
1660
1661 // we do not convert file, function, line literals to local encoding due to overhead
1662 for (int i = 0; pattern->tokens[i]; ++i) {
1663 const char *token = pattern->tokens[i];
1664 if (token == endifTokenC) {
1665 skip = false;
1666 } else if (skip) {
1667 // we skip adding messages, but we have to iterate over
1668 // timeArgsIdx and backtraceArgsIdx anyway
1669 if (token == timeTokenC)
1670 timeArgsIdx++;
1671#ifdef QLOGGING_HAVE_BACKTRACE
1672 else if (token == backtraceTokenC)
1673 backtraceArgsIdx++;
1674#endif
1675 } else if (token == messageTokenC) {
1676 message.append(str);
1677 } else if (token == categoryTokenC) {
1678 message.append(QLatin1StringView(context.category));
1679 } else if (token == typeTokenC) {
1680 switch (type) {
1681 case QtDebugMsg: message.append("debug"_L1); break;
1682 case QtInfoMsg: message.append("info"_L1); break;
1683 case QtWarningMsg: message.append("warning"_L1); break;
1684 case QtCriticalMsg:message.append("critical"_L1); break;
1685 case QtFatalMsg: message.append("fatal"_L1); break;
1686 }
1687 } else if (token == fileTokenC) {
1688 if (context.file)
1689 message.append(QLatin1StringView(context.file));
1690 else
1691 message.append("unknown"_L1);
1692 } else if (token == lineTokenC) {
1693 message.append(QString::number(context.line));
1694 } else if (token == functionTokenC) {
1695 if (context.function)
1696 message.append(QString::fromLatin1(qCleanupFuncinfo(context.function)));
1697 else
1698 message.append("unknown"_L1);
1699 } else if (token == pidTokenC) {
1700 message.append(QString::number(QCoreApplication::applicationPid()));
1701 } else if (token == appnameTokenC) {
1702 message.append(QCoreApplication::applicationName());
1703 } else if (token == threadidTokenC) {
1704 // print the TID as decimal
1705 message.append(QString::number(qt_gettid()));
1706 } else if (token == threadnameTokenC) {
1707 if (!qt_append_thread_name_to(message))
1708 message.append(QString::number(qt_gettid())); // fallback to the TID
1709 } else if (token == qthreadptrTokenC) {
1710 message.append("0x"_L1);
1711 message.append(QString::number(qlonglong(QThread::currentThread()->currentThread()), 16));
1712#ifdef QLOGGING_HAVE_BACKTRACE
1713 } else if (token == backtraceTokenC) {
1714 QMessagePattern::BacktraceParams backtraceParams = pattern->backtraceArgs.at(backtraceArgsIdx);
1715 backtraceArgsIdx++;
1716 message.append(formatBacktraceForLogMessage(backtraceParams, context));
1717#endif
1718 } else if (token == timeTokenC) {
1719 using namespace std::chrono;
1720 auto formatElapsedTime = [](steady_clock::duration time) {
1721 // we assume time > 0
1722 auto ms = duration_cast<milliseconds>(time);
1723 auto sec = duration_cast<seconds>(ms);
1724 ms -= sec;
1725 return QString::asprintf("%6lld.%03u", qint64(sec.count()), uint(ms.count()));
1726 };
1727 QString timeFormat = pattern->timeArgs.at(timeArgsIdx);
1728 timeArgsIdx++;
1729 if (timeFormat == "process"_L1) {
1730 message += formatElapsedTime(steady_clock::now() - pattern->appStartTime);
1731 } else if (timeFormat == "boot"_L1) {
1732 // just print the milliseconds since the elapsed timer reference
1733 // like the Linux kernel does
1734 message += formatElapsedTime(steady_clock::now().time_since_epoch());
1735#if QT_CONFIG(datestring)
1736 } else if (timeFormat.isEmpty()) {
1737 message.append(QDateTime::currentDateTime().toString(Qt::ISODate));
1738 } else {
1739 message.append(QDateTime::currentDateTime().toString(timeFormat));
1740#endif // QT_CONFIG(datestring)
1741 }
1742 } else if (token == ifCategoryTokenC) {
1744 skip = true;
1745#define HANDLE_IF_TOKEN(LEVEL)
1746 } else if (token == if##LEVEL##TokenC) {
1747 skip = type != Qt##LEVEL##Msg;
1748 HANDLE_IF_TOKEN(Debug)
1749 HANDLE_IF_TOKEN(Info)
1750 HANDLE_IF_TOKEN(Warning)
1751 HANDLE_IF_TOKEN(Critical)
1752 HANDLE_IF_TOKEN(Fatal)
1753#undef HANDLE_IF_TOKEN
1754 } else {
1755 message.append(QLatin1StringView(token));
1756 }
1757 }
1758 return message;
1759}
1760
1761static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &buf);
1762
1763// pointer to QtMessageHandler debug handler (with context)
1764Q_CONSTINIT static QBasicAtomicPointer<void (QtMsgType, const QMessageLogContext &, const QString &)> messageHandler = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
1765
1766// ------------------------ Alternate logging sinks -------------------------
1767
1768#if QT_CONFIG(slog2)
1769#ifndef QT_LOG_CODE
1770#define QT_LOG_CODE 9000
1771#endif
1772
1773static bool slog2_default_handler(QtMsgType type, const QMessageLogContext &,
1774 const QString &message)
1775{
1776 if (shouldLogToStderr())
1777 return false; // Leave logging up to stderr handler
1778
1779 QString formattedMessage = message;
1780 formattedMessage.append(u'\n');
1781 if (slog2_set_default_buffer((slog2_buffer_t)-1) == 0) {
1782 slog2_buffer_set_config_t buffer_config;
1783 slog2_buffer_t buffer_handle;
1784
1785 buffer_config.buffer_set_name = __progname;
1786 buffer_config.num_buffers = 1;
1787 buffer_config.verbosity_level = SLOG2_DEBUG1;
1788 buffer_config.buffer_config[0].buffer_name = "default";
1789 buffer_config.buffer_config[0].num_pages = 8;
1790
1791 if (slog2_register(&buffer_config, &buffer_handle, 0) == -1) {
1792 fprintf(stderr, "Error registering slogger2 buffer!\n");
1793 fprintf(stderr, "%s", formattedMessage.toLocal8Bit().constData());
1794 fflush(stderr);
1795 return false;
1796 }
1797
1798 // Set as the default buffer
1799 slog2_set_default_buffer(buffer_handle);
1800 }
1801 int severity = SLOG2_INFO;
1802 //Determines the severity level
1803 switch (type) {
1804 case QtDebugMsg:
1805 severity = SLOG2_DEBUG1;
1806 break;
1807 case QtInfoMsg:
1808 severity = SLOG2_INFO;
1809 break;
1810 case QtWarningMsg:
1811 severity = SLOG2_NOTICE;
1812 break;
1813 case QtCriticalMsg:
1814 severity = SLOG2_WARNING;
1815 break;
1816 case QtFatalMsg:
1817 severity = SLOG2_ERROR;
1818 break;
1819 }
1820 //writes to the slog2 buffer
1821 slog2c(NULL, QT_LOG_CODE, severity, formattedMessage.toLocal8Bit().constData());
1822
1823 return true; // Prevent further output to stderr
1824}
1825#endif // slog2
1826
1827#if QT_CONFIG(journald)
1828static bool systemd_default_message_handler(QtMsgType type,
1829 const QMessageLogContext &context,
1830 const QString &message)
1831{
1832 if (shouldLogToStderr())
1833 return false; // Leave logging up to stderr handler
1834
1835 int priority = LOG_INFO; // Informational
1836 switch (type) {
1837 case QtDebugMsg:
1838 priority = LOG_DEBUG; // Debug-level messages
1839 break;
1840 case QtInfoMsg:
1841 priority = LOG_INFO; // Informational conditions
1842 break;
1843 case QtWarningMsg:
1844 priority = LOG_WARNING; // Warning conditions
1845 break;
1846 case QtCriticalMsg:
1847 priority = LOG_CRIT; // Critical conditions
1848 break;
1849 case QtFatalMsg:
1850 priority = LOG_ALERT; // Action must be taken immediately
1851 break;
1852 }
1853
1854 // Explicit QByteArray instead of auto, to resolve the QStringBuilder proxy
1855 const QByteArray messageField = "MESSAGE="_ba + message.toUtf8().constData();
1856 const QByteArray priorityField = "PRIORITY="_ba + QByteArray::number(priority);
1857 const QByteArray tidField = "TID="_ba + QByteArray::number(qlonglong(qt_gettid()));
1858 const QByteArray fileField = context.file
1859 ? "CODE_FILE="_ba + context.file : QByteArray();
1860 const QByteArray funcField = context.function
1861 ? "CODE_FUNC="_ba + context.function : QByteArray();
1862 const QByteArray lineField = context.line
1863 ? "CODE_LINE="_ba + QByteArray::number(context.line) : QByteArray();
1864 const QByteArray categoryField = context.category
1865 ? "QT_CATEGORY="_ba + context.category : QByteArray();
1866
1867 auto toIovec = [](const QByteArray &ba) {
1868 return iovec{ const_cast<char*>(ba.data()), size_t(ba.size()) };
1869 };
1870
1871 struct iovec fields[7] = {
1872 toIovec(messageField),
1873 toIovec(priorityField),
1874 toIovec(tidField),
1875 };
1876 int nFields = 3;
1877 if (context.file)
1878 fields[nFields++] = toIovec(fileField);
1879 if (context.function)
1880 fields[nFields++] = toIovec(funcField);
1881 if (context.line)
1882 fields[nFields++] = toIovec(lineField);
1883 if (context.category)
1884 fields[nFields++] = toIovec(categoryField);
1885
1886 sd_journal_sendv(fields, nFields);
1887
1888 return true; // Prevent further output to stderr
1889}
1890#endif
1891
1892#if QT_CONFIG(syslog)
1893static bool syslog_default_message_handler(QtMsgType type, const QMessageLogContext &context,
1894 const QString &formattedMessage)
1895{
1896 if (shouldLogToStderr())
1897 return false; // Leave logging up to stderr handler
1898
1899 int priority = LOG_INFO; // Informational
1900 switch (type) {
1901 case QtDebugMsg:
1902 priority = LOG_DEBUG; // Debug-level messages
1903 break;
1904 case QtInfoMsg:
1905 priority = LOG_INFO; // Informational conditions
1906 break;
1907 case QtWarningMsg:
1908 priority = LOG_WARNING; // Warning conditions
1909 break;
1910 case QtCriticalMsg:
1911 priority = LOG_CRIT; // Critical conditions
1912 break;
1913 case QtFatalMsg:
1914 priority = LOG_ALERT; // Action must be taken immediately
1915 break;
1916 }
1917
1918 syslog(priority, "%s", formattedMessage.toUtf8().constData());
1919
1920 return true; // Prevent further output to stderr
1921}
1922#endif
1923
1924#ifdef Q_OS_ANDROID
1925static bool android_default_message_handler(QtMsgType type,
1926 const QMessageLogContext &context,
1927 const QString &formattedMessage)
1928{
1929 if (shouldLogToStderr())
1930 return false; // Leave logging up to stderr handler
1931
1932 android_LogPriority priority = ANDROID_LOG_DEBUG;
1933 switch (type) {
1934 case QtDebugMsg:
1935 priority = ANDROID_LOG_DEBUG;
1936 break;
1937 case QtInfoMsg:
1938 priority = ANDROID_LOG_INFO;
1939 break;
1940 case QtWarningMsg:
1941 priority = ANDROID_LOG_WARN;
1942 break;
1943 case QtCriticalMsg:
1944 priority = ANDROID_LOG_ERROR;
1945 break;
1946 case QtFatalMsg:
1947 priority = ANDROID_LOG_FATAL;
1948 break;
1949 };
1950
1951 QMessagePattern *pattern = qMessagePattern();
1952 const QString tag = (pattern && pattern->containsToken(categoryTokenC))
1953 // If application name is a tag ensure it has no spaces
1954 ? QCoreApplication::applicationName().replace(u' ', u'_')
1955 : QString::fromUtf8(context.category);
1956 __android_log_print(priority, qPrintable(tag), "%s\n", qPrintable(formattedMessage));
1957
1958 return true; // Prevent further output to stderr
1959}
1960#endif //Q_OS_ANDROID
1961
1962#ifdef Q_OS_WIN
1963static void win_outputDebugString_helper(const QString &message)
1964{
1965 const qsizetype maxOutputStringLength = 32766;
1966 Q_CONSTINIT static QBasicMutex m;
1967 auto locker = qt_unique_lock(m);
1968 // fast path: Avoid string copies if one output is enough
1969 if (message.length() <= maxOutputStringLength) {
1970 OutputDebugString(reinterpret_cast<const wchar_t *>(message.utf16()));
1971 } else {
1972 wchar_t messagePart[maxOutputStringLength + 1];
1973 for (qsizetype i = 0; i < message.length(); i += maxOutputStringLength) {
1974 const qsizetype length = qMin(message.length() - i, maxOutputStringLength);
1975 const qsizetype len = QStringView{message}.mid(i, length).toWCharArray(messagePart);
1976 Q_ASSERT(len == length);
1977 messagePart[len] = 0;
1978 OutputDebugString(messagePart);
1979 }
1980 }
1981}
1982
1983static bool win_message_handler(QtMsgType, const QMessageLogContext &,
1984 const QString &formattedMessage)
1985{
1986 if (shouldLogToStderr())
1987 return false; // Leave logging up to stderr handler
1988
1989 win_outputDebugString_helper(formattedMessage + u'\n');
1990
1991 return true; // Prevent further output to stderr
1992}
1993#endif
1994
1995#ifdef Q_OS_WASM
1996static bool wasm_default_message_handler(QtMsgType type,
1997 const QMessageLogContext &,
1998 const QString &formattedMessage)
1999{
2000 static bool forceStderrLogging = qEnvironmentVariableIntValue("QT_FORCE_STDERR_LOGGING");
2001 if (forceStderrLogging)
2002 return false;
2003
2004 int emOutputFlags = EM_LOG_CONSOLE;
2005 QByteArray localMsg = formattedMessage.toLocal8Bit();
2006 switch (type) {
2007 case QtDebugMsg:
2008 break;
2009 case QtInfoMsg:
2010 break;
2011 case QtWarningMsg:
2012 emOutputFlags |= EM_LOG_WARN;
2013 break;
2014 case QtCriticalMsg:
2015 emOutputFlags |= EM_LOG_ERROR;
2016 break;
2017 case QtFatalMsg:
2018 emOutputFlags |= EM_LOG_ERROR;
2019 }
2020 emscripten_log(emOutputFlags, "%s\n", qPrintable(formattedMessage));
2021
2022 return true; // Prevent further output to stderr
2023}
2024#endif
2025
2026// --------------------------------------------------------------------------
2027
2028static void stderr_message_handler(QtMsgType type, const QMessageLogContext &context,
2029 const QString &formattedMessage)
2030{
2031 Q_UNUSED(type);
2032 Q_UNUSED(context);
2033
2034 // print nothing if message pattern didn't apply / was empty.
2035 // (still print empty lines, e.g. because message itself was empty)
2036 if (formattedMessage.isNull())
2037 return;
2038 fprintf(stderr, "%s\n", formattedMessage.toLocal8Bit().constData());
2039 fflush(stderr);
2040}
2041
2042namespace {
2043struct SystemMessageSink
2044{
2045 using Fn = bool(QtMsgType, const QMessageLogContext &, const QString &);
2046 Fn *sink;
2047 bool messageIsUnformatted = false;
2048};
2049}
2050
2051static constexpr SystemMessageSink systemMessageSink = {
2052#if defined(Q_OS_WIN)
2053 win_message_handler
2054#elif QT_CONFIG(slog2)
2055 slog2_default_handler
2056#elif QT_CONFIG(journald)
2057 systemd_default_message_handler, true
2058#elif QT_CONFIG(syslog)
2059 syslog_default_message_handler
2060#elif defined(Q_OS_ANDROID)
2061 android_default_message_handler
2062#elif defined(QT_USE_APPLE_UNIFIED_LOGGING)
2063 AppleUnifiedLogger::messageHandler, true
2064#elif defined Q_OS_WASM
2065 wasm_default_message_handler
2066#else
2067 nullptr
2068#endif
2069};
2070
2072 const QString &formattedMessage)
2073{
2074QT_WARNING_PUSH
2075QT_WARNING_DISABLE_GCC("-Waddress") // "the address of ~~ will never be NULL
2076 if (systemMessageSink.sink && systemMessageSink.sink(type, context, formattedMessage))
2077 return;
2078QT_WARNING_POP
2079
2080 stderr_message_handler(type, context, formattedMessage);
2081}
2082
2083/*!
2084 \internal
2085*/
2086static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context,
2087 const QString &message)
2088{
2089 // A message sink logs the message to a structured or unstructured destination,
2090 // optionally formatting the message if the latter, and returns true if the sink
2091 // handled stderr output as well, which will shortcut our default stderr output.
2092
2093 if (systemMessageSink.messageIsUnformatted) {
2094 if (systemMessageSink.sink(type, context, message))
2095 return;
2096 }
2097
2098 preformattedMessageHandler(type, context, formatLogMessage(type, context, message));
2099}
2100
2101Q_CONSTINIT static thread_local bool msgHandlerGrabbed = false;
2102
2104{
2106 return false;
2107
2108 msgHandlerGrabbed = true;
2109 return true;
2110}
2111
2113{
2114 msgHandlerGrabbed = false;
2115}
2116
2117static void qt_message_print(QtMsgType msgType, const QMessageLogContext &context, const QString &message)
2118{
2119 Q_TRACE(qt_message_print, msgType, context.category, context.function, context.file, context.line, message);
2120
2121 // qDebug, qWarning, ... macros do not check whether category is enabledgc
2122 if (msgType != QtFatalMsg && isDefaultCategory(context.category)) {
2123 if (QLoggingCategory *defaultCategory = QLoggingCategory::defaultCategory()) {
2124 if (!defaultCategory->isEnabled(msgType))
2125 return;
2126 }
2127 }
2128
2129 // prevent recursion in case the message handler generates messages
2130 // itself, e.g. by using Qt API
2132 const auto ungrab = qScopeGuard([]{ ungrabMessageHandler(); });
2133 auto msgHandler = messageHandler.loadAcquire();
2134 (msgHandler ? msgHandler : qDefaultMessageHandler)(msgType, context, message);
2135 } else {
2136 stderr_message_handler(msgType, context, message);
2137 }
2138}
2139
2140template <typename String> static void
2141qt_maybe_message_fatal(QtMsgType msgType, const QMessageLogContext &context, String &&message)
2142{
2143 if (!isFatal(msgType))
2144 return;
2145#if defined(Q_CC_MSVC_ONLY) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR)
2146 wchar_t contextFileL[256];
2147 // we probably should let the compiler do this for us, by declaring QMessageLogContext::file to
2148 // be const wchar_t * in the first place, but the #ifdefery above is very complex and we
2149 // wouldn't be able to change it later on...
2150 convert_to_wchar_t_elided(contextFileL, sizeof contextFileL / sizeof *contextFileL,
2151 context.file);
2152 // get the current report mode
2153 int reportMode = _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW);
2154 _CrtSetReportMode(_CRT_ERROR, reportMode);
2155
2156 int ret = _CrtDbgReportW(_CRT_ERROR, contextFileL, context.line, _CRT_WIDE(QT_VERSION_STR),
2157 reinterpret_cast<const wchar_t *>(message.utf16()));
2158 if ((ret == 0) && (reportMode & _CRTDBG_MODE_WNDW))
2159 return; // ignore
2160 else if (ret == 1)
2161 _CrtDbgBreak();
2162#else
2163 Q_UNUSED(context);
2164#endif
2165
2166 if constexpr (std::is_class_v<String> && !std::is_const_v<String>)
2167 message.clear();
2168 else
2169 Q_UNUSED(message);
2170 qAbort();
2171}
2172
2173/*!
2174 \internal
2175*/
2176void qt_message_output(QtMsgType msgType, const QMessageLogContext &context, const QString &message)
2177{
2178 QInternalMessageLogContext ctx(context);
2179 qt_message_print(msgType, ctx, message);
2180 qt_maybe_message_fatal(msgType, ctx, message);
2181}
2182
2183void qErrnoWarning(const char *msg, ...)
2184{
2185 // qt_error_string() will allocate anyway, so we don't have
2186 // to be careful here (like we do in plain qWarning())
2187 QString error_string = qt_error_string(-1); // before vasprintf changes errno/GetLastError()
2188
2189 va_list ap;
2190 va_start(ap, msg);
2191 QString buf = QString::vasprintf(msg, ap);
2192 va_end(ap);
2193
2194 buf += " ("_L1 + error_string + u')';
2196 qt_message_output(QtWarningMsg, context, buf);
2197}
2198
2199void qErrnoWarning(int code, const char *msg, ...)
2200{
2201 // qt_error_string() will allocate anyway, so we don't have
2202 // to be careful here (like we do in plain qWarning())
2203 va_list ap;
2204 va_start(ap, msg);
2205 QString buf = QString::vasprintf(msg, ap);
2206 va_end(ap);
2207
2208 buf += " ("_L1 + qt_error_string(code) + u')';
2210 qt_message_output(QtWarningMsg, context, buf);
2211}
2212
2213/*!
2214 \typedef QtMessageHandler
2215 \relates <QtLogging>
2216 \since 5.0
2217
2218 This is a typedef for a pointer to a function with the following
2219 signature:
2220
2221 \snippet code/src_corelib_global_qglobal.cpp 49
2222
2223 \sa QtMsgType, qInstallMessageHandler()
2224*/
2225
2226/*!
2227 \fn QtMessageHandler qInstallMessageHandler(QtMessageHandler handler)
2228 \relates <QtLogging>
2229 \since 5.0
2230
2231 Installs a Qt message \a handler.
2232 Returns a pointer to the previously installed message handler.
2233
2234 A message handler is a function that prints out debug, info,
2235 warning, critical, and fatal messages from Qt's logging infrastructure.
2236 By default, Qt uses a standard message handler that formats and
2237 prints messages to different sinks specific to the operating system
2238 and Qt configuration. Installing your own message handler allows you
2239 to assume full control, and for instance log messages to the
2240 file system.
2241
2242 Note that Qt supports \l{QLoggingCategory}{logging categories} for
2243 grouping related messages in semantic categories. You can use these
2244 to enable or disable logging per category and \l{QtMsgType}{message type}.
2245 As the filtering for logging categories is done even before a message
2246 is created, messages for disabled types and categories will not reach
2247 the message handler.
2248
2249 A message handler needs to be
2250 \l{Reentrancy and Thread-Safety}{reentrant}. That is, it might be called
2251 from different threads, in parallel. Therefore, writes to common sinks
2252 (like a database, or a file) often need to be synchronized.
2253
2254 Qt allows to enrich logging messages with further meta-information
2255 by calling \l qSetMessagePattern(), or setting the \c QT_MESSAGE_PATTERN
2256 environment variable. To keep this formatting, a custom message handler
2257 can use \l qFormatLogMessage().
2258
2259 Try to keep the code in the message handler itself minimal, as expensive
2260 operations might block the application. Also, to avoid recursion, any
2261 logging messages generated in the message handler itself will be ignored.
2262
2263 The message handler should always return. For
2264 \l{QtFatalMsg}{fatal messages}, the application aborts immediately after
2265 handling that message.
2266
2267 Only one message handler can be installed at a time, for the whole application.
2268 If there was a previous custom message handler installed,
2269 the function will return a pointer to it. This handler can then
2270 be later reinstalled by another call to the method. Also, calling
2271 \c qInstallMessageHandler(nullptr) will restore the default
2272 message handler.
2273
2274 Here is an example of a message handler that logs to a local file
2275 before calling the default handler:
2276
2277 \snippet code/src_corelib_global_qglobal_widgets.cpp 2
2278
2279 Note that the C++ standard guarantees that \c{static FILE *f} is
2280 initialized in a thread-safe way. We can also expect \c{fprintf()}
2281 and \c{fflush()} to be thread-safe, so no further synchronization
2282 is necessary.
2283
2284 \sa QtMessageHandler, QtMsgType, qDebug(), qInfo(), qWarning(), qCritical(), qFatal(),
2285 {Debugging Techniques}, qFormatLogMessage()
2286*/
2287
2288/*!
2289 \fn void qSetMessagePattern(const QString &pattern)
2290 \relates <QtLogging>
2291 \since 5.0
2292
2293 \brief Changes the output of the default message handler.
2294
2295 Allows to tweak the output of qDebug(), qInfo(), qWarning(), qCritical(),
2296 and qFatal(). The category logging output of qCDebug(), qCInfo(),
2297 qCWarning(), and qCCritical() is formatted, too.
2298
2299 Following placeholders are supported:
2300
2301 \table
2302 \header \li Placeholder \li Description
2303 \row \li \c %{appname} \li QCoreApplication::applicationName()
2304 \row \li \c %{category} \li Logging category
2305 \row \li \c %{file} \li Path to source file
2306 \row \li \c %{function} \li Function
2307 \row \li \c %{line} \li Line in source file
2308 \row \li \c %{message} \li The actual message
2309 \row \li \c %{pid} \li QCoreApplication::applicationPid()
2310 \row \li \c %{threadid} \li The system-wide ID of current thread (if it can be obtained)
2311 \row \li \c %{threadname} \li The current thread name (if it can be obtained, or the thread ID, since Qt 6.10)
2312 \row \li \c %{qthreadptr} \li A pointer to the current QThread (result of QThread::currentThread())
2313 \row \li \c %{type} \li "debug", "warning", "critical" or "fatal"
2314 \row \li \c %{time process} \li time of the message, in seconds since the process started (the token "process" is literal)
2315 \row \li \c %{time boot} \li the time of the message, in seconds since the system boot if that
2316 can be determined (the token "boot" is literal). If the time since boot could not be obtained,
2317 the output is indeterminate (see QElapsedTimer::msecsSinceReference()).
2318 \row \li \c %{time [format]} \li system time when the message occurred, formatted by
2319 passing the \c format to \l QDateTime::toString(). If the format is
2320 not specified, the format of Qt::ISODate is used.
2321 \row \li \c{%{backtrace [depth=N] [separator="..."]}} \li A backtrace with the number of frames
2322 specified by the optional \c depth parameter (defaults to 5), and separated by the optional
2323 \c separator parameter (defaults to "|").
2324
2325 This expansion is available only on some platforms:
2326
2327 \list
2328 \li platforms using glibc;
2329 \li platforms shipping C++23's \c{<stacktrace>} header (requires compiling Qt in C++23 mode).
2330 \endlist
2331
2332 Depending on the platform, there are some restrictions on the function
2333 names printed by this expansion.
2334
2335 On some platforms,
2336 names are only known for exported functions. If you want to see the name of every function
2337 in your application, make sure your application is compiled and linked with \c{-rdynamic},
2338 or an equivalent of it.
2339
2340 When reading backtraces, take into account that frames might be missing due to inlining or
2341 tail call optimization.
2342 \endtable
2343
2344 You can also use conditionals on the type of the message using \c %{if-debug}, \c %{if-info}
2345 \c %{if-warning}, \c %{if-critical} or \c %{if-fatal} followed by an \c %{endif}.
2346 What is inside the \c %{if-*} and \c %{endif} will only be printed if the type matches.
2347
2348 Finally, text inside \c %{if-category} ... \c %{endif} is only printed if the category
2349 is not the default one.
2350
2351 Example:
2352 \snippet code/src_corelib_global_qlogging.cpp 0
2353
2354 The default \a pattern is \c{%{if-category}%{category}: %{endif}%{message}}.
2355
2356 \note On Android, the default \a pattern is \c{%{message}} because the category is used as
2357 \l{Android: log_print}{tag} since Android logcat has a dedicated field for the logging
2358 categories, see \l{Android: Log}{Android Logging}. If a custom \a pattern including the
2359 category is used, QCoreApplication::applicationName() is used as \l{Android: log_print}{tag}.
2360
2361 The \a pattern can also be changed at runtime by setting the QT_MESSAGE_PATTERN
2362 environment variable; if both \l qSetMessagePattern() is called and QT_MESSAGE_PATTERN is
2363 set, the environment variable takes precedence.
2364
2365 \note The information for the placeholders \c category, \c file, \c function and \c line is
2366 only recorded in debug builds. Alternatively, \c QT_MESSAGELOGCONTEXT can be defined
2367 explicitly. For more information refer to the QMessageLogContext documentation.
2368
2369 \note The message pattern only applies to unstructured logging, such as the default
2370 \c stderr output. Structured logging such as systemd will record the message as is,
2371 along with as much structured information as can be captured.
2372
2373 Custom message handlers can use qFormatLogMessage() to take \a pattern into account.
2374
2375 \sa qInstallMessageHandler(), {Debugging Techniques}, {QLoggingCategory}, QMessageLogContext
2376 */
2377
2379{
2380 const auto old = messageHandler.fetchAndStoreOrdered(h);
2381 if (old)
2382 return old;
2383 else
2385}
2386
2387void qSetMessagePattern(const QString &pattern)
2388{
2389 const auto locker = qt_scoped_lock(QMessagePattern::mutex);
2390
2391 if (!qMessagePattern()->fromEnvironment)
2392 qMessagePattern()->setPattern(pattern);
2393}
2394
2396 const QMessageLogContext &logContext) noexcept
2397{
2398 if (logContext.version == self->version) {
2399 auto other = static_cast<const QInternalMessageLogContext *>(&logContext);
2400 self->backtrace = other->backtrace;
2401 }
2402}
2403
2404/*!
2405 \internal
2406 Copies context information from \a logContext into this QMessageLogContext.
2407 Returns the number of backtrace frames that are desired.
2408*/
2410{
2411 version = CurrentVersion + 1;
2412 copyContextFrom(logContext);
2413
2414#ifdef QLOGGING_HAVE_BACKTRACE
2415 if (backtrace.has_value())
2416 return 0; // we have a stored backtrace, no need to get it again
2417
2418 // initializes the message pattern, if needed
2419 if (auto pattern = qMessagePattern())
2420 return pattern->maxBacktraceDepth;
2421#endif
2422
2423 return 0;
2424}
2425
2426/*!
2427 Copies context information from \a logContext into this QMessageLogContext.
2428 Returns a reference to this object.
2429
2430 Note that the version is \b not copied, only the context information.
2431
2432 \internal
2433*/
2434QMessageLogContext &QMessageLogContext::copyContextFrom(const QMessageLogContext &logContext) noexcept
2435{
2436 this->category = logContext.category;
2437 this->file = logContext.file;
2438 this->line = logContext.line;
2439 this->function = logContext.function;
2440 if (Q_UNLIKELY(version == CurrentVersion + 1))
2441 copyInternalContext(static_cast<QInternalMessageLogContext *>(this), logContext);
2442 return *this;
2443}
2444
2445/*!
2446 \fn QMessageLogger::QMessageLogger()
2447
2448 Constructs a default QMessageLogger. See the other constructors to specify
2449 context information.
2450*/
2451
2452/*!
2453 \fn QMessageLogger::QMessageLogger(const char *file, int line, const char *function)
2454
2455 Constructs a QMessageLogger to record log messages for \a file at \a line
2456 in \a function. The is equivalent to QMessageLogger(file, line, function, "default")
2457*/
2458/*!
2459 \fn QMessageLogger::QMessageLogger(const char *file, int line, const char *function, const char *category)
2460
2461 Constructs a QMessageLogger to record \a category messages for \a file at \a line
2462 in \a function.
2463
2464 \sa QLoggingCategory
2465*/
2466
2467/*!
2468 \fn void QMessageLogger::noDebug(const char *, ...) const
2469 \internal
2470
2471 Ignores logging output
2472
2473 \sa QNoDebug, qDebug()
2474*/
2475
2476/*!
2477 \fn QMessageLogContext::QMessageLogContext()
2478 \internal
2479
2480 Constructs a QMessageLogContext
2481*/
2482
2483/*!
2484 \fn QMessageLogContext::QMessageLogContext(const char *fileName, int lineNumber, const char *functionName, const char *categoryName)
2485 \internal
2486
2487 Constructs a QMessageLogContext with for file \a fileName at line
2488 \a lineNumber, in function \a functionName, and category \a categoryName.
2489
2490 \sa QLoggingCategory
2491*/
2492
2493/*!
2494 \macro qDebug(const char *format, ...)
2495 \relates <QtLogging>
2496 \threadsafe
2497
2498 Logs debug message \a format to the central message handler.
2499 \a format can contain format specifiers that are
2500 replaced by values specificed in additional arguments.
2501
2502 Example:
2503
2504 \snippet code/src_corelib_global_qglobal.cpp 24
2505
2506 \a format can contain format specifiers like \c {%s} for UTF-8 strings, or
2507 \c {%i} for integers. This is similar to how the C \c{printf()} function works.
2508 For more details on the formatting, see \l QString::asprintf().
2509
2510 For more convenience and further type support, you can also use
2511 \l{QDebug::qDebug()}, which follows the streaming paradigm (similar to
2512 \c{std::cout} or \c{std::cerr}).
2513
2514 This function does nothing if \c QT_NO_DEBUG_OUTPUT was defined during compilation.
2515
2516 To suppress the output at runtime, install your own message handler
2517 with qInstallMessageHandler().
2518
2519 \sa QDebug::qDebug(), qCDebug(), qInfo(), qWarning(), qCritical(), qFatal(),
2520 qInstallMessageHandler(), {Debugging Techniques}
2521*/
2522
2523/*!
2524 \macro qInfo(const char *format, ...)
2525 \relates <QtLogging>
2526 \threadsafe
2527 \since 5.5
2528
2529 Logs informational message \a format to the central message handler.
2530 \a format can contain format specifiers that are
2531 replaced by values specificed in additional arguments.
2532
2533 Example:
2534
2535 \snippet code/src_corelib_global_qglobal.cpp qInfo_printf
2536
2537 \a format can contain format specifiers like \c {%s} for UTF-8 strings, or
2538 \c {%i} for integers. This is similar to how the C \c{printf()} function works.
2539 For more details on the formatting, see \l QString::asprintf().
2540
2541 For more convenience and further type support, you can also use
2542 \l{QDebug::qInfo()}, which follows the streaming paradigm (similar to
2543 \c{std::cout} or \c{std::cerr}).
2544
2545 This function does nothing if \c QT_NO_INFO_OUTPUT was defined during compilation.
2546
2547 To suppress the output at runtime, install your own message handler
2548 using qInstallMessageHandler().
2549
2550 \sa QDebug::qInfo(), qCInfo(), qDebug(), qWarning(), qCritical(), qFatal(),
2551 qInstallMessageHandler(), {Debugging Techniques}
2552*/
2553
2554/*!
2555 \macro qWarning(const char *format, ...)
2556 \relates <QtLogging>
2557 \threadsafe
2558
2559 Logs warning message \a format to the central message handler.
2560 \a format can contain format specifiers that are
2561 replaced by values specificed in additional arguments.
2562
2563 Example:
2564 \snippet code/src_corelib_global_qglobal.cpp 26
2565
2566 \a format can contain format specifiers like \c {%s} for UTF-8 strings, or
2567 \c {%i} for integers. This is similar to how the C \c{printf()} function works.
2568 For more details on the formatting, see \l QString::asprintf().
2569
2570 For more convenience and further type support, you can also use
2571 \l{QDebug::qWarning()}, which follows the streaming paradigm (similar to
2572 \c{std::cout} or \c{std::cerr}).
2573
2574 This function does nothing if \c QT_NO_WARNING_OUTPUT was defined
2575 during compilation.
2576 To suppress the output at runtime, you can set
2577 \l{QLoggingCategory}{logging rules} or register a custom
2578 \l{QLoggingCategory::installFilter()}{filter}.
2579
2580 For debugging purposes, it is sometimes convenient to let the
2581 program abort for warning messages. This allows you
2582 to inspect the core dump, or attach a debugger - see also \l{qFatal()}.
2583 To enable this, set the environment variable \c{QT_FATAL_WARNINGS}
2584 to a number \c n. The program terminates then for the n-th warning.
2585 That is, if the environment variable is set to 1, it will terminate
2586 on the first call; if it contains the value 10, it will exit on the 10th
2587 call. Any non-numeric value in the environment variable is equivalent to 1.
2588
2589 \sa QDebug::qWarning(), qCWarning(), qDebug(), qInfo(), qCritical(), qFatal(),
2590 qInstallMessageHandler(), {Debugging Techniques}
2591*/
2592
2593/*!
2594 \macro qCritical(const char *format, ...)
2595 \relates <QtLogging>
2596 \threadsafe
2597
2598 Logs critical message \a format to the central message handler.
2599 \a format can contain format specifiers that are
2600 replaced by values specificed in additional arguments.
2601
2602 Example:
2603 \snippet code/src_corelib_global_qglobal.cpp 28
2604
2605 \a format can contain format specifiers like \c {%s} for UTF-8 strings, or
2606 \c {%i} for integers. This is similar to how the C \c{printf()} function works.
2607 For more details on the formatting, see \l QString::asprintf().
2608
2609 For more convenience and further type support, you can also use
2610 \l{QDebug::qCritical()}, which follows the streaming paradigm (similar to
2611 \c{std::cout} or \c{std::cerr}).
2612
2613 To suppress the output at runtime, you can define
2614 \l{QLoggingCategory}{logging rules} or register a custom
2615 \l{QLoggingCategory::installFilter()}{filter}.
2616
2617 For debugging purposes, it is sometimes convenient to let the
2618 program abort for critical messages. This allows you
2619 to inspect the core dump, or attach a debugger - see also \l{qFatal()}.
2620 To enable this, set the environment variable \c{QT_FATAL_CRITICALS}
2621 to a number \c n. The program terminates then for the n-th critical
2622 message.
2623 That is, if the environment variable is set to 1, it will terminate
2624 on the first call; if it contains the value 10, it will exit on the 10th
2625 call. Any non-numeric value in the environment variable is equivalent to 1.
2626
2627 \sa QDebug::qCritical, qCCritical(), qDebug(), qInfo(), qWarning(), qFatal(),
2628 qInstallMessageHandler(), {Debugging Techniques}
2629*/
2630
2631/*!
2632 \macro qFatal(const char *format, ...)
2633 \relates <QtLogging>
2634
2635 Logs fatal message \a format to the central message handler.
2636 \a format can contain format specifiers that are
2637 replaced by values specificed in additional arguments.
2638
2639 Example:
2640 \snippet code/src_corelib_global_qglobal.cpp 30
2641
2642 If you are using the \b{default message handler} this function will
2643 abort to create a core dump. On Windows, for debug builds,
2644 this function will report a _CRT_ERROR enabling you to connect a debugger
2645 to the application.
2646
2647 To suppress the output at runtime, install your own message handler
2648 with qInstallMessageHandler().
2649
2650 \sa qCFatal(), qDebug(), qInfo(), qWarning(), qCritical(),
2651 qInstallMessageHandler(), {Debugging Techniques}
2652*/
2653
2654/*!
2655 \enum QtMsgType
2656 \relates <QtLogging>
2657
2658 This enum describes the messages that can be sent to a message
2659 handler (QtMessageHandler). You can use the enum to identify and
2660 associate the various message types with the appropriate
2661 actions. Its values are, in order of increasing severity:
2662
2663 \value QtDebugMsg
2664 A message generated by the qDebug() function.
2665 \value QtInfoMsg
2666 A message generated by the qInfo() function.
2667 \value QtWarningMsg
2668 A message generated by the qWarning() function.
2669 \value QtCriticalMsg
2670 A message generated by the qCritical() function.
2671 \value QtFatalMsg
2672 A message generated by the qFatal() function.
2673 \omitvalue QtSystemMsg
2674
2675 \sa QtMessageHandler, qInstallMessageHandler(), QLoggingCategory
2676*/
2677
2678QT_END_NAMESPACE
\inmodule QtCore
Definition qbytearray.h:58
int initFrom(const QMessageLogContext &logContext)
void populateBacktrace(int frameCount)
Definition qlist.h:81
\inmodule QtCore
Definition qlogging.h:44
constexpr QMessageLogContext(const char *fileName, int lineNumber, const char *functionName, const char *categoryName) noexcept
Definition qlogging.h:49
const char * category
Definition qlogging.h:56
constexpr QMessageLogContext() noexcept=default
const char * function
Definition qlogging.h:55
const char * file
Definition qlogging.h:54
static const char ifCriticalTokenC[]
static bool grabMessageHandler()
void qt_message_output(QtMsgType msgType, const QMessageLogContext &context, const QString &message)
static const char emptyTokenC[]
static Q_NEVER_INLINE void qt_message(QtMsgType msgType, const QMessageLogContext &context, const char *msg, va_list ap)
Definition qlogging.cpp:409
static void preformattedMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &formattedMessage)
static bool systemHasStderr()
Returns true if writing to stderr is supported.
Definition qlogging.cpp:263
static const char endifTokenC[]
static bool isDefaultCategory(const char *category)
Definition qlogging.cpp:955
static const char messageTokenC[]
static bool qt_append_thread_name_to(QString &message)
Definition qlogging.cpp:248
static constexpr SystemMessageSink systemMessageSink
static void qt_maybe_message_fatal(QtMsgType, const QMessageLogContext &context, String &&message)
\inmodule QtCore \title Qt Logging Types
#define HANDLE_IF_TOKEN(LEVEL)
Q_DECLARE_TYPEINFO(QMessagePattern::BacktraceParams, Q_RELOCATABLE_TYPE)
static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &buf)
static const char timeTokenC[]
static bool isFatalCountDown(const char *varname, QBasicAtomicInt &n)
Definition qlogging.cpp:153
void qErrnoWarning(int code, const char *msg,...)
static const char qthreadptrTokenC[]
static const char fileTokenC[]
static const char ifDebugTokenC[]
static const char ifFatalTokenC[]
static const char categoryTokenC[]
static void stderr_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &formattedMessage)
static const char lineTokenC[]
static const char typeTokenC[]
static void ungrabMessageHandler()
static void copyInternalContext(QInternalMessageLogContext *self, const QMessageLogContext &logContext) noexcept
static const char ifCategoryTokenC[]
static int checked_var_value(const char *varname)
Definition qlogging.cpp:139
static const char threadnameTokenC[]
static const char pidTokenC[]
Q_TRACE_POINT(qtcore, qt_message_print, int type, const char *category, const char *function, const char *file, int line, const QString &message)
static const char threadidTokenC[]
static QString formatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &str)
static Q_CONSTINIT bool msgHandlerGrabbed
static const char backtraceTokenC[]
void qErrnoWarning(const char *msg,...)
static const char functionTokenC[]
#define IF_TOKEN(LEVEL)
static const char ifWarningTokenC[]
static const char appnameTokenC[]
static bool isFatal(QtMsgType msgType)
Definition qlogging.cpp:187
static const char ifInfoTokenC[]
QtMessageHandler qInstallMessageHandler(QtMessageHandler h)
static void qt_message_print(QtMsgType, const QMessageLogContext &context, const QString &message)
static bool stderrHasConsoleAttached()
Returns true if writing to stderr will end up in a console/terminal visible to the user.
Definition qlogging.cpp:288
void qSetMessagePattern(const QString &pattern)
Combined button and popup list for selecting options.
bool shouldLogToStderr()
Returns true if logging stderr should be ensured.
Definition qlogging.cpp:341
#define __has_include(x)
#define QT_MESSAGELOG_FILE
Definition qlogging.h:160
#define QT_MESSAGELOG_LINE
Definition qlogging.h:161
QtMsgType
Definition qlogging.h:30
@ QtCriticalMsg
Definition qlogging.h:34
@ QtFatalMsg
Definition qlogging.h:35
@ QtDebugMsg
Definition qlogging.h:31
void(* QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &)
Definition qlogging.h:197
QMutex QBasicMutex
Definition qmutex.h:360
void setPattern(const QString &pattern)
std::unique_ptr< std::unique_ptr< const char[]>[]> literals
std::chrono::steady_clock::time_point appStartTime
std::unique_ptr< const char *[]> tokens
QList< QString > timeArgs
static QBasicMutex mutex
void setDefaultPattern()