7#include <QtCore/qfile.h>
8#include <QtCore/qlibraryinfo.h>
9#include <QtCore/private/qlocking_p.h>
10#include <QtCore/qscopedvaluerollback.h>
11#include <QtCore/qstandardpaths.h>
12#include <QtCore/qstringtokenizer.h>
13#include <QtCore/qdir.h>
14#include <QtCore/qcoreapplication.h>
15#include <qplatformdefs.h>
17#if QT_CONFIG(settings)
18#include <QtCore/qsettings.h>
19#include <QtCore/private/qsettings_p.h>
24#define debugMsg QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC, "qt.core.logging").debug
25#define warnMsg QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC, "qt.core.logging").warning
26#define registryMsg QMessageLogger(QT_MESSAGELOG_FILE, QT_MESSAGELOG_LINE, QT_MESSAGELOG_FUNC, "_logging_categories").debug
30using namespace Qt::StringLiterals;
36
37
38
39QLoggingRule::QLoggingRule()
44
45
46
47QLoggingRule::QLoggingRule(QStringView pattern,
bool enabled) : enabled(enabled)
53
54
55
56
57int QLoggingRule::pass(QLatin1StringView cat, QtMsgType msgType)
const
60 if (messageType > -1 && messageType != msgType)
63 if (flags == FullText) {
66 return (enabled ? 1 : -1);
71 const qsizetype idx = cat.indexOf(category);
73 if (flags == MidFilter) {
75 return (enabled ? 1 : -1);
76 }
else if (flags == LeftFilter) {
79 return (enabled ? 1 : -1);
80 }
else if (flags == RightFilter) {
82 if (idx == (cat.size() - category.size()))
83 return (enabled ? 1 : -1);
90
91
92
93
94
95
96
97
98void QLoggingRule::parse(QStringView pattern)
103 if (pattern.endsWith(
".debug"_L1)) {
104 p = pattern.chopped(6);
105 messageType = QtDebugMsg;
106 }
else if (pattern.endsWith(
".info"_L1)) {
107 p = pattern.chopped(5);
108 messageType = QtInfoMsg;
109 }
else if (pattern.endsWith(
".warning"_L1)) {
110 p = pattern.chopped(8);
111 messageType = QtWarningMsg;
112 }
else if (pattern.endsWith(
".critical"_L1)) {
113 p = pattern.chopped(9);
114 messageType = QtCriticalMsg;
119 const QChar asterisk = u'*';
120 if (!p.contains(asterisk)) {
123 if (p.endsWith(asterisk)) {
127 if (p.startsWith(asterisk)) {
128 flags |= RightFilter;
131 if (p.contains(asterisk))
132 flags = PatternFlags();
135 category = p.toString();
139
140
141
142
143
144
145
146
147
148
149
150
151
154
155
156
157void QLoggingSettingsParser::setContent(QStringView content,
char16_t separator)
160 for (
auto line : qTokenize(content, separator))
165
166
167
168void QLoggingSettingsParser::setContent(FILE *stream)
172 constexpr size_t ChunkSize = 240;
173 QByteArray buffer(ChunkSize, Qt::Uninitialized);
174 auto readline = [&](FILE *stream) {
179 Q_ASSERT(buffer.size() + 1 ==
int(buffer.size() + 1));
180 char *s = fgets(buffer.begin(),
int(buffer.size() + 1), stream);
182 return QByteArrayView{};
184 qsizetype len = strlen(s);
185 while (len == buffer.size()) {
187 buffer.resizeForOverwrite(buffer.size() + ChunkSize);
188 s = fgets(buffer.end() - ChunkSize, ChunkSize + 1, stream);
193 QByteArrayView result(buffer.constBegin(), len);
194 if (result.endsWith(
'\n'))
200 while (!(line = readline(stream)).isNull())
201 parseNextLine(QString::fromUtf8(line));
205
206
207
209void QLoggingSettingsParser::parseNextLine(QStringView line)
212 line = line.trimmed();
215 if (line.startsWith(u';'))
218 if (line.startsWith(u'[') && line.endsWith(u']')) {
220 auto sectionName = line.mid(1).chopped(1).trimmed();
221 m_inRulesSection = sectionName.compare(
"rules"_L1, Qt::CaseInsensitive) == 0;
225 if (m_inRulesSection) {
226 const qsizetype equalPos = line.indexOf(u'=');
227 if (equalPos != -1) {
228 if (line.lastIndexOf(u'=') == equalPos) {
229 const auto key = line.left(equalPos).trimmed();
230#if QT_CONFIG(settings)
232 QSettingsPrivate::iniUnescapedKey(key.toUtf8(), tmp);
233 QStringView pattern = qToStringViewIgnoringNull(tmp);
235 QStringView pattern = key;
237 const auto valueStr = line.mid(equalPos + 1).trimmed();
239 if (valueStr ==
"true"_L1)
241 else if (valueStr ==
"false"_L1)
243 QLoggingRule rule(pattern, (value == 1));
244 if (rule.flags != 0 && (value != -1))
245 _rules.append(std::move(rule));
247 warnMsg(
"Ignoring malformed logging rule: '%s'", line.toUtf8().constData());
249 warnMsg(
"Ignoring malformed logging rule: '%s'", line.toUtf8().constData());
256
257
258
260 : categoryFilter(defaultCategoryFilter)
262 using U = QLoggingCategory::UnregisteredInitialization;
263 Q_ASSERT_X(!self,
"QLoggingRegistry",
"Singleton recreated");
267 auto cat =
new (defaultLoggingCategory) QLoggingCategory(U{}, defaultCategoryName);
268 categories.emplace(cat, QtDebugMsg);
270#if defined(Q_OS_ANDROID)
284 static const bool debugEnv = [] {
285 bool debug = qEnvironmentVariableIsSet(
"QT_LOGGING_DEBUG");
287 debugMsg(
"QT_LOGGING_DEBUG environment variable is set.");
290 return Q_UNLIKELY(debugEnv);
295 Q_ASSERT(!filePath.isEmpty());
297 debugMsg(
"Checking \"%s\" for rules",
298 QDir::toNativeSeparators(filePath).toUtf8().constData());
302 if (Q_UNLIKELY(filePath.at(0) == u':')) {
304 warnMsg(
"Attempted to load config rules from Qt resource path \"%ls\"",
305 qUtf16Printable(filePath));
312 FILE *f = _wfopen(
reinterpret_cast<
const wchar_t *>(filePath.constBegin()), L"rtN");
314 FILE *f = QT_FOPEN(QFile::encodeName(filePath).constBegin(),
"re");
318 parser.setContent(f);
320 if (qtLoggingDebug())
321 debugMsg(
"Loaded %td rules from \"%ls\"",
static_cast<ptrdiff_t>(parser.rules().size()),
322 qUtf16Printable(filePath));
323 return parser.rules();
324 }
else if (
int err = errno; err != ENOENT) {
325 warnMsg(
"Failed to load file \"%ls\": %ls", qUtf16Printable(filePath),
326 qUtf16Printable(qt_error_string(err)));
332
333
334
335
339 debugMsg(
"Initializing the rules database ...");
340 debugMsg(
"Checking %s environment variable",
"QT_LOGGING_CONF");
342 QList<QLoggingRule> er, qr, cr;
344 if (QString rulesFilePath = qEnvironmentVariable(
"QT_LOGGING_CONF"); !rulesFilePath.isEmpty())
345 er = loadRulesFromFile(rulesFilePath);
347 if (qtLoggingDebug())
348 debugMsg(
"Checking %s environment variable",
"QT_LOGGING_RULES");
350 const QString rulesSrc = qEnvironmentVariable(
"QT_LOGGING_RULES");
351 if (!rulesSrc.isEmpty()) {
353 parser.setImplicitRulesSection(
true);
354 parser.setContent(rulesSrc, u';');
356 if (qtLoggingDebug())
357 debugMsg(
"Loaded %td rules",
static_cast<ptrdiff_t>(parser.rules().size()));
359 er += parser.rules();
362 const QString configFileName = u"QtProject/qtlogging.ini"_s;
363 QStringView baseConfigFileName = QStringView(configFileName).sliced(strlen(
"QtProject"));
364 Q_ASSERT(baseConfigFileName.startsWith(u'/'));
367 qr = loadRulesFromFile(QLibraryInfo::path(QLibraryInfo::DataPath) + baseConfigFileName);
371 const QStringList configPaths =
372 QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, configFileName);
373 for (qsizetype i = configPaths.size(); i > 0; --i)
374 cr += loadRulesFromFile(configPaths[i - 1]);
376 const QMutexLocker locker(®istryMutex);
378 ruleSets[EnvironmentRules] = std::move(er);
379 ruleSets[QtConfigRules] = std::move(qr);
380 ruleSets[ConfigRules] = std::move(cr);
382 if (!ruleSets[EnvironmentRules].isEmpty() || !ruleSets[QtConfigRules].isEmpty() || !ruleSets[ConfigRules].isEmpty())
387
388
389
390
391
394 const auto locker = qt_scoped_lock(registryMutex);
396 auto r = categories.tryEmplace(cat, enableForLevel);
399 (*categoryFilter)(cat);
404
405
406
409 const auto locker = qt_scoped_lock(registryMutex);
410 categories.remove(cat);
414
415
416
417
418
419
420
422 const char *environment)
424 qtCategoryEnvironmentOverrides.insert_or_assign(categoryName, environment);
428
429
430
434 parser.setImplicitRulesSection(
true);
435 parser.setContent(content);
437 if (qtLoggingDebug())
438 debugMsg(
"Loading logging rules set by QLoggingCategory::setFilterRules ...");
440 const QMutexLocker locker(®istryMutex);
442 ruleSets[ApiRules] = parser.rules();
448
449
450
451
452
455 for (
auto it = categories.keyBegin(), end = categories.keyEnd(); it != end; ++it)
456 (*categoryFilter)(*it);
460
461
462
463QLoggingCategory::CategoryFilter
466 const auto locker = qt_scoped_lock(registryMutex);
469 filter = defaultCategoryFilter;
471 QLoggingCategory::CategoryFilter old = categoryFilter;
472 categoryFilter = filter;
481 Q_CONSTINIT
thread_local bool recursionGuard =
false;
484 QScopedValueRollback<
bool> rollback(recursionGuard,
true);
485 return qtLoggingRegistry();
497 return std::launder(
reinterpret_cast<QLoggingCategory *>(defaultLoggingCategory));
501
502
503
504
505
509 Q_ASSERT(reg->categories.contains(cat));
510 QtMsgType enableForLevel = reg->categories.value(cat);
514 bool debug = (enableForLevel == QtDebugMsg);
515 bool info = debug || (enableForLevel == QtInfoMsg);
516 bool warning = info || (enableForLevel == QtWarningMsg);
517 bool critical = warning || (enableForLevel == QtCriticalMsg);
522 if (
const char *categoryName = cat->categoryName()) {
524 if (strcmp(categoryName,
"qt") == 0) {
526 }
else if (strncmp(categoryName,
"qt.", 3) == 0) {
528 auto it = reg->qtCategoryEnvironmentOverrides.find(categoryName);
529 if (it == reg->qtCategoryEnvironmentOverrides.end())
532 debug = qEnvironmentVariableIntValue(it->second);
536 const auto categoryName = QLatin1StringView(cat->categoryName());
538 for (
const auto &ruleSet : reg->ruleSets) {
539 for (
const auto &rule : ruleSet) {
540 int filterpass = rule.pass(categoryName, QtDebugMsg);
542 debug = (filterpass > 0);
543 filterpass = rule.pass(categoryName, QtInfoMsg);
545 info = (filterpass > 0);
546 filterpass = rule.pass(categoryName, QtWarningMsg);
548 warning = (filterpass > 0);
549 filterpass = rule.pass(categoryName, QtCriticalMsg);
551 critical = (filterpass > 0);
555 cat->setEnabled(QtDebugMsg, debug);
556 cat->setEnabled(QtInfoMsg, info);
557 cat->setEnabled(QtWarningMsg, warning);
558 cat->setEnabled(QtCriticalMsg, critical);
560 for (
const auto &ruleSet : reg->ruleSets) {
561 for (
const auto &rule : ruleSet) {
563 if (rule.messageType != QtDebugMsg && rule.flags != QLoggingRule::FullText)
565 if (rule.category !=
"_logging_categories"_L1)
570 cat->isDebugEnabled(),
571 cat->isWarningEnabled(),
572 cat->isCriticalEnabled(),
void unregisterCategory(QLoggingCategory *category)
Q_CORE_EXPORT void registerEnvironmentOverrideForCategory(const char *categoryName, const char *environment)
Q_AUTOTEST_EXPORT void initializeRules()
void registerCategory(QLoggingCategory *category, QtMsgType enableForLevel)
void setApiRules(const QString &content)
static QList< QLoggingRule > loadRulesFromFile(const QString &filePath)
static bool qtLoggingDebug()
static unsigned char defaultLoggingCategory[sizeof(QLoggingCategory)]