summaryrefslogtreecommitdiff
path: root/utils/rbutilqt/logger/src/Logger.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'utils/rbutilqt/logger/src/Logger.cpp')
-rw-r--r--utils/rbutilqt/logger/src/Logger.cpp1108
1 files changed, 1108 insertions, 0 deletions
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}