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

source code of qtbase/src/corelib/global/qlogging.cpp