qlogging: Journal: Log thread id, suppress empty fields

The TID field can be used to differentiate the threads of a process
where the message originates from. This allows to create a timeline
of events when identical messages are created from multiple threads,
or filter out messages from "uninteresting" threads in post-processing.

The file/line/func fields are typically empty for non-debug builds, so
storing these in the journal with dummy values is a waste of CPU time
and storage.

Use sd_journal_sendv instead of sd_journal_send, as that allows to vary
the number of sent fields, and skip the ones without actual information.
It is also slightly more performant, as it avoids the var-arg handling,
sprintf parsing and formatting etc. done by sd_journal_send.

[ChangeLog][QtCore][Logging] Qt now logs the thread id (TID) in journal,
allowing separation of identical messages from multiple threads.

Fixes: QTBUG-120047
Fixes: QTBUG-120048
Change-Id: Iccf3fe708dc4b896161693e13fb9012686bd1871
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Kai Köhne 2025-05-11 15:06:36 +02:00 committed by Thiago Macieira
parent 927798f5de
commit a6070847f0

View File

@ -1846,13 +1846,39 @@ static bool systemd_default_message_handler(QtMsgType type,
break;
}
sd_journal_send("MESSAGE=%s", message.toUtf8().constData(),
"PRIORITY=%i", priority,
"CODE_FUNC=%s", context.function ? context.function : "unknown",
"CODE_LINE=%d", context.line,
"CODE_FILE=%s", context.file ? context.file : "unknown",
"QT_CATEGORY=%s", context.category ? context.category : "unknown",
NULL);
// Explicit QByteArray instead of auto, to resolve the QStringBuilder proxy
const QByteArray messageField = "MESSAGE="_ba + message.toUtf8().constData();
const QByteArray priorityField = "PRIORITY="_ba + QByteArray::number(priority);
const QByteArray tidField = "TID="_ba + QByteArray::number(qlonglong(qt_gettid()));
const QByteArray fileField = context.file
? "CODE_FILE="_ba + context.file : QByteArray();
const QByteArray funcField = context.function
? "CODE_FUNC="_ba + context.function : QByteArray();
const QByteArray lineField = context.line
? "CODE_LINE="_ba + QByteArray::number(context.line) : QByteArray();
const QByteArray categoryField = context.category
? "QT_CATEGORY="_ba + context.category : QByteArray();
auto toIovec = [](const QByteArray &ba) {
return iovec{const_cast<char*>(ba.data()), ba.size()};
};
struct iovec fields[7] = {
toIovec(messageField),
toIovec(priorityField),
toIovec(tidField),
};
int nFields = 3;
if (context.file)
fields[nFields++] = toIovec(fileField);
if (context.function)
fields[nFields++] = toIovec(funcField);
if (context.line)
fields[nFields++] = toIovec(lineField);
if (context.category)
fields[nFields++] = toIovec(categoryField);
sd_journal_sendv(fields, nFields);
return true; // Prevent further output to stderr
}