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
6#include "qlogging.h"
7#include "qlogging_p.h"
8
9#include "qbytearray.h"
10#include "qlist.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"
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"
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
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);
144 if (str.isEmpty())
145 return 0;
146
147 bool ok;
148 int value = str.toInt(&ok, 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(NeverFatal);
166 return false;
167 } else if (env == ImmediatelyFatal) {
168 return true;
169 } else if (n.testAndSetRelaxed(Uninitialized, env - 1, 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(v, v - 1, 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("QT_FATAL_CRITICALS", fatalCriticalsCount);
194
195 case QtWarningMsg:
196 return isFatalCountDown("QT_FATAL_WARNINGS", 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(pthread_self(), name.data(), name.size()) == 0) {
211 QUtf8StringView threadName(name.data());
212 if (!threadName.isEmpty()) {
213 message.append(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*/
288{
289 static const bool stderrHasConsoleAttached = []() -> bool {
291 return false;
292
293 if (qEnvironmentVariableIntValue("QT_LOGGING_TO_CONSOLE")) {
294 fprintf(stderr, "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("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(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*/
341{
342 static bool forceStderrLogging = qEnvironmentVariableIntValue("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 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*/
408static void qt_message(QtMsgType msgType, const QMessageLogContext &context, const char *msg, va_list ap)
409{
410 QString buf = QString::vasprintf(msg, ap);
411 qt_message_print(msgType, context, buf);
412 qt_maybe_message_fatal(msgType, context, 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(QtDebugMsg, 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(QtInfoMsg, 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 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(QtDebugMsg, 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(QtDebugMsg, 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(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(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((*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(QtInfoMsg, 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(QtInfoMsg, 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(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(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((*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(QtWarningMsg, 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(QtWarningMsg, 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(QtWarningMsg, 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(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(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((*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(QtCriticalMsg, 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(QtCriticalMsg, 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(QtCriticalMsg, 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(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(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((*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(QtFatalMsg, 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(QtFatalMsg, 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(QtFatalMsg, 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(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(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((*catFunc)());
951}
952#endif // QT_NO_DEBUG_STREAM
953
954static bool isDefaultCategory(const char *category)
955{
956 return !category || strcmp(category, 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(']') && !(info.startsWith('+') || info.startsWith('-'))) {
977 while (--pos) {
978 if (info.at(pos) == '[') {
979 info.truncate(pos);
980 break;
981 }
982 }
983 if (info.endsWith(' ')) {
984 info.chop(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("operator ", "operator");
997
998 pos = -1;
999 // remove argument list
1000 forever {
1001 int parencount = 0;
1002 pos = info.lastIndexOf(')', pos);
1003 if (pos == -1) {
1004 // Don't know how to parse this function name
1005 return info;
1006 }
1007 if (info.indexOf('>', pos) != -1
1008 || info.indexOf(':', 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(pos) == ')')
1019 ++parencount;
1020 else if (info.at(pos) == '(')
1021 --parencount;
1022 --pos;
1023 }
1024 if (parencount != 0)
1025 return info;
1026
1027 info.truncate(++pos);
1028
1029 if (info.at(pos - 1) == ')') {
1030 if (info.indexOf(operator_call) == pos - qsizetype(strlen(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(0, info.indexOf('('));
1037 info.chop(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(pos)) {
1052 case ')':
1053 if (info.indexOf(operator_call) == pos - qsizetype(strlen(operator_call)) + 1)
1054 pos -= 2;
1055 break;
1056 case '<':
1057 if (info.indexOf(operator_lessThan) == pos - qsizetype(strlen(operator_lessThan)) + 1)
1058 --pos;
1059 break;
1060 case '>':
1061 if (info.indexOf(operator_greaterThan) == pos - qsizetype(strlen(operator_greaterThan)) + 1)
1062 --pos;
1063 break;
1064 case '=': {
1065 auto operatorLength = qsizetype(strlen(operator_lessThanEqual));
1066 if (info.indexOf(operator_lessThanEqual) == pos - operatorLength + 1)
1067 pos -= 2;
1068 else if (info.indexOf(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(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(pos + 1);
1096
1097 // remove trailing '*', '&' that are part of the return argument
1098 while ((info.at(0) == '*')
1099 || (info.at(0) == '&'))
1100 info = info.mid(1);
1101
1102 // we have the full function name now.
1103 // clean up the templates
1104 while ((pos = info.lastIndexOf('>')) != -1) {
1105 if (!info.contains('<'))
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(pos);
1114 if (c == '>')
1115 ++templatecount;
1116 else if (c == '<')
1117 --templatecount;
1118 --pos;
1119 }
1120 ++pos;
1121 info.remove(pos, 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
1151{
1154
1155 void setPattern(const QString &pattern);
1157 {
1158 const char *const defaultTokens[] = {
1159#ifndef Q_OS_ANDROID
1160 // "%{if-category}%{category}: %{endif}%{message}"
1163 ": ", // won't point to literals[] but that's ok
1165#endif
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(std::begin(defaultTokens), std::end(defaultTokens), ptr);
1175 *end = nullptr;
1176 tokens.release();
1177 tokens.reset(ptr);
1178 }
1179
1180 // 0 terminated arrays of literal tokens / literal or placeholder tokens
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();
1190#ifdef QLOGGING_HAVE_BACKTRACE
1191 QList<BacktraceParams> backtraceArgs; // backtrace arguments in sequence of %{backtrace
1192 int maxBacktraceDepth = 0;
1193#endif
1194
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};
1211
1212Q_CONSTINIT QBasicMutex QMessagePattern::mutex;
1213
1215{
1216 const QString envPattern = qEnvironmentVariable("QT_MESSAGE_PATTERN");
1217 if (envPattern.isEmpty()) {
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 + 1) == u'{') {
1245 // beginning of placeholder
1246 if (!lexeme.isEmpty()) {
1247 lexemes.append(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(lexeme);
1259 lexeme.clear();
1260 inPlaceholder = false;
1261 }
1262 }
1263 if (!lexeme.isEmpty())
1264 lexemes.append(lexeme);
1265
1266 // tokenizer
1267 std::vector<std::unique_ptr<const char[]>> literalsVar;
1268 tokens.reset(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("%{"_L1) && lexeme.endsWith(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(QLatin1StringView(timeTokenC))) {
1302 tokens[i] = timeTokenC;
1303 qsizetype spaceIdx = lexeme.indexOf(QChar::fromLatin1(' '));
1304 if (spaceIdx > 0)
1305 timeArgs.append(lexeme.mid(spaceIdx + 1, lexeme.size() - spaceIdx - 2));
1306 else
1307 timeArgs.append(QString());
1308 } else if (lexeme.startsWith(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(lexeme);
1316 if (m.hasMatch()) {
1317 int depth = m.capturedView(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(lexeme);
1324 if (m.hasMatch())
1325 backtraceSeparator = m.captured(1);
1326 BacktraceParams backtraceParams;
1327 backtraceParams.backtraceDepth = backtraceDepth;
1328 backtraceParams.backtraceSeparator = backtraceSeparator;
1329 backtraceArgs.append(backtraceParams);
1330 maxBacktraceDepth = qMax(maxBacktraceDepth, 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(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(1);
1373
1375 "QMessagePattern::setPattern", nullptr);
1376 preformattedMessageHandler(QtWarningMsg, ctx, error);
1377 }
1378
1379 literals.reset(new std::unique_ptr<const char[]>[literalsVar.size() + 1]);
1380 std::move(literalsVar.begin(), literalsVar.end(), &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(TypicalBacktraceFrameCount + frameCount);
1448 Q_ASSERT(result.size() == int(result.size()));
1449 int n = ::backtrace(result.data(), int(result.size()));
1450 if (n <= 0)
1451 result.clear();
1452 else
1453 result.resize(n);
1454}
1455
1456static QStringList
1457backtraceFramesForLogMessage(int frameCount,
1458 const QInternalMessageLogContext::BacktraceStorage &buffer)
1459{
1460 struct DecodedFrame {
1461 QString library;
1462 QString function;
1463 };
1464
1465 QStringList result;
1466 if (frameCount == 0)
1467 return result;
1468
1469 auto shouldSkipFrame = [&result](const auto &library, const auto &function) {
1470 if (!result.isEmpty() || !library.contains(QLatin1StringView(QtCoreLibraryName)))
1471 return false;
1472 if (function.isEmpty())
1473 return true;
1474 if (function.contains("6QDebug"_L1))
1475 return true;
1476 if (function.contains("14QMessageLogger"_L1))
1477 return true;
1478 if (function.contains("17qt_message_output"_L1))
1479 return true;
1480 if (function.contains("26QInternalMessageLogContext"_L1))
1481 return true;
1482 return false;
1483 };
1484
1485 auto demangled = [](auto &function) -> QString {
1486 if (!function.startsWith("_Z"_L1))
1487 return function;
1488
1489 // we optimize for the case where __cxa_demangle succeeds
1490 auto fn = [&]() {
1491 if constexpr (sizeof(function.at(0)) == 1)
1492 return function.data(); // -> const char *
1493 else
1494 return std::move(function).toUtf8(); // -> QByteArray
1495 }();
1496 auto cleanup = [](auto *p) { free(p); };
1497 using Ptr = std::unique_ptr<char, decltype(cleanup)>;
1498 auto demangled = Ptr(abi::__cxa_demangle(fn, nullptr, nullptr, nullptr), cleanup);
1499
1500 if (demangled)
1501 return QString::fromUtf8(qCleanupFuncinfo(demangled.get()));
1502 else
1503 return QString::fromUtf8(fn); // restore
1504 };
1505
1506# if QT_CONFIG(dladdr)
1507 // use dladdr() instead of backtrace_symbols()
1508 QString cachedLibrary;
1509 const char *cachedFname = nullptr;
1510 auto decodeFrame = [&](void *addr) -> DecodedFrame {
1511 Dl_info info;
1512 if (!dladdr(addr, &info))
1513 return {};
1514
1515 // These are actually UTF-8, so we'll correct below
1516 QLatin1StringView fn(info.dli_sname);
1517 QLatin1StringView lib;
1518 if (const char *lastSlash = strrchr(info.dli_fname, '/'))
1519 lib = QLatin1StringView(lastSlash + 1);
1520 else
1521 lib = QLatin1StringView(info.dli_fname);
1522
1523 if (shouldSkipFrame(lib, fn))
1524 return {};
1525
1526 QString function = demangled(fn);
1527 if (lib.data() != cachedFname) {
1528 cachedFname = lib.data();
1529 cachedLibrary = QString::fromUtf8(cachedFname, lib.size());
1530 }
1531 return { cachedLibrary, function };
1532 };
1533# else
1534 // The results of backtrace_symbols looks like this:
1535 // /lib/libc.so.6(__libc_start_main+0xf3) [0x4a937413]
1536 // The offset and function name are optional.
1537 // This regexp tries to extract the library name (without the path) and the function name.
1538 // This code is protected by QMessagePattern::mutex so it is thread safe on all compilers
1539 static const QRegularExpression rx(QStringLiteral("^(?:[^(]*/)?([^(/]+)\\‍(([^+]*)(?:[\\+[a-f0-9x]*)?\\‍) \\[[a-f0-9x]*\\]$"));
1540
1541 auto decodeFrame = [&](void *&addr) -> DecodedFrame {
1542 auto cleanup = [](auto *p) { free(p); };
1543 auto strings =
1544 std::unique_ptr<char *, decltype(cleanup)>(backtrace_symbols(&addr, 1), cleanup);
1545 QString trace = QString::fromUtf8(strings.get()[0]);
1546 QRegularExpressionMatch m = rx.match(trace);
1547 if (!m.hasMatch())
1548 return {};
1549
1550 QString library = m.captured(1);
1551 QString function = m.captured(2);
1552
1553 // skip the trace from QtCore that are because of the qDebug itself
1554 if (shouldSkipFrame(library, function))
1555 return {};
1556
1557 function = demangled(function);
1558 return { library, function };
1559 };
1560# endif
1561
1562 for (void *const &addr : buffer) {
1563 DecodedFrame frame = decodeFrame(addr);
1564 if (!frame.library.isEmpty()) {
1565 if (frame.function.isEmpty())
1566 result.append(u'?' + frame.library + u'?');
1567 else
1568 result.append(frame.function);
1569 } else {
1570 // innermost, unknown frames are usually the logging framework itself
1571 if (!result.isEmpty())
1572 result.append(QStringLiteral("???"));
1573 }
1574
1575 if (result.size() == frameCount)
1576 break;
1577 }
1578 return result;
1579}
1580#else
1581#error "Internal error: backtrace enabled, but no way to gather backtraces available"
1582#endif // QLOGGING_USE_..._BACKTRACE
1583
1584static QString formatBacktraceForLogMessage(const QMessagePattern::BacktraceParams backtraceParams,
1585 const QMessageLogContext &ctx)
1586{
1587 // do we have a backtrace stored?
1588 if (ctx.version <= QMessageLogContext::CurrentVersion)
1589 return QString();
1590
1591 auto &fullctx = static_cast<const QInternalMessageLogContext &>(ctx);
1592 if (!fullctx.backtrace.has_value())
1593 return QString();
1594
1595 QString backtraceSeparator = backtraceParams.backtraceSeparator;
1596 int backtraceDepth = backtraceParams.backtraceDepth;
1597
1598 QStringList frames = backtraceFramesForLogMessage(backtraceDepth, *fullctx.backtrace);
1599 if (frames.isEmpty())
1600 return QString();
1601
1602 // if the first frame is unknown, replace it with the context function
1603 if (ctx.function && frames.at(0).startsWith(u'?'))
1604 frames[0] = QString::fromUtf8(qCleanupFuncinfo(ctx.function));
1605
1606 return frames.join(backtraceSeparator);
1607}
1608#else
1610{
1611 // initFrom() returns 0 to our caller, so we should never get here
1612 Q_UNREACHABLE();
1613}
1614#endif // !QLOGGING_HAVE_BACKTRACE
1615
1616Q_GLOBAL_STATIC(QMessagePattern, qMessagePattern)
1617
1618/*!
1619 \relates <QtLogging>
1620 \since 5.4
1621
1622 Generates a formatted string out of the \a type, \a context, \a str arguments.
1623
1624 qFormatLogMessage returns a QString that is formatted according to the current message pattern.
1625 It can be used by custom message handlers to format output similar to Qt's default message
1626 handler.
1627
1628 The function is thread-safe.
1629
1630 \sa qInstallMessageHandler(), qSetMessagePattern()
1631 */
1632QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &str)
1633{
1634 return formatLogMessage(type, context, str);
1635}
1636
1637// Separate function so the default message handler can bypass the public,
1638// exported function above. Static functions can't get added to the dynamic
1639// symbol tables, so they never show up in backtrace_symbols() or equivalent.
1640static QString formatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &str)
1641{
1642 QString message;
1643
1644 const auto locker = qt_scoped_lock(QMessagePattern::mutex);
1645
1646 QMessagePattern *pattern = qMessagePattern();
1647 if (!pattern) {
1648 // after destruction of static QMessagePattern instance
1649 message.append(str);
1650 return message;
1651 }
1652
1653 bool skip = false;
1654
1655 int timeArgsIdx = 0;
1656#ifdef QLOGGING_HAVE_BACKTRACE
1657 int backtraceArgsIdx = 0;
1658#endif
1659
1660 // we do not convert file, function, line literals to local encoding due to overhead
1661 for (int i = 0; pattern->tokens[i]; ++i) {
1662 const char *token = pattern->tokens[i];
1663 if (token == endifTokenC) {
1664 skip = false;
1665 } else if (skip) {
1666 // we skip adding messages, but we have to iterate over
1667 // timeArgsIdx and backtraceArgsIdx anyway
1668 if (token == timeTokenC)
1669 timeArgsIdx++;
1670#ifdef QLOGGING_HAVE_BACKTRACE
1671 else if (token == backtraceTokenC)
1672 backtraceArgsIdx++;
1673#endif
1674 } else if (token == messageTokenC) {
1675 message.append(str);
1676 } else if (token == categoryTokenC) {
1677 message.append(QLatin1StringView(context.category));
1678 } else if (token == typeTokenC) {
1679 switch (type) {
1680 case QtDebugMsg: message.append("debug"_L1); break;
1681 case QtInfoMsg: message.append("info"_L1); break;
1682 case QtWarningMsg: message.append("warning"_L1); break;
1683 case QtCriticalMsg:message.append("critical"_L1); break;
1684 case QtFatalMsg: message.append("fatal"_L1); break;
1685 }
1686 } else if (token == fileTokenC) {
1687 if (context.file)
1688 message.append(QLatin1StringView(context.file));
1689 else
1690 message.append("unknown"_L1);
1691 } else if (token == lineTokenC) {
1692 message.append(QString::number(context.line));
1693 } else if (token == functionTokenC) {
1694 if (context.function)
1695 message.append(QString::fromLatin1(qCleanupFuncinfo(context.function)));
1696 else
1697 message.append("unknown"_L1);
1698 } else if (token == pidTokenC) {
1699 message.append(QString::number(QCoreApplication::applicationPid()));
1700 } else if (token == appnameTokenC) {
1701 message.append(QCoreApplication::applicationName());
1702 } else if (token == threadidTokenC) {
1703 // print the TID as decimal
1704 message.append(QString::number(qt_gettid()));
1705 } else if (token == threadnameTokenC) {
1706 if (!qt_append_thread_name_to(message))
1707 message.append(QString::number(qt_gettid())); // fallback to the TID
1708 } else if (token == qthreadptrTokenC) {
1709 message.append("0x"_L1);
1710 message.append(QString::number(qlonglong(QThread::currentThread()->currentThread()), 16));
1711#ifdef QLOGGING_HAVE_BACKTRACE
1712 } else if (token == backtraceTokenC) {
1713 QMessagePattern::BacktraceParams backtraceParams = pattern->backtraceArgs.at(backtraceArgsIdx);
1714 backtraceArgsIdx++;
1715 message.append(formatBacktraceForLogMessage(backtraceParams, context));
1716#endif
1717 } else if (token == timeTokenC) {
1718 using namespace std::chrono;
1719 auto formatElapsedTime = [](steady_clock::duration time) {
1720 // we assume time > 0
1721 auto ms = duration_cast<milliseconds>(time);
1722 auto sec = duration_cast<seconds>(ms);
1723 ms -= sec;
1724 return QString::asprintf("%6lld.%03u", qint64(sec.count()), uint(ms.count()));
1725 };
1726 QString timeFormat = pattern->timeArgs.at(timeArgsIdx);
1727 timeArgsIdx++;
1728 if (timeFormat == "process"_L1) {
1729 message += formatElapsedTime(steady_clock::now() - pattern->appStartTime);
1730 } else if (timeFormat == "boot"_L1) {
1731 // just print the milliseconds since the elapsed timer reference
1732 // like the Linux kernel does
1733 message += formatElapsedTime(steady_clock::now().time_since_epoch());
1734#if QT_CONFIG(datestring)
1735 } else if (timeFormat.isEmpty()) {
1736 message.append(QDateTime::currentDateTime().toString(Qt::ISODate));
1737 } else {
1738 message.append(QDateTime::currentDateTime().toString(timeFormat));
1739#endif // QT_CONFIG(datestring)
1740 }
1741 } else if (token == ifCategoryTokenC) {
1743 skip = true;
1744#define HANDLE_IF_TOKEN(LEVEL)
1745 } else if (token == if##LEVEL##TokenC) {
1746 skip = type != Qt##LEVEL##Msg;
1747 HANDLE_IF_TOKEN(Debug)
1748 HANDLE_IF_TOKEN(Info)
1749 HANDLE_IF_TOKEN(Warning)
1750 HANDLE_IF_TOKEN(Critical)
1751 HANDLE_IF_TOKEN(Fatal)
1752#undef HANDLE_IF_TOKEN
1753 } else {
1754 message.append(QLatin1StringView(token));
1755 }
1756 }
1757 return message;
1758}
1759
1760static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &buf);
1761
1762// pointer to QtMessageHandler debug handler (with context)
1763Q_CONSTINIT static QBasicAtomicPointer<void (QtMsgType, const QMessageLogContext &, const QString &)> messageHandler = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
1764
1765// ------------------------ Alternate logging sinks -------------------------
1766
1767#if QT_CONFIG(slog2)
1768#ifndef QT_LOG_CODE
1769#define QT_LOG_CODE 9000
1770#endif
1771
1772static bool slog2_default_handler(QtMsgType type, const QMessageLogContext &,
1773 const QString &message)
1774{
1775 if (shouldLogToStderr())
1776 return false; // Leave logging up to stderr handler
1777
1778 QString formattedMessage = message;
1779 formattedMessage.append(u'\n');
1780 if (slog2_set_default_buffer((slog2_buffer_t)-1) == 0) {
1781 slog2_buffer_set_config_t buffer_config;
1782 slog2_buffer_t buffer_handle;
1783
1784 buffer_config.buffer_set_name = __progname;
1785 buffer_config.num_buffers = 1;
1786 buffer_config.verbosity_level = SLOG2_DEBUG1;
1787 buffer_config.buffer_config[0].buffer_name = "default";
1788 buffer_config.buffer_config[0].num_pages = 8;
1789
1790 if (slog2_register(&buffer_config, &buffer_handle, 0) == -1) {
1791 fprintf(stderr, "Error registering slogger2 buffer!\n");
1792 fprintf(stderr, "%s", formattedMessage.toLocal8Bit().constData());
1793 fflush(stderr);
1794 return false;
1795 }
1796
1797 // Set as the default buffer
1798 slog2_set_default_buffer(buffer_handle);
1799 }
1800 int severity = SLOG2_INFO;
1801 //Determines the severity level
1802 switch (type) {
1803 case QtDebugMsg:
1804 severity = SLOG2_DEBUG1;
1805 break;
1806 case QtInfoMsg:
1807 severity = SLOG2_INFO;
1808 break;
1809 case QtWarningMsg:
1810 severity = SLOG2_NOTICE;
1811 break;
1812 case QtCriticalMsg:
1813 severity = SLOG2_WARNING;
1814 break;
1815 case QtFatalMsg:
1816 severity = SLOG2_ERROR;
1817 break;
1818 }
1819 //writes to the slog2 buffer
1820 slog2c(NULL, QT_LOG_CODE, severity, formattedMessage.toLocal8Bit().constData());
1821
1822 return true; // Prevent further output to stderr
1823}
1824#endif // slog2
1825
1826#if QT_CONFIG(journald)
1827static bool systemd_default_message_handler(QtMsgType type,
1828 const QMessageLogContext &context,
1829 const QString &message)
1830{
1831 if (shouldLogToStderr())
1832 return false; // Leave logging up to stderr handler
1833
1834 int priority = LOG_INFO; // Informational
1835 switch (type) {
1836 case QtDebugMsg:
1837 priority = LOG_DEBUG; // Debug-level messages
1838 break;
1839 case QtInfoMsg:
1840 priority = LOG_INFO; // Informational conditions
1841 break;
1842 case QtWarningMsg:
1843 priority = LOG_WARNING; // Warning conditions
1844 break;
1845 case QtCriticalMsg:
1846 priority = LOG_CRIT; // Critical conditions
1847 break;
1848 case QtFatalMsg:
1849 priority = LOG_ALERT; // Action must be taken immediately
1850 break;
1851 }
1852
1853 // Explicit QByteArray instead of auto, to resolve the QStringBuilder proxy
1854 const QByteArray messageField = "MESSAGE="_ba + message.toUtf8().constData();
1855 const QByteArray priorityField = "PRIORITY="_ba + QByteArray::number(priority);
1856 const QByteArray tidField = "TID="_ba + QByteArray::number(qlonglong(qt_gettid()));
1857 const QByteArray fileField = context.file
1858 ? "CODE_FILE="_ba + context.file : QByteArray();
1859 const QByteArray funcField = context.function
1860 ? "CODE_FUNC="_ba + context.function : QByteArray();
1861 const QByteArray lineField = context.line
1862 ? "CODE_LINE="_ba + QByteArray::number(context.line) : QByteArray();
1863 const QByteArray categoryField = context.category
1864 ? "QT_CATEGORY="_ba + context.category : QByteArray();
1865
1866 auto toIovec = [](const QByteArray &ba) {
1867 return iovec{ const_cast<char*>(ba.data()), size_t(ba.size()) };
1868 };
1869
1870 struct iovec fields[7] = {
1871 toIovec(messageField),
1872 toIovec(priorityField),
1873 toIovec(tidField),
1874 };
1875 int nFields = 3;
1876 if (context.file)
1877 fields[nFields++] = toIovec(fileField);
1878 if (context.function)
1879 fields[nFields++] = toIovec(funcField);
1880 if (context.line)
1881 fields[nFields++] = toIovec(lineField);
1882 if (context.category)
1883 fields[nFields++] = toIovec(categoryField);
1884
1885 sd_journal_sendv(fields, nFields);
1886
1887 return true; // Prevent further output to stderr
1888}
1889#endif
1890
1891#if QT_CONFIG(syslog)
1892static bool syslog_default_message_handler(QtMsgType type, const QMessageLogContext &context,
1893 const QString &formattedMessage)
1894{
1895 if (shouldLogToStderr())
1896 return false; // Leave logging up to stderr handler
1897
1898 int priority = LOG_INFO; // Informational
1899 switch (type) {
1900 case QtDebugMsg:
1901 priority = LOG_DEBUG; // Debug-level messages
1902 break;
1903 case QtInfoMsg:
1904 priority = LOG_INFO; // Informational conditions
1905 break;
1906 case QtWarningMsg:
1907 priority = LOG_WARNING; // Warning conditions
1908 break;
1909 case QtCriticalMsg:
1910 priority = LOG_CRIT; // Critical conditions
1911 break;
1912 case QtFatalMsg:
1913 priority = LOG_ALERT; // Action must be taken immediately
1914 break;
1915 }
1916
1917 syslog(priority, "%s", formattedMessage.toUtf8().constData());
1918
1919 return true; // Prevent further output to stderr
1920}
1921#endif
1922
1923#ifdef Q_OS_ANDROID
1924static bool android_default_message_handler(QtMsgType type,
1925 const QMessageLogContext &context,
1926 const QString &formattedMessage)
1927{
1928 if (shouldLogToStderr())
1929 return false; // Leave logging up to stderr handler
1930
1931 android_LogPriority priority = ANDROID_LOG_DEBUG;
1932 switch (type) {
1933 case QtDebugMsg:
1934 priority = ANDROID_LOG_DEBUG;
1935 break;
1936 case QtInfoMsg:
1937 priority = ANDROID_LOG_INFO;
1938 break;
1939 case QtWarningMsg:
1940 priority = ANDROID_LOG_WARN;
1941 break;
1942 case QtCriticalMsg:
1943 priority = ANDROID_LOG_ERROR;
1944 break;
1945 case QtFatalMsg:
1946 priority = ANDROID_LOG_FATAL;
1947 break;
1948 };
1949
1950 QMessagePattern *pattern = qMessagePattern();
1951 const QString tag = (pattern && pattern->containsToken(categoryTokenC))
1952 // If application name is a tag ensure it has no spaces
1953 ? QCoreApplication::applicationName().replace(u' ', u'_')
1954 : QString::fromUtf8(context.category);
1955 __android_log_print(priority, qPrintable(tag), "%s\n", qPrintable(formattedMessage));
1956
1957 return true; // Prevent further output to stderr
1958}
1959#endif //Q_OS_ANDROID
1960
1961#ifdef Q_OS_WIN
1962static void win_outputDebugString_helper(const QString &message)
1963{
1964 const qsizetype maxOutputStringLength = 32766;
1965 Q_CONSTINIT static QBasicMutex m;
1966 auto locker = qt_unique_lock(m);
1967 // fast path: Avoid string copies if one output is enough
1968 if (message.length() <= maxOutputStringLength) {
1969 OutputDebugString(reinterpret_cast<const wchar_t *>(message.utf16()));
1970 } else {
1971 wchar_t messagePart[maxOutputStringLength + 1];
1972 for (qsizetype i = 0; i < message.length(); i += maxOutputStringLength) {
1973 const qsizetype length = qMin(message.length() - i, maxOutputStringLength);
1974 const qsizetype len = QStringView{message}.mid(i, length).toWCharArray(messagePart);
1975 Q_ASSERT(len == length);
1976 messagePart[len] = 0;
1977 OutputDebugString(messagePart);
1978 }
1979 }
1980}
1981
1982static bool win_message_handler(QtMsgType, const QMessageLogContext &,
1983 const QString &formattedMessage)
1984{
1985 if (shouldLogToStderr())
1986 return false; // Leave logging up to stderr handler
1987
1988 win_outputDebugString_helper(formattedMessage + u'\n');
1989
1990 return true; // Prevent further output to stderr
1991}
1992#endif
1993
1994#ifdef Q_OS_WASM
1995static bool wasm_default_message_handler(QtMsgType type,
1996 const QMessageLogContext &,
1997 const QString &formattedMessage)
1998{
1999 static bool forceStderrLogging = qEnvironmentVariableIntValue("QT_FORCE_STDERR_LOGGING");
2000 if (forceStderrLogging)
2001 return false;
2002
2003 int emOutputFlags = EM_LOG_CONSOLE;
2004 QByteArray localMsg = formattedMessage.toLocal8Bit();
2005 switch (type) {
2006 case QtDebugMsg:
2007 break;
2008 case QtInfoMsg:
2009 break;
2010 case QtWarningMsg:
2011 emOutputFlags |= EM_LOG_WARN;
2012 break;
2013 case QtCriticalMsg:
2014 emOutputFlags |= EM_LOG_ERROR;
2015 break;
2016 case QtFatalMsg:
2017 emOutputFlags |= EM_LOG_ERROR;
2018 }
2019 emscripten_log(emOutputFlags, "%s\n", qPrintable(formattedMessage));
2020
2021 return true; // Prevent further output to stderr
2022}
2023#endif
2024
2025// --------------------------------------------------------------------------
2026
2027static void stderr_message_handler(QtMsgType type, const QMessageLogContext &context,
2028 const QString &formattedMessage)
2029{
2030 Q_UNUSED(type);
2031 Q_UNUSED(context);
2032
2033 // print nothing if message pattern didn't apply / was empty.
2034 // (still print empty lines, e.g. because message itself was empty)
2035 if (formattedMessage.isNull())
2036 return;
2037 fprintf(stderr, "%s\n", formattedMessage.toLocal8Bit().constData());
2038 fflush(stderr);
2039}
2040
2041namespace {
2042struct SystemMessageSink
2043{
2044 using Fn = bool(QtMsgType, const QMessageLogContext &, const QString &);
2045 Fn *sink;
2046 bool messageIsUnformatted = false;
2047};
2048}
2049
2050static constexpr SystemMessageSink systemMessageSink = {
2051#if defined(Q_OS_WIN)
2052 win_message_handler
2053#elif QT_CONFIG(slog2)
2054 slog2_default_handler
2055#elif QT_CONFIG(journald)
2056 systemd_default_message_handler, true
2057#elif QT_CONFIG(syslog)
2058 syslog_default_message_handler
2059#elif defined(Q_OS_ANDROID)
2060 android_default_message_handler
2061#elif defined(QT_USE_APPLE_UNIFIED_LOGGING)
2062 AppleUnifiedLogger::messageHandler, true
2063#elif defined Q_OS_WASM
2064 wasm_default_message_handler
2065#else
2066 nullptr
2067#endif
2068};
2069
2071 const QString &formattedMessage)
2072{
2073QT_WARNING_PUSH
2074QT_WARNING_DISABLE_GCC("-Waddress") // "the address of ~~ will never be NULL
2075 if (systemMessageSink.sink && systemMessageSink.sink(type, context, formattedMessage))
2076 return;
2077QT_WARNING_POP
2078
2079 stderr_message_handler(type, context, formattedMessage);
2080}
2081
2082/*!
2083 \internal
2084*/
2085static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context,
2086 const QString &message)
2087{
2088 // A message sink logs the message to a structured or unstructured destination,
2089 // optionally formatting the message if the latter, and returns true if the sink
2090 // handled stderr output as well, which will shortcut our default stderr output.
2091
2092 if (systemMessageSink.messageIsUnformatted) {
2093 if (systemMessageSink.sink(type, context, message))
2094 return;
2095 }
2096
2097 preformattedMessageHandler(type, context, formatLogMessage(type, context, message));
2098}
2099
2100Q_CONSTINIT static thread_local bool msgHandlerGrabbed = false;
2101
2103{
2105 return false;
2106
2107 msgHandlerGrabbed = true;
2108 return true;
2109}
2110
2112{
2113 msgHandlerGrabbed = false;
2114}
2115
2116static void qt_message_print(QtMsgType msgType, const QMessageLogContext &context, const QString &message)
2117{
2118 Q_TRACE(qt_message_print, msgType, context.category, context.function, context.file, context.line, message);
2119
2120 // qDebug, qWarning, ... macros do not check whether category is enabledgc
2121 if (msgType != QtFatalMsg && isDefaultCategory(context.category)) {
2122 if (QLoggingCategory *defaultCategory = QLoggingCategory::defaultCategory()) {
2123 if (!defaultCategory->isEnabled(msgType))
2124 return;
2125 }
2126 }
2127
2128 // prevent recursion in case the message handler generates messages
2129 // itself, e.g. by using Qt API
2131 const auto ungrab = qScopeGuard([]{ ungrabMessageHandler(); });
2132 auto msgHandler = messageHandler.loadAcquire();
2133 (msgHandler ? msgHandler : qDefaultMessageHandler)(msgType, context, message);
2134 } else {
2135 stderr_message_handler(msgType, context, message);
2136 }
2137}
2138
2139template <typename String> static void
2140qt_maybe_message_fatal(QtMsgType msgType, const QMessageLogContext &context, String &&message)
2141{
2142 if (!isFatal(msgType))
2143 return;
2144#if defined(Q_CC_MSVC_ONLY) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR)
2145 wchar_t contextFileL[256];
2146 // we probably should let the compiler do this for us, by declaring QMessageLogContext::file to
2147 // be const wchar_t * in the first place, but the #ifdefery above is very complex and we
2148 // wouldn't be able to change it later on...
2149 convert_to_wchar_t_elided(contextFileL, sizeof contextFileL / sizeof *contextFileL,
2150 context.file);
2151 // get the current report mode
2152 int reportMode = _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW);
2153 _CrtSetReportMode(_CRT_ERROR, reportMode);
2154
2155 int ret = _CrtDbgReportW(_CRT_ERROR, contextFileL, context.line, _CRT_WIDE(QT_VERSION_STR),
2156 reinterpret_cast<const wchar_t *>(message.utf16()));
2157 if ((ret == 0) && (reportMode & _CRTDBG_MODE_WNDW))
2158 return; // ignore
2159 else if (ret == 1)
2160 _CrtDbgBreak();
2161#else
2162 Q_UNUSED(context);
2163#endif
2164
2165 if constexpr (std::is_class_v<String> && !std::is_const_v<String>)
2166 message.clear();
2167 else
2168 Q_UNUSED(message);
2169 qAbort();
2170}
2171
2172/*!
2173 \internal
2174*/
2175void qt_message_output(QtMsgType msgType, const QMessageLogContext &context, const QString &message)
2176{
2177 QInternalMessageLogContext ctx(context);
2178 qt_message_print(msgType, ctx, message);
2179 qt_maybe_message_fatal(msgType, ctx, message);
2180}
2181
2182void qErrnoWarning(const char *msg, ...)
2183{
2184 // qt_error_string() will allocate anyway, so we don't have
2185 // to be careful here (like we do in plain qWarning())
2186 QString error_string = qt_error_string(-1); // before vasprintf changes errno/GetLastError()
2187
2188 va_list ap;
2189 va_start(ap, msg);
2190 QString buf = QString::vasprintf(msg, ap);
2191 va_end(ap);
2192
2193 buf += " ("_L1 + error_string + u')';
2195 qt_message_output(QtWarningMsg, context, buf);
2196}
2197
2198void qErrnoWarning(int code, const char *msg, ...)
2199{
2200 // qt_error_string() will allocate anyway, so we don't have
2201 // to be careful here (like we do in plain qWarning())
2202 va_list ap;
2203 va_start(ap, msg);
2204 QString buf = QString::vasprintf(msg, ap);
2205 va_end(ap);
2206
2207 buf += " ("_L1 + qt_error_string(code) + u')';
2209 qt_message_output(QtWarningMsg, context, buf);
2210}
2211
2212/*!
2213 \typedef QtMessageHandler
2214 \relates <QtLogging>
2215 \since 5.0
2216
2217 This is a typedef for a pointer to a function with the following
2218 signature:
2219
2220 \snippet code/src_corelib_global_qglobal.cpp 49
2221
2222 \sa QtMsgType, qInstallMessageHandler()
2223*/
2224
2225/*!
2226 \fn QtMessageHandler qInstallMessageHandler(QtMessageHandler handler)
2227 \relates <QtLogging>
2228 \since 5.0
2229
2230 Installs a Qt message \a handler.
2231 Returns a pointer to the previously installed message handler.
2232
2233 A message handler is a function that prints out debug, info,
2234 warning, critical, and fatal messages from Qt's logging infrastructure.
2235 By default, Qt uses a standard message handler that formats and
2236 prints messages to different sinks specific to the operating system
2237 and Qt configuration. Installing your own message handler allows you
2238 to assume full control, and for instance log messages to the
2239 file system.
2240
2241 Note that Qt supports \l{QLoggingCategory}{logging categories} for
2242 grouping related messages in semantic categories. You can use these
2243 to enable or disable logging per category and \l{QtMsgType}{message type}.
2244 As the filtering for logging categories is done even before a message
2245 is created, messages for disabled types and categories will not reach
2246 the message handler.
2247
2248 A message handler needs to be
2249 \l{Reentrancy and Thread-Safety}{reentrant}. That is, it might be called
2250 from different threads, in parallel. Therefore, writes to common sinks
2251 (like a database, or a file) often need to be synchronized.
2252
2253 Qt allows to enrich logging messages with further meta-information
2254 by calling \l qSetMessagePattern(), or setting the \c QT_MESSAGE_PATTERN
2255 environment variable. To keep this formatting, a custom message handler
2256 can use \l qFormatLogMessage().
2257
2258 Try to keep the code in the message handler itself minimal, as expensive
2259 operations might block the application. Also, to avoid recursion, any
2260 logging messages generated in the message handler itself will be ignored.
2261
2262 The message handler should always return. For
2263 \l{QtFatalMsg}{fatal messages}, the application aborts immediately after
2264 handling that message.
2265
2266 Only one message handler can be installed at a time, for the whole application.
2267 If there was a previous custom message handler installed,
2268 the function will return a pointer to it. This handler can then
2269 be later reinstalled by another call to the method. Also, calling
2270 \c qInstallMessageHandler(nullptr) will restore the default
2271 message handler.
2272
2273 Here is an example of a message handler that logs to a local file
2274 before calling the default handler:
2275
2276 \snippet code/src_corelib_global_qglobal_widgets.cpp 2
2277
2278 Note that the C++ standard guarantees that \c{static FILE *f} is
2279 initialized in a thread-safe way. We can also expect \c{fprintf()}
2280 and \c{fflush()} to be thread-safe, so no further synchronization
2281 is necessary.
2282
2283 \sa QtMessageHandler, QtMsgType, qDebug(), qInfo(), qWarning(), qCritical(), qFatal(),
2284 {Debugging Techniques}, qFormatLogMessage()
2285*/
2286
2287/*!
2288 \fn void qSetMessagePattern(const QString &pattern)
2289 \relates <QtLogging>
2290 \since 5.0
2291
2292 \brief Changes the output of the default message handler.
2293
2294 Allows to tweak the output of qDebug(), qInfo(), qWarning(), qCritical(),
2295 and qFatal(). The category logging output of qCDebug(), qCInfo(),
2296 qCWarning(), and qCCritical() is formatted, too.
2297
2298 Following placeholders are supported:
2299
2300 \table
2301 \header \li Placeholder \li Description
2302 \row \li \c %{appname} \li QCoreApplication::applicationName()
2303 \row \li \c %{category} \li Logging category
2304 \row \li \c %{file} \li Path to source file
2305 \row \li \c %{function} \li Function
2306 \row \li \c %{line} \li Line in source file
2307 \row \li \c %{message} \li The actual message
2308 \row \li \c %{pid} \li QCoreApplication::applicationPid()
2309 \row \li \c %{threadid} \li The system-wide ID of current thread (if it can be obtained)
2310 \row \li \c %{threadname} \li The current thread name (if it can be obtained, or the thread ID, since Qt 6.10)
2311 \row \li \c %{qthreadptr} \li A pointer to the current QThread (result of QThread::currentThread())
2312 \row \li \c %{type} \li "debug", "warning", "critical" or "fatal"
2313 \row \li \c %{time process} \li time of the message, in seconds since the process started (the token "process" is literal)
2314 \row \li \c %{time boot} \li the time of the message, in seconds since the system boot if that
2315 can be determined (the token "boot" is literal). If the time since boot could not be obtained,
2316 the output is indeterminate (see QElapsedTimer::msecsSinceReference()).
2317 \row \li \c %{time [format]} \li system time when the message occurred, formatted by
2318 passing the \c format to \l QDateTime::toString(). If the format is
2319 not specified, the format of Qt::ISODate is used.
2320 \row \li \c{%{backtrace [depth=N] [separator="..."]}} \li A backtrace with the number of frames
2321 specified by the optional \c depth parameter (defaults to 5), and separated by the optional
2322 \c separator parameter (defaults to "|").
2323
2324 This expansion is available only on some platforms:
2325
2326 \list
2327 \li platforms using glibc;
2328 \li platforms shipping C++23's \c{<stacktrace>} header (requires compiling Qt in C++23 mode).
2329 \endlist
2330
2331 Depending on the platform, there are some restrictions on the function
2332 names printed by this expansion.
2333
2334 On some platforms,
2335 names are only known for exported functions. If you want to see the name of every function
2336 in your application, make sure your application is compiled and linked with \c{-rdynamic},
2337 or an equivalent of it.
2338
2339 When reading backtraces, take into account that frames might be missing due to inlining or
2340 tail call optimization.
2341 \endtable
2342
2343 You can also use conditionals on the type of the message using \c %{if-debug}, \c %{if-info}
2344 \c %{if-warning}, \c %{if-critical} or \c %{if-fatal} followed by an \c %{endif}.
2345 What is inside the \c %{if-*} and \c %{endif} will only be printed if the type matches.
2346
2347 Finally, text inside \c %{if-category} ... \c %{endif} is only printed if the category
2348 is not the default one.
2349
2350 Example:
2351 \snippet code/src_corelib_global_qlogging.cpp 0
2352
2353 The default \a pattern is \c{%{if-category}%{category}: %{endif}%{message}}.
2354
2355 \note On Android, the default \a pattern is \c{%{message}} because the category is used as
2356 \l{Android: log_print}{tag} since Android logcat has a dedicated field for the logging
2357 categories, see \l{Android: Log}{Android Logging}. If a custom \a pattern including the
2358 category is used, QCoreApplication::applicationName() is used as \l{Android: log_print}{tag}.
2359
2360 The \a pattern can also be changed at runtime by setting the QT_MESSAGE_PATTERN
2361 environment variable; if both \l qSetMessagePattern() is called and QT_MESSAGE_PATTERN is
2362 set, the environment variable takes precedence.
2363
2364 \note The information for the placeholders \c category, \c file, \c function and \c line is
2365 only recorded in debug builds. Alternatively, \c QT_MESSAGELOGCONTEXT can be defined
2366 explicitly. For more information refer to the QMessageLogContext documentation.
2367
2368 \note The message pattern only applies to unstructured logging, such as the default
2369 \c stderr output. Structured logging such as systemd will record the message as is,
2370 along with as much structured information as can be captured.
2371
2372 Custom message handlers can use qFormatLogMessage() to take \a pattern into account.
2373
2374 \sa qInstallMessageHandler(), {Debugging Techniques}, {QLoggingCategory}, QMessageLogContext
2375 */
2376
2378{
2379 const auto old = messageHandler.fetchAndStoreOrdered(h);
2380 if (old)
2381 return old;
2382 else
2384}
2385
2386void qSetMessagePattern(const QString &pattern)
2387{
2388 const auto locker = qt_scoped_lock(QMessagePattern::mutex);
2389
2390 if (!qMessagePattern()->fromEnvironment)
2391 qMessagePattern()->setPattern(pattern);
2392}
2393
2395 const QMessageLogContext &logContext) noexcept
2396{
2397 if (logContext.version == self->version) {
2398 auto other = static_cast<const QInternalMessageLogContext *>(&logContext);
2399 self->backtrace = other->backtrace;
2400 }
2401}
2402
2403/*!
2404 \internal
2405 Copies context information from \a logContext into this QMessageLogContext.
2406 Returns the number of backtrace frames that are desired.
2407*/
2409{
2410 version = CurrentVersion + 1;
2411 copyContextFrom(logContext);
2412
2413#ifdef QLOGGING_HAVE_BACKTRACE
2414 if (backtrace.has_value())
2415 return 0; // we have a stored backtrace, no need to get it again
2416
2417 // initializes the message pattern, if needed
2418 if (auto pattern = qMessagePattern())
2419 return pattern->maxBacktraceDepth;
2420#endif
2421
2422 return 0;
2423}
2424
2425/*!
2426 Copies context information from \a logContext into this QMessageLogContext.
2427 Returns a reference to this object.
2428
2429 Note that the version is \b not copied, only the context information.
2430
2431 \internal
2432*/
2433QMessageLogContext &QMessageLogContext::copyContextFrom(const QMessageLogContext &logContext) noexcept
2434{
2435 this->category = logContext.category;
2436 this->file = logContext.file;
2437 this->line = logContext.line;
2438 this->function = logContext.function;
2439 if (Q_UNLIKELY(version == CurrentVersion + 1))
2440 copyInternalContext(static_cast<QInternalMessageLogContext *>(this), logContext);
2441 return *this;
2442}
2443
2444/*!
2445 \fn QMessageLogger::QMessageLogger()
2446
2447 Constructs a default QMessageLogger. See the other constructors to specify
2448 context information.
2449*/
2450
2451/*!
2452 \fn QMessageLogger::QMessageLogger(const char *file, int line, const char *function)
2453
2454 Constructs a QMessageLogger to record log messages for \a file at \a line
2455 in \a function. The is equivalent to QMessageLogger(file, line, function, "default")
2456*/
2457/*!
2458 \fn QMessageLogger::QMessageLogger(const char *file, int line, const char *function, const char *category)
2459
2460 Constructs a QMessageLogger to record \a category messages for \a file at \a line
2461 in \a function.
2462
2463 \sa QLoggingCategory
2464*/
2465
2466/*!
2467 \fn void QMessageLogger::noDebug(const char *, ...) const
2468 \internal
2469
2470 Ignores logging output
2471
2472 \sa QNoDebug, qDebug()
2473*/
2474
2475/*!
2476 \fn QMessageLogContext::QMessageLogContext()
2477 \internal
2478
2479 Constructs a QMessageLogContext
2480*/
2481
2482/*!
2483 \fn QMessageLogContext::QMessageLogContext(const char *fileName, int lineNumber, const char *functionName, const char *categoryName)
2484 \internal
2485
2486 Constructs a QMessageLogContext with for file \a fileName at line
2487 \a lineNumber, in function \a functionName, and category \a categoryName.
2488
2489 \sa QLoggingCategory
2490*/
2491
2492/*!
2493 \macro qDebug(const char *format, ...)
2494 \relates <QtLogging>
2495 \threadsafe
2496
2497 Logs debug message \a format to the central message handler.
2498 \a format can contain format specifiers that are
2499 replaced by values specificed in additional arguments.
2500
2501 Example:
2502
2503 \snippet code/src_corelib_global_qglobal.cpp 24
2504
2505 \a format can contain format specifiers like \c {%s} for UTF-8 strings, or
2506 \c {%i} for integers. This is similar to how the C \c{printf()} function works.
2507 For more details on the formatting, see \l QString::asprintf().
2508
2509 For more convenience and further type support, you can also use
2510 \l{QDebug::qDebug()}, which follows the streaming paradigm (similar to
2511 \c{std::cout} or \c{std::cerr}).
2512
2513 This function does nothing if \c QT_NO_DEBUG_OUTPUT was defined during compilation.
2514
2515 To suppress the output at runtime, install your own message handler
2516 with qInstallMessageHandler().
2517
2518 \sa QDebug::qDebug(), qCDebug(), qInfo(), qWarning(), qCritical(), qFatal(),
2519 qInstallMessageHandler(), {Debugging Techniques}
2520*/
2521
2522/*!
2523 \macro qInfo(const char *format, ...)
2524 \relates <QtLogging>
2525 \threadsafe
2526 \since 5.5
2527
2528 Logs informational message \a format to the central message handler.
2529 \a format can contain format specifiers that are
2530 replaced by values specificed in additional arguments.
2531
2532 Example:
2533
2534 \snippet code/src_corelib_global_qglobal.cpp qInfo_printf
2535
2536 \a format can contain format specifiers like \c {%s} for UTF-8 strings, or
2537 \c {%i} for integers. This is similar to how the C \c{printf()} function works.
2538 For more details on the formatting, see \l QString::asprintf().
2539
2540 For more convenience and further type support, you can also use
2541 \l{QDebug::qInfo()}, which follows the streaming paradigm (similar to
2542 \c{std::cout} or \c{std::cerr}).
2543
2544 This function does nothing if \c QT_NO_INFO_OUTPUT was defined during compilation.
2545
2546 To suppress the output at runtime, install your own message handler
2547 using qInstallMessageHandler().
2548
2549 \sa QDebug::qInfo(), qCInfo(), qDebug(), qWarning(), qCritical(), qFatal(),
2550 qInstallMessageHandler(), {Debugging Techniques}
2551*/
2552
2553/*!
2554 \macro qWarning(const char *format, ...)
2555 \relates <QtLogging>
2556 \threadsafe
2557
2558 Logs warning message \a format to the central message handler.
2559 \a format can contain format specifiers that are
2560 replaced by values specificed in additional arguments.
2561
2562 Example:
2563 \snippet code/src_corelib_global_qglobal.cpp 26
2564
2565 \a format can contain format specifiers like \c {%s} for UTF-8 strings, or
2566 \c {%i} for integers. This is similar to how the C \c{printf()} function works.
2567 For more details on the formatting, see \l QString::asprintf().
2568
2569 For more convenience and further type support, you can also use
2570 \l{QDebug::qWarning()}, which follows the streaming paradigm (similar to
2571 \c{std::cout} or \c{std::cerr}).
2572
2573 This function does nothing if \c QT_NO_WARNING_OUTPUT was defined
2574 during compilation.
2575 To suppress the output at runtime, you can set
2576 \l{QLoggingCategory}{logging rules} or register a custom
2577 \l{QLoggingCategory::installFilter()}{filter}.
2578
2579 For debugging purposes, it is sometimes convenient to let the
2580 program abort for warning messages. This allows you
2581 to inspect the core dump, or attach a debugger - see also \l{qFatal()}.
2582 To enable this, set the environment variable \c{QT_FATAL_WARNINGS}
2583 to a number \c n. The program terminates then for the n-th warning.
2584 That is, if the environment variable is set to 1, it will terminate
2585 on the first call; if it contains the value 10, it will exit on the 10th
2586 call. Any non-numeric value in the environment variable is equivalent to 1.
2587
2588 \sa QDebug::qWarning(), qCWarning(), qDebug(), qInfo(), qCritical(), qFatal(),
2589 qInstallMessageHandler(), {Debugging Techniques}
2590*/
2591
2592/*!
2593 \macro qCritical(const char *format, ...)
2594 \relates <QtLogging>
2595 \threadsafe
2596
2597 Logs critical message \a format to the central message handler.
2598 \a format can contain format specifiers that are
2599 replaced by values specificed in additional arguments.
2600
2601 Example:
2602 \snippet code/src_corelib_global_qglobal.cpp 28
2603
2604 \a format can contain format specifiers like \c {%s} for UTF-8 strings, or
2605 \c {%i} for integers. This is similar to how the C \c{printf()} function works.
2606 For more details on the formatting, see \l QString::asprintf().
2607
2608 For more convenience and further type support, you can also use
2609 \l{QDebug::qCritical()}, which follows the streaming paradigm (similar to
2610 \c{std::cout} or \c{std::cerr}).
2611
2612 To suppress the output at runtime, you can define
2613 \l{QLoggingCategory}{logging rules} or register a custom
2614 \l{QLoggingCategory::installFilter()}{filter}.
2615
2616 For debugging purposes, it is sometimes convenient to let the
2617 program abort for critical messages. This allows you
2618 to inspect the core dump, or attach a debugger - see also \l{qFatal()}.
2619 To enable this, set the environment variable \c{QT_FATAL_CRITICALS}
2620 to a number \c n. The program terminates then for the n-th critical
2621 message.
2622 That is, if the environment variable is set to 1, it will terminate
2623 on the first call; if it contains the value 10, it will exit on the 10th
2624 call. Any non-numeric value in the environment variable is equivalent to 1.
2625
2626 \sa QDebug::qCritical, qCCritical(), qDebug(), qInfo(), qWarning(), qFatal(),
2627 qInstallMessageHandler(), {Debugging Techniques}
2628*/
2629
2630/*!
2631 \macro qFatal(const char *format, ...)
2632 \relates <QtLogging>
2633
2634 Logs fatal message \a format to the central message handler.
2635 \a format can contain format specifiers that are
2636 replaced by values specificed in additional arguments.
2637
2638 Example:
2639 \snippet code/src_corelib_global_qglobal.cpp 30
2640
2641 If you are using the \b{default message handler} this function will
2642 abort to create a core dump. On Windows, for debug builds,
2643 this function will report a _CRT_ERROR enabling you to connect a debugger
2644 to the application.
2645
2646 To suppress the output at runtime, install your own message handler
2647 with qInstallMessageHandler().
2648
2649 \sa qCFatal(), qDebug(), qInfo(), qWarning(), qCritical(),
2650 qInstallMessageHandler(), {Debugging Techniques}
2651*/
2652
2653/*!
2654 \enum QtMsgType
2655 \relates <QtLogging>
2656
2657 This enum describes the messages that can be sent to a message
2658 handler (QtMessageHandler). You can use the enum to identify and
2659 associate the various message types with the appropriate
2660 actions. Its values are, in order of increasing severity:
2661
2662 \value QtDebugMsg
2663 A message generated by the qDebug() function.
2664 \value QtInfoMsg
2665 A message generated by the qInfo() function.
2666 \value QtWarningMsg
2667 A message generated by the qWarning() function.
2668 \value QtCriticalMsg
2669 A message generated by the qCritical() function.
2670 \value QtFatalMsg
2671 A message generated by the qFatal() function.
2672 \omitvalue QtSystemMsg
2673
2674 \sa QtMessageHandler, qInstallMessageHandler(), QLoggingCategory
2675*/
2676
2677QT_END_NAMESPACE
\inmodule QtCore
Definition qbytearray.h:58
int initFrom(const QMessageLogContext &logContext)
void populateBacktrace(int frameCount)
Definition qlist.h:80
\inmodule QtCore
Definition qlogging.h:43
constexpr QMessageLogContext(const char *fileName, int lineNumber, const char *functionName, const char *categoryName) noexcept
Definition qlogging.h:48
const char * category
Definition qlogging.h:55
constexpr QMessageLogContext() noexcept=default
const char * function
Definition qlogging.h:54
const char * file
Definition qlogging.h:53
static Q_CONSTINIT thread_local bool msgHandlerGrabbed
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:408
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:262
static const char endifTokenC[]
static bool isDefaultCategory(const char *category)
Definition qlogging.cpp:954
static const char messageTokenC[]
static bool qt_append_thread_name_to(QString &message)
Definition qlogging.cpp:247
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:152
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:138
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 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:186
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:287
void qSetMessagePattern(const QString &pattern)
bool shouldLogToStderr()
Returns true if logging stderr should be ensured.
Definition qlogging.cpp:340
#define __has_include(x)
#define QT_MESSAGELOG_FILE
Definition qlogging.h:159
#define QT_MESSAGELOG_LINE
Definition qlogging.h:160
QtMsgType
Definition qlogging.h:29
@ QtFatalMsg
Definition qlogging.h:34
@ QtDebugMsg
Definition qlogging.h:30
void(* QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &)
Definition qlogging.h:196
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()