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 // scanner
1240 QVarLengthArray<QStringView, 16> lexemes;
1241 qsizetype literalLexemeCount = 0; // those not matching any token
1242 qsizetype lexemeStart = 0;
1243 bool inPlaceholder = false;
1244 for (qsizetype i = 0; i < pattern.size(); ++i) {
1245 const QChar c = pattern.at(i);
1246 if (c == u'%' && !inPlaceholder) {
1247 if ((i + 1 < pattern.size())
1248 && pattern.at(i + 1) == u'{') {
1249 // beginning of placeholder
1250 if (lexemeStart != i) {
1251 lexemes.append(QStringView(pattern.cbegin() + lexemeStart,
1252 pattern.cbegin() + i));
1253 ++literalLexemeCount;
1254 lexemeStart = i;
1255 }
1256 inPlaceholder = true;
1257 }
1258 }
1259
1260 if (c == u'}' && inPlaceholder) {
1261 // end of placeholder
1262 // +1 because we need to include '}'
1263 lexemes.append(QStringView(pattern.cbegin() + lexemeStart,
1264 pattern.cbegin() + i + 1));
1265 lexemeStart = i + 1;
1266 inPlaceholder = false;
1267 }
1268 }
1269 if (lexemeStart < pattern.size()) {
1270 lexemes.append(QStringView(pattern.cbegin() + lexemeStart,
1271 pattern.cend()));
1272 ++literalLexemeCount;
1273 }
1274
1275 // tokenizer - use local variables, so that we do not corrupt the pattern
1276 // in case of an exception
1277
1278 QList<QString> newTimeArgs;
1279#ifdef QLOGGING_HAVE_BACKTRACE
1280 QList<BacktraceParams> newBacktraceArgs;
1281 int newMaxBacktraceDepth = 0;
1282#endif
1283
1284 auto newLiterals = std::make_unique<std::unique_ptr<const char[]>[]>(literalLexemeCount + 1);
1285 auto newTokens = std::make_unique<const char *[]>(lexemes.size() + 1);
1286 newTokens[lexemes.size()] = nullptr;
1287
1288 bool nestedIfError = false;
1289 bool inIf = false;
1290 QString error;
1291 qsizetype literalLexemeIndex = 0;
1292
1293 for (qsizetype i = 0; i < lexemes.size(); ++i) {
1294 const QStringView lexeme = lexemes.at(i);
1295 if (lexeme.startsWith("%{"_L1) && lexeme.endsWith(u'}')) {
1296 // placeholder
1297 if (lexeme == QLatin1StringView(typeTokenC)) {
1298 newTokens[i] = typeTokenC;
1299 } else if (lexeme == QLatin1StringView(categoryTokenC))
1300 newTokens[i] = categoryTokenC;
1301 else if (lexeme == QLatin1StringView(messageTokenC))
1302 newTokens[i] = messageTokenC;
1303 else if (lexeme == QLatin1StringView(fileTokenC))
1304 newTokens[i] = fileTokenC;
1305 else if (lexeme == QLatin1StringView(lineTokenC))
1306 newTokens[i] = lineTokenC;
1307 else if (lexeme == QLatin1StringView(functionTokenC))
1308 newTokens[i] = functionTokenC;
1309 else if (lexeme == QLatin1StringView(pidTokenC))
1310 newTokens[i] = pidTokenC;
1311 else if (lexeme == QLatin1StringView(appnameTokenC))
1312 newTokens[i] = appnameTokenC;
1313 else if (lexeme == QLatin1StringView(threadidTokenC))
1314 newTokens[i] = threadidTokenC;
1315 else if (lexeme == QLatin1StringView(threadnameTokenC))
1316 newTokens[i] = threadnameTokenC;
1317 else if (lexeme == QLatin1StringView(qthreadptrTokenC))
1318 newTokens[i] = qthreadptrTokenC;
1319 else if (lexeme.startsWith(QLatin1StringView(timeTokenC))) {
1320 newTokens[i] = timeTokenC;
1321 qsizetype spaceIdx = lexeme.indexOf(QChar::fromLatin1(' '));
1322 if (spaceIdx > 0)
1323 newTimeArgs.append(QString(lexeme.mid(spaceIdx + 1, lexeme.size() - spaceIdx - 2)));
1324 else
1325 newTimeArgs.append(QString());
1326 } else if (lexeme.startsWith(QLatin1StringView(backtraceTokenC))) {
1327#ifdef QLOGGING_HAVE_BACKTRACE
1328 newTokens[i] = backtraceTokenC;
1329 QString backtraceSeparator = QStringLiteral("|");
1330 int backtraceDepth = 5;
1331 static const QRegularExpression depthRx(QStringLiteral(" depth=(?|\"([^\"]*)\"|([^ }]*))"));
1332 static const QRegularExpression separatorRx(QStringLiteral(" separator=(?|\"([^\"]*)\"|([^ }]*))"));
1333 QRegularExpressionMatch m = depthRx.matchView(lexeme);
1334 if (m.hasMatch()) {
1335 int depth = m.capturedView(1).toInt();
1336 if (depth <= 0)
1337 error += "QT_MESSAGE_PATTERN: %{backtrace} depth must be a number greater than 0\n"_L1;
1338 else
1339 backtraceDepth = std::min(depth, QInternalMessageLogContext::MaxBacktraceDepth);
1340 }
1341 m = separatorRx.matchView(lexeme);
1342 if (m.hasMatch())
1343 backtraceSeparator = m.captured(1);
1344 BacktraceParams backtraceParams;
1345 backtraceParams.backtraceDepth = backtraceDepth;
1346 backtraceParams.backtraceSeparator = backtraceSeparator;
1347 newBacktraceArgs.append(backtraceParams);
1348 newMaxBacktraceDepth = qMax(newMaxBacktraceDepth, backtraceDepth);
1349#else
1350 error += "QT_MESSAGE_PATTERN: %{backtrace} is not supported by this Qt build\n"_L1;
1351 newTokens[i] = "";
1352#endif
1353 }
1354
1355#define IF_TOKEN(LEVEL)
1356 else if (lexeme == QLatin1StringView(LEVEL)) {
1357 if (inIf)
1358 nestedIfError = true;
1359 newTokens[i] = LEVEL;
1360 inIf = true;
1361 }
1368#undef IF_TOKEN
1369 else if (lexeme == QLatin1StringView(endifTokenC)) {
1370 newTokens[i] = endifTokenC;
1371 if (!inIf && !nestedIfError)
1372 error += "QT_MESSAGE_PATTERN: %{endif} without an %{if-*}\n"_L1;
1373 inIf = false;
1374 } else {
1375 newTokens[i] = emptyTokenC;
1376 error += "QT_MESSAGE_PATTERN: Unknown placeholder "_L1 + lexeme + '\n'_L1;
1377 }
1378 } else {
1379 Q_ASSERT(literalLexemeIndex < literalLexemeCount);
1380 using UP = std::unique_ptr<char[]>;
1381 newLiterals[literalLexemeIndex] = UP(qstrdup(lexeme.toUtf8().constData()));
1382 newTokens[i] = newLiterals[literalLexemeIndex].get();
1383 ++literalLexemeIndex;
1384 }
1385 }
1386 if (nestedIfError)
1387 error += "QT_MESSAGE_PATTERN: %{if-*} cannot be nested\n"_L1;
1388 else if (inIf)
1389 error += "QT_MESSAGE_PATTERN: missing %{endif}\n"_L1;
1390
1391 if (!error.isEmpty()) {
1392 // remove the last '\n' because the sinks deal with that on their own
1393 error.chop(1);
1394
1396 "QMessagePattern::setPattern", nullptr);
1397 preformattedMessageHandler(QtWarningMsg, ctx, error);
1398 }
1399
1400 literals = std::move(newLiterals);
1401 tokens = std::move(newTokens);
1402 timeArgs = std::move(newTimeArgs);
1403#ifdef QLOGGING_HAVE_BACKTRACE
1404 backtraceArgs = std::move(newBacktraceArgs);
1405 maxBacktraceDepth = newMaxBacktraceDepth;
1406#endif
1407}
1408
1409#if defined(QLOGGING_HAVE_BACKTRACE)
1410// make sure the function has "Message" in the name so the function is removed
1411/*
1412 A typical backtrace in debug mode looks like:
1413 #0 QInternalMessageLogContext::populateBacktrace (this=0x7fffffffd660, frameCount=5) at qlogging.cpp:1342
1414 #1 QInternalMessageLogContext::QInternalMessageLogContext (logContext=..., this=<optimized out>) at qlogging_p.h:42
1415 #2 QDebug::~QDebug (this=0x7fffffffdac8, __in_chrg=<optimized out>) at qdebug.cpp:160
1416
1417 In release mode, the QInternalMessageLogContext constructor will be usually
1418 inlined. Empirical testing with GCC 13 and Clang 17 suggest they do obey the
1419 Q_ALWAYS_INLINE in that constructor even in debug mode and do inline it.
1420 Unfortunately, we can't know for sure if it has been.
1421*/
1422static constexpr int TypicalBacktraceFrameCount = 3;
1423static constexpr const char *QtCoreLibraryName = "Qt" QT_STRINGIFY(QT_VERSION_MAJOR) "Core";
1424
1425#if defined(QLOGGING_USE_STD_BACKTRACE)
1426Q_NEVER_INLINE void QInternalMessageLogContext::populateBacktrace(int frameCount)
1427{
1428 assert(frameCount >= 0);
1429 backtrace = std::stacktrace::current(0, TypicalBacktraceFrameCount + frameCount);
1430}
1431
1432static QStringList
1433backtraceFramesForLogMessage(int frameCount,
1434 const QInternalMessageLogContext::BacktraceStorage &buffer)
1435{
1436 QStringList result;
1437 result.reserve(buffer.size());
1438
1439 const auto shouldSkipFrame = [](QByteArrayView description)
1440 {
1441#if defined(_MSVC_STL_VERSION)
1442 const auto libraryNameEnd = description.indexOf('!');
1443 if (libraryNameEnd != -1) {
1444 const auto libraryName = description.first(libraryNameEnd);
1445 if (!libraryName.contains(QtCoreLibraryName))
1446 return false;
1447 }
1448#endif
1449 if (description.contains("populateBacktrace"))
1450 return true;
1451 if (description.contains("QInternalMessageLogContext"))
1452 return true;
1453 if (description.contains("~QDebug"))
1454 return true;
1455 return false;
1456 };
1457
1458 for (const auto &entry : buffer) {
1459 const std::string description = entry.description();
1460 if (result.isEmpty() && shouldSkipFrame(description))
1461 continue;
1462 result.append(QString::fromStdString(description));
1463 }
1464
1465 return result;
1466}
1467
1468#elif defined(QLOGGING_USE_EXECINFO_BACKTRACE)
1469
1470Q_NEVER_INLINE void QInternalMessageLogContext::populateBacktrace(int frameCount)
1471{
1472 assert(frameCount >= 0);
1473 BacktraceStorage &result = backtrace.emplace(TypicalBacktraceFrameCount + frameCount);
1474 Q_ASSERT(result.size() == int(result.size()));
1475 int n = ::backtrace(result.data(), int(result.size()));
1476 if (n <= 0)
1477 result.clear();
1478 else
1479 result.resize(n);
1480}
1481
1482static QStringList
1483backtraceFramesForLogMessage(int frameCount,
1484 const QInternalMessageLogContext::BacktraceStorage &buffer)
1485{
1486 struct DecodedFrame {
1487 QString library;
1488 QString function;
1489 };
1490
1491 QStringList result;
1492 if (frameCount == 0)
1493 return result;
1494
1495 auto shouldSkipFrame = [&result](const auto &library, const auto &function) {
1496 if (!result.isEmpty() || !library.contains(QLatin1StringView(QtCoreLibraryName)))
1497 return false;
1498 if (function.isEmpty())
1499 return true;
1500 if (function.contains("6QDebug"_L1))
1501 return true;
1502 if (function.contains("14QMessageLogger"_L1))
1503 return true;
1504 if (function.contains("17qt_message_output"_L1))
1505 return true;
1506 if (function.contains("26QInternalMessageLogContext"_L1))
1507 return true;
1508 return false;
1509 };
1510
1511 auto demangled = [](auto &function) -> QString {
1512 if (!function.startsWith("_Z"_L1))
1513 return function;
1514
1515 // we optimize for the case where __cxa_demangle succeeds
1516 auto fn = [&]() {
1517 if constexpr (sizeof(function.at(0)) == 1)
1518 return function.data(); // -> const char *
1519 else
1520 return std::move(function).toUtf8(); // -> QByteArray
1521 }();
1522 auto cleanup = [](auto *p) { free(p); };
1523 using Ptr = std::unique_ptr<char, decltype(cleanup)>;
1524 auto demangled = Ptr(abi::__cxa_demangle(fn, nullptr, nullptr, nullptr), cleanup);
1525
1526 if (demangled)
1527 return QString::fromUtf8(qCleanupFuncinfo(demangled.get()));
1528 else
1529 return QString::fromUtf8(fn); // restore
1530 };
1531
1532# if QT_CONFIG(dladdr)
1533 // use dladdr() instead of backtrace_symbols()
1534 QString cachedLibrary;
1535 const char *cachedFname = nullptr;
1536 auto decodeFrame = [&](void *addr) -> DecodedFrame {
1537 Dl_info info;
1538 if (!dladdr(addr, &info))
1539 return {};
1540
1541 // These are actually UTF-8, so we'll correct below
1542 QLatin1StringView fn(info.dli_sname);
1543 QLatin1StringView lib;
1544 if (const char *lastSlash = strrchr(info.dli_fname, '/'))
1545 lib = QLatin1StringView(lastSlash + 1);
1546 else
1547 lib = QLatin1StringView(info.dli_fname);
1548
1549 if (shouldSkipFrame(lib, fn))
1550 return {};
1551
1552 QString function = demangled(fn);
1553 if (lib.data() != cachedFname) {
1554 cachedFname = lib.data();
1555 cachedLibrary = QString::fromUtf8(cachedFname, lib.size());
1556 }
1557 return { cachedLibrary, function };
1558 };
1559# else
1560 // The results of backtrace_symbols looks like this:
1561 // /lib/libc.so.6(__libc_start_main+0xf3) [0x4a937413]
1562 // The offset and function name are optional.
1563 // This regexp tries to extract the library name (without the path) and the function name.
1564 // This code is protected by QMessagePattern::mutex so it is thread safe on all compilers
1565 static const QRegularExpression rx(QStringLiteral("^(?:[^(]*/)?([^(/]+)\\‍(([^+]*)(?:[\\+[a-f0-9x]*)?\\‍) \\‍[[a-f0-9x]*\\‍]$"));
1566
1567 auto decodeFrame = [&](void *&addr) -> DecodedFrame {
1568 auto cleanup = [](auto *p) { free(p); };
1569 auto strings =
1570 std::unique_ptr<char *, decltype(cleanup)>(backtrace_symbols(&addr, 1), cleanup);
1571 QString trace = QString::fromUtf8(strings.get()[0]);
1572 QRegularExpressionMatch m = rx.match(trace);
1573 if (!m.hasMatch())
1574 return {};
1575
1576 QString library = m.captured(1);
1577 QString function = m.captured(2);
1578
1579 // skip the trace from QtCore that are because of the qDebug itself
1580 if (shouldSkipFrame(library, function))
1581 return {};
1582
1583 function = demangled(function);
1584 return { library, function };
1585 };
1586# endif
1587
1588 for (void *const &addr : buffer) {
1589 DecodedFrame frame = decodeFrame(addr);
1590 if (!frame.library.isEmpty()) {
1591 if (frame.function.isEmpty())
1592 result.append(u'?' + frame.library + u'?');
1593 else
1594 result.append(frame.function);
1595 } else {
1596 // innermost, unknown frames are usually the logging framework itself
1597 if (!result.isEmpty())
1598 result.append(QStringLiteral("???"));
1599 }
1600
1601 if (result.size() == frameCount)
1602 break;
1603 }
1604 return result;
1605}
1606#else
1607#error "Internal error: backtrace enabled, but no way to gather backtraces available"
1608#endif // QLOGGING_USE_..._BACKTRACE
1609
1610/*
1611 Always call with QMessagePattern::maxBacktraceDepth to populate the maximum
1612 possible backtrace
1613*/
1614static QStringList generateBacktraceFrames(int frameCount, const QMessageLogContext &ctx)
1615{
1616 // do we have a backtrace stored?
1617 if (ctx.version <= QMessageLogContext::CurrentVersion)
1618 return {};
1619
1620 auto &fullctx = static_cast<const QInternalMessageLogContext &>(ctx);
1621 if (!fullctx.backtrace.has_value())
1622 return {};
1623
1624 QStringList frames = backtraceFramesForLogMessage(frameCount, *fullctx.backtrace);
1625 if (frames.isEmpty())
1626 return {};
1627
1628 // if the first frame is unknown, replace it with the context function
1629 if (ctx.function && frames.at(0).startsWith(u'?'))
1630 frames[0] = QString::fromUtf8(qCleanupFuncinfo(ctx.function));
1631
1632 return frames;
1633}
1634
1635static QString formatBacktraceForLogMessage(const QMessagePattern::BacktraceParams backtraceParams,
1636 const QStringList &backtrace)
1637{
1638 if (backtrace.isEmpty() || backtraceParams.backtraceDepth <= 0)
1639 return {};
1640
1641 const qsizetype backtraceDepth = (std::min)(qsizetype(backtraceParams.backtraceDepth),
1642 backtrace.size());
1643
1644 // hand-rolled qJoin(), because we want it in 6.8
1645 QString result;
1646 for (auto it = backtrace.cbegin(); it != backtrace.cbegin() + backtraceDepth; ++it) {
1647 if (it != backtrace.cbegin())
1648 result += backtraceParams.backtraceSeparator;
1649 result += *it;
1650 }
1651 return result;
1652}
1653#else
1655{
1656 // initFrom() returns 0 to our caller, so we should never get here
1657 Q_UNREACHABLE();
1658}
1659#endif // !QLOGGING_HAVE_BACKTRACE
1660
1661Q_GLOBAL_STATIC(QMessagePattern, qMessagePattern)
1662
1663/*!
1664 \relates <QtLogging>
1665 \since 5.4
1666
1667 Generates a formatted string out of the \a type, \a context, \a str arguments.
1668
1669 qFormatLogMessage returns a QString that is formatted according to the current message pattern.
1670 It can be used by custom message handlers to format output similar to Qt's default message
1671 handler.
1672
1673 The function is thread-safe.
1674
1675 \sa qInstallMessageHandler(), qSetMessagePattern()
1676 */
1677QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &str)
1678{
1679 return formatLogMessage(type, context, str);
1680}
1681
1682// Separate function so the default message handler can bypass the public,
1683// exported function above. Static functions can't get added to the dynamic
1684// symbol tables, so they never show up in backtrace_symbols() or equivalent.
1685static QString formatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &str)
1686{
1687 QString message;
1688
1689 const auto locker = qt_scoped_lock(QMessagePattern::mutex);
1690
1691 QMessagePattern *pattern = qMessagePattern();
1692 if (!pattern) {
1693 // after destruction of static QMessagePattern instance
1694 message.append(str);
1695 return message;
1696 }
1697
1698 bool skip = false;
1699
1700 qsizetype timeArgsIdx = 0;
1701#ifdef QLOGGING_HAVE_BACKTRACE
1702 qsizetype backtraceArgsIdx = 0;
1703 QStringList fullBacktrace;
1704#endif
1705
1706 // we do not convert file, function, line literals to local encoding due to overhead
1707 for (qsizetype i = 0; pattern->tokens[i]; ++i) {
1708 const char *token = pattern->tokens[i];
1709 if (token == endifTokenC) {
1710 skip = false;
1711 } else if (skip) {
1712 // we skip adding messages, but we have to iterate over
1713 // timeArgsIdx and backtraceArgsIdx anyway
1714 if (token == timeTokenC)
1715 timeArgsIdx++;
1716#ifdef QLOGGING_HAVE_BACKTRACE
1717 else if (token == backtraceTokenC)
1718 backtraceArgsIdx++;
1719#endif
1720 } else if (token == messageTokenC) {
1721 message.append(str);
1722 } else if (token == categoryTokenC) {
1723 message.append(QUtf8StringView(context.category));
1724 } else if (token == typeTokenC) {
1725 switch (type) {
1726 case QtDebugMsg: message.append("debug"_L1); break;
1727 case QtInfoMsg: message.append("info"_L1); break;
1728 case QtWarningMsg: message.append("warning"_L1); break;
1729 case QtCriticalMsg:message.append("critical"_L1); break;
1730 case QtFatalMsg: message.append("fatal"_L1); break;
1731 }
1732 } else if (token == fileTokenC) {
1733 if (context.file)
1734 message.append(QUtf8StringView(context.file));
1735 else
1736 message.append("unknown"_L1);
1737 } else if (token == lineTokenC) {
1738 message.append(QString::number(context.line));
1739 } else if (token == functionTokenC) {
1740 if (context.function)
1741 message.append(QString::fromLatin1(qCleanupFuncinfo(context.function)));
1742 else
1743 message.append("unknown"_L1);
1744 } else if (token == pidTokenC) {
1745 message.append(QString::number(QCoreApplication::applicationPid()));
1746 } else if (token == appnameTokenC) {
1747 message.append(QCoreApplication::applicationName());
1748 } else if (token == threadidTokenC) {
1749 // print the TID as decimal
1750 message.append(QString::number(qt_gettid()));
1751 } else if (token == threadnameTokenC) {
1752 if (!qt_append_thread_name_to(message))
1753 message.append(QString::number(qt_gettid())); // fallback to the TID
1754 } else if (token == qthreadptrTokenC) {
1755 message.append("0x"_L1);
1756 message.append(QString::number(qlonglong(QThread::currentThread()->currentThread()), 16));
1757#ifdef QLOGGING_HAVE_BACKTRACE
1758 } else if (token == backtraceTokenC) {
1759 if (fullBacktrace.isEmpty())
1760 fullBacktrace = generateBacktraceFrames(pattern->maxBacktraceDepth, context);
1761 QMessagePattern::BacktraceParams backtraceParams = pattern->backtraceArgs.at(backtraceArgsIdx);
1762 backtraceArgsIdx++;
1763 message.append(formatBacktraceForLogMessage(backtraceParams, fullBacktrace));
1764#endif
1765 } else if (token == timeTokenC) {
1766 using namespace std::chrono;
1767 auto formatElapsedTime = [](steady_clock::duration time) {
1768 // we assume time > 0
1769 auto ms = duration_cast<milliseconds>(time);
1770 auto sec = duration_cast<seconds>(ms);
1771 ms -= sec;
1772 return QString::asprintf("%6lld.%03u", qint64(sec.count()), uint(ms.count()));
1773 };
1774 QString timeFormat = pattern->timeArgs.at(timeArgsIdx);
1775 timeArgsIdx++;
1776 if (timeFormat == "process"_L1) {
1777 message += formatElapsedTime(steady_clock::now() - pattern->appStartTime);
1778 } else if (timeFormat == "boot"_L1) {
1779 // just print the milliseconds since the elapsed timer reference
1780 // like the Linux kernel does
1781 message += formatElapsedTime(steady_clock::now().time_since_epoch());
1782#if QT_CONFIG(datestring)
1783 } else if (timeFormat.isEmpty()) {
1784 message.append(QDateTime::currentDateTime().toString(Qt::ISODate));
1785 } else {
1786 message.append(QDateTime::currentDateTime().toString(timeFormat));
1787#endif // QT_CONFIG(datestring)
1788 }
1789 } else if (token == ifCategoryTokenC) {
1791 skip = true;
1792#define HANDLE_IF_TOKEN(LEVEL)
1793 } else if (token == if##LEVEL##TokenC) {
1794 skip = type != Qt##LEVEL##Msg;
1795 HANDLE_IF_TOKEN(Debug)
1796 HANDLE_IF_TOKEN(Info)
1797 HANDLE_IF_TOKEN(Warning)
1798 HANDLE_IF_TOKEN(Critical)
1799 HANDLE_IF_TOKEN(Fatal)
1800#undef HANDLE_IF_TOKEN
1801 } else {
1802 message.append(QUtf8StringView(token));
1803 }
1804 }
1805 return message;
1806}
1807
1808static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &buf);
1809
1810// pointer to QtMessageHandler debug handler (with context)
1811Q_CONSTINIT static QBasicAtomicPointer<void (QtMsgType, const QMessageLogContext &, const QString &)> messageHandler = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
1812
1813// ------------------------ Alternate logging sinks -------------------------
1814
1815#if QT_CONFIG(slog2)
1816#ifndef QT_LOG_CODE
1817#define QT_LOG_CODE 9000
1818#endif
1819
1820static bool slog2_default_handler(QtMsgType type, const QMessageLogContext &,
1821 const QString &message)
1822{
1823 if (shouldLogToStderr())
1824 return false; // Leave logging up to stderr handler
1825
1826 QString formattedMessage = message;
1827 formattedMessage.append(u'\n');
1828 if (slog2_set_default_buffer((slog2_buffer_t)-1) == 0) {
1829 slog2_buffer_set_config_t buffer_config;
1830 slog2_buffer_t buffer_handle;
1831
1832 buffer_config.buffer_set_name = __progname;
1833 buffer_config.num_buffers = 1;
1834 buffer_config.verbosity_level = SLOG2_DEBUG1;
1835 buffer_config.buffer_config[0].buffer_name = "default";
1836 buffer_config.buffer_config[0].num_pages = 8;
1837
1838 if (slog2_register(&buffer_config, &buffer_handle, 0) == -1) {
1839 fprintf(stderr, "Error registering slogger2 buffer!\n");
1840 return false;
1841 }
1842
1843 // Set as the default buffer
1844 slog2_set_default_buffer(buffer_handle);
1845 }
1846 int severity = SLOG2_INFO;
1847 //Determines the severity level
1848 switch (type) {
1849 case QtDebugMsg:
1850 severity = SLOG2_DEBUG1;
1851 break;
1852 case QtInfoMsg:
1853 severity = SLOG2_INFO;
1854 break;
1855 case QtWarningMsg:
1856 severity = SLOG2_NOTICE;
1857 break;
1858 case QtCriticalMsg:
1859 severity = SLOG2_WARNING;
1860 break;
1861 case QtFatalMsg:
1862 severity = SLOG2_ERROR;
1863 break;
1864 }
1865 //writes to the slog2 buffer
1866 slog2c(NULL, QT_LOG_CODE, severity, formattedMessage.toLocal8Bit().constData());
1867
1868 return true; // Prevent further output to stderr
1869}
1870#endif // slog2
1871
1872#if QT_CONFIG(journald)
1873static bool systemd_default_message_handler(QtMsgType type,
1874 const QMessageLogContext &context,
1875 const QString &message)
1876{
1877 if (shouldLogToStderr())
1878 return false; // Leave logging up to stderr handler
1879
1880 int priority = LOG_INFO; // Informational
1881 switch (type) {
1882 case QtDebugMsg:
1883 priority = LOG_DEBUG; // Debug-level messages
1884 break;
1885 case QtInfoMsg:
1886 priority = LOG_INFO; // Informational conditions
1887 break;
1888 case QtWarningMsg:
1889 priority = LOG_WARNING; // Warning conditions
1890 break;
1891 case QtCriticalMsg:
1892 priority = LOG_CRIT; // Critical conditions
1893 break;
1894 case QtFatalMsg:
1895 priority = LOG_ALERT; // Action must be taken immediately
1896 break;
1897 }
1898
1899 // Explicit QByteArray instead of auto, to resolve the QStringBuilder proxy
1900 const QByteArray messageField = "MESSAGE="_ba + message.toUtf8();
1901 const QByteArray priorityField = "PRIORITY="_ba + QByteArray::number(priority);
1902 const QByteArray tidField = "TID="_ba + QByteArray::number(qlonglong(qt_gettid()));
1903 const QByteArray fileField = context.file
1904 ? "CODE_FILE="_ba + context.file : QByteArray();
1905 const QByteArray funcField = context.function
1906 ? "CODE_FUNC="_ba + context.function : QByteArray();
1907 const QByteArray lineField = context.line
1908 ? "CODE_LINE="_ba + QByteArray::number(context.line) : QByteArray();
1909 const QByteArray categoryField = context.category
1910 ? "QT_CATEGORY="_ba + context.category : QByteArray();
1911
1912 auto toIovec = [](const QByteArray &ba) {
1913 return iovec{ const_cast<char*>(ba.data()), size_t(ba.size()) };
1914 };
1915
1916 struct iovec fields[7] = {
1917 toIovec(messageField),
1918 toIovec(priorityField),
1919 toIovec(tidField),
1920 };
1921 int nFields = 3;
1922 if (context.file)
1923 fields[nFields++] = toIovec(fileField);
1924 if (context.function)
1925 fields[nFields++] = toIovec(funcField);
1926 if (context.line)
1927 fields[nFields++] = toIovec(lineField);
1928 if (context.category)
1929 fields[nFields++] = toIovec(categoryField);
1930
1931 sd_journal_sendv(fields, nFields);
1932
1933 return true; // Prevent further output to stderr
1934}
1935#endif
1936
1937#if QT_CONFIG(syslog)
1938static bool syslog_default_message_handler(QtMsgType type, const QMessageLogContext &context,
1939 const QString &formattedMessage)
1940{
1941 if (shouldLogToStderr())
1942 return false; // Leave logging up to stderr handler
1943
1944 int priority = LOG_INFO; // Informational
1945 switch (type) {
1946 case QtDebugMsg:
1947 priority = LOG_DEBUG; // Debug-level messages
1948 break;
1949 case QtInfoMsg:
1950 priority = LOG_INFO; // Informational conditions
1951 break;
1952 case QtWarningMsg:
1953 priority = LOG_WARNING; // Warning conditions
1954 break;
1955 case QtCriticalMsg:
1956 priority = LOG_CRIT; // Critical conditions
1957 break;
1958 case QtFatalMsg:
1959 priority = LOG_ALERT; // Action must be taken immediately
1960 break;
1961 }
1962
1963 syslog(priority, "%s", formattedMessage.toUtf8().constData());
1964
1965 return true; // Prevent further output to stderr
1966}
1967#endif
1968
1969#ifdef Q_OS_ANDROID
1970static bool android_default_message_handler(QtMsgType type,
1971 const QMessageLogContext &context,
1972 const QString &formattedMessage)
1973{
1974 if (shouldLogToStderr())
1975 return false; // Leave logging up to stderr handler
1976
1977 android_LogPriority priority = ANDROID_LOG_DEBUG;
1978 switch (type) {
1979 case QtDebugMsg:
1980 priority = ANDROID_LOG_DEBUG;
1981 break;
1982 case QtInfoMsg:
1983 priority = ANDROID_LOG_INFO;
1984 break;
1985 case QtWarningMsg:
1986 priority = ANDROID_LOG_WARN;
1987 break;
1988 case QtCriticalMsg:
1989 priority = ANDROID_LOG_ERROR;
1990 break;
1991 case QtFatalMsg:
1992 priority = ANDROID_LOG_FATAL;
1993 break;
1994 };
1995
1996 QMessagePattern *pattern = qMessagePattern();
1997 const QString tag = (pattern && pattern->containsToken(categoryTokenC))
1998 // If application name is a tag ensure it has no spaces
1999 ? QCoreApplication::applicationName().replace(u' ', u'_')
2000 : QString::fromUtf8(context.category);
2001 __android_log_print(priority, qPrintable(tag), "%s\n", qPrintable(formattedMessage));
2002
2003 return true; // Prevent further output to stderr
2004}
2005#endif //Q_OS_ANDROID
2006
2007#if defined(Q_OS_OHOS)
2008static bool ohos_default_message_handler(QtMsgType type,
2009 const QMessageLogContext &context,
2010 const QString &message)
2011{
2012 QString formattedMessage = qFormatLogMessage(type, context, message);
2013
2014 LogLevel priority = LOG_DEBUG;
2015 switch (type) {
2016 case QtDebugMsg: priority = LOG_DEBUG; break;
2017 case QtInfoMsg: priority = LOG_INFO; break;
2018 case QtWarningMsg: priority = LOG_WARN; break;
2019 case QtCriticalMsg: priority = LOG_ERROR; break;
2020 case QtFatalMsg: priority = LOG_FATAL; break;
2021 };
2022
2023 qOhosLogMessage(priority, qPrintable(QCoreApplication::applicationName()), qPrintable(formattedMessage));
2024
2025 return true; // Prevent further output to stderr
2026}
2027#endif //Q_OS_OHOS
2028
2029#ifdef Q_OS_WIN
2030static void win_outputDebugString_helper(const QString &message)
2031{
2032 const qsizetype maxOutputStringLength = 32766;
2033 Q_CONSTINIT static QBasicMutex m;
2034 const auto locker = qt_scoped_lock(m);
2035
2036 // fast path: Avoid string copies if one output is enough
2037 if (message.length() <= maxOutputStringLength) {
2038 OutputDebugString(reinterpret_cast<const wchar_t *>(message.utf16()));
2039 } else {
2040 wchar_t *messagePart = new wchar_t[maxOutputStringLength + 1];
2041 for (qsizetype i = 0; i < message.length(); i += maxOutputStringLength) {
2042 const qsizetype length = qMin(message.length() - i, maxOutputStringLength);
2043 const qsizetype len = QStringView{message}.mid(i, length).toWCharArray(messagePart);
2044 Q_ASSERT(len == length);
2045 messagePart[len] = 0;
2046 OutputDebugString(messagePart);
2047 }
2048 delete[] messagePart;
2049 }
2050}
2051
2052static bool win_message_handler(QtMsgType, const QMessageLogContext &,
2053 const QString &formattedMessage)
2054{
2055 if (shouldLogToStderr())
2056 return false; // Leave logging up to stderr handler
2057
2058 win_outputDebugString_helper(formattedMessage + u'\n');
2059
2060 return true; // Prevent further output to stderr
2061}
2062#endif
2063
2064#ifdef Q_OS_WASM
2065static bool wasm_default_message_handler(QtMsgType type,
2066 const QMessageLogContext &,
2067 const QString &formattedMessage)
2068{
2069 static bool forceStderrLogging = qEnvironmentVariableIntValue("QT_FORCE_STDERR_LOGGING");
2070 if (forceStderrLogging)
2071 return false;
2072
2073 int emOutputFlags = EM_LOG_CONSOLE;
2074 QByteArray localMsg = formattedMessage.toLocal8Bit();
2075 switch (type) {
2076 case QtDebugMsg:
2077 break;
2078 case QtInfoMsg:
2079 break;
2080 case QtWarningMsg:
2081 emOutputFlags |= EM_LOG_WARN;
2082 break;
2083 case QtCriticalMsg:
2084 emOutputFlags |= EM_LOG_ERROR;
2085 break;
2086 case QtFatalMsg:
2087 emOutputFlags |= EM_LOG_ERROR;
2088 }
2089 emscripten_log(emOutputFlags, "%s\n", qPrintable(formattedMessage));
2090
2091 return true; // Prevent further output to stderr
2092}
2093#endif
2094
2095// --------------------------------------------------------------------------
2096
2097static void stderr_message_handler(QtMsgType type, const QMessageLogContext &context,
2098 const QString &formattedMessage)
2099{
2100 Q_UNUSED(type);
2101 Q_UNUSED(context);
2102
2103 // print nothing if message pattern didn't apply / was empty.
2104 // (still print empty lines, e.g. because message itself was empty)
2105 if (formattedMessage.isNull())
2106 return;
2107 const QByteArray msg = formattedMessage.toLocal8Bit() + '\n';
2108 fwrite(msg.constData(), 1, msg.size(), stderr);
2109 fflush(stderr);
2110}
2111
2112namespace {
2113struct SystemMessageSink
2114{
2115 using Fn = bool(QtMsgType, const QMessageLogContext &, const QString &);
2116 Fn *sink;
2117 bool messageIsUnformatted = false;
2118};
2119}
2120
2121static constexpr SystemMessageSink systemMessageSink = {
2122#if defined(Q_OS_WIN)
2123 win_message_handler
2124#elif QT_CONFIG(slog2)
2125 slog2_default_handler
2126#elif QT_CONFIG(journald)
2127 systemd_default_message_handler, true
2128#elif QT_CONFIG(syslog)
2129 syslog_default_message_handler
2130#elif defined(Q_OS_ANDROID)
2131 android_default_message_handler
2132# elif defined(Q_OS_OHOS)
2133 ohos_default_message_handler
2134#elif defined(QT_USE_APPLE_UNIFIED_LOGGING)
2135 AppleUnifiedLogger::messageHandler, true
2136#elif defined Q_OS_WASM
2137 wasm_default_message_handler
2138#else
2139 nullptr
2140#endif
2141};
2142
2144 const QString &formattedMessage)
2145{
2146 if (!systemMessageSink.messageIsUnformatted) {
2147QT_WARNING_PUSH
2148QT_WARNING_DISABLE_GCC("-Waddress") // "the address of ~~ will never be NULL
2149 if (systemMessageSink.sink && systemMessageSink.sink(type, context, formattedMessage))
2150 return;
2151QT_WARNING_POP
2152 }
2153
2154 stderr_message_handler(type, context, formattedMessage);
2155}
2156
2157/*!
2158 \internal
2159*/
2160static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context,
2161 const QString &message)
2162{
2163 // A message sink logs the message to a structured or unstructured destination,
2164 // optionally formatting the message if the latter, and returns true if the sink
2165 // handled stderr output as well, which will shortcut our default stderr output.
2166
2167 if (systemMessageSink.messageIsUnformatted) {
2168 if (systemMessageSink.sink(type, context, message))
2169 return;
2170 }
2171
2172 preformattedMessageHandler(type, context, formatLogMessage(type, context, message));
2173}
2174
2175Q_CONSTINIT static thread_local bool msgHandlerGrabbed = false;
2176
2178{
2180 return false;
2181
2182 msgHandlerGrabbed = true;
2183 return true;
2184}
2185
2187{
2188 msgHandlerGrabbed = false;
2189}
2190
2191static void qt_message_print(QtMsgType msgType, const QMessageLogContext &context, const QString &message)
2192{
2193 Q_TRACE(qt_message_print, msgType, context.category, context.function, context.file, context.line, message);
2194
2195 // qDebug, qWarning, ... macros do not check whether category is enabledgc
2196 if (msgType != QtFatalMsg && isDefaultCategory(context.category)) {
2197 if (QLoggingCategory *defaultCategory = QLoggingCategory::defaultCategory()) {
2198 if (!defaultCategory->isEnabled(msgType))
2199 return;
2200 }
2201 }
2202
2203 // prevent recursion in case the message handler generates messages
2204 // itself, e.g. by using Qt API
2206 const auto ungrab = qScopeGuard([]{ ungrabMessageHandler(); });
2207 auto msgHandler = messageHandler.loadAcquire();
2208 (msgHandler ? msgHandler : qDefaultMessageHandler)(msgType, context, message);
2209 } else {
2210 stderr_message_handler(msgType, context, message);
2211 }
2212}
2213
2214template <typename String> static void
2215qt_maybe_message_fatal(QtMsgType msgType, const QMessageLogContext &context, String &&message)
2216{
2217 if (!isFatal(msgType))
2218 return;
2219#if defined(Q_CC_MSVC_ONLY) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR)
2220 wchar_t contextFileL[256];
2221 // we probably should let the compiler do this for us, by declaring QMessageLogContext::file to
2222 // be const wchar_t * in the first place, but the #ifdefery above is very complex and we
2223 // wouldn't be able to change it later on...
2224 convert_to_wchar_t_elided(contextFileL, sizeof contextFileL / sizeof *contextFileL,
2225 context.file);
2226 // get the current report mode
2227 int reportMode = _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW);
2228 _CrtSetReportMode(_CRT_ERROR, reportMode);
2229
2230 int ret = _CrtDbgReportW(_CRT_ERROR, contextFileL, context.line, _CRT_WIDE(QT_VERSION_STR),
2231 reinterpret_cast<const wchar_t *>(message.utf16()));
2232 if ((ret == 0) && (reportMode & _CRTDBG_MODE_WNDW))
2233 return; // ignore
2234 else if (ret == 1)
2235 _CrtDbgBreak();
2236#else
2237 Q_UNUSED(context);
2238#endif
2239
2240 if constexpr (std::is_class_v<String> && !std::is_const_v<String>)
2241 message.clear();
2242 else
2243 Q_UNUSED(message);
2244 qAbort();
2245}
2246
2247/*!
2248 \internal
2249*/
2250void qt_message_output(QtMsgType msgType, const QMessageLogContext &context, const QString &message)
2251{
2252 QInternalMessageLogContext ctx(context);
2253 qt_message_print(msgType, ctx, message);
2254 qt_maybe_message_fatal(msgType, ctx, message);
2255}
2256
2257void qErrnoWarning(const char *msg, ...)
2258{
2259 // qt_error_string() will allocate anyway, so we don't have
2260 // to be careful here (like we do in plain qWarning())
2261 QString error_string = qt_error_string(-1); // before vasprintf changes errno/GetLastError()
2262
2263 va_list ap;
2264 va_start(ap, msg);
2265 QString buf = QString::vasprintf(msg, ap);
2266 va_end(ap);
2267
2268 buf += " ("_L1 + error_string + u')';
2270 qt_message_output(QtWarningMsg, context, buf);
2271}
2272
2273void qErrnoWarning(int code, const char *msg, ...)
2274{
2275 // qt_error_string() will allocate anyway, so we don't have
2276 // to be careful here (like we do in plain qWarning())
2277 va_list ap;
2278 va_start(ap, msg);
2279 QString buf = QString::vasprintf(msg, ap);
2280 va_end(ap);
2281
2282 buf += " ("_L1 + qt_error_string(code) + u')';
2284 qt_message_output(QtWarningMsg, context, buf);
2285}
2286
2287/*!
2288 \typedef QtMessageHandler
2289 \relates <QtLogging>
2290 \since 5.0
2291
2292 This is a typedef for a pointer to a function with the following
2293 signature:
2294
2295 \snippet code/src_corelib_global_qglobal.cpp 49
2296
2297 \sa QtMsgType, qInstallMessageHandler()
2298*/
2299
2300/*!
2301 \fn QtMessageHandler qInstallMessageHandler(QtMessageHandler handler)
2302 \relates <QtLogging>
2303 \since 5.0
2304
2305 Installs a Qt message \a handler.
2306 Returns a pointer to the previously installed message handler.
2307
2308 A message handler is a function that prints out debug, info,
2309 warning, critical, and fatal messages from Qt's logging infrastructure.
2310 By default, Qt uses a standard message handler that formats and
2311 prints messages to different sinks specific to the operating system
2312 and Qt configuration. Installing your own message handler allows you
2313 to assume full control, and for instance log messages to the
2314 file system.
2315
2316 Note that Qt supports \l{QLoggingCategory}{logging categories} for
2317 grouping related messages in semantic categories. You can use these
2318 to enable or disable logging per category and \l{QtMsgType}{message type}.
2319 As the filtering for logging categories is done even before a message
2320 is created, messages for disabled types and categories will not reach
2321 the message handler.
2322
2323 A message handler needs to be
2324 \l{Reentrancy and Thread-Safety}{reentrant}. That is, it might be called
2325 from different threads, in parallel. Therefore, writes to common sinks
2326 (like a database, or a file) often need to be synchronized.
2327
2328 Qt allows to enrich logging messages with further meta-information
2329 by calling \l qSetMessagePattern(), or setting the \c QT_MESSAGE_PATTERN
2330 environment variable. To keep this formatting, a custom message handler
2331 can use \l qFormatLogMessage().
2332
2333 Try to keep the code in the message handler itself minimal, as expensive
2334 operations might block the application. Also, to avoid recursion, any
2335 logging messages generated in the message handler itself will be ignored.
2336
2337 The message handler should always return. For
2338 \l{QtFatalMsg}{fatal messages}, the application aborts immediately after
2339 handling that message.
2340
2341 Only one message handler can be installed at a time, for the whole application.
2342 If there was a previous custom message handler installed,
2343 the function will return a pointer to it. This handler can then
2344 be later reinstalled by another call to the method. Also, calling
2345 \c qInstallMessageHandler(nullptr) will restore the default
2346 message handler.
2347
2348 Here is an example of a message handler that logs to a local file
2349 before calling the default handler:
2350
2351 \snippet code/src_corelib_global_qglobal_widgets.cpp 2
2352
2353 Note that the C++ standard guarantees that \c{static FILE *f} is
2354 initialized in a thread-safe way. We can also expect \c{fprintf()}
2355 and \c{fflush()} to be thread-safe, so no further synchronization
2356 is necessary.
2357
2358 \sa QtMessageHandler, QtMsgType, qDebug(), qInfo(), qWarning(), qCritical(), qFatal(),
2359 {Debugging Techniques}, qFormatLogMessage()
2360*/
2361
2362/*!
2363 \fn void qSetMessagePattern(const QString &pattern)
2364 \relates <QtLogging>
2365 \since 5.0
2366
2367 \brief Changes the output of the default message handler.
2368
2369 Allows to tweak the output of qDebug(), qInfo(), qWarning(), qCritical(),
2370 and qFatal(). The category logging output of qCDebug(), qCInfo(),
2371 qCWarning(), and qCCritical() is formatted, too.
2372
2373 Following placeholders are supported:
2374
2375 \table
2376 \header \li Placeholder \li Description
2377 \row \li \c %{appname} \li QCoreApplication::applicationName()
2378 \row \li \c %{category} \li Logging category
2379 \row \li \c %{file} \li Path to source file
2380 \row \li \c %{function} \li Function
2381 \row \li \c %{line} \li Line in source file
2382 \row \li \c %{message} \li The actual message
2383 \row \li \c %{pid} \li QCoreApplication::applicationPid()
2384 \row \li \c %{threadid} \li The system-wide ID of current thread (if it can be obtained)
2385 \row \li \c %{threadname} \li The current thread name (if it can be obtained, or the thread ID, since Qt 6.10)
2386 \row \li \c %{qthreadptr} \li A pointer to the current QThread (result of QThread::currentThread())
2387 \row \li \c %{type} \li "debug", "warning", "critical" or "fatal"
2388 \row \li \c %{time process} \li time of the message, in seconds since the process started (the token "process" is literal)
2389 \row \li \c %{time boot} \li the time of the message, in seconds since the system boot if that
2390 can be determined (the token "boot" is literal). If the time since boot could not be obtained,
2391 the output is indeterminate (see QElapsedTimer::msecsSinceReference()).
2392 \row \li \c %{time [format]} \li system time when the message occurred, formatted by
2393 passing the \c format to \l QDateTime::toString(). If the format is
2394 not specified, the format of Qt::ISODate is used.
2395 \row \li \c{%{backtrace [depth=N] [separator="..."]}} \li A backtrace with the number of frames
2396 specified by the optional \c depth parameter (defaults to 5), and separated by the optional
2397 \c separator parameter (defaults to "|"). Starting from Qt 6.12, the maximum \c depth is
2398 limited to 16384 frames.
2399
2400 This expansion is available only on some platforms:
2401
2402 \list
2403 \li platforms using glibc;
2404 \li platforms shipping C++23's \c{<stacktrace>} header (requires compiling Qt in C++23 mode).
2405 \endlist
2406
2407 Depending on the platform, there are some restrictions on the function
2408 names printed by this expansion.
2409
2410 On some platforms,
2411 names are only known for exported functions. If you want to see the name of every function
2412 in your application, make sure your application is compiled and linked with \c{-rdynamic},
2413 or an equivalent of it.
2414
2415 When reading backtraces, take into account that frames might be missing due to inlining or
2416 tail call optimization.
2417 \endtable
2418
2419 You can also use conditionals on the type of the message using \c %{if-debug}, \c %{if-info}
2420 \c %{if-warning}, \c %{if-critical} or \c %{if-fatal} followed by an \c %{endif}.
2421 What is inside the \c %{if-*} and \c %{endif} will only be printed if the type matches.
2422
2423 Finally, text inside \c %{if-category} ... \c %{endif} is only printed if the category
2424 is not the default one.
2425
2426 Example:
2427 \snippet code/src_corelib_global_qlogging.cpp 0
2428
2429 The default \a pattern is \c{%{if-category}%{category}: %{endif}%{message}}.
2430
2431 \note On Android, the default \a pattern is \c{%{message}} because the category is used as
2432 \l{Android: log_print}{tag} since Android logcat has a dedicated field for the logging
2433 categories, see \l{Android: Log}{Android Logging}. If a custom \a pattern including the
2434 category is used, QCoreApplication::applicationName() is used as \l{Android: log_print}{tag}.
2435
2436 The \a pattern can also be changed at runtime by setting the QT_MESSAGE_PATTERN
2437 environment variable; if both \l qSetMessagePattern() is called and QT_MESSAGE_PATTERN is
2438 set, the environment variable takes precedence.
2439
2440 \note The information for the placeholders \c category, \c file, \c function and \c line is
2441 only recorded in debug builds. Alternatively, \c QT_MESSAGELOGCONTEXT can be defined
2442 explicitly. For more information refer to the QMessageLogContext documentation.
2443
2444 \note The message pattern only applies to unstructured logging, such as the default
2445 \c stderr output. Structured logging such as systemd will record the message as is,
2446 along with as much structured information as can be captured.
2447
2448 Custom message handlers can use qFormatLogMessage() to take \a pattern into account.
2449
2450 \section2 Security Considerations
2451
2452 Qt does not strip or escape control characters from \a pattern -
2453 including \c {LF}, \c {CR}, \c {NUL} bytes, and terminal control sequences.
2454 Accepting a pattern from an untrusted source therefore enables log forging
2455 or sending control sequences to the consumer of the log stream. In addition,
2456 on some logging backends, messages may be truncated at the \c {NUL} byte, if any.
2457
2458
2459 \sa qInstallMessageHandler(), {Debugging Techniques}, {QLoggingCategory}, QMessageLogContext
2460 */
2461
2463{
2464 const auto old = messageHandler.fetchAndStoreOrdered(h);
2465 if (old)
2466 return old;
2467 else
2469}
2470
2471void qSetMessagePattern(const QString &pattern)
2472{
2473 const auto locker = qt_scoped_lock(QMessagePattern::mutex);
2474
2475 if (!qMessagePattern()->fromEnvironment)
2476 qMessagePattern()->setPattern(pattern);
2477}
2478
2480 const QMessageLogContext &logContext) noexcept
2481{
2482 if (logContext.version == self->version) {
2483 auto other = static_cast<const QInternalMessageLogContext *>(&logContext);
2484 self->backtrace = other->backtrace;
2485 }
2486}
2487
2488/*!
2489 \internal
2490 Copies context information from \a logContext into this QMessageLogContext.
2491 Returns the number of backtrace frames that are desired.
2492*/
2494{
2495 version = CurrentVersion + 1;
2496 copyContextFrom(logContext);
2497
2498#ifdef QLOGGING_HAVE_BACKTRACE
2499 if (backtrace.has_value())
2500 return 0; // we have a stored backtrace, no need to get it again
2501
2502 // initializes the message pattern, if needed
2503 if (auto pattern = qMessagePattern())
2504 return pattern->maxBacktraceDepth;
2505#endif
2506
2507 return 0;
2508}
2509
2510/*!
2511 Copies context information from \a logContext into this QMessageLogContext.
2512 Returns a reference to this object.
2513
2514 Note that the version is \b not copied, only the context information.
2515
2516 \internal
2517*/
2518QMessageLogContext &QMessageLogContext::copyContextFrom(const QMessageLogContext &logContext) noexcept
2519{
2520 this->category = logContext.category;
2521 this->file = logContext.file;
2522 this->line = logContext.line;
2523 this->function = logContext.function;
2524 if (Q_UNLIKELY(version == CurrentVersion + 1))
2525 copyInternalContext(static_cast<QInternalMessageLogContext *>(this), logContext);
2526 return *this;
2527}
2528
2529/*!
2530 \fn QMessageLogger::QMessageLogger()
2531
2532 Constructs a default QMessageLogger. See the other constructors to specify
2533 context information.
2534*/
2535
2536/*!
2537 \fn QMessageLogger::QMessageLogger(const char *file, int line, const char *function)
2538
2539 Constructs a QMessageLogger to record log messages for \a file at \a line
2540 in \a function. The is equivalent to QMessageLogger(file, line, function, "default")
2541*/
2542/*!
2543 \fn QMessageLogger::QMessageLogger(const char *file, int line, const char *function, const char *category)
2544
2545 Constructs a QMessageLogger to record \a category messages for \a file at \a line
2546 in \a function.
2547
2548 \sa QLoggingCategory
2549*/
2550
2551/*!
2552 \fn void QMessageLogger::noDebug(const char *, ...) const
2553 \internal
2554
2555 Ignores logging output
2556
2557 \sa QNoDebug, qDebug()
2558*/
2559
2560/*!
2561 \fn QMessageLogContext::QMessageLogContext()
2562 \internal
2563
2564 Constructs a QMessageLogContext
2565*/
2566
2567/*!
2568 \fn QMessageLogContext::QMessageLogContext(const char *fileName, int lineNumber, const char *functionName, const char *categoryName)
2569 \internal
2570
2571 Constructs a QMessageLogContext with for file \a fileName at line
2572 \a lineNumber, in function \a functionName, and category \a categoryName.
2573
2574 \sa QLoggingCategory
2575*/
2576
2577/*!
2578 \macro qDebug(const char *format, ...)
2579 \relates <QtLogging>
2580 \threadsafe
2581
2582 Logs debug message \a format to the central message handler.
2583 \a format can contain format specifiers that are
2584 replaced by values specificed in additional arguments.
2585
2586 Example:
2587
2588 \snippet code/src_corelib_global_qglobal.cpp 24
2589
2590 \a format can contain format specifiers like \c {%s} for UTF-8 strings, or
2591 \c {%i} for integers. This is similar to how the C \c{printf()} function works.
2592 For more details on the formatting, see \l QString::asprintf().
2593
2594 For more convenience and further type support, you can also use
2595 \l{QDebug::qDebug()}, which follows the streaming paradigm (similar to
2596 \c{std::cout} or \c{std::cerr}).
2597
2598 This function does nothing if \c QT_NO_DEBUG_OUTPUT was defined during compilation.
2599
2600 To suppress the output at runtime, install your own message handler
2601 with qInstallMessageHandler().
2602
2603 \sa QDebug::qDebug(), qCDebug(), qInfo(), qWarning(), qCritical(), qFatal(),
2604 qInstallMessageHandler(), {Debugging Techniques}
2605*/
2606
2607/*!
2608 \macro qInfo(const char *format, ...)
2609 \relates <QtLogging>
2610 \threadsafe
2611 \since 5.5
2612
2613 Logs informational message \a format to the central message handler.
2614 \a format can contain format specifiers that are
2615 replaced by values specificed in additional arguments.
2616
2617 Example:
2618
2619 \snippet code/src_corelib_global_qglobal.cpp qInfo_printf
2620
2621 \a format can contain format specifiers like \c {%s} for UTF-8 strings, or
2622 \c {%i} for integers. This is similar to how the C \c{printf()} function works.
2623 For more details on the formatting, see \l QString::asprintf().
2624
2625 For more convenience and further type support, you can also use
2626 \l{QDebug::qInfo()}, which follows the streaming paradigm (similar to
2627 \c{std::cout} or \c{std::cerr}).
2628
2629 This function does nothing if \c QT_NO_INFO_OUTPUT was defined during compilation.
2630
2631 To suppress the output at runtime, install your own message handler
2632 using qInstallMessageHandler().
2633
2634 \sa QDebug::qInfo(), qCInfo(), qDebug(), qWarning(), qCritical(), qFatal(),
2635 qInstallMessageHandler(), {Debugging Techniques}
2636*/
2637
2638/*!
2639 \macro qWarning(const char *format, ...)
2640 \relates <QtLogging>
2641 \threadsafe
2642
2643 Logs warning message \a format to the central message handler.
2644 \a format can contain format specifiers that are
2645 replaced by values specificed in additional arguments.
2646
2647 Example:
2648 \snippet code/src_corelib_global_qglobal.cpp 26
2649
2650 \a format can contain format specifiers like \c {%s} for UTF-8 strings, or
2651 \c {%i} for integers. This is similar to how the C \c{printf()} function works.
2652 For more details on the formatting, see \l QString::asprintf().
2653
2654 For more convenience and further type support, you can also use
2655 \l{QDebug::qWarning()}, which follows the streaming paradigm (similar to
2656 \c{std::cout} or \c{std::cerr}).
2657
2658 This function does nothing if \c QT_NO_WARNING_OUTPUT was defined
2659 during compilation.
2660 To suppress the output at runtime, you can set
2661 \l{QLoggingCategory}{logging rules} or register a custom
2662 \l{QLoggingCategory::installFilter()}{filter}.
2663
2664 For debugging purposes, it is sometimes convenient to let the
2665 program abort for warning messages. This allows you
2666 to inspect the core dump, or attach a debugger - see also \l{qFatal()}.
2667 To enable this, set the environment variable \c{QT_FATAL_WARNINGS}
2668 to a number \c n. The program terminates then for the n-th warning.
2669 That is, if the environment variable is set to 1, it will terminate
2670 on the first call; if it contains the value 10, it will exit on the 10th
2671 call. Any non-numeric value in the environment variable is equivalent to 1.
2672
2673 \sa QDebug::qWarning(), qCWarning(), qDebug(), qInfo(), qCritical(), qFatal(),
2674 qInstallMessageHandler(), {Debugging Techniques}
2675*/
2676
2677/*!
2678 \macro qCritical(const char *format, ...)
2679 \relates <QtLogging>
2680 \threadsafe
2681
2682 Logs critical message \a format to the central message handler.
2683 \a format can contain format specifiers that are
2684 replaced by values specificed in additional arguments.
2685
2686 Example:
2687 \snippet code/src_corelib_global_qglobal.cpp 28
2688
2689 \a format can contain format specifiers like \c {%s} for UTF-8 strings, or
2690 \c {%i} for integers. This is similar to how the C \c{printf()} function works.
2691 For more details on the formatting, see \l QString::asprintf().
2692
2693 For more convenience and further type support, you can also use
2694 \l{QDebug::qCritical()}, which follows the streaming paradigm (similar to
2695 \c{std::cout} or \c{std::cerr}).
2696
2697 To suppress the output at runtime, you can define
2698 \l{QLoggingCategory}{logging rules} or register a custom
2699 \l{QLoggingCategory::installFilter()}{filter}.
2700
2701 For debugging purposes, it is sometimes convenient to let the
2702 program abort for critical messages. This allows you
2703 to inspect the core dump, or attach a debugger - see also \l{qFatal()}.
2704 To enable this, set the environment variable \c{QT_FATAL_CRITICALS}
2705 to a number \c n. The program terminates then for the n-th critical
2706 message.
2707 That is, if the environment variable is set to 1, it will terminate
2708 on the first call; if it contains the value 10, it will exit on the 10th
2709 call. Any non-numeric value in the environment variable is equivalent to 1.
2710
2711 \sa QDebug::qCritical, qCCritical(), qDebug(), qInfo(), qWarning(), qFatal(),
2712 qInstallMessageHandler(), {Debugging Techniques}
2713*/
2714
2715/*!
2716 \macro qFatal(const char *format, ...)
2717 \relates <QtLogging>
2718
2719 Logs fatal message \a format to the central message handler.
2720 \a format can contain format specifiers that are
2721 replaced by values specificed in additional arguments.
2722
2723 Example:
2724 \snippet code/src_corelib_global_qglobal.cpp 30
2725
2726 If you are using the \b{default message handler} this function will
2727 abort to create a core dump. On Windows, for debug builds,
2728 this function will report a _CRT_ERROR enabling you to connect a debugger
2729 to the application.
2730
2731 To suppress the output at runtime, install your own message handler
2732 with qInstallMessageHandler().
2733
2734 \sa qCFatal(), qDebug(), qInfo(), qWarning(), qCritical(),
2735 qInstallMessageHandler(), {Debugging Techniques}
2736*/
2737
2738/*!
2739 \enum QtMsgType
2740 \relates <QtLogging>
2741
2742 This enum describes the messages that can be sent to a message
2743 handler (QtMessageHandler). You can use the enum to identify and
2744 associate the various message types with the appropriate
2745 actions. Its values are, in order of increasing severity:
2746
2747 \value QtDebugMsg
2748 A message generated by the qDebug() function.
2749 \value QtInfoMsg
2750 A message generated by the qInfo() function.
2751 \value QtWarningMsg
2752 A message generated by the qWarning() function.
2753 \value QtCriticalMsg
2754 A message generated by the qCritical() function.
2755 \value QtFatalMsg
2756 A message generated by the qFatal() function.
2757 \omitvalue QtSystemMsg
2758
2759 \sa QtMessageHandler, qInstallMessageHandler(), QLoggingCategory
2760*/
2761
2762QT_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()