summaryrefslogtreecommitdiffstats
path: root/rbutil/rbutilqt/logger/src
diff options
context:
space:
mode:
Diffstat (limited to 'rbutil/rbutilqt/logger/src')
-rw-r--r--rbutil/rbutilqt/logger/src/AbstractAppender.cpp147
-rw-r--r--rbutil/rbutilqt/logger/src/AbstractStringAppender.cpp459
-rw-r--r--rbutil/rbutilqt/logger/src/ConsoleAppender.cpp64
-rw-r--r--rbutil/rbutilqt/logger/src/FileAppender.cpp116
-rw-r--r--rbutil/rbutilqt/logger/src/Logger.cpp1099
-rw-r--r--rbutil/rbutilqt/logger/src/OutputDebugAppender.cpp43
6 files changed, 1928 insertions, 0 deletions
diff --git a/rbutil/rbutilqt/logger/src/AbstractAppender.cpp b/rbutil/rbutilqt/logger/src/AbstractAppender.cpp
new file mode 100644
index 0000000000..778bbddd11
--- /dev/null
+++ b/rbutil/rbutilqt/logger/src/AbstractAppender.cpp
@@ -0,0 +1,147 @@
+/*
+ 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());
+}
+