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

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