Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qlogging.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
3// Copyright (C) 2022 Intel Corporation.
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5
6#include "qglobal_p.h"
7#include "qlogging.h"
8#include "qlogging_p.h"
9#include "qlist.h"
10#include "qbytearray.h"
11#include "qscopeguard.h"
12#include "qstring.h"
13#include "qvarlengtharray.h"
14#include "qdebug.h"
15#include "qmutex.h"
16#include <QtCore/private/qlocking_p.h>
17#include <QtCore/private/qsimd_p.h>
18#include "qloggingcategory.h"
19#ifndef QT_BOOTSTRAPPED
20#include "qelapsedtimer.h"
21#include "qdeadlinetimer.h"
22#include "qdatetime.h"
23#include "qcoreapplication.h"
24#include "qthread.h"
25#include "private/qloggingregistry_p.h"
26#include "private/qcoreapplication_p.h"
27#include <qtcore_tracepoints_p.h>
28#endif
29#ifdef Q_OS_WIN
30#include <qt_windows.h>
31#endif
32#ifdef Q_CC_MSVC
33#include <intrin.h>
34#endif
35#if QT_CONFIG(slog2)
36#include <sys/slog2.h>
37#endif
38#if __has_include(<paths.h>)
39#include <paths.h>
40#endif
41
42#ifdef Q_OS_ANDROID
43#include <android/log.h>
44#endif
45
46#ifdef Q_OS_DARWIN
47#include <QtCore/private/qcore_mac_p.h>
48#endif
49
50#if QT_CONFIG(journald)
51# define SD_JOURNAL_SUPPRESS_LOCATION
52# include <systemd/sd-journal.h>
53# include <syslog.h>
54#endif
55#if QT_CONFIG(syslog)
56# include <syslog.h>
57#endif
58#ifdef Q_OS_UNIX
59# include <sys/types.h>
60# include <sys/stat.h>
61# include <unistd.h>
62# include "private/qcore_unix_p.h"
63#endif
64
65#ifdef Q_OS_WASM
66#include <emscripten/emscripten.h>
67#endif
68
69#if QT_CONFIG(slog2)
70extern char *__progname;
71#endif
72
73#ifndef QT_BOOTSTRAPPED
74#if __has_include(<cxxabi.h>) && QT_CONFIG(backtrace) && QT_CONFIG(regularexpression)
75# include <qregularexpression.h>
76# if QT_CONFIG(dladdr)
77# include <dlfcn.h>
78# endif
79# include BACKTRACE_HEADER
80# include <cxxabi.h>
81# define QLOGGING_HAVE_BACKTRACE
82#endif
83
84#if defined(Q_OS_LINUX) && (defined(__GLIBC__) || __has_include(<sys/syscall.h>))
85# include <sys/syscall.h>
86
87# if defined(Q_OS_ANDROID) && !defined(SYS_gettid)
88# define SYS_gettid __NR_gettid
89# endif
90
91static long qt_gettid()
92{
93 // no error handling
94 // this syscall has existed since Linux 2.4.11 and cannot fail
95 return syscall(SYS_gettid);
96}
97#elif defined(Q_OS_DARWIN)
98# include <pthread.h>
99static int qt_gettid()
100{
101 // no error handling: this call cannot fail
102 __uint64_t tid;
103 pthread_threadid_np(NULL, &tid);
104 return tid;
105}
106#elif defined(Q_OS_FREEBSD_KERNEL) && defined(__FreeBSD_version) && __FreeBSD_version >= 900031
107# include <pthread_np.h>
108static int qt_gettid()
109{
110 return pthread_getthreadid_np();
111}
112#else
113static QT_PREPEND_NAMESPACE(qint64) qt_gettid()
114{
117}
118#endif
119#endif // !QT_BOOTSTRAPPED
120
121#include <cstdlib>
122#include <algorithm>
123#include <memory>
124#include <vector>
125
126#include <stdio.h>
127
129
130using namespace Qt::StringLiterals;
131
132#ifndef QT_BOOTSTRAPPED
133Q_TRACE_POINT(qtcore, qt_message_print, int type, const char *category, const char *function, const char *file, int line, const QString &message);
134#endif
135
166#if !defined(Q_CC_MSVC_ONLY)
168#endif
171static void qt_message_print(const QString &message);
172
173static int checked_var_value(const char *varname)
174{
175 // qEnvironmentVariableIntValue returns 0 on both parsing failure and on
176 // empty, but we need to distinguish between the two for backwards
177 // compatibility reasons.
178 QByteArray str = qgetenv(varname);
179 if (str.isEmpty())
180 return 0;
181
182 bool ok;
183 int value = str.toInt(&ok, 0);
184 return ok ? value : 1;
185}
186
188{
189 // it's fatal if the current value is exactly 1,
190 // otherwise decrement if it's non-zero
191
192 int v = n.loadRelaxed();
193 while (v != 0 && !n.testAndSetRelaxed(v, v - 1, v))
194 qYieldCpu();
195 return v == 1; // we exited the loop, so either v == 0 or CAS succeeded to set n from v to v-1
196}
197
198static bool isFatal(QtMsgType msgType)
199{
200 if (msgType == QtFatalMsg)
201 return true;
202
203 if (msgType == QtCriticalMsg) {
204 static QAtomicInt fatalCriticals = checked_var_value("QT_FATAL_CRITICALS");
205 return is_fatal_count_down(fatalCriticals);
206 }
207
208 if (msgType == QtWarningMsg || msgType == QtCriticalMsg) {
209 static QAtomicInt fatalWarnings = checked_var_value("QT_FATAL_WARNINGS");
210 return is_fatal_count_down(fatalWarnings);
211 }
212
213 return false;
214}
215
216static bool isDefaultCategory(const char *category)
217{
218 return !category || strcmp(category, "default") == 0;
219}
220
227static bool systemHasStderr()
228{
229 return true;
230}
231
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
291}
292
293
294namespace QtPrivate {
295
306{
307 static bool forceStderrLogging = qEnvironmentVariableIntValue("QT_FORCE_STDERR_LOGGING");
308 return forceStderrLogging || stderrHasConsoleAttached();
309}
310
311
312} // QtPrivate
313
314using namespace QtPrivate;
315
350#if defined(Q_CC_MSVC_ONLY) && 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
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
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))
394}
395
396
397#undef qInfo
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))
414}
415
437void QMessageLogger::debug(const QLoggingCategory &cat, const char *msg, ...) const
438{
439 if (!cat.isDebugEnabled())
440 return;
441
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))
453}
454
463 const char *msg, ...) const
464{
465 const QLoggingCategory &cat = (*catFunc)();
466 if (!cat.isDebugEnabled())
467 return;
468
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))
480}
481
482#ifndef QT_NO_DEBUG_STREAM
483
490{
491 QDebug dbg = QDebug(QtDebugMsg);
492 QMessageLogContext &ctxt = dbg.stream->context;
493 ctxt.copyContextFrom(context);
494 return dbg;
495}
496
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
523{
524 return debug((*catFunc)());
525}
526
534QNoDebug QMessageLogger::noDebug() const noexcept
535{
536 return QNoDebug();
537}
538
539#endif
540
548void QMessageLogger::info(const QLoggingCategory &cat, const char *msg, ...) const
549{
550 if (!cat.isInfoEnabled())
551 return;
552
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))
564}
565
574 const char *msg, ...) const
575{
576 const QLoggingCategory &cat = (*catFunc)();
577 if (!cat.isInfoEnabled())
578 return;
579
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))
591}
592
593#ifndef QT_NO_DEBUG_STREAM
594
602{
603 QDebug dbg = QDebug(QtInfoMsg);
604 QMessageLogContext &ctxt = dbg.stream->context;
605 ctxt.copyContextFrom(context);
606 return dbg;
607}
608
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
635{
636 return info((*catFunc)());
637}
638
639#endif
640
641#undef qWarning
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
657}
658
666void QMessageLogger::warning(const QLoggingCategory &cat, const char *msg, ...) const
667{
668 if (!cat.isWarningEnabled())
669 return;
670
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
682}
683
692 const char *msg, ...) const
693{
694 const QLoggingCategory &cat = (*catFunc)();
695 if (!cat.isWarningEnabled())
696 return;
697
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
709}
710
711#ifndef QT_NO_DEBUG_STREAM
718{
720 QMessageLogContext &ctxt = dbg.stream->context;
721 ctxt.copyContextFrom(context);
722 return dbg;
723}
724
731{
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
750{
751 return warning((*catFunc)());
752}
753
754#endif
755
756#undef qCritical
757
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
773}
774
782void QMessageLogger::critical(const QLoggingCategory &cat, const char *msg, ...) const
783{
784 if (!cat.isCriticalEnabled())
785 return;
786
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
798}
799
808 const char *msg, ...) const
809{
810 const QLoggingCategory &cat = (*catFunc)();
811 if (!cat.isCriticalEnabled())
812 return;
813
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
825}
826
827#ifndef QT_NO_DEBUG_STREAM
834{
836 QMessageLogContext &ctxt = dbg.stream->context;
837 ctxt.copyContextFrom(context);
838 return dbg;
839}
840
848{
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
867{
868 return critical((*catFunc)());
869}
870
871#endif
872
873#undef qFatal
874
882void QMessageLogger::fatal(const QLoggingCategory &cat, const char *msg, ...) const noexcept
883{
885 ctxt.copyContextFrom(context);
886 ctxt.category = cat.categoryName();
887
889
890 va_list ap;
891 va_start(ap, msg); // use variable arg list
893 va_end(ap);
894
896}
897
906 const char *msg, ...) const noexcept
907{
908 const QLoggingCategory &cat = (*catFunc)();
909
911 ctxt.copyContextFrom(context);
912 ctxt.category = cat.categoryName();
913
915
916 va_list ap;
917 va_start(ap, msg); // use variable arg list
919 va_end(ap);
920
922}
923
930void QMessageLogger::fatal(const char *msg, ...) const noexcept
931{
933
934 va_list ap;
935 va_start(ap, msg); // use variable arg list
937 va_end(ap);
938
940}
941
942#ifndef QT_NO_DEBUG_STREAM
951{
952 QDebug dbg = QDebug(QtFatalMsg);
953 QMessageLogContext &ctxt = dbg.stream->context;
954 ctxt.copyContextFrom(context);
955 return dbg;
956}
957
965{
966 QDebug dbg = QDebug(QtFatalMsg);
967
968 QMessageLogContext &ctxt = dbg.stream->context;
969 ctxt.copyContextFrom(context);
970 ctxt.category = cat.categoryName();
971
972 return dbg;
973}
974
982{
983 return fatal((*catFunc)());
984}
985#endif // QT_NO_DEBUG_STREAM
986
991{
992 // Strip the function info down to the base function name
993 // note that this throws away the template definitions,
994 // the parameter types (overloads) and any const/volatile qualifiers.
995
996 if (info.isEmpty())
997 return info;
998
1000
1001 // Skip trailing [with XXX] for templates (gcc), but make
1002 // sure to not affect Objective-C message names.
1003 pos = info.size() - 1;
1004 if (info.endsWith(']') && !(info.startsWith('+') || info.startsWith('-'))) {
1005 while (--pos) {
1006 if (info.at(pos) == '[') {
1007 info.truncate(pos);
1008 break;
1009 }
1010 }
1011 if (info.endsWith(' ')) {
1012 info.chop(1);
1013 }
1014 }
1015
1016 // operator names with '(', ')', '<', '>' in it
1017 static const char operator_call[] = "operator()";
1018 static const char operator_lessThan[] = "operator<";
1019 static const char operator_greaterThan[] = "operator>";
1020 static const char operator_lessThanEqual[] = "operator<=";
1021 static const char operator_greaterThanEqual[] = "operator>=";
1022
1023 // canonize operator names
1024 info.replace("operator ", "operator");
1025
1026 pos = -1;
1027 // remove argument list
1028 forever {
1029 int parencount = 0;
1030 pos = info.lastIndexOf(')', pos);
1031 if (pos == -1) {
1032 // Don't know how to parse this function name
1033 return info;
1034 }
1035 if (info.indexOf('>', pos) != -1
1036 || info.indexOf(':', pos) != -1) {
1037 // that wasn't the function argument list.
1038 --pos;
1039 continue;
1040 }
1041
1042 // find the beginning of the argument list
1043 --pos;
1044 ++parencount;
1045 while (pos && parencount) {
1046 if (info.at(pos) == ')')
1047 ++parencount;
1048 else if (info.at(pos) == '(')
1049 --parencount;
1050 --pos;
1051 }
1052 if (parencount != 0)
1053 return info;
1054
1055 info.truncate(++pos);
1056
1057 if (info.at(pos - 1) == ')') {
1058 if (info.indexOf(operator_call) == pos - qsizetype(strlen(operator_call)))
1059 break;
1060
1061 // this function returns a pointer to a function
1062 // and we matched the arguments of the return type's parameter list
1063 // try again
1064 info.remove(0, info.indexOf('('));
1065 info.chop(1);
1066 continue;
1067 } else {
1068 break;
1069 }
1070 }
1071
1072 // find the beginning of the function name
1073 int parencount = 0;
1074 int templatecount = 0;
1075 --pos;
1076
1077 // make sure special characters in operator names are kept
1078 if (pos > -1) {
1079 switch (info.at(pos)) {
1080 case ')':
1081 if (info.indexOf(operator_call) == pos - qsizetype(strlen(operator_call)) + 1)
1082 pos -= 2;
1083 break;
1084 case '<':
1085 if (info.indexOf(operator_lessThan) == pos - qsizetype(strlen(operator_lessThan)) + 1)
1086 --pos;
1087 break;
1088 case '>':
1089 if (info.indexOf(operator_greaterThan) == pos - qsizetype(strlen(operator_greaterThan)) + 1)
1090 --pos;
1091 break;
1092 case '=': {
1093 auto operatorLength = qsizetype(strlen(operator_lessThanEqual));
1094 if (info.indexOf(operator_lessThanEqual) == pos - operatorLength + 1)
1095 pos -= 2;
1096 else if (info.indexOf(operator_greaterThanEqual) == pos - operatorLength + 1)
1097 pos -= 2;
1098 break;
1099 }
1100 default:
1101 break;
1102 }
1103 }
1104
1105 while (pos > -1) {
1106 if (parencount < 0 || templatecount < 0)
1107 return info;
1108
1109 char c = info.at(pos);
1110 if (c == ')')
1111 ++parencount;
1112 else if (c == '(')
1113 --parencount;
1114 else if (c == '>')
1115 ++templatecount;
1116 else if (c == '<')
1117 --templatecount;
1118 else if (c == ' ' && templatecount == 0 && parencount == 0)
1119 break;
1120
1121 --pos;
1122 }
1123 info = info.mid(pos + 1);
1124
1125 // remove trailing '*', '&' that are part of the return argument
1126 while ((info.at(0) == '*')
1127 || (info.at(0) == '&'))
1128 info = info.mid(1);
1129
1130 // we have the full function name now.
1131 // clean up the templates
1132 while ((pos = info.lastIndexOf('>')) != -1) {
1133 if (!info.contains('<'))
1134 break;
1135
1136 // find the matching close
1137 qsizetype end = pos;
1138 templatecount = 1;
1139 --pos;
1140 while (pos && templatecount) {
1141 char c = info.at(pos);
1142 if (c == '>')
1143 ++templatecount;
1144 else if (c == '<')
1145 --templatecount;
1146 --pos;
1147 }
1148 ++pos;
1149 info.remove(pos, end - pos + 1);
1150 }
1151
1152 return info;
1153}
1154
1155// tokens as recognized in QT_MESSAGE_PATTERN
1156static const char categoryTokenC[] = "%{category}";
1157static const char typeTokenC[] = "%{type}";
1158static const char messageTokenC[] = "%{message}";
1159static const char fileTokenC[] = "%{file}";
1160static const char lineTokenC[] = "%{line}";
1161static const char functionTokenC[] = "%{function}";
1162static const char pidTokenC[] = "%{pid}";
1163static const char appnameTokenC[] = "%{appname}";
1164static const char threadidTokenC[] = "%{threadid}";
1165static const char qthreadptrTokenC[] = "%{qthreadptr}";
1166static const char timeTokenC[] = "%{time"; //not a typo: this command has arguments
1167static const char backtraceTokenC[] = "%{backtrace"; //ditto
1168static const char ifCategoryTokenC[] = "%{if-category}";
1169static const char ifDebugTokenC[] = "%{if-debug}";
1170static const char ifInfoTokenC[] = "%{if-info}";
1171static const char ifWarningTokenC[] = "%{if-warning}";
1172static const char ifCriticalTokenC[] = "%{if-critical}";
1173static const char ifFatalTokenC[] = "%{if-fatal}";
1174static const char endifTokenC[] = "%{endif}";
1175static const char emptyTokenC[] = "";
1176
1177static const char defaultPattern[] = "%{if-category}%{category}: %{endif}%{message}";
1178
1180{
1183
1184 void setPattern(const QString &pattern);
1185
1186 // 0 terminated arrays of literal tokens / literal or placeholder tokens
1187 std::unique_ptr<std::unique_ptr<const char[]>[]> literals;
1188 std::unique_ptr<const char *[]> tokens;
1189 QList<QString> timeArgs; // timeFormats in sequence of %{time
1190#ifndef QT_BOOTSTRAPPED
1192#endif
1193#ifdef QLOGGING_HAVE_BACKTRACE
1194 struct BacktraceParams
1195 {
1196 QString backtraceSeparator;
1197 int backtraceDepth;
1198 };
1199 QList<BacktraceParams> backtraceArgs; // backtrace arguments in sequence of %{backtrace
1200#endif
1201
1204};
1205#ifdef QLOGGING_HAVE_BACKTRACE
1206Q_DECLARE_TYPEINFO(QMessagePattern::BacktraceParams, Q_RELOCATABLE_TYPE);
1207#endif
1208
1210
1212{
1213#ifndef QT_BOOTSTRAPPED
1214 timer.start();
1215#endif
1216 const QString envPattern = QString::fromLocal8Bit(qgetenv("QT_MESSAGE_PATTERN"));
1217 if (envPattern.isEmpty()) {
1219 fromEnvironment = false;
1220 } else {
1221 setPattern(envPattern);
1222 fromEnvironment = true;
1223 }
1224}
1225
1227
1229{
1230 timeArgs.clear();
1231#ifdef QLOGGING_HAVE_BACKTRACE
1232 backtraceArgs.clear();
1233#endif
1234
1235 // scanner
1236 QList<QString> lexemes;
1237 QString lexeme;
1238 bool inPlaceholder = false;
1239 for (int i = 0; i < pattern.size(); ++i) {
1240 const QChar c = pattern.at(i);
1241 if (c == u'%' && !inPlaceholder) {
1242 if ((i + 1 < pattern.size())
1243 && pattern.at(i + 1) == u'{') {
1244 // beginning of placeholder
1245 if (!lexeme.isEmpty()) {
1246 lexemes.append(lexeme);
1247 lexeme.clear();
1248 }
1249 inPlaceholder = true;
1250 }
1251 }
1252
1253 lexeme.append(c);
1254
1255 if (c == u'}' && inPlaceholder) {
1256 // end of placeholder
1257 lexemes.append(lexeme);
1258 lexeme.clear();
1259 inPlaceholder = false;
1260 }
1261 }
1262 if (!lexeme.isEmpty())
1263 lexemes.append(lexeme);
1264
1265 // tokenizer
1266 std::vector<std::unique_ptr<const char[]>> literalsVar;
1267 tokens.reset(new const char *[lexemes.size() + 1]);
1268 tokens[lexemes.size()] = nullptr;
1269
1270 bool nestedIfError = false;
1271 bool inIf = false;
1272 QString error;
1273
1274 for (int i = 0; i < lexemes.size(); ++i) {
1275 const QString lexeme = lexemes.at(i);
1276 if (lexeme.startsWith("%{"_L1) && lexeme.endsWith(u'}')) {
1277 // placeholder
1278 if (lexeme == QLatin1StringView(typeTokenC)) {
1279 tokens[i] = typeTokenC;
1280 } else if (lexeme == QLatin1StringView(categoryTokenC))
1282 else if (lexeme == QLatin1StringView(messageTokenC))
1284 else if (lexeme == QLatin1StringView(fileTokenC))
1285 tokens[i] = fileTokenC;
1286 else if (lexeme == QLatin1StringView(lineTokenC))
1287 tokens[i] = lineTokenC;
1288 else if (lexeme == QLatin1StringView(functionTokenC))
1290 else if (lexeme == QLatin1StringView(pidTokenC))
1291 tokens[i] = pidTokenC;
1292 else if (lexeme == QLatin1StringView(appnameTokenC))
1294 else if (lexeme == QLatin1StringView(threadidTokenC))
1296 else if (lexeme == QLatin1StringView(qthreadptrTokenC))
1298 else if (lexeme.startsWith(QLatin1StringView(timeTokenC))) {
1299 tokens[i] = timeTokenC;
1300 qsizetype spaceIdx = lexeme.indexOf(QChar::fromLatin1(' '));
1301 if (spaceIdx > 0)
1302 timeArgs.append(lexeme.mid(spaceIdx + 1, lexeme.size() - spaceIdx - 2));
1303 else
1305 } else if (lexeme.startsWith(QLatin1StringView(backtraceTokenC))) {
1306#ifdef QLOGGING_HAVE_BACKTRACE
1308 QString backtraceSeparator = QStringLiteral("|");
1309 int backtraceDepth = 5;
1310 static const QRegularExpression depthRx(QStringLiteral(" depth=(?|\"([^\"]*)\"|([^ }]*))"));
1311 static const QRegularExpression separatorRx(QStringLiteral(" separator=(?|\"([^\"]*)\"|([^ }]*))"));
1312 QRegularExpressionMatch m = depthRx.match(lexeme);
1313 if (m.hasMatch()) {
1314 int depth = m.capturedView(1).toInt();
1315 if (depth <= 0)
1316 error += "QT_MESSAGE_PATTERN: %{backtrace} depth must be a number greater than 0\n"_L1;
1317 else
1318 backtraceDepth = depth;
1319 }
1320 m = separatorRx.match(lexeme);
1321 if (m.hasMatch())
1322 backtraceSeparator = m.captured(1);
1323 BacktraceParams backtraceParams;
1324 backtraceParams.backtraceDepth = backtraceDepth;
1325 backtraceParams.backtraceSeparator = backtraceSeparator;
1326 backtraceArgs.append(backtraceParams);
1327#else
1328 error += "QT_MESSAGE_PATTERN: %{backtrace} is not supported by this Qt build\n"_L1;
1329 tokens[i] = "";
1330#endif
1331 }
1332
1333#define IF_TOKEN(LEVEL) \
1334 else if (lexeme == QLatin1StringView(LEVEL)) { \
1335 if (inIf) \
1336 nestedIfError = true; \
1337 tokens[i] = LEVEL; \
1338 inIf = true; \
1339 }
1346#undef IF_TOKEN
1347 else if (lexeme == QLatin1StringView(endifTokenC)) {
1348 tokens[i] = endifTokenC;
1349 if (!inIf && !nestedIfError)
1350 error += "QT_MESSAGE_PATTERN: %{endif} without an %{if-*}\n"_L1;
1351 inIf = false;
1352 } else {
1353 tokens[i] = emptyTokenC;
1354 error += QStringLiteral("QT_MESSAGE_PATTERN: Unknown placeholder %1\n")
1355 .arg(lexeme);
1356 }
1357 } else {
1358 using UP = std::unique_ptr<char[]>;
1359 tokens[i] = literalsVar.emplace_back(UP(qstrdup(lexeme.toLatin1().constData()))).get();
1360 }
1361 }
1362 if (nestedIfError)
1363 error += "QT_MESSAGE_PATTERN: %{if-*} cannot be nested\n"_L1;
1364 else if (inIf)
1365 error += "QT_MESSAGE_PATTERN: missing %{endif}\n"_L1;
1366
1367 if (!error.isEmpty())
1369
1370 literals.reset(new std::unique_ptr<const char[]>[literalsVar.size() + 1]);
1371 std::move(literalsVar.begin(), literalsVar.end(), &literals[0]);
1372}
1373
1374#if defined(QLOGGING_HAVE_BACKTRACE) && !defined(QT_BOOTSTRAPPED)
1375// make sure the function has "Message" in the name so the function is removed
1376/*
1377 A typical backtrace in debug mode looks like:
1378 #0 backtraceFramesForLogMessage (frameCount=5) at qlogging.cpp:1296
1379 #1 formatBacktraceForLogMessage (backtraceParams=..., function=0x4040b8 "virtual void MyClass::myFunction(int)") at qlogging.cpp:1344
1380 #2 qFormatLogMessage (type=QtDebugMsg, context=..., str=...) at qlogging.cpp:1452
1381 #3 stderr_message_handler (type=QtDebugMsg, context=..., message=...) at qlogging.cpp:1744
1382 #4 qDefaultMessageHandler (type=QtDebugMsg, context=..., message=...) at qlogging.cpp:1795
1383 #5 qt_message_print (msgType=QtDebugMsg, context=..., message=...) at qlogging.cpp:1840
1384 #6 qt_message_output (msgType=QtDebugMsg, context=..., message=...) at qlogging.cpp:1891
1385 #7 QDebug::~QDebug (this=<optimized out>, __in_chrg=<optimized out>) at qdebug.h:111
1386*/
1387static constexpr int TypicalBacktraceFrameCount = 8;
1388
1389# if defined(Q_CC_GNU) && !defined(Q_CC_CLANG)
1390// force skipping the frame pointer, to save the backtrace() function some work
1391# pragma GCC push_options
1392# pragma GCC optimize ("omit-frame-pointer")
1393# endif
1394
1395static QStringList backtraceFramesForLogMessage(int frameCount)
1396{
1397 struct DecodedFrame {
1398 QString library;
1400 };
1401
1403 if (frameCount == 0)
1404 return result;
1405
1406 QVarLengthArray<void *, 32> buffer(TypicalBacktraceFrameCount + frameCount);
1407 int n = backtrace(buffer.data(), buffer.size());
1408 if (n <= 0)
1409 return result;
1410 buffer.resize(n);
1411
1412 auto shouldSkipFrame = [&result](const auto &library, const auto &function) {
1413 if (!result.isEmpty() || !library.contains("Qt6Core"_L1))
1414 return false;
1415 if (function.isEmpty())
1416 return true;
1417 if (function.contains("6QDebug"_L1))
1418 return true;
1419 if (function.contains("Message"_L1) || function.contains("_message"_L1))
1420 return true;
1421 return false;
1422 };
1423
1424 auto demangled = [](auto &function) -> QString {
1425 if (!function.startsWith("_Z"_L1))
1426 return function;
1427
1428 // we optimize for the case where __cxa_demangle succeeds
1429 auto fn = [&]() {
1430 if constexpr (sizeof(function.at(0)) == 1)
1431 return function.data(); // -> const char *
1432 else
1433 return std::move(function).toUtf8(); // -> QByteArray
1434 }();
1436 demangled.reset(abi::__cxa_demangle(fn, nullptr, nullptr, nullptr));
1437
1438 if (demangled)
1439 return QString::fromUtf8(qCleanupFuncinfo(demangled.data()));
1440 else
1441 return QString::fromUtf8(fn); // restore
1442 };
1443
1444# if QT_CONFIG(dladdr)
1445 // use dladdr() instead of backtrace_symbols()
1446 QString cachedLibrary;
1447 const char *cachedFname = nullptr;
1448 auto decodeFrame = [&](void *addr) -> DecodedFrame {
1449 Dl_info info;
1450 if (!dladdr(addr, &info))
1451 return {};
1452
1453 // These are actually UTF-8, so we'll correct below
1454 QLatin1StringView fn(info.dli_sname);
1456 if (const char *lastSlash = strrchr(info.dli_fname, '/'))
1457 lib = QLatin1StringView(lastSlash + 1);
1458 else
1459 lib = QLatin1StringView(info.dli_fname);
1460
1461 if (shouldSkipFrame(lib, fn))
1462 return {};
1463
1464 QString function = demangled(fn);
1465 if (lib.data() != cachedFname) {
1466 cachedFname = lib.data();
1467 cachedLibrary = QString::fromUtf8(cachedFname, lib.size());
1468 }
1469 return { cachedLibrary, function };
1470 };
1471# else
1472 // The results of backtrace_symbols looks like this:
1473 // /lib/libc.so.6(__libc_start_main+0xf3) [0x4a937413]
1474 // The offset and function name are optional.
1475 // This regexp tries to extract the library name (without the path) and the function name.
1476 // This code is protected by QMessagePattern::mutex so it is thread safe on all compilers
1477 static const QRegularExpression rx(QStringLiteral("^(?:[^(]*/)?([^(/]+)\\(([^+]*)(?:[\\+[a-f0-9x]*)?\\) \\[[a-f0-9x]*\\]$"));
1478
1479 auto decodeFrame = [&](void *&addr) -> DecodedFrame {
1483 if (!m.hasMatch())
1484 return {};
1485
1486 QString library = m.captured(1);
1487 QString function = m.captured(2);
1488
1489 // skip the trace from QtCore that are because of the qDebug itself
1490 if (shouldSkipFrame(library, function))
1491 return {};
1492
1493 function = demangled(function);
1494 return { library, function };
1495 };
1496# endif
1497
1498 for (void *&addr : buffer) {
1499 DecodedFrame frame = decodeFrame(addr);
1500 if (!frame.library.isEmpty()) {
1501 if (frame.function.isEmpty())
1502 result.append(u'?' + frame.library + u'?');
1503 else
1504 result.append(frame.function);
1505 } else {
1506 // innermost, unknown frames are usually the logging framework itself
1507 if (!result.isEmpty())
1508 result.append(QStringLiteral("???"));
1509 }
1510
1511 if (result.size() == frameCount)
1512 break;
1513 }
1514 return result;
1515}
1516
1517static QString formatBacktraceForLogMessage(const QMessagePattern::BacktraceParams backtraceParams,
1518 const char *function)
1519{
1520 QString backtraceSeparator = backtraceParams.backtraceSeparator;
1521 int backtraceDepth = backtraceParams.backtraceDepth;
1522
1523 QStringList frames = backtraceFramesForLogMessage(backtraceDepth);
1524 if (frames.isEmpty())
1525 return QString();
1526
1527 // if the first frame is unknown, replace it with the context function
1528 if (function && frames.at(0).startsWith(u'?'))
1529 frames[0] = QString::fromUtf8(qCleanupFuncinfo(function));
1530
1531 return frames.join(backtraceSeparator);
1532}
1533
1534# if defined(Q_CC_GNU) && !defined(Q_CC_CLANG)
1535# pragma GCC pop_options
1536# endif
1537#endif // QLOGGING_HAVE_BACKTRACE && !QT_BOOTSTRAPPED
1538
1539Q_GLOBAL_STATIC(QMessagePattern, qMessagePattern)
1540
1541
1556{
1558
1559 const auto locker = qt_scoped_lock(QMessagePattern::mutex);
1560
1561 QMessagePattern *pattern = qMessagePattern();
1562 if (!pattern) {
1563 // after destruction of static QMessagePattern instance
1564 message.append(str);
1565 return message;
1566 }
1567
1568 bool skip = false;
1569
1570#ifndef QT_BOOTSTRAPPED
1571 int timeArgsIdx = 0;
1572#ifdef QLOGGING_HAVE_BACKTRACE
1573 int backtraceArgsIdx = 0;
1574#endif
1575#endif
1576
1577 // we do not convert file, function, line literals to local encoding due to overhead
1578 for (int i = 0; pattern->tokens[i]; ++i) {
1579 const char *token = pattern->tokens[i];
1580 if (token == endifTokenC) {
1581 skip = false;
1582 } else if (skip) {
1583 // we skip adding messages, but we have to iterate over
1584 // timeArgsIdx and backtraceArgsIdx anyway
1585#ifndef QT_BOOTSTRAPPED
1586 if (token == timeTokenC)
1587 timeArgsIdx++;
1588#ifdef QLOGGING_HAVE_BACKTRACE
1589 else if (token == backtraceTokenC)
1590 backtraceArgsIdx++;
1591#endif
1592#endif
1593 } else if (token == messageTokenC) {
1594 message.append(str);
1595 } else if (token == categoryTokenC) {
1596#ifndef Q_OS_ANDROID
1597 // Don't add the category to the message on Android
1598 message.append(QLatin1StringView(context.category));
1599#endif
1600 } else if (token == typeTokenC) {
1601 switch (type) {
1602 case QtDebugMsg: message.append("debug"_L1); break;
1603 case QtInfoMsg: message.append("info"_L1); break;
1604 case QtWarningMsg: message.append("warning"_L1); break;
1605 case QtCriticalMsg:message.append("critical"_L1); break;
1606 case QtFatalMsg: message.append("fatal"_L1); break;
1607 }
1608 } else if (token == fileTokenC) {
1609 if (context.file)
1610 message.append(QLatin1StringView(context.file));
1611 else
1612 message.append("unknown"_L1);
1613 } else if (token == lineTokenC) {
1614 message.append(QString::number(context.line));
1615 } else if (token == functionTokenC) {
1616 if (context.function)
1618 else
1619 message.append("unknown"_L1);
1620#ifndef QT_BOOTSTRAPPED
1621 } else if (token == pidTokenC) {
1623 } else if (token == appnameTokenC) {
1625 } else if (token == threadidTokenC) {
1626 // print the TID as decimal
1627 message.append(QString::number(qt_gettid()));
1628 } else if (token == qthreadptrTokenC) {
1629 message.append("0x"_L1);
1630 message.append(QString::number(qlonglong(QThread::currentThread()->currentThread()), 16));
1631#ifdef QLOGGING_HAVE_BACKTRACE
1632 } else if (token == backtraceTokenC) {
1633 QMessagePattern::BacktraceParams backtraceParams = pattern->backtraceArgs.at(backtraceArgsIdx);
1634 backtraceArgsIdx++;
1635 message.append(formatBacktraceForLogMessage(backtraceParams, context.function));
1636#endif
1637 } else if (token == timeTokenC) {
1638 QString timeFormat = pattern->timeArgs.at(timeArgsIdx);
1639 timeArgsIdx++;
1640 if (timeFormat == "process"_L1) {
1641 quint64 ms = pattern->timer.elapsed();
1642 message.append(QString::asprintf("%6d.%03d", uint(ms / 1000), uint(ms % 1000)));
1643 } else if (timeFormat == "boot"_L1) {
1644 // just print the milliseconds since the elapsed timer reference
1645 // like the Linux kernel does
1647 message.append(QString::asprintf("%6d.%03d", uint(ms / 1000), uint(ms % 1000)));
1648#if QT_CONFIG(datestring)
1649 } else if (timeFormat.isEmpty()) {
1651 } else {
1652 message.append(QDateTime::currentDateTime().toString(timeFormat));
1653#endif // QT_CONFIG(datestring)
1654 }
1655#endif // !QT_BOOTSTRAPPED
1656 } else if (token == ifCategoryTokenC) {
1657 if (isDefaultCategory(context.category))
1658 skip = true;
1659#define HANDLE_IF_TOKEN(LEVEL) \
1660 } else if (token == if##LEVEL##TokenC) { \
1661 skip = type != Qt##LEVEL##Msg;
1662 HANDLE_IF_TOKEN(Debug)
1664 HANDLE_IF_TOKEN(Warning)
1665 HANDLE_IF_TOKEN(Critical)
1666 HANDLE_IF_TOKEN(Fatal)
1667#undef HANDLE_IF_TOKEN
1668 } else {
1670 }
1671 }
1672 return message;
1673}
1674
1676
1677// pointer to QtMessageHandler debug handler (with context)
1679
1680// ------------------------ Alternate logging sinks -------------------------
1681
1682#if defined(QT_BOOTSTRAPPED)
1683 // Bootstrapped tools always print to stderr, so no need for alternate sinks
1684#else
1685
1686#if QT_CONFIG(slog2)
1687#ifndef QT_LOG_CODE
1688#define QT_LOG_CODE 9000
1689#endif
1690
1691static bool slog2_default_handler(QtMsgType type, const QMessageLogContext &context, const QString &message)
1692{
1693 if (shouldLogToStderr())
1694 return false; // Leave logging up to stderr handler
1695
1696 QString formattedMessage = qFormatLogMessage(type, context, message);
1697 formattedMessage.append(u'\n');
1698 if (slog2_set_default_buffer((slog2_buffer_t)-1) == 0) {
1699 slog2_buffer_set_config_t buffer_config;
1700 slog2_buffer_t buffer_handle;
1701
1702 buffer_config.buffer_set_name = __progname;
1703 buffer_config.num_buffers = 1;
1704 buffer_config.verbosity_level = SLOG2_DEBUG1;
1705 buffer_config.buffer_config[0].buffer_name = "default";
1706 buffer_config.buffer_config[0].num_pages = 8;
1707
1708 if (slog2_register(&buffer_config, &buffer_handle, 0) == -1) {
1709 fprintf(stderr, "Error registering slogger2 buffer!\n");
1710 fprintf(stderr, "%s", formattedMessage.toLocal8Bit().constData());
1711 fflush(stderr);
1712 return false;
1713 }
1714
1715 // Set as the default buffer
1716 slog2_set_default_buffer(buffer_handle);
1717 }
1718 int severity = SLOG2_INFO;
1719 //Determines the severity level
1720 switch (type) {
1721 case QtDebugMsg:
1722 severity = SLOG2_DEBUG1;
1723 break;
1724 case QtInfoMsg:
1725 severity = SLOG2_INFO;
1726 break;
1727 case QtWarningMsg:
1728 severity = SLOG2_NOTICE;
1729 break;
1730 case QtCriticalMsg:
1731 severity = SLOG2_WARNING;
1732 break;
1733 case QtFatalMsg:
1734 severity = SLOG2_ERROR;
1735 break;
1736 }
1737 //writes to the slog2 buffer
1738 slog2c(NULL, QT_LOG_CODE, severity, formattedMessage.toLocal8Bit().constData());
1739
1740 return true; // Prevent further output to stderr
1741}
1742#endif // slog2
1743
1744#if QT_CONFIG(journald)
1745static bool systemd_default_message_handler(QtMsgType type,
1747 const QString &message)
1748{
1749 if (shouldLogToStderr())
1750 return false; // Leave logging up to stderr handler
1751
1752 QString formattedMessage = qFormatLogMessage(type, context, message);
1753
1754 int priority = LOG_INFO; // Informational
1755 switch (type) {
1756 case QtDebugMsg:
1757 priority = LOG_DEBUG; // Debug-level messages
1758 break;
1759 case QtInfoMsg:
1760 priority = LOG_INFO; // Informational conditions
1761 break;
1762 case QtWarningMsg:
1763 priority = LOG_WARNING; // Warning conditions
1764 break;
1765 case QtCriticalMsg:
1766 priority = LOG_CRIT; // Critical conditions
1767 break;
1768 case QtFatalMsg:
1769 priority = LOG_ALERT; // Action must be taken immediately
1770 break;
1771 }
1772
1773 sd_journal_send("MESSAGE=%s", formattedMessage.toUtf8().constData(),
1774 "PRIORITY=%i", priority,
1775 "CODE_FUNC=%s", context.function ? context.function : "unknown",
1776 "CODE_LINE=%d", context.line,
1777 "CODE_FILE=%s", context.file ? context.file : "unknown",
1778 "QT_CATEGORY=%s", context.category ? context.category : "unknown",
1779 NULL);
1780
1781 return true; // Prevent further output to stderr
1782}
1783#endif
1784
1785#if QT_CONFIG(syslog)
1786static bool syslog_default_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message)
1787{
1788 if (shouldLogToStderr())
1789 return false; // Leave logging up to stderr handler
1790
1791 QString formattedMessage = qFormatLogMessage(type, context, message);
1792
1793 int priority = LOG_INFO; // Informational
1794 switch (type) {
1795 case QtDebugMsg:
1796 priority = LOG_DEBUG; // Debug-level messages
1797 break;
1798 case QtInfoMsg:
1799 priority = LOG_INFO; // Informational conditions
1800 break;
1801 case QtWarningMsg:
1802 priority = LOG_WARNING; // Warning conditions
1803 break;
1804 case QtCriticalMsg:
1805 priority = LOG_CRIT; // Critical conditions
1806 break;
1807 case QtFatalMsg:
1808 priority = LOG_ALERT; // Action must be taken immediately
1809 break;
1810 }
1811
1812 syslog(priority, "%s", formattedMessage.toUtf8().constData());
1813
1814 return true; // Prevent further output to stderr
1815}
1816#endif
1817
1818#ifdef Q_OS_ANDROID
1819static bool android_default_message_handler(QtMsgType type,
1821 const QString &message)
1822{
1823 if (shouldLogToStderr())
1824 return false; // Leave logging up to stderr handler
1825
1826 QString formattedMessage = qFormatLogMessage(type, context, message);
1827
1828 android_LogPriority priority = ANDROID_LOG_DEBUG;
1829 switch (type) {
1830 case QtDebugMsg:
1831 priority = ANDROID_LOG_DEBUG;
1832 break;
1833 case QtInfoMsg:
1834 priority = ANDROID_LOG_INFO;
1835 break;
1836 case QtWarningMsg:
1837 priority = ANDROID_LOG_WARN;
1838 break;
1839 case QtCriticalMsg:
1840 priority = ANDROID_LOG_ERROR;
1841 break;
1842 case QtFatalMsg:
1843 priority = ANDROID_LOG_FATAL;
1844 break;
1845 };
1846
1847 // If application name is a tag ensure it has no spaces
1848 // If a category is defined, use it as an Android logging tag
1849 __android_log_print(priority, isDefaultCategory(context.category) ?
1851 "%s\n", qPrintable(formattedMessage));
1852
1853 return true; // Prevent further output to stderr
1854}
1855#endif //Q_OS_ANDROID
1856
1857#ifdef Q_OS_WIN
1858static void win_outputDebugString_helper(const QString &message)
1859{
1860 const qsizetype maxOutputStringLength = 32766;
1861 Q_CONSTINIT static QBasicMutex m;
1862 auto locker = qt_unique_lock(m);
1863 // fast path: Avoid string copies if one output is enough
1864 if (message.length() <= maxOutputStringLength) {
1865 OutputDebugString(reinterpret_cast<const wchar_t *>(message.utf16()));
1866 } else {
1867 wchar_t *messagePart = new wchar_t[maxOutputStringLength + 1];
1868 for (qsizetype i = 0; i < message.length(); i += maxOutputStringLength) {
1869 const qsizetype length = qMin(message.length() - i, maxOutputStringLength);
1870 const qsizetype len = QStringView{message}.mid(i, length).toWCharArray(messagePart);
1871 Q_ASSERT(len == length);
1872 messagePart[len] = 0;
1873 OutputDebugString(messagePart);
1874 }
1875 delete[] messagePart;
1876 }
1877}
1878
1879static bool win_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message)
1880{
1881 if (shouldLogToStderr())
1882 return false; // Leave logging up to stderr handler
1883
1884 const QString formattedMessage = qFormatLogMessage(type, context, message).append(u'\n');
1885 win_outputDebugString_helper(formattedMessage);
1886
1887 return true; // Prevent further output to stderr
1888}
1889#endif
1890
1891#ifdef Q_OS_WASM
1892static bool wasm_default_message_handler(QtMsgType type,
1894 const QString &message)
1895{
1896 if (shouldLogToStderr())
1897 return false; // Leave logging up to stderr handler
1898
1899 QString formattedMessage = qFormatLogMessage(type, context, message);
1900 int emOutputFlags = (EM_LOG_CONSOLE | EM_LOG_DEMANGLE);
1901 QByteArray localMsg = message.toLocal8Bit();
1902 switch (type) {
1903 case QtDebugMsg:
1904 break;
1905 case QtInfoMsg:
1906 break;
1907 case QtWarningMsg:
1908 emOutputFlags |= EM_LOG_WARN;
1909 break;
1910 case QtCriticalMsg:
1911 emOutputFlags |= EM_LOG_ERROR;
1912 break;
1913 case QtFatalMsg:
1914 emOutputFlags |= EM_LOG_ERROR;
1915 }
1916 emscripten_log(emOutputFlags, "%s\n", qPrintable(formattedMessage));
1917
1918 return true; // Prevent further output to stderr
1919}
1920#endif
1921
1922#endif // Bootstrap check
1923
1924// --------------------------------------------------------------------------
1925
1927{
1928 QString formattedMessage = qFormatLogMessage(type, context, message);
1929
1930 // print nothing if message pattern didn't apply / was empty.
1931 // (still print empty lines, e.g. because message itself was empty)
1932 if (formattedMessage.isNull())
1933 return;
1934
1935 fprintf(stderr, "%s\n", formattedMessage.toLocal8Bit().constData());
1936 fflush(stderr);
1937}
1938
1943 const QString &message)
1944{
1945 bool handledStderr = false;
1946
1947 // A message sink logs the message to a structured or unstructured destination,
1948 // optionally formatting the message if the latter, and returns true if the sink
1949 // handled stderr output as well, which will shortcut our default stderr output.
1950 // In the future, if we allow multiple/dynamic sinks, this will be iterating
1951 // a list of sinks.
1952
1953#if !defined(QT_BOOTSTRAPPED)
1954# if defined(Q_OS_WIN)
1955 handledStderr |= win_message_handler(type, context, message);
1956# elif QT_CONFIG(slog2)
1957 handledStderr |= slog2_default_handler(type, context, message);
1958# elif QT_CONFIG(journald)
1959 handledStderr |= systemd_default_message_handler(type, context, message);
1960# elif QT_CONFIG(syslog)
1961 handledStderr |= syslog_default_message_handler(type, context, message);
1962# elif defined(Q_OS_ANDROID)
1963 handledStderr |= android_default_message_handler(type, context, message);
1964# elif defined(QT_USE_APPLE_UNIFIED_LOGGING)
1966# elif defined Q_OS_WASM
1967 handledStderr |= wasm_default_message_handler(type, context, message);
1968# endif
1969#endif
1970
1971 if (!handledStderr)
1973}
1974
1975#if defined(Q_COMPILER_THREAD_LOCAL)
1976
1977Q_CONSTINIT static thread_local bool msgHandlerGrabbed = false;
1978
1979static bool grabMessageHandler()
1980{
1981 if (msgHandlerGrabbed)
1982 return false;
1983
1984 msgHandlerGrabbed = true;
1985 return true;
1986}
1987
1988static void ungrabMessageHandler()
1989{
1990 msgHandlerGrabbed = false;
1991}
1992
1993#else
1994static bool grabMessageHandler() { return true; }
1995static void ungrabMessageHandler() { }
1996#endif // (Q_COMPILER_THREAD_LOCAL)
1997
1999{
2000#ifndef QT_BOOTSTRAPPED
2001 Q_TRACE(qt_message_print, msgType, context.category, context.function, context.file, context.line, message);
2002
2003 // qDebug, qWarning, ... macros do not check whether category is enabledgc
2004 if (msgType != QtFatalMsg && isDefaultCategory(context.category)) {
2005 if (QLoggingCategory *defaultCategory = QLoggingCategory::defaultCategory()) {
2006 if (!defaultCategory->isEnabled(msgType))
2007 return;
2008 }
2009 }
2010#endif
2011
2012 // prevent recursion in case the message handler generates messages
2013 // itself, e.g. by using Qt API
2014 if (grabMessageHandler()) {
2015 const auto ungrab = qScopeGuard([]{ ungrabMessageHandler(); });
2016 auto msgHandler = messageHandler.loadAcquire();
2017 (msgHandler ? msgHandler : qDefaultMessageHandler)(msgType, context, message);
2018 } else {
2019 fprintf(stderr, "%s\n", message.toLocal8Bit().constData());
2020 }
2021}
2022
2024{
2025#if defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED)
2026 if (!shouldLogToStderr()) {
2027 win_outputDebugString_helper(message);
2028 return;
2029 }
2030#endif
2031 fprintf(stderr, "%s", message.toLocal8Bit().constData());
2032 fflush(stderr);
2033}
2034
2036{
2037#if defined(Q_CC_MSVC_ONLY) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR)
2038 wchar_t contextFileL[256];
2039 // we probably should let the compiler do this for us, by declaring QMessageLogContext::file to
2040 // be const wchar_t * in the first place, but the #ifdefery above is very complex and we
2041 // wouldn't be able to change it later on...
2042 convert_to_wchar_t_elided(contextFileL, sizeof contextFileL / sizeof *contextFileL,
2043 context.file);
2044 // get the current report mode
2045 int reportMode = _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW);
2046 _CrtSetReportMode(_CRT_ERROR, reportMode);
2047
2048 int ret = _CrtDbgReportW(_CRT_ERROR, contextFileL, context.line, _CRT_WIDE(QT_VERSION_STR),
2049 reinterpret_cast<const wchar_t *>(message.utf16()));
2050 if ((ret == 0) && (reportMode & _CRTDBG_MODE_WNDW))
2051 return; // ignore
2052 else if (ret == 1)
2053 _CrtDbgBreak();
2054#else
2057#endif
2058
2059 qAbort();
2060}
2061
2062
2067{
2068 qt_message_print(msgType, context, message);
2069 if (isFatal(msgType))
2070 qt_message_fatal(msgType, context, message);
2071}
2072
2073void qErrnoWarning(const char *msg, ...)
2074{
2075 // qt_error_string() will allocate anyway, so we don't have
2076 // to be careful here (like we do in plain qWarning())
2077 QString error_string = qt_error_string(-1); // before vasprintf changes errno/GetLastError()
2078
2079 va_list ap;
2080 va_start(ap, msg);
2081 QString buf = QString::vasprintf(msg, ap);
2082 va_end(ap);
2083
2084 buf += " ("_L1 + error_string + u')';
2087}
2088
2089void qErrnoWarning(int code, const char *msg, ...)
2090{
2091 // qt_error_string() will allocate anyway, so we don't have
2092 // to be careful here (like we do in plain qWarning())
2093 va_list ap;
2094 va_start(ap, msg);
2095 QString buf = QString::vasprintf(msg, ap);
2096 va_end(ap);
2097
2098 buf += " ("_L1 + qt_error_string(code) + u')';
2101}
2102
2226{
2227 const auto old = messageHandler.fetchAndStoreOrdered(h);
2228 if (old)
2229 return old;
2230 else
2232}
2233
2235{
2236 const auto locker = qt_scoped_lock(QMessagePattern::mutex);
2237
2238 if (!qMessagePattern()->fromEnvironment)
2239 qMessagePattern()->setPattern(pattern);
2240}
2241
2242
2251QMessageLogContext &QMessageLogContext::copyContextFrom(const QMessageLogContext &logContext) noexcept
2252{
2253 this->category = logContext.category;
2254 this->file = logContext.file;
2255 this->line = logContext.line;
2256 this->function = logContext.function;
2257 return *this;
2258}
2259
static bool messageHandler(QtMsgType msgType, const QMessageLogContext &context, const QString &message, const QString &subsystem=QString())
\inmodule QtCore
Definition qatomic.h:112
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
\inmodule QtCore
Definition qchar.h:48
static constexpr QChar fromLatin1(char c) noexcept
Converts the Latin-1 character c to its equivalent QChar.
Definition qchar.h:461
static qint64 applicationPid() Q_DECL_CONST_FUNCTION
QString applicationName
the name of this application
static QDateTime currentDateTime()
This is an overloaded member function, provided for convenience. It differs from the above function o...
static QDeadlineTimer current(Qt::TimerType timerType=Qt::CoarseTimer) noexcept
Returns a QDeadlineTimer that is expired but is guaranteed to contain the current time.
qint64 deadline() const noexcept Q_DECL_PURE_FUNCTION
Returns the absolute time point for the deadline stored in QDeadlineTimer object, calculated in milli...
\inmodule QtCore
\inmodule QtCore
void start() noexcept
Starts this timer.
qint64 size() const
Returns the file size in bytes.
constexpr const char * data() const noexcept
constexpr qsizetype size() const noexcept
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
void append(parameter_type t)
Definition qlist.h:441
void clear()
Definition qlist.h:417
\inmodule QtCore
bool isInfoEnabled() const
Returns true if informational messages should be shown for this category; false otherwise.
static QLoggingCategory * defaultCategory()
Returns a pointer to the global category "default" that is used, for example, by qDebug(),...
bool isWarningEnabled() const
Returns true if warning messages should be shown for this category; false otherwise.
bool isCriticalEnabled() const
Returns true if critical messages should be shown for this category; false otherwise.
const char * categoryName() const
Returns the name of the category.
bool isDebugEnabled() const
Returns true if debug messages should be shown for this category; false otherwise.
\inmodule QtCore
Definition qlogging.h:39
const char * category
Definition qlogging.h:50
Q_DECL_COLD_FUNCTION QDebug critical() const
Logs a critical message using a QDebug stream.
Definition qlogging.cpp:833
Q_DECL_COLD_FUNCTION QDebug warning() const
Logs a warning message using a QDebug stream.
Definition qlogging.cpp:717
void void void void Q_DECL_COLD_FUNCTION void Q_DECL_COLD_FUNCTION void Q_DECL_COLD_FUNCTION void Q_DECL_COLD_FUNCTION void QT_MESSAGE_LOGGER_NORETURN Q_DECL_COLD_FUNCTION void QT_MESSAGE_LOGGER_NORETURN Q_DECL_COLD_FUNCTION void QDebug debug() const
Logs a debug message using a QDebug stream.
Definition qlogging.cpp:489
Q_DECL_COLD_FUNCTION QDebug fatal() const
Logs a fatal message using a QDebug stream.
Definition qlogging.cpp:950
QDebug info() const
Logs an informational message using a QDebug stream.
Definition qlogging.cpp:601
QNoDebug noDebug() const noexcept
Definition qlogging.cpp:534
void void Q_DECL_COLD_FUNCTION void Q_DECL_COLD_FUNCTION void QT_MESSAGE_LOGGER_NORETURN Q_DECL_COLD_FUNCTION void typedef const QLoggingCategory &(* CategoryFunction)()
This is a typedef for a pointer to a function with the following signature:
Definition qlogging.h:88
\inmodule QtCore
Definition qmutex.h:285
\inmodule QtCore \reentrant
QStringView capturedView(int nth=0) const
\inmodule QtCore \reentrant
QRegularExpressionMatch match(const QString &subject, qsizetype offset=0, MatchType matchType=NormalMatch, MatchOptions matchOptions=NoMatchOption) const
Attempts to match the regular expression against the given subject string, starting at the position o...
\inmodule QtCore
T * data() const noexcept
Returns the value of the pointer referenced by this object.
void reset(T *other=nullptr) noexcept(noexcept(Cleanup::cleanup(std::declval< T * >())))
Deletes the existing object it is pointing to (if any), and sets its pointer to other.
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:76
int toInt(bool *ok=nullptr, int base=10) const
Returns the string view converted to an int using base base, which is 10 by default and must be betwe...
Definition qstring.h:1025
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
qsizetype toWCharArray(wchar_t *array) const
Definition qstring.h:1151
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
QByteArray toLatin1() const &
Definition qstring.h:559
int toInt(bool *ok=nullptr, int base=10) const
Returns the string converted to an int using base base, which is 10 by default and must be between 2 ...
Definition qstring.h:660
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5788
void clear()
Clears the contents of the string and makes it null.
Definition qstring.h:1107
bool isNull() const
Returns true if this string is null; otherwise returns false.
Definition qstring.h:898
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
const QChar at(qsizetype i) const
Returns the character at the given index position in the string.
Definition qstring.h:1079
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string ends with s; otherwise returns false.
Definition qstring.cpp:5350
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
QByteArray toLocal8Bit() const &
Definition qstring.h:567
static QString vasprintf(const char *format, va_list ap) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7099
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:7822
QString & append(QChar c)
Definition qstring.cpp:3227
static QString static QString qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4420
QByteArray toUtf8() const &
Definition qstring.h:563
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7005
static Qt::HANDLE currentThreadId() noexcept Q_DECL_PURE_FUNCTION
Definition qthread.h:154
static QThread * currentThread()
Definition qthread.cpp:966
const QLoggingCategory & category()
[1]
QString str
[2]
static QT_PREPEND_NAMESPACE(qint64) qt_gettid()
Definition qlogging.cpp:113
static const char functionTokenC[]
static const char typeTokenC[]
static Q_NEVER_INLINE QString qt_message(QtMsgType msgType, const QMessageLogContext &context, const char *msg, va_list ap)
Definition qlogging.cpp:371
static Q_CONSTINIT QBasicAtomicPointer< void(QtMsgType, const QMessageLogContext &, const QString &)> messageHandler
static const char timeTokenC[]
static const char endifTokenC[]
static bool grabMessageHandler()
static const char ifInfoTokenC[]
void qt_message_output(QtMsgType msgType, const QMessageLogContext &context, const QString &message)
static const char emptyTokenC[]
static bool systemHasStderr()
Returns true if writing to stderr is supported.
Definition qlogging.cpp:227
static const char ifCategoryTokenC[]
static bool isDefaultCategory(const char *category)
Definition qlogging.cpp:216
static const char appnameTokenC[]
Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info)
Definition qlogging.cpp:990
static const char ifCriticalTokenC[]
#define HANDLE_IF_TOKEN(LEVEL)
static Q_NORETURN void qt_message_fatal(QtMsgType, const QMessageLogContext &context, const QString &message)
\inmodule QtCore \title Qt Logging Types
static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &buf)
static bool is_fatal_count_down(QAtomicInt &n)
Definition qlogging.cpp:187
static const char qthreadptrTokenC[]
static const char categoryTokenC[]
static const char lineTokenC[]
static void stderr_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message)
static const char messageTokenC[]
static const char fileTokenC[]
static const char pidTokenC[]
static const char threadidTokenC[]
static const char defaultPattern[]
static void ungrabMessageHandler()
static int checked_var_value(const char *varname)
Definition qlogging.cpp:173
void qErrnoWarning(const char *msg,...)
static const char ifWarningTokenC[]
#define IF_TOKEN(LEVEL)
static const char ifDebugTokenC[]
static const char backtraceTokenC[]
static const char ifFatalTokenC[]
static bool isFatal(QtMsgType msgType)
Definition qlogging.cpp:198
QtMessageHandler qInstallMessageHandler(QtMessageHandler h)
static void qt_message_print(QtMsgType, const QMessageLogContext &context, const QString &message)
static bool stderrHasConsoleAttached()
Returns true if writing to stderr will end up in a console/terminal visible to the user.
Definition qlogging.cpp:252
void qSetMessagePattern(const QString &pattern)
Token token
Definition keywords.cpp:444
Combined button and popup list for selecting options.
Lock qt_scoped_lock(Mutex &mutex)
Definition qlocking_p.h:58
Lock qt_unique_lock(Mutex &mutex)
Definition qlocking_p.h:64
\macro QT_NAMESPACE
bool shouldLogToStderr()
Returns true if logging stderr should be ensured.
Definition qlogging.cpp:305
@ ISODate
Definition qctf_p.h:77
static void * context
#define Q_BASIC_ATOMIC_INITIALIZER(a)
size_t qstrlen(const char *str)
Q_CORE_EXPORT char * qstrdup(const char *)
#define Q_NORETURN
#define Q_NEVER_INLINE
static int qt_safe_open(const char *pathname, int flags, mode_t mode=0777)
static int qt_safe_close(int fd)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction function
DBusConnection const char DBusError * error
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define QT_TERMINATE_ON_EXCEPTION(expr)
#define forever
Definition qforeach.h:78
void qAbort()
Definition qglobal.cpp:136
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
Q_DECL_COLD_FUNCTION Q_CORE_EXPORT QString qt_error_string(int errorCode=-1)
Q_CORE_EXPORT QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &buf)
QtMsgType
Definition qlogging.h:29
@ QtCriticalMsg
Definition qlogging.h:32
@ QtInfoMsg
Definition qlogging.h:34
@ QtWarningMsg
Definition qlogging.h:31
@ QtFatalMsg
Definition qlogging.h:33
@ QtDebugMsg
Definition qlogging.h:30
void(* QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &)
Definition qlogging.h:187
return ret
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLsizei const GLfloat * v
[13]
GLint GLenum GLsizei GLsizei GLsizei depth
const GLfloat * m
GLuint GLuint end
GLsizei const GLchar ** strings
[1]
GLenum GLuint GLenum GLsizei length
GLenum GLuint buffer
GLenum type
GLenum GLuint GLenum GLsizei const GLchar * buf
GLuint GLsizei const GLchar * message
GLenum GLenum severity
GLfloat n
GLfloat GLfloat GLfloat GLfloat h
const GLubyte * c
GLenum GLsizei len
GLenum const void * addr
GLuint64EXT * result
[6]
GLdouble s
[6]
Definition qopenglext.h:235
GLubyte * pattern
#define _PATH_TTY
static void decodeFrame(const QString &f, QV4::CppStackFrame **frame)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition qscopeguard.h:60
static void qYieldCpu()
Definition qsimd_p.h:403
#define qPrintable(string)
Definition qstring.h:1391
#define QStringLiteral(str)
#define Q_AUTOTEST_EXPORT
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
#define Q_UNUSED(x)
#define Q_TRACE(x,...)
Definition qtrace_p.h:144
#define Q_TRACE_POINT(provider, tracepoint,...)
Definition qtrace_p.h:232
@ Q_RELOCATABLE_TYPE
Definition qtypeinfo.h:145
#define Q_DECLARE_TYPEINFO(TYPE, FLAGS)
Definition qtypeinfo.h:163
unsigned long long quint64
Definition qtypes.h:56
ptrdiff_t qsizetype
Definition qtypes.h:70
unsigned int uint
Definition qtypes.h:29
long long qint64
Definition qtypes.h:55
qint64 qlonglong
Definition qtypes.h:58
ptrdiff_t qintptr
Definition qtypes.h:71
QFile file
[0]
QFileInfo info(fileName)
[8]
p rx()++
QFrame frame
[0]
char * toString(const MyType &t)
[31]
QList< QString > timeArgs
std::unique_ptr< std::unique_ptr< const char[]>[]> literals
void setPattern(const QString &pattern)
std::unique_ptr< const char *[]> tokens
QElapsedTimer timer
static QBasicMutex mutex