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