QtBase  v6.3.1
qlogging.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2021 The Qt Company Ltd.
4 ** Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com>
5 ** Copyright (C) 2022 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 "qdeadlinetimer.h"
57 #include "qdatetime.h"
58 #include "qcoreapplication.h"
59 #include "qthread.h"
60 #include "private/qloggingregistry_p.h"
61 #include "private/qcoreapplication_p.h"
62 #include "private/qsimd_p.h"
63 #include <qtcore_tracepoints_p.h>
64 #endif
65 #ifdef Q_OS_WIN
66 #include <qt_windows.h>
67 #endif
68 #ifdef Q_CC_MSVC
69 #include <intrin.h>
70 #endif
71 #if QT_CONFIG(slog2)
72 #include <sys/slog2.h>
73 #endif
74 #if __has_include(<paths.h>)
75 #include <paths.h>
76 #endif
77 
78 #ifdef Q_OS_ANDROID
79 #include <android/log.h>
80 #endif
81 
82 #ifdef Q_OS_DARWIN
83 #include <QtCore/private/qcore_mac_p.h>
84 #endif
85 
86 #if QT_CONFIG(journald)
87 # define SD_JOURNAL_SUPPRESS_LOCATION
88 # include <systemd/sd-journal.h>
89 # include <syslog.h>
90 #endif
91 #if QT_CONFIG(syslog)
92 # include <syslog.h>
93 #endif
94 #ifdef Q_OS_UNIX
95 # include <sys/types.h>
96 # include <sys/stat.h>
97 # include <unistd.h>
98 # include "private/qcore_unix_p.h"
99 #endif
100 
101 #ifdef Q_OS_WASM
102 #include <emscripten/emscripten.h>
103 #endif
104 
105 #if QT_CONFIG(slog2)
106 extern char *__progname;
107 #endif
108 
109 #ifndef QT_BOOTSTRAPPED
110 #if __has_include(<cxxabi.h>) && QT_CONFIG(backtrace) && QT_CONFIG(regularexpression)
111 # include <qregularexpression.h>
112 # include BACKTRACE_HEADER
113 # include <cxxabi.h>
114 # define QLOGGING_HAVE_BACKTRACE
115 #endif
116 
117 #if defined(Q_OS_LINUX) && (defined(__GLIBC__) || __has_include(<sys/syscall.h>))
118 # include <sys/syscall.h>
119 
120 # if defined(Q_OS_ANDROID) && !defined(SYS_gettid)
121 # define SYS_gettid __NR_gettid
122 # endif
123 
124 static long qt_gettid()
125 {
126  // no error handling
127  // this syscall has existed since Linux 2.4.11 and cannot fail
128  return syscall(SYS_gettid);
129 }
130 #elif defined(Q_OS_DARWIN)
131 # include <pthread.h>
132 static int qt_gettid()
133 {
134  // no error handling: this call cannot fail
135  __uint64_t tid;
136  pthread_threadid_np(NULL, &tid);
137  return tid;
138 }
139 #elif defined(Q_OS_FREEBSD_KERNEL) && defined(__FreeBSD_version) && __FreeBSD_version >= 900031
140 # include <pthread_np.h>
141 static int qt_gettid()
142 {
143  return pthread_getthreadid_np();
144 }
145 #else
146 static QT_PREPEND_NAMESPACE(qint64) qt_gettid()
147 {
150 }
151 #endif
152 #endif // !QT_BOOTSTRAPPED
153 
154 #include <cstdlib>
155 #include <algorithm>
156 #include <memory>
157 #include <vector>
158 
159 #include <stdio.h>
160 
162 
163 #if !defined(Q_CC_MSVC)
165 #endif
166 static void qt_message_fatal(QtMsgType, const QMessageLogContext &context, const QString &message);
167 static void qt_message_print(QtMsgType, const QMessageLogContext &context, const QString &message);
168 static void qt_message_print(const QString &message);
169 
170 static int checked_var_value(const char *varname)
171 {
172  // qEnvironmentVariableIntValue returns 0 on both parsing failure and on
173  // empty, but we need to distinguish between the two for backwards
174  // compatibility reasons.
175  QByteArray str = qgetenv(varname);
176  if (str.isEmpty())
177  return 0;
178 
179  bool ok;
180  int value = str.toInt(&ok, 0);
181  return ok ? value : 1;
182 }
183 
184 static bool isFatal(QtMsgType msgType)
185 {
186  if (msgType == QtFatalMsg)
187  return true;
188 
189  if (msgType == QtCriticalMsg) {
190  static QAtomicInt fatalCriticals = checked_var_value("QT_FATAL_CRITICALS");
191 
192  // it's fatal if the current value is exactly 1,
193  // otherwise decrement if it's non-zero
194  return fatalCriticals.loadRelaxed() && fatalCriticals.fetchAndAddRelaxed(-1) == 1;
195  }
196 
197  if (msgType == QtWarningMsg || msgType == QtCriticalMsg) {
198  static QAtomicInt fatalWarnings = checked_var_value("QT_FATAL_WARNINGS");
199 
200  // it's fatal if the current value is exactly 1,
201  // otherwise decrement if it's non-zero
202  return fatalWarnings.loadRelaxed() && fatalWarnings.fetchAndAddRelaxed(-1) == 1;
203  }
204 
205  return false;
206 }
207 
208 static bool isDefaultCategory(const char *category)
209 {
210  return !category || strcmp(category, "default") == 0;
211 }
212 
219 static bool systemHasStderr()
220 {
221  return true;
222 }
223 
244 static bool stderrHasConsoleAttached()
245 {
246  static const bool stderrHasConsoleAttached = []() -> bool {
247  if (!systemHasStderr())
248  return false;
249 
250  if (qEnvironmentVariableIntValue("QT_LOGGING_TO_CONSOLE")) {
251  fprintf(stderr, "warning: Environment variable QT_LOGGING_TO_CONSOLE is deprecated, use\n"
252  "QT_ASSUME_STDERR_HAS_CONSOLE and/or QT_FORCE_STDERR_LOGGING instead.\n");
253  return true;
254  }
255 
256  if (qEnvironmentVariableIntValue("QT_ASSUME_STDERR_HAS_CONSOLE"))
257  return true;
258 
259 #if defined(Q_OS_WIN)
260  return GetConsoleWindow();
261 #elif defined(Q_OS_UNIX)
262 # ifndef _PATH_TTY
263 # define _PATH_TTY "/dev/tty"
264 # endif
265 
266  // If we can open /dev/tty, we have a controlling TTY
267  int ttyDevice = -1;
268  if ((ttyDevice = qt_safe_open(_PATH_TTY, O_RDONLY)) >= 0) {
269  qt_safe_close(ttyDevice);
270  return true;
271  } else if (errno == ENOENT || errno == EPERM || errno == ENXIO) {
272  // Fall back to isatty for some non-critical errors
273  return isatty(STDERR_FILENO);
274  } else {
275  return false;
276  }
277 #else
278  return false; // No way to detect if stderr has a console attached
279 #endif
280  }();
281 
282  return stderrHasConsoleAttached;
283 }
284 
285 
286 namespace QtPrivate {
287 
298 {
299  static bool forceStderrLogging = qEnvironmentVariableIntValue("QT_FORCE_STDERR_LOGGING");
300  return forceStderrLogging || stderrHasConsoleAttached();
301 }
302 
303 
304 } // QtPrivate
305 
306 using namespace QtPrivate;
307 
342 #if defined(Q_CC_MSVC) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR)
343 static inline void convert_to_wchar_t_elided(wchar_t *d, size_t space, const char *s) noexcept
344 {
345  size_t len = qstrlen(s);
346  if (len + 1 > space) {
347  const size_t skip = len - space + 4; // 4 for "..." + '\0'
348  s += skip;
349  len -= skip;
350  for (int i = 0; i < 3; ++i)
351  *d++ = L'.';
352  }
353  while (len--)
354  *d++ = *s++;
355  *d++ = 0;
356 }
357 #endif
358 
362 Q_NEVER_INLINE
363 static QString qt_message(QtMsgType msgType, const QMessageLogContext &context, const char *msg, va_list ap)
364 {
366  qt_message_print(msgType, context, buf);
367  return buf;
368 }
369 
370 #undef qDebug
377 void QMessageLogger::debug(const char *msg, ...) const
378 {
379  va_list ap;
380  va_start(ap, msg); // use variable arg list
381  const QString message = qt_message(QtDebugMsg, context, msg, ap);
382  va_end(ap);
383 
384  if (isFatal(QtDebugMsg))
385  qt_message_fatal(QtDebugMsg, context, message);
386 }
387 
388 
389 #undef qInfo
397 void QMessageLogger::info(const char *msg, ...) const
398 {
399  va_list ap;
400  va_start(ap, msg); // use variable arg list
401  const QString message = qt_message(QtInfoMsg, context, msg, ap);
402  va_end(ap);
403 
404  if (isFatal(QtInfoMsg))
405  qt_message_fatal(QtInfoMsg, context, message);
406 }
407 
429 void QMessageLogger::debug(const QLoggingCategory &cat, const char *msg, ...) const
430 {
431  if (!cat.isDebugEnabled())
432  return;
433 
434  QMessageLogContext ctxt;
435  ctxt.copyContextFrom(context);
436  ctxt.category = cat.categoryName();
437 
438  va_list ap;
439  va_start(ap, msg); // use variable arg list
440  const QString message = qt_message(QtDebugMsg, ctxt, msg, ap);
441  va_end(ap);
442 
443  if (isFatal(QtDebugMsg))
444  qt_message_fatal(QtDebugMsg, ctxt, message);
445 }
446 
455  const char *msg, ...) const
456 {
457  const QLoggingCategory &cat = (*catFunc)();
458  if (!cat.isDebugEnabled())
459  return;
460 
461  QMessageLogContext ctxt;
462  ctxt.copyContextFrom(context);
463  ctxt.category = cat.categoryName();
464 
465  va_list ap;
466  va_start(ap, msg); // use variable arg list
467  const QString message = qt_message(QtDebugMsg, ctxt, msg, ap);
468  va_end(ap);
469 
470  if (isFatal(QtDebugMsg))
471  qt_message_fatal(QtDebugMsg, ctxt, message);
472 }
473 
474 #ifndef QT_NO_DEBUG_STREAM
475 
482 {
483  QDebug dbg = QDebug(QtDebugMsg);
484  QMessageLogContext &ctxt = dbg.stream->context;
485  ctxt.copyContextFrom(context);
486  return dbg;
487 }
488 
496 {
497  QDebug dbg = QDebug(QtDebugMsg);
498  if (!cat.isDebugEnabled())
499  dbg.stream->message_output = false;
500 
501  QMessageLogContext &ctxt = dbg.stream->context;
502  ctxt.copyContextFrom(context);
503  ctxt.category = cat.categoryName();
504 
505  return dbg;
506 }
507 
515 {
516  return debug((*catFunc)());
517 }
518 
527 {
528  return QNoDebug();
529 }
530 
531 #endif
532 
540 void QMessageLogger::info(const QLoggingCategory &cat, const char *msg, ...) const
541 {
542  if (!cat.isInfoEnabled())
543  return;
544 
545  QMessageLogContext ctxt;
546  ctxt.copyContextFrom(context);
547  ctxt.category = cat.categoryName();
548 
549  va_list ap;
550  va_start(ap, msg); // use variable arg list
551  const QString message = qt_message(QtInfoMsg, ctxt, msg, ap);
552  va_end(ap);
553 
554  if (isFatal(QtInfoMsg))
555  qt_message_fatal(QtInfoMsg, ctxt, message);
556 }
557 
566  const char *msg, ...) const
567 {
568  const QLoggingCategory &cat = (*catFunc)();
569  if (!cat.isInfoEnabled())
570  return;
571 
572  QMessageLogContext ctxt;
573  ctxt.copyContextFrom(context);
574  ctxt.category = cat.categoryName();
575 
576  va_list ap;
577  va_start(ap, msg); // use variable arg list
578  const QString message = qt_message(QtInfoMsg, ctxt, msg, ap);
579  va_end(ap);
580 
581  if (isFatal(QtInfoMsg))
582  qt_message_fatal(QtInfoMsg, ctxt, message);
583 }
584 
585 #ifndef QT_NO_DEBUG_STREAM
586 
594 {
595  QDebug dbg = QDebug(QtInfoMsg);
596  QMessageLogContext &ctxt = dbg.stream->context;
597  ctxt.copyContextFrom(context);
598  return dbg;
599 }
600 
608 {
609  QDebug dbg = QDebug(QtInfoMsg);
610  if (!cat.isInfoEnabled())
611  dbg.stream->message_output = false;
612 
613  QMessageLogContext &ctxt = dbg.stream->context;
614  ctxt.copyContextFrom(context);
615  ctxt.category = cat.categoryName();
616 
617  return dbg;
618 }
619 
627 {
628  return info((*catFunc)());
629 }
630 
631 #endif
632 
633 #undef qWarning
640 void QMessageLogger::warning(const char *msg, ...) const
641 {
642  va_list ap;
643  va_start(ap, msg); // use variable arg list
644  const QString message = qt_message(QtWarningMsg, context, msg, ap);
645  va_end(ap);
646 
647  if (isFatal(QtWarningMsg))
648  qt_message_fatal(QtWarningMsg, context, message);
649 }
650 
658 void QMessageLogger::warning(const QLoggingCategory &cat, const char *msg, ...) const
659 {
660  if (!cat.isWarningEnabled())
661  return;
662 
663  QMessageLogContext ctxt;
664  ctxt.copyContextFrom(context);
665  ctxt.category = cat.categoryName();
666 
667  va_list ap;
668  va_start(ap, msg); // use variable arg list
669  const QString message = qt_message(QtWarningMsg, ctxt, msg, ap);
670  va_end(ap);
671 
672  if (isFatal(QtWarningMsg))
673  qt_message_fatal(QtWarningMsg, ctxt, message);
674 }
675 
684  const char *msg, ...) const
685 {
686  const QLoggingCategory &cat = (*catFunc)();
687  if (!cat.isWarningEnabled())
688  return;
689 
690  QMessageLogContext ctxt;
691  ctxt.copyContextFrom(context);
692  ctxt.category = cat.categoryName();
693 
694  va_list ap;
695  va_start(ap, msg); // use variable arg list
696  const QString message = qt_message(QtWarningMsg, ctxt, msg, ap);
697  va_end(ap);
698 
699  if (isFatal(QtWarningMsg))
700  qt_message_fatal(QtWarningMsg, ctxt, message);
701 }
702 
703 #ifndef QT_NO_DEBUG_STREAM
710 {
711  QDebug dbg = QDebug(QtWarningMsg);
712  QMessageLogContext &ctxt = dbg.stream->context;
713  ctxt.copyContextFrom(context);
714  return dbg;
715 }
716 
723 {
724  QDebug dbg = QDebug(QtWarningMsg);
725  if (!cat.isWarningEnabled())
726  dbg.stream->message_output = false;
727 
728  QMessageLogContext &ctxt = dbg.stream->context;
729  ctxt.copyContextFrom(context);
730  ctxt.category = cat.categoryName();
731 
732  return dbg;
733 }
734 
742 {
743  return warning((*catFunc)());
744 }
745 
746 #endif
747 
748 #undef qCritical
749 
756 void QMessageLogger::critical(const char *msg, ...) const
757 {
758  va_list ap;
759  va_start(ap, msg); // use variable arg list
760  const QString message = qt_message(QtCriticalMsg, context, msg, ap);
761  va_end(ap);
762 
763  if (isFatal(QtCriticalMsg))
764  qt_message_fatal(QtCriticalMsg, context, message);
765 }
766 
774 void QMessageLogger::critical(const QLoggingCategory &cat, const char *msg, ...) const
775 {
776  if (!cat.isCriticalEnabled())
777  return;
778 
779  QMessageLogContext ctxt;
780  ctxt.copyContextFrom(context);
781  ctxt.category = cat.categoryName();
782 
783  va_list ap;
784  va_start(ap, msg); // use variable arg list
785  const QString message = qt_message(QtCriticalMsg, ctxt, msg, ap);
786  va_end(ap);
787 
788  if (isFatal(QtCriticalMsg))
789  qt_message_fatal(QtCriticalMsg, ctxt, message);
790 }
791 
800  const char *msg, ...) const
801 {
802  const QLoggingCategory &cat = (*catFunc)();
803  if (!cat.isCriticalEnabled())
804  return;
805 
806  QMessageLogContext ctxt;
807  ctxt.copyContextFrom(context);
808  ctxt.category = cat.categoryName();
809 
810  va_list ap;
811  va_start(ap, msg); // use variable arg list
812  const QString message = qt_message(QtCriticalMsg, ctxt, msg, ap);
813  va_end(ap);
814 
815  if (isFatal(QtCriticalMsg))
816  qt_message_fatal(QtCriticalMsg, ctxt, message);
817 }
818 
819 #ifndef QT_NO_DEBUG_STREAM
826 {
827  QDebug dbg = QDebug(QtCriticalMsg);
828  QMessageLogContext &ctxt = dbg.stream->context;
829  ctxt.copyContextFrom(context);
830  return dbg;
831 }
832 
840 {
841  QDebug dbg = QDebug(QtCriticalMsg);
842  if (!cat.isCriticalEnabled())
843  dbg.stream->message_output = false;
844 
845  QMessageLogContext &ctxt = dbg.stream->context;
846  ctxt.copyContextFrom(context);
847  ctxt.category = cat.categoryName();
848 
849  return dbg;
850 }
851 
859 {
860  return critical((*catFunc)());
861 }
862 
863 #endif
864 
865 #undef qFatal
872 void QMessageLogger::fatal(const char *msg, ...) const noexcept
873 {
875 
876  va_list ap;
877  va_start(ap, msg); // use variable arg list
878  QT_TERMINATE_ON_EXCEPTION(message = qt_message(QtFatalMsg, context, msg, ap));
879  va_end(ap);
880 
881  qt_message_fatal(QtFatalMsg, context, message);
882 }
883 
888 {
889  // Strip the function info down to the base function name
890  // note that this throws away the template definitions,
891  // the parameter types (overloads) and any const/volatile qualifiers.
892 
893  if (info.isEmpty())
894  return info;
895 
896  int pos;
897 
898  // Skip trailing [with XXX] for templates (gcc), but make
899  // sure to not affect Objective-C message names.
900  pos = info.size() - 1;
901  if (info.endsWith(']') && !(info.startsWith('+') || info.startsWith('-'))) {
902  while (--pos) {
903  if (info.at(pos) == '[')
904  info.truncate(pos);
905  }
906  }
907 
908  // operator names with '(', ')', '<', '>' in it
909  static const char operator_call[] = "operator()";
910  static const char operator_lessThan[] = "operator<";
911  static const char operator_greaterThan[] = "operator>";
912  static const char operator_lessThanEqual[] = "operator<=";
913  static const char operator_greaterThanEqual[] = "operator>=";
914 
915  // canonize operator names
916  info.replace("operator ", "operator");
917 
918  // remove argument list
919  forever {
920  int parencount = 0;
921  pos = info.lastIndexOf(')');
922  if (pos == -1) {
923  // Don't know how to parse this function name
924  return info;
925  }
926 
927  // find the beginning of the argument list
928  --pos;
929  ++parencount;
930  while (pos && parencount) {
931  if (info.at(pos) == ')')
932  ++parencount;
933  else if (info.at(pos) == '(')
934  --parencount;
935  --pos;
936  }
937  if (parencount != 0)
938  return info;
939 
940  info.truncate(++pos);
941 
942  if (info.at(pos - 1) == ')') {
943  if (info.indexOf(operator_call) == pos - (int)strlen(operator_call))
944  break;
945 
946  // this function returns a pointer to a function
947  // and we matched the arguments of the return type's parameter list
948  // try again
949  info.remove(0, info.indexOf('('));
950  info.chop(1);
951  continue;
952  } else {
953  break;
954  }
955  }
956 
957  // find the beginning of the function name
958  int parencount = 0;
959  int templatecount = 0;
960  --pos;
961 
962  // make sure special characters in operator names are kept
963  if (pos > -1) {
964  switch (info.at(pos)) {
965  case ')':
966  if (info.indexOf(operator_call) == pos - (int)strlen(operator_call) + 1)
967  pos -= 2;
968  break;
969  case '<':
970  if (info.indexOf(operator_lessThan) == pos - (int)strlen(operator_lessThan) + 1)
971  --pos;
972  break;
973  case '>':
974  if (info.indexOf(operator_greaterThan) == pos - (int)strlen(operator_greaterThan) + 1)
975  --pos;
976  break;
977  case '=': {
978  int operatorLength = (int)strlen(operator_lessThanEqual);
979  if (info.indexOf(operator_lessThanEqual) == pos - operatorLength + 1)
980  pos -= 2;
981  else if (info.indexOf(operator_greaterThanEqual) == pos - operatorLength + 1)
982  pos -= 2;
983  break;
984  }
985  default:
986  break;
987  }
988  }
989 
990  while (pos > -1) {
991  if (parencount < 0 || templatecount < 0)
992  return info;
993 
994  char c = info.at(pos);
995  if (c == ')')
996  ++parencount;
997  else if (c == '(')
998  --parencount;
999  else if (c == '>')
1000  ++templatecount;
1001  else if (c == '<')
1002  --templatecount;
1003  else if (c == ' ' && templatecount == 0 && parencount == 0)
1004  break;
1005 
1006  --pos;
1007  }
1008  info = info.mid(pos + 1);
1009 
1010  // remove trailing '*', '&' that are part of the return argument
1011  while ((info.at(0) == '*')
1012  || (info.at(0) == '&'))
1013  info = info.mid(1);
1014 
1015  // we have the full function name now.
1016  // clean up the templates
1017  while ((pos = info.lastIndexOf('>')) != -1) {
1018  if (!info.contains('<'))
1019  break;
1020 
1021  // find the matching close
1022  int end = pos;
1023  templatecount = 1;
1024  --pos;
1025  while (pos && templatecount) {
1026  char c = info.at(pos);
1027  if (c == '>')
1028  ++templatecount;
1029  else if (c == '<')
1030  --templatecount;
1031  --pos;
1032  }
1033  ++pos;
1034  info.remove(pos, end - pos + 1);
1035  }
1036 
1037  return info;
1038 }
1039 
1040 // tokens as recognized in QT_MESSAGE_PATTERN
1041 static const char categoryTokenC[] = "%{category}";
1042 static const char typeTokenC[] = "%{type}";
1043 static const char messageTokenC[] = "%{message}";
1044 static const char fileTokenC[] = "%{file}";
1045 static const char lineTokenC[] = "%{line}";
1046 static const char functionTokenC[] = "%{function}";
1047 static const char pidTokenC[] = "%{pid}";
1048 static const char appnameTokenC[] = "%{appname}";
1049 static const char threadidTokenC[] = "%{threadid}";
1050 static const char qthreadptrTokenC[] = "%{qthreadptr}";
1051 static const char timeTokenC[] = "%{time"; //not a typo: this command has arguments
1052 static const char backtraceTokenC[] = "%{backtrace"; //ditto
1053 static const char ifCategoryTokenC[] = "%{if-category}";
1054 static const char ifDebugTokenC[] = "%{if-debug}";
1055 static const char ifInfoTokenC[] = "%{if-info}";
1056 static const char ifWarningTokenC[] = "%{if-warning}";
1057 static const char ifCriticalTokenC[] = "%{if-critical}";
1058 static const char ifFatalTokenC[] = "%{if-fatal}";
1059 static const char endifTokenC[] = "%{endif}";
1060 static const char emptyTokenC[] = "";
1061 
1062 static const char defaultPattern[] = "%{if-category}%{category}: %{endif}%{message}";
1063 
1065 {
1066  QMessagePattern();
1068 
1069  void setPattern(const QString &pattern);
1070 
1071  // 0 terminated arrays of literal tokens / literal or placeholder tokens
1072  std::unique_ptr<std::unique_ptr<const char[]>[]> literals;
1073  std::unique_ptr<const char *[]> tokens;
1074  QList<QString> timeArgs; // timeFormats in sequence of %{time
1075 #ifndef QT_BOOTSTRAPPED
1077 #endif
1078 #ifdef QLOGGING_HAVE_BACKTRACE
1080  {
1083  };
1084  QList<BacktraceParams> backtraceArgs; // backtrace arguments in sequence of %{backtrace
1085 #endif
1086 
1089 };
1090 #ifdef QLOGGING_HAVE_BACKTRACE
1092 #endif
1093 
1095 
1097 {
1098 #ifndef QT_BOOTSTRAPPED
1099  timer.start();
1100 #endif
1101  const QString envPattern = QString::fromLocal8Bit(qgetenv("QT_MESSAGE_PATTERN"));
1102  if (envPattern.isEmpty()) {
1103  setPattern(QLatin1String(defaultPattern));
1104  fromEnvironment = false;
1105  } else {
1106  setPattern(envPattern);
1107  fromEnvironment = true;
1108  }
1109 }
1110 
1112 
1114 {
1115  timeArgs.clear();
1116 #ifdef QLOGGING_HAVE_BACKTRACE
1117  backtraceArgs.clear();
1118 #endif
1119 
1120  // scanner
1121  QList<QString> lexemes;
1122  QString lexeme;
1123  bool inPlaceholder = false;
1124  for (int i = 0; i < pattern.size(); ++i) {
1125  const QChar c = pattern.at(i);
1126  if ((c == QLatin1Char('%'))
1127  && !inPlaceholder) {
1128  if ((i + 1 < pattern.size())
1129  && pattern.at(i + 1) == QLatin1Char('{')) {
1130  // beginning of placeholder
1131  if (!lexeme.isEmpty()) {
1132  lexemes.append(lexeme);
1133  lexeme.clear();
1134  }
1135  inPlaceholder = true;
1136  }
1137  }
1138 
1139  lexeme.append(c);
1140 
1141  if ((c == QLatin1Char('}') && inPlaceholder)) {
1142  // end of placeholder
1143  lexemes.append(lexeme);
1144  lexeme.clear();
1145  inPlaceholder = false;
1146  }
1147  }
1148  if (!lexeme.isEmpty())
1149  lexemes.append(lexeme);
1150 
1151  // tokenizer
1152  std::vector<std::unique_ptr<const char[]>> literalsVar;
1153  tokens.reset(new const char *[lexemes.size() + 1]);
1154  tokens[lexemes.size()] = nullptr;
1155 
1156  bool nestedIfError = false;
1157  bool inIf = false;
1158  QString error;
1159 
1160  for (int i = 0; i < lexemes.size(); ++i) {
1161  const QString lexeme = lexemes.at(i);
1162  if (lexeme.startsWith(QLatin1String("%{"))
1163  && lexeme.endsWith(QLatin1Char('}'))) {
1164  // placeholder
1165  if (lexeme == QLatin1String(typeTokenC)) {
1166  tokens[i] = typeTokenC;
1167  } else if (lexeme == QLatin1String(categoryTokenC))
1168  tokens[i] = categoryTokenC;
1169  else if (lexeme == QLatin1String(messageTokenC))
1170  tokens[i] = messageTokenC;
1171  else if (lexeme == QLatin1String(fileTokenC))
1172  tokens[i] = fileTokenC;
1173  else if (lexeme == QLatin1String(lineTokenC))
1174  tokens[i] = lineTokenC;
1175  else if (lexeme == QLatin1String(functionTokenC))
1176  tokens[i] = functionTokenC;
1177  else if (lexeme == QLatin1String(pidTokenC))
1178  tokens[i] = pidTokenC;
1179  else if (lexeme == QLatin1String(appnameTokenC))
1180  tokens[i] = appnameTokenC;
1181  else if (lexeme == QLatin1String(threadidTokenC))
1182  tokens[i] = threadidTokenC;
1183  else if (lexeme == QLatin1String(qthreadptrTokenC))
1184  tokens[i] = qthreadptrTokenC;
1185  else if (lexeme.startsWith(QLatin1String(timeTokenC))) {
1186  tokens[i] = timeTokenC;
1187  int spaceIdx = lexeme.indexOf(QChar::fromLatin1(' '));
1188  if (spaceIdx > 0)
1189  timeArgs.append(lexeme.mid(spaceIdx + 1, lexeme.length() - spaceIdx - 2));
1190  else
1191  timeArgs.append(QString());
1192  } else if (lexeme.startsWith(QLatin1String(backtraceTokenC))) {
1193 #ifdef QLOGGING_HAVE_BACKTRACE
1194  tokens[i] = backtraceTokenC;
1195  QString backtraceSeparator = QStringLiteral("|");
1196  int backtraceDepth = 5;
1197  static const QRegularExpression depthRx(QStringLiteral(" depth=(?|\"([^\"]*)\"|([^ }]*))"));
1198  static const QRegularExpression separatorRx(QStringLiteral(" separator=(?|\"([^\"]*)\"|([^ }]*))"));
1199  QRegularExpressionMatch m = depthRx.match(lexeme);
1200  if (m.hasMatch()) {
1201  int depth = m.capturedView(1).toInt();
1202  if (depth <= 0)
1203  error += QLatin1String("QT_MESSAGE_PATTERN: %{backtrace} depth must be a number greater than 0\n");
1204  else
1205  backtraceDepth = depth;
1206  }
1207  m = separatorRx.match(lexeme);
1208  if (m.hasMatch())
1209  backtraceSeparator = m.captured(1);
1210  BacktraceParams backtraceParams;
1211  backtraceParams.backtraceDepth = backtraceDepth;
1212  backtraceParams.backtraceSeparator = backtraceSeparator;
1213  backtraceArgs.append(backtraceParams);
1214 #else
1215  error += QLatin1String("QT_MESSAGE_PATTERN: %{backtrace} is not supported by this Qt build\n");
1216  tokens[i] = "";
1217 #endif
1218  }
1219 
1220 #define IF_TOKEN(LEVEL) \
1221  else if (lexeme == QLatin1String(LEVEL)) { \
1222  if (inIf) \
1223  nestedIfError = true; \
1224  tokens[i] = LEVEL; \
1225  inIf = true; \
1226  }
1227  IF_TOKEN(ifCategoryTokenC)
1228  IF_TOKEN(ifDebugTokenC)
1229  IF_TOKEN(ifInfoTokenC)
1230  IF_TOKEN(ifWarningTokenC)
1231  IF_TOKEN(ifCriticalTokenC)
1232  IF_TOKEN(ifFatalTokenC)
1233 #undef IF_TOKEN
1234  else if (lexeme == QLatin1String(endifTokenC)) {
1235  tokens[i] = endifTokenC;
1236  if (!inIf && !nestedIfError)
1237  error += QLatin1String("QT_MESSAGE_PATTERN: %{endif} without an %{if-*}\n");
1238  inIf = false;
1239  } else {
1240  tokens[i] = emptyTokenC;
1241  error += QStringLiteral("QT_MESSAGE_PATTERN: Unknown placeholder %1\n")
1242  .arg(lexeme);
1243  }
1244  } else {
1245  char *literal = new char[lexeme.size() + 1];
1246  strncpy(literal, lexeme.toLatin1().constData(), lexeme.size());
1247  literal[lexeme.size()] = '\0';
1248  literalsVar.emplace_back(literal);
1249  tokens[i] = literal;
1250  }
1251  }
1252  if (nestedIfError)
1253  error += QLatin1String("QT_MESSAGE_PATTERN: %{if-*} cannot be nested\n");
1254  else if (inIf)
1255  error += QLatin1String("QT_MESSAGE_PATTERN: missing %{endif}\n");
1256 
1257  if (!error.isEmpty())
1258  qt_message_print(error);
1259 
1260  literals.reset(new std::unique_ptr<const char[]>[literalsVar.size() + 1]);
1261  std::move(literalsVar.begin(), literalsVar.end(), &literals[0]);
1262 }
1263 
1264 #if defined(QLOGGING_HAVE_BACKTRACE) && !defined(QT_BOOTSTRAPPED)
1265 // make sure the function has "Message" in the name so the function is removed
1266 /*
1267  A typical backtrace in debug mode looks like:
1268  #0 backtraceFramesForLogMessage (frameCount=5) at qlogging.cpp:1296
1269  #1 formatBacktraceForLogMessage (backtraceParams=..., function=0x4040b8 "virtual void MyClass::myFunction(int)") at qlogging.cpp:1344
1270  #2 qFormatLogMessage (type=QtDebugMsg, context=..., str=...) at qlogging.cpp:1452
1271  #3 stderr_message_handler (type=QtDebugMsg, context=..., message=...) at qlogging.cpp:1744
1272  #4 qDefaultMessageHandler (type=QtDebugMsg, context=..., message=...) at qlogging.cpp:1795
1273  #5 qt_message_print (msgType=QtDebugMsg, context=..., message=...) at qlogging.cpp:1840
1274  #6 qt_message_output (msgType=QtDebugMsg, context=..., message=...) at qlogging.cpp:1891
1275  #7 QDebug::~QDebug (this=<optimized out>, __in_chrg=<optimized out>) at qdebug.h:111
1276 */
1277 static constexpr int TypicalBacktraceFrameCount = 8;
1278 
1279 #if ((defined(Q_CC_GNU) && defined(QT_COMPILER_SUPPORTS_SIMD_ALWAYS)) || __has_attribute(optimize)) \
1280  && !defined(Q_CC_INTEL) && !defined(Q_CC_CLANG)
1281 // force skipping the frame pointer, to save the backtrace() function some work
1282 __attribute__((optimize("omit-frame-pointer")))
1283 #endif
1284 static QStringList backtraceFramesForLogMessage(int frameCount)
1285 {
1287  if (frameCount == 0)
1288  return result;
1289 
1290  // The results of backtrace_symbols looks like this:
1291  // /lib/libc.so.6(__libc_start_main+0xf3) [0x4a937413]
1292  // The offset and function name are optional.
1293  // This regexp tries to extract the library name (without the path) and the function name.
1294  // This code is protected by QMessagePattern::mutex so it is thread safe on all compilers
1295  static const QRegularExpression rx(QStringLiteral("^(?:[^(]*/)?([^(/]+)\\(([^+]*)(?:[\\+[a-f0-9x]*)?\\) \\[[a-f0-9x]*\\]$"));
1296 
1297  QVarLengthArray<void *, 32> buffer(TypicalBacktraceFrameCount + frameCount);
1298  int n = backtrace(buffer.data(), buffer.size());
1299  if (n > 0) {
1300  int numberPrinted = 0;
1301  for (int i = 0; i < n && numberPrinted < frameCount; ++i) {
1302  QScopedPointer<char*, QScopedPointerPodDeleter> strings(backtrace_symbols(buffer.data() + i, 1));
1303  QString trace = QString::fromUtf8(strings.data()[0]);
1304  QRegularExpressionMatch m = rx.match(trace);
1305  if (m.hasMatch()) {
1306  QString library = m.captured(1);
1307  QString function = m.captured(2);
1308 
1309  // skip the trace from QtCore that are because of the qDebug itself
1310  if (!numberPrinted && library.contains(QLatin1String("Qt6Core"))
1311  && (function.isEmpty() || function.contains(QLatin1String("Message"), Qt::CaseInsensitive)
1312  || function.contains(QLatin1String("QDebug")))) {
1313  continue;
1314  }
1315 
1316  if (function.startsWith(QLatin1String("_Z"))) {
1318  abi::__cxa_demangle(function.toUtf8(), nullptr, nullptr, nullptr));
1319  if (demangled)
1320  function = QString::fromUtf8(qCleanupFuncinfo(demangled.data()));
1321  }
1322 
1323  if (function.isEmpty()) {
1324  result.append(QLatin1Char('?') + library + QLatin1Char('?'));
1325  } else {
1326  result.append(function);
1327  }
1328  } else {
1329  if (numberPrinted == 0) {
1330  // innermost, unknown frames are usually the logging framework itself
1331  continue;
1332  }
1333  result.append(QStringLiteral("???"));
1334  }
1335  numberPrinted++;
1336  }
1337  }
1338  return result;
1339 }
1340 
1341 static QString formatBacktraceForLogMessage(const QMessagePattern::BacktraceParams backtraceParams,
1342  const char *function)
1343 {
1344  QString backtraceSeparator = backtraceParams.backtraceSeparator;
1345  int backtraceDepth = backtraceParams.backtraceDepth;
1346 
1347  QStringList frames = backtraceFramesForLogMessage(backtraceDepth);
1348  if (frames.isEmpty())
1349  return QString();
1350 
1351  // if the first frame is unknown, replace it with the context function
1352  if (function && frames.at(0).startsWith(QLatin1Char('?')))
1353  frames[0] = QString::fromUtf8(qCleanupFuncinfo(function));
1354 
1355  return frames.join(backtraceSeparator);
1356 }
1357 #endif // QLOGGING_HAVE_BACKTRACE && !QT_BOOTSTRAPPED
1358 
1359 Q_GLOBAL_STATIC(QMessagePattern, qMessagePattern)
1360 
1361 
1376 {
1377  QString message;
1378 
1379  const auto locker = qt_scoped_lock(QMessagePattern::mutex);
1380 
1381  QMessagePattern *pattern = qMessagePattern();
1382  if (!pattern) {
1383  // after destruction of static QMessagePattern instance
1384  message.append(str);
1385  return message;
1386  }
1387 
1388  bool skip = false;
1389 
1390 #ifndef QT_BOOTSTRAPPED
1391  int timeArgsIdx = 0;
1392 #ifdef QLOGGING_HAVE_BACKTRACE
1393  int backtraceArgsIdx = 0;
1394 #endif
1395 #endif
1396 
1397  // we do not convert file, function, line literals to local encoding due to overhead
1398  for (int i = 0; pattern->tokens[i]; ++i) {
1399  const char *token = pattern->tokens[i];
1400  if (token == endifTokenC) {
1401  skip = false;
1402  } else if (skip) {
1403  // we skip adding messages, but we have to iterate over
1404  // timeArgsIdx and backtraceArgsIdx anyway
1405 #ifndef QT_BOOTSTRAPPED
1406  if (token == timeTokenC)
1407  timeArgsIdx++;
1408 #ifdef QLOGGING_HAVE_BACKTRACE
1409  else if (token == backtraceTokenC)
1410  backtraceArgsIdx++;
1411 #endif
1412 #endif
1413  } else if (token == messageTokenC) {
1414  message.append(str);
1415  } else if (token == categoryTokenC) {
1416 #ifndef Q_OS_ANDROID
1417  // Don't add the category to the message on Android
1418  message.append(QLatin1String(context.category));
1419 #endif
1420  } else if (token == typeTokenC) {
1421  switch (type) {
1422  case QtDebugMsg: message.append(QLatin1String("debug")); break;
1423  case QtInfoMsg: message.append(QLatin1String("info")); break;
1424  case QtWarningMsg: message.append(QLatin1String("warning")); break;
1425  case QtCriticalMsg:message.append(QLatin1String("critical")); break;
1426  case QtFatalMsg: message.append(QLatin1String("fatal")); break;
1427  }
1428  } else if (token == fileTokenC) {
1429  if (context.file)
1430  message.append(QLatin1String(context.file));
1431  else
1432  message.append(QLatin1String("unknown"));
1433  } else if (token == lineTokenC) {
1434  message.append(QString::number(context.line));
1435  } else if (token == functionTokenC) {
1436  if (context.function)
1438  else
1439  message.append(QLatin1String("unknown"));
1440 #ifndef QT_BOOTSTRAPPED
1441  } else if (token == pidTokenC) {
1443  } else if (token == appnameTokenC) {
1445  } else if (token == threadidTokenC) {
1446  // print the TID as decimal
1447  message.append(QString::number(qt_gettid()));
1448  } else if (token == qthreadptrTokenC) {
1449  message.append(QLatin1String("0x"));
1450  message.append(QString::number(qlonglong(QThread::currentThread()->currentThread()), 16));
1451 #ifdef QLOGGING_HAVE_BACKTRACE
1452  } else if (token == backtraceTokenC) {
1453  QMessagePattern::BacktraceParams backtraceParams = pattern->backtraceArgs.at(backtraceArgsIdx);
1454  backtraceArgsIdx++;
1455  message.append(formatBacktraceForLogMessage(backtraceParams, context.function));
1456 #endif
1457  } else if (token == timeTokenC) {
1458  QString timeFormat = pattern->timeArgs.at(timeArgsIdx);
1459  timeArgsIdx++;
1460  if (timeFormat == QLatin1String("process")) {
1461  quint64 ms = pattern->timer.elapsed();
1462  message.append(QString::asprintf("%6d.%03d", uint(ms / 1000), uint(ms % 1000)));
1463  } else if (timeFormat == QLatin1String("boot")) {
1464  // just print the milliseconds since the elapsed timer reference
1465  // like the Linux kernel does
1467  message.append(QString::asprintf("%6d.%03d", uint(ms / 1000), uint(ms % 1000)));
1468 #if QT_CONFIG(datestring)
1469  } else if (timeFormat.isEmpty()) {
1471  } else {
1472  message.append(QDateTime::currentDateTime().toString(timeFormat));
1473 #endif // QT_CONFIG(datestring)
1474  }
1475 #endif // !QT_BOOTSTRAPPED
1476  } else if (token == ifCategoryTokenC) {
1477  if (isDefaultCategory(context.category))
1478  skip = true;
1479 #define HANDLE_IF_TOKEN(LEVEL) \
1480  } else if (token == if##LEVEL##TokenC) { \
1481  skip = type != Qt##LEVEL##Msg;
1482  HANDLE_IF_TOKEN(Debug)
1483  HANDLE_IF_TOKEN(Info)
1484  HANDLE_IF_TOKEN(Warning)
1485  HANDLE_IF_TOKEN(Critical)
1486  HANDLE_IF_TOKEN(Fatal)
1487 #undef HANDLE_IF_TOKEN
1488  } else {
1489  message.append(QLatin1String(token));
1490  }
1491  }
1492  return message;
1493 }
1494 
1495 static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &buf);
1496 
1497 // pointer to QtMessageHandler debug handler (with context)
1498 static QBasicAtomicPointer<void (QtMsgType, const QMessageLogContext &, const QString &)> messageHandler = Q_BASIC_ATOMIC_INITIALIZER(nullptr);
1499 
1500 // ------------------------ Alternate logging sinks -------------------------
1501 
1502 #if defined(QT_BOOTSTRAPPED)
1503  // Bootstrapped tools always print to stderr, so no need for alternate sinks
1504 #else
1505 
1506 #if QT_CONFIG(slog2)
1507 #ifndef QT_LOG_CODE
1508 #define QT_LOG_CODE 9000
1509 #endif
1510 
1511 static bool slog2_default_handler(QtMsgType type, const QMessageLogContext &context, const QString &message)
1512 {
1513  if (shouldLogToStderr())
1514  return false; // Leave logging up to stderr handler
1515 
1516  QString formattedMessage = qFormatLogMessage(type, context, message);
1517  formattedMessage.append(QLatin1Char('\n'));
1518  if (slog2_set_default_buffer((slog2_buffer_t)-1) == 0) {
1519  slog2_buffer_set_config_t buffer_config;
1520  slog2_buffer_t buffer_handle;
1521 
1522  buffer_config.buffer_set_name = __progname;
1523  buffer_config.num_buffers = 1;
1524  buffer_config.verbosity_level = SLOG2_DEBUG1;
1525  buffer_config.buffer_config[0].buffer_name = "default";
1526  buffer_config.buffer_config[0].num_pages = 8;
1527 
1528  if (slog2_register(&buffer_config, &buffer_handle, 0) == -1) {
1529  fprintf(stderr, "Error registering slogger2 buffer!\n");
1530  fprintf(stderr, "%s", formattedMessage.toLocal8Bit().constData());
1531  fflush(stderr);
1532  return false;
1533  }
1534 
1535  // Set as the default buffer
1536  slog2_set_default_buffer(buffer_handle);
1537  }
1538  int severity = SLOG2_INFO;
1539  //Determines the severity level
1540  switch (type) {
1541  case QtDebugMsg:
1542  severity = SLOG2_DEBUG1;
1543  break;
1544  case QtInfoMsg:
1545  severity = SLOG2_INFO;
1546  break;
1547  case QtWarningMsg:
1548  severity = SLOG2_NOTICE;
1549  break;
1550  case QtCriticalMsg:
1551  severity = SLOG2_WARNING;
1552  break;
1553  case QtFatalMsg:
1554  severity = SLOG2_ERROR;
1555  break;
1556  }
1557  //writes to the slog2 buffer
1558  slog2c(NULL, QT_LOG_CODE, severity, formattedMessage.toLocal8Bit().constData());
1559 
1560  return true; // Prevent further output to stderr
1561 }
1562 #endif // slog2
1563 
1564 #if QT_CONFIG(journald)
1565 static bool systemd_default_message_handler(QtMsgType type,
1566  const QMessageLogContext &context,
1567  const QString &message)
1568 {
1569  if (shouldLogToStderr())
1570  return false; // Leave logging up to stderr handler
1571 
1572  QString formattedMessage = qFormatLogMessage(type, context, message);
1573 
1574  int priority = LOG_INFO; // Informational
1575  switch (type) {
1576  case QtDebugMsg:
1577  priority = LOG_DEBUG; // Debug-level messages
1578  break;
1579  case QtInfoMsg:
1580  priority = LOG_INFO; // Informational conditions
1581  break;
1582  case QtWarningMsg:
1583  priority = LOG_WARNING; // Warning conditions
1584  break;
1585  case QtCriticalMsg:
1586  priority = LOG_CRIT; // Critical conditions
1587  break;
1588  case QtFatalMsg:
1589  priority = LOG_ALERT; // Action must be taken immediately
1590  break;
1591  }
1592 
1593  sd_journal_send("MESSAGE=%s", formattedMessage.toUtf8().constData(),
1594  "PRIORITY=%i", priority,
1595  "CODE_FUNC=%s", context.function ? context.function : "unknown",
1596  "CODE_LINE=%d", context.line,
1597  "CODE_FILE=%s", context.file ? context.file : "unknown",
1598  "QT_CATEGORY=%s", context.category ? context.category : "unknown",
1599  NULL);
1600 
1601  return true; // Prevent further output to stderr
1602 }
1603 #endif
1604 
1605 #if QT_CONFIG(syslog)
1606 static bool syslog_default_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message)
1607 {
1608  if (shouldLogToStderr())
1609  return false; // Leave logging up to stderr handler
1610 
1611  QString formattedMessage = qFormatLogMessage(type, context, message);
1612 
1613  int priority = LOG_INFO; // Informational
1614  switch (type) {
1615  case QtDebugMsg:
1616  priority = LOG_DEBUG; // Debug-level messages
1617  break;
1618  case QtInfoMsg:
1619  priority = LOG_INFO; // Informational conditions
1620  break;
1621  case QtWarningMsg:
1622  priority = LOG_WARNING; // Warning conditions
1623  break;
1624  case QtCriticalMsg:
1625  priority = LOG_CRIT; // Critical conditions
1626  break;
1627  case QtFatalMsg:
1628  priority = LOG_ALERT; // Action must be taken immediately
1629  break;
1630  }
1631 
1632  syslog(priority, "%s", formattedMessage.toUtf8().constData());
1633 
1634  return true; // Prevent further output to stderr
1635 }
1636 #endif
1637 
1638 #ifdef Q_OS_ANDROID
1639 static bool android_default_message_handler(QtMsgType type,
1640  const QMessageLogContext &context,
1641  const QString &message)
1642 {
1643  if (shouldLogToStderr())
1644  return false; // Leave logging up to stderr handler
1645 
1646  QString formattedMessage = qFormatLogMessage(type, context, message);
1647 
1648  android_LogPriority priority = ANDROID_LOG_DEBUG;
1649  switch (type) {
1650  case QtDebugMsg:
1651  priority = ANDROID_LOG_DEBUG;
1652  break;
1653  case QtInfoMsg:
1654  priority = ANDROID_LOG_INFO;
1655  break;
1656  case QtWarningMsg:
1657  priority = ANDROID_LOG_WARN;
1658  break;
1659  case QtCriticalMsg:
1660  priority = ANDROID_LOG_ERROR;
1661  break;
1662  case QtFatalMsg:
1663  priority = ANDROID_LOG_FATAL;
1664  break;
1665  };
1666 
1667  // If application name is a tag ensure it has no spaces
1668  // If a category is defined, use it as an Android logging tag
1669  __android_log_print(priority, isDefaultCategory(context.category) ?
1670  qPrintable(QCoreApplication::applicationName().replace(" ", "_")) : context.category,
1671  "%s\n", qPrintable(formattedMessage));
1672 
1673  return true; // Prevent further output to stderr
1674 }
1675 #endif //Q_OS_ANDROID
1676 
1677 #ifdef Q_OS_WIN
1678 static void win_outputDebugString_helper(const QString &message)
1679 {
1680  const qsizetype maxOutputStringLength = 32766;
1681  static QBasicMutex m;
1682  auto locker = qt_unique_lock(m);
1683  // fast path: Avoid string copies if one output is enough
1684  if (message.length() <= maxOutputStringLength) {
1685  OutputDebugString(reinterpret_cast<const wchar_t *>(message.utf16()));
1686  } else {
1687  wchar_t *messagePart = new wchar_t[maxOutputStringLength + 1];
1688  for (qsizetype i = 0; i < message.length(); i += maxOutputStringLength) {
1689  const qsizetype length = qMin(message.length() - i, maxOutputStringLength);
1690  const qsizetype len = QStringView{message}.mid(i, length).toWCharArray(messagePart);
1691  Q_ASSERT(len == length);
1692  messagePart[len] = 0;
1693  OutputDebugString(messagePart);
1694  }
1695  delete[] messagePart;
1696  }
1697 }
1698 
1699 static bool win_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message)
1700 {
1701  if (shouldLogToStderr())
1702  return false; // Leave logging up to stderr handler
1703 
1704  const QString formattedMessage = qFormatLogMessage(type, context, message).append(QLatin1Char('\n'));
1705  win_outputDebugString_helper(formattedMessage);
1706 
1707  return true; // Prevent further output to stderr
1708 }
1709 #endif
1710 
1711 #ifdef Q_OS_WASM
1712 static bool wasm_default_message_handler(QtMsgType type,
1713  const QMessageLogContext &context,
1714  const QString &message)
1715 {
1716  if (shouldLogToStderr())
1717  return false; // Leave logging up to stderr handler
1718 
1719  QString formattedMessage = qFormatLogMessage(type, context, message);
1720  int emOutputFlags = (EM_LOG_CONSOLE | EM_LOG_DEMANGLE);
1721  QByteArray localMsg = message.toLocal8Bit();
1722  switch (type) {
1723  case QtDebugMsg:
1724  break;
1725  case QtInfoMsg:
1726  break;
1727  case QtWarningMsg:
1728  emOutputFlags |= EM_LOG_WARN;
1729  break;
1730  case QtCriticalMsg:
1731  emOutputFlags |= EM_LOG_ERROR;
1732  break;
1733  case QtFatalMsg:
1734  emOutputFlags |= EM_LOG_ERROR;
1735  }
1736  emscripten_log(emOutputFlags, "%s\n", qPrintable(formattedMessage));
1737 
1738  return true; // Prevent further output to stderr
1739 }
1740 #endif
1741 
1742 #endif // Bootstrap check
1743 
1744 // --------------------------------------------------------------------------
1745 
1746 static void stderr_message_handler(QtMsgType type, const QMessageLogContext &context, const QString &message)
1747 {
1748  QString formattedMessage = qFormatLogMessage(type, context, message);
1749 
1750  // print nothing if message pattern didn't apply / was empty.
1751  // (still print empty lines, e.g. because message itself was empty)
1752  if (formattedMessage.isNull())
1753  return;
1754 
1755  fprintf(stderr, "%s\n", formattedMessage.toLocal8Bit().constData());
1756  fflush(stderr);
1757 }
1758 
1762 static void qDefaultMessageHandler(QtMsgType type, const QMessageLogContext &context,
1763  const QString &message)
1764 {
1765  bool handledStderr = false;
1766 
1767  // A message sink logs the message to a structured or unstructured destination,
1768  // optionally formatting the message if the latter, and returns true if the sink
1769  // handled stderr output as well, which will shortcut our default stderr output.
1770  // In the future, if we allow multiple/dynamic sinks, this will be iterating
1771  // a list of sinks.
1772 
1773 #if !defined(QT_BOOTSTRAPPED)
1774 # if defined(Q_OS_WIN)
1775  handledStderr |= win_message_handler(type, context, message);
1776 # elif QT_CONFIG(slog2)
1777  handledStderr |= slog2_default_handler(type, context, message);
1778 # elif QT_CONFIG(journald)
1779  handledStderr |= systemd_default_message_handler(type, context, message);
1780 # elif QT_CONFIG(syslog)
1781  handledStderr |= syslog_default_message_handler(type, context, message);
1782 # elif defined(Q_OS_ANDROID)
1783  handledStderr |= android_default_message_handler(type, context, message);
1784 # elif defined(QT_USE_APPLE_UNIFIED_LOGGING)
1786 # elif defined Q_OS_WASM
1787  handledStderr |= wasm_default_message_handler(type, context, message);
1788 # endif
1789 #endif
1790 
1791  if (!handledStderr)
1792  stderr_message_handler(type, context, message);
1793 }
1794 
1795 #if defined(Q_COMPILER_THREAD_LOCAL)
1796 
1797 static thread_local bool msgHandlerGrabbed = false;
1798 
1799 static bool grabMessageHandler()
1800 {
1801  if (msgHandlerGrabbed)
1802  return false;
1803 
1804  msgHandlerGrabbed = true;
1805  return true;
1806 }
1807 
1808 static void ungrabMessageHandler()
1809 {
1810  msgHandlerGrabbed = false;
1811 }
1812 
1813 #else
1814 static bool grabMessageHandler() { return true; }
1815 static void ungrabMessageHandler() { }
1816 #endif // (Q_COMPILER_THREAD_LOCAL)
1817 
1818 static void qt_message_print(QtMsgType msgType, const QMessageLogContext &context, const QString &message)
1819 {
1820 #ifndef QT_BOOTSTRAPPED
1821  Q_TRACE(qt_message_print, msgType, context.category, context.function, context.file, context.line, message);
1822 
1823  // qDebug, qWarning, ... macros do not check whether category is enabledgc
1824  if (msgType != QtFatalMsg && isDefaultCategory(context.category)) {
1825  if (QLoggingCategory *defaultCategory = QLoggingCategory::defaultCategory()) {
1826  if (!defaultCategory->isEnabled(msgType))
1827  return;
1828  }
1829  }
1830 #endif
1831 
1832  // prevent recursion in case the message handler generates messages
1833  // itself, e.g. by using Qt API
1834  if (grabMessageHandler()) {
1835  const auto ungrab = qScopeGuard([]{ ungrabMessageHandler(); });
1836  auto msgHandler = messageHandler.loadAcquire();
1837  (msgHandler ? msgHandler : qDefaultMessageHandler)(msgType, context, message);
1838  } else {
1839  fprintf(stderr, "%s\n", message.toLocal8Bit().constData());
1840  }
1841 }
1842 
1843 static void qt_message_print(const QString &message)
1844 {
1845 #if defined(Q_OS_WIN) && !defined(QT_BOOTSTRAPPED)
1846  if (!shouldLogToStderr()) {
1847  win_outputDebugString_helper(message);
1848  return;
1849  }
1850 #endif
1851  fprintf(stderr, "%s", message.toLocal8Bit().constData());
1852  fflush(stderr);
1853 }
1854 
1855 static void qt_message_fatal(QtMsgType, const QMessageLogContext &context, const QString &message)
1856 {
1857 #if defined(Q_CC_MSVC) && defined(QT_DEBUG) && defined(_DEBUG) && defined(_CRT_ERROR)
1858  wchar_t contextFileL[256];
1859  // we probably should let the compiler do this for us, by declaring QMessageLogContext::file to
1860  // be const wchar_t * in the first place, but the #ifdefery above is very complex and we
1861  // wouldn't be able to change it later on...
1862  convert_to_wchar_t_elided(contextFileL, sizeof contextFileL / sizeof *contextFileL,
1863  context.file);
1864  // get the current report mode
1865  int reportMode = _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_WNDW);
1866  _CrtSetReportMode(_CRT_ERROR, reportMode);
1867 
1868  int ret = _CrtDbgReportW(_CRT_ERROR, contextFileL, context.line, _CRT_WIDE(QT_VERSION_STR),
1869  reinterpret_cast<const wchar_t *>(message.utf16()));
1870  if ((ret == 0) && (reportMode & _CRTDBG_MODE_WNDW))
1871  return; // ignore
1872  else if (ret == 1)
1873  _CrtDbgBreak();
1874 #else
1875  Q_UNUSED(context);
1876  Q_UNUSED(message);
1877 #endif
1878 
1879  qAbort();
1880 }
1881 
1882 
1887 {
1888  qt_message_print(msgType, context, message);
1889  if (isFatal(msgType))
1890  qt_message_fatal(msgType, context, message);
1891 }
1892 
1893 void qErrnoWarning(const char *msg, ...)
1894 {
1895  // qt_error_string() will allocate anyway, so we don't have
1896  // to be careful here (like we do in plain qWarning())
1897  QString error_string = qt_error_string(-1); // before vasprintf changes errno/GetLastError()
1898 
1899  va_list ap;
1900  va_start(ap, msg);
1902  va_end(ap);
1903 
1904  buf += QLatin1String(" (") + error_string + QLatin1Char(')');
1907 }
1908 
1909 void qErrnoWarning(int code, const char *msg, ...)
1910 {
1911  // qt_error_string() will allocate anyway, so we don't have
1912  // to be careful here (like we do in plain qWarning())
1913  va_list ap;
1914  va_start(ap, msg);
1916  va_end(ap);
1917 
1918  buf += QLatin1String(" (") + qt_error_string(code) + QLatin1Char(')');
1921 }
1922 
2043 {
2044  const auto old = messageHandler.fetchAndStoreOrdered(h);
2045  if (old)
2046  return old;
2047  else
2048  return qDefaultMessageHandler;
2049 }
2050 
2051 void qSetMessagePattern(const QString &pattern)
2052 {
2053  const auto locker = qt_scoped_lock(QMessagePattern::mutex);
2054 
2055  if (!qMessagePattern()->fromEnvironment)
2056  qMessagePattern()->setPattern(pattern);
2057 }
2058 
2059 
2068 QMessageLogContext &QMessageLogContext::copyContextFrom(const QMessageLogContext &logContext) noexcept
2069 {
2070  this->category = logContext.category;
2071  this->file = logContext.file;
2072  this->line = logContext.line;
2073  this->function = logContext.function;
2074  return *this;
2075 }
2076 
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
const char msg[]
Definition: arch.cpp:46
#define value
[5]
FT_Error error
Definition: cffdrivr.c:657
FT_Library library
Definition: cffdrivr.c:660
static bool messageHandler(QtMsgType msgType, const QMessageLogContext &context, const QString &message, const QString &subsystem=QString())
The QAtomicInt class provides platform-independent atomic operations on int.
Definition: qatomic.h:158
T fetchAndAddRelaxed(T valueToAdd) noexcept
Definition: qbasicatomic.h:140
T loadRelaxed() const noexcept
Definition: qbasicatomic.h:90
Type fetchAndStoreOrdered(Type newValue) noexcept
Definition: qbasicatomic.h:266
Type loadAcquire() const noexcept
Definition: qbasicatomic.h:233
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:85
const char * constData() const noexcept
Definition: qbytearray.h:144
The QChar class provides a 16-bit Unicode character.
Definition: qchar.h:84
static constexpr QChar fromLatin1(char c) noexcept
Definition: qchar.h:492
static qint64 applicationPid() Q_DECL_CONST_FUNCTION
QString applicationName
the name of this application
static QDateTime currentDateTime()
static QDeadlineTimer current(Qt::TimerType timerType=Qt::CoarseTimer) noexcept
qint64 deadline() const noexcept Q_DECL_PURE_FUNCTION
The QDebug class provides an output stream for debugging information.
Definition: qdebug.h:65
The QElapsedTimer class provides a fast way to calculate elapsed times.
Definition: qelapsedtimer.h:49
The QLatin1String class provides a thin wrapper around an US-ASCII/Latin-1 encoded string literal.
Definition: qstring.h:84
qsizetype size() const noexcept
Definition: qlist.h:414
const_reference at(qsizetype i) const noexcept
Definition: qlist.h:457
void append(parameter_type t)
Definition: qlist.h:469
The QLoggingCategory class represents a category, or 'area' in the logging infrastructure.
bool isInfoEnabled() const
static QLoggingCategory * defaultCategory()
bool isWarningEnabled() const
bool isCriticalEnabled() const
const char * categoryName() const
bool isDebugEnabled() const
The QMessageLogContext class provides additional information about a log message.
Definition: qlogging.h:70
const char * category
Definition: qlogging.h:81
Q_DECL_COLD_FUNCTION QDebug critical() const
Definition: qlogging.cpp:825
Q_DECL_COLD_FUNCTION QDebug warning() const
Definition: qlogging.cpp:709
void void void void Q_DECL_COLD_FUNCTION void Q_DECL_COLD_FUNCTION void Q_DECL_COLD_FUNCTION void Q_DECL_COLD_FUNCTION void Q_NORETURN Q_DECL_COLD_FUNCTION void QDebug debug() const
Definition: qlogging.cpp:481
void void void void Q_DECL_COLD_FUNCTION void Q_DECL_COLD_FUNCTION void Q_DECL_COLD_FUNCTION void Q_DECL_COLD_FUNCTION void Q_NORETURN Q_DECL_COLD_FUNCTION void fatal(const char *msg,...) const noexcept Q_ATTRIBUTE_FORMAT_PRINTF(2
Definition: qlogging.cpp:872
QDebug info() const
Definition: qlogging.cpp:593
QNoDebug noDebug() const noexcept
Definition: qlogging.cpp:526
void void Q_DECL_COLD_FUNCTION void Q_DECL_COLD_FUNCTION void typedef const QLoggingCategory &(* CategoryFunction)()
Definition: qlogging.h:111
The QMutex class provides access serialization between threads.
Definition: qmutex.h:285
The QRegularExpression class provides pattern matching using regular expressions.
QRegularExpressionMatch match(const QString &subject, qsizetype offset=0, MatchType matchType=NormalMatch, MatchOptions matchOptions=NoMatchOption) const
The QRegularExpressionMatch class provides the results of a matching a QRegularExpression against a s...
QStringView capturedView(int nth=0) const
The QScopedPointer class stores a pointer to a dynamically allocated object, and deletes it upon dest...
The QString class provides a Unicode character string.
Definition: qstring.h:388
QByteArray toLatin1() const &
Definition: qstring.h:745
int toInt(bool *ok=nullptr, int base=10) const
Definition: qstring.h:848
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition: qstring.cpp:5092
static QString fromLatin1(QByteArrayView ba)
Definition: qstring.cpp:5488
static QString fromLocal8Bit(QByteArrayView ba)
Definition: qstring.cpp:5563
void clear()
Definition: qstring.h:1240
bool isNull() const
Definition: qstring.h:1078
qsizetype size() const
Definition: qstring.h:413
static QString fromUtf8(QByteArrayView utf8)
Definition: qstring.cpp:5632
QString mid(qsizetype position, qsizetype n=-1) const
Definition: qstring.cpp:4994
const QChar at(qsizetype i) const
Definition: qstring.h:1212
bool endsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition: qstring.cpp:5143
bool isEmpty() const
Definition: qstring.h:1216
QByteArray toLocal8Bit() const &
Definition: qstring.h:753
static QString vasprintf(const char *format, va_list ap) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition: qstring.cpp:6853
static QString number(int, int base=10)
Definition: qstring.cpp:7538
QString & append(QChar c)
Definition: qstring.cpp:3152
static QString static QString qsizetype indexOf(QChar c, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition: qstring.cpp:4197
QByteArray toUtf8() const &
Definition: qstring.h:749
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition: qstring.cpp:6759
qsizetype length() const
Definition: qstring.h:415
The QStringList class provides a list of strings.
The QStringView class provides a unified view on UTF-16 strings with a read-only subset of the QStrin...
Definition: qstringview.h:122
int toInt(bool *ok=nullptr, int base=10) const
Definition: qstring.h:1176
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Definition: qstringview.h:261
qsizetype toWCharArray(wchar_t *array) const
static Qt::HANDLE currentThreadId() noexcept Q_DECL_PURE_FUNCTION
Definition: qthread.h:187
static QThread * currentThread()
Definition: qthread.cpp:879
void start(int msec)
Definition: qtimer.cpp:259
float rx
const QLoggingCategory & category()
[1]
QString str
[2]
set contains("Julia")
#define NULL
Definition: ftobjs.h:61
#define forever
Definition: ftrandom.c:53
Q_AUTOTEST_EXPORT QByteArray qCleanupFuncinfo(QByteArray info)
Definition: qlogging.cpp:887
#define HANDLE_IF_TOKEN(LEVEL)
Q_DECLARE_TYPEINFO(QMessagePattern::BacktraceParams, Q_RELOCATABLE_TYPE)
#define IF_TOKEN(LEVEL)
va_end(ap)
static void const void const char bool unsigned int int const char va_start(ap, message)
auto it unsigned count const
Definition: hb-iter.hh:848
#define __attribute__(x)
Definition: hb.hh:256
backing_store_ptr info
[4]
Definition: jmemsys.h:161
Token token
Definition: keywords.cpp:453
void toString(QString &appendTo, IPv4Address address)
Definition: qipaddress.cpp:131
std::chrono::milliseconds ms
Lock qt_scoped_lock(Mutex &mutex)
Definition: qlocking_p.h:94
Lock qt_unique_lock(Mutex &mutex)
Definition: qlocking_p.h:100
int toUtf8(char16_t u, OutputPtr &dst, InputPtr &src, InputPtr end)
@ ISODate
Definition: qnamespace.h:1254
@ CaseInsensitive
Definition: qnamespace.h:1283
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool startsWith(QByteArrayView haystack, QByteArrayView needle) noexcept
bool shouldLogToStderr()
Definition: qlogging.cpp:297
#define QString()
Definition: parse-defines.h:51
int PRIV() strcmp(PCRE2_SPTR str1, PCRE2_SPTR str2)
PCRE2_SIZE PRIV() strlen(PCRE2_SPTR str)
void
Definition: png.h:1080
#define Q_BASIC_ATOMIC_INITIALIZER(a)
size_t qstrlen(const char *str)
#define Q_NORETURN
EGLOutputLayerEXT EGLint EGLAttrib value
void qAbort()
Definition: qglobal.cpp:3372
#define Q_AUTOTEST_EXPORT
Definition: qglobal.h:579
unsigned long long quint64
Definition: qglobal.h:299
ptrdiff_t qsizetype
Definition: qglobal.h:308
unsigned int uint
Definition: qglobal.h:334
long long qint64
Definition: qglobal.h:298
qint64 qlonglong
Definition: qglobal.h:301
ptrdiff_t qintptr
Definition: qglobal.h:309
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
Q_CORE_EXPORT Q_DECL_COLD_FUNCTION void qErrnoWarning(int code, const char *msg,...)
Q_CORE_EXPORT QString qFormatLogMessage(QtMsgType type, const QMessageLogContext &context, const QString &buf)
Q_CORE_EXPORT void qSetMessagePattern(const QString &messagePattern)
Q_CORE_EXPORT QtMessageHandler qInstallMessageHandler(QtMessageHandler)
QtMsgType
Definition: qlogging.h:60
@ QtCriticalMsg
Definition: qlogging.h:63
@ QtInfoMsg
Definition: qlogging.h:65
@ QtWarningMsg
Definition: qlogging.h:62
@ QtFatalMsg
Definition: qlogging.h:64
@ QtDebugMsg
Definition: qlogging.h:61
Q_CORE_EXPORT void qt_message_output(QtMsgType, const QMessageLogContext &context, const QString &message)
void(* QtMessageHandler)(QtMsgType, const QMessageLogContext &, const QString &)
Definition: qlogging.h:204
GLenum GLuint GLenum GLsizei length
Definition: qopengl.h:270
GLenum type
Definition: qopengl.h:270
GLenum GLuint GLenum GLsizei const GLchar * message
Definition: qopengl.h:270
GLenum GLuint GLenum severity
Definition: qopengl.h:270
GLint GLenum GLsizei GLsizei GLsizei depth
const GLfloat * m
GLuint GLuint end
GLsizei const GLchar ** strings
[1]
GLenum GLuint buffer
GLenum GLuint GLenum GLsizei const GLchar * buf
GLfloat n
GLfloat GLfloat GLfloat GLfloat h
const GLubyte * c
Definition: qopenglext.h:12701
GLenum GLsizei len
Definition: qopenglext.h:3292
GLuint64EXT * result
[6]
Definition: qopenglext.h:10932
GLdouble s
[6]
Definition: qopenglext.h:235
GLubyte * pattern
Definition: qopenglext.h:2744
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
QScopeGuard< typename std::decay< F >::type > qScopeGuard(F &&f)
[qScopeGuard]
Definition: qscopeguard.h:93
#define QStringLiteral(str)
QString qt_error_string(int code)
#define Q_TRACE(x,...)
Definition: qtrace_p.h:136
@ Q_RELOCATABLE_TYPE
Definition: qtypeinfo.h:156
Q_UNUSED(salary)
[21]
QFile file
[0]
QTimer * timer
[3]
The QLatin1Char class provides an 8-bit ASCII/Latin-1 character.
Definition: qchar.h:53
QList< QString > timeArgs
Definition: qlogging.cpp:1074
std::unique_ptr< std::unique_ptr< const char[]>[]> literals
Definition: qlogging.cpp:1072
void setPattern(const QString &pattern)
Definition: qlogging.cpp:1113
std::unique_ptr< const char *[]> tokens
Definition: qlogging.cpp:1073
static QBasicMutex mutex
Definition: qlogging.cpp:1088
QList< BacktraceParams > backtraceArgs
Definition: qlogging.cpp:1084
QElapsedTimer timer
Definition: qlogging.cpp:1076
Definition: inftrees.h:24
int frameCount
Definition: texuploads.cpp:66
void optimize(RX &)
#define STDERR_FILENO
Definition: tst_qfile.cpp:115