summaryrefslogtreecommitdiff
path: root/rbutil/rbutilqt/logger/src
diff options
context:
space:
mode:
Diffstat (limited to 'rbutil/rbutilqt/logger/src')
-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.cpp116
-rw-r--r--rbutil/rbutilqt/logger/src/Logger.cpp1099
-rw-r--r--rbutil/rbutilqt/logger/src/OutputDebugAppender.cpp43
6 files changed, 1928 insertions, 0 deletions
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/src/FileAppender.cpp b/rbutil/rbutilqt/logger/src/FileAppender.cpp
new file mode 100644
index 0000000000..b9018b0324
--- /dev/null
+++ b/rbutil/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/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/src/OutputDebugAppender.cpp b/rbutil/rbutilqt/logger/src/OutputDebugAppender.cpp
new file mode 100644
index 0000000000..44d640512f
--- /dev/null
+++ b/rbutil/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