summaryrefslogtreecommitdiff
path: root/utils/rbutilqt/logger/src
diff options
context:
space:
mode:
Diffstat (limited to 'utils/rbutilqt/logger/src')
-rw-r--r--utils/rbutilqt/logger/src/AbstractAppender.cpp147
-rw-r--r--utils/rbutilqt/logger/src/AbstractStringAppender.cpp460
-rw-r--r--utils/rbutilqt/logger/src/ConsoleAppender.cpp64
-rw-r--r--utils/rbutilqt/logger/src/FileAppender.cpp116
-rw-r--r--utils/rbutilqt/logger/src/Logger.cpp1108
-rw-r--r--utils/rbutilqt/logger/src/OutputDebugAppender.cpp43
6 files changed, 1938 insertions, 0 deletions
diff --git a/utils/rbutilqt/logger/src/AbstractAppender.cpp b/utils/rbutilqt/logger/src/AbstractAppender.cpp
new file mode 100644
index 0000000000..778bbddd11
--- /dev/null
+++ b/utils/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/utils/rbutilqt/logger/src/AbstractStringAppender.cpp b/utils/rbutilqt/logger/src/AbstractStringAppender.cpp
new file mode 100644
index 0000000000..ea5883f744
--- /dev/null
+++ b/utils/rbutilqt/logger/src/AbstractStringAppender.cpp
@@ -0,0 +1,460 @@
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 <QRegularExpression>
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 QRegularExpression lambdaRegex("::<lambda\\(.*?\\)>");
159 QRegularExpressionMatch match = lambdaRegex.match(QString::fromLatin1(info));
160 int lambdaIndex = match.capturedStart();
161 if (lambdaIndex != -1)
162 {
163 hasLambda = true;
164 info.remove(lambdaIndex, match.capturedLength());
165 }
166
167 // operator names with '(', ')', '<', '>' in it
168 static const char operator_call[] = "operator()";
169 static const char operator_lessThan[] = "operator<";
170 static const char operator_greaterThan[] = "operator>";
171 static const char operator_lessThanEqual[] = "operator<=";
172 static const char operator_greaterThanEqual[] = "operator>=";
173
174 // canonize operator names
175 info.replace("operator ", "operator");
176
177 // remove argument list
178 forever {
179 int parencount = 0;
180 pos = info.lastIndexOf(')');
181 if (pos == -1) {
182 // Don't know how to parse this function name
183 return info;
184 }
185
186 // find the beginning of the argument list
187 --pos;
188 ++parencount;
189 while (pos && parencount) {
190 if (info.at(pos) == ')')
191 ++parencount;
192 else if (info.at(pos) == '(')
193 --parencount;
194 --pos;
195 }
196 if (parencount != 0)
197 return info;
198
199 info.truncate(++pos);
200
201 if (info.at(pos - 1) == ')') {
202 if (info.indexOf(operator_call) == pos - (int)strlen(operator_call))
203 break;
204
205 // this function returns a pointer to a function
206 // and we matched the arguments of the return type's parameter list
207 // try again
208 info.remove(0, info.indexOf('('));
209 info.chop(1);
210 continue;
211 } else {
212 break;
213 }
214 }
215
216 if (hasLambda)
217 info.append("::lambda");
218
219 // find the beginning of the function name
220 int parencount = 0;
221 int templatecount = 0;
222 --pos;
223
224 // make sure special characters in operator names are kept
225 if (pos > -1) {
226 switch (info.at(pos)) {
227 case ')':
228 if (info.indexOf(operator_call) == pos - (int)strlen(operator_call) + 1)
229 pos -= 2;
230 break;
231 case '<':
232 if (info.indexOf(operator_lessThan) == pos - (int)strlen(operator_lessThan) + 1)
233 --pos;
234 break;
235 case '>':
236 if (info.indexOf(operator_greaterThan) == pos - (int)strlen(operator_greaterThan) + 1)
237 --pos;
238 break;
239 case '=': {
240 int operatorLength = (int)strlen(operator_lessThanEqual);
241 if (info.indexOf(operator_lessThanEqual) == pos - operatorLength + 1)
242 pos -= 2;
243 else if (info.indexOf(operator_greaterThanEqual) == pos - operatorLength + 1)
244 pos -= 2;
245 break;
246 }
247 default:
248 break;
249 }
250 }
251
252 while (pos > -1) {
253 if (parencount < 0 || templatecount < 0)
254 return info;
255
256 char c = info.at(pos);
257 if (c == ')')
258 ++parencount;
259 else if (c == '(')
260 --parencount;
261 else if (c == '>')
262 ++templatecount;
263 else if (c == '<')
264 --templatecount;
265 else if (c == ' ' && templatecount == 0 && parencount == 0)
266 break;
267
268 --pos;
269 }
270 info = info.mid(pos + 1);
271
272 // remove trailing '*', '&' that are part of the return argument
273 while ((info.at(0) == '*')
274 || (info.at(0) == '&'))
275 info = info.mid(1);
276
277 // we have the full function name now.
278 // clean up the templates
279 while ((pos = info.lastIndexOf('>')) != -1) {
280 if (!info.contains('<'))
281 break;
282
283 // find the matching close
284 int end = pos;
285 templatecount = 1;
286 --pos;
287 while (pos && templatecount) {
288 char c = info.at(pos);
289 if (c == '>')
290 ++templatecount;
291 else if (c == '<')
292 --templatecount;
293 --pos;
294 }
295 ++pos;
296 info.remove(pos, end - pos + 1);
297 }
298
299 return info;
300}
301
302
303//! Returns the string to record to the logging target, formatted according to the format().
304/**
305 * \sa format()
306 * \sa setFormat(const QString&)
307 */
308QString AbstractStringAppender::formattedString(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file,
309 int line, const char* function, const QString& category, const QString& message) const
310{
311 QString f = format();
312 const int size = f.size();
313
314 QString result;
315
316 int i = 0;
317 while (i < f.size())
318 {
319 QChar c = f.at(i);
320
321 // We will silently ignore the broken % marker at the end of string
322 if (c != QLatin1Char(formattingMarker) || (i + 2) >= size)
323 {
324 result.append(c);
325 }
326 else
327 {
328 i += 2;
329 QChar currentChar = f.at(i);
330 QString command;
331 int fieldWidth = 0;
332
333 if (currentChar.isLetter())
334 {
335 command.append(currentChar);
336 int j = 1;
337 while ((i + j) < size && f.at(i + j).isLetter())
338 {
339 command.append(f.at(i+j));
340 j++;
341 }
342
343 i+=j;
344 currentChar = f.at(i);
345
346 // Check for the padding instruction
347 if (currentChar == QLatin1Char(':'))
348 {
349 currentChar = f.at(++i);
350 if (currentChar.isDigit() || currentChar.category() == QChar::Punctuation_Dash)
351 {
352 int j = 1;
353 while ((i + j) < size && f.at(i + j).isDigit())
354 j++;
355 fieldWidth = f.mid(i, j).toInt();
356
357 i += j;
358 }
359 }
360 }
361
362 // Log record chunk to insert instead of formatting instruction
363 QString chunk;
364
365 // Time stamp
366 if (command == QLatin1String("time"))
367 {
368 if (f.at(i + 1) == QLatin1Char('{'))
369 {
370 int j = 1;
371 while ((i + 2 + j) < size && f.at(i + 2 + j) != QLatin1Char('}'))
372 j++;
373
374 if ((i + 2 + j) < size)
375 {
376 chunk = timeStamp.toString(f.mid(i + 2, j));
377
378 i += j;
379 i += 2;
380 }
381 }
382
383 if (chunk.isNull())
384 chunk = timeStamp.toString(QLatin1String("HH:mm:ss.zzz"));
385 }
386
387 // Log level
388 else if (command == QLatin1String("type"))
389 chunk = Logger::levelToString(logLevel);
390
391 // Uppercased log level
392 else if (command == QLatin1String("Type"))
393 chunk = Logger::levelToString(logLevel).toUpper();
394
395 // One letter log level
396 else if (command == QLatin1String("typeOne"))
397 chunk = Logger::levelToString(logLevel).left(1).toLower();
398
399 // One uppercase letter log level
400 else if (command == QLatin1String("TypeOne"))
401 chunk = Logger::levelToString(logLevel).left(1).toUpper();
402
403 // Filename
404 else if (command == QLatin1String("File"))
405 chunk = QLatin1String(file);
406
407 // Filename without a path
408 else if (command == QLatin1String("file"))
409 chunk = QString(QLatin1String(file)).section(QRegularExpression("[/\\\\]"), -1);
410
411 // Source line number
412 else if (command == QLatin1String("line"))
413 chunk = QString::number(line);
414
415 // Function name, as returned by Q_FUNC_INFO
416 else if (command == QLatin1String("Function"))
417 chunk = QString::fromLatin1(function);
418
419 // Stripped function name
420 else if (command == QLatin1String("function"))
421 chunk = stripFunctionName(function);
422
423 // Log message
424 else if (command == QLatin1String("message"))
425 chunk = message;
426
427 else if (command == QLatin1String("category"))
428 chunk = category;
429
430 // Application pid
431 else if (command == QLatin1String("pid"))
432 chunk = QString::number(QCoreApplication::applicationPid());
433
434 // Appplication name
435 else if (command == QLatin1String("appname"))
436 chunk = QCoreApplication::applicationName();
437
438 // Thread ID (duplicates Qt5 threadid debbuging way)
439 else if (command == QLatin1String("threadid"))
440 chunk = QLatin1String("0x") + QString::number(qlonglong(QThread::currentThread()->currentThread()), 16);
441
442 // We simply replace the double formatting marker (%) with one
443 else if (command == QString(formattingMarker))
444 chunk = QLatin1Char(formattingMarker);
445
446 // Do not process any unknown commands
447 else
448 {
449 chunk = QString(formattingMarker);
450 chunk.append(command);
451 }
452
453 result.append(QString(QLatin1String("%1")).arg(chunk, fieldWidth));
454 }
455
456 ++i;
457 }
458
459 return result;
460}
diff --git a/utils/rbutilqt/logger/src/ConsoleAppender.cpp b/utils/rbutilqt/logger/src/ConsoleAppender.cpp
new file mode 100644
index 0000000000..932ffab787
--- /dev/null
+++ b/utils/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/utils/rbutilqt/logger/src/FileAppender.cpp b/utils/rbutilqt/logger/src/FileAppender.cpp
new file mode 100644
index 0000000000..b9018b0324
--- /dev/null
+++ b/utils/rbutilqt/logger/src/FileAppender.cpp
@@ -0,0 +1,116 @@
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 "FileAppender.h"
16
17// STL
18#include <iostream>
19
20/**
21 * \class FileAppender
22 *
23 * \brief Simple appender that writes the log records to the plain text file.
24 */
25
26
27//! Constructs the new file appender assigned to file with the given name.
28FileAppender::FileAppender(const QString& fileName)
29{
30 setFileName(fileName);
31}
32
33
34FileAppender::~FileAppender()
35{
36 closeFile();
37}
38
39
40//! Returns the name set by setFileName() or to the FileAppender constructor.
41/**
42 * \sa setFileName()
43 */
44QString FileAppender::fileName() const
45{
46 QMutexLocker locker(&m_logFileMutex);
47 return m_logFile.fileName();
48}
49
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 */
55void FileAppender::setFileName(const QString& s)
56{
57 if (s.isEmpty())
58 std::cerr << "<FileAppender::FileAppender> File name is empty. The appender will do nothing" << std::endl;
59
60 QMutexLocker locker(&m_logFileMutex);
61 if (m_logFile.isOpen())
62 m_logFile.close();
63
64 m_logFile.setFileName(s);
65}
66
67
68bool FileAppender::reopenFile()
69{
70 closeFile();
71 return openFile();
72}
73
74
75bool FileAppender::openFile()
76{
77 if (m_logFile.fileName().isEmpty())
78 return false;
79
80 bool isOpen = m_logFile.isOpen();
81 if (!isOpen)
82 {
83 isOpen = m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
84 if (isOpen)
85 m_logStream.setDevice(&m_logFile);
86 else
87 std::cerr << "<FileAppender::append> Cannot open the log file " << qPrintable(m_logFile.fileName()) << std::endl;
88 }
89 return isOpen;
90}
91
92
93//! Write the log record to the file.
94/**
95 * \sa fileName()
96 * \sa AbstractStringAppender::format()
97 */
98void FileAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
99 const char* function, const QString& category, const QString& message)
100{
101 QMutexLocker locker(&m_logFileMutex);
102
103 if (openFile())
104 {
105 m_logStream << formattedString(timeStamp, logLevel, file, line, function, category, message);
106 m_logStream.flush();
107 m_logFile.flush();
108 }
109}
110
111
112void FileAppender::closeFile()
113{
114 QMutexLocker locker(&m_logFileMutex);
115 m_logFile.close();
116}
diff --git a/utils/rbutilqt/logger/src/Logger.cpp b/utils/rbutilqt/logger/src/Logger.cpp
new file mode 100644
index 0000000000..689bc42e80
--- /dev/null
+++ b/utils/rbutilqt/logger/src/Logger.cpp
@@ -0,0 +1,1108 @@
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#if QT_VERSION >= 0x050e00
604 QSet<AbstractAppender*> deleteList(QSet<AbstractAppender*>(d->appenders.begin(), d->appenders.end()));
605 deleteList.unite(QSet<AbstractAppender*>(d->categoryAppenders.values().begin(), d->categoryAppenders.values().end()));
606#else
607 QSet<AbstractAppender*> deleteList(QSet<AbstractAppender*>::fromList(d->appenders));
608 deleteList.unite(QSet<AbstractAppender*>::fromList(d->categoryAppenders.values()));
609#endif
610 qDeleteAll(deleteList);
611
612 appendersLocker.unlock();
613
614 delete d_ptr;
615}
616
617
618//! Converts the LogLevel enum value to its string representation
619/**
620 * \param logLevel Log level to convert
621 *
622 * \sa LogLevel
623 * \sa levelFromString()
624 */
625QString Logger::levelToString(Logger::LogLevel logLevel)
626{
627 switch (logLevel)
628 {
629 case Trace:
630 return QLatin1String("Trace");
631 case Debug:
632 return QLatin1String("Debug");
633 case Info:
634 return QLatin1String("Info");
635 case Warning:
636 return QLatin1String("Warning");
637 case Error:
638 return QLatin1String("Error");
639 case Fatal:
640 return QLatin1String("Fatal");
641 }
642
643 return QString();
644}
645
646
647//! Converts the LogLevel string representation to enum value
648/**
649 * Comparation of the strings is case independent. If the log level string provided cannot be understood
650 * Logger::Debug is returned.
651 *
652 * \param s String to be decoded
653 *
654 * \sa LogLevel
655 * \sa levelToString()
656 */
657Logger::LogLevel Logger::levelFromString(const QString& s)
658{
659 QString str = s.trimmed().toLower();
660
661 LogLevel result = Debug;
662
663 if (str == QLatin1String("trace"))
664 result = Trace;
665 else if (str == QLatin1String("debug"))
666 result = Debug;
667 else if (str == QLatin1String("info"))
668 result = Info;
669 else if (str == QLatin1String("warning"))
670 result = Warning;
671 else if (str == QLatin1String("error"))
672 result = Error;
673 else if (str == QLatin1String("fatal"))
674 result = Fatal;
675
676 return result;
677}
678
679
680//! Returns the global instance of Logger
681/**
682 * In a most cases you shouldn't use this function directly. Consider using [cuteLogger](@ref cuteLogger) macro instead.
683 *
684 * \sa cuteLogger
685 */
686Logger* Logger::globalInstance()
687{
688 Logger* result = nullptr;
689 {
690 QReadLocker locker(&LoggerPrivate::globalInstanceLock);
691 result = LoggerPrivate::globalInstance;
692 }
693
694 if (!result)
695 {
696 QWriteLocker locker(&LoggerPrivate::globalInstanceLock);
697 LoggerPrivate::globalInstance = new Logger;
698
699#if QT_VERSION >= 0x050000
700 qInstallMessageHandler(qtLoggerMessageHandler);
701#else
702 qInstallMsgHandler(qtLoggerMessageHandler);
703#endif
704 qAddPostRoutine(cleanupLoggerGlobalInstance);
705 result = LoggerPrivate::globalInstance;
706 }
707
708 return result;
709}
710
711
712//! Registers the appender to write the log records to
713/**
714 * On the log writing call (using one of the macros or the write() function) Logger traverses through the list of
715 * the appenders and writes a log records to the each of them. Please, look through the AbstractAppender
716 * documentation to understand the concept of appenders.
717 *
718 * If no appenders was added to Logger, it falls back to logging into the \c std::cerr STL stream.
719 *
720 * \param appender Appender to register in the Logger
721 *
722 * \note Logger takes ownership on the appender and it will delete it on the application exit. According to this,
723 * appenders must be created on heap to prevent double destruction of the appender.
724 *
725 * \sa registerCategoryAppender
726 * \sa AbstractAppender
727 */
728void Logger::registerAppender(AbstractAppender* appender)
729{
730 Q_D(Logger);
731
732 QMutexLocker locker(&d->loggerMutex);
733
734 if (!d->appenders.contains(appender))
735 d->appenders.append(appender);
736 else
737 std::cerr << "Trying to register appender that was already registered" << std::endl;
738}
739
740//! Registers the appender to write the log records to the specific category
741/**
742 * Calling this method, you can link some appender with the named category.
743 * On the log writing call to the specific category (calling write() with category parameter directly,
744 * writing to the default category, or using special LOG_CDEBUG(), LOG_CWARNING() etc. macros),
745 * Logger writes the log message only to the list of registered category appenders.
746 *
747 * You can call logToGlobalInstance() to pass all category log messages to the global logger instance appenders
748 * (registered using registerAppender()).
749 * If no category appenders with specific name was registered to the Logger,
750 * it falls back to logging into the \c std::cerr STL stream, both with simple warning message.
751 *
752 * \param category Category name
753 * \param appender Appender to register in the Logger
754 *
755 * \note Logger takes ownership on the appender and it will delete it on the application exit. According to this,
756 * appenders must be created on heap to prevent double destruction of the appender.
757 *
758 * \sa registerAppender
759 * \sa LOG_CTRACE(), LOG_CDEBUG(), LOG_CINFO(), LOG_CWARNING(), LOG_CERROR(), LOG_CFATAL()
760 * \sa LOG_CATEGORY(), LOG_GLOBAL_CATEGORY()
761 * \sa logToGlobalInstance
762 * \sa setDefaultCategory
763 */
764void Logger::registerCategoryAppender(const QString& category, AbstractAppender* appender)
765{
766 Q_D(Logger);
767
768 QMutexLocker locker(&d->loggerMutex);
769
770 if (!d->categoryAppenders.values().contains(appender))
771 d->categoryAppenders.insert(category, appender);
772 else
773 std::cerr << "Trying to register appender that was already registered" << std::endl;
774}
775
776
777//! Removes the registered appender from logger
778/**
779 * After calling this function logger stops writing any of the records to the appender.
780 *
781 * \param appender Pointer to appender to remove from logger
782 * \note Removed appender will not be deleted on the application shutdown and you will need to destroy the object
783 * yourself.
784 * \sa registerAppender
785 */
786void Logger::removeAppender(AbstractAppender* appender)
787{
788 Q_D(Logger);
789
790 QMutexLocker locker(&d->loggerMutex);
791
792 d->appenders.removeAll(appender);
793 for (QMultiMap<QString,AbstractAppender*>::iterator it = d->categoryAppenders.begin(); it != d->categoryAppenders.end();)
794 {
795 if (it.value() == appender)
796 it = d->categoryAppenders.erase(it);
797 else
798 ++it;
799 }
800}
801
802
803//! Sets default logging category
804/**
805 * All log messages to this category appenders will also be written to general logger instance appenders (registered
806 * using [registerAppender](@ref registerAppender) method), and vice versa.
807 * In particular, any calls to the LOG_DEBUG() macro will be treated as category logging,
808 * so you needn't to specify category name using LOG_CDEBUG() macro.
809 *
810 * To unset the default category, pass a null string as a parameter.
811 *
812 * \param category Category name
813 *
814 * \note "category" format marker will be set to the category name for all of these messages
815 * (see [AbstractStringAppender::setFormat](@ref AbstractStringAppender::setFormat)).
816 *
817 * \sa defaultCategory()
818 * \sa registerCategoryAppender()
819 * \sa logToGlobalInstance()
820 */
821void Logger::setDefaultCategory(const QString& category)
822{
823 Q_D(Logger);
824
825 QMutexLocker locker(&d->loggerMutex);
826
827 d->defaultCategory = category;
828}
829
830//! Returns default logging category name
831/**
832 * \sa setDefaultCategory
833 */
834QString Logger::defaultCategory() const
835{
836 Q_D(const Logger);
837 return d->defaultCategory;
838}
839
840//! Links some logging category with the global logger instance appenders.
841/**
842 * If set to true, all log messages to the specified category appenders will also be written to the global logger instance appenders,
843 * registered using registerAppender().
844 *
845 * By default, all messages to the specific category are written only to the specific category appenders
846 * (registered using registerCategoryAppender()).
847 *
848 * \param category Category name
849 * \param logToGlobal Link or onlink the category from global logger instance appender
850 *
851 * \sa globalInstance
852 * \sa registerAppender
853 * \sa registerCategoryAppender
854 */
855void Logger::logToGlobalInstance(const QString& category, bool logToGlobal)
856{
857 Q_D(Logger);
858
859 if (this == globalInstance())
860 {
861 QMutexLocker locker(&d->loggerMutex);
862 d->categories.insert(category, logToGlobal);
863 }
864 else
865 {
866 globalInstance()->logToGlobalInstance(category, logToGlobal);
867 }
868}
869
870
871void Logger::write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function, const char* category,
872 const QString& message, bool fromLocalInstance)
873{
874 Q_D(Logger);
875
876 QMutexLocker locker(&d->loggerMutex);
877
878 QString logCategory = QString::fromLatin1(category);
879 if (logCategory.isNull() && !d->defaultCategory.isNull())
880 logCategory = d->defaultCategory;
881
882 bool wasWritten = false;
883 bool isGlobalInstance = this == globalInstance();
884 bool linkedToGlobal = isGlobalInstance && d->categories.value(logCategory, false);
885
886 if (!logCategory.isNull())
887 {
888 QList<AbstractAppender*> appenders = d->categoryAppenders.values(logCategory);
889 if (appenders.length() == 0)
890 {
891 if (logCategory != d->defaultCategory && !linkedToGlobal && !fromLocalInstance && !d->noAppendersCategories.contains(logCategory))
892 {
893 std::cerr << "No appenders associated with category " << qPrintable(logCategory) << std::endl;
894 d->noAppendersCategories.append(logCategory);
895 }
896 }
897 else
898 {
899 foreach (AbstractAppender* appender, appenders)
900 appender->write(timeStamp, logLevel, file, line, function, logCategory, message);
901 wasWritten = true;
902 }
903 }
904
905 // the default category is linked to the main logger appenders
906 // global logger instance also writes all linked categories to the main appenders
907 if (logCategory.isNull() || logCategory == d->defaultCategory || linkedToGlobal)
908 {
909 if (!d->appenders.isEmpty())
910 {
911 foreach (AbstractAppender* appender, d->appenders)
912 appender->write(timeStamp, logLevel, file, line, function, logCategory, message);
913 wasWritten = true;
914 }
915 else
916 {
917 static bool noAppendersWarningShown = false;
918 if (!noAppendersWarningShown)
919 {
920#if defined(Q_OS_ANDROID)
921 __android_log_write(ANDROID_LOG_WARN, "Logger", "No appenders registered with logger");
922#else
923 std::cerr << "No appenders registered with logger" << std::endl;
924#endif
925 noAppendersWarningShown = true;
926 }
927 }
928 }
929
930 // local logger instances send category messages to the global instance
931 if (!isGlobalInstance)
932 {
933 if (!logCategory.isNull())
934 {
935 globalInstance()->write(timeStamp, logLevel, file, line, function, logCategory.toLatin1(), message, true);
936 wasWritten = true;
937 }
938
939 if (d->writeDefaultCategoryToGlobalInstance && logCategory == d->defaultCategory)
940 {
941 globalInstance()->write(timeStamp, logLevel, file, line, function, nullptr, message, true);
942 wasWritten = true;
943 }
944 }
945
946 if (!wasWritten && !fromLocalInstance)
947 {
948 // Fallback
949#if defined(Q_OS_ANDROID)
950 QString result = QString(QLatin1String("<%2> %3")).arg(AbstractStringAppender::stripFunctionName(function)).arg(message);
951 __android_log_write(AndroidAppender::androidLogPriority(logLevel), "Logger", qPrintable(result));
952#else
953 QString result = QString(QLatin1String("[%1] <%2> %3")).arg(levelToString(logLevel), -7)
954 .arg(AbstractStringAppender::stripFunctionName(function)).arg(message);
955 std::cerr << qPrintable(result) << std::endl;
956#endif
957 }
958
959 if (logLevel == Logger::Fatal)
960 abort();
961}
962
963
964//! Writes the log record
965/**
966 * Writes the log records with the supplied arguments to all the registered appenders.
967 *
968 * \note It is not recommended to call this function directly. Instead of this you can just call one of the macros
969 * (LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_FATAL) that will supply all the needed
970 * information to this function.
971 *
972 * \param timeStamp - the time stamp of the record
973 * \param logLevel - the log level of the record
974 * \param file - the name of the source file that requested the log record
975 * \param line - the line of the code of source file that requested the log record
976 * \param function - name of the function that requested the log record
977 * \param category - logging category (0 for default category)
978 * \param message - log message
979 *
980 * \note Recording of the log record using the Logger::Fatal log level will lead to calling the STL abort()
981 * function, which will interrupt the running of your software and begin the writing of the core dump.
982 *
983 * \sa LogLevel
984 * \sa LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_FATAL
985 * \sa AbstractAppender
986 */
987void Logger::write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function, const char* category,
988 const QString& message)
989{
990 write(timeStamp, logLevel, file, line, function, category, message, /* fromLocalInstance = */ false);
991}
992
993/**
994 * This is the overloaded function provided for the convinience. It behaves similar to the above function.
995 *
996 * This function uses the current timestamp obtained with \c QDateTime::currentDateTime().
997 *
998 * \sa write()
999 */
1000void Logger::write(LogLevel logLevel, const char* file, int line, const char* function, const char* category,
1001 const QString& message)
1002{
1003 write(QDateTime::currentDateTime(), logLevel, file, line, function, category, message);
1004}
1005
1006
1007//! Writes the assertion
1008/**
1009 * This function writes the assertion record using the write() function.
1010 *
1011 * The assertion record is always written using the Logger::Fatal log level which leads to the abortation of the
1012 * program and generation of the core dump (if supported).
1013 *
1014 * The message written to the appenders will be identical to the \c condition argument prefixed with the
1015 * <tt>ASSERT:</tt> notification.
1016 *
1017 * \note It is not recommended to call this function directly. Instead of this you can just call the LOG_ASSERT
1018 * macro that will supply all the needed information to this function.
1019 *
1020 * \sa LOG_ASSERT
1021 * \sa write()
1022 */
1023void Logger::writeAssert(const char* file, int line, const char* function, const char* condition)
1024{
1025 write(Logger::Fatal, file, line, function, nullptr, QString("ASSERT: \"%1\"").arg(condition));
1026}
1027
1028
1029Logger* cuteLoggerInstance()
1030{
1031 return Logger::globalInstance();
1032}
1033
1034
1035
1036void LoggerTimingHelper::start(const char* msg, ...)
1037{
1038 va_list va;
1039 va_start(va, msg);
1040#if QT_VERSION >= 0x050500
1041 m_block = QString().vasprintf(msg, va);
1042#else
1043 m_block = QString().vsprintf(msg, va);
1044#endif
1045 va_end(va);
1046
1047 m_time.start();
1048}
1049
1050
1051void LoggerTimingHelper::start(const QString& block)
1052{
1053 m_block = block;
1054 m_time.start();
1055}
1056
1057
1058void LoggerTimingHelper::start(Logger::TimingMode mode, const QString& block)
1059{
1060 m_timingMode = mode;
1061 m_block = block;
1062 m_time.start();
1063}
1064
1065
1066LoggerTimingHelper::~LoggerTimingHelper()
1067{
1068 QString message;
1069 if (m_block.isEmpty())
1070 message = QString(QLatin1String("Function %1 finished in ")).arg(AbstractStringAppender::stripFunctionName(m_function));
1071 else
1072 message = QString(QLatin1String("\"%1\" finished in ")).arg(m_block);
1073
1074 qint64 elapsed = m_time.elapsed();
1075 if (elapsed >= 10000 && m_timingMode == Logger::TimingAuto)
1076 message += QString(QLatin1String("%1 s.")).arg(elapsed / 1000);
1077 else
1078 message += QString(QLatin1String("%1 ms.")).arg(elapsed);
1079
1080 m_logger->write(m_logLevel, m_file, m_line, m_function, nullptr, message);
1081}
1082
1083
1084CuteMessageLogger::~CuteMessageLogger()
1085{
1086 m_l->write(m_level, m_file, m_line, m_function, m_category, m_message);
1087}
1088
1089void CuteMessageLogger::write(const char* msg, ...)
1090{
1091 va_list va;
1092 va_start(va, msg);
1093 m_message = QString::vasprintf(msg, va);
1094 va_end(va);
1095}
1096
1097
1098void CuteMessageLogger::write(const QString& msg)
1099{
1100 m_message = msg;
1101}
1102
1103
1104QDebug CuteMessageLogger::write()
1105{
1106 QDebug d(&m_message);
1107 return d;
1108}
diff --git a/utils/rbutilqt/logger/src/OutputDebugAppender.cpp b/utils/rbutilqt/logger/src/OutputDebugAppender.cpp
new file mode 100644
index 0000000000..44d640512f
--- /dev/null
+++ b/utils/rbutilqt/logger/src/OutputDebugAppender.cpp
@@ -0,0 +1,43 @@
1/*
2 Copyright (c) 2010 Karl-Heinz Reichel (khreichel at googlemail 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 "OutputDebugAppender.h"
16
17// STL
18#include <windows.h>
19
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 */
32void OutputDebugAppender::append(const QDateTime& timeStamp,
33 Logger::LogLevel logLevel,
34 const char* file,
35 int line,
36 const char* function,
37 const QString& category,
38 const QString& message)
39{
40 QString s = formattedString(timeStamp, logLevel, file, line, function, category, message);
41 OutputDebugStringW((LPCWSTR) s.utf16());
42}
43