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