summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKai Koehne <kai.koehne@nokia.com>2012-04-13 13:37:56 +0200
committerQt by Nokia <qt-info@nokia.com>2012-04-24 17:12:46 +0200
commitc0d249019b098890fb8e5e9e144c2dd8029a670c (patch)
treec65c071920b8e8ebb3b1ad65ae1f5df7851ddcf3
parente92e5fda44602d7595aca329a3133ecb9991a6dd (diff)
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>
-rw-r--r--src/corelib/global/qglobal.cpp33
-rw-r--r--src/corelib/global/qlogging.cpp64
-rw-r--r--src/corelib/global/qlogging.h2
-rw-r--r--tests/auto/corelib/global/qlogging/app/main.cpp7
-rw-r--r--tests/auto/corelib/global/qlogging/tst_qlogging.cpp38
5 files changed, 127 insertions, 17 deletions
diff --git a/src/corelib/global/qglobal.cpp b/src/corelib/global/qglobal.cpp
index 7036b72f08..38ddd7bf97 100644
--- a/src/corelib/global/qglobal.cpp
+++ b/src/corelib/global/qglobal.cpp
@@ -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
diff --git a/src/corelib/global/qlogging.cpp b/src/corelib/global/qlogging.cpp
index 1f5b121143..8726c18689 100644
--- a/src/corelib/global/qlogging.cpp
+++ b/src/corelib/global/qlogging.cpp
@@ -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
diff --git a/src/corelib/global/qlogging.h b/src/corelib/global/qlogging.h
index 216b847952..ae388b0a38 100644
--- a/src/corelib/global/qlogging.h
+++ b/src/corelib/global/qlogging.h
@@ -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
diff --git a/tests/auto/corelib/global/qlogging/app/main.cpp b/tests/auto/corelib/global/qlogging/app/main.cpp
index dfa52315c7..2f5a975e43 100644
--- a/tests/auto/corelib/global/qlogging/app/main.cpp
+++ b/tests/auto/corelib/global/qlogging/app/main.cpp
@@ -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;
}
diff --git a/tests/auto/corelib/global/qlogging/tst_qlogging.cpp b/tests/auto/corelib/global/qlogging/tst_qlogging.cpp
index 1d6aa89035..5474b9aa3b 100644
--- a/tests/auto/corelib/global/qlogging/tst_qlogging.cpp
+++ b/tests/auto/corelib/global/qlogging/tst_qlogging.cpp
@@ -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)