summaryrefslogtreecommitdiffstats
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 @@
+/*
+ Copyright (c) 2012 Boris Moiseev (cyberbobs at gmail dot com)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1
+ as published by the Free Software Foundation and appearing in the file
+ LICENSE.LGPL included in the packaging of this file.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+*/
+// Local
+#include "Logger.h"
+#include "AbstractAppender.h"
+#include "AbstractStringAppender.h"
+
+// Qt
+#include <QCoreApplication>
+#include <QReadWriteLock>
+#include <QSemaphore>
+#include <QDateTime>
+#include <QIODevice>
+#include <QTextCodec>
+
+#if defined(Q_OS_ANDROID)
+# include <android/log.h>
+# include <AndroidAppender.h>
+#endif
+
+// STL
+#include <iostream>
+
+
+/**
+ * \file Logger.h
+ * \brief A file containing the description of Logger class and and additional useful macros for logging
+ */
+
+
+/**
+ * \mainpage
+ *
+ * Logger is a simple way to write the history of your application lifecycle to any target logging device (which is
+ * called Appender and may write to any target you will implement with it: console, text file, XML or something - you
+ * choose) and to map logging message to a class, function, source file and line of code which it is called from.
+ *
+ * Some simple appenders (which may be considered an examples) are provided with the Logger itself: see ConsoleAppender
+ * and FileAppender documentation.
+ *
+ * It supports using it in a multithreaded applications, so all of its functions are thread safe.
+ *
+ * Simple usage example:
+ * \code
+ * #include <QCoreApplication>
+ *
+ * #include <Logger.h>
+ * #include <ConsoleAppender.h>
+ *
+ * int main(int argc, char* argv[])
+ * {
+ * QCoreApplication app(argc, argv);
+ * ...
+ * ConsoleAppender* consoleAppender = new ConsoleAppender;
+ * consoleAppender->setFormat("[%{type:-7}] <%{Function}> %{message}\n");
+ * cuteLogger->registerAppender(consoleAppender);
+ * ...
+ * LOG_INFO("Starting the application");
+ * int result = app.exec();
+ * ...
+ * if (result)
+ * LOG_WARNING() << "Something went wrong." << "Result code is" << result;
+ *
+ * return result;
+ * }
+ * \endcode
+ *
+ * Logger internally uses the lazy-initialized singleton object and needs no definite initialization, but you may
+ * consider registering a log appender before calling any log recording functions or macros.
+ *
+ * The library design of Logger allows you to simply mass-replace all occurrences of qDebug and similar calls with
+ * similar Logger macros (e.g. LOG_DEBUG())
+ *
+ * \note Logger uses a singleton global instance which lives through all the application life cycle and self-destroys
+ * destruction of the QCoreApplication (or QApplication) instance. It needs a QCoreApplication instance to be
+ * created before any of the Logger's functions are called.
+ *
+ * \sa cuteLogger
+ * \sa LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_FATAL
+ * \sa LOG_CTRACE, LOG_CDEBUG, LOG_CINFO, LOG_CWARNING, LOG_CERROR, LOG_CFATAL
+ * \sa LOG_ASSERT
+ * \sa LOG_TRACE_TIME, LOG_DEBUG_TIME, LOG_INFO_TIME
+ * \sa AbstractAppender
+ */
+
+
+/**
+ * \def cuteLogger
+ *
+ * \brief Macro returning the current instance of Logger object
+ *
+ * If you haven't created a local Logger object it returns the same value as the Logger::globalInstance() functions.
+ * This macro is a recommended way to get an access to the Logger instance used in current class.
+ *
+ * Example:
+ * \code
+ * ConsoleAppender* consoleAppender = new ConsoleAppender;
+ * cuteLogger->registerAppender(consoleAppender);
+ * \endcode
+ *
+ * \sa Logger::globalInstance()
+ */
+
+
+/**
+ * \def LOG_TRACE
+ *
+ * \brief Writes the trace log record
+ *
+ * This macro is the convinient way to call Logger::write(). It uses the common preprocessor macros \c __FILE__,
+ * \c __LINE__ and the standart Qt \c Q_FUNC_INFO macros to automatically determine the needed parameters to call
+ * Logger::write().
+ *
+ * \note This and other (LOG_INFO() etc...) macros uses the variadic macro arguments to give convinient usage form for
+ * the different versions of Logger::write() (using the QString or const char* argument or returning the QDebug class
+ * instance). Not all compilers will support this. Please, consider reviewing your compiler documentation to ensure
+ * it support __VA_ARGS__ macro.
+ *
+ * \sa Logger::LogLevel
+ * \sa Logger::write()
+ */
+
+
+/**
+ * \def LOG_DEBUG
+ *
+ * \brief Writes the debug log record
+ *
+ * This macro records the debug log record using the Logger::write() function. It works similar to the LOG_TRACE()
+ * macro.
+ *
+ * \sa LOG_TRACE()
+ * \sa Logger::LogLevel
+ * \sa Logger::write()
+ */
+
+
+/**
+ * \def LOG_INFO
+ *
+ * \brief Writes the info log record
+ *
+ * This macro records the info log record using the Logger::write() function. It works similar to the LOG_TRACE()
+ * macro.
+ *
+ * \sa LOG_TRACE()
+ * \sa Logger::LogLevel
+ * \sa Logger::write()
+ */
+
+
+/**
+ * \def LOG_WARNING
+ *
+ * \brief Write the warning log record
+ *
+ * This macro records the warning log record using the Logger::write() function. It works similar to the LOG_TRACE()
+ * macro.
+ *
+ * \sa LOG_TRACE()
+ * \sa Logger::LogLevel
+ * \sa Logger::write()
+ */
+
+
+/**
+ * \def LOG_ERROR
+ *
+ * \brief Write the error log record
+ * This macro records the error log record using the Logger::write() function. It works similar to the LOG_TRACE()
+ * macro.
+ *
+ * \sa LOG_TRACE()
+ * \sa Logger::LogLevel
+ * \sa Logger::write()
+ */
+
+
+/**
+ * \def LOG_FATAL
+ *
+ * \brief Write the fatal log record
+ *
+ * This macro records the fatal log record using the Logger::write() function. It works similar to the LOG_TRACE()
+ * macro.
+ *
+ * \note Recording of the log record using the Logger::Fatal log level will lead to calling the STL abort()
+ * function, which will interrupt the running of your software and begin the writing of the core dump.
+ *
+ * \sa LOG_TRACE()
+ * \sa Logger::LogLevel
+ * \sa Logger::write()
+ */
+
+
+/**
+ * \def LOG_CTRACE(category)
+ *
+ * \brief Writes the trace log record to the specific category
+ *
+ * This macro is the similar to the LOG_TRACE() macro, but has a category parameter
+ * to write only to the category appenders (registered using Logger::registerCategoryAppender() method).
+ *
+ * \param category category name string
+ *
+ * \sa LOG_TRACE()
+ * \sa Logger::LogLevel
+ * \sa Logger::registerCategoryAppender()
+ * \sa Logger::write()
+ * \sa LOG_CATEGORY(), LOG_GLOBAL_CATEGORY()
+ */
+
+
+/**
+ * \def LOG_CDEBUG
+ *
+ * \brief Writes the debug log record to the specific category
+ *
+ * This macro records the debug log record using the Logger::write() function. It works similar to the LOG_CTRACE()
+ * macro.
+ *
+ * \sa LOG_CTRACE()
+ */
+
+
+/**
+ * \def LOG_CINFO
+ *
+ * \brief Writes the info log record to the specific category
+ *
+ * This macro records the info log record using the Logger::write() function. It works similar to the LOG_CTRACE()
+ * macro.
+ *
+ * \sa LOG_CTRACE()
+ */
+
+
+/**
+ * \def LOG_CWARNING
+ *
+ * \brief Writes the warning log record to the specific category
+ *
+ * This macro records the warning log record using the Logger::write() function. It works similar to the LOG_CTRACE()
+ * macro.
+ *
+ * \sa LOG_CTRACE()
+ */
+
+
+/**
+ * \def LOG_CERROR
+ *
+ * \brief Writes the error log record to the specific category
+ *
+ * This macro records the error log record using the Logger::write() function. It works similar to the LOG_CTRACE()
+ * macro.
+ *
+ * \sa LOG_CTRACE()
+ */
+
+
+/**
+ * \def LOG_CFATAL
+ *
+ * \brief Write the fatal log record to the specific category
+ *
+ * This macro records the fatal log record using the Logger::write() function. It works similar to the LOG_CTRACE()
+ * macro.
+ *
+ * \note Recording of the log record using the Logger::Fatal log level will lead to calling the STL abort()
+ * function, which will interrupt the running of your software and begin the writing of the core dump.
+ *
+ * \sa LOG_CTRACE()
+ */
+
+
+/**
+ * \def LOG_CATEGORY(category)
+ *
+ * \brief Create logger instance inside your custom class to log all messages to the specified category
+ *
+ * This macro is used to pass all log messages inside your custom class to the specific category.
+ * You must include this macro inside your class declaration (similarly to the Q_OBJECT macro).
+ * Internally, this macro redefines cuteLoggerInstance() function, creates the local Logger object inside your class and
+ * sets the default category to the specified parameter.
+ *
+ * Thus, any call to cuteLoggerInstance() (for example, inside LOG_TRACE() macro) will return the local Logger object,
+ * so any logging message will be directed to the default category.
+ *
+ * \note This macro does not register any appender to the newly created logger instance. You should register
+ * logger appenders manually, inside your class.
+ *
+ * Usage example:
+ * \code
+ * class CustomClass : public QObject
+ * {
+ * Q_OBJECT
+ * LOG_CATEGORY("custom_category")
+ * ...
+ * };
+ *
+ * CustomClass::CustomClass(QObject* parent) : QObject(parent)
+ * {
+ * cuteLogger->registerAppender(new FileAppender("custom_category_log"));
+ * LOG_TRACE() << "Trace to the custom category log";
+ * }
+ * \endcode
+ *
+ * If used compiler supports C++11 standard, LOG_CATEGORY and LOG_GLOBAL_CATEGORY macros would also work when added
+ * inside of any scope. It could be useful, for example, to log every single run of a method to a different file.
+ *
+ * \code
+ * void foo()
+ * {
+ * QString categoryName = QDateTime::currentDateTime().toString("yyyy-MM-ddThh-mm-ss-zzz");
+ * LOG_CATEGORY(categoryName);
+ * cuteLogger->registerAppender(new FileAppender(categoryName + ".log"));
+ * ...
+ * }
+ * \endcode
+ *
+ * \sa Logger::write()
+ * \sa LOG_TRACE
+ * \sa Logger::registerCategoryAppender()
+ * \sa Logger::setDefaultCategory()
+ * \sa LOG_GLOBAL_CATEGORY
+ */
+
+
+/**
+ * \def LOG_GLOBAL_CATEGORY(category)
+ *
+ * \brief Create logger instance inside your custom class to log all messages both to the specified category and to
+ * the global logger instance.
+ *
+ * This macro is similar to LOG_CATEGORY(), but also passes all log messages to the global logger instance appenders.
+ * It is equal to defining the local category logger using LOG_CATEGORY macro and calling:
+ * \code cuteLogger->logToGlobalInstance(cuteLogger->defaultCategory(), true); \endcode
+ *
+ * \sa LOG_CATEGORY
+ * \sa Logger::logToGlobalInstance()
+ * \sa Logger::defaultCategory()
+ * \sa Logger::registerCategoryAppender()
+ * \sa Logger::write()
+ */
+
+
+
+/**
+ * \def LOG_ASSERT
+ *
+ * \brief Check the assertion
+ *
+ * This macro is a convinient and recommended to use way to call Logger::writeAssert() function. It uses the
+ * preprocessor macros (as the LOG_DEBUG() does) to fill the necessary arguments of the Logger::writeAssert() call. It
+ * also uses undocumented but rather mature and stable \c qt_noop() function (which does nothing) when the assertion
+ * is true.
+ *
+ * Example:
+ * \code
+ * bool b = checkSomething();
+ * ...
+ * LOG_ASSERT(b == true);
+ * \endcode
+ *
+ * \sa Logger::writeAssert()
+ */
+
+
+/**
+ * \def LOG_TRACE_TIME
+ *
+ * \brief Logs the processing time of current function / code block
+ *
+ * This macro automagically measures the function or code of block execution time and outputs it as a Logger::Trace
+ * level log record.
+ *
+ * Example:
+ * \code
+ * int foo()
+ * {
+ * LOG_TRACE_TIME();
+ * ... // Do some long operations
+ * return 0;
+ * } // Outputs: Function foo finished in <time> ms.
+ * \endcode
+ *
+ * If you are measuring a code of block execution time you may also add a name of block to the macro:
+ * \code
+ * int bar(bool doFoo)
+ * {
+ * LOG_TRACE_TIME();
+ *
+ * if (doFoo)
+ * {
+ * LOG_TRACE_TIME("Foo");
+ * ...
+ * }
+ *
+ * ...
+ * }
+ * // Outputs:
+ * // "Foo" finished in <time1> ms.
+ * // Function bar finished in <time2> ms.
+ * \endcode
+ *
+ * \note Macro switches to logging the seconds instead of milliseconds when the execution time reaches 10000 ms.
+ * \sa LOG_DEBUG_TIME, LOG_INFO_TIME
+ */
+
+
+/**
+ * \def LOG_DEBUG_TIME
+ *
+ * \brief Logs the processing time of current function / code block
+ *
+ * This macro automagically measures the function or code of block execution time and outputs it as a Logger::Debug
+ * level log record. It works similar to LOG_TRACE_TIME() macro.
+ *
+ * \sa LOG_TRACE_TIME
+ */
+
+
+/**
+ * \def LOG_INFO_TIME
+ *
+ * \brief Logs the processing time of current function / code block
+ *
+ * This macro automagically measures the function or code of block execution time and outputs it as a Logger::Info
+ * level log record. It works similar to LOG_TRACE_TIME() macro.
+ *
+ * \sa LOG_TRACE_TIME
+ */
+
+
+/**
+ * \class Logger
+ *
+ * \brief Very simple but rather powerful component which may be used for logging your application activities.
+ *
+ * Global logger instance created on a first access to it (e.g. registering appenders, calling a LOG_DEBUG() macro
+ * etc.) registers itself as a Qt default message handler and captures all the qDebug/qWarning/qCritical output.
+ *
+ * \note Qt 4 qDebug set of macro doesn't support capturing source function name, file name or line number so we
+ * recommend to use LOG_DEBUG() and other Logger macros instead.
+ *
+ * \sa cuteLogger
+ * \sa [CuteLogger Documentation](index.html)
+ */
+
+// Forward declarations
+static void cleanupLoggerGlobalInstance();
+
+#if QT_VERSION >= 0x050000
+static void qtLoggerMessageHandler(QtMsgType, const QMessageLogContext& context, const QString& msg);
+#else
+static void qtLoggerMessageHandler(QtMsgType type, const char* msg);
+#endif
+
+/**
+ * \internal
+ *
+ * LoggerPrivate class implements the Singleton pattern in a thread-safe way. It contains a static pointer to the
+ * global logger instance protected by QReadWriteLock
+ */
+class LoggerPrivate
+{
+ public:
+ static Logger* globalInstance;
+ static QReadWriteLock globalInstanceLock;
+
+ QList<AbstractAppender*> appenders;
+ QMutex loggerMutex;
+
+ QMap<QString, bool> categories;
+ QMultiMap<QString, AbstractAppender*> categoryAppenders;
+ QStringList noAppendersCategories; //<! Categories without appenders that was already warned about
+ QString defaultCategory;
+ bool writeDefaultCategoryToGlobalInstance;
+};
+
+
+// Static fields initialization
+Logger* LoggerPrivate::globalInstance = nullptr;
+QReadWriteLock LoggerPrivate::globalInstanceLock;
+
+
+static void cleanupLoggerGlobalInstance()
+{
+ QWriteLocker locker(&LoggerPrivate::globalInstanceLock);
+
+ delete LoggerPrivate::globalInstance;
+ LoggerPrivate::globalInstance = nullptr;
+}
+
+
+#if QT_VERSION >= 0x050000
+static void qtLoggerMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg)
+{
+ Logger::LogLevel level = Logger::Debug;
+ switch (type)
+ {
+ case QtDebugMsg:
+ level = Logger::Debug;
+ break;
+#if QT_VERSION >= 0x050500
+ case QtInfoMsg:
+ level = Logger::Info;
+ break;
+#endif
+ case QtWarningMsg:
+ level = Logger::Warning;
+ break;
+ case QtCriticalMsg:
+ level = Logger::Error;
+ break;
+ case QtFatalMsg:
+ level = Logger::Fatal;
+ break;
+ }
+
+ bool isDefaultCategory = QString::fromLatin1(context.category) == "default";
+ Logger::globalInstance()->write(level, context.file, context.line, context.function, isDefaultCategory ? nullptr : context.category, msg);
+}
+
+#else
+
+static void qtLoggerMessageHandler(QtMsgType type, const char* msg)
+{
+ switch (type)
+ {
+ case QtDebugMsg:
+ cuteLoggerInstance()->write(Logger::Debug, "", 0, "qDebug", 0, msg);
+ break;
+ case QtWarningMsg:
+ cuteLoggerInstance()->write(Logger::Warning, "", 0, "qDebug", 0, msg);
+ break;
+ case QtCriticalMsg:
+ cuteLoggerInstance()->write(Logger::Error, "", 0, "qDebug", 0, msg);
+ break;
+ case QtFatalMsg:
+ cuteLoggerInstance()->write(Logger::Fatal, "", 0, "qDebug", 0, msg);
+ break;
+ }
+}
+#endif
+
+
+//! Construct the instance of Logger
+/**
+ * If you're only using one global instance of logger you wouldn't probably need to use this constructor manually.
+ * Consider using [cuteLogger](@ref cuteLogger) macro instead to access the logger instance
+ */
+Logger::Logger()
+ : d_ptr(new LoggerPrivate)
+{
+ Q_D(Logger);
+ d->writeDefaultCategoryToGlobalInstance = false;
+}
+
+
+//! Construct the instance of Logger and set logger default category
+/**
+ * If you're only using one global instance of logger you wouldn't probably need to use this constructor manually.
+ * Consider using [cuteLogger](@ref cuteLogger) macro instead to access the logger instance and call
+ * [setDefaultCategory](@ref setDefaultCategory) method.
+ *
+ * \sa Logger()
+ * \sa setDefaultCategory()
+ */
+Logger::Logger(const QString& defaultCategory, bool writeToGlobalInstance)
+ : d_ptr(new LoggerPrivate)
+{
+ Q_D(Logger);
+ d->writeDefaultCategoryToGlobalInstance = writeToGlobalInstance;
+
+ setDefaultCategory(defaultCategory);
+}
+
+
+//! Destroy the instance of Logger
+/**
+ * You probably wouldn't need to use this function directly. Global instance of logger will be destroyed automatically
+ * at the end of your QCoreApplication execution
+ */
+Logger::~Logger()
+{
+ Q_D(Logger);
+
+ // Cleanup appenders
+ QMutexLocker appendersLocker(&d->loggerMutex);
+#if QT_VERSION >= 0x050e00
+ QSet<AbstractAppender*> deleteList(QSet<AbstractAppender*>(d->appenders.begin(), d->appenders.end()));
+ deleteList.unite(QSet<AbstractAppender*>(d->categoryAppenders.values().begin(), d->categoryAppenders.values().end()));
+#else
+ QSet<AbstractAppender*> deleteList(QSet<AbstractAppender*>::fromList(d->appenders));
+ deleteList.unite(QSet<AbstractAppender*>::fromList(d->categoryAppenders.values()));
+#endif
+ qDeleteAll(deleteList);
+
+ appendersLocker.unlock();
+
+ delete d_ptr;
+}
+
+
+//! Converts the LogLevel enum value to its string representation
+/**
+ * \param logLevel Log level to convert
+ *
+ * \sa LogLevel
+ * \sa levelFromString()
+ */
+QString Logger::levelToString(Logger::LogLevel logLevel)
+{
+ switch (logLevel)
+ {
+ case Trace:
+ return QLatin1String("Trace");
+ case Debug:
+ return QLatin1String("Debug");
+ case Info:
+ return QLatin1String("Info");
+ case Warning:
+ return QLatin1String("Warning");
+ case Error:
+ return QLatin1String("Error");
+ case Fatal:
+ return QLatin1String("Fatal");
+ }
+
+ return QString();
+}
+
+
+//! Converts the LogLevel string representation to enum value
+/**
+ * Comparation of the strings is case independent. If the log level string provided cannot be understood
+ * Logger::Debug is returned.
+ *
+ * \param s String to be decoded
+ *
+ * \sa LogLevel
+ * \sa levelToString()
+ */
+Logger::LogLevel Logger::levelFromString(const QString& s)
+{
+ QString str = s.trimmed().toLower();
+
+ LogLevel result = Debug;
+
+ if (str == QLatin1String("trace"))
+ result = Trace;
+ else if (str == QLatin1String("debug"))
+ result = Debug;
+ else if (str == QLatin1String("info"))
+ result = Info;
+ else if (str == QLatin1String("warning"))
+ result = Warning;
+ else if (str == QLatin1String("error"))
+ result = Error;
+ else if (str == QLatin1String("fatal"))
+ result = Fatal;
+
+ return result;
+}
+
+
+//! Returns the global instance of Logger
+/**
+ * In a most cases you shouldn't use this function directly. Consider using [cuteLogger](@ref cuteLogger) macro instead.
+ *
+ * \sa cuteLogger
+ */
+Logger* Logger::globalInstance()
+{
+ Logger* result = nullptr;
+ {
+ QReadLocker locker(&LoggerPrivate::globalInstanceLock);
+ result = LoggerPrivate::globalInstance;
+ }
+
+ if (!result)
+ {
+ QWriteLocker locker(&LoggerPrivate::globalInstanceLock);
+ LoggerPrivate::globalInstance = new Logger;
+
+#if QT_VERSION >= 0x050000
+ qInstallMessageHandler(qtLoggerMessageHandler);
+#else
+ qInstallMsgHandler(qtLoggerMessageHandler);
+#endif
+ qAddPostRoutine(cleanupLoggerGlobalInstance);
+ result = LoggerPrivate::globalInstance;
+ }
+
+ return result;
+}
+
+
+//! Registers the appender to write the log records to
+/**
+ * On the log writing call (using one of the macros or the write() function) Logger traverses through the list of
+ * the appenders and writes a log records to the each of them. Please, look through the AbstractAppender
+ * documentation to understand the concept of appenders.
+ *
+ * If no appenders was added to Logger, it falls back to logging into the \c std::cerr STL stream.
+ *
+ * \param appender Appender to register in the Logger
+ *
+ * \note Logger takes ownership on the appender and it will delete it on the application exit. According to this,
+ * appenders must be created on heap to prevent double destruction of the appender.
+ *
+ * \sa registerCategoryAppender
+ * \sa AbstractAppender
+ */
+void Logger::registerAppender(AbstractAppender* appender)
+{
+ Q_D(Logger);
+
+ QMutexLocker locker(&d->loggerMutex);
+
+ if (!d->appenders.contains(appender))
+ d->appenders.append(appender);
+ else
+ std::cerr << "Trying to register appender that was already registered" << std::endl;
+}
+
+//! Registers the appender to write the log records to the specific category
+/**
+ * Calling this method, you can link some appender with the named category.
+ * On the log writing call to the specific category (calling write() with category parameter directly,
+ * writing to the default category, or using special LOG_CDEBUG(), LOG_CWARNING() etc. macros),
+ * Logger writes the log message only to the list of registered category appenders.
+ *
+ * You can call logToGlobalInstance() to pass all category log messages to the global logger instance appenders
+ * (registered using registerAppender()).
+ * If no category appenders with specific name was registered to the Logger,
+ * it falls back to logging into the \c std::cerr STL stream, both with simple warning message.
+ *
+ * \param category Category name
+ * \param appender Appender to register in the Logger
+ *
+ * \note Logger takes ownership on the appender and it will delete it on the application exit. According to this,
+ * appenders must be created on heap to prevent double destruction of the appender.
+ *
+ * \sa registerAppender
+ * \sa LOG_CTRACE(), LOG_CDEBUG(), LOG_CINFO(), LOG_CWARNING(), LOG_CERROR(), LOG_CFATAL()
+ * \sa LOG_CATEGORY(), LOG_GLOBAL_CATEGORY()
+ * \sa logToGlobalInstance
+ * \sa setDefaultCategory
+ */
+void Logger::registerCategoryAppender(const QString& category, AbstractAppender* appender)
+{
+ Q_D(Logger);
+
+ QMutexLocker locker(&d->loggerMutex);
+
+ if (!d->categoryAppenders.values().contains(appender))
+ d->categoryAppenders.insert(category, appender);
+ else
+ std::cerr << "Trying to register appender that was already registered" << std::endl;
+}
+
+
+//! Removes the registered appender from logger
+/**
+ * After calling this function logger stops writing any of the records to the appender.
+ *
+ * \param appender Pointer to appender to remove from logger
+ * \note Removed appender will not be deleted on the application shutdown and you will need to destroy the object
+ * yourself.
+ * \sa registerAppender
+ */
+void Logger::removeAppender(AbstractAppender* appender)
+{
+ Q_D(Logger);
+
+ QMutexLocker locker(&d->loggerMutex);
+
+ d->appenders.removeAll(appender);
+ for (QMultiMap<QString,AbstractAppender*>::iterator it = d->categoryAppenders.begin(); it != d->categoryAppenders.end();)
+ {
+ if (it.value() == appender)
+ it = d->categoryAppenders.erase(it);
+ else
+ ++it;
+ }
+}
+
+
+//! Sets default logging category
+/**
+ * All log messages to this category appenders will also be written to general logger instance appenders (registered
+ * using [registerAppender](@ref registerAppender) method), and vice versa.
+ * In particular, any calls to the LOG_DEBUG() macro will be treated as category logging,
+ * so you needn't to specify category name using LOG_CDEBUG() macro.
+ *
+ * To unset the default category, pass a null string as a parameter.
+ *
+ * \param category Category name
+ *
+ * \note "category" format marker will be set to the category name for all of these messages
+ * (see [AbstractStringAppender::setFormat](@ref AbstractStringAppender::setFormat)).
+ *
+ * \sa defaultCategory()
+ * \sa registerCategoryAppender()
+ * \sa logToGlobalInstance()
+ */
+void Logger::setDefaultCategory(const QString& category)
+{
+ Q_D(Logger);
+
+ QMutexLocker locker(&d->loggerMutex);
+
+ d->defaultCategory = category;
+}
+
+//! Returns default logging category name
+/**
+ * \sa setDefaultCategory
+ */
+QString Logger::defaultCategory() const
+{
+ Q_D(const Logger);
+ return d->defaultCategory;
+}
+
+//! Links some logging category with the global logger instance appenders.
+/**
+ * If set to true, all log messages to the specified category appenders will also be written to the global logger instance appenders,
+ * registered using registerAppender().
+ *
+ * By default, all messages to the specific category are written only to the specific category appenders
+ * (registered using registerCategoryAppender()).
+ *
+ * \param category Category name
+ * \param logToGlobal Link or onlink the category from global logger instance appender
+ *
+ * \sa globalInstance
+ * \sa registerAppender
+ * \sa registerCategoryAppender
+ */
+void Logger::logToGlobalInstance(const QString& category, bool logToGlobal)
+{
+ Q_D(Logger);
+
+ if (this == globalInstance())
+ {
+ QMutexLocker locker(&d->loggerMutex);
+ d->categories.insert(category, logToGlobal);
+ }
+ else
+ {
+ globalInstance()->logToGlobalInstance(category, logToGlobal);
+ }
+}
+
+
+void Logger::write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function, const char* category,
+ const QString& message, bool fromLocalInstance)
+{
+ Q_D(Logger);
+
+ QMutexLocker locker(&d->loggerMutex);
+
+ QString logCategory = QString::fromLatin1(category);
+ if (logCategory.isNull() && !d->defaultCategory.isNull())
+ logCategory = d->defaultCategory;
+
+ bool wasWritten = false;
+ bool isGlobalInstance = this == globalInstance();
+ bool linkedToGlobal = isGlobalInstance && d->categories.value(logCategory, false);
+
+ if (!logCategory.isNull())
+ {
+ QList<AbstractAppender*> appenders = d->categoryAppenders.values(logCategory);
+ if (appenders.length() == 0)
+ {
+ if (logCategory != d->defaultCategory && !linkedToGlobal && !fromLocalInstance && !d->noAppendersCategories.contains(logCategory))
+ {
+ std::cerr << "No appenders associated with category " << qPrintable(logCategory) << std::endl;
+ d->noAppendersCategories.append(logCategory);
+ }
+ }
+ else
+ {
+ foreach (AbstractAppender* appender, appenders)
+ appender->write(timeStamp, logLevel, file, line, function, logCategory, message);
+ wasWritten = true;
+ }
+ }
+
+ // the default category is linked to the main logger appenders
+ // global logger instance also writes all linked categories to the main appenders
+ if (logCategory.isNull() || logCategory == d->defaultCategory || linkedToGlobal)
+ {
+ if (!d->appenders.isEmpty())
+ {
+ foreach (AbstractAppender* appender, d->appenders)
+ appender->write(timeStamp, logLevel, file, line, function, logCategory, message);
+ wasWritten = true;
+ }
+ else
+ {
+ static bool noAppendersWarningShown = false;
+ if (!noAppendersWarningShown)
+ {
+#if defined(Q_OS_ANDROID)
+ __android_log_write(ANDROID_LOG_WARN, "Logger", "No appenders registered with logger");
+#else
+ std::cerr << "No appenders registered with logger" << std::endl;
+#endif
+ noAppendersWarningShown = true;
+ }
+ }
+ }
+
+ // local logger instances send category messages to the global instance
+ if (!isGlobalInstance)
+ {
+ if (!logCategory.isNull())
+ {
+ globalInstance()->write(timeStamp, logLevel, file, line, function, logCategory.toLatin1(), message, true);
+ wasWritten = true;
+ }
+
+ if (d->writeDefaultCategoryToGlobalInstance && logCategory == d->defaultCategory)
+ {
+ globalInstance()->write(timeStamp, logLevel, file, line, function, nullptr, message, true);
+ wasWritten = true;
+ }
+ }
+
+ if (!wasWritten && !fromLocalInstance)
+ {
+ // Fallback
+#if defined(Q_OS_ANDROID)
+ QString result = QString(QLatin1String("<%2> %3")).arg(AbstractStringAppender::stripFunctionName(function)).arg(message);
+ __android_log_write(AndroidAppender::androidLogPriority(logLevel), "Logger", qPrintable(result));
+#else
+ QString result = QString(QLatin1String("[%1] <%2> %3")).arg(levelToString(logLevel), -7)
+ .arg(AbstractStringAppender::stripFunctionName(function)).arg(message);
+ std::cerr << qPrintable(result) << std::endl;
+#endif
+ }
+
+ if (logLevel == Logger::Fatal)
+ abort();
+}
+
+
+//! Writes the log record
+/**
+ * Writes the log records with the supplied arguments to all the registered appenders.
+ *
+ * \note It is not recommended to call this function directly. Instead of this you can just call one of the macros
+ * (LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_FATAL) that will supply all the needed
+ * information to this function.
+ *
+ * \param timeStamp - the time stamp of the record
+ * \param logLevel - the log level of the record
+ * \param file - the name of the source file that requested the log record
+ * \param line - the line of the code of source file that requested the log record
+ * \param function - name of the function that requested the log record
+ * \param category - logging category (0 for default category)
+ * \param message - log message
+ *
+ * \note Recording of the log record using the Logger::Fatal log level will lead to calling the STL abort()
+ * function, which will interrupt the running of your software and begin the writing of the core dump.
+ *
+ * \sa LogLevel
+ * \sa LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_FATAL
+ * \sa AbstractAppender
+ */
+void Logger::write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function, const char* category,
+ const QString& message)
+{
+ write(timeStamp, logLevel, file, line, function, category, message, /* fromLocalInstance = */ false);
+}
+
+/**
+ * This is the overloaded function provided for the convinience. It behaves similar to the above function.
+ *
+ * This function uses the current timestamp obtained with \c QDateTime::currentDateTime().
+ *
+ * \sa write()
+ */
+void Logger::write(LogLevel logLevel, const char* file, int line, const char* function, const char* category,
+ const QString& message)
+{
+ write(QDateTime::currentDateTime(), logLevel, file, line, function, category, message);
+}
+
+
+//! Writes the assertion
+/**
+ * This function writes the assertion record using the write() function.
+ *
+ * The assertion record is always written using the Logger::Fatal log level which leads to the abortation of the
+ * program and generation of the core dump (if supported).
+ *
+ * The message written to the appenders will be identical to the \c condition argument prefixed with the
+ * <tt>ASSERT:</tt> notification.
+ *
+ * \note It is not recommended to call this function directly. Instead of this you can just call the LOG_ASSERT
+ * macro that will supply all the needed information to this function.
+ *
+ * \sa LOG_ASSERT
+ * \sa write()
+ */
+void Logger::writeAssert(const char* file, int line, const char* function, const char* condition)
+{
+ write(Logger::Fatal, file, line, function, nullptr, QString("ASSERT: \"%1\"").arg(condition));
+}
+
+
+Logger* cuteLoggerInstance()
+{
+ return Logger::globalInstance();
+}
+
+
+
+void LoggerTimingHelper::start(const char* msg, ...)
+{
+ va_list va;
+ va_start(va, msg);
+#if QT_VERSION >= 0x050500
+ m_block = QString().vasprintf(msg, va);
+#else
+ m_block = QString().vsprintf(msg, va);
+#endif
+ va_end(va);
+
+ m_time.start();
+}
+
+
+void LoggerTimingHelper::start(const QString& block)
+{
+ m_block = block;
+ m_time.start();
+}
+
+
+void LoggerTimingHelper::start(Logger::TimingMode mode, const QString& block)
+{
+ m_timingMode = mode;
+ m_block = block;
+ m_time.start();
+}
+
+
+LoggerTimingHelper::~LoggerTimingHelper()
+{
+ QString message;
+ if (m_block.isEmpty())
+ message = QString(QLatin1String("Function %1 finished in ")).arg(AbstractStringAppender::stripFunctionName(m_function));
+ else
+ message = QString(QLatin1String("\"%1\" finished in ")).arg(m_block);
+
+ qint64 elapsed = m_time.elapsed();
+ if (elapsed >= 10000 && m_timingMode == Logger::TimingAuto)
+ message += QString(QLatin1String("%1 s.")).arg(elapsed / 1000);
+ else
+ message += QString(QLatin1String("%1 ms.")).arg(elapsed);
+
+ m_logger->write(m_logLevel, m_file, m_line, m_function, nullptr, message);
+}
+
+
+CuteMessageLogger::~CuteMessageLogger()
+{
+ m_l->write(m_level, m_file, m_line, m_function, m_category, m_message);
+}
+
+void CuteMessageLogger::write(const char* msg, ...)
+{
+ va_list va;
+ va_start(va, msg);
+ m_message = QString::vasprintf(msg, va);
+ va_end(va);
+}
+
+
+void CuteMessageLogger::write(const QString& msg)
+{
+ m_message = msg;
+}
+
+
+QDebug CuteMessageLogger::write()
+{
+ QDebug d(&m_message);
+ return d;
+}