Allow qDebug output to be configured by qSetMessagePattern()
Add qSetMessagePattern() to configure the default message pattern. This one can still be overwritten by setting the QT_MESSAGE_PATTERN environment variable. Without this method, there's actually no way to change the default output programatically. Since QT_MESSAGE_PATTERN is evaluated when the first message arrives, setting it via e.g. qputenv might have no effect/be too late. Change-Id: I115e0c30606f128fdbf5c169a951ffa2a6a48517 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>bb10
parent
e92e5fda44
commit
c0d249019b
|
|
@ -3088,5 +3088,38 @@ bool QInternal::activateCallbacks(Callback cb, void **parameters)
|
|||
instead.
|
||||
\sa QtMsgHandler, qInstallMessageHandler
|
||||
*/
|
||||
/*!
|
||||
\fn void qSetMessagePattern(const QString &pattern)
|
||||
\relates <QtGlobal>
|
||||
\since 5.0
|
||||
|
||||
\brief Changes the output of the default message handler.
|
||||
|
||||
Allows to tweak the output of qDebug(), qWarning(), qCritical() and qFatal().
|
||||
|
||||
Following placeholders are supported:
|
||||
|
||||
\table
|
||||
\header \li Placeholder \li Description
|
||||
\row \li \c %{appname} \li QCoreApplication::applicationName()
|
||||
\row \li \c %{file} \li Path to source file
|
||||
\row \li \c %{function} \li Function
|
||||
\row \li \c %{line} \li Line in source file
|
||||
\row \li \c %{message} \li The actual message
|
||||
\row \li \c %{pid} \li QCoreApplication::applicationPid()
|
||||
\row \li \c %{threadid} \li ID of current thread
|
||||
\row \li \c %{type} \li "debug", "warning", "critical" or "fatal"
|
||||
\endtable
|
||||
|
||||
The default pattern is "%{message}".
|
||||
|
||||
The pattern can also be changed at runtime by setting the QT_MESSAGE_PATTERN
|
||||
environment variable; if both qSetMessagePattern() is called and QT_MESSAGE_PATTERN is
|
||||
set, the environment variable takes precedence.
|
||||
|
||||
qSetMessagePattern() has no effect if a custom message handler is installed.
|
||||
|
||||
\sa qInstallMessageHandler, Debugging Techniques
|
||||
*/
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
#include "qstring.h"
|
||||
#include "qvarlengtharray.h"
|
||||
#include "qdebug.h"
|
||||
#include "qmutex.h"
|
||||
#ifndef QT_BOOTSTRAPPED
|
||||
#include "qcoreapplication.h"
|
||||
#include "qthread.h"
|
||||
|
|
@ -400,21 +401,53 @@ static const char appnameTokenC[] = "%{appname}";
|
|||
static const char threadidTokenC[] = "%{threadid}";
|
||||
static const char emptyTokenC[] = "";
|
||||
|
||||
static const char defaultPattern[] = "%{message}";
|
||||
|
||||
|
||||
struct QMessagePattern {
|
||||
QMessagePattern();
|
||||
~QMessagePattern();
|
||||
|
||||
void setPattern(const QString &pattern);
|
||||
|
||||
// 0 terminated arrays of literal tokens / literal or placeholder tokens
|
||||
const char **literals;
|
||||
const char **tokens;
|
||||
|
||||
bool fromEnvironment;
|
||||
static QBasicMutex mutex;
|
||||
};
|
||||
|
||||
QBasicMutex QMessagePattern::mutex;
|
||||
|
||||
QMessagePattern::QMessagePattern()
|
||||
: literals(0)
|
||||
, tokens(0)
|
||||
, fromEnvironment(false)
|
||||
{
|
||||
QString pattern = QString::fromLocal8Bit(qgetenv("QT_MESSAGE_PATTERN"));
|
||||
if (pattern.isEmpty()) {
|
||||
pattern = QLatin1String("%{message}");
|
||||
const QString envPattern = QString::fromLocal8Bit(qgetenv("QT_MESSAGE_PATTERN"));
|
||||
if (envPattern.isEmpty()) {
|
||||
setPattern(QLatin1String(defaultPattern));
|
||||
} else {
|
||||
setPattern(envPattern);
|
||||
fromEnvironment = true;
|
||||
}
|
||||
}
|
||||
|
||||
QMessagePattern::~QMessagePattern()
|
||||
{
|
||||
for (int i = 0; literals[i] != 0; ++i)
|
||||
delete [] literals[i];
|
||||
delete [] literals;
|
||||
literals = 0;
|
||||
delete [] tokens;
|
||||
tokens = 0;
|
||||
}
|
||||
|
||||
void QMessagePattern::setPattern(const QString &pattern)
|
||||
{
|
||||
delete [] tokens;
|
||||
delete [] literals;
|
||||
|
||||
// scanner
|
||||
QList<QString> lexemes;
|
||||
|
|
@ -495,16 +528,6 @@ QMessagePattern::QMessagePattern()
|
|||
memcpy(literals, literalsVar.constData(), literalsVar.size() * sizeof(const char*));
|
||||
}
|
||||
|
||||
QMessagePattern::~QMessagePattern()
|
||||
{
|
||||
for (int i = 0; literals[i] != 0; ++i)
|
||||
delete [] literals[i];
|
||||
delete [] literals;
|
||||
literals = 0;
|
||||
delete [] tokens;
|
||||
tokens = 0;
|
||||
}
|
||||
|
||||
Q_GLOBAL_STATIC(QMessagePattern, qMessagePattern)
|
||||
|
||||
/*!
|
||||
|
|
@ -515,6 +538,8 @@ Q_CORE_EXPORT QString qMessageFormatString(QtMsgType type, const QMessageLogCont
|
|||
{
|
||||
QString message;
|
||||
|
||||
QMutexLocker lock(&QMessagePattern::mutex);
|
||||
|
||||
QMessagePattern *pattern = qMessagePattern();
|
||||
if (!pattern) {
|
||||
// after destruction of static QMessagePattern instance
|
||||
|
|
@ -523,6 +548,10 @@ Q_CORE_EXPORT QString qMessageFormatString(QtMsgType type, const QMessageLogCont
|
|||
return message;
|
||||
}
|
||||
|
||||
// don't print anything if pattern was empty
|
||||
if (pattern->tokens[0] == 0)
|
||||
return message;
|
||||
|
||||
// we do not convert file, function, line literals to local encoding due to overhead
|
||||
for (int i = 0; pattern->tokens[i] != 0; ++i) {
|
||||
const char *token = pattern->tokens[i];
|
||||
|
|
@ -741,6 +770,14 @@ QtMsgHandler qInstallMsgHandler(QtMsgHandler h)
|
|||
return old;
|
||||
}
|
||||
|
||||
void qSetMessagePattern(const QString &pattern)
|
||||
{
|
||||
QMutexLocker lock(&QMessagePattern::mutex);
|
||||
|
||||
if (!qMessagePattern()->fromEnvironment)
|
||||
qMessagePattern()->setPattern(pattern);
|
||||
}
|
||||
|
||||
void QMessageLogContext::copy(const QMessageLogContext &logContext)
|
||||
{
|
||||
this->category = logContext.category;
|
||||
|
|
@ -748,4 +785,5 @@ void QMessageLogContext::copy(const QMessageLogContext &logContext)
|
|||
this->line = logContext.line;
|
||||
this->function = logContext.function;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -168,6 +168,8 @@ Q_CORE_EXPORT QtMessageHandler qInstallMessageHandler(QtMessageHandler);
|
|||
typedef void (*QMessageHandler)(QtMsgType, const QMessageLogContext &, const char *);
|
||||
Q_CORE_EXPORT QMessageHandler qInstallMessageHandler(QMessageHandler);
|
||||
|
||||
Q_CORE_EXPORT void qSetMessagePattern(const QString &messagePattern);
|
||||
|
||||
QT_END_HEADER
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
|
|
|||
|
|
@ -51,8 +51,15 @@ int main(int argc, char **argv)
|
|||
QCoreApplication app(argc, argv);
|
||||
app.setApplicationName("tst_qlogging");
|
||||
|
||||
qSetMessagePattern("[%{type}] %{message}");
|
||||
|
||||
qDebug("qDebug");
|
||||
qWarning("qWarning");
|
||||
qCritical("qCritical");
|
||||
|
||||
qSetMessagePattern(QString());
|
||||
|
||||
qDebug("qDebug2");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -630,13 +630,16 @@ void tst_qmessagehandler::cleanupFuncinfo()
|
|||
void tst_qmessagehandler::qMessagePattern()
|
||||
{
|
||||
QProcess process;
|
||||
const QString appExe = m_appDir + "/app";
|
||||
|
||||
//
|
||||
// test QT_MESSAGE_PATTERN
|
||||
//
|
||||
QStringList environment = QProcess::systemEnvironment();
|
||||
// %{file} is tricky because of shadow builds
|
||||
environment.prepend("QT_MESSAGE_PATTERN=\"%{type} %{appname} %{line} %{function} %{message}\"");
|
||||
process.setEnvironment(environment);
|
||||
|
||||
QString appExe = m_appDir + "/app";
|
||||
process.start(appExe);
|
||||
QVERIFY2(process.waitForStarted(), qPrintable(
|
||||
QString::fromLatin1("Could not start %1: %2").arg(appExe, process.errorString())));
|
||||
|
|
@ -649,9 +652,10 @@ void tst_qmessagehandler::qMessagePattern()
|
|||
QVERIFY(output.contains("debug 45 T::T static constructor"));
|
||||
// we can't be sure whether the QT_MESSAGE_PATTERN is already destructed
|
||||
QVERIFY(output.contains("static destructor"));
|
||||
QVERIFY(output.contains("debug tst_qlogging 54 main qDebug"));
|
||||
QVERIFY(output.contains("warning tst_qlogging 55 main qWarning"));
|
||||
QVERIFY(output.contains("critical tst_qlogging 56 main qCritical"));
|
||||
QVERIFY(output.contains("debug tst_qlogging 56 main qDebug"));
|
||||
QVERIFY(output.contains("warning tst_qlogging 57 main qWarning"));
|
||||
QVERIFY(output.contains("critical tst_qlogging 58 main qCritical"));
|
||||
QVERIFY(output.contains("debug tst_qlogging 62 main qDebug2"));
|
||||
|
||||
environment = QProcess::systemEnvironment();
|
||||
environment.prepend("QT_MESSAGE_PATTERN=\"PREFIX: %{unknown} %{message}\"");
|
||||
|
|
@ -668,6 +672,32 @@ void tst_qmessagehandler::qMessagePattern()
|
|||
|
||||
QVERIFY(output.contains("QT_MESSAGE_PATTERN: Unknown placeholder %{unknown}"));
|
||||
QVERIFY(output.contains("PREFIX: qDebug"));
|
||||
|
||||
//
|
||||
// test qSetMessagePattern
|
||||
//
|
||||
QMutableListIterator<QString> iter(environment);
|
||||
while (iter.hasNext()) {
|
||||
if (iter.next().startsWith("QT_MESSAGE_PATTERN"))
|
||||
iter.remove();
|
||||
}
|
||||
process.setEnvironment(environment);
|
||||
|
||||
process.start(appExe);
|
||||
QVERIFY2(process.waitForStarted(), qPrintable(
|
||||
QString::fromLatin1("Could not start %1: %2").arg(appExe, process.errorString())));
|
||||
process.waitForFinished();
|
||||
|
||||
output = process.readAllStandardError();
|
||||
//qDebug() << output;
|
||||
QByteArray expected = "static constructor\n"
|
||||
"[debug] qDebug\n"
|
||||
"[warning] qWarning\n"
|
||||
"[critical] qCritical\n";
|
||||
#ifdef Q_OS_WIN
|
||||
output.replace("\r\n", "\n");
|
||||
#endif
|
||||
QCOMPARE(QString::fromLatin1(output), QString::fromLatin1(expected));
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_qmessagehandler)
|
||||
|
|
|
|||
Loading…
Reference in New Issue