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