summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDominik Riebeling <Dominik.Riebeling@gmail.com>2020-06-08 21:13:11 +0200
committerDominik Riebeling <Dominik.Riebeling@gmail.com>2020-08-08 10:01:42 +0200
commit48d2927ecca77ec7f0876960bff624d5d39a4e0f (patch)
tree26a739aed7a63fa1a3cc611f77ef100509ed2138
parentc425d4627ea7a0df3db12600d832de276c691e8b (diff)
downloadrockbox-48d2927ecca77ec7f0876960bff624d5d39a4e0f.tar.gz
rockbox-48d2927ecca77ec7f0876960bff624d5d39a4e0f.zip
rbutil: Update CuteLogger to most recent upstream.
Update to the most recent git version. This changes the folder structure and renames some classes to follow upstream. Restore MSVC static link fix, and fix wrong variable in qmake project file. Change-Id: I874bb9ed60e37af09a841988e771fd341414d145
-rw-r--r--rbutil/rbutilqt/logger/AbstractAppender.cpp58
-rw-r--r--rbutil/rbutilqt/logger/AbstractAppender.h125
-rw-r--r--rbutil/rbutilqt/logger/AbstractStringAppender.cpp161
-rw-r--r--rbutil/rbutilqt/logger/AbstractStringAppender.h116
-rw-r--r--rbutil/rbutilqt/logger/ConsoleAppender.cpp25
-rw-r--r--rbutil/rbutilqt/logger/Logger.cpp370
-rw-r--r--rbutil/rbutilqt/logger/Logger.h319
-rw-r--r--rbutil/rbutilqt/logger/README.ROCKBOX6
-rw-r--r--rbutil/rbutilqt/logger/include/AbstractAppender.h49
-rw-r--r--rbutil/rbutilqt/logger/include/AbstractStringAppender.h46
-rw-r--r--rbutil/rbutilqt/logger/include/ConsoleAppender.h (renamed from rbutil/rbutilqt/logger/ConsoleAppender.h)18
-rw-r--r--rbutil/rbutilqt/logger/include/CuteLogger_global.h (renamed from rbutil/rbutilqt/logger/CuteLogger_global.h)0
-rw-r--r--rbutil/rbutilqt/logger/include/FileAppender.h (renamed from rbutil/rbutilqt/logger/FileAppender.h)20
-rw-r--r--rbutil/rbutilqt/logger/include/Logger.h237
-rw-r--r--rbutil/rbutilqt/logger/include/OutputDebugAppender.h (renamed from rbutil/rbutilqt/logger/OutputDebugAppender.h)8
-rw-r--r--rbutil/rbutilqt/logger/logger.pri29
-rw-r--r--rbutil/rbutilqt/logger/src/AbstractAppender.cpp147
-rw-r--r--rbutil/rbutilqt/logger/src/AbstractStringAppender.cpp459
-rw-r--r--rbutil/rbutilqt/logger/src/ConsoleAppender.cpp64
-rw-r--r--rbutil/rbutilqt/logger/src/FileAppender.cpp (renamed from rbutil/rbutilqt/logger/FileAppender.cpp)59
-rw-r--r--rbutil/rbutilqt/logger/src/Logger.cpp1099
-rw-r--r--rbutil/rbutilqt/logger/src/OutputDebugAppender.cpp (renamed from rbutil/rbutilqt/logger/OutputDebugAppender.cpp)14
-rw-r--r--rbutil/rbutilqt/main.cpp8
-rw-r--r--rbutil/rbutilqt/systrace.cpp6
24 files changed, 2201 insertions, 1242 deletions
diff --git a/rbutil/rbutilqt/logger/AbstractAppender.cpp b/rbutil/rbutilqt/logger/AbstractAppender.cpp
deleted file mode 100644
index de86b930d0..0000000000
--- a/rbutil/rbutilqt/logger/AbstractAppender.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
1/*
2 Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License version 2.1
6 as published by the Free Software Foundation and appearing in the file
7 LICENSE.LGPL included in the packaging of this file.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13*/
14// Local
15#include "AbstractAppender.h"
16
17// Qt
18#include <QMutexLocker>
19
20
21AbstractAppender::AbstractAppender()
22 : m_detailsLevel(Logger::Debug)
23{}
24
25
26AbstractAppender::~AbstractAppender()
27{}
28
29
30Logger::LogLevel AbstractAppender::detailsLevel() const
31{
32 QMutexLocker locker(&m_detailsLevelMutex);
33 return m_detailsLevel;
34}
35
36
37void AbstractAppender::setDetailsLevel(Logger::LogLevel level)
38{
39 QMutexLocker locker(&m_detailsLevelMutex);
40 m_detailsLevel = level;
41}
42
43
44void AbstractAppender::setDetailsLevel(const QString& level)
45{
46 setDetailsLevel(Logger::levelFromString(level));
47}
48
49
50void AbstractAppender::write(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
51 const char* function, const QString& message)
52{
53 if (logLevel >= detailsLevel())
54 {
55 QMutexLocker locker(&m_writeMutex);
56 append(timeStamp, logLevel, file, line, function, message);
57 }
58}
diff --git a/rbutil/rbutilqt/logger/AbstractAppender.h b/rbutil/rbutilqt/logger/AbstractAppender.h
deleted file mode 100644
index df1df4957c..0000000000
--- a/rbutil/rbutilqt/logger/AbstractAppender.h
+++ /dev/null
@@ -1,125 +0,0 @@
1/*
2 Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License version 2.1
6 as published by the Free Software Foundation and appearing in the file
7 LICENSE.LGPL included in the packaging of this file.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13*/
14#ifndef ABSTRACTAPPENDER_H
15#define ABSTRACTAPPENDER_H
16
17// Local
18#include "CuteLogger_global.h"
19#include <Logger.h>
20
21// Qt
22#include <QMutex>
23
24//! The AbstractAppender class provides an abstract base class for writing a log entries.
25/**
26 * The AbstractAppender class is the base interface class for all log appenders that could be used with Logger.
27 *
28 * AbstractAppender provides a common implementation for the thread safe, mutex-protected logging of application
29 * messages, such as ConsoleAppender, FileAppender or something else. AbstractAppender is abstract and can not be
30 * instantiated, but you can use any of its subclasses or create a custom log appender at your choice.
31 *
32 * Appenders are the logical devices that is aimed to be attached to Logger object by calling
33 * Logger::registerAppender(). On each log record call from the application Logger object sequentially calls write()
34 * function on all the appenders registered in it.
35 *
36 * You can subclass AbstractAppender to implement a logging target of any kind you like. It may be the external logging
37 * subsystem (for example, syslog in *nix), XML file, SQL database entries, D-Bus messages or anything else you can
38 * imagine.
39 *
40 * For the simple non-structured plain text logging (for example, to a plain text file or to the console output) you may
41 * like to subclass the AbstractStringAppender instead of AbstractAppender, which will give you a more convinient way to
42 * control the format of the log output.
43 *
44 * \sa AbstractStringAppender
45 * \sa Logger::registerAppender()
46 */
47class CUTELOGGERSHARED_EXPORT AbstractAppender
48{
49 public:
50 //! Constructs a AbstractAppender object.
51 AbstractAppender();
52
53 //! Destructs the AbstractAppender object.
54 virtual ~AbstractAppender();
55
56 //! Returns the current details level of appender.
57 /**
58 * Log records with a log level lower than a current detailsLevel() will be silently ignored by appender and would not
59 * be sent to its append() function.
60 *
61 * It provides additional logging flexibility, allowing you to set the different severity levels for different types
62 * of logs.
63 *
64 * \note This function is thread safe.
65 *
66 * \sa setDetailsLevel()
67 * \sa Logger::LogLevel
68 */
69 Logger::LogLevel detailsLevel() const;
70
71 //! Sets the current details level of appender.
72 /**
73 * \note This function is thread safe.
74 *
75 * \sa detalsLevel()
76 * \sa Logger::LogLevel
77 */
78 void setDetailsLevel(Logger::LogLevel level);
79
80 //! Sets the current details level of appender
81 /**
82 * This function is provided for convinience, it behaves like an above function.
83 *
84 * \sa detalsLevel()
85 * \sa Logger::LogLevel
86 */
87 void setDetailsLevel(const QString& level);
88
89 //! Tries to write the log record to this logger
90 /**
91 * This is the function called by Logger object to write a log message to the appender.
92 *
93 * \note This function is thread safe.
94 *
95 * \sa Logger::write()
96 * \sa detailsLevel()
97 */
98 void write(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, const char* function,
99 const QString& message);
100
101 protected:
102 //! Writes the log record to the logger instance
103 /**
104 * This function is called every time when user tries to write a message to this AbstractAppender instance using
105 * the write() function. Write function works as proxy and transfers only the messages with log level more or equal
106 * to the current logLevel().
107 *
108 * Overload this function when you are implementing a custom appender.
109 *
110 * \note This function is not needed to be thread safe because it is never called directly by Logger object. The
111 * write() function works as a proxy and protects this function from concurrent access.
112 *
113 * \sa Logger::write()
114 */
115 virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
116 const char* function, const QString& message) = 0;
117
118 private:
119 QMutex m_writeMutex;
120
121 Logger::LogLevel m_detailsLevel;
122 mutable QMutex m_detailsLevelMutex;
123};
124
125#endif // ABSTRACTAPPENDER_H
diff --git a/rbutil/rbutilqt/logger/AbstractStringAppender.cpp b/rbutil/rbutilqt/logger/AbstractStringAppender.cpp
deleted file mode 100644
index 073ecb7782..0000000000
--- a/rbutil/rbutilqt/logger/AbstractStringAppender.cpp
+++ /dev/null
@@ -1,161 +0,0 @@
1/*
2 Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License version 2.1
6 as published by the Free Software Foundation and appearing in the file
7 LICENSE.LGPL included in the packaging of this file.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13*/
14// Local
15#include "AbstractStringAppender.h"
16
17// Qt
18#include <QReadLocker>
19#include <QWriteLocker>
20#include <QDateTime>
21#include <QRegExp>
22
23const char formattingMarker = '%';
24
25AbstractStringAppender::AbstractStringAppender()
26 : m_format(QLatin1String("%t{yyyy-MM-ddTHH:mm:ss.zzz} [%-7l] <%c> %m\n"))
27{}
28
29
30QString AbstractStringAppender::format() const
31{
32 QReadLocker locker(&m_formatLock);
33 return m_format;
34}
35
36
37void AbstractStringAppender::setFormat(const QString& format)
38{
39 QWriteLocker locker(&m_formatLock);
40 m_format = format;
41}
42
43
44QString AbstractStringAppender::stripFunctionName(const char* name)
45{
46 QRegExp rx("^.+\\s((?:[\\w\\d]+::)+)?([\\w\\d\\<\\>~]+)(?:\\(.*\\)).*$"); // XXX: SLOW!
47 return QString::fromLatin1(name).replace(rx, QString(QLatin1String("\\1\\2")));
48}
49
50
51QString AbstractStringAppender::formattedString(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file,
52 int line, const char* function, const QString& message) const
53{
54 QString f = format();
55 const int size = f.size();
56
57 QString result;
58
59 int i = 0;
60 while (i < f.size())
61 {
62 QChar c = f.at(i);
63
64 // We will silently ignore the broken % marker at the end of string
65 if (c != QLatin1Char(formattingMarker) || (i + 1) == size)
66 {
67 result.append(c);
68 }
69 else
70 {
71 QChar command = f.at(++i);
72
73 // Check for the padding instruction
74 int fieldWidth = 0;
75 if (command.isDigit() || command.category() == QChar::Punctuation_Dash)
76 {
77 int j = 1;
78 while ((i + j) < size && f.at(i + j).isDigit())
79 j++;
80 fieldWidth = f.mid(i, j).toInt();
81
82 i += j;
83 command = f.at(i);
84 }
85
86 // Log record chunk to insert instead of formatting instruction
87 QString chunk;
88
89 // Time stamp
90 if (command == QLatin1Char('t'))
91 {
92 if (f.at(i + 1) == QLatin1Char('{'))
93 {
94 int j = 1;
95 while ((i + 2 + j) < size && f.at(i + 2 + j) != QLatin1Char('}'))
96 j++;
97
98 if ((i + 2 + j) < size)
99 {
100 chunk = timeStamp.toString(f.mid(i + 2, j));
101
102 i += j;
103 i += 2;
104 }
105 }
106
107 if (chunk.isNull())
108 chunk = timeStamp.toString(QLatin1String("HH:mm:ss.zzz"));
109 }
110
111 // Log level
112 else if (command == QLatin1Char('l'))
113 chunk = Logger::levelToString(logLevel);
114
115 // Uppercased log level
116 else if (command == QLatin1Char('L'))
117 chunk = Logger::levelToString(logLevel).toUpper();
118
119 // Filename
120 else if (command == QLatin1Char('F'))
121 chunk = QLatin1String(file);
122
123 // Filename without a path
124 else if (command == QLatin1Char('f'))
125 chunk = QString(QLatin1String(file)).section('/', -1);
126
127 // Source line number
128 else if (command == QLatin1Char('i'))
129 chunk = QString::number(line);
130
131 // Function name, as returned by Q_FUNC_INFO
132 else if (command == QLatin1Char('C'))
133 chunk = QString::fromLatin1(function);
134
135 // Stripped function name
136 else if (command == QLatin1Char('c'))
137 chunk = stripFunctionName(function);
138
139 // Log message
140 else if (command == QLatin1Char('m'))
141 chunk = message;
142
143 // We simply replace the double formatting marker (%) with one
144 else if (command == QLatin1Char(formattingMarker))
145 chunk = QLatin1Char(formattingMarker);
146
147 // Do not process any unknown commands
148 else
149 {
150 chunk = QLatin1Char(formattingMarker);
151 chunk.append(command);
152 }
153
154 result.append(QString(QLatin1String("%1")).arg(chunk, fieldWidth));
155 }
156
157 ++i;
158 }
159
160 return result;
161}
diff --git a/rbutil/rbutilqt/logger/AbstractStringAppender.h b/rbutil/rbutilqt/logger/AbstractStringAppender.h
deleted file mode 100644
index 3cef63bff9..0000000000
--- a/rbutil/rbutilqt/logger/AbstractStringAppender.h
+++ /dev/null
@@ -1,116 +0,0 @@
1/*
2 Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License version 2.1
6 as published by the Free Software Foundation and appearing in the file
7 LICENSE.LGPL included in the packaging of this file.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13*/
14#ifndef ABSTRACTSTRINGAPPENDER_H
15#define ABSTRACTSTRINGAPPENDER_H
16
17// Local
18#include "CuteLogger_global.h"
19#include <AbstractAppender.h>
20
21// Qt
22#include <QReadWriteLock>
23
24
25//! The AbstractStringAppender class provides a convinient base for appenders working with plain text formatted logs.
26/**
27 * AbstractSringAppender is the simple extension of the AbstractAppender class providing the convinient way to create
28 * custom log appenders working with a plain text formatted log targets.
29 *
30 * It have the formattedString() protected function that formats the logging arguments according to a format set with
31 * setFormat().
32 *
33 * This class can not be directly instantiated because it contains pure virtual function inherited from AbstractAppender
34 * class.
35 *
36 * For more detailed description of customizing the log output format see the documentation on the setFormat() function.
37 */
38class CUTELOGGERSHARED_EXPORT AbstractStringAppender : public AbstractAppender
39{
40 public:
41 //! Constructs a new string appender object
42 AbstractStringAppender();
43
44 //! Returns the current log format string.
45 /**
46 * The default format is set to "%t{yyyy-MM-ddTHH:mm:ss.zzz} [%-7l] <%C> %m\n". You can set a different log record
47 * format using the setFormat() function.
48 *
49 * \sa setFormat(const QString&)
50 */
51 QString format() const;
52
53 //! Sets the logging format for writing strings to the log target with this appender.
54 /**
55 * The string format seems to be very common to those developers who have used a standart sprintf function.
56 *
57 * Log output format is a simple QString with the special markers (starting with % sign) which will be replaced with
58 * it's internal meaning when writing a log record.
59 *
60 * Controlling marker begins with the percent sign (%) which is followed by (optional) field width argument, the
61 * (necessary) single-letter command (which describes, what will be put to log record instead of marker, and an
62 * additional formatting argument (in the {} brackets) supported for some of the log commands.
63 *
64 * Field width argument works almost identically to the \c QString::arg() \c fieldWidth argument (and uses it
65 * internally). For example, \c "%-7l" will be replaced with the left padded debug level of the message
66 * (\c "Debug ") or something. For the more detailed description of it you may consider to look to the Qt
67 * Reference Documentation.
68 *
69 * Supported marker commands are:
70 * \arg \c %t - timestamp. You may specify your custom timestamp format using the {} brackets after the marker,
71 * timestamp format here will be similiar to those used in QDateTime::toString() function. For example,
72 * "%t{dd-MM-yyyy, HH:mm}" may be replaced with "17-12-2010, 20:17" depending on current date and time.
73 * The default format used here is "HH:mm:ss.zzz".
74 * \arg \c %l - Log level. Possible log levels are shown in the Logger::LogLevel enumerator.
75 * \arg \c %L - Uppercased log level.
76 * \arg \c %F - Full source file name (with path) of the file that requested log recording. Uses the \c __FILE__
77 * preprocessor macro.
78 * \arg \c %f - Short file name (with stripped path).
79 * \arg \c %i - Line number in the source file. Uses the \c __LINE__ preprocessor macro.
80 * \arg \c %C - Name of function that called on of the LOG_* macros. Uses the \c Q_FUNC_INFO macro provided with
81 * Qt.
82 * \arg \c %c - [EXPERIMENTAL] Similiar to the %C, but the function name is stripped using stripFunctionName
83 * \arg \c %m - The log message sent by the caller.
84 * \arg \c %% - Convinient marker that is replaced with the single \c % mark.
85 *
86 * \note Format doesn't add \c '\\n' to the end of the format line. Please consider adding it manually.
87 *
88 * \sa format()
89 * \sa stripFunctionName()
90 * \sa Logger::LogLevel
91 */
92 void setFormat(const QString&);
93
94 //! Strips the long function signature (as added by Q_FUNC_INFO macro)
95 /**
96 * The string processing drops the returning type, arguments and template parameters of function. It is definitely
97 * useful for enchancing the log output readability.
98 * \return stripped function name
99 */
100 static QString stripFunctionName(const char*);
101
102 protected:
103 //! Returns the string to record to the logging target, formatted according to the format().
104 /**
105 * \sa format()
106 * \sa setFormat(const QString&)
107 */
108 QString formattedString(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
109 const char* function, const QString& message) const;
110
111 private:
112 QString m_format;
113 mutable QReadWriteLock m_formatLock;
114};
115
116#endif // ABSTRACTSTRINGAPPENDER_H
diff --git a/rbutil/rbutilqt/logger/ConsoleAppender.cpp b/rbutil/rbutilqt/logger/ConsoleAppender.cpp
deleted file mode 100644
index da4a43c740..0000000000
--- a/rbutil/rbutilqt/logger/ConsoleAppender.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
1/*
2 Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License version 2.1
6 as published by the Free Software Foundation and appearing in the file
7 LICENSE.LGPL included in the packaging of this file.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13*/
14// Local
15#include "ConsoleAppender.h"
16
17// STL
18#include <iostream>
19
20
21void ConsoleAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
22 const char* function, const QString& message)
23{
24 std::cerr << qPrintable(formattedString(timeStamp, logLevel, file, line, function, message));
25}
diff --git a/rbutil/rbutilqt/logger/Logger.cpp b/rbutil/rbutilqt/logger/Logger.cpp
deleted file mode 100644
index 33ba50ec58..0000000000
--- a/rbutil/rbutilqt/logger/Logger.cpp
+++ /dev/null
@@ -1,370 +0,0 @@
1/*
2 Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License version 2.1
6 as published by the Free Software Foundation and appearing in the file
7 LICENSE.LGPL included in the packaging of this file.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13*/
14// Local
15#include "Logger.h"
16#include "AbstractAppender.h"
17
18// Qt
19#include <QCoreApplication>
20#include <QReadWriteLock>
21#include <QSemaphore>
22#include <QDateTime>
23#include <QIODevice>
24#include <QTextCodec>
25
26// STL
27#include <iostream>
28
29
30class LogDevice : public QIODevice
31{
32 public:
33 LogDevice()
34 : m_semaphore(1)
35 {}
36
37 void lock(Logger::LogLevel logLevel, const char* file, int line, const char* function)
38 {
39 m_semaphore.acquire();
40
41 if (!isOpen())
42 open(QIODevice::WriteOnly);
43
44 m_logLevel = logLevel;
45 m_file = file;
46 m_line = line;
47 m_function = function;
48 }
49
50 protected:
51 qint64 readData(char*, qint64)
52 {
53 return 0;
54 }
55
56 qint64 writeData(const char* data, qint64 maxSize)
57 {
58 if (maxSize > 0)
59 Logger::write(m_logLevel, m_file, m_line, m_function, QString::fromLocal8Bit(QByteArray(data, maxSize)));
60
61 m_semaphore.release();
62 return maxSize;
63 }
64
65 private:
66 QSemaphore m_semaphore;
67 Logger::LogLevel m_logLevel;
68 const char* m_file;
69 int m_line;
70 const char* m_function;
71};
72
73
74// Forward declarations
75static void cleanupLoggerPrivate();
76
77#if QT_VERSION >= 0x050000
78static void qtLoggerMessageHandler(QtMsgType, const QMessageLogContext& context, const QString& msg);
79#else
80static void qtLoggerMessageHandler(QtMsgType type, const char* msg);
81#endif
82
83/**
84 * \internal
85 *
86 * LoggerPrivate class implements the Singleton pattern in a thread-safe way. It uses a static pointer to itself
87 * protected by QReadWriteLock
88 *
89 * The appender list inside the LoggerPrivate class is also protected by QReadWriteLock so this class could be safely
90 * used in a multi-threaded application.
91 */
92class LoggerPrivate
93{
94 public:
95 static LoggerPrivate* m_self;
96 static QReadWriteLock m_selfLock;
97
98 static LoggerPrivate* instance()
99 {
100 LoggerPrivate* result = 0;
101 {
102 QReadLocker locker(&m_selfLock);
103 result = m_self;
104 }
105
106 if (!result)
107 {
108 QWriteLocker locker(&m_selfLock);
109 m_self = new LoggerPrivate;
110
111#if QT_VERSION >= 0x050000
112 qInstallMessageHandler(qtLoggerMessageHandler);
113#else
114 qInstallMsgHandler(qtLoggerMessageHandler);
115#endif
116 qAddPostRoutine(cleanupLoggerPrivate);
117 result = m_self;
118 }
119
120 return result;
121 }
122
123
124 LoggerPrivate()
125 : m_logDevice(0)
126 {}
127
128
129 ~LoggerPrivate()
130 {
131 // Cleanup appenders
132 QReadLocker appendersLocker(&m_appendersLock);
133 foreach (AbstractAppender* appender, m_appenders)
134 delete appender;
135
136 // Cleanup device
137 QReadLocker deviceLocker(&m_logDeviceLock);
138 delete m_logDevice;
139 }
140
141
142 void registerAppender(AbstractAppender* appender)
143 {
144 QWriteLocker locker(&m_appendersLock);
145
146 if (!m_appenders.contains(appender))
147 m_appenders.append(appender);
148 else
149 std::cerr << "Trying to register appender that was already registered" << std::endl;
150 }
151
152
153 LogDevice* logDevice()
154 {
155 LogDevice* result = 0;
156 {
157 QReadLocker locker(&m_logDeviceLock);
158 result = m_logDevice;
159 }
160
161 if (!result)
162 {
163 QWriteLocker locker(&m_logDeviceLock);
164 m_logDevice = new LogDevice;
165 result = m_logDevice;
166 }
167
168 return result;
169 }
170
171
172 void write(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, const char* function,
173 const QString& message)
174 {
175 QReadLocker locker(&m_appendersLock);
176
177 if (!m_appenders.isEmpty())
178 {
179 foreach (AbstractAppender* appender, m_appenders)
180 appender->write(timeStamp, logLevel, file, line, function, message);
181 }
182 else
183 {
184 // Fallback
185 QString result = QString(QLatin1String("[%1] <%2> %3")).arg(Logger::levelToString(logLevel), -7)
186 .arg(function).arg(message);
187
188 std::cerr << qPrintable(result) << std::endl;
189 }
190
191 if (logLevel == Logger::Fatal)
192 abort();
193 }
194
195
196 void write(Logger::LogLevel logLevel, const char* file, int line, const char* function, const QString& message)
197 {
198 write(QDateTime::currentDateTime(), logLevel, file, line, function, message);
199 }
200
201
202 void write(Logger::LogLevel logLevel, const char* file, int line, const char* function, const char* message)
203 {
204 write(logLevel, file, line, function, QString(message));
205 }
206
207
208 QDebug write(Logger::LogLevel logLevel, const char* file, int line, const char* function)
209 {
210 LogDevice* d = logDevice();
211 d->lock(logLevel, file, line, function);
212 return QDebug(d);
213 }
214
215
216 void writeAssert(const char* file, int line, const char* function, const char* condition)
217 {
218 write(Logger::Fatal, file, line, function, QString("ASSERT: \"%1\"").arg(condition));
219 }
220
221 private:
222 QList<AbstractAppender*> m_appenders;
223 QReadWriteLock m_appendersLock;
224
225 LogDevice* m_logDevice;
226 QReadWriteLock m_logDeviceLock;
227};
228
229// Static fields initialization
230LoggerPrivate* LoggerPrivate::m_self = 0;
231QReadWriteLock LoggerPrivate::m_selfLock;
232
233
234static void cleanupLoggerPrivate()
235{
236 QWriteLocker locker(&LoggerPrivate::m_selfLock);
237
238 delete LoggerPrivate::m_self;
239 LoggerPrivate::m_self = 0;
240}
241
242
243#if QT_VERSION >= 0x050000
244static void qtLoggerMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
245{
246 Logger::LogLevel level;
247 switch (type)
248 {
249 case QtDebugMsg:
250 level = Logger::Debug;
251 break;
252 case QtWarningMsg:
253 level = Logger::Warning;
254 break;
255 case QtCriticalMsg:
256 level = Logger::Error;
257 break;
258 case QtFatalMsg:
259 level = Logger::Fatal;
260 break;
261 }
262
263 Logger::write(level, context.file, context.line, context.function, msg);
264}
265
266#else
267
268static void qtLoggerMessageHandler(QtMsgType type, const char* msg)
269{
270 switch (type)
271 {
272 case QtDebugMsg:
273 LOG_DEBUG(msg);
274 break;
275 case QtWarningMsg:
276 LOG_WARNING(msg);
277 break;
278 case QtCriticalMsg:
279 LOG_ERROR(msg);
280 break;
281 case QtFatalMsg:
282 LOG_FATAL(msg);
283 break;
284 }
285}
286#endif
287
288
289QString Logger::levelToString(Logger::LogLevel logLevel)
290{
291 switch (logLevel)
292 {
293 case Trace:
294 return QLatin1String("Trace");
295 case Debug:
296 return QLatin1String("Debug");
297 case Info:
298 return QLatin1String("Info");
299 case Warning:
300 return QLatin1String("Warning");
301 case Error:
302 return QLatin1String("Error");
303 case Fatal:
304 return QLatin1String("Fatal");
305 }
306
307 return QString();
308}
309
310
311Logger::LogLevel Logger::levelFromString(const QString& s)
312{
313 QString str = s.trimmed().toLower();
314
315 LogLevel result = Debug;
316
317 if (str == QLatin1String("trace"))
318 result = Trace;
319 else if (str == QLatin1String("debug"))
320 result = Debug;
321 else if (str == QLatin1String("info"))
322 result = Info;
323 else if (str == QLatin1String("warning"))
324 result = Warning;
325 else if (str == QLatin1String("error"))
326 result = Error;
327 else if (str == QLatin1String("fatal"))
328 result = Fatal;
329
330 return result;
331}
332
333
334void Logger::registerAppender(AbstractAppender* appender)
335{
336 LoggerPrivate::instance()->registerAppender(appender);
337}
338
339
340void Logger::write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function,
341 const QString& message)
342{
343 LoggerPrivate::instance()->write(timeStamp, logLevel, file, line, function, message);
344}
345
346
347void Logger::write(LogLevel logLevel, const char* file, int line, const char* function, const QString& message)
348{
349 LoggerPrivate::instance()->write(logLevel, file, line, function, message);
350}
351
352
353void Logger::write(LogLevel logLevel, const char* file, int line, const char* function, const char* message, ...)
354{
355 va_list va;
356 va_start(va, message);
357 LoggerPrivate::instance()->write(logLevel, file, line, function, QString().vsprintf(message,va));
358 va_end(va);
359}
360
361QDebug Logger::write(LogLevel logLevel, const char* file, int line, const char* function)
362{
363 return LoggerPrivate::instance()->write(logLevel, file, line, function);
364}
365
366
367void Logger::writeAssert(const char* file, int line, const char* function, const char* condition)
368{
369 LoggerPrivate::instance()->writeAssert(file, line, function, condition);
370}
diff --git a/rbutil/rbutilqt/logger/Logger.h b/rbutil/rbutilqt/logger/Logger.h
deleted file mode 100644
index d056dfc25d..0000000000
--- a/rbutil/rbutilqt/logger/Logger.h
+++ /dev/null
@@ -1,319 +0,0 @@
1/*
2 Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License version 2.1
6 as published by the Free Software Foundation and appearing in the file
7 LICENSE.LGPL included in the packaging of this file.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13*/
14#ifndef LOGGER_H
15#define LOGGER_H
16/**
17 * \file Logger.h
18 * \brief A file containing the description of Logger class and and additional useful macros for logging
19 */
20
21// Qt
22#include <QString>
23#include <QDebug>
24class QDateTime;
25
26// Local
27#include "CuteLogger_global.h"
28class AbstractAppender;
29
30
31//! Writes the trace log record
32/**
33 * This macro is the convinient way to call Logger::write(). It uses the common preprocessor macros \c __FILE__,
34 * \c __LINE__ and the standart Qt \c Q_FUNC_INFO macros to automatically determine the needed parameters to call
35 * Logger::write().
36 *
37 * \note This and other (LOG_INFO() etc...) macros uses the variadic macro arguments to give convinient usage form for
38 * the different versions of Logger::write() (using the QString or const char* argument or returning the QDebug class
39 * instance). Not all compilers will support this. Please, consider reviewing your compiler documentation to ensure
40 * it support __VA_ARGS__ macro.
41 *
42 * It is checked to work with GCC 4.4 or later.
43 *
44 * \sa Logger::LogLevel
45 * \sa Logger::write()
46 */
47#define LOG_TRACE(...) Logger::write(Logger::Trace, __FILE__, __LINE__, Q_FUNC_INFO, ##__VA_ARGS__)
48
49//! Writes the debug log record
50/**
51 * This macro records the info log record using the Logger::write() function. It works identically to the LOG_TRACE()
52 * macro.
53 *
54 * \sa LOG_TRACE()
55 * \sa Logger::LogLevel
56 * \sa Logger::write()
57 */
58#define LOG_DEBUG(...) Logger::write(Logger::Debug, __FILE__, __LINE__, Q_FUNC_INFO, ##__VA_ARGS__)
59
60//! Write the info log record
61/**
62 * This macro records the info log record using the Logger::write() function. It works identically to the LOG_TRACE()
63 * macro.
64 *
65 * \sa LOG_TRACE()
66 * \sa Logger::LogLevel
67 * \sa Logger::write()
68 */
69#define LOG_INFO(...) Logger::write(Logger::Info, __FILE__, __LINE__, Q_FUNC_INFO, ##__VA_ARGS__)
70
71//! Write the warning log record
72/**
73 * This macro records the warning log record using the Logger::write() function. It works identically to the LOG_TRACE()
74 * macro.
75 *
76 * \sa LOG_TRACE()
77 * \sa Logger::LogLevel
78 * \sa Logger::write()
79 */
80#define LOG_WARNING(...) Logger::write(Logger::Warning, __FILE__, __LINE__, Q_FUNC_INFO, ##__VA_ARGS__)
81
82//! Write the error log record
83/**
84 * This macro records the error log record using the Logger::write() function. It works identically to the LOG_TRACE()
85 * macro.
86 *
87 * \sa LOG_TRACE()
88 * \sa Logger::LogLevel
89 * \sa Logger::write()
90 */
91#define LOG_ERROR(...) Logger::write(Logger::Error, __FILE__, __LINE__, Q_FUNC_INFO, ##__VA_ARGS__)
92
93//! Write the fatal log record
94/**
95 * This macro records the fatal log record using the Logger::write() function. It works identically to the LOG_TRACE()
96 * macro.
97 *
98 * \note Recording of the log record using the Logger::Fatal log level will lead to calling the STL abort()
99 * function, which will interrupt the running of your software and begin the writing of the core dump.
100 *
101 * \sa LOG_TRACE()
102 * \sa Logger::LogLevel
103 * \sa Logger::write()
104 */
105#define LOG_FATAL(...) Logger::write(Logger::Fatal, __FILE__, __LINE__, Q_FUNC_INFO, ##__VA_ARGS__)
106
107//! Check the assertion
108/**
109 * This macro is a convinient and recommended to use way to call Logger::writeAssert() function. It uses the
110 * preprocessor macros (as the LOG_DEBUG() does) to fill the necessary arguments of the Logger::writeAssert() call. It
111 * also uses undocumented but rather mature and stable \c qt_noop() function (which does nothing) when the assertion
112 * is true.
113 *
114 * Example:
115 * \code
116 * bool b = checkSomething();
117 * ...
118 * LOG_ASSERT(b == true);
119 * \endcode
120 *
121 * \sa Logger::writeAssert()
122 */
123#define LOG_ASSERT(cond) ((!(cond)) ? Logger::writeAssert(__FILE__, __LINE__, Q_FUNC_INFO, #cond) : qt_noop())
124
125
126/**
127 * \mainpage
128 *
129 * Logger is a simple way to write the history of your application lifecycle to any target logging device (which is
130 * called Appender and may write to any target you will implement with it: console, text file, XML or something - you
131 * choose) and to map logging message to a class, function, source file and line of code which it is called from.
132 *
133 * Some simple appenders (which may be considered an examples) are provided with the logger itself: see ConsoleAppender
134 * and FileAppender documentation.
135 *
136 * It supports using it in a multithreaded applications, so ALL of its functions are thread safe.
137 *
138 * Simple usage example:
139 * \code
140 * #include <QCoreApplication>
141 *
142 * #include <Logger.h>
143 * #include <ConsoleAppender.h>
144 *
145 * int main(int argc, char* argv[])
146 * {
147 * QCoreApplication app(argc, argv);
148 * ...
149 * ConsoleAppender* consoleAppender = new ConsoleAppender();
150 * consoleAppender->setFormat("[%-7l] <%C> %m\n");
151 * Logger::registerAppender(consoleAppender);
152 * ...
153 * LOG_INFO("Starting the application");
154 * int result = app.exec();
155 * ...
156 * if (result)
157 * LOG_WARNING() << "Something went wrong." << "Result code is" << result;
158 *
159 * return result;
160 * }
161 * \endcode
162 *
163 * Logger internally uses the lazy-initialized singleton object and needs no definite initialization, but you may
164 * consider registering a log appender before calling any log recording functions or macros.
165 *
166 * The library design of Logger allows you to simply mass-replace all occurrences of qDebug and similiar calls with
167 * similiar Logger macros (e.g. LOG_DEBUG)
168 *
169 * \note Logger uses a singleton class which must live through all the application life cycle and cleans it on the
170 * destruction of the QCoreApplication (or QApplication) instance. It needs a QCoreApplication instance to be
171 * created before any of the Logger's functions are called.
172 *
173 * \sa AbstractAppender
174 * \sa LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_FATAL
175 * \sa LOG_ASSERT
176 */
177
178//! Very simple but rather powerful component which may be used for logging your application activities.
179class CUTELOGGERSHARED_EXPORT Logger
180{
181 public:
182 //! Describes the possible severity levels of the log records
183 enum LogLevel
184 {
185 Trace, //!< Trace level. Can be used for mostly unneeded records used for internal code tracing.
186 Debug, //!< Debug level. Useful for non-necessary records used for the debugging of the software.
187 Info, //!< Info level. Can be used for informational records, which may be interesting for not only developers.
188 Warning, //!< Warning. May be used to log some non-fatal warnings detected by your application.
189 Error, //!< Error. May be used for a big problems making your application work wrong but not crashing.
190 Fatal //!< Fatal. Used for unrecoverable errors, crashes the application right after the log record is written.
191 };
192
193 //! Converts the LogLevel enum value to its string representation
194 /**
195 * \param logLevel Log level to convert
196 *
197 * \sa LogLevel
198 * \sa levelFromString()
199 */
200 static QString levelToString(LogLevel logLevel);
201
202 //! Converts the LogLevel string representation to enum value
203 /**
204 * Comparation of the strings is case independent. If the log level string provided cannot be understood
205 * Logger::Debug is returned.
206 *
207 * \param s String to be decoded
208 *
209 * \sa LogLevel
210 * \sa levelToString()
211 */
212 static LogLevel levelFromString(const QString& s);
213
214 //! Registers the appender to write the log records to
215 /**
216 * On the log writing call (using one of the macros or the write() function) Logger traverses through the list of
217 * the appenders and writes a log records to the each of them. Please, look through the AbstractAppender
218 * documentation to understand the concept of appenders.
219 *
220 * If no appenders was added to Logger, it falls back to logging into the \c std::cerr STL stream.
221 *
222 * \param appender Appender to register in the Logger
223 *
224 * \note Logger takes ownership on the appender and it will delete it on the application exit. According to this,
225 * appenders must be created on heap to prevent double destruction of the appender.
226 *
227 * \sa AbstractAppender
228 */
229 static void registerAppender(AbstractAppender* appender);
230
231 //! Writes the log record
232 /**
233 * Writes the log records with the supplied arguments to all the registered appenders.
234 *
235 * \note It is not recommended to call this function directly. Instead of this you can just call one of the macros
236 * (LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_FATAL) that will supply all the needed
237 * information to this function.
238 *
239 * \param timeStamp - the time stamp of the record
240 * \param logLevel - the log level of the record
241 * \param file - the name of the source file that requested the log record
242 * \param line - the line of the code of source file that requested the log record
243 * \param function - name of the function that requested the log record
244 * \param message - log message
245 *
246 * \note Recording of the log record using the Logger::Fatal log level will lead to calling the STL abort()
247 * function, which will interrupt the running of your software and begin the writing of the core dump.
248 *
249 * \sa LogLevel
250 * \sa LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_FATAL
251 * \sa AbstractAppender
252 */
253 static void write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function,
254 const QString& message);
255
256 /**
257 * This is the overloaded function provided for the convinience. It behaves identically to the above function.
258 *
259 * This function uses the current timestamp obtained with \c QDateTime::currentDateTime().
260 *
261 * \sa write()
262 */
263 static void write(LogLevel logLevel, const char* file, int line, const char* function, const QString& message);
264
265 /**
266 * This is the overloaded function provided for the convinience. It behaves identically to the above function.
267 *
268 * This function uses the current timestamp obtained with \c QDateTime::currentDateTime(). Also it supports writing
269 * <tt>const char*</tt> instead of \c QString and converts it internally using the \c QString::fromAscii(). If you
270 * want this function to support the non-ascii strings, you will need to setup the codec using the
271 * \c QTextCodec::setCodecForCStrings()
272 *
273 * \sa write()
274 */
275 static void write(LogLevel logLevel, const char* file, int line, const char* function, const char* message, ...);
276
277 /**
278 * This is the overloaded function provided for the convinience. It behaves identically to the above function.
279 *
280 * This function doesn't accept any log message as argument. It returns the \c QDebug object that can be written
281 * using the stream functions. For example, you may like to write:
282 * \code
283 * LOG_DEBUG() << "This is the size" << size << "of the element" << elementName;
284 * \endcode
285 * instead of writing
286 * \code
287 * LOG_DEBUG(QString(QLatin1String("This is the size %1x%2 of the element %3"))
288 * .arg(size.x()).arg(size.y()).arg(elementName));
289 * \endcode
290 *
291 * Please consider reading the Qt Reference Documentation for the description of the QDebug class usage syntax.
292 *
293 * \note This overload is definitely more pleasant to use than the first write() overload, but it behaves definitely
294 * slower than all the above overloads.
295 *
296 * \sa write()
297 */
298 static QDebug write(LogLevel logLevel, const char* file, int line, const char* function);
299
300 //! Writes the assertion
301 /**
302 * This function writes the assertion record using the write() function.
303 *
304 * The assertion record is always written using the Logger::Fatal log level which leads to the abortation of the
305 * program and generation of the core dump (if supported).
306 *
307 * The message written to the appenders will be identical to the \c condition argument prefixed with the
308 * <tt>ASSERT:</tt> notification.
309 *
310 * \note It is not recommended to call this function directly. Instead of this you can just call the LOG_ASSERT
311 * macro that will supply all the needed information to this function.
312 *
313 * \sa LOG_ASSERT
314 * \sa write()
315 */
316 static void writeAssert(const char* file, int line, const char* function, const char* condition);
317};
318
319#endif // LOGGER_H
diff --git a/rbutil/rbutilqt/logger/README.ROCKBOX b/rbutil/rbutilqt/logger/README.ROCKBOX
index 08f537fb3f..f501a410dd 100644
--- a/rbutil/rbutilqt/logger/README.ROCKBOX
+++ b/rbutil/rbutilqt/logger/README.ROCKBOX
@@ -1,7 +1,7 @@
1This folder contains the cutelogger project for logging functionality. 1This folder contains the cutelogger project for logging functionality.
2These files are distributed under the LGPL v2 or later. 2These files are distributed under the LGPL v2 or later.
3The source files have been last synced with the projects at 3The source files have been last synced with the projects at
4https://gitorious.org/cutelogger to commit 4https://github.com/dept2/CuteLogger/ to commit
5e3c2745c6c5f38896f87472e01ea2caf2d9e211b. 55ae6b9ac13e0cc2821d236e3542a83990b63c95c
6 6on Aug 7, 2020.
7 7
diff --git a/rbutil/rbutilqt/logger/include/AbstractAppender.h b/rbutil/rbutilqt/logger/include/AbstractAppender.h
new file mode 100644
index 0000000000..e029b045aa
--- /dev/null
+++ b/rbutil/rbutilqt/logger/include/AbstractAppender.h
@@ -0,0 +1,49 @@
1/*
2 Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License version 2.1
6 as published by the Free Software Foundation and appearing in the file
7 LICENSE.LGPL included in the packaging of this file.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13*/
14#ifndef ABSTRACTAPPENDER_H
15#define ABSTRACTAPPENDER_H
16
17// Local
18#include "CuteLogger_global.h"
19#include <Logger.h>
20
21// Qt
22#include <QMutex>
23
24
25class CUTELOGGERSHARED_EXPORT AbstractAppender
26{
27 public:
28 AbstractAppender();
29 virtual ~AbstractAppender();
30
31 Logger::LogLevel detailsLevel() const;
32 void setDetailsLevel(Logger::LogLevel level);
33 void setDetailsLevel(const QString& level);
34
35 void write(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, const char* function,
36 const QString& category, const QString& message);
37
38 protected:
39 virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
40 const char* function, const QString& category, const QString& message) = 0;
41
42 private:
43 QMutex m_writeMutex;
44
45 Logger::LogLevel m_detailsLevel;
46 mutable QMutex m_detailsLevelMutex;
47};
48
49#endif // ABSTRACTAPPENDER_H
diff --git a/rbutil/rbutilqt/logger/include/AbstractStringAppender.h b/rbutil/rbutilqt/logger/include/AbstractStringAppender.h
new file mode 100644
index 0000000000..78df9e6176
--- /dev/null
+++ b/rbutil/rbutilqt/logger/include/AbstractStringAppender.h
@@ -0,0 +1,46 @@
1/*
2 Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License version 2.1
6 as published by the Free Software Foundation and appearing in the file
7 LICENSE.LGPL included in the packaging of this file.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13*/
14#ifndef ABSTRACTSTRINGAPPENDER_H
15#define ABSTRACTSTRINGAPPENDER_H
16
17// Local
18#include "CuteLogger_global.h"
19#include <AbstractAppender.h>
20
21// Qt
22#include <QReadWriteLock>
23
24
25class CUTELOGGERSHARED_EXPORT AbstractStringAppender : public AbstractAppender
26{
27 public:
28 AbstractStringAppender();
29
30 virtual QString format() const;
31 void setFormat(const QString&);
32
33 static QString stripFunctionName(const char*);
34
35 protected:
36 QString formattedString(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
37 const char* function, const QString& category, const QString& message) const;
38
39 private:
40 static QByteArray qCleanupFuncinfo(const char*);
41
42 QString m_format;
43 mutable QReadWriteLock m_formatLock;
44};
45
46#endif // ABSTRACTSTRINGAPPENDER_H
diff --git a/rbutil/rbutilqt/logger/ConsoleAppender.h b/rbutil/rbutilqt/logger/include/ConsoleAppender.h
index fa685b5e82..64ef2e7a19 100644
--- a/rbutil/rbutilqt/logger/ConsoleAppender.h
+++ b/rbutil/rbutilqt/logger/include/ConsoleAppender.h
@@ -16,17 +16,21 @@
16 16
17#include "CuteLogger_global.h" 17#include "CuteLogger_global.h"
18#include <AbstractStringAppender.h> 18#include <AbstractStringAppender.h>
19 19
20//! ConsoleAppender is the simple appender that writes the log records to the std::cerr output stream. 20
21class CUTELOGGERSHARED_EXPORT ConsoleAppender : public AbstractStringAppender 21class CUTELOGGERSHARED_EXPORT ConsoleAppender : public AbstractStringAppender
22{ 22{
23 public:
24 ConsoleAppender();
25 virtual QString format() const;
26 void ignoreEnvironmentPattern(bool ignore);
27
23 protected: 28 protected:
24 //! Writes the log record to the std::cerr stream.
25 /**
26 * \sa AbstractStringAppender::format()
27 */
28 virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, 29 virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
29 const char* function, const QString& message); 30 const char* function, const QString& category, const QString& message);
31
32 private:
33 bool m_ignoreEnvPattern;
30}; 34};
31 35
32#endif // CONSOLEAPPENDER_H 36#endif // CONSOLEAPPENDER_H
diff --git a/rbutil/rbutilqt/logger/CuteLogger_global.h b/rbutil/rbutilqt/logger/include/CuteLogger_global.h
index c5e7680845..c5e7680845 100644
--- a/rbutil/rbutilqt/logger/CuteLogger_global.h
+++ b/rbutil/rbutilqt/logger/include/CuteLogger_global.h
diff --git a/rbutil/rbutilqt/logger/FileAppender.h b/rbutil/rbutilqt/logger/include/FileAppender.h
index 70a70c3e43..ab9e12a91d 100644
--- a/rbutil/rbutilqt/logger/FileAppender.h
+++ b/rbutil/rbutilqt/logger/include/FileAppender.h
@@ -23,34 +23,20 @@
23#include <QTextStream> 23#include <QTextStream>
24 24
25 25
26//! File is the simple appender that writes the log records to the plain text file.
27class CUTELOGGERSHARED_EXPORT FileAppender : public AbstractStringAppender 26class CUTELOGGERSHARED_EXPORT FileAppender : public AbstractStringAppender
28{ 27{
29 public: 28 public:
30 //! Constructs the new file appender assigned to file with the given name.
31 FileAppender(const QString& fileName = QString()); 29 FileAppender(const QString& fileName = QString());
32 ~FileAppender(); 30 ~FileAppender();
33 31
34 //! Returns the name set by setFileName() or to the FileAppender constructor.
35 /**
36 * \sa setFileName()
37 */
38 QString fileName() const; 32 QString fileName() const;
39
40 //! Sets the name of the file. The name can have no path, a relative path, or an absolute path.
41 /**
42 * \sa fileName()
43 */
44 void setFileName(const QString&); 33 void setFileName(const QString&);
45 34
35 bool reopenFile();
36
46 protected: 37 protected:
47 //! Write the log record to the file.
48 /**
49 * \sa fileName()
50 * \sa AbstractStringAppender::format()
51 */
52 virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, 38 virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
53 const char* function, const QString& message); 39 const char* function, const QString& category, const QString& message);
54 bool openFile(); 40 bool openFile();
55 void closeFile(); 41 void closeFile();
56 42
diff --git a/rbutil/rbutilqt/logger/include/Logger.h b/rbutil/rbutilqt/logger/include/Logger.h
new file mode 100644
index 0000000000..509bc5f435
--- /dev/null
+++ b/rbutil/rbutilqt/logger/include/Logger.h
@@ -0,0 +1,237 @@
1/*
2 Copyright (c) 2012 Boris Moiseev (cyberbobs at gmail dot com)
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License version 2.1
6 as published by the Free Software Foundation and appearing in the file
7 LICENSE.LGPL included in the packaging of this file.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13*/
14#ifndef LOGGER_H
15#define LOGGER_H
16
17// Qt
18#include <QString>
19#include <QDebug>
20#include <QDateTime>
21
22// Local
23#include "CuteLogger_global.h"
24class AbstractAppender;
25
26
27class Logger;
28CUTELOGGERSHARED_EXPORT Logger* cuteLoggerInstance();
29#define cuteLogger cuteLoggerInstance()
30
31
32#define LOG_TRACE CuteMessageLogger(cuteLoggerInstance(), Logger::Trace, __FILE__, __LINE__, Q_FUNC_INFO).write
33#define LOG_DEBUG CuteMessageLogger(cuteLoggerInstance(), Logger::Debug, __FILE__, __LINE__, Q_FUNC_INFO).write
34#define LOG_INFO CuteMessageLogger(cuteLoggerInstance(), Logger::Info, __FILE__, __LINE__, Q_FUNC_INFO).write
35#define LOG_WARNING CuteMessageLogger(cuteLoggerInstance(), Logger::Warning, __FILE__, __LINE__, Q_FUNC_INFO).write
36#define LOG_ERROR CuteMessageLogger(cuteLoggerInstance(), Logger::Error, __FILE__, __LINE__, Q_FUNC_INFO).write
37#define LOG_FATAL CuteMessageLogger(cuteLoggerInstance(), Logger::Fatal, __FILE__, __LINE__, Q_FUNC_INFO).write
38
39#define LOG_CTRACE(category) CuteMessageLogger(cuteLoggerInstance(), Logger::Trace, __FILE__, __LINE__, Q_FUNC_INFO, category).write()
40#define LOG_CDEBUG(category) CuteMessageLogger(cuteLoggerInstance(), Logger::Debug, __FILE__, __LINE__, Q_FUNC_INFO, category).write()
41#define LOG_CINFO(category) CuteMessageLogger(cuteLoggerInstance(), Logger::Info, __FILE__, __LINE__, Q_FUNC_INFO, category).write()
42#define LOG_CWARNING(category) CuteMessageLogger(cuteLoggerInstance(), Logger::Warning, __FILE__, __LINE__, Q_FUNC_INFO, category).write()
43#define LOG_CERROR(category) CuteMessageLogger(cuteLoggerInstance(), Logger::Error, __FILE__, __LINE__, Q_FUNC_INFO, category).write()
44#define LOG_CFATAL(category) CuteMessageLogger(cuteLoggerInstance(), Logger::Fatal, __FILE__, __LINE__, Q_FUNC_INFO, category).write()
45
46#define LOG_TRACE_TIME LoggerTimingHelper loggerTimingHelper(cuteLoggerInstance(), Logger::Trace, __FILE__, __LINE__, Q_FUNC_INFO); loggerTimingHelper.start
47#define LOG_DEBUG_TIME LoggerTimingHelper loggerTimingHelper(cuteLoggerInstance(), Logger::Debug, __FILE__, __LINE__, Q_FUNC_INFO); loggerTimingHelper.start
48#define LOG_INFO_TIME LoggerTimingHelper loggerTimingHelper(cuteLoggerInstance(), Logger::Info, __FILE__, __LINE__, Q_FUNC_INFO); loggerTimingHelper.start
49
50#define LOG_ASSERT(cond) ((!(cond)) ? cuteLoggerInstance()->writeAssert(__FILE__, __LINE__, Q_FUNC_INFO, #cond) : qt_noop())
51#define LOG_ASSERT_X(cond, msg) ((!(cond)) ? cuteLoggerInstance()->writeAssert(__FILE__, __LINE__, Q_FUNC_INFO, msg) : qt_noop())
52
53#if (__cplusplus >= 201103L)
54#include <functional>
55
56#define LOG_CATEGORY(category) \
57 Logger customCuteLoggerInstance{category};\
58 std::function<Logger*()> cuteLoggerInstance = [&customCuteLoggerInstance]() {\
59 return &customCuteLoggerInstance;\
60 };\
61
62#define LOG_GLOBAL_CATEGORY(category) \
63 Logger customCuteLoggerInstance{category, true};\
64 std::function<Logger*()> cuteLoggerInstance = [&customCuteLoggerInstance]() {\
65 return &customCuteLoggerInstance;\
66 };\
67
68#else
69
70#define LOG_CATEGORY(category) \
71 Logger* cuteLoggerInstance()\
72 {\
73 static Logger customCuteLoggerInstance(category);\
74 return &customCuteLoggerInstance;\
75 }\
76
77#define LOG_GLOBAL_CATEGORY(category) \
78 Logger* cuteLoggerInstance()\
79 {\
80 static Logger customCuteLoggerInstance(category);\
81 customCuteLoggerInstance.logToGlobalInstance(category, true);\
82 return &customCuteLoggerInstance;\
83 }\
84
85#endif
86
87
88class LoggerPrivate;
89class CUTELOGGERSHARED_EXPORT Logger
90{
91 Q_DISABLE_COPY(Logger)
92
93 public:
94 Logger();
95 Logger(const QString& defaultCategory, bool writeToGlobalInstance = false);
96 ~Logger();
97
98 //! Describes the possible severity levels of the log records
99 enum LogLevel
100 {
101 Trace, //!< Trace level. Can be used for mostly unneeded records used for internal code tracing.
102 Debug, //!< Debug level. Useful for non-necessary records used for the debugging of the software.
103 Info, //!< Info level. Can be used for informational records, which may be interesting for not only developers.
104 Warning, //!< Warning. May be used to log some non-fatal warnings detected by your application.
105 Error, //!< Error. May be used for a big problems making your application work wrong but not crashing.
106 Fatal //!< Fatal. Used for unrecoverable errors, crashes the application right after the log record is written.
107 };
108
109 //! Sets the timing display mode for the LOG_TRACE_TIME, LOG_DEBUG_TIME and LOG_INFO_TIME macros
110 enum TimingMode
111 {
112 TimingAuto, //!< Show time in seconds, if it exceeds 10s (default)
113 TimingMs //!< Always use milliseconds to display
114 };
115
116 static QString levelToString(LogLevel logLevel);
117 static LogLevel levelFromString(const QString& s);
118
119 static Logger* globalInstance();
120
121 void registerAppender(AbstractAppender* appender);
122 void registerCategoryAppender(const QString& category, AbstractAppender* appender);
123
124 void removeAppender(AbstractAppender* appender);
125
126 void logToGlobalInstance(const QString& category, bool logToGlobal = false);
127
128 void setDefaultCategory(const QString& category);
129 QString defaultCategory() const;
130
131 void write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function, const char* category,
132 const QString& message);
133 void write(LogLevel logLevel, const char* file, int line, const char* function, const char* category, const QString& message);
134
135 void writeAssert(const char* file, int line, const char* function, const char* condition);
136
137 private:
138 void write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function, const char* category,
139 const QString& message, bool fromLocalInstance);
140 Q_DECLARE_PRIVATE(Logger)
141 LoggerPrivate* d_ptr;
142};
143
144
145class CUTELOGGERSHARED_EXPORT CuteMessageLogger
146{
147 Q_DISABLE_COPY(CuteMessageLogger)
148
149 public:
150 CuteMessageLogger(Logger* l, Logger::LogLevel level, const char* file, int line, const char* function)
151 : m_l(l),
152 m_level(level),
153 m_file(file),
154 m_line(line),
155 m_function(function),
156 m_category(nullptr)
157 {}
158
159 CuteMessageLogger(Logger* l, Logger::LogLevel level, const char* file, int line, const char* function, const char* category)
160 : m_l(l),
161 m_level(level),
162 m_file(file),
163 m_line(line),
164 m_function(function),
165 m_category(category)
166 {}
167
168 ~CuteMessageLogger();
169
170 void write(const char* msg, ...)
171#if defined(Q_CC_GNU) && !defined(__INSURE__)
172# if defined(Q_CC_MINGW) && !defined(Q_CC_CLANG)
173 __attribute__ ((format (gnu_printf, 2, 3)))
174# else
175 __attribute__ ((format (printf, 2, 3)))
176# endif
177#endif
178 ;
179
180 void write(const QString& msg);
181
182 QDebug write();
183
184 private:
185 Logger* m_l;
186 Logger::LogLevel m_level;
187 const char* m_file;
188 int m_line;
189 const char* m_function;
190 const char* m_category;
191 QString m_message;
192};
193
194
195class CUTELOGGERSHARED_EXPORT LoggerTimingHelper
196{
197 Q_DISABLE_COPY(LoggerTimingHelper)
198
199 public:
200 inline explicit LoggerTimingHelper(Logger* l, Logger::LogLevel logLevel, const char* file, int line,
201 const char* function)
202 : m_logger(l),
203 m_logLevel(logLevel),
204 m_timingMode(Logger::TimingAuto),
205 m_file(file),
206 m_line(line),
207 m_function(function)
208 {}
209
210 void start(const char* msg, ...)
211#if defined(Q_CC_GNU) && !defined(__INSURE__)
212 # if defined(Q_CC_MINGW) && !defined(Q_CC_CLANG)
213 __attribute__ ((format (gnu_printf, 2, 3)))
214 # else
215 __attribute__ ((format (printf, 2, 3)))
216 # endif
217#endif
218 ;
219
220 void start(const QString& msg = QString());
221 void start(Logger::TimingMode mode, const QString& msg);
222
223 ~LoggerTimingHelper();
224
225 private:
226 Logger* m_logger;
227 QTime m_time;
228 Logger::LogLevel m_logLevel;
229 Logger::TimingMode m_timingMode;
230 const char* m_file;
231 int m_line;
232 const char* m_function;
233 QString m_block;
234};
235
236
237#endif // LOGGER_H
diff --git a/rbutil/rbutilqt/logger/OutputDebugAppender.h b/rbutil/rbutilqt/logger/include/OutputDebugAppender.h
index f5a5b8c588..dd7ad4deb7 100644
--- a/rbutil/rbutilqt/logger/OutputDebugAppender.h
+++ b/rbutil/rbutilqt/logger/include/OutputDebugAppender.h
@@ -18,16 +18,12 @@
18#include "CuteLogger_global.h" 18#include "CuteLogger_global.h"
19#include <AbstractStringAppender.h> 19#include <AbstractStringAppender.h>
20 20
21//! OutputDebugAppender is the appender that writes the log records to the Microsoft Debug Log 21
22class CUTELOGGERSHARED_EXPORT OutputDebugAppender : public AbstractStringAppender 22class CUTELOGGERSHARED_EXPORT OutputDebugAppender : public AbstractStringAppender
23{ 23{
24 protected: 24 protected:
25 //! Writes the log record to the windows debug log.
26 /**
27 * \sa AbstractStringAppender::format()
28 */
29 virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, 25 virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
30 const char* function, const QString& message); 26 const char* function, const QString& category, const QString& message);
31}; 27};
32 28
33#endif // OUTPUTDEBUGAPPENDER_H 29#endif // OUTPUTDEBUGAPPENDER_H
diff --git a/rbutil/rbutilqt/logger/logger.pri b/rbutil/rbutilqt/logger/logger.pri
index ec8d68b399..e34bccb3cf 100644
--- a/rbutil/rbutilqt/logger/logger.pri
+++ b/rbutil/rbutilqt/logger/logger.pri
@@ -1,19 +1,22 @@
1 1
2SOURCES += \ 2SOURCES += \
3 $$PWD/AbstractAppender.cpp \ 3 $$PWD/src/AbstractAppender.cpp \
4 $$PWD/AbstractStringAppender.cpp \ 4 $$PWD/src/AbstractStringAppender.cpp \
5 $$PWD/ConsoleAppender.cpp \ 5 $$PWD/src/ConsoleAppender.cpp \
6 $$PWD/FileAppender.cpp \ 6 $$PWD/src/FileAppender.cpp \
7 $$PWD/Logger.cpp \ 7 $$PWD/src/Logger.cpp \
8 8
9INCLUDES += \ 9HEADERS += \
10 $$PWD/AbstractAppender.h \ 10 $$PWD/include/AbstractAppender.h \
11 $$PWD/ConsoleAppender.h \ 11 $$PWD/include/ConsoleAppender.h \
12 $$PWD/FileAppender.h \ 12 $$PWD/include/FileAppender.h \
13 $$PWD/OutputDebugAppender.h \ 13 $$PWD/include/OutputDebugAppender.h \
14 $$PWD/AbstractStringAppender.h \ 14 $$PWD/include/AbstractStringAppender.h \
15 $$PWD/CuteLogger_global.h \ 15 $$PWD/include/CuteLogger_global.h \
16 $$PWD/Logger.h \ 16 $$PWD/include/Logger.h \
17
18INCLUDEPATH += $$PWD/include
17 19
18DEFINES += \ 20DEFINES += \
19 CUTELOGGER_STATIC 21 CUTELOGGER_STATIC
22
diff --git a/rbutil/rbutilqt/logger/src/AbstractAppender.cpp b/rbutil/rbutilqt/logger/src/AbstractAppender.cpp
new file mode 100644
index 0000000000..778bbddd11
--- /dev/null
+++ b/rbutil/rbutilqt/logger/src/AbstractAppender.cpp
@@ -0,0 +1,147 @@
1/*
2 Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License version 2.1
6 as published by the Free Software Foundation and appearing in the file
7 LICENSE.LGPL included in the packaging of this file.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13*/
14// Local
15#include "AbstractAppender.h"
16
17// Qt
18#include <QMutexLocker>
19
20
21/**
22 * \class AbstractAppender
23 *
24 * \brief The AbstractAppender class provides an abstract base class for writing a log entries.
25 *
26 * The AbstractAppender class is the base interface class for all log appenders that could be used with Logger.
27 *
28 * AbstractAppender provides a common implementation for the thread safe, mutex-protected logging of application
29 * messages, such as ConsoleAppender, FileAppender or something else. AbstractAppender is abstract and can not be
30 * instantiated, but you can use any of its subclasses or create a custom log appender at your choice.
31 *
32 * Appenders are the logical devices that is aimed to be attached to Logger object by calling
33 * Logger::registerAppender(). On each log record call from the application Logger object sequentially calls write()
34 * function on all the appenders registered in it.
35 *
36 * You can subclass AbstractAppender to implement a logging target of any kind you like. It may be the external logging
37 * subsystem (for example, syslog in *nix), XML file, SQL database entries, D-Bus messages or anything else you can
38 * imagine.
39 *
40 * For the simple non-structured plain text logging (for example, to a plain text file or to the console output) you may
41 * like to subclass the AbstractStringAppender instead of AbstractAppender, which will give you a more convinient way to
42 * control the format of the log output.
43 *
44 * \sa AbstractStringAppender
45 * \sa Logger::registerAppender()
46 */
47
48
49//! Constructs a AbstractAppender object.
50AbstractAppender::AbstractAppender()
51 : m_detailsLevel(Logger::Debug)
52{}
53
54
55//! Destructs the AbstractAppender object.
56AbstractAppender::~AbstractAppender()
57{}
58
59
60//! Returns the current details level of appender.
61/**
62 * Log records with a log level lower than a current detailsLevel() will be silently ignored by appender and would not
63 * be sent to its append() function.
64 *
65 * It provides additional logging flexibility, allowing you to set the different severity levels for different types
66 * of logs.
67 *
68 * \note This function is thread safe.
69 *
70 * \sa setDetailsLevel()
71 * \sa Logger::LogLevel
72 */
73Logger::LogLevel AbstractAppender::detailsLevel() const
74{
75 QMutexLocker locker(&m_detailsLevelMutex);
76 return m_detailsLevel;
77}
78
79
80//! Sets the current details level of appender.
81/**
82 * Default details level is Logger::Debug
83 *
84 * \note This function is thread safe.
85 *
86 * \sa detailsLevel()
87 * \sa Logger::LogLevel
88 */
89void AbstractAppender::setDetailsLevel(Logger::LogLevel level)
90{
91 QMutexLocker locker(&m_detailsLevelMutex);
92 m_detailsLevel = level;
93}
94
95
96
97//! Sets the current details level of appender
98/**
99 * This function is provided for convenience, it behaves like an above function.
100 *
101 * \sa detailsLevel()
102 * \sa Logger::LogLevel
103 */
104void AbstractAppender::setDetailsLevel(const QString& level)
105{
106 setDetailsLevel(Logger::levelFromString(level));
107}
108
109
110//! Tries to write the log record to this logger
111/**
112 * This is the function called by Logger object to write a log message to the appender.
113 *
114 * \note This function is thread safe.
115 *
116 * \sa Logger::write()
117 * \sa detailsLevel()
118 */
119void AbstractAppender::write(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
120 const char* function, const QString& category, const QString& message)
121{
122 if (logLevel >= detailsLevel())
123 {
124 QMutexLocker locker(&m_writeMutex);
125 append(timeStamp, logLevel, file, line, function, category, message);
126 }
127}
128
129
130/**
131 * \fn virtual void AbstractAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file,
132 * int line, const char* function, const QString& message)
133 *
134 * \brief Writes the log record to the logger instance
135 *
136 * This function is called every time when user tries to write a message to this AbstractAppender instance using
137 * the write() function. Write function works as proxy and transfers only the messages with log level more or equal
138 * to the current logLevel().
139 *
140 * Overload this function when you are implementing a custom appender.
141 *
142 * \note This function is not needed to be thread safe because it is never called directly by Logger object. The
143 * write() function works as a proxy and protects this function from concurrent access.
144 *
145 * \sa Logger::write()
146 */
147
diff --git a/rbutil/rbutilqt/logger/src/AbstractStringAppender.cpp b/rbutil/rbutilqt/logger/src/AbstractStringAppender.cpp
new file mode 100644
index 0000000000..ce64aaeb43
--- /dev/null
+++ b/rbutil/rbutilqt/logger/src/AbstractStringAppender.cpp
@@ -0,0 +1,459 @@
1/*
2 Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com) Nikolay Matyunin (matyunin.n at gmail dot com)
3
4 Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
5
6 This program is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License version 2.1
8 as published by the Free Software Foundation and appearing in the file
9 LICENSE.LGPL included in the packaging of this file.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15*/
16// Local
17#include "AbstractStringAppender.h"
18
19// Qt
20#include <QReadLocker>
21#include <QWriteLocker>
22#include <QDateTime>
23#include <QRegExp>
24#include <QCoreApplication>
25#include <QThread>
26
27
28/**
29 * \class AbstractStringAppender
30 *
31 * \brief The AbstractStringAppender class provides a convinient base for appenders working with plain text formatted
32 * logs.
33 *
34 * AbstractSringAppender is the simple extension of the AbstractAppender class providing the convinient way to create
35 * custom log appenders working with a plain text formatted log targets.
36 *
37 * It have the formattedString() protected function that formats the logging arguments according to a format set with
38 * setFormat().
39 *
40 * This class can not be directly instantiated because it contains pure virtual function inherited from AbstractAppender
41 * class.
42 *
43 * For more detailed description of customizing the log output format see the documentation on the setFormat() function.
44 */
45
46
47const char formattingMarker = '%';
48
49
50//! Constructs a new string appender object
51AbstractStringAppender::AbstractStringAppender()
52 : m_format(QLatin1String("%{time}{yyyy-MM-ddTHH:mm:ss.zzz} [%{type:-7}] <%{function}> %{message}\n"))
53{}
54
55
56//! Returns the current log format string.
57/**
58 * The default format is set to "%{time}{yyyy-MM-ddTHH:mm:ss.zzz} [%{type:-7}] <%{function}> %{message}\n". You can set a different log record
59 * format using the setFormat() function.
60 *
61 * \sa setFormat(const QString&)
62 */
63QString AbstractStringAppender::format() const
64{
65 QReadLocker locker(&m_formatLock);
66 return m_format;
67}
68
69
70//! Sets the logging format for writing strings to the log target with this appender.
71/**
72 * The string format seems to be very common to those developers who have used a standart sprintf function.
73 *
74 * Log output format is a simple QString with the special markers (starting with % sign) which will be replaced with
75 * it's internal meaning when writing a log record.
76 *
77 * Controlling marker begins with the percent sign (%) which is followed by the command inside {} brackets
78 * (the command describes, what will be put to log record instead of marker).
79 * Optional field width argument may be specified right after the command (through the colon symbol before the closing bracket)
80 * Some commands requires an additional formatting argument (in the second {} brackets).
81 *
82 * Field width argument works almost identically to the \c QString::arg() \c fieldWidth argument (and uses it
83 * internally). For example, \c "%{type:-7}" will be replaced with the left padded debug level of the message
84 * (\c "Debug ") or something. For the more detailed description of it you may consider to look to the Qt
85 * Reference Documentation.
86 *
87 * Supported marker commands are:
88 * \arg \c %{time} - timestamp. You may specify your custom timestamp format using the second {} brackets after the marker,
89 * timestamp format here will be similiar to those used in QDateTime::toString() function. For example,
90 * "%{time}{dd-MM-yyyy, HH:mm}" may be replaced with "17-12-2010, 20:17" depending on current date and time.
91 * The default format used here is "HH:mm:ss.zzz".
92 * \arg \c %{type} - Log level. Possible log levels are shown in the Logger::LogLevel enumerator.
93 * \arg \c %{Type} - Uppercased log level.
94 * \arg \c %{typeOne} - One letter log level.
95 * \arg \c %{TypeOne} - One uppercase letter log level.
96 * \arg \c %{File} - Full source file name (with path) of the file that requested log recording. Uses the \c __FILE__
97 * preprocessor macro.
98 * \arg \c %{file} - Short file name (with stripped path).
99 * \arg \c %{line} - Line number in the source file. Uses the \c __LINE__ preprocessor macro.
100 * \arg \c %{Function} - Name of function that called on of the LOG_* macros. Uses the \c Q_FUNC_INFO macro provided with
101 * Qt.
102 * \arg \c %{function} - Similiar to the %{Function}, but the function name is stripped using stripFunctionName
103 * \arg \c %{message} - The log message sent by the caller.
104 * \arg \c %{category} - The log category.
105 * \arg \c %{appname} - Application name (returned by QCoreApplication::applicationName() function).
106 * \arg \c %{pid} - Application pid (returned by QCoreApplication::applicationPid() function).
107 * \arg \c %{threadid} - ID of current thread.
108 * \arg \c %% - Convinient marker that is replaced with the single \c % mark.
109 *
110 * \note Format doesn't add \c '\\n' to the end of the format line. Please consider adding it manually.
111 *
112 * \sa format()
113 * \sa stripFunctionName()
114 * \sa Logger::LogLevel
115 */
116void AbstractStringAppender::setFormat(const QString& format)
117{
118 QWriteLocker locker(&m_formatLock);
119 m_format = format;
120}
121
122
123//! Strips the long function signature (as added by Q_FUNC_INFO macro)
124/**
125 * The string processing drops the returning type, arguments and template parameters of function. It is definitely
126 * useful for enchancing the log output readability.
127 * \return stripped function name
128 */
129QString AbstractStringAppender::stripFunctionName(const char* name)
130{
131 return QString::fromLatin1(qCleanupFuncinfo(name));
132}
133
134
135// The function was backported from Qt5 sources (qlogging.h)
136QByteArray AbstractStringAppender::qCleanupFuncinfo(const char* name)
137{
138 QByteArray info(name);
139
140 // Strip the function info down to the base function name
141 // note that this throws away the template definitions,
142 // the parameter types (overloads) and any const/volatile qualifiers.
143 if (info.isEmpty())
144 return info;
145
146 int pos;
147
148 // skip trailing [with XXX] for templates (gcc)
149 pos = info.size() - 1;
150 if (info.endsWith(']')) {
151 while (--pos) {
152 if (info.at(pos) == '[')
153 info.truncate(pos);
154 }
155 }
156
157 bool hasLambda = false;
158 QRegExp lambdaRegex("::<lambda\\(.*\\)>");
159 int lambdaIndex = lambdaRegex.indexIn(QString::fromLatin1(info));
160 if (lambdaIndex != -1)
161 {
162 hasLambda = true;
163 info.remove(lambdaIndex, lambdaRegex.matchedLength());
164 }
165
166 // operator names with '(', ')', '<', '>' in it
167 static const char operator_call[] = "operator()";
168 static const char operator_lessThan[] = "operator<";
169 static const char operator_greaterThan[] = "operator>";
170 static const char operator_lessThanEqual[] = "operator<=";
171 static const char operator_greaterThanEqual[] = "operator>=";
172
173 // canonize operator names
174 info.replace("operator ", "operator");
175
176 // remove argument list
177 forever {
178 int parencount = 0;
179 pos = info.lastIndexOf(')');
180 if (pos == -1) {
181 // Don't know how to parse this function name
182 return info;
183 }
184
185 // find the beginning of the argument list
186 --pos;
187 ++parencount;
188 while (pos && parencount) {
189 if (info.at(pos) == ')')
190 ++parencount;
191 else if (info.at(pos) == '(')
192 --parencount;
193 --pos;
194 }
195 if (parencount != 0)
196 return info;
197
198 info.truncate(++pos);
199
200 if (info.at(pos - 1) == ')') {
201 if (info.indexOf(operator_call) == pos - (int)strlen(operator_call))
202 break;
203
204 // this function returns a pointer to a function
205 // and we matched the arguments of the return type's parameter list
206 // try again
207 info.remove(0, info.indexOf('('));
208 info.chop(1);
209 continue;
210 } else {
211 break;
212 }
213 }
214
215 if (hasLambda)
216 info.append("::lambda");
217
218 // find the beginning of the function name
219 int parencount = 0;
220 int templatecount = 0;
221 --pos;
222
223 // make sure special characters in operator names are kept
224 if (pos > -1) {
225 switch (info.at(pos)) {
226 case ')':
227 if (info.indexOf(operator_call) == pos - (int)strlen(operator_call) + 1)
228 pos -= 2;
229 break;
230 case '<':
231 if (info.indexOf(operator_lessThan) == pos - (int)strlen(operator_lessThan) + 1)
232 --pos;
233 break;
234 case '>':
235 if (info.indexOf(operator_greaterThan) == pos - (int)strlen(operator_greaterThan) + 1)
236 --pos;
237 break;
238 case '=': {
239 int operatorLength = (int)strlen(operator_lessThanEqual);
240 if (info.indexOf(operator_lessThanEqual) == pos - operatorLength + 1)
241 pos -= 2;
242 else if (info.indexOf(operator_greaterThanEqual) == pos - operatorLength + 1)
243 pos -= 2;
244 break;
245 }
246 default:
247 break;
248 }
249 }
250
251 while (pos > -1) {
252 if (parencount < 0 || templatecount < 0)
253 return info;
254
255 char c = info.at(pos);
256 if (c == ')')
257 ++parencount;
258 else if (c == '(')
259 --parencount;
260 else if (c == '>')
261 ++templatecount;
262 else if (c == '<')
263 --templatecount;
264 else if (c == ' ' && templatecount == 0 && parencount == 0)
265 break;
266
267 --pos;
268 }
269 info = info.mid(pos + 1);
270
271 // remove trailing '*', '&' that are part of the return argument
272 while ((info.at(0) == '*')
273 || (info.at(0) == '&'))
274 info = info.mid(1);
275
276 // we have the full function name now.
277 // clean up the templates
278 while ((pos = info.lastIndexOf('>')) != -1) {
279 if (!info.contains('<'))
280 break;
281
282 // find the matching close
283 int end = pos;
284 templatecount = 1;
285 --pos;
286 while (pos && templatecount) {
287 char c = info.at(pos);
288 if (c == '>')
289 ++templatecount;
290 else if (c == '<')
291 --templatecount;
292 --pos;
293 }
294 ++pos;
295 info.remove(pos, end - pos + 1);
296 }
297
298 return info;
299}
300
301
302//! Returns the string to record to the logging target, formatted according to the format().
303/**
304 * \sa format()
305 * \sa setFormat(const QString&)
306 */
307QString AbstractStringAppender::formattedString(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file,
308 int line, const char* function, const QString& category, const QString& message) const
309{
310 QString f = format();
311 const int size = f.size();
312
313 QString result;
314
315 int i = 0;
316 while (i < f.size())
317 {
318 QChar c = f.at(i);
319
320 // We will silently ignore the broken % marker at the end of string
321 if (c != QLatin1Char(formattingMarker) || (i + 2) >= size)
322 {
323 result.append(c);
324 }
325 else
326 {
327 i += 2;
328 QChar currentChar = f.at(i);
329 QString command;
330 int fieldWidth = 0;
331
332 if (currentChar.isLetter())
333 {
334 command.append(currentChar);
335 int j = 1;
336 while ((i + j) < size && f.at(i + j).isLetter())
337 {
338 command.append(f.at(i+j));
339 j++;
340 }
341
342 i+=j;
343 currentChar = f.at(i);
344
345 // Check for the padding instruction
346 if (currentChar == QLatin1Char(':'))
347 {
348 currentChar = f.at(++i);
349 if (currentChar.isDigit() || currentChar.category() == QChar::Punctuation_Dash)
350 {
351 int j = 1;
352 while ((i + j) < size && f.at(i + j).isDigit())
353 j++;
354 fieldWidth = f.mid(i, j).toInt();
355
356 i += j;
357 }
358 }
359 }
360
361 // Log record chunk to insert instead of formatting instruction
362 QString chunk;
363
364 // Time stamp
365 if (command == QLatin1String("time"))
366 {
367 if (f.at(i + 1) == QLatin1Char('{'))
368 {
369 int j = 1;
370 while ((i + 2 + j) < size && f.at(i + 2 + j) != QLatin1Char('}'))
371 j++;
372
373 if ((i + 2 + j) < size)
374 {
375 chunk = timeStamp.toString(f.mid(i + 2, j));
376
377 i += j;
378 i += 2;
379 }
380 }
381
382 if (chunk.isNull())
383 chunk = timeStamp.toString(QLatin1String("HH:mm:ss.zzz"));
384 }
385
386 // Log level
387 else if (command == QLatin1String("type"))
388 chunk = Logger::levelToString(logLevel);
389
390 // Uppercased log level
391 else if (command == QLatin1String("Type"))
392 chunk = Logger::levelToString(logLevel).toUpper();
393
394 // One letter log level
395 else if (command == QLatin1String("typeOne"))
396 chunk = Logger::levelToString(logLevel).left(1).toLower();
397
398 // One uppercase letter log level
399 else if (command == QLatin1String("TypeOne"))
400 chunk = Logger::levelToString(logLevel).left(1).toUpper();
401
402 // Filename
403 else if (command == QLatin1String("File"))
404 chunk = QLatin1String(file);
405
406 // Filename without a path
407 else if (command == QLatin1String("file"))
408 chunk = QString(QLatin1String(file)).section(QRegExp("[/\\\\]"), -1);
409
410 // Source line number
411 else if (command == QLatin1String("line"))
412 chunk = QString::number(line);
413
414 // Function name, as returned by Q_FUNC_INFO
415 else if (command == QLatin1String("Function"))
416 chunk = QString::fromLatin1(function);
417
418 // Stripped function name
419 else if (command == QLatin1String("function"))
420 chunk = stripFunctionName(function);
421
422 // Log message
423 else if (command == QLatin1String("message"))
424 chunk = message;
425
426 else if (command == QLatin1String("category"))
427 chunk = category;
428
429 // Application pid
430 else if (command == QLatin1String("pid"))
431 chunk = QString::number(QCoreApplication::applicationPid());
432
433 // Appplication name
434 else if (command == QLatin1String("appname"))
435 chunk = QCoreApplication::applicationName();
436
437 // Thread ID (duplicates Qt5 threadid debbuging way)
438 else if (command == QLatin1String("threadid"))
439 chunk = QLatin1String("0x") + QString::number(qlonglong(QThread::currentThread()->currentThread()), 16);
440
441 // We simply replace the double formatting marker (%) with one
442 else if (command == QString(formattingMarker))
443 chunk = QLatin1Char(formattingMarker);
444
445 // Do not process any unknown commands
446 else
447 {
448 chunk = QString(formattingMarker);
449 chunk.append(command);
450 }
451
452 result.append(QString(QLatin1String("%1")).arg(chunk, fieldWidth));
453 }
454
455 ++i;
456 }
457
458 return result;
459}
diff --git a/rbutil/rbutilqt/logger/src/ConsoleAppender.cpp b/rbutil/rbutilqt/logger/src/ConsoleAppender.cpp
new file mode 100644
index 0000000000..932ffab787
--- /dev/null
+++ b/rbutil/rbutilqt/logger/src/ConsoleAppender.cpp
@@ -0,0 +1,64 @@
1/*
2 Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License version 2.1
6 as published by the Free Software Foundation and appearing in the file
7 LICENSE.LGPL included in the packaging of this file.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13*/
14// Local
15#include "ConsoleAppender.h"
16
17// STL
18#include <iostream>
19
20
21/**
22 * \class ConsoleAppender
23 *
24 * \brief ConsoleAppender is the simple appender that writes the log records to the std::cerr output stream.
25 *
26 * ConsoleAppender uses "[%{type:-7}] <%{function}> %{message}\n" as a default output format. It is similar to the
27 * AbstractStringAppender but doesn't show a timestamp.
28 *
29 * You can modify ConsoleAppender output format without modifying your code by using \c QT_MESSAGE_PATTERN environment
30 * variable. If you need your application to ignore this environment variable you can call
31 * ConsoleAppender::ignoreEnvironmentPattern(true)
32 */
33
34
35ConsoleAppender::ConsoleAppender()
36 : AbstractStringAppender()
37 , m_ignoreEnvPattern(false)
38{
39 setFormat("[%{type:-7}] <%{function}> %{message}\n");
40}
41
42
43QString ConsoleAppender::format() const
44{
45 const QString envPattern = QString::fromLocal8Bit(qgetenv("QT_MESSAGE_PATTERN"));
46 return (m_ignoreEnvPattern || envPattern.isEmpty()) ? AbstractStringAppender::format() : (envPattern + "\n");
47}
48
49
50void ConsoleAppender::ignoreEnvironmentPattern(bool ignore)
51{
52 m_ignoreEnvPattern = ignore;
53}
54
55
56//! Writes the log record to the std::cerr stream.
57/**
58 * \sa AbstractStringAppender::format()
59 */
60void ConsoleAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
61 const char* function, const QString& category, const QString& message)
62{
63 std::cerr << qPrintable(formattedString(timeStamp, logLevel, file, line, function, category, message));
64}
diff --git a/rbutil/rbutilqt/logger/FileAppender.cpp b/rbutil/rbutilqt/logger/src/FileAppender.cpp
index 3e4d0e22f9..b9018b0324 100644
--- a/rbutil/rbutilqt/logger/FileAppender.cpp
+++ b/rbutil/rbutilqt/logger/src/FileAppender.cpp
@@ -17,7 +17,14 @@
17// STL 17// STL
18#include <iostream> 18#include <iostream>
19 19
20/**
21 * \class FileAppender
22 *
23 * \brief Simple appender that writes the log records to the plain text file.
24 */
20 25
26
27//! Constructs the new file appender assigned to file with the given name.
21FileAppender::FileAppender(const QString& fileName) 28FileAppender::FileAppender(const QString& fileName)
22{ 29{
23 setFileName(fileName); 30 setFileName(fileName);
@@ -30,6 +37,10 @@ FileAppender::~FileAppender()
30} 37}
31 38
32 39
40//! Returns the name set by setFileName() or to the FileAppender constructor.
41/**
42 * \sa setFileName()
43 */
33QString FileAppender::fileName() const 44QString FileAppender::fileName() const
34{ 45{
35 QMutexLocker locker(&m_logFileMutex); 46 QMutexLocker locker(&m_logFileMutex);
@@ -37,8 +48,15 @@ QString FileAppender::fileName() const
37} 48}
38 49
39 50
51//! Sets the name of the file. The name can have no path, a relative path, or an absolute path.
52/**
53 * \sa fileName()
54 */
40void FileAppender::setFileName(const QString& s) 55void FileAppender::setFileName(const QString& s)
41{ 56{
57 if (s.isEmpty())
58 std::cerr << "<FileAppender::FileAppender> File name is empty. The appender will do nothing" << std::endl;
59
42 QMutexLocker locker(&m_logFileMutex); 60 QMutexLocker locker(&m_logFileMutex);
43 if (m_logFile.isOpen()) 61 if (m_logFile.isOpen())
44 m_logFile.close(); 62 m_logFile.close();
@@ -47,37 +65,50 @@ void FileAppender::setFileName(const QString& s)
47} 65}
48 66
49 67
68bool FileAppender::reopenFile()
69{
70 closeFile();
71 return openFile();
72}
73
74
50bool FileAppender::openFile() 75bool FileAppender::openFile()
51{ 76{
52 bool isOpen = false; 77 if (m_logFile.fileName().isEmpty())
53 if (!m_logFile.isOpen()) 78 return false;
79
80 bool isOpen = m_logFile.isOpen();
81 if (!isOpen)
54 { 82 {
55 if (m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) 83 isOpen = m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
56 { 84 if (isOpen)
57 m_logStream.setDevice(&m_logFile); 85 m_logStream.setDevice(&m_logFile);
58 isOpen = true;
59 }
60 else 86 else
61 {
62 std::cerr << "<FileAppender::append> Cannot open the log file " << qPrintable(m_logFile.fileName()) << std::endl; 87 std::cerr << "<FileAppender::append> Cannot open the log file " << qPrintable(m_logFile.fileName()) << std::endl;
63 }
64 } 88 }
65 return isOpen; 89 return isOpen;
66} 90}
67 91
68 92
93//! Write the log record to the file.
94/**
95 * \sa fileName()
96 * \sa AbstractStringAppender::format()
97 */
69void FileAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, 98void FileAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
70 const char* function, const QString& message) 99 const char* function, const QString& category, const QString& message)
71{ 100{
72 QMutexLocker locker(&m_logFileMutex); 101 QMutexLocker locker(&m_logFileMutex);
73 102
74 openFile(); 103 if (openFile())
75 104 {
76 m_logStream << formattedString(timeStamp, logLevel, file, line, function, message); 105 m_logStream << formattedString(timeStamp, logLevel, file, line, function, category, message);
77 m_logStream.flush(); 106 m_logStream.flush();
78 m_logFile.flush(); 107 m_logFile.flush();
108 }
79} 109}
80 110
111
81void FileAppender::closeFile() 112void FileAppender::closeFile()
82{ 113{
83 QMutexLocker locker(&m_logFileMutex); 114 QMutexLocker locker(&m_logFileMutex);
diff --git a/rbutil/rbutilqt/logger/src/Logger.cpp b/rbutil/rbutilqt/logger/src/Logger.cpp
new file mode 100644
index 0000000000..16a18db728
--- /dev/null
+++ b/rbutil/rbutilqt/logger/src/Logger.cpp
@@ -0,0 +1,1099 @@
1/*
2 Copyright (c) 2012 Boris Moiseev (cyberbobs at gmail dot com)
3
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU Lesser General Public License version 2.1
6 as published by the Free Software Foundation and appearing in the file
7 LICENSE.LGPL included in the packaging of this file.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU Lesser General Public License for more details.
13*/
14// Local
15#include "Logger.h"
16#include "AbstractAppender.h"
17#include "AbstractStringAppender.h"
18
19// Qt
20#include <QCoreApplication>
21#include <QReadWriteLock>
22#include <QSemaphore>
23#include <QDateTime>
24#include <QIODevice>
25#include <QTextCodec>
26
27#if defined(Q_OS_ANDROID)
28# include <android/log.h>
29# include <AndroidAppender.h>
30#endif
31
32// STL
33#include <iostream>
34
35
36/**
37 * \file Logger.h
38 * \brief A file containing the description of Logger class and and additional useful macros for logging
39 */
40
41
42/**
43 * \mainpage
44 *
45 * Logger is a simple way to write the history of your application lifecycle to any target logging device (which is
46 * called Appender and may write to any target you will implement with it: console, text file, XML or something - you
47 * choose) and to map logging message to a class, function, source file and line of code which it is called from.
48 *
49 * Some simple appenders (which may be considered an examples) are provided with the Logger itself: see ConsoleAppender
50 * and FileAppender documentation.
51 *
52 * It supports using it in a multithreaded applications, so all of its functions are thread safe.
53 *
54 * Simple usage example:
55 * \code
56 * #include <QCoreApplication>
57 *
58 * #include <Logger.h>
59 * #include <ConsoleAppender.h>
60 *
61 * int main(int argc, char* argv[])
62 * {
63 * QCoreApplication app(argc, argv);
64 * ...
65 * ConsoleAppender* consoleAppender = new ConsoleAppender;
66 * consoleAppender->setFormat("[%{type:-7}] <%{Function}> %{message}\n");
67 * cuteLogger->registerAppender(consoleAppender);
68 * ...
69 * LOG_INFO("Starting the application");
70 * int result = app.exec();
71 * ...
72 * if (result)
73 * LOG_WARNING() << "Something went wrong." << "Result code is" << result;
74 *
75 * return result;
76 * }
77 * \endcode
78 *
79 * Logger internally uses the lazy-initialized singleton object and needs no definite initialization, but you may
80 * consider registering a log appender before calling any log recording functions or macros.
81 *
82 * The library design of Logger allows you to simply mass-replace all occurrences of qDebug and similar calls with
83 * similar Logger macros (e.g. LOG_DEBUG())
84 *
85 * \note Logger uses a singleton global instance which lives through all the application life cycle and self-destroys
86 * destruction of the QCoreApplication (or QApplication) instance. It needs a QCoreApplication instance to be
87 * created before any of the Logger's functions are called.
88 *
89 * \sa cuteLogger
90 * \sa LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_FATAL
91 * \sa LOG_CTRACE, LOG_CDEBUG, LOG_CINFO, LOG_CWARNING, LOG_CERROR, LOG_CFATAL
92 * \sa LOG_ASSERT
93 * \sa LOG_TRACE_TIME, LOG_DEBUG_TIME, LOG_INFO_TIME
94 * \sa AbstractAppender
95 */
96
97
98/**
99 * \def cuteLogger
100 *
101 * \brief Macro returning the current instance of Logger object
102 *
103 * If you haven't created a local Logger object it returns the same value as the Logger::globalInstance() functions.
104 * This macro is a recommended way to get an access to the Logger instance used in current class.
105 *
106 * Example:
107 * \code
108 * ConsoleAppender* consoleAppender = new ConsoleAppender;
109 * cuteLogger->registerAppender(consoleAppender);
110 * \endcode
111 *
112 * \sa Logger::globalInstance()
113 */
114
115
116/**
117 * \def LOG_TRACE
118 *
119 * \brief Writes the trace log record
120 *
121 * This macro is the convinient way to call Logger::write(). It uses the common preprocessor macros \c __FILE__,
122 * \c __LINE__ and the standart Qt \c Q_FUNC_INFO macros to automatically determine the needed parameters to call
123 * Logger::write().
124 *
125 * \note This and other (LOG_INFO() etc...) macros uses the variadic macro arguments to give convinient usage form for
126 * the different versions of Logger::write() (using the QString or const char* argument or returning the QDebug class
127 * instance). Not all compilers will support this. Please, consider reviewing your compiler documentation to ensure
128 * it support __VA_ARGS__ macro.
129 *
130 * \sa Logger::LogLevel
131 * \sa Logger::write()
132 */
133
134
135/**
136 * \def LOG_DEBUG
137 *
138 * \brief Writes the debug log record
139 *
140 * This macro records the debug log record using the Logger::write() function. It works similar to the LOG_TRACE()
141 * macro.
142 *
143 * \sa LOG_TRACE()
144 * \sa Logger::LogLevel
145 * \sa Logger::write()
146 */
147
148
149/**
150 * \def LOG_INFO
151 *
152 * \brief Writes the info log record
153 *
154 * This macro records the info log record using the Logger::write() function. It works similar to the LOG_TRACE()
155 * macro.
156 *
157 * \sa LOG_TRACE()
158 * \sa Logger::LogLevel
159 * \sa Logger::write()
160 */
161
162
163/**
164 * \def LOG_WARNING
165 *
166 * \brief Write the warning log record
167 *
168 * This macro records the warning log record using the Logger::write() function. It works similar to the LOG_TRACE()
169 * macro.
170 *
171 * \sa LOG_TRACE()
172 * \sa Logger::LogLevel
173 * \sa Logger::write()
174 */
175
176
177/**
178 * \def LOG_ERROR
179 *
180 * \brief Write the error log record
181 * This macro records the error log record using the Logger::write() function. It works similar to the LOG_TRACE()
182 * macro.
183 *
184 * \sa LOG_TRACE()
185 * \sa Logger::LogLevel
186 * \sa Logger::write()
187 */
188
189
190/**
191 * \def LOG_FATAL
192 *
193 * \brief Write the fatal log record
194 *
195 * This macro records the fatal log record using the Logger::write() function. It works similar to the LOG_TRACE()
196 * macro.
197 *
198 * \note Recording of the log record using the Logger::Fatal log level will lead to calling the STL abort()
199 * function, which will interrupt the running of your software and begin the writing of the core dump.
200 *
201 * \sa LOG_TRACE()
202 * \sa Logger::LogLevel
203 * \sa Logger::write()
204 */
205
206
207/**
208 * \def LOG_CTRACE(category)
209 *
210 * \brief Writes the trace log record to the specific category
211 *
212 * This macro is the similar to the LOG_TRACE() macro, but has a category parameter
213 * to write only to the category appenders (registered using Logger::registerCategoryAppender() method).
214 *
215 * \param category category name string
216 *
217 * \sa LOG_TRACE()
218 * \sa Logger::LogLevel
219 * \sa Logger::registerCategoryAppender()
220 * \sa Logger::write()
221 * \sa LOG_CATEGORY(), LOG_GLOBAL_CATEGORY()
222 */
223
224
225/**
226 * \def LOG_CDEBUG
227 *
228 * \brief Writes the debug log record to the specific category
229 *
230 * This macro records the debug log record using the Logger::write() function. It works similar to the LOG_CTRACE()
231 * macro.
232 *
233 * \sa LOG_CTRACE()
234 */
235
236
237/**
238 * \def LOG_CINFO
239 *
240 * \brief Writes the info log record to the specific category
241 *
242 * This macro records the info log record using the Logger::write() function. It works similar to the LOG_CTRACE()
243 * macro.
244 *
245 * \sa LOG_CTRACE()
246 */
247
248
249/**
250 * \def LOG_CWARNING
251 *
252 * \brief Writes the warning log record to the specific category
253 *
254 * This macro records the warning log record using the Logger::write() function. It works similar to the LOG_CTRACE()
255 * macro.
256 *
257 * \sa LOG_CTRACE()
258 */
259
260
261/**
262 * \def LOG_CERROR
263 *
264 * \brief Writes the error log record to the specific category
265 *
266 * This macro records the error log record using the Logger::write() function. It works similar to the LOG_CTRACE()
267 * macro.
268 *
269 * \sa LOG_CTRACE()
270 */
271
272
273/**
274 * \def LOG_CFATAL
275 *
276 * \brief Write the fatal log record to the specific category
277 *
278 * This macro records the fatal log record using the Logger::write() function. It works similar to the LOG_CTRACE()
279 * macro.
280 *
281 * \note Recording of the log record using the Logger::Fatal log level will lead to calling the STL abort()
282 * function, which will interrupt the running of your software and begin the writing of the core dump.
283 *
284 * \sa LOG_CTRACE()
285 */
286
287
288/**
289 * \def LOG_CATEGORY(category)
290 *
291 * \brief Create logger instance inside your custom class to log all messages to the specified category
292 *
293 * This macro is used to pass all log messages inside your custom class to the specific category.
294 * You must include this macro inside your class declaration (similarly to the Q_OBJECT macro).
295 * Internally, this macro redefines cuteLoggerInstance() function, creates the local Logger object inside your class and
296 * sets the default category to the specified parameter.
297 *
298 * Thus, any call to cuteLoggerInstance() (for example, inside LOG_TRACE() macro) will return the local Logger object,
299 * so any logging message will be directed to the default category.
300 *
301 * \note This macro does not register any appender to the newly created logger instance. You should register
302 * logger appenders manually, inside your class.
303 *
304 * Usage example:
305 * \code
306 * class CustomClass : public QObject
307 * {
308 * Q_OBJECT
309 * LOG_CATEGORY("custom_category")
310 * ...
311 * };
312 *
313 * CustomClass::CustomClass(QObject* parent) : QObject(parent)
314 * {
315 * cuteLogger->registerAppender(new FileAppender("custom_category_log"));
316 * LOG_TRACE() << "Trace to the custom category log";
317 * }
318 * \endcode
319 *
320 * If used compiler supports C++11 standard, LOG_CATEGORY and LOG_GLOBAL_CATEGORY macros would also work when added
321 * inside of any scope. It could be useful, for example, to log every single run of a method to a different file.
322 *
323 * \code
324 * void foo()
325 * {
326 * QString categoryName = QDateTime::currentDateTime().toString("yyyy-MM-ddThh-mm-ss-zzz");
327 * LOG_CATEGORY(categoryName);
328 * cuteLogger->registerAppender(new FileAppender(categoryName + ".log"));
329 * ...
330 * }
331 * \endcode
332 *
333 * \sa Logger::write()
334 * \sa LOG_TRACE
335 * \sa Logger::registerCategoryAppender()
336 * \sa Logger::setDefaultCategory()
337 * \sa LOG_GLOBAL_CATEGORY
338 */
339
340
341/**
342 * \def LOG_GLOBAL_CATEGORY(category)
343 *
344 * \brief Create logger instance inside your custom class to log all messages both to the specified category and to
345 * the global logger instance.
346 *
347 * This macro is similar to LOG_CATEGORY(), but also passes all log messages to the global logger instance appenders.
348 * It is equal to defining the local category logger using LOG_CATEGORY macro and calling:
349 * \code cuteLogger->logToGlobalInstance(cuteLogger->defaultCategory(), true); \endcode
350 *
351 * \sa LOG_CATEGORY
352 * \sa Logger::logToGlobalInstance()
353 * \sa Logger::defaultCategory()
354 * \sa Logger::registerCategoryAppender()
355 * \sa Logger::write()
356 */
357
358
359
360/**
361 * \def LOG_ASSERT
362 *
363 * \brief Check the assertion
364 *
365 * This macro is a convinient and recommended to use way to call Logger::writeAssert() function. It uses the
366 * preprocessor macros (as the LOG_DEBUG() does) to fill the necessary arguments of the Logger::writeAssert() call. It
367 * also uses undocumented but rather mature and stable \c qt_noop() function (which does nothing) when the assertion
368 * is true.
369 *
370 * Example:
371 * \code
372 * bool b = checkSomething();
373 * ...
374 * LOG_ASSERT(b == true);
375 * \endcode
376 *
377 * \sa Logger::writeAssert()
378 */
379
380
381/**
382 * \def LOG_TRACE_TIME
383 *
384 * \brief Logs the processing time of current function / code block
385 *
386 * This macro automagically measures the function or code of block execution time and outputs it as a Logger::Trace
387 * level log record.
388 *
389 * Example:
390 * \code
391 * int foo()
392 * {
393 * LOG_TRACE_TIME();
394 * ... // Do some long operations
395 * return 0;
396 * } // Outputs: Function foo finished in <time> ms.
397 * \endcode
398 *
399 * If you are measuring a code of block execution time you may also add a name of block to the macro:
400 * \code
401 * int bar(bool doFoo)
402 * {
403 * LOG_TRACE_TIME();
404 *
405 * if (doFoo)
406 * {
407 * LOG_TRACE_TIME("Foo");
408 * ...
409 * }
410 *
411 * ...
412 * }
413 * // Outputs:
414 * // "Foo" finished in <time1> ms.
415 * // Function bar finished in <time2> ms.
416 * \endcode
417 *
418 * \note Macro switches to logging the seconds instead of milliseconds when the execution time reaches 10000 ms.
419 * \sa LOG_DEBUG_TIME, LOG_INFO_TIME
420 */
421
422
423/**
424 * \def LOG_DEBUG_TIME
425 *
426 * \brief Logs the processing time of current function / code block
427 *
428 * This macro automagically measures the function or code of block execution time and outputs it as a Logger::Debug
429 * level log record. It works similar to LOG_TRACE_TIME() macro.
430 *
431 * \sa LOG_TRACE_TIME
432 */
433
434
435/**
436 * \def LOG_INFO_TIME
437 *
438 * \brief Logs the processing time of current function / code block
439 *
440 * This macro automagically measures the function or code of block execution time and outputs it as a Logger::Info
441 * level log record. It works similar to LOG_TRACE_TIME() macro.
442 *
443 * \sa LOG_TRACE_TIME
444 */
445
446
447/**
448 * \class Logger
449 *
450 * \brief Very simple but rather powerful component which may be used for logging your application activities.
451 *
452 * Global logger instance created on a first access to it (e.g. registering appenders, calling a LOG_DEBUG() macro
453 * etc.) registers itself as a Qt default message handler and captures all the qDebug/qWarning/qCritical output.
454 *
455 * \note Qt 4 qDebug set of macro doesn't support capturing source function name, file name or line number so we
456 * recommend to use LOG_DEBUG() and other Logger macros instead.
457 *
458 * \sa cuteLogger
459 * \sa [CuteLogger Documentation](index.html)
460 */
461
462// Forward declarations
463static void cleanupLoggerGlobalInstance();
464
465#if QT_VERSION >= 0x050000
466static void qtLoggerMessageHandler(QtMsgType, const QMessageLogContext& context, const QString& msg);
467#else
468static void qtLoggerMessageHandler(QtMsgType type, const char* msg);
469#endif
470
471/**
472 * \internal
473 *
474 * LoggerPrivate class implements the Singleton pattern in a thread-safe way. It contains a static pointer to the
475 * global logger instance protected by QReadWriteLock
476 */
477class LoggerPrivate
478{
479 public:
480 static Logger* globalInstance;
481 static QReadWriteLock globalInstanceLock;
482
483 QList<AbstractAppender*> appenders;
484 QMutex loggerMutex;
485
486 QMap<QString, bool> categories;
487 QMultiMap<QString, AbstractAppender*> categoryAppenders;
488 QStringList noAppendersCategories; //<! Categories without appenders that was already warned about
489 QString defaultCategory;
490 bool writeDefaultCategoryToGlobalInstance;
491};
492
493
494// Static fields initialization
495Logger* LoggerPrivate::globalInstance = nullptr;
496QReadWriteLock LoggerPrivate::globalInstanceLock;
497
498
499static void cleanupLoggerGlobalInstance()
500{
501 QWriteLocker locker(&LoggerPrivate::globalInstanceLock);
502
503 delete LoggerPrivate::globalInstance;
504 LoggerPrivate::globalInstance = nullptr;
505}
506
507
508#if QT_VERSION >= 0x050000
509static void qtLoggerMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
510{
511 Logger::LogLevel level = Logger::Debug;
512 switch (type)
513 {
514 case QtDebugMsg:
515 level = Logger::Debug;
516 break;
517#if QT_VERSION >= 0x050500
518 case QtInfoMsg:
519 level = Logger::Info;
520 break;
521#endif
522 case QtWarningMsg:
523 level = Logger::Warning;
524 break;
525 case QtCriticalMsg:
526 level = Logger::Error;
527 break;
528 case QtFatalMsg:
529 level = Logger::Fatal;
530 break;
531 }
532
533 bool isDefaultCategory = QString::fromLatin1(context.category) == "default";
534 Logger::globalInstance()->write(level, context.file, context.line, context.function, isDefaultCategory ? nullptr : context.category, msg);
535}
536
537#else
538
539static void qtLoggerMessageHandler(QtMsgType type, const char* msg)
540{
541 switch (type)
542 {
543 case QtDebugMsg:
544 cuteLoggerInstance()->write(Logger::Debug, "", 0, "qDebug", 0, msg);
545 break;
546 case QtWarningMsg:
547 cuteLoggerInstance()->write(Logger::Warning, "", 0, "qDebug", 0, msg);
548 break;
549 case QtCriticalMsg:
550 cuteLoggerInstance()->write(Logger::Error, "", 0, "qDebug", 0, msg);
551 break;
552 case QtFatalMsg:
553 cuteLoggerInstance()->write(Logger::Fatal, "", 0, "qDebug", 0, msg);
554 break;
555 }
556}
557#endif
558
559
560//! Construct the instance of Logger
561/**
562 * If you're only using one global instance of logger you wouldn't probably need to use this constructor manually.
563 * Consider using [cuteLogger](@ref cuteLogger) macro instead to access the logger instance
564 */
565Logger::Logger()
566 : d_ptr(new LoggerPrivate)
567{
568 Q_D(Logger);
569 d->writeDefaultCategoryToGlobalInstance = false;
570}
571
572
573//! Construct the instance of Logger and set logger default category
574/**
575 * If you're only using one global instance of logger you wouldn't probably need to use this constructor manually.
576 * Consider using [cuteLogger](@ref cuteLogger) macro instead to access the logger instance and call
577 * [setDefaultCategory](@ref setDefaultCategory) method.
578 *
579 * \sa Logger()
580 * \sa setDefaultCategory()
581 */
582Logger::Logger(const QString& defaultCategory, bool writeToGlobalInstance)
583 : d_ptr(new LoggerPrivate)
584{
585 Q_D(Logger);
586 d->writeDefaultCategoryToGlobalInstance = writeToGlobalInstance;
587
588 setDefaultCategory(defaultCategory);
589}
590
591
592//! Destroy the instance of Logger
593/**
594 * You probably wouldn't need to use this function directly. Global instance of logger will be destroyed automatically
595 * at the end of your QCoreApplication execution
596 */
597Logger::~Logger()
598{
599 Q_D(Logger);
600
601 // Cleanup appenders
602 QMutexLocker appendersLocker(&d->loggerMutex);
603 QSet<AbstractAppender*> deleteList(QSet<AbstractAppender*>::fromList(d->appenders));
604 deleteList.unite(QSet<AbstractAppender*>::fromList(d->categoryAppenders.values()));
605 qDeleteAll(deleteList);
606
607 appendersLocker.unlock();
608
609 delete d_ptr;
610}
611
612
613//! Converts the LogLevel enum value to its string representation
614/**
615 * \param logLevel Log level to convert
616 *
617 * \sa LogLevel
618 * \sa levelFromString()
619 */
620QString Logger::levelToString(Logger::LogLevel logLevel)
621{
622 switch (logLevel)
623 {
624 case Trace:
625 return QLatin1String("Trace");
626 case Debug:
627 return QLatin1String("Debug");
628 case Info:
629 return QLatin1String("Info");
630 case Warning:
631 return QLatin1String("Warning");
632 case Error:
633 return QLatin1String("Error");
634 case Fatal:
635 return QLatin1String("Fatal");
636 }
637
638 return QString();
639}
640
641
642//! Converts the LogLevel string representation to enum value
643/**
644 * Comparation of the strings is case independent. If the log level string provided cannot be understood
645 * Logger::Debug is returned.
646 *
647 * \param s String to be decoded
648 *
649 * \sa LogLevel
650 * \sa levelToString()
651 */
652Logger::LogLevel Logger::levelFromString(const QString& s)
653{
654 QString str = s.trimmed().toLower();
655
656 LogLevel result = Debug;
657
658 if (str == QLatin1String("trace"))
659 result = Trace;
660 else if (str == QLatin1String("debug"))
661 result = Debug;
662 else if (str == QLatin1String("info"))
663 result = Info;
664 else if (str == QLatin1String("warning"))
665 result = Warning;
666 else if (str == QLatin1String("error"))
667 result = Error;
668 else if (str == QLatin1String("fatal"))
669 result = Fatal;
670
671 return result;
672}
673
674
675//! Returns the global instance of Logger
676/**
677 * In a most cases you shouldn't use this function directly. Consider using [cuteLogger](@ref cuteLogger) macro instead.
678 *
679 * \sa cuteLogger
680 */
681Logger* Logger::globalInstance()
682{
683 Logger* result = nullptr;
684 {
685 QReadLocker locker(&LoggerPrivate::globalInstanceLock);
686 result = LoggerPrivate::globalInstance;
687 }
688
689 if (!result)
690 {
691 QWriteLocker locker(&LoggerPrivate::globalInstanceLock);
692 LoggerPrivate::globalInstance = new Logger;
693
694#if QT_VERSION >= 0x050000
695 qInstallMessageHandler(qtLoggerMessageHandler);
696#else
697 qInstallMsgHandler(qtLoggerMessageHandler);
698#endif
699 qAddPostRoutine(cleanupLoggerGlobalInstance);
700 result = LoggerPrivate::globalInstance;
701 }
702
703 return result;
704}
705
706
707//! Registers the appender to write the log records to
708/**
709 * On the log writing call (using one of the macros or the write() function) Logger traverses through the list of
710 * the appenders and writes a log records to the each of them. Please, look through the AbstractAppender
711 * documentation to understand the concept of appenders.
712 *
713 * If no appenders was added to Logger, it falls back to logging into the \c std::cerr STL stream.
714 *
715 * \param appender Appender to register in the Logger
716 *
717 * \note Logger takes ownership on the appender and it will delete it on the application exit. According to this,
718 * appenders must be created on heap to prevent double destruction of the appender.
719 *
720 * \sa registerCategoryAppender
721 * \sa AbstractAppender
722 */
723void Logger::registerAppender(AbstractAppender* appender)
724{
725 Q_D(Logger);
726
727 QMutexLocker locker(&d->loggerMutex);
728
729 if (!d->appenders.contains(appender))
730 d->appenders.append(appender);
731 else
732 std::cerr << "Trying to register appender that was already registered" << std::endl;
733}
734
735//! Registers the appender to write the log records to the specific category
736/**
737 * Calling this method, you can link some appender with the named category.
738 * On the log writing call to the specific category (calling write() with category parameter directly,
739 * writing to the default category, or using special LOG_CDEBUG(), LOG_CWARNING() etc. macros),
740 * Logger writes the log message only to the list of registered category appenders.
741 *
742 * You can call logToGlobalInstance() to pass all category log messages to the global logger instance appenders
743 * (registered using registerAppender()).
744 * If no category appenders with specific name was registered to the Logger,
745 * it falls back to logging into the \c std::cerr STL stream, both with simple warning message.
746 *
747 * \param category Category name
748 * \param appender Appender to register in the Logger
749 *
750 * \note Logger takes ownership on the appender and it will delete it on the application exit. According to this,
751 * appenders must be created on heap to prevent double destruction of the appender.
752 *
753 * \sa registerAppender
754 * \sa LOG_CTRACE(), LOG_CDEBUG(), LOG_CINFO(), LOG_CWARNING(), LOG_CERROR(), LOG_CFATAL()
755 * \sa LOG_CATEGORY(), LOG_GLOBAL_CATEGORY()
756 * \sa logToGlobalInstance
757 * \sa setDefaultCategory
758 */
759void Logger::registerCategoryAppender(const QString& category, AbstractAppender* appender)
760{
761 Q_D(Logger);
762
763 QMutexLocker locker(&d->loggerMutex);
764
765 if (!d->categoryAppenders.values().contains(appender))
766 d->categoryAppenders.insert(category, appender);
767 else
768 std::cerr << "Trying to register appender that was already registered" << std::endl;
769}
770
771
772//! Removes the registered appender from logger
773/**
774 * After calling this function logger stops writing any of the records to the appender.
775 *
776 * \param appender Pointer to appender to remove from logger
777 * \note Removed appender will not be deleted on the application shutdown and you will need to destroy the object
778 * yourself.
779 * \sa registerAppender
780 */
781void Logger::removeAppender(AbstractAppender* appender)
782{
783 Q_D(Logger);
784
785 QMutexLocker locker(&d->loggerMutex);
786
787 d->appenders.removeAll(appender);
788 for (QMultiMap<QString,AbstractAppender*>::iterator it = d->categoryAppenders.begin(); it != d->categoryAppenders.end();)
789 {
790 if (it.value() == appender)
791 it = d->categoryAppenders.erase(it);
792 else
793 ++it;
794 }
795}
796
797
798//! Sets default logging category
799/**
800 * All log messages to this category appenders will also be written to general logger instance appenders (registered
801 * using [registerAppender](@ref registerAppender) method), and vice versa.
802 * In particular, any calls to the LOG_DEBUG() macro will be treated as category logging,
803 * so you needn't to specify category name using LOG_CDEBUG() macro.
804 *
805 * To unset the default category, pass a null string as a parameter.
806 *
807 * \param category Category name
808 *
809 * \note "category" format marker will be set to the category name for all of these messages
810 * (see [AbstractStringAppender::setFormat](@ref AbstractStringAppender::setFormat)).
811 *
812 * \sa defaultCategory()
813 * \sa registerCategoryAppender()
814 * \sa logToGlobalInstance()
815 */
816void Logger::setDefaultCategory(const QString& category)
817{
818 Q_D(Logger);
819
820 QMutexLocker locker(&d->loggerMutex);
821
822 d->defaultCategory = category;
823}
824
825//! Returns default logging category name
826/**
827 * \sa setDefaultCategory
828 */
829QString Logger::defaultCategory() const
830{
831 Q_D(const Logger);
832 return d->defaultCategory;
833}
834
835//! Links some logging category with the global logger instance appenders.
836/**
837 * If set to true, all log messages to the specified category appenders will also be written to the global logger instance appenders,
838 * registered using registerAppender().
839 *
840 * By default, all messages to the specific category are written only to the specific category appenders
841 * (registered using registerCategoryAppender()).
842 *
843 * \param category Category name
844 * \param logToGlobal Link or onlink the category from global logger instance appender
845 *
846 * \sa globalInstance
847 * \sa registerAppender
848 * \sa registerCategoryAppender
849 */
850void Logger::logToGlobalInstance(const QString& category, bool logToGlobal)
851{
852 Q_D(Logger);
853
854 if (this == globalInstance())
855 {
856 QMutexLocker locker(&d->loggerMutex);
857 d->categories.insert(category, logToGlobal);
858 }
859 else
860 {
861 globalInstance()->logToGlobalInstance(category, logToGlobal);
862 }
863}
864
865
866void Logger::write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function, const char* category,
867 const QString& message, bool fromLocalInstance)
868{
869 Q_D(Logger);
870
871 QMutexLocker locker(&d->loggerMutex);
872
873 QString logCategory = QString::fromLatin1(category);
874 if (logCategory.isNull() && !d->defaultCategory.isNull())
875 logCategory = d->defaultCategory;
876
877 bool wasWritten = false;
878 bool isGlobalInstance = this == globalInstance();
879 bool linkedToGlobal = isGlobalInstance && d->categories.value(logCategory, false);
880
881 if (!logCategory.isNull())
882 {
883 QList<AbstractAppender*> appenders = d->categoryAppenders.values(logCategory);
884 if (appenders.length() == 0)
885 {
886 if (logCategory != d->defaultCategory && !linkedToGlobal && !fromLocalInstance && !d->noAppendersCategories.contains(logCategory))
887 {
888 std::cerr << "No appenders associated with category " << qPrintable(logCategory) << std::endl;
889 d->noAppendersCategories.append(logCategory);
890 }
891 }
892 else
893 {
894 foreach (AbstractAppender* appender, appenders)
895 appender->write(timeStamp, logLevel, file, line, function, logCategory, message);
896 wasWritten = true;
897 }
898 }
899
900 // the default category is linked to the main logger appenders
901 // global logger instance also writes all linked categories to the main appenders
902 if (logCategory.isNull() || logCategory == d->defaultCategory || linkedToGlobal)
903 {
904 if (!d->appenders.isEmpty())
905 {
906 foreach (AbstractAppender* appender, d->appenders)
907 appender->write(timeStamp, logLevel, file, line, function, logCategory, message);
908 wasWritten = true;
909 }
910 else
911 {
912 static bool noAppendersWarningShown = false;
913 if (!noAppendersWarningShown)
914 {
915#if defined(Q_OS_ANDROID)
916 __android_log_write(ANDROID_LOG_WARN, "Logger", "No appenders registered with logger");
917#else
918 std::cerr << "No appenders registered with logger" << std::endl;
919#endif
920 noAppendersWarningShown = true;
921 }
922 }
923 }
924
925 // local logger instances send category messages to the global instance
926 if (!isGlobalInstance)
927 {
928 if (!logCategory.isNull())
929 {
930 globalInstance()->write(timeStamp, logLevel, file, line, function, logCategory.toLatin1(), message, true);
931 wasWritten = true;
932 }
933
934 if (d->writeDefaultCategoryToGlobalInstance && logCategory == d->defaultCategory)
935 {
936 globalInstance()->write(timeStamp, logLevel, file, line, function, nullptr, message, true);
937 wasWritten = true;
938 }
939 }
940
941 if (!wasWritten && !fromLocalInstance)
942 {
943 // Fallback
944#if defined(Q_OS_ANDROID)
945 QString result = QString(QLatin1String("<%2> %3")).arg(AbstractStringAppender::stripFunctionName(function)).arg(message);
946 __android_log_write(AndroidAppender::androidLogPriority(logLevel), "Logger", qPrintable(result));
947#else
948 QString result = QString(QLatin1String("[%1] <%2> %3")).arg(levelToString(logLevel), -7)
949 .arg(AbstractStringAppender::stripFunctionName(function)).arg(message);
950 std::cerr << qPrintable(result) << std::endl;
951#endif
952 }
953
954 if (logLevel == Logger::Fatal)
955 abort();
956}
957
958
959//! Writes the log record
960/**
961 * Writes the log records with the supplied arguments to all the registered appenders.
962 *
963 * \note It is not recommended to call this function directly. Instead of this you can just call one of the macros
964 * (LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_FATAL) that will supply all the needed
965 * information to this function.
966 *
967 * \param timeStamp - the time stamp of the record
968 * \param logLevel - the log level of the record
969 * \param file - the name of the source file that requested the log record
970 * \param line - the line of the code of source file that requested the log record
971 * \param function - name of the function that requested the log record
972 * \param category - logging category (0 for default category)
973 * \param message - log message
974 *
975 * \note Recording of the log record using the Logger::Fatal log level will lead to calling the STL abort()
976 * function, which will interrupt the running of your software and begin the writing of the core dump.
977 *
978 * \sa LogLevel
979 * \sa LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_FATAL
980 * \sa AbstractAppender
981 */
982void Logger::write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function, const char* category,
983 const QString& message)
984{
985 write(timeStamp, logLevel, file, line, function, category, message, /* fromLocalInstance = */ false);
986}
987
988/**
989 * This is the overloaded function provided for the convinience. It behaves similar to the above function.
990 *
991 * This function uses the current timestamp obtained with \c QDateTime::currentDateTime().
992 *
993 * \sa write()
994 */
995void Logger::write(LogLevel logLevel, const char* file, int line, const char* function, const char* category,
996 const QString& message)
997{
998 write(QDateTime::currentDateTime(), logLevel, file, line, function, category, message);
999}
1000
1001
1002//! Writes the assertion
1003/**
1004 * This function writes the assertion record using the write() function.
1005 *
1006 * The assertion record is always written using the Logger::Fatal log level which leads to the abortation of the
1007 * program and generation of the core dump (if supported).
1008 *
1009 * The message written to the appenders will be identical to the \c condition argument prefixed with the
1010 * <tt>ASSERT:</tt> notification.
1011 *
1012 * \note It is not recommended to call this function directly. Instead of this you can just call the LOG_ASSERT
1013 * macro that will supply all the needed information to this function.
1014 *
1015 * \sa LOG_ASSERT
1016 * \sa write()
1017 */
1018void Logger::writeAssert(const char* file, int line, const char* function, const char* condition)
1019{
1020 write(Logger::Fatal, file, line, function, nullptr, QString("ASSERT: \"%1\"").arg(condition));
1021}
1022
1023
1024Logger* cuteLoggerInstance()
1025{
1026 return Logger::globalInstance();
1027}
1028
1029
1030
1031void LoggerTimingHelper::start(const char* msg, ...)
1032{
1033 va_list va;
1034 va_start(va, msg);
1035 m_block = QString().vsprintf(msg, va);
1036 va_end(va);
1037
1038 m_time.start();
1039}
1040
1041
1042void LoggerTimingHelper::start(const QString& block)
1043{
1044 m_block = block;
1045 m_time.start();
1046}
1047
1048
1049void LoggerTimingHelper::start(Logger::TimingMode mode, const QString& block)
1050{
1051 m_timingMode = mode;
1052 m_block = block;
1053 m_time.start();
1054}
1055
1056
1057LoggerTimingHelper::~LoggerTimingHelper()
1058{
1059 QString message;
1060 if (m_block.isEmpty())
1061 message = QString(QLatin1String("Function %1 finished in ")).arg(AbstractStringAppender::stripFunctionName(m_function));
1062 else
1063 message = QString(QLatin1String("\"%1\" finished in ")).arg(m_block);
1064
1065 int elapsed = m_time.elapsed();
1066 if (elapsed >= 10000 && m_timingMode == Logger::TimingAuto)
1067 message += QString(QLatin1String("%1 s.")).arg(elapsed / 1000);
1068 else
1069 message += QString(QLatin1String("%1 ms.")).arg(elapsed);
1070
1071 m_logger->write(m_logLevel, m_file, m_line, m_function, nullptr, message);
1072}
1073
1074
1075CuteMessageLogger::~CuteMessageLogger()
1076{
1077 m_l->write(m_level, m_file, m_line, m_function, m_category, m_message);
1078}
1079
1080void CuteMessageLogger::write(const char* msg, ...)
1081{
1082 va_list va;
1083 va_start(va, msg);
1084 m_message = QString::vasprintf(msg, va);
1085 va_end(va);
1086}
1087
1088
1089void CuteMessageLogger::write(const QString& msg)
1090{
1091 m_message = msg;
1092}
1093
1094
1095QDebug CuteMessageLogger::write()
1096{
1097 QDebug d(&m_message);
1098 return d;
1099}
diff --git a/rbutil/rbutilqt/logger/OutputDebugAppender.cpp b/rbutil/rbutilqt/logger/src/OutputDebugAppender.cpp
index 2d7facbd98..44d640512f 100644
--- a/rbutil/rbutilqt/logger/OutputDebugAppender.cpp
+++ b/rbutil/rbutilqt/logger/src/OutputDebugAppender.cpp
@@ -18,14 +18,26 @@
18#include <windows.h> 18#include <windows.h>
19 19
20 20
21/**
22 * \class OutputDebugAppender
23 *
24 * \brief Appender that writes the log records to the Microsoft Debug Log
25 */
26
27
28//! Writes the log record to the windows debug log.
29/**
30 * \sa AbstractStringAppender::format()
31 */
21void OutputDebugAppender::append(const QDateTime& timeStamp, 32void OutputDebugAppender::append(const QDateTime& timeStamp,
22 Logger::LogLevel logLevel, 33 Logger::LogLevel logLevel,
23 const char* file, 34 const char* file,
24 int line, 35 int line,
25 const char* function, 36 const char* function,
37 const QString& category,
26 const QString& message) 38 const QString& message)
27{ 39{
28 QString s = formattedString(timeStamp, logLevel, file, line, function, message); 40 QString s = formattedString(timeStamp, logLevel, file, line, function, category, message);
29 OutputDebugStringW((LPCWSTR) s.utf16()); 41 OutputDebugStringW((LPCWSTR) s.utf16());
30} 42}
31 43
diff --git a/rbutil/rbutilqt/main.cpp b/rbutil/rbutilqt/main.cpp
index cac93395fe..160a31ba0e 100644
--- a/rbutil/rbutilqt/main.cpp
+++ b/rbutil/rbutilqt/main.cpp
@@ -40,14 +40,14 @@ Q_IMPORT_PLUGIN(AccessibleFactory)
40int main( int argc, char ** argv ) { 40int main( int argc, char ** argv ) {
41 QApplication app( argc, argv ); 41 QApplication app( argc, argv );
42 ConsoleAppender* consoleAppender = new ConsoleAppender(); 42 ConsoleAppender* consoleAppender = new ConsoleAppender();
43 consoleAppender->setFormat("[%f:%i %L] %m\n"); 43 consoleAppender->setFormat("[%{file}:%{line} %{type}] %{message}\n");
44 Logger::registerAppender(consoleAppender); 44 cuteLoggerInstance()->registerAppender(consoleAppender);
45 SysTrace::rotateTrace(); 45 SysTrace::rotateTrace();
46 QString tracefile = QDir::tempPath() + "/rbutil-trace.log"; 46 QString tracefile = QDir::tempPath() + "/rbutil-trace.log";
47 FileAppender* fileAppender = new FileAppender(); 47 FileAppender* fileAppender = new FileAppender();
48 fileAppender->setFormat("[%f:%i %L] %m\n"); 48 fileAppender->setFormat("[%{file}:%{line} %{type}] %{message}\n");
49 fileAppender->setFileName(tracefile); 49 fileAppender->setFileName(tracefile);
50 Logger::registerAppender(fileAppender); 50 cuteLoggerInstance()->registerAppender(fileAppender);
51 LOG_INFO() << "Starting trace at" << QDateTime::currentDateTime().toString(Qt::ISODate); 51 LOG_INFO() << "Starting trace at" << QDateTime::currentDateTime().toString(Qt::ISODate);
52 52
53#if defined(Q_OS_MAC) 53#if defined(Q_OS_MAC)
diff --git a/rbutil/rbutilqt/systrace.cpp b/rbutil/rbutilqt/systrace.cpp
index 83f70b3da5..5f3450eae8 100644
--- a/rbutil/rbutilqt/systrace.cpp
+++ b/rbutil/rbutilqt/systrace.cpp
@@ -50,11 +50,11 @@ void SysTrace::refresh(void)
50 QString color; 50 QString color;
51 while(!c.atEnd()) { 51 while(!c.atEnd()) {
52 line = c.readLine(); 52 line = c.readLine();
53 if(line.contains("WARNING")) 53 if(line.contains("Warning"))
54 color = "orange"; 54 color = "orange";
55 else if(line.contains("ERROR")) 55 else if(line.contains("Error"))
56 color = "red"; 56 color = "red";
57 else if(line.contains("DEBUG")) 57 else if(line.contains("Debug"))
58 color = "blue"; 58 color = "blue";
59#if 0 59#if 0
60 else if(line.contains("INFO")) 60 else if(line.contains("INFO"))