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

Provided by KDAB

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

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