diff options
Diffstat (limited to 'rbutil/rbutilqt')
24 files changed, 2201 insertions, 1242 deletions
diff --git a/rbutil/rbutilqt/logger/AbstractAppender.cpp b/rbutil/rbutilqt/logger/AbstractAppender.cpp deleted file mode 100644 index de86b930d0..0000000000 --- a/rbutil/rbutilqt/logger/AbstractAppender.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - 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> - - -AbstractAppender::AbstractAppender() - : m_detailsLevel(Logger::Debug) -{} - - -AbstractAppender::~AbstractAppender() -{} - - -Logger::LogLevel AbstractAppender::detailsLevel() const -{ - QMutexLocker locker(&m_detailsLevelMutex); - return m_detailsLevel; -} - - -void AbstractAppender::setDetailsLevel(Logger::LogLevel level) -{ - QMutexLocker locker(&m_detailsLevelMutex); - m_detailsLevel = level; -} - - -void AbstractAppender::setDetailsLevel(const QString& level) -{ - setDetailsLevel(Logger::levelFromString(level)); -} - - -void AbstractAppender::write(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, - const char* function, const QString& message) -{ - if (logLevel >= detailsLevel()) - { - QMutexLocker locker(&m_writeMutex); - append(timeStamp, logLevel, file, line, function, message); - } -} diff --git a/rbutil/rbutilqt/logger/AbstractAppender.h b/rbutil/rbutilqt/logger/AbstractAppender.h deleted file mode 100644 index df1df4957c..0000000000 --- a/rbutil/rbutilqt/logger/AbstractAppender.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - 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. -*/ -#ifndef ABSTRACTAPPENDER_H -#define ABSTRACTAPPENDER_H - -// Local -#include "CuteLogger_global.h" -#include <Logger.h> - -// Qt -#include <QMutex> - -//! 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() - */ -class CUTELOGGERSHARED_EXPORT AbstractAppender -{ - public: - //! Constructs a AbstractAppender object. - AbstractAppender(); - - //! Destructs the AbstractAppender object. - virtual ~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 detailsLevel() const; - - //! Sets the current details level of appender. - /** - * \note This function is thread safe. - * - * \sa detalsLevel() - * \sa Logger::LogLevel - */ - void setDetailsLevel(Logger::LogLevel level); - - //! Sets the current details level of appender - /** - * This function is provided for convinience, it behaves like an above function. - * - * \sa detalsLevel() - * \sa Logger::LogLevel - */ - void setDetailsLevel(const QString& 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 write(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, const char* function, - const QString& message); - - protected: - //! 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() - */ - virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, - const char* function, const QString& message) = 0; - - private: - QMutex m_writeMutex; - - Logger::LogLevel m_detailsLevel; - mutable QMutex m_detailsLevelMutex; -}; - -#endif // ABSTRACTAPPENDER_H diff --git a/rbutil/rbutilqt/logger/AbstractStringAppender.cpp b/rbutil/rbutilqt/logger/AbstractStringAppender.cpp deleted file mode 100644 index 073ecb7782..0000000000 --- a/rbutil/rbutilqt/logger/AbstractStringAppender.cpp +++ /dev/null @@ -1,161 +0,0 @@ -/* - 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 "AbstractStringAppender.h" - -// Qt -#include <QReadLocker> -#include <QWriteLocker> -#include <QDateTime> -#include <QRegExp> - -const char formattingMarker = '%'; - -AbstractStringAppender::AbstractStringAppender() - : m_format(QLatin1String("%t{yyyy-MM-ddTHH:mm:ss.zzz} [%-7l] <%c> %m\n")) -{} - - -QString AbstractStringAppender::format() const -{ - QReadLocker locker(&m_formatLock); - return m_format; -} - - -void AbstractStringAppender::setFormat(const QString& format) -{ - QWriteLocker locker(&m_formatLock); - m_format = format; -} - - -QString AbstractStringAppender::stripFunctionName(const char* name) -{ - QRegExp rx("^.+\\s((?:[\\w\\d]+::)+)?([\\w\\d\\<\\>~]+)(?:\\(.*\\)).*$"); // XXX: SLOW! - return QString::fromLatin1(name).replace(rx, QString(QLatin1String("\\1\\2"))); -} - - -QString AbstractStringAppender::formattedString(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, - int line, const char* function, 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 + 1) == size) - { - result.append(c); - } - else - { - QChar command = f.at(++i); - - // Check for the padding instruction - int fieldWidth = 0; - if (command.isDigit() || command.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; - command = f.at(i); - } - - // Log record chunk to insert instead of formatting instruction - QString chunk; - - // Time stamp - if (command == QLatin1Char('t')) - { - 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 == QLatin1Char('l')) - chunk = Logger::levelToString(logLevel); - - // Uppercased log level - else if (command == QLatin1Char('L')) - chunk = Logger::levelToString(logLevel).toUpper(); - - // Filename - else if (command == QLatin1Char('F')) - chunk = QLatin1String(file); - - // Filename without a path - else if (command == QLatin1Char('f')) - chunk = QString(QLatin1String(file)).section('/', -1); - - // Source line number - else if (command == QLatin1Char('i')) - chunk = QString::number(line); - - // Function name, as returned by Q_FUNC_INFO - else if (command == QLatin1Char('C')) - chunk = QString::fromLatin1(function); - - // Stripped function name - else if (command == QLatin1Char('c')) - chunk = stripFunctionName(function); - - // Log message - else if (command == QLatin1Char('m')) - chunk = message; - - // We simply replace the double formatting marker (%) with one - else if (command == QLatin1Char(formattingMarker)) - chunk = QLatin1Char(formattingMarker); - - // Do not process any unknown commands - else - { - chunk = QLatin1Char(formattingMarker); - chunk.append(command); - } - - result.append(QString(QLatin1String("%1")).arg(chunk, fieldWidth)); - } - - ++i; - } - - return result; -} diff --git a/rbutil/rbutilqt/logger/AbstractStringAppender.h b/rbutil/rbutilqt/logger/AbstractStringAppender.h deleted file mode 100644 index 3cef63bff9..0000000000 --- a/rbutil/rbutilqt/logger/AbstractStringAppender.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - 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. -*/ -#ifndef ABSTRACTSTRINGAPPENDER_H -#define ABSTRACTSTRINGAPPENDER_H - -// Local -#include "CuteLogger_global.h" -#include <AbstractAppender.h> - -// Qt -#include <QReadWriteLock> - - -//! 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. - */ -class CUTELOGGERSHARED_EXPORT AbstractStringAppender : public AbstractAppender -{ - public: - //! Constructs a new string appender object - AbstractStringAppender(); - - //! Returns the current log format string. - /** - * The default format is set to "%t{yyyy-MM-ddTHH:mm:ss.zzz} [%-7l] <%C> %m\n". You can set a different log record - * format using the setFormat() function. - * - * \sa setFormat(const QString&) - */ - QString format() const; - - //! 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 (optional) field width argument, the - * (necessary) single-letter command (which describes, what will be put to log record instead of marker, and an - * additional formatting argument (in the {} brackets) supported for some of the log commands. - * - * Field width argument works almost identically to the \c QString::arg() \c fieldWidth argument (and uses it - * internally). For example, \c "%-7l" 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 %t - timestamp. You may specify your custom timestamp format using the {} brackets after the marker, - * timestamp format here will be similiar to those used in QDateTime::toString() function. For example, - * "%t{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 %l - Log level. Possible log levels are shown in the Logger::LogLevel enumerator. - * \arg \c %L - Uppercased log level. - * \arg \c %F - Full source file name (with path) of the file that requested log recording. Uses the \c __FILE__ - * preprocessor macro. - * \arg \c %f - Short file name (with stripped path). - * \arg \c %i - Line number in the source file. Uses the \c __LINE__ preprocessor macro. - * \arg \c %C - Name of function that called on of the LOG_* macros. Uses the \c Q_FUNC_INFO macro provided with - * Qt. - * \arg \c %c - [EXPERIMENTAL] Similiar to the %C, but the function name is stripped using stripFunctionName - * \arg \c %m - The log message sent by the caller. - * \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 setFormat(const QString&); - - //! 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 - */ - static QString stripFunctionName(const char*); - - protected: - //! Returns the string to record to the logging target, formatted according to the format(). - /** - * \sa format() - * \sa setFormat(const QString&) - */ - QString formattedString(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, - const char* function, const QString& message) const; - - private: - QString m_format; - mutable QReadWriteLock m_formatLock; -}; - -#endif // ABSTRACTSTRINGAPPENDER_H diff --git a/rbutil/rbutilqt/logger/ConsoleAppender.cpp b/rbutil/rbutilqt/logger/ConsoleAppender.cpp deleted file mode 100644 index da4a43c740..0000000000 --- a/rbutil/rbutilqt/logger/ConsoleAppender.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - 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> - - -void ConsoleAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, - const char* function, const QString& message) -{ - std::cerr << qPrintable(formattedString(timeStamp, logLevel, file, line, function, message)); -} diff --git a/rbutil/rbutilqt/logger/Logger.cpp b/rbutil/rbutilqt/logger/Logger.cpp deleted file mode 100644 index 33ba50ec58..0000000000 --- a/rbutil/rbutilqt/logger/Logger.cpp +++ /dev/null @@ -1,370 +0,0 @@ -/* - 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 "Logger.h" -#include "AbstractAppender.h" - -// Qt -#include <QCoreApplication> -#include <QReadWriteLock> -#include <QSemaphore> -#include <QDateTime> -#include <QIODevice> -#include <QTextCodec> - -// STL -#include <iostream> - - -class LogDevice : public QIODevice -{ - public: - LogDevice() - : m_semaphore(1) - {} - - void lock(Logger::LogLevel logLevel, const char* file, int line, const char* function) - { - m_semaphore.acquire(); - - if (!isOpen()) - open(QIODevice::WriteOnly); - - m_logLevel = logLevel; - m_file = file; - m_line = line; - m_function = function; - } - - protected: - qint64 readData(char*, qint64) - { - return 0; - } - - qint64 writeData(const char* data, qint64 maxSize) - { - if (maxSize > 0) - Logger::write(m_logLevel, m_file, m_line, m_function, QString::fromLocal8Bit(QByteArray(data, maxSize))); - - m_semaphore.release(); - return maxSize; - } - - private: - QSemaphore m_semaphore; - Logger::LogLevel m_logLevel; - const char* m_file; - int m_line; - const char* m_function; -}; - - -// Forward declarations -static void cleanupLoggerPrivate(); - -#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 uses a static pointer to itself - * protected by QReadWriteLock - * - * The appender list inside the LoggerPrivate class is also protected by QReadWriteLock so this class could be safely - * used in a multi-threaded application. - */ -class LoggerPrivate -{ - public: - static LoggerPrivate* m_self; - static QReadWriteLock m_selfLock; - - static LoggerPrivate* instance() - { - LoggerPrivate* result = 0; - { - QReadLocker locker(&m_selfLock); - result = m_self; - } - - if (!result) - { - QWriteLocker locker(&m_selfLock); - m_self = new LoggerPrivate; - -#if QT_VERSION >= 0x050000 - qInstallMessageHandler(qtLoggerMessageHandler); -#else - qInstallMsgHandler(qtLoggerMessageHandler); -#endif - qAddPostRoutine(cleanupLoggerPrivate); - result = m_self; - } - - return result; - } - - - LoggerPrivate() - : m_logDevice(0) - {} - - - ~LoggerPrivate() - { - // Cleanup appenders - QReadLocker appendersLocker(&m_appendersLock); - foreach (AbstractAppender* appender, m_appenders) - delete appender; - - // Cleanup device - QReadLocker deviceLocker(&m_logDeviceLock); - delete m_logDevice; - } - - - void registerAppender(AbstractAppender* appender) - { - QWriteLocker locker(&m_appendersLock); - - if (!m_appenders.contains(appender)) - m_appenders.append(appender); - else - std::cerr << "Trying to register appender that was already registered" << std::endl; - } - - - LogDevice* logDevice() - { - LogDevice* result = 0; - { - QReadLocker locker(&m_logDeviceLock); - result = m_logDevice; - } - - if (!result) - { - QWriteLocker locker(&m_logDeviceLock); - m_logDevice = new LogDevice; - result = m_logDevice; - } - - return result; - } - - - void write(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, const char* function, - const QString& message) - { - QReadLocker locker(&m_appendersLock); - - if (!m_appenders.isEmpty()) - { - foreach (AbstractAppender* appender, m_appenders) - appender->write(timeStamp, logLevel, file, line, function, message); - } - else - { - // Fallback - QString result = QString(QLatin1String("[%1] <%2> %3")).arg(Logger::levelToString(logLevel), -7) - .arg(function).arg(message); - - std::cerr << qPrintable(result) << std::endl; - } - - if (logLevel == Logger::Fatal) - abort(); - } - - - void write(Logger::LogLevel logLevel, const char* file, int line, const char* function, const QString& message) - { - write(QDateTime::currentDateTime(), logLevel, file, line, function, message); - } - - - void write(Logger::LogLevel logLevel, const char* file, int line, const char* function, const char* message) - { - write(logLevel, file, line, function, QString(message)); - } - - - QDebug write(Logger::LogLevel logLevel, const char* file, int line, const char* function) - { - LogDevice* d = logDevice(); - d->lock(logLevel, file, line, function); - return QDebug(d); - } - - - void writeAssert(const char* file, int line, const char* function, const char* condition) - { - write(Logger::Fatal, file, line, function, QString("ASSERT: \"%1\"").arg(condition)); - } - - private: - QList<AbstractAppender*> m_appenders; - QReadWriteLock m_appendersLock; - - LogDevice* m_logDevice; - QReadWriteLock m_logDeviceLock; -}; - -// Static fields initialization -LoggerPrivate* LoggerPrivate::m_self = 0; -QReadWriteLock LoggerPrivate::m_selfLock; - - -static void cleanupLoggerPrivate() -{ - QWriteLocker locker(&LoggerPrivate::m_selfLock); - - delete LoggerPrivate::m_self; - LoggerPrivate::m_self = 0; -} - - -#if QT_VERSION >= 0x050000 -static void qtLoggerMessageHandler(QtMsgType type, const QMessageLogContext& context, const QString& msg) -{ - Logger::LogLevel level; - switch (type) - { - case QtDebugMsg: - level = Logger::Debug; - break; - case QtWarningMsg: - level = Logger::Warning; - break; - case QtCriticalMsg: - level = Logger::Error; - break; - case QtFatalMsg: - level = Logger::Fatal; - break; - } - - Logger::write(level, context.file, context.line, context.function, msg); -} - -#else - -static void qtLoggerMessageHandler(QtMsgType type, const char* msg) -{ - switch (type) - { - case QtDebugMsg: - LOG_DEBUG(msg); - break; - case QtWarningMsg: - LOG_WARNING(msg); - break; - case QtCriticalMsg: - LOG_ERROR(msg); - break; - case QtFatalMsg: - LOG_FATAL(msg); - break; - } -} -#endif - - -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(); -} - - -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; -} - - -void Logger::registerAppender(AbstractAppender* appender) -{ - LoggerPrivate::instance()->registerAppender(appender); -} - - -void Logger::write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function, - const QString& message) -{ - LoggerPrivate::instance()->write(timeStamp, logLevel, file, line, function, message); -} - - -void Logger::write(LogLevel logLevel, const char* file, int line, const char* function, const QString& message) -{ - LoggerPrivate::instance()->write(logLevel, file, line, function, message); -} - - -void Logger::write(LogLevel logLevel, const char* file, int line, const char* function, const char* message, ...) -{ - va_list va; - va_start(va, message); - LoggerPrivate::instance()->write(logLevel, file, line, function, QString().vsprintf(message,va)); - va_end(va); -} - -QDebug Logger::write(LogLevel logLevel, const char* file, int line, const char* function) -{ - return LoggerPrivate::instance()->write(logLevel, file, line, function); -} - - -void Logger::writeAssert(const char* file, int line, const char* function, const char* condition) -{ - LoggerPrivate::instance()->writeAssert(file, line, function, condition); -} diff --git a/rbutil/rbutilqt/logger/Logger.h b/rbutil/rbutilqt/logger/Logger.h deleted file mode 100644 index d056dfc25d..0000000000 --- a/rbutil/rbutilqt/logger/Logger.h +++ /dev/null @@ -1,319 +0,0 @@ -/* - 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. -*/ -#ifndef LOGGER_H -#define LOGGER_H -/** - * \file Logger.h - * \brief A file containing the description of Logger class and and additional useful macros for logging - */ - -// Qt -#include <QString> -#include <QDebug> -class QDateTime; - -// Local -#include "CuteLogger_global.h" -class AbstractAppender; - - -//! 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. - * - * It is checked to work with GCC 4.4 or later. - * - * \sa Logger::LogLevel - * \sa Logger::write() - */ -#define LOG_TRACE(...) Logger::write(Logger::Trace, __FILE__, __LINE__, Q_FUNC_INFO, ##__VA_ARGS__) - -//! Writes the debug log record -/** - * This macro records the info log record using the Logger::write() function. It works identically to the LOG_TRACE() - * macro. - * - * \sa LOG_TRACE() - * \sa Logger::LogLevel - * \sa Logger::write() - */ -#define LOG_DEBUG(...) Logger::write(Logger::Debug, __FILE__, __LINE__, Q_FUNC_INFO, ##__VA_ARGS__) - -//! Write the info log record -/** - * This macro records the info log record using the Logger::write() function. It works identically to the LOG_TRACE() - * macro. - * - * \sa LOG_TRACE() - * \sa Logger::LogLevel - * \sa Logger::write() - */ -#define LOG_INFO(...) Logger::write(Logger::Info, __FILE__, __LINE__, Q_FUNC_INFO, ##__VA_ARGS__) - -//! Write the warning log record -/** - * This macro records the warning log record using the Logger::write() function. It works identically to the LOG_TRACE() - * macro. - * - * \sa LOG_TRACE() - * \sa Logger::LogLevel - * \sa Logger::write() - */ -#define LOG_WARNING(...) Logger::write(Logger::Warning, __FILE__, __LINE__, Q_FUNC_INFO, ##__VA_ARGS__) - -//! Write the error log record -/** - * This macro records the error log record using the Logger::write() function. It works identically to the LOG_TRACE() - * macro. - * - * \sa LOG_TRACE() - * \sa Logger::LogLevel - * \sa Logger::write() - */ -#define LOG_ERROR(...) Logger::write(Logger::Error, __FILE__, __LINE__, Q_FUNC_INFO, ##__VA_ARGS__) - -//! Write the fatal log record -/** - * This macro records the fatal log record using the Logger::write() function. It works identically 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() - */ -#define LOG_FATAL(...) Logger::write(Logger::Fatal, __FILE__, __LINE__, Q_FUNC_INFO, ##__VA_ARGS__) - -//! 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() - */ -#define LOG_ASSERT(cond) ((!(cond)) ? Logger::writeAssert(__FILE__, __LINE__, Q_FUNC_INFO, #cond) : qt_noop()) - - -/** - * \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("[%-7l] <%C> %m\n"); - * Logger::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 similiar calls with - * similiar Logger macros (e.g. LOG_DEBUG) - * - * \note Logger uses a singleton class which must live through all the application life cycle and cleans it on the - * 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 AbstractAppender - * \sa LOG_TRACE, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERROR, LOG_FATAL - * \sa LOG_ASSERT - */ - -//! Very simple but rather powerful component which may be used for logging your application activities. -class CUTELOGGERSHARED_EXPORT Logger -{ - public: - //! Describes the possible severity levels of the log records - enum LogLevel - { - Trace, //!< Trace level. Can be used for mostly unneeded records used for internal code tracing. - Debug, //!< Debug level. Useful for non-necessary records used for the debugging of the software. - Info, //!< Info level. Can be used for informational records, which may be interesting for not only developers. - Warning, //!< Warning. May be used to log some non-fatal warnings detected by your application. - Error, //!< Error. May be used for a big problems making your application work wrong but not crashing. - Fatal //!< Fatal. Used for unrecoverable errors, crashes the application right after the log record is written. - }; - - //! Converts the LogLevel enum value to its string representation - /** - * \param logLevel Log level to convert - * - * \sa LogLevel - * \sa levelFromString() - */ - static QString levelToString(LogLevel logLevel); - - //! 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() - */ - static LogLevel levelFromString(const QString& s); - - //! 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 AbstractAppender - */ - static void registerAppender(AbstractAppender* appender); - - //! 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 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 - */ - static void write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function, - const QString& message); - - /** - * This is the overloaded function provided for the convinience. It behaves identically to the above function. - * - * This function uses the current timestamp obtained with \c QDateTime::currentDateTime(). - * - * \sa write() - */ - static void write(LogLevel logLevel, const char* file, int line, const char* function, const QString& message); - - /** - * This is the overloaded function provided for the convinience. It behaves identically to the above function. - * - * This function uses the current timestamp obtained with \c QDateTime::currentDateTime(). Also it supports writing - * <tt>const char*</tt> instead of \c QString and converts it internally using the \c QString::fromAscii(). If you - * want this function to support the non-ascii strings, you will need to setup the codec using the - * \c QTextCodec::setCodecForCStrings() - * - * \sa write() - */ - static void write(LogLevel logLevel, const char* file, int line, const char* function, const char* message, ...); - - /** - * This is the overloaded function provided for the convinience. It behaves identically to the above function. - * - * This function doesn't accept any log message as argument. It returns the \c QDebug object that can be written - * using the stream functions. For example, you may like to write: - * \code - * LOG_DEBUG() << "This is the size" << size << "of the element" << elementName; - * \endcode - * instead of writing - * \code - * LOG_DEBUG(QString(QLatin1String("This is the size %1x%2 of the element %3")) - * .arg(size.x()).arg(size.y()).arg(elementName)); - * \endcode - * - * Please consider reading the Qt Reference Documentation for the description of the QDebug class usage syntax. - * - * \note This overload is definitely more pleasant to use than the first write() overload, but it behaves definitely - * slower than all the above overloads. - * - * \sa write() - */ - static QDebug write(LogLevel logLevel, const char* file, int line, const char* function); - - //! 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() - */ - static void writeAssert(const char* file, int line, const char* function, const char* condition); -}; - -#endif // LOGGER_H diff --git a/rbutil/rbutilqt/logger/README.ROCKBOX b/rbutil/rbutilqt/logger/README.ROCKBOX index 08f537fb3f..f501a410dd 100644 --- a/rbutil/rbutilqt/logger/README.ROCKBOX +++ b/rbutil/rbutilqt/logger/README.ROCKBOX @@ -1,7 +1,7 @@ This folder contains the cutelogger project for logging functionality. These files are distributed under the LGPL v2 or later. The source files have been last synced with the projects at -https://gitorious.org/cutelogger to commit -e3c2745c6c5f38896f87472e01ea2caf2d9e211b. - +https://github.com/dept2/CuteLogger/ to commit +5ae6b9ac13e0cc2821d236e3542a83990b63c95c +on Aug 7, 2020. diff --git a/rbutil/rbutilqt/logger/include/AbstractAppender.h b/rbutil/rbutilqt/logger/include/AbstractAppender.h new file mode 100644 index 0000000000..e029b045aa --- /dev/null +++ b/rbutil/rbutilqt/logger/include/AbstractAppender.h @@ -0,0 +1,49 @@ +/* + 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. +*/ +#ifndef ABSTRACTAPPENDER_H +#define ABSTRACTAPPENDER_H + +// Local +#include "CuteLogger_global.h" +#include <Logger.h> + +// Qt +#include <QMutex> + + +class CUTELOGGERSHARED_EXPORT AbstractAppender +{ + public: + AbstractAppender(); + virtual ~AbstractAppender(); + + Logger::LogLevel detailsLevel() const; + void setDetailsLevel(Logger::LogLevel level); + void setDetailsLevel(const QString& level); + + void write(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, const char* function, + const QString& category, const QString& message); + + protected: + virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, + const char* function, const QString& category, const QString& message) = 0; + + private: + QMutex m_writeMutex; + + Logger::LogLevel m_detailsLevel; + mutable QMutex m_detailsLevelMutex; +}; + +#endif // ABSTRACTAPPENDER_H diff --git a/rbutil/rbutilqt/logger/include/AbstractStringAppender.h b/rbutil/rbutilqt/logger/include/AbstractStringAppender.h new file mode 100644 index 0000000000..78df9e6176 --- /dev/null +++ b/rbutil/rbutilqt/logger/include/AbstractStringAppender.h @@ -0,0 +1,46 @@ +/* + 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. +*/ +#ifndef ABSTRACTSTRINGAPPENDER_H +#define ABSTRACTSTRINGAPPENDER_H + +// Local +#include "CuteLogger_global.h" +#include <AbstractAppender.h> + +// Qt +#include <QReadWriteLock> + + +class CUTELOGGERSHARED_EXPORT AbstractStringAppender : public AbstractAppender +{ + public: + AbstractStringAppender(); + + virtual QString format() const; + void setFormat(const QString&); + + static QString stripFunctionName(const char*); + + protected: + QString formattedString(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, + const char* function, const QString& category, const QString& message) const; + + private: + static QByteArray qCleanupFuncinfo(const char*); + + QString m_format; + mutable QReadWriteLock m_formatLock; +}; + +#endif // ABSTRACTSTRINGAPPENDER_H diff --git a/rbutil/rbutilqt/logger/ConsoleAppender.h b/rbutil/rbutilqt/logger/include/ConsoleAppender.h index fa685b5e82..64ef2e7a19 100644 --- a/rbutil/rbutilqt/logger/ConsoleAppender.h +++ b/rbutil/rbutilqt/logger/include/ConsoleAppender.h @@ -16,17 +16,21 @@ #include "CuteLogger_global.h" #include <AbstractStringAppender.h> - -//! ConsoleAppender is the simple appender that writes the log records to the std::cerr output stream. + + class CUTELOGGERSHARED_EXPORT ConsoleAppender : public AbstractStringAppender { + public: + ConsoleAppender(); + virtual QString format() const; + void ignoreEnvironmentPattern(bool ignore); + protected: - //! Writes the log record to the std::cerr stream. - /** - * \sa AbstractStringAppender::format() - */ virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, - const char* function, const QString& message); + const char* function, const QString& category, const QString& message); + + private: + bool m_ignoreEnvPattern; }; #endif // CONSOLEAPPENDER_H diff --git a/rbutil/rbutilqt/logger/CuteLogger_global.h b/rbutil/rbutilqt/logger/include/CuteLogger_global.h index c5e7680845..c5e7680845 100644 --- a/rbutil/rbutilqt/logger/CuteLogger_global.h +++ b/rbutil/rbutilqt/logger/include/CuteLogger_global.h diff --git a/rbutil/rbutilqt/logger/FileAppender.h b/rbutil/rbutilqt/logger/include/FileAppender.h index 70a70c3e43..ab9e12a91d 100644 --- a/rbutil/rbutilqt/logger/FileAppender.h +++ b/rbutil/rbutilqt/logger/include/FileAppender.h @@ -23,34 +23,20 @@ #include <QTextStream> -//! File is the simple appender that writes the log records to the plain text file. class CUTELOGGERSHARED_EXPORT FileAppender : public AbstractStringAppender { public: - //! Constructs the new file appender assigned to file with the given name. FileAppender(const QString& fileName = QString()); ~FileAppender(); - //! Returns the name set by setFileName() or to the FileAppender constructor. - /** - * \sa setFileName() - */ QString fileName() const; - - //! Sets the name of the file. The name can have no path, a relative path, or an absolute path. - /** - * \sa fileName() - */ void setFileName(const QString&); + bool reopenFile(); + protected: - //! Write the log record to the file. - /** - * \sa fileName() - * \sa AbstractStringAppender::format() - */ virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, - const char* function, const QString& message); + const char* function, const QString& category, const QString& message); bool openFile(); void closeFile(); diff --git a/rbutil/rbutilqt/logger/include/Logger.h b/rbutil/rbutilqt/logger/include/Logger.h new file mode 100644 index 0000000000..509bc5f435 --- /dev/null +++ b/rbutil/rbutilqt/logger/include/Logger.h @@ -0,0 +1,237 @@ +/* + 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. +*/ +#ifndef LOGGER_H +#define LOGGER_H + +// Qt +#include <QString> +#include <QDebug> +#include <QDateTime> + +// Local +#include "CuteLogger_global.h" +class AbstractAppender; + + +class Logger; +CUTELOGGERSHARED_EXPORT Logger* cuteLoggerInstance(); +#define cuteLogger cuteLoggerInstance() + + +#define LOG_TRACE CuteMessageLogger(cuteLoggerInstance(), Logger::Trace, __FILE__, __LINE__, Q_FUNC_INFO).write +#define LOG_DEBUG CuteMessageLogger(cuteLoggerInstance(), Logger::Debug, __FILE__, __LINE__, Q_FUNC_INFO).write +#define LOG_INFO CuteMessageLogger(cuteLoggerInstance(), Logger::Info, __FILE__, __LINE__, Q_FUNC_INFO).write +#define LOG_WARNING CuteMessageLogger(cuteLoggerInstance(), Logger::Warning, __FILE__, __LINE__, Q_FUNC_INFO).write +#define LOG_ERROR CuteMessageLogger(cuteLoggerInstance(), Logger::Error, __FILE__, __LINE__, Q_FUNC_INFO).write +#define LOG_FATAL CuteMessageLogger(cuteLoggerInstance(), Logger::Fatal, __FILE__, __LINE__, Q_FUNC_INFO).write + +#define LOG_CTRACE(category) CuteMessageLogger(cuteLoggerInstance(), Logger::Trace, __FILE__, __LINE__, Q_FUNC_INFO, category).write() +#define LOG_CDEBUG(category) CuteMessageLogger(cuteLoggerInstance(), Logger::Debug, __FILE__, __LINE__, Q_FUNC_INFO, category).write() +#define LOG_CINFO(category) CuteMessageLogger(cuteLoggerInstance(), Logger::Info, __FILE__, __LINE__, Q_FUNC_INFO, category).write() +#define LOG_CWARNING(category) CuteMessageLogger(cuteLoggerInstance(), Logger::Warning, __FILE__, __LINE__, Q_FUNC_INFO, category).write() +#define LOG_CERROR(category) CuteMessageLogger(cuteLoggerInstance(), Logger::Error, __FILE__, __LINE__, Q_FUNC_INFO, category).write() +#define LOG_CFATAL(category) CuteMessageLogger(cuteLoggerInstance(), Logger::Fatal, __FILE__, __LINE__, Q_FUNC_INFO, category).write() + +#define LOG_TRACE_TIME LoggerTimingHelper loggerTimingHelper(cuteLoggerInstance(), Logger::Trace, __FILE__, __LINE__, Q_FUNC_INFO); loggerTimingHelper.start +#define LOG_DEBUG_TIME LoggerTimingHelper loggerTimingHelper(cuteLoggerInstance(), Logger::Debug, __FILE__, __LINE__, Q_FUNC_INFO); loggerTimingHelper.start +#define LOG_INFO_TIME LoggerTimingHelper loggerTimingHelper(cuteLoggerInstance(), Logger::Info, __FILE__, __LINE__, Q_FUNC_INFO); loggerTimingHelper.start + +#define LOG_ASSERT(cond) ((!(cond)) ? cuteLoggerInstance()->writeAssert(__FILE__, __LINE__, Q_FUNC_INFO, #cond) : qt_noop()) +#define LOG_ASSERT_X(cond, msg) ((!(cond)) ? cuteLoggerInstance()->writeAssert(__FILE__, __LINE__, Q_FUNC_INFO, msg) : qt_noop()) + +#if (__cplusplus >= 201103L) +#include <functional> + +#define LOG_CATEGORY(category) \ + Logger customCuteLoggerInstance{category};\ + std::function<Logger*()> cuteLoggerInstance = [&customCuteLoggerInstance]() {\ + return &customCuteLoggerInstance;\ + };\ + +#define LOG_GLOBAL_CATEGORY(category) \ + Logger customCuteLoggerInstance{category, true};\ + std::function<Logger*()> cuteLoggerInstance = [&customCuteLoggerInstance]() {\ + return &customCuteLoggerInstance;\ + };\ + +#else + +#define LOG_CATEGORY(category) \ + Logger* cuteLoggerInstance()\ + {\ + static Logger customCuteLoggerInstance(category);\ + return &customCuteLoggerInstance;\ + }\ + +#define LOG_GLOBAL_CATEGORY(category) \ + Logger* cuteLoggerInstance()\ + {\ + static Logger customCuteLoggerInstance(category);\ + customCuteLoggerInstance.logToGlobalInstance(category, true);\ + return &customCuteLoggerInstance;\ + }\ + +#endif + + +class LoggerPrivate; +class CUTELOGGERSHARED_EXPORT Logger +{ + Q_DISABLE_COPY(Logger) + + public: + Logger(); + Logger(const QString& defaultCategory, bool writeToGlobalInstance = false); + ~Logger(); + + //! Describes the possible severity levels of the log records + enum LogLevel + { + Trace, //!< Trace level. Can be used for mostly unneeded records used for internal code tracing. + Debug, //!< Debug level. Useful for non-necessary records used for the debugging of the software. + Info, //!< Info level. Can be used for informational records, which may be interesting for not only developers. + Warning, //!< Warning. May be used to log some non-fatal warnings detected by your application. + Error, //!< Error. May be used for a big problems making your application work wrong but not crashing. + Fatal //!< Fatal. Used for unrecoverable errors, crashes the application right after the log record is written. + }; + + //! Sets the timing display mode for the LOG_TRACE_TIME, LOG_DEBUG_TIME and LOG_INFO_TIME macros + enum TimingMode + { + TimingAuto, //!< Show time in seconds, if it exceeds 10s (default) + TimingMs //!< Always use milliseconds to display + }; + + static QString levelToString(LogLevel logLevel); + static LogLevel levelFromString(const QString& s); + + static Logger* globalInstance(); + + void registerAppender(AbstractAppender* appender); + void registerCategoryAppender(const QString& category, AbstractAppender* appender); + + void removeAppender(AbstractAppender* appender); + + void logToGlobalInstance(const QString& category, bool logToGlobal = false); + + void setDefaultCategory(const QString& category); + QString defaultCategory() const; + + void write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function, const char* category, + const QString& message); + void write(LogLevel logLevel, const char* file, int line, const char* function, const char* category, const QString& message); + + void writeAssert(const char* file, int line, const char* function, const char* condition); + + private: + void write(const QDateTime& timeStamp, LogLevel logLevel, const char* file, int line, const char* function, const char* category, + const QString& message, bool fromLocalInstance); + Q_DECLARE_PRIVATE(Logger) + LoggerPrivate* d_ptr; +}; + + +class CUTELOGGERSHARED_EXPORT CuteMessageLogger +{ + Q_DISABLE_COPY(CuteMessageLogger) + + public: + CuteMessageLogger(Logger* l, Logger::LogLevel level, const char* file, int line, const char* function) + : m_l(l), + m_level(level), + m_file(file), + m_line(line), + m_function(function), + m_category(nullptr) + {} + + CuteMessageLogger(Logger* l, Logger::LogLevel level, const char* file, int line, const char* function, const char* category) + : m_l(l), + m_level(level), + m_file(file), + m_line(line), + m_function(function), + m_category(category) + {} + + ~CuteMessageLogger(); + + void write(const char* msg, ...) +#if defined(Q_CC_GNU) && !defined(__INSURE__) +# if defined(Q_CC_MINGW) && !defined(Q_CC_CLANG) + __attribute__ ((format (gnu_printf, 2, 3))) +# else + __attribute__ ((format (printf, 2, 3))) +# endif +#endif + ; + + void write(const QString& msg); + + QDebug write(); + + private: + Logger* m_l; + Logger::LogLevel m_level; + const char* m_file; + int m_line; + const char* m_function; + const char* m_category; + QString m_message; +}; + + +class CUTELOGGERSHARED_EXPORT LoggerTimingHelper +{ + Q_DISABLE_COPY(LoggerTimingHelper) + + public: + inline explicit LoggerTimingHelper(Logger* l, Logger::LogLevel logLevel, const char* file, int line, + const char* function) + : m_logger(l), + m_logLevel(logLevel), + m_timingMode(Logger::TimingAuto), + m_file(file), + m_line(line), + m_function(function) + {} + + void start(const char* msg, ...) +#if defined(Q_CC_GNU) && !defined(__INSURE__) + # if defined(Q_CC_MINGW) && !defined(Q_CC_CLANG) + __attribute__ ((format (gnu_printf, 2, 3))) + # else + __attribute__ ((format (printf, 2, 3))) + # endif +#endif + ; + + void start(const QString& msg = QString()); + void start(Logger::TimingMode mode, const QString& msg); + + ~LoggerTimingHelper(); + + private: + Logger* m_logger; + QTime m_time; + Logger::LogLevel m_logLevel; + Logger::TimingMode m_timingMode; + const char* m_file; + int m_line; + const char* m_function; + QString m_block; +}; + + +#endif // LOGGER_H diff --git a/rbutil/rbutilqt/logger/OutputDebugAppender.h b/rbutil/rbutilqt/logger/include/OutputDebugAppender.h index f5a5b8c588..dd7ad4deb7 100644 --- a/rbutil/rbutilqt/logger/OutputDebugAppender.h +++ b/rbutil/rbutilqt/logger/include/OutputDebugAppender.h @@ -18,16 +18,12 @@ #include "CuteLogger_global.h" #include <AbstractStringAppender.h> -//! OutputDebugAppender is the appender that writes the log records to the Microsoft Debug Log + class CUTELOGGERSHARED_EXPORT OutputDebugAppender : public AbstractStringAppender { protected: - //! Writes the log record to the windows debug log. - /** - * \sa AbstractStringAppender::format() - */ virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line, - const char* function, const QString& message); + const char* function, const QString& category, const QString& message); }; #endif // OUTPUTDEBUGAPPENDER_H diff --git a/rbutil/rbutilqt/logger/logger.pri b/rbutil/rbutilqt/logger/logger.pri index ec8d68b399..e34bccb3cf 100644 --- a/rbutil/rbutilqt/logger/logger.pri +++ b/rbutil/rbutilqt/logger/logger.pri @@ -1,19 +1,22 @@ SOURCES += \ - $$PWD/AbstractAppender.cpp \ - $$PWD/AbstractStringAppender.cpp \ - $$PWD/ConsoleAppender.cpp \ - $$PWD/FileAppender.cpp \ - $$PWD/Logger.cpp \ + $$PWD/src/AbstractAppender.cpp \ + $$PWD/src/AbstractStringAppender.cpp \ + $$PWD/src/ConsoleAppender.cpp \ + $$PWD/src/FileAppender.cpp \ + $$PWD/src/Logger.cpp \ -INCLUDES += \ - $$PWD/AbstractAppender.h \ - $$PWD/ConsoleAppender.h \ - $$PWD/FileAppender.h \ - $$PWD/OutputDebugAppender.h \ - $$PWD/AbstractStringAppender.h \ - $$PWD/CuteLogger_global.h \ - $$PWD/Logger.h \ +HEADERS += \ + $$PWD/include/AbstractAppender.h \ + $$PWD/include/ConsoleAppender.h \ + $$PWD/include/FileAppender.h \ + $$PWD/include/OutputDebugAppender.h \ + $$PWD/include/AbstractStringAppender.h \ + $$PWD/include/CuteLogger_global.h \ + $$PWD/include/Logger.h \ + +INCLUDEPATH += $$PWD/include DEFINES += \ CUTELOGGER_STATIC + 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/FileAppender.cpp b/rbutil/rbutilqt/logger/src/FileAppender.cpp index 3e4d0e22f9..b9018b0324 100644 --- a/rbutil/rbutilqt/logger/FileAppender.cpp +++ b/rbutil/rbutilqt/logger/src/FileAppender.cpp @@ -17,7 +17,14 @@ // 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); @@ -30,6 +37,10 @@ FileAppender::~FileAppender() } +//! Returns the name set by setFileName() or to the FileAppender constructor. +/** + * \sa setFileName() + */ QString FileAppender::fileName() const { QMutexLocker locker(&m_logFileMutex); @@ -37,8 +48,15 @@ QString FileAppender::fileName() const } +//! 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(); @@ -47,37 +65,50 @@ void FileAppender::setFileName(const QString& s) } +bool FileAppender::reopenFile() +{ + closeFile(); + return openFile(); +} + + bool FileAppender::openFile() { - bool isOpen = false; - if (!m_logFile.isOpen()) + if (m_logFile.fileName().isEmpty()) + return false; + + bool isOpen = m_logFile.isOpen(); + if (!isOpen) { - if (m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) - { + isOpen = m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text); + if (isOpen) m_logStream.setDevice(&m_logFile); - isOpen = true; - } 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& message) + const char* function, const QString& category, const QString& message) { QMutexLocker locker(&m_logFileMutex); - openFile(); - - m_logStream << formattedString(timeStamp, logLevel, file, line, function, message); - m_logStream.flush(); - m_logFile.flush(); + 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); 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/OutputDebugAppender.cpp b/rbutil/rbutilqt/logger/src/OutputDebugAppender.cpp index 2d7facbd98..44d640512f 100644 --- a/rbutil/rbutilqt/logger/OutputDebugAppender.cpp +++ b/rbutil/rbutilqt/logger/src/OutputDebugAppender.cpp @@ -18,14 +18,26 @@ #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, message); + QString s = formattedString(timeStamp, logLevel, file, line, function, category, message); OutputDebugStringW((LPCWSTR) s.utf16()); } diff --git a/rbutil/rbutilqt/main.cpp b/rbutil/rbutilqt/main.cpp index cac93395fe..160a31ba0e 100644 --- a/rbutil/rbutilqt/main.cpp +++ b/rbutil/rbutilqt/main.cpp @@ -40,14 +40,14 @@ Q_IMPORT_PLUGIN(AccessibleFactory) int main( int argc, char ** argv ) { QApplication app( argc, argv ); ConsoleAppender* consoleAppender = new ConsoleAppender(); - consoleAppender->setFormat("[%f:%i %L] %m\n"); - Logger::registerAppender(consoleAppender); + consoleAppender->setFormat("[%{file}:%{line} %{type}] %{message}\n"); + cuteLoggerInstance()->registerAppender(consoleAppender); SysTrace::rotateTrace(); QString tracefile = QDir::tempPath() + "/rbutil-trace.log"; FileAppender* fileAppender = new FileAppender(); - fileAppender->setFormat("[%f:%i %L] %m\n"); + fileAppender->setFormat("[%{file}:%{line} %{type}] %{message}\n"); fileAppender->setFileName(tracefile); - Logger::registerAppender(fileAppender); + cuteLoggerInstance()->registerAppender(fileAppender); LOG_INFO() << "Starting trace at" << QDateTime::currentDateTime().toString(Qt::ISODate); #if defined(Q_OS_MAC) diff --git a/rbutil/rbutilqt/systrace.cpp b/rbutil/rbutilqt/systrace.cpp index 83f70b3da5..5f3450eae8 100644 --- a/rbutil/rbutilqt/systrace.cpp +++ b/rbutil/rbutilqt/systrace.cpp @@ -50,11 +50,11 @@ void SysTrace::refresh(void) QString color; while(!c.atEnd()) { line = c.readLine(); - if(line.contains("WARNING")) + if(line.contains("Warning")) color = "orange"; - else if(line.contains("ERROR")) + else if(line.contains("Error")) color = "red"; - else if(line.contains("DEBUG")) + else if(line.contains("Debug")) color = "blue"; #if 0 else if(line.contains("INFO")) |