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