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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtbase/src/corelib/global/qlogging.cpp