Add tracing to logging framework

Change-Id: I4d5b9a24a214785019ff1238c1790ead79205b15
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@digia.com>
Reviewed-by: Topi Reiniö <topi.reinio@digia.com>
Reviewed-by: Robin Burchell <robin+qt@viroteck.net>
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Shawn Rutledge <shawn.rutledge@digia.com>
This commit is contained in:
hjk 2013-09-19 17:15:43 +02:00 committed by The Qt Project
parent 774d74df91
commit 466e0dff4b
10 changed files with 618 additions and 6 deletions

View File

@ -0,0 +1,180 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
** of its contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <QCoreApplication>
#include <QLoggingCategory>
//![1]
QLoggingCategory theFooArea("foo");
QLoggingCategory theBarArea("bar");
QLoggingCategory theBazArea("baz");
//![1]
// Note: These locations are Ubuntu specific.
// Note: To make the example work with user permissions, make sure
// the files are user-writable and the path leading there accessible.
const char traceSwitch[] = "/sys/kernel/debug/tracing/tracing_on";
const char traceSink[] = "/sys/kernel/debug/tracing/trace_marker";
// The base class only serves as a facility to share code
// between the "single line" data logging aspect and the
// scoped "measuring" aspect.
// Both aspects and the base class could be combined into
// a single tracer serving both purposes, but are split
// here for clarity.
// Error handling is left as an exercise.
//![2]
class MyTracerBase : public QTracer
{
public:
MyTracerBase() {
enable = ::open(traceSwitch, O_WRONLY);
marker = ::open(traceSink, O_WRONLY);
}
~MyTracerBase() {
::close(enable);
::close(marker);
}
protected:
int enable;
int marker;
};
//![2]
//![2]
class MyTracer : public MyTracerBase
{
public:
void start() { ::write(marker, "B", 1); }
void end() { ::write(marker, "E", 1); }
};
//![2]
//![3]
class MyDataLogger : public MyTracerBase
{
public:
MyDataLogger() {
buf[0] = 0;
pos = 0;
}
void record(int i) { pos += sprintf(buf + pos, "%d", i); }
void record(const char *msg) { pos += sprintf(buf + pos, "%s", msg); }
void end() { ::write(marker, buf, pos); pos = 0; }
private:
char buf[100];
int pos;
};
//![3]
// Simplest possible example for "measuring".
//![4]
int foo(int i)
{
qCTraceGuard(theFooArea);
// Here could be some lengthy code.
return i * i;
}
//![4]
// We can switch on/off tracing dynamically.
// The tracer will be temporarily switched off at the third call
// and re-enabled at the eighth.
//![5]
int bar(int i)
{
static int n = 0;
++n;
if (n == 3)
theBarArea.setEnabled(QtTraceMsg, false);
if (n == 8)
theBarArea.setEnabled(QtTraceMsg, true);
qCTraceGuard(theBarArea);
return i * i;
}
//![5]
// An example to create "complex" log messages.
//![6]
int baz(int i)
{
qCTrace(theBazArea) << 32 << "some stuff";
return i * i;
}
//![6]
//![7]
namespace {
static struct Init
{
Init() {
tracer.addToCategory(theFooArea);
tracer.addToCategory(theBarArea);
logger.addToCategory(theBazArea);
}
MyTracer tracer;
MyDataLogger logger;
} initializer;
}
//![7]

View File

@ -0,0 +1,59 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the examples of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
** of its contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
int foo(int i);
int bar(int i);
int baz(int i);
int main()
{
int s = 0;
for (int i = 0; i != 10; ++i)
s += foo(i);
for (int i = 0; i != 10; ++i)
s += bar(i);
for (int i = 0; i != 10; ++i)
s += baz(i);
return s;
}

View File

@ -0,0 +1,2 @@
SOURCES += ftracer.cpp main.cpp

View File

@ -717,6 +717,10 @@ Q_CORE_EXPORT void *qMemSet(void *dest, int c, size_t n);
A message generated by the qCritical() function.
\value QtFatalMsg
A message generated by the qFatal() function.
\value QtTraceMsg
Used by the qCTrace() macro. Trace events are usually passed only
to dedicated \a QTracer objects, and do not appear in the installed
message handler.
\value QtSystemMsg

View File

@ -814,6 +814,7 @@ Q_CORE_EXPORT QString qMessageFormatString(QtMsgType type, const QMessageLogCont
case QtWarningMsg: message.append(QLatin1String("warning")); break;
case QtCriticalMsg:message.append(QLatin1String("critical")); break;
case QtFatalMsg: message.append(QLatin1String("fatal")); break;
case QtTraceMsg: message.append(QLatin1String("trace")); break;
}
} else if (token == fileTokenC) {
if (context.file)

View File

@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE
class QDebug;
class QNoDebug;
enum QtMsgType { QtDebugMsg, QtWarningMsg, QtCriticalMsg, QtFatalMsg, QtSystemMsg = QtCriticalMsg };
enum QtMsgType { QtDebugMsg, QtWarningMsg, QtCriticalMsg, QtFatalMsg, QtTraceMsg, QtSystemMsg = QtCriticalMsg };
class QMessageLogContext
{

View File

@ -107,7 +107,8 @@ QLoggingCategory::QLoggingCategory(const char *category)
: name(0),
enabledDebug(false),
enabledWarning(true),
enabledCritical(true)
enabledCritical(true),
enabledTrace(false)
{
bool isDefaultCategory
= (category == 0) || (strcmp(category, qtDefaultCategoryName) == 0);
@ -122,7 +123,8 @@ QLoggingCategory::QLoggingCategory(const char *category)
}
if (QLoggingRegistry *reg = QLoggingRegistry::instance())
reg->registerCategory(this);}
reg->registerCategory(this);
}
/*!
Destructs a QLoggingCategory object
@ -164,6 +166,7 @@ bool QLoggingCategory::isEnabled(QtMsgType msgtype) const
case QtDebugMsg: return enabledDebug;
case QtWarningMsg: return enabledWarning;
case QtCriticalMsg: return enabledCritical;
case QtTraceMsg: return enabledTrace;
case QtFatalMsg: return true;
default: break;
}
@ -177,6 +180,10 @@ bool QLoggingCategory::isEnabled(QtMsgType msgtype) const
change e.g. the settings of another objects for the same category name.
\note QtFatalMsg cannot be changed. It will always return true.
Example:
\snippet qtracer/ftracer.cpp 5
*/
void QLoggingCategory::setEnabled(QtMsgType type, bool enable)
{
@ -184,6 +191,7 @@ void QLoggingCategory::setEnabled(QtMsgType type, bool enable)
case QtDebugMsg: enabledDebug = enable; break;
case QtWarningMsg: enabledWarning = enable; break;
case QtCriticalMsg: enabledCritical = enable; break;
case QtTraceMsg: enabledTrace = enable; break;
case QtFatalMsg:
default: break;
}
@ -319,11 +327,60 @@ void QLoggingCategory::setFilterRules(const QString &rules)
\snippet qloggingcategory/main.cpp 12
\note Arguments are not processed if critical output for the category is not
enabled, so do not reply on any side effects.
enabled, so do not rely on any side effects.
\sa qCritical()
*/
/*!
\relates QLoggingCategory
\macro qCTrace(category)
\since 5.2
Returns an output stream for trace messages in the logging category
\a category.
The macro expands to code that first checks whether
\l QLoggingCategory::isEnabled() evaluates for trace output to \c{true}.
If so, the stream arguments are processed and sent to the tracers
registered with the category.
\note Arguments are not processed if trace output for the category is not
enabled, so do not rely on any side effects.
Example:
\snippet qtracer/ftracer.cpp 6
\sa qCTraceGuard()
*/
/*!
\relates QLoggingCategory
\macro qCTraceGuard(category)
\since 5.2
The macro expands to code that creates a guard object with automatic
storage. The guard constructor checks whether
\l QLoggingCategory::isEnabled() evaluates for trace output to \c{true}.
If so, the stream arguments are processed and the \c{start()}
functions of the tracers registered with the \a category are called.
The guard destructor also checks whether the category is enabled for
tracing and if so, the \c{end()}
functions of the tracers registered with the \a category are called.
\note Arguments are always processed, even if trace output for the
category is disabled. They will, however, in that case not be passed
to the \c{record()} functions of the registered tracers.
Example:
\snippet qtracer/ftracer.cpp 4
\sa qCTrace()
*/
/*!
\macro Q_DECLARE_LOGGING_CATEGORY(name)
\relates QLoggingCategory
@ -349,4 +406,234 @@ void QLoggingCategory::setFilterRules(const QString &rules)
This macro must be used outside of a class or method.
*/
/*!
\class QTracer
\inmodule QtCore
\since 5.2
\brief The QTracer class provides an interface for handling
trace events associated with a logging category.
\c QTracer objects are registered with logging categories.
Multiple \c QTracer objects
can be registered with the same category, and the same
\c QTracer object can be registered with different categories.
If code containing \c qCTrace is executed, and the associated
logging category is enabled for tracing, all \c QTracer objects
that are registered with the category are notified.
\c QTracer objects
*/
/*!
\fn QTracer::QTracer()
Constructs a tracer object.
Example:
\snippet qtracer/ftracer.cpp 2
*/
/*!
\fn QTracer::~QTracer()
Destroys the tracer object.
*/
/*!
Registers this tracer for the \a category.
The tracer will later be notified of messages of type
\c QtTraceMsg, as long as that message type
is enabled in the category.
Example:
\snippet qtracer/ftracer.cpp 1
\codeline
\snippet qtracer/ftracer.cpp 7
*/
void QTracer::addToCategory(QLoggingCategory &category)
{
category.tracers.append(this);
}
/*!
\fn void QTracer::start()
This function is invoked when a tracing activity starts,
typically from the constructor of a \c QTraceGuard object
defined by \c qCTrace() or \c qCTraceGuard().
The base implementation does nothing. \c QTracer subclasses
are advised to override it if needed.
\sa qCTrace(), qCTraceGuard()
*/
/*!
\fn void QTracer::end()
This function is invoked when a tracing activity ends,
typically from the destructor of a \c QTraceGuard object
defined by \c qCTrace() or \c qCTraceGuard().
The base implementation does nothing. It is common for
\c QTracer subclasses to override it to perform flushing
of collected data.
\sa qCTrace(), qCTraceGuard()
*/
/*!
\fn void QTracer::record(int data)
This function is invoked during a tracing activity to
pass integer \a data to the \c QTracer object.
Example:
\snippet qtracer/ftracer.cpp 3
*/
/*!
\fn void QTracer::record(const char *data)
This function is invoked during a tracing activity to
pass string \a data to the \c QTracer object.
*/
/*!
\fn void QTracer::record(const QVariant &data)
This function is invoked during a tracing activity to
pass abitrary (non-integer, non-string) \a data to
the \c QTracer object.
*/
/*!
\class QTraceGuard
\since 5.2
\inmodule QtCore
\brief The QTraceGuard class facilitates notifications to
\c QTracer objects.
\c QTraceGuard objects are typically implicitly created on the
stack when using the \c qCTrace or \c qCTraceGuard macros and
are associated to a \c QLoggingCategory.
The constructor of a \c QTraceGuard objects checks whether
its associated category is enabled, and if so, informs all
\c QTracer objects registered with the category that a
tracing activity starts.
The destructor of a \c QTraceGuard objects checks whether
its associated category is enabled, and if so, informs all
\c QTracer objects registered with the category that a
tracing activity ended.
A \c QTraceGuard object created by \c qCTrace will be destroyed
at the end of the full expression, a guard created by
\c qCTraceGuard at the end of the block containing the macro.
During the lifetime of a QTraceGuard object, its \c operator<<()
can be used to pass additional data to the active tracers.
The fast path handles only \c int and \c{const char *} data,
but it is possible to use arbitrary values wrapped in \c QVariants.
\sa QTracer
*/
/*!
\fn QTraceGuard::QTraceGuard(QLoggingCategory &category)
\internal
Constructs a trace guard object relaying to \a category.
*/
/*!
\fn QTraceGuard::~QTraceGuard()
\internal
Destroys the trace guard object.
*/
/*!
\internal
Calls \c start() on all registered tracers.
*/
void QTraceGuard::start()
{
QLoggingCategory::Tracers &tracers = target->tracers;
for (int i = tracers.size(); --i >= 0; )
tracers.at(i)->start();
}
/*!
\internal
Calls \c end() on all registered tracers.
*/
void QTraceGuard::end()
{
QLoggingCategory::Tracers &tracers = target->tracers;
for (int i = tracers.size(); --i >= 0; )
tracers.at(i)->end();
}
/*!
\internal
This function is called for int parameters passed to the
qCTrace stream.
*/
QTraceGuard &QTraceGuard::operator<<(int msg)
{
QLoggingCategory::Tracers &tracers = target->tracers;
for (int i = tracers.size(); --i >= 0; )
tracers.at(i)->record(msg);
return *this;
}
/*!
\internal
This function is called for string parameters passed to the
qCTrace stream.
*/
QTraceGuard &QTraceGuard::operator<<(const char *msg)
{
QLoggingCategory::Tracers &tracers = target->tracers;
for (int i = tracers.size(); --i >= 0; )
tracers.at(i)->record(msg);
return *this;
}
/*!
\internal
This function is called for QVariant parameters passed to the
qCTrace stream.
*/
QTraceGuard &QTraceGuard::operator<<(const QVariant &msg)
{
QLoggingCategory::Tracers &tracers = target->tracers;
for (int i = tracers.size(); --i >= 0; )
tracers.at(i)->record(msg);
return *this;
}
QT_END_NAMESPACE

View File

@ -44,9 +44,13 @@
#include <QtCore/qglobal.h>
#include <QtCore/qdebug.h>
#include <QtCore/qvector.h>
QT_BEGIN_NAMESPACE
class QTracer;
class QTraceGuard;
class Q_CORE_EXPORT QLoggingCategory
{
Q_DISABLE_COPY(QLoggingCategory)
@ -76,13 +80,18 @@ public:
static void setFilterRules(const QString &rules);
private:
friend class QLoggingRegistry;
friend class QTraceGuard;
friend class QTracer;
const char *name;
bool enabledDebug;
bool enabledWarning;
bool enabledCritical;
friend class QLoggingRegistry;
bool enabledTrace;
typedef QVector<QTracer *> Tracers;
Tracers tracers;
};
template <>
@ -103,6 +112,56 @@ inline bool QLoggingCategory::isEnabled<QtCriticalMsg>() const
return enabledCritical;
}
template <>
inline bool QLoggingCategory::isEnabled<QtTraceMsg>() const
{
return enabledTrace;
}
class Q_CORE_EXPORT QTracer
{
Q_DISABLE_COPY(QTracer)
public:
QTracer() {}
virtual ~QTracer() {}
void addToCategory(QLoggingCategory &category);
virtual void start() {}
virtual void end() {}
virtual void record(int) {}
virtual void record(const char *) {}
virtual void record(const QVariant &) {}
};
class Q_CORE_EXPORT QTraceGuard
{
Q_DISABLE_COPY(QTraceGuard)
public:
QTraceGuard(QLoggingCategory &category)
{
target = category.isEnabled<QtTraceMsg>() ? &category : 0;
if (target)
start();
}
~QTraceGuard()
{
if (target)
end();
}
QTraceGuard &operator<<(int msg);
QTraceGuard &operator<<(const char *msg);
QTraceGuard &operator<<(const QVariant &msg);
private:
void start();
void end();
QLoggingCategory *target;
};
#define Q_DECLARE_LOGGING_CATEGORY(name) \
extern QLoggingCategory &name();
@ -123,6 +182,17 @@ inline bool QLoggingCategory::isEnabled<QtCriticalMsg>() const
#define qCCritical(category) \
for (bool enabled = category().isEnabled<QtCriticalMsg>(); enabled; enabled = false) \
QMessageLogger(__FILE__, __LINE__, Q_FUNC_INFO, category().categoryName()).critical()
#define qCTrace(category) \
for (bool enabled = category.isEnabled<QtTraceMsg>(); enabled; enabled = false) \
QTraceGuard(category)
#define Q_TRACE_GUARD_NAME_HELPER(line) qTraceGuard ## line
#define Q_TRACE_GUARD_NAME(line) Q_TRACE_GUARD_NAME_HELPER(line)
#define qCTraceGuard(category) \
QTraceGuard Q_TRACE_GUARD_NAME(__LINE__)(category);
#if defined(QT_NO_DEBUG_OUTPUT)
# undef qCDebug

View File

@ -86,6 +86,9 @@ int QLoggingRule::pass(const QString &categoryName, QtMsgType msgType) const
case QtCriticalMsg:
fullCategory += QLatin1String(".critical");
break;
case QtTraceMsg:
fullCategory += QLatin1String(".trace");
break;
default:
break;
}
@ -288,6 +291,7 @@ void QLoggingRegistry::defaultCategoryFilter(QLoggingCategory *cat)
bool debug = (cat->categoryName() == qtDefaultCategoryName);
bool warning = true;
bool critical = true;
bool trace = true;
QString categoryName = QLatin1String(cat->categoryName());
QLoggingRegistry *reg = QLoggingRegistry::instance();
@ -301,11 +305,15 @@ void QLoggingRegistry::defaultCategoryFilter(QLoggingCategory *cat)
filterpass = item.pass(categoryName, QtCriticalMsg);
if (filterpass != 0)
critical = (filterpass > 0);
filterpass = item.pass(categoryName, QtTraceMsg);
if (filterpass != 0)
trace = (filterpass > 0);
}
cat->setEnabled(QtDebugMsg, debug);
cat->setEnabled(QtWarningMsg, warning);
cat->setEnabled(QtCriticalMsg, critical);
cat->setEnabled(QtTraceMsg, trace);
}

View File

@ -257,6 +257,7 @@ namespace QTest {
}
switch (type) {
case QtTraceMsg:
case QtDebugMsg:
QTest::TestLoggers::addMessage(QAbstractTestLogger::QDebug, msg);
break;