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