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