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

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