summaryrefslogtreecommitdiffstats
path: root/rbutil/rbutilqt/logger/src
diff options
context:
space:
mode:
authorDominik Riebeling <Dominik.Riebeling@gmail.com>2020-06-08 21:13:11 +0200
committerDominik Riebeling <Dominik.Riebeling@gmail.com>2020-08-08 10:01:42 +0200
commit48d2927ecca77ec7f0876960bff624d5d39a4e0f (patch)
tree26a739aed7a63fa1a3cc611f77ef100509ed2138 /rbutil/rbutilqt/logger/src
parentc425d4627ea7a0df3db12600d832de276c691e8b (diff)
downloadrockbox-48d2927ecca77ec7f0876960bff624d5d39a4e0f.tar.gz
rockbox-48d2927ecca77ec7f0876960bff624d5d39a4e0f.zip
rbutil: Update CuteLogger to most recent upstream.
Update to the most recent git version. This changes the folder structure and renames some classes to follow upstream. Restore MSVC static link fix, and fix wrong variable in qmake project file. Change-Id: I874bb9ed60e37af09a841988e771fd341414d145
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 @@
+/*
+ Copyright (c) 2010 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 "AbstractAppender.h"
+
+// Qt
+#include <QMutexLocker>
+
+
+/**
+ * \class AbstractAppender
+ *
+ * \brief The AbstractAppender class provides an abstract base class for writing a log entries.
+ *
+ * The AbstractAppender class is the base interface class for all log appenders that could be used with Logger.
+ *
+ * AbstractAppender provides a common implementation for the thread safe, mutex-protected logging of application
+ * messages, such as ConsoleAppender, FileAppender or something else. AbstractAppender is abstract and can not be
+ * instantiated, but you can use any of its subclasses or create a custom log appender at your choice.
+ *
+ * Appenders are the logical devices that is aimed to be attached to Logger object by calling
+ * Logger::registerAppender(). On each log record call from the application Logger object sequentially calls write()
+ * function on all the appenders registered in it.
+ *
+ * You can subclass AbstractAppender to implement a logging target of any kind you like. It may be the external logging
+ * subsystem (for example, syslog in *nix), XML file, SQL database entries, D-Bus messages or anything else you can
+ * imagine.
+ *
+ * For the simple non-structured plain text logging (for example, to a plain text file or to the console output) you may
+ * like to subclass the AbstractStringAppender instead of AbstractAppender, which will give you a more convinient way to
+ * control the format of the log output.
+ *
+ * \sa AbstractStringAppender
+ * \sa Logger::registerAppender()
+ */
+
+
+//! Constructs a AbstractAppender object.
+AbstractAppender::AbstractAppender()
+ : m_detailsLevel(Logger::Debug)
+{}
+
+
+//! Destructs the AbstractAppender object.
+AbstractAppender::~AbstractAppender()
+{}
+
+
+//! Returns the current details level of appender.
+/**
+ * Log records with a log level lower than a current detailsLevel() will be silently ignored by appender and would not
+ * be sent to its append() function.
+ *
+ * It provides additional logging flexibility, allowing you to set the different severity levels for different types
+ * of logs.
+ *
+ * \note This function is thread safe.
+ *
+ * \sa setDetailsLevel()
+ * \sa Logger::LogLevel
+ */
+Logger::LogLevel AbstractAppender::detailsLevel() const
+{
+ QMutexLocker locker(&m_detailsLevelMutex);
+ return m_detailsLevel;
+}
+
+
+//! Sets the current details level of appender.
+/**
+ * Default details level is Logger::Debug
+ *
+ * \note This function is thread safe.
+ *
+ * \sa detailsLevel()
+ * \sa Logger::LogLevel
+ */
+void AbstractAppender::setDetailsLevel(Logger::LogLevel level)
+{
+ QMutexLocker locker(&m_detailsLevelMutex);
+ m_detailsLevel = level;
+}
+
+
+
+//! Sets the current details level of appender
+/**
+ * This function is provided for convenience, it behaves like an above function.
+ *
+ * \sa detailsLevel()
+ * \sa Logger::LogLevel
+ */
+void AbstractAppender::setDetailsLevel(const QString& level)
+{
+ setDetailsLevel(Logger::levelFromString(level));
+}
+
+
+//! Tries to write the log record to this logger
+/**
+ * This is the function called by Logger object to write a log message to the appender.
+ *
+ * \note This function is thread safe.
+ *
+ * \sa Logger::write()
+ * \sa detailsLevel()
+ */
+void AbstractAppender::write(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
+ const char* function, const QString& category, const QString& message)
+{
+ if (logLevel >= detailsLevel())
+ {
+ QMutexLocker locker(&m_writeMutex);
+ append(timeStamp, logLevel, file, line, function, category, message);
+ }
+}
+
+
+/**
+ * \fn virtual void AbstractAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file,
+ * int line, const char* function, const QString& message)
+ *
+ * \brief Writes the log record to the logger instance
+ *
+ * This function is called every time when user tries to write a message to this AbstractAppender instance using
+ * the write() function. Write function works as proxy and transfers only the messages with log level more or equal
+ * to the current logLevel().
+ *
+ * Overload this function when you are implementing a custom appender.
+ *
+ * \note This function is not needed to be thread safe because it is never called directly by Logger object. The
+ * write() function works as a proxy and protects this function from concurrent access.
+ *
+ * \sa Logger::write()
+ */
+
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 @@
+/*
+ Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com) Nikolay Matyunin (matyunin.n at gmail dot com)
+
+ Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
+
+ 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 "AbstractStringAppender.h"
+
+// Qt
+#include <QReadLocker>
+#include <QWriteLocker>
+#include <QDateTime>
+#include <QRegExp>
+#include <QCoreApplication>
+#include <QThread>
+
+
+/**
+ * \class AbstractStringAppender
+ *
+ * \brief The AbstractStringAppender class provides a convinient base for appenders working with plain text formatted
+ * logs.
+ *
+ * AbstractSringAppender is the simple extension of the AbstractAppender class providing the convinient way to create
+ * custom log appenders working with a plain text formatted log targets.
+ *
+ * It have the formattedString() protected function that formats the logging arguments according to a format set with
+ * setFormat().
+ *
+ * This class can not be directly instantiated because it contains pure virtual function inherited from AbstractAppender
+ * class.
+ *
+ * For more detailed description of customizing the log output format see the documentation on the setFormat() function.
+ */
+
+
+const char formattingMarker = '%';
+
+
+//! Constructs a new string appender object
+AbstractStringAppender::AbstractStringAppender()
+ : m_format(QLatin1String("%{time}{yyyy-MM-ddTHH:mm:ss.zzz} [%{type:-7}] <%{function}> %{message}\n"))
+{}
+
+
+//! Returns the current log format string.
+/**
+ * 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
+ * format using the setFormat() function.
+ *
+ * \sa setFormat(const QString&)
+ */
+QString AbstractStringAppender::format() const
+{
+ QReadLocker locker(&m_formatLock);
+ return m_format;
+}
+
+
+//! Sets the logging format for writing strings to the log target with this appender.
+/**
+ * The string format seems to be very common to those developers who have used a standart sprintf function.
+ *
+ * Log output format is a simple QString with the special markers (starting with % sign) which will be replaced with
+ * it's internal meaning when writing a log record.
+ *
+ * Controlling marker begins with the percent sign (%) which is followed by the command inside {} brackets
+ * (the command describes, what will be put to log record instead of marker).
+ * Optional field width argument may be specified right after the command (through the colon symbol before the closing bracket)
+ * Some commands requires an additional formatting argument (in the second {} brackets).
+ *
+ * Field width argument works almost identically to the \c QString::arg() \c fieldWidth argument (and uses it
+ * internally). For example, \c "%{type:-7}" will be replaced with the left padded debug level of the message
+ * (\c "Debug ") or something. For the more detailed description of it you may consider to look to the Qt
+ * Reference Documentation.
+ *
+ * Supported marker commands are:
+ * \arg \c %{time} - timestamp. You may specify your custom timestamp format using the second {} brackets after the marker,
+ * timestamp format here will be similiar to those used in QDateTime::toString() function. For example,
+ * "%{time}{dd-MM-yyyy, HH:mm}" may be replaced with "17-12-2010, 20:17" depending on current date and time.
+ * The default format used here is "HH:mm:ss.zzz".
+ * \arg \c %{type} - Log level. Possible log levels are shown in the Logger::LogLevel enumerator.
+ * \arg \c %{Type} - Uppercased log level.
+ * \arg \c %{typeOne} - One letter log level.
+ * \arg \c %{TypeOne} - One uppercase letter log level.
+ * \arg \c %{File} - Full source file name (with path) of the file that requested log recording. Uses the \c __FILE__
+ * preprocessor macro.
+ * \arg \c %{file} - Short file name (with stripped path).
+ * \arg \c %{line} - Line number in the source file. Uses the \c __LINE__ preprocessor macro.
+ * \arg \c %{Function} - Name of function that called on of the LOG_* macros. Uses the \c Q_FUNC_INFO macro provided with
+ * Qt.
+ * \arg \c %{function} - Similiar to the %{Function}, but the function name is stripped using stripFunctionName
+ * \arg \c %{message} - The log message sent by the caller.
+ * \arg \c %{category} - The log category.
+ * \arg \c %{appname} - Application name (returned by QCoreApplication::applicationName() function).
+ * \arg \c %{pid} - Application pid (returned by QCoreApplication::applicationPid() function).
+ * \arg \c %{threadid} - ID of current thread.
+ * \arg \c %% - Convinient marker that is replaced with the single \c % mark.
+ *
+ * \note Format doesn't add \c '\\n' to the end of the format line. Please consider adding it manually.
+ *
+ * \sa format()
+ * \sa stripFunctionName()
+ * \sa Logger::LogLevel
+ */
+void AbstractStringAppender::setFormat(const QString& format)
+{
+ QWriteLocker locker(&m_formatLock);
+ m_format = format;
+}
+
+
+//! Strips the long function signature (as added by Q_FUNC_INFO macro)
+/**
+ * The string processing drops the returning type, arguments and template parameters of function. It is definitely
+ * useful for enchancing the log output readability.
+ * \return stripped function name
+ */
+QString AbstractStringAppender::stripFunctionName(const char* name)
+{
+ return QString::fromLatin1(qCleanupFuncinfo(name));
+}
+
+
+// The function was backported from Qt5 sources (qlogging.h)
+QByteArray AbstractStringAppender::qCleanupFuncinfo(const char* name)
+{
+ QByteArray info(name);
+
+ // Strip the function info down to the base function name
+ // note that this throws away the template definitions,
+ // the parameter types (overloads) and any const/volatile qualifiers.
+ if (info.isEmpty())
+ return info;
+
+ int pos;
+
+ // skip trailing [with XXX] for templates (gcc)
+ pos = info.size() - 1;
+ if (info.endsWith(']')) {
+ while (--pos) {
+ if (info.at(pos) == '[')
+ info.truncate(pos);
+ }
+ }
+
+ bool hasLambda = false;
+ QRegExp lambdaRegex("::<lambda\\(.*\\)>");
+ int lambdaIndex = lambdaRegex.indexIn(QString::fromLatin1(info));
+ if (lambdaIndex != -1)
+ {
+ hasLambda = true;
+ info.remove(lambdaIndex, lambdaRegex.matchedLength());
+ }
+
+ // operator names with '(', ')', '<', '>' in it
+ static const char operator_call[] = "operator()";
+ static const char operator_lessThan[] = "operator<";
+ static const char operator_greaterThan[] = "operator>";
+ static const char operator_lessThanEqual[] = "operator<=";
+ static const char operator_greaterThanEqual[] = "operator>=";
+
+ // canonize operator names
+ info.replace("operator ", "operator");
+
+ // remove argument list
+ forever {
+ int parencount = 0;
+ pos = info.lastIndexOf(')');
+ if (pos == -1) {
+ // Don't know how to parse this function name
+ return info;
+ }
+
+ // find the beginning of the argument list
+ --pos;
+ ++parencount;
+ while (pos && parencount) {
+ if (info.at(pos) == ')')
+ ++parencount;
+ else if (info.at(pos) == '(')
+ --parencount;
+ --pos;
+ }
+ if (parencount != 0)
+ return info;
+
+ info.truncate(++pos);
+
+ if (info.at(pos - 1) == ')') {
+ if (info.indexOf(operator_call) == pos - (int)strlen(operator_call))
+ break;
+
+ // this function returns a pointer to a function
+ // and we matched the arguments of the return type's parameter list
+ // try again
+ info.remove(0, info.indexOf('('));
+ info.chop(1);
+ continue;
+ } else {
+ break;
+ }
+ }
+
+ if (hasLambda)
+ info.append("::lambda");
+
+ // find the beginning of the function name
+ int parencount = 0;
+ int templatecount = 0;
+ --pos;
+
+ // make sure special characters in operator names are kept
+ if (pos > -1) {
+ switch (info.at(pos)) {
+ case ')':
+ if (info.indexOf(operator_call) == pos - (int)strlen(operator_call) + 1)
+ pos -= 2;
+ break;
+ case '<':
+ if (info.indexOf(operator_lessThan) == pos - (int)strlen(operator_lessThan) + 1)
+ --pos;
+ break;
+ case '>':
+ if (info.indexOf(operator_greaterThan) == pos - (int)strlen(operator_greaterThan) + 1)
+ --pos;
+ break;
+ case '=': {
+ int operatorLength = (int)strlen(operator_lessThanEqual);
+ if (info.indexOf(operator_lessThanEqual) == pos - operatorLength + 1)
+ pos -= 2;
+ else if (info.indexOf(operator_greaterThanEqual) == pos - operatorLength + 1)
+ pos -= 2;
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ while (pos > -1) {
+ if (parencount < 0 || templatecount < 0)
+ return info;
+
+ char c = info.at(pos);
+ if (c == ')')
+ ++parencount;
+ else if (c == '(')
+ --parencount;
+ else if (c == '>')
+ ++templatecount;
+ else if (c == '<')
+ --templatecount;
+ else if (c == ' ' && templatecount == 0 && parencount == 0)
+ break;
+
+ --pos;
+ }
+ info = info.mid(pos + 1);
+
+ // remove trailing '*', '&' that are part of the return argument
+ while ((info.at(0) == '*')
+ || (info.at(0) == '&'))
+ info = info.mid(1);
+
+ // we have the full function name now.
+ // clean up the templates
+ while ((pos = info.lastIndexOf('>')) != -1) {
+ if (!info.contains('<'))
+ break;
+
+ // find the matching close
+ int end = pos;
+ templatecount = 1;
+ --pos;
+ while (pos && templatecount) {
+ char c = info.at(pos);
+ if (c == '>')
+ ++templatecount;
+ else if (c == '<')
+ --templatecount;
+ --pos;
+ }
+ ++pos;
+ info.remove(pos, end - pos + 1);
+ }
+
+ return info;
+}
+
+
+//! Returns the string to record to the logging target, formatted according to the format().
+/**
+ * \sa format()
+ * \sa setFormat(const QString&)
+ */
+QString AbstractStringAppender::formattedString(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file,
+ int line, const char* function, const QString& category, const QString& message) const
+{
+ QString f = format();
+ const int size = f.size();
+
+ QString result;
+
+ int i = 0;
+ while (i < f.size())
+ {
+ QChar c = f.at(i);
+
+ // We will silently ignore the broken % marker at the end of string
+ if (c != QLatin1Char(formattingMarker) || (i + 2) >= size)
+ {
+ result.append(c);
+ }
+ else
+ {
+ i += 2;
+ QChar currentChar = f.at(i);
+ QString command;
+ int fieldWidth = 0;
+
+ if (currentChar.isLetter())
+ {
+ command.append(currentChar);
+ int j = 1;
+ while ((i + j) < size && f.at(i + j).isLetter())
+ {
+ command.append(f.at(i+j));
+ j++;
+ }
+
+ i+=j;
+ currentChar = f.at(i);
+
+ // Check for the padding instruction
+ if (currentChar == QLatin1Char(':'))
+ {
+ currentChar = f.at(++i);
+ if (currentChar.isDigit() || currentChar.category() == QChar::Punctuation_Dash)
+ {
+ int j = 1;
+ while ((i + j) < size && f.at(i + j).isDigit())
+ j++;
+ fieldWidth = f.mid(i, j).toInt();
+
+ i += j;
+ }
+ }
+ }
+
+ // Log record chunk to insert instead of formatting instruction
+ QString chunk;
+
+ // Time stamp
+ if (command == QLatin1String("time"))
+ {
+ if (f.at(i + 1) == QLatin1Char('{'))
+ {
+ int j = 1;
+ while ((i + 2 + j) < size && f.at(i + 2 + j) != QLatin1Char('}'))
+ j++;
+
+ if ((i + 2 + j) < size)
+ {
+ chunk = timeStamp.toString(f.mid(i + 2, j));
+
+ i += j;
+ i += 2;
+ }
+ }
+
+ if (chunk.isNull())
+ chunk = timeStamp.toString(QLatin1String("HH:mm:ss.zzz"));
+ }
+
+ // Log level
+ else if (command == QLatin1String("type"))
+ chunk = Logger::levelToString(logLevel);
+
+ // Uppercased log level
+ else if (command == QLatin1String("Type"))
+ chunk = Logger::levelToString(logLevel).toUpper();
+
+ // One letter log level
+ else if (command == QLatin1String("typeOne"))
+ chunk = Logger::levelToString(logLevel).left(1).toLower();
+
+ // One uppercase letter log level
+ else if (command == QLatin1String("TypeOne"))
+ chunk = Logger::levelToString(logLevel).left(1).toUpper();
+
+ // Filename
+ else if (command == QLatin1String("File"))
+ chunk = QLatin1String(file);
+
+ // Filename without a path
+ else if (command == QLatin1String("file"))
+ chunk = QString(QLatin1String(file)).section(QRegExp("[/\\\\]"), -1);
+
+ // Source line number
+ else if (command == QLatin1String("line"))
+ chunk = QString::number(line);
+
+ // Function name, as returned by Q_FUNC_INFO
+ else if (command == QLatin1String("Function"))
+ chunk = QString::fromLatin1(function);
+
+ // Stripped function name
+ else if (command == QLatin1String("function"))
+ chunk = stripFunctionName(function);
+
+ // Log message
+ else if (command == QLatin1String("message"))
+ chunk = message;
+
+ else if (command == QLatin1String("category"))
+ chunk = category;
+
+ // Application pid
+ else if (command == QLatin1String("pid"))
+ chunk = QString::number(QCoreApplication::applicationPid());
+
+ // Appplication name
+ else if (command == QLatin1String("appname"))
+ chunk = QCoreApplication::applicationName();
+
+ // Thread ID (duplicates Qt5 threadid debbuging way)
+ else if (command == QLatin1String("threadid"))
+ chunk = QLatin1String("0x") + QString::number(qlonglong(QThread::currentThread()->currentThread()), 16);
+
+ // We simply replace the double formatting marker (%) with one
+ else if (command == QString(formattingMarker))
+ chunk = QLatin1Char(formattingMarker);
+
+ // Do not process any unknown commands
+ else
+ {
+ chunk = QString(formattingMarker);
+ chunk.append(command);
+ }
+
+ result.append(QString(QLatin1String("%1")).arg(chunk, fieldWidth));
+ }
+
+ ++i;
+ }
+
+ return result;
+}
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 @@
+/*
+ Copyright (c) 2010 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 "ConsoleAppender.h"
+
+// STL
+#include <iostream>
+
+
+/**
+ * \class ConsoleAppender
+ *
+ * \brief ConsoleAppender is the simple appender that writes the log records to the std::cerr output stream.
+ *
+ * ConsoleAppender uses "[%{type:-7}] <%{function}> %{message}\n" as a default output format. It is similar to the
+ * AbstractStringAppender but doesn't show a timestamp.
+ *
+ * You can modify ConsoleAppender output format without modifying your code by using \c QT_MESSAGE_PATTERN environment
+ * variable. If you need your application to ignore this environment variable you can call
+ * ConsoleAppender::ignoreEnvironmentPattern(true)
+ */
+
+
+ConsoleAppender::ConsoleAppender()
+ : AbstractStringAppender()
+ , m_ignoreEnvPattern(false)
+{
+ setFormat("[%{type:-7}] <%{function}> %{message}\n");
+}
+
+
+QString ConsoleAppender::format() const
+{
+ const QString envPattern = QString::fromLocal8Bit(qgetenv("QT_MESSAGE_PATTERN"));
+ return (m_ignoreEnvPattern || envPattern.isEmpty()) ? AbstractStringAppender::format() : (envPattern + "\n");
+}
+
+
+void ConsoleAppender::ignoreEnvironmentPattern(bool ignore)
+{
+ m_ignoreEnvPattern = ignore;
+}
+
+
+//! Writes the log record to the std::cerr stream.
+/**
+ * \sa AbstractStringAppender::format()
+ */
+void ConsoleAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
+ const char* function, const QString& category, const QString& message)
+{
+ std::cerr << qPrintable(formattedString(timeStamp, logLevel, file, line, function, category, message));
+}
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 @@
+/*
+ Copyright (c) 2010 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 "FileAppender.h"
+
+// STL
+#include <iostream>
+
+/**
+ * \class FileAppender
+ *
+ * \brief Simple appender that writes the log records to the plain text file.
+ */
+
+
+//! Constructs the new file appender assigned to file with the given name.
+FileAppender::FileAppender(const QString& fileName)
+{
+ setFileName(fileName);
+}
+
+
+FileAppender::~FileAppender()
+{
+ closeFile();
+}
+
+
+//! Returns the name set by setFileName() or to the FileAppender constructor.
+/**
+ * \sa setFileName()
+ */
+QString FileAppender::fileName() const
+{
+ QMutexLocker locker(&m_logFileMutex);
+ return m_logFile.fileName();
+}
+
+
+//! Sets the name of the file. The name can have no path, a relative path, or an absolute path.
+/**
+ * \sa fileName()
+ */
+void FileAppender::setFileName(const QString& s)
+{
+ if (s.isEmpty())
+ std::cerr << "<FileAppender::FileAppender> File name is empty. The appender will do nothing" << std::endl;
+
+ QMutexLocker locker(&m_logFileMutex);
+ if (m_logFile.isOpen())
+ m_logFile.close();
+
+ m_logFile.setFileName(s);
+}
+
+
+bool FileAppender::reopenFile()
+{
+ closeFile();
+ return openFile();
+}
+
+
+bool FileAppender::openFile()
+{
+ if (m_logFile.fileName().isEmpty())
+ return false;
+
+ bool isOpen = m_logFile.isOpen();
+ if (!isOpen)
+ {
+ isOpen = m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
+ if (isOpen)
+ m_logStream.setDevice(&m_logFile);
+ else
+ std::cerr << "<FileAppender::append> Cannot open the log file " << qPrintable(m_logFile.fileName()) << std::endl;
+ }
+ return isOpen;
+}
+
+
+//! Write the log record to the file.
+/**
+ * \sa fileName()
+ * \sa AbstractStringAppender::format()
+ */
+void FileAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
+ const char* function, const QString& category, const QString& message)
+{
+ QMutexLocker locker(&m_logFileMutex);
+
+ if (openFile())
+ {
+ m_logStream << formattedString(timeStamp, logLevel, file, line, function, category, message);
+ m_logStream.flush();
+ m_logFile.flush();
+ }
+}
+
+
+void FileAppender::closeFile()
+{
+ QMutexLocker locker(&m_logFileMutex);
+ m_logFile.close();
+}
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 @@
+/*
+ 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);
+ QSet<AbstractAppender*> deleteList(QSet<AbstractAppender*>::fromList(d->appenders));
+ deleteList.unite(QSet<AbstractAppender*>::fromList(d->categoryAppenders.values()));
+ 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);
+ m_block = QString().vsprintf(msg, va);
+ 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);
+
+ int 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;
+}
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 @@
+/*
+ Copyright (c) 2010 Karl-Heinz Reichel (khreichel at googlemail 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 "OutputDebugAppender.h"
+
+// STL
+#include <windows.h>
+
+
+/**
+ * \class OutputDebugAppender
+ *
+ * \brief Appender that writes the log records to the Microsoft Debug Log
+ */
+
+
+//! Writes the log record to the windows debug log.
+/**
+ * \sa AbstractStringAppender::format()
+ */
+void OutputDebugAppender::append(const QDateTime& timeStamp,
+ Logger::LogLevel logLevel,
+ const char* file,
+ int line,
+ const char* function,
+ const QString& category,
+ const QString& message)
+{
+ QString s = formattedString(timeStamp, logLevel, file, line, function, category, message);
+ OutputDebugStringW((LPCWSTR) s.utf16());
+}
+