#if defined(STATIC_LIB)
# define LIBDTKCORESHARED_EXPORT
#else
-#if defined(LIBDTKBASE_LIBRARY)
+#if defined(LIBDTKCORE_LIBRARY)
# define LIBDTKCORESHARED_EXPORT Q_DECL_EXPORT
#else
# define LIBDTKCORESHARED_EXPORT Q_DECL_IMPORT
$$PWD/base/DObject \
$$PWD/base/DSingleton
+includes.files += $$PWD/log/*.h
+includes.files += $$PWD/log/*.cpp
+includes.files += \
+ $$PWD/log/DLog
+
INSTALLS += includes target
--- /dev/null
+ // Local
+#include "AbstractAppender.h"
+
+// Qt
+#include <QMutexLocker>
+
+DCORE_BEGIN_NAMESPACE
+
+/**
+ * \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()
+ */
+
+DCORE_END_NAMESPACE
--- /dev/null
+/*
+ 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>
+
+DCORE_BEGIN_NAMESPACE
+
+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;
+};
+
+DCORE_END_NAMESPACE
+
+#endif // ABSTRACTAPPENDER_H
--- /dev/null
+/*
+ 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>
+
+DCORE_BEGIN_NAMESPACE
+
+/**
+ * \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 similar 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} - Similar 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
+ Q_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) {
+ register 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('/', -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;
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+/*
+ 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>
+
+DCORE_BEGIN_NAMESPACE
+
+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;
+};
+
+DCORE_END_NAMESPACE
+
+#endif // ABSTRACTSTRINGAPPENDER_H
--- /dev/null
+/*
+ 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>
+
+DCORE_BEGIN_NAMESPACE
+
+/**
+ * \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));
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+/*
+ 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 CONSOLEAPPENDER_H
+#define CONSOLEAPPENDER_H
+
+#include "CuteLogger_global.h"
+#include <AbstractStringAppender.h>
+
+DCORE_BEGIN_NAMESPACE
+
+class CUTELOGGERSHARED_EXPORT ConsoleAppender : public AbstractStringAppender
+{
+ public:
+ ConsoleAppender();
+ virtual QString format() const;
+ void ignoreEnvironmentPattern(bool ignore);
+
+ protected:
+ virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
+ const char* function, const QString& category, const QString& message);
+
+ private:
+ bool m_ignoreEnvPattern;
+};
+
+DCORE_END_NAMESPACE
+
+#endif // CONSOLEAPPENDER_H
--- /dev/null
+#ifndef CUTELOGGER_GLOBAL_H
+#define CUTELOGGER_GLOBAL_H
+
+#include "dtkcore_global.h"
+
+#if defined(CUTELOGGER_LIBRARY)
+# define CUTELOGGERSHARED_EXPORT Q_DECL_EXPORT
+#else
+#if defined(Q_OS_WIN32)
+# define CUTELOGGERSHARED_EXPORT
+#else
+# define CUTELOGGERSHARED_EXPORT Q_DECL_IMPORT
+#endif
+#endif
+
+#endif // CUTELOGGER_GLOBAL_H
--- /dev/null
+#include "CuteLogger_global.h"
+#include "RollingFileAppender.h"
+#include "Logger.h"
+#include "LogManager.h"
+#include "FileAppender.h"
+#include "ConsoleAppender.h"
+#include "AbstractStringAppender.h"
+#include "AbstractAppender.h"
--- /dev/null
+/*
+ Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1
+ as published by the Free Software Foundation and appearing in the file
+ LICENSE.LGPL included in the packaging of this file.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+*/
+// Local
+#include "FileAppender.h"
+
+// STL
+#include <iostream>
+
+DCORE_BEGIN_NAMESPACE
+
+/**
+ * \class FileAppender
+ *
+ * \brief Simple appender that writes the log records to the plain text file.
+ */
+
+
+//! Constructs the new file appender assigned to file with the given name.
+FileAppender::FileAppender(const QString& fileName)
+{
+ setFileName(fileName);
+}
+
+
+FileAppender::~FileAppender()
+{
+ closeFile();
+}
+
+
+//! Returns the name set by setFileName() or to the FileAppender constructor.
+/**
+ * \sa setFileName()
+ */
+QString FileAppender::fileName() const
+{
+ QMutexLocker locker(&m_logFileMutex);
+ return m_logFile.fileName();
+}
+
+
+//! Sets the name of the file. The name can have no path, a relative path, or an absolute path.
+/**
+ * \sa fileName()
+ */
+void FileAppender::setFileName(const QString& s)
+{
+ QMutexLocker locker(&m_logFileMutex);
+ if (m_logFile.isOpen())
+ m_logFile.close();
+
+ m_logFile.setFileName(s);
+}
+
+qint64 FileAppender::size() const
+{
+ return m_logFile.size();
+}
+
+
+bool FileAppender::openFile()
+{
+ bool isOpen = m_logFile.isOpen();
+ if (!isOpen)
+ {
+ isOpen = m_logFile.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text);
+ if (isOpen)
+ m_logStream.setDevice(&m_logFile);
+ else
+ std::cerr << "<FileAppender::append> Cannot open the log file " << qPrintable(m_logFile.fileName()) << std::endl;
+ }
+ return isOpen;
+}
+
+
+//! Write the log record to the file.
+/**
+ * \sa fileName()
+ * \sa AbstractStringAppender::format()
+ */
+void FileAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
+ const char* function, const QString& category, const QString& message)
+{
+ QMutexLocker locker(&m_logFileMutex);
+
+ if (openFile())
+ {
+ m_logStream << formattedString(timeStamp, logLevel, file, line, function, category, message);
+ m_logStream.flush();
+ m_logFile.flush();
+ }
+}
+
+
+void FileAppender::closeFile()
+{
+ QMutexLocker locker(&m_logFileMutex);
+ m_logFile.close();
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+/*
+ 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 FILEAPPENDER_H
+#define FILEAPPENDER_H
+
+// Logger
+#include "CuteLogger_global.h"
+#include <AbstractStringAppender.h>
+
+// Qt
+#include <QFile>
+#include <QTextStream>
+
+DCORE_BEGIN_NAMESPACE
+
+class CUTELOGGERSHARED_EXPORT FileAppender : public AbstractStringAppender
+{
+ public:
+ FileAppender(const QString& fileName = QString());
+ ~FileAppender();
+
+ QString fileName() const;
+ void setFileName(const QString&);
+
+ qint64 size() const;
+
+ protected:
+ virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
+ const char* function, const QString& category, const QString& message);
+ bool openFile();
+ void closeFile();
+
+ private:
+ QFile m_logFile;
+ QTextStream m_logStream;
+ mutable QMutex m_logFileMutex;
+};
+
+DCORE_END_NAMESPACE
+
+#endif // FILEAPPENDER_H
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ {one line to give the program's name and a brief idea of what it does.}
+ Copyright (C) {year} {name of author}
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ {project} Copyright (C) {year} {fullname}
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
--- /dev/null
+#include "LogManager.h"
+#include <Logger.h>
+#include <ConsoleAppender.h>
+#include <RollingFileAppender.h>
+
+DCORE_BEGIN_NAMESPACE
+
+/**
+ * \class DLogManager
+ *
+ * \brief DLogManager is the deepin user application log manager
+ */
+
+DLogManager::DLogManager()
+{
+ QString cachePath = QStandardPaths::standardLocations(QStandardPaths::CacheLocation).at(0);
+ if (!QDir(cachePath).exists()){
+ QDir(cachePath).mkpath(cachePath);
+ }
+ m_logPath = joinPath(cachePath, QString("%1.log").arg(qApp->applicationName()));
+ m_format = "%{time}{yyyy-MM-dd, HH:mm:ss.zzz} [%{type:-7}] [%{file:-20} %{function:-35} %{line}] %{message}\n";
+}
+
+void DLogManager::initConsoleAppender(){
+ m_consoleAppender = new ConsoleAppender;
+ m_consoleAppender->setFormat(m_format);
+ logger->registerAppender(m_consoleAppender);
+}
+
+void DLogManager::initRollingFileAppender(){
+ m_rollingFileAppender = new RollingFileAppender(m_logPath);
+ m_rollingFileAppender->setFormat(m_format);
+ m_rollingFileAppender->setLogFilesLimit(5);
+ m_rollingFileAppender->setDatePattern(RollingFileAppender::DailyRollover);
+ logger->registerAppender(m_rollingFileAppender);
+}
+
+//! Registers the appender to write the log records to the Console
+/**
+ * \sa registerFileAppender
+ */
+void DLogManager::registerConsoleAppender(){
+ DLogManager::instance()->initConsoleAppender();
+}
+
+//! Registers the appender to write the log records to the file
+/**
+ * \sa getlogFilePath
+ * \sa registerConsoleAppender
+ */
+void DLogManager::registerFileAppender() {
+ DLogManager::instance()->initRollingFileAppender();
+}
+
+//! Return the path file log storage
+/**
+ * \sa registerFileAppender
+ */
+QString DLogManager::getlogFilePath(){
+ return QDir::toNativeSeparators(DLogManager::instance()->m_logPath);
+}
+
+void DLogManager::setLogFormat(const QString &format)
+{
+ //m_format = "%{time}{yyyy-MM-dd, HH:mm:ss.zzz} [%{type:-7}] [%{file:-20} %{function:-35} %{line}] %{message}\n";
+ DLogManager::instance()->m_format = format;
+}
+
+QString DLogManager::joinPath(const QString &path, const QString &fileName){
+ QString separator(QDir::separator());
+ return QString("%1%2%3").arg(path, separator, fileName);
+}
+
+DLogManager::~DLogManager()
+{
+
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+#ifndef LOGMANAGER_H
+#define LOGMANAGER_H
+
+#include <QtCore>
+
+#include "CuteLogger_global.h"
+
+DCORE_BEGIN_NAMESPACE
+
+class ConsoleAppender;
+class RollingFileAppender;
+
+class DLogManager
+{
+public:
+ static void registerConsoleAppender();
+ static void registerFileAppender();
+ static QString getlogFilePath();
+ static void setLogFormat(const QString& format);
+
+Q_SIGNALS:
+
+public Q_SLOTS:
+
+private:
+ QString m_format;
+ QString m_logPath;
+ ConsoleAppender* m_consoleAppender;
+ RollingFileAppender* m_rollingFileAppender;
+
+ void initConsoleAppender();
+ void initRollingFileAppender();
+ QString joinPath(const QString& path, const QString& fileName);
+
+ inline static DLogManager* instance(){
+ static DLogManager instance;
+ return &instance;
+ }
+ explicit DLogManager();
+ ~DLogManager();
+ DLogManager(const DLogManager &);
+ DLogManager & operator = (const DLogManager &);
+};
+
+DCORE_END_NAMESPACE
+
+#endif // LOGMANAGER_H
--- /dev/null
+/*
+ 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>
+
+DCORE_BEGIN_NAMESPACE
+
+/**
+ * \file Logger.h
+ * \brief A file containing the description of Logger class and and additional useful macros for logging
+ */
+
+/**
+ * \def logger
+ *
+ * \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;
+ * logger->registerAppender(consoleAppender);
+ * \endcode
+ *
+ * \sa Dtk::Logger::globalInstance()
+ */
+
+
+/**
+ * \def dTrace
+ *
+ * \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 (dInfo() 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 Dtk::Logger::LogLevel
+ * \sa Dtk::Logger::write()
+ */
+
+
+/**
+ * \def dDebug
+ *
+ * \brief Writes the debug log record
+ *
+ * This macro records the debug log record using the Logger::write() function. It works similar to the dTrace()
+ * macro.
+ *
+ * \sa dTrace()
+ * \sa Dtk::Logger::LogLevel
+ * \sa Dtk::Logger::write()
+ */
+
+
+/**
+ * \def dInfo
+ *
+ * \brief Writes the info log record
+ *
+ * This macro records the info log record using the Logger::write() function. It works similar to the dTrace()
+ * macro.
+ *
+ * \sa dTrace()
+ * \sa Dtk::Logger::LogLevel
+ * \sa Dtk::Logger::write()
+ */
+
+
+/**
+ * \def dWarning
+ *
+ * \brief Write the warning log record
+ *
+ * This macro records the warning log record using the Logger::write() function. It works similar to the dTrace()
+ * macro.
+ *
+ * \sa dTrace()
+ * \sa Dtk::Logger::LogLevel
+ * \sa Dtk::Logger::write()
+ */
+
+
+/**
+ * \def dError
+ *
+ * \brief Write the error log record
+ * This macro records the error log record using the Logger::write() function. It works similar to the dTrace()
+ * macro.
+ *
+ * \sa dTrace()
+ * \sa Dtk::Logger::LogLevel
+ * \sa Dtk::Logger::write()
+ */
+
+
+/**
+ * \def dFatal
+ *
+ * \brief Write the fatal log record
+ *
+ * This macro records the fatal log record using the Logger::write() function. It works similar to the dTrace()
+ * 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 dTrace()
+ * \sa Dtk::Logger::LogLevel
+ * \sa Dtk::Logger::write()
+ */
+
+
+/**
+ * \def dCTrace(category)
+ *
+ * \brief Writes the trace log record to the specific category
+ *
+ * This macro is the similar to the dTrace() macro, but has a category parameter
+ * to write only to the category appenders (registered using Logger::registerCategoryAppender() method).
+ *
+ * \param category category name string
+ *
+ * \sa dTrace()
+ * \sa Dtk::Logger::LogLevel
+ * \sa Dtk::Logger::registerCategoryAppender()
+ * \sa Dtk::Logger::write()
+ * \sa dCategory(), dGlobalCategory()
+ */
+
+
+/**
+ * \def dCDebug
+ *
+ * \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 dCTrace()
+ * macro.
+ *
+ * \sa dCTrace()
+ */
+
+
+/**
+ * \def dCInfo
+ *
+ * \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 dCTrace()
+ * macro.
+ *
+ * \sa dCTrace()
+ */
+
+
+/**
+ * \def dCWarning
+ *
+ * \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 dCTrace()
+ * macro.
+ *
+ * \sa dCTrace()
+ */
+
+
+/**
+ * \def dCError
+ *
+ * \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 dCTrace()
+ * macro.
+ *
+ * \sa dCTrace()
+ */
+
+
+/**
+ * \def dCFatal
+ *
+ * \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 dCTrace()
+ * 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 dCTrace()
+ */
+
+
+/**
+ * \def dCategory(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 loggerInstance() function, creates the local Logger object inside your class and
+ * sets the default category to the specified parameter.
+ *
+ * Thus, any call to loggerInstance() (for example, inside dTrace() 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
+ * dCategory("custom_category")
+ * ...
+ * };
+ *
+ * CustomClass::CustomClass(QObject* parent) : QObject(parent)
+ * {
+ * logger->registerAppender(new FileAppender("custom_category_log"));
+ * dTrace() << "Trace to the custom category log";
+ * }
+ * \endcode
+ *
+ * \sa Dtk::Logger::write()
+ * \sa dTrace
+ * \sa Dtk::Logger::registerCategoryAppender()
+ * \sa Dtk::Logger::setDefaultCategory()
+ * \sa dGlobalCategory
+ */
+
+
+/**
+ * \def dGlobalCategory(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 dCategory(), but also passes all log messages to the global logger instance appenders.
+ * It is equal to defining the local category logger using dCategory macro and calling:
+ * \code logger->logToGlobalInstance(logger->defaultCategory(), true); \endcode
+ *
+ * \sa dCategory
+ * \sa Dtk::Logger::logToGlobalInstance()
+ * \sa Dtk::Logger::defaultCategory()
+ * \sa Dtk::Logger::registerCategoryAppender()
+ * \sa Dtk::Logger::write()
+ */
+
+
+
+/**
+ * \def dAssert
+ *
+ * \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 dDebug() 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();
+ * ...
+ * dAssert(b == true);
+ * \endcode
+ *
+ * \sa Dtk::Logger::writeAssert()
+ */
+
+
+/**
+ * \def dTraceTime
+ *
+ * \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()
+ * {
+ * dTraceTime();
+ * ... // 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)
+ * {
+ * dTraceTime();
+ *
+ * if (doFoo)
+ * {
+ * dTraceTime("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 dDebugTime, dInfoTime
+ */
+
+
+/**
+ * \def dDebugTime
+ *
+ * \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 dTraceTime() macro.
+ *
+ * \sa dTraceTime
+ */
+
+
+/**
+ * \def dInfoTime
+ *
+ * \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 dTraceTime() macro.
+ *
+ * \sa dTraceTime
+ */
+
+
+/**
+ * \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 dDebug() macro
+ * etc.) registers itself as a Qt default message handler and captures all the qDebug/dWarning/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 dDebug() and other Logger macros instead.
+ *
+ * \sa logger
+ * \sa [DLogger Documentation](index.html)
+ */
+
+class LogDevice : public QIODevice
+{
+ public:
+ LogDevice(Logger* l)
+ : m_logger(l),
+ m_semaphore(1)
+ {}
+
+ void lock(Logger::LogLevel logLevel, const char* file, int line, const char* function, const char* category)
+ {
+ m_semaphore.acquire();
+
+ if (!isOpen())
+ open(QIODevice::WriteOnly);
+
+ m_logLevel = logLevel;
+ m_file = file;
+ m_line = line;
+ m_function = function;
+ m_category = category;
+ }
+
+ protected:
+ qint64 readData(char*, qint64)
+ {
+ return 0;
+ }
+
+ qint64 writeData(const char* data, qint64 maxSize)
+ {
+ if (maxSize > 0)
+ m_logger->write(m_logLevel, m_file, m_line, m_function, m_category, QString::fromLocal8Bit(QByteArray(data, maxSize)));
+
+ m_semaphore.release();
+ return maxSize;
+ }
+
+ private:
+ Logger* m_logger;
+ QSemaphore m_semaphore;
+ Logger::LogLevel m_logLevel;
+ const char* m_file;
+ int m_line;
+ const char* m_function;
+ const char* m_category;
+};
+
+
+// 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;
+ QString defaultCategory;
+
+ LogDevice* logDevice;
+};
+
+
+// Static fields initialization
+Logger* LoggerPrivate::globalInstance = 0;
+QReadWriteLock LoggerPrivate::globalInstanceLock;
+
+
+static void cleanupLoggerGlobalInstance()
+{
+ QWriteLocker locker(&LoggerPrivate::globalInstanceLock);
+
+ delete LoggerPrivate::globalInstance;
+ LoggerPrivate::globalInstance = 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;
+#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;
+ default:
+ level = Logger::Warning;
+ break;
+ }
+
+ bool isDefaultCategory = QString::fromLatin1(context.category) == "default";
+ Logger::globalInstance()->write(level, context.file, context.line, context.function, isDefaultCategory ? 0 : context.category, msg);
+}
+
+#else
+
+static void qtLoggerMessageHandler(QtMsgType type, const char* msg)
+{
+ switch (type)
+ {
+ case QtDebugMsg:
+ loggerInstance()->write(Logger::Debug, "", 0, "qDebug", 0, msg);
+ break;
+ case QtWarningMsg:
+ loggerInstance()->write(Logger::Warning, "", 0, "qDebug", 0, msg);
+ break;
+ case QtCriticalMsg:
+ loggerInstance()->write(Logger::Error, "", 0, "qDebug", 0, msg);
+ break;
+ case QtFatalMsg:
+ loggerInstance()->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 [logger](@ref logger) macro instead to access the logger instance
+ */
+Logger::Logger()
+ : d_ptr(new LoggerPrivate)
+{
+ Q_D(Logger);
+
+ d->logDevice = new LogDevice(this);
+}
+
+
+//! 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 [logger](@ref logger) macro instead to access the logger instance and call
+ * [setDefaultCategory](@ref setDefaultCategory) method.
+ *
+ * \sa Logger()
+ * \sa setDefaultCategory()
+ */
+Logger::Logger(const QString& defaultCategory)
+ : d_ptr(new LoggerPrivate)
+{
+ Q_D(Logger);
+ d->logDevice = new LogDevice(this);
+
+ 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);
+ qDeleteAll(d->appenders);
+ qDeleteAll(d->categoryAppenders);
+
+ // Cleanup device
+ delete d->logDevice;
+ 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 [logger](@ref logger) macro instead.
+ *
+ * \sa logger
+ */
+Logger* Logger::globalInstance()
+{
+ Logger* result = 0;
+ {
+ 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 dCDebug(), dCWarning() 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 dCTrace(), dCDebug(), dCInfo(), dCWarning(), dCError(), dCFatal()
+ * \sa dCategory(), dGlobalCategory()
+ * \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;
+}
+
+//! 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 dDebug() macro will be treated as category logging,
+ * so you needn't to specify category name using dCDebug() 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)
+ std::cerr << "No appenders assotiated with category " << qPrintable(logCategory) << std::endl;
+ }
+ else
+ {
+ Q_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())
+ {
+ Q_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_ 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 (!logCategory.isNull() && !isGlobalInstance)
+ globalInstance()->write(timeStamp, logLevel, file, line, function, logCategory.toLatin1(), message, 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
+ * (dTrace, dTrac, dInfo, dWarning, dError, dFatal) 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 dTrace, dDebug, dInfo, dWarning, dError, dFatal
+ * \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 convenience. 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);
+}
+
+
+/**
+ * This is the overloaded function provided for the convenience. It behaves similar 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
+ * dDebug() << "This is the size" << size << "of the element" << elementName;
+ * \endcode
+ * instead of writing
+ * \code
+ * dDebug(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()
+ */
+QDebug Logger::write(LogLevel logLevel, const char* file, int line, const char* function, const char* category)
+{
+ Q_D(Logger);
+
+ d->logDevice->lock(logLevel, file, line, function, category);
+ return QDebug(d->logDevice);
+}
+
+
+//! 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, 0, QString("ASSERT: \"%1\"").arg(condition));
+}
+
+
+Logger* loggerInstance()
+{
+ 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();
+}
+
+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)
+ 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, 0, message);
+}
+
+
+void CuteMessageLogger::write(const char* msg, ...) const
+{
+ va_list va;
+ va_start(va, msg);
+ m_l->write(m_level, m_file, m_line, m_function, m_category, QString().vsprintf(msg, va));
+ va_end(va);
+}
+
+
+void CuteMessageLogger::write(const QString& msg) const
+{
+ m_l->write(m_level, m_file, m_line, m_function, m_category, msg);
+}
+
+
+QDebug CuteMessageLogger::write() const
+{
+ return m_l->write(m_level, m_file, m_line, m_function, m_category);
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+/*
+ 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"
+
+DCORE_BEGIN_NAMESPACE
+
+class AbstractAppender;
+class Logger;
+CUTELOGGERSHARED_EXPORT Logger *loggerInstance();
+#define logger loggerInstance()
+
+
+#define dTrace CuteMessageLogger(loggerInstance(), Logger::Trace, __FILE__, __LINE__, Q_FUNC_INFO).write
+#define dDebug CuteMessageLogger(loggerInstance(), Logger::Debug, __FILE__, __LINE__, Q_FUNC_INFO).write
+#define dInfo CuteMessageLogger(loggerInstance(), Logger::Info, __FILE__, __LINE__, Q_FUNC_INFO).write
+#define dWarning CuteMessageLogger(loggerInstance(), Logger::Warning, __FILE__, __LINE__, Q_FUNC_INFO).write
+#define dError CuteMessageLogger(loggerInstance(), Logger::Error, __FILE__, __LINE__, Q_FUNC_INFO).write
+#define dFatal CuteMessageLogger(loggerInstance(), Logger::Fatal, __FILE__, __LINE__, Q_FUNC_INFO).write
+
+#define dCTrace(category) CuteMessageLogger(loggerInstance(), Logger::Trace, __FILE__, __LINE__, Q_FUNC_INFO, category).write()
+#define dCDebug(category) CuteMessageLogger(loggerInstance(), Logger::Debug, __FILE__, __LINE__, Q_FUNC_INFO, category).write()
+#define dCInfo(category) CuteMessageLogger(loggerInstance(), Logger::Info, __FILE__, __LINE__, Q_FUNC_INFO, category).write()
+#define dCWarning(category) CuteMessageLogger(loggerInstance(), Logger::Warning, __FILE__, __LINE__, Q_FUNC_INFO, category).write()
+#define dCError(category) CuteMessageLogger(loggerInstance(), Logger::Error, __FILE__, __LINE__, Q_FUNC_INFO, category).write()
+#define dCFatal(category) CuteMessageLogger(loggerInstance(), Logger::Fatal, __FILE__, __LINE__, Q_FUNC_INFO, category).write()
+
+#define dTraceTime LoggerTimingHelper loggerTimingHelper(loggerInstance(), Logger::Trace, __FILE__, __LINE__, Q_FUNC_INFO); loggerTimingHelper.start
+#define dDebugTime LoggerTimingHelper loggerTimingHelper(loggerInstance(), Logger::Debug, __FILE__, __LINE__, Q_FUNC_INFO); loggerTimingHelper.start
+#define dInfoTime LoggerTimingHelper loggerTimingHelper(loggerInstance(), Logger::Info, __FILE__, __LINE__, Q_FUNC_INFO); loggerTimingHelper.start
+
+#define dAssert(cond) ((!(cond)) ? loggerInstance()->writeAssert(__FILE__, __LINE__, Q_FUNC_INFO, #cond) : qt_noop())
+#define dAssertX(cond, msg) ((!(cond)) ? loggerInstance()->writeAssert(__FILE__, __LINE__, Q_FUNC_INFO, msg) : qt_noop())
+
+#define dCategory(category) \
+ private:\
+ Logger* loggerInstance()\
+ {\
+ static Logger customLoggerInstance(category);\
+ return &customLoggerInstance;\
+ }\
+
+#define dGlobalCategory(category) \
+ private:\
+ Logger* loggerInstance()\
+ {\
+ static Logger customLoggerInstance(category);\
+ customLoggerInstance.logToGlobalInstance(category, true);\
+ return &customLoggerInstance;\
+ }\
+
+
+ class LoggerPrivate;
+ class CUTELOGGERSHARED_EXPORT Logger
+ {
+ Q_DISABLE_COPY(Logger)
+
+ public:
+ Logger();
+ Logger(const QString &defaultCategory);
+ ~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.
+ };
+
+ 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 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);
+ QDebug write(LogLevel logLevel, const char *file, int line, const char *function, const char *category);
+
+ 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:
+ Q_DECL_CONSTEXPR 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(0)
+ {}
+
+ Q_DECL_CONSTEXPR 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)
+ {}
+
+ void write(const char *msg, ...) const
+#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) const;
+
+ QDebug write() const;
+
+ private:
+ Logger *m_l;
+ Logger::LogLevel m_level;
+ const char *m_file;
+ int m_line;
+ const char *m_function;
+ const char *m_category;
+ };
+
+
+ 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_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());
+
+ ~LoggerTimingHelper();
+
+ private:
+ Logger *m_logger;
+ QTime m_time;
+ Logger::LogLevel m_logLevel;
+ const char *m_file;
+ int m_line;
+ const char *m_function;
+ QString m_block;
+ };
+
+ DCORE_END_NAMESPACE
+
+#endif // LOGGER_H
--- /dev/null
+/*
+ Copyright (c) 2010 Karl-Heinz Reichel (khreichel at googlemail dot com)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1
+ as published by the Free Software Foundation and appearing in the file
+ LICENSE.LGPL included in the packaging of this file.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+*/
+// Local
+#include "OutputDebugAppender.h"
+
+// STL
+#include <windows.h>
+
+DCORE_BEGIN_NAMESPACE
+
+/**
+ * \class OutputDebugAppender
+ *
+ * \brief Appender that writes the log records to the Microsoft Debug Log
+ */
+
+
+//! Writes the log record to the windows debug log.
+/**
+ * \sa AbstractStringAppender::format()
+ */
+void OutputDebugAppender::append(const QDateTime& timeStamp,
+ Logger::LogLevel logLevel,
+ const char* file,
+ int line,
+ const char* function,
+ const QString& category,
+ const QString& message)
+{
+ QString s = formattedString(timeStamp, logLevel, file, line, function, category, message);
+ OutputDebugStringW((LPCWSTR) s.utf16());
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+/*
+ Copyright (c) 2010 Karl-Heinz Reichel (khreichel at googlemail dot com)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License version 2.1
+ as published by the Free Software Foundation and appearing in the file
+ LICENSE.LGPL included in the packaging of this file.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+*/
+
+#ifndef OUTPUTDEBUGAPPENDER_H
+#define OUTPUTDEBUGAPPENDER_H
+
+#include "CuteLogger_global.h"
+#include <AbstractStringAppender.h>
+
+DCORE_BEGIN_NAMESPACE
+
+class CUTELOGGERSHARED_EXPORT OutputDebugAppender : public AbstractStringAppender
+{
+ protected:
+ virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
+ const char* function, const QString& category, const QString& message);
+};
+
+DCORE_END_NAMESPACE
+
+#endif // OUTPUTDEBUGAPPENDER_H
--- /dev/null
+# Dtk/Core/DLog
+
+DLog is the log module of deepin tool kit for Qt/C++
+
+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.
+
+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. dDebug())
+
+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.
+
+## Usage
+
+Just add pkgconfig in .pro file
+
+````
+unix {
+ CONFIG+=link_pkgconfig
+ PKGCONFIG+=dtkcore
+}
+````
+
+### Example
+
+````
+
+#include <QCoreApplication>
+#include <DLog>
+using namespace Dtk::Log;
+int main(int argc, char* argv[])
+{
+ QCoreApplication app(argc, argv);
+ // 1 you can use standrd deepin application log format
+ // 1.1 log to console
+ DLogManager::registerConsoleAppender();
+ // 1.2 log to standrd deepin user cache path: ~/.cache/{organ}/{appname}/
+ // app.setOrganizationName("dtk-test"); // must set
+ // app.setApplicationName("dlog-example"); // must set
+ // dInfo()<< "LogFile:" << DLogManager::getlogFilePath();
+ // DLogManager::registerFileAppender();
+ // 1.3 log to stdout and file
+ // DLogManager::registerFileAppender();
+ // DLogManager::registerConsoleAppender();
+ // 2 Register your own logger format;
+ // ConsoleAppender* consoleAppender = new ConsoleAppender;
+ // consoleAppender->setFormat("[%{type:-7}] <%{Function}> %{message}\n");
+ // logger->registerAppender(consoleAppender);
+ dInfo("Starting the application");
+ int result = 1;
+ dWarning() << "Something went wrong." << "Result code is" << result;
+ return result;
+}
+````
+
+\sa Dtk::Core::DLogManager
+
+
--- /dev/null
+#include <QDateTime>
+#include <QDir>
+#include <QFileInfo>
+
+#include "RollingFileAppender.h"
+
+DCORE_BEGIN_NAMESPACE
+
+RollingFileAppender::RollingFileAppender(const QString& fileName)
+ : FileAppender(fileName),
+ m_logFilesLimit(0),
+ m_logSizeLimit(1024*1024*20)
+{}
+
+void RollingFileAppender::append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
+ const char* function, const QString& category, const QString& message)
+{
+
+ if (!m_rollOverTime.isNull() && QDateTime::currentDateTime() > m_rollOverTime)
+ rollOver();
+
+ if (size()> m_logSizeLimit)
+ rollOver();
+
+ FileAppender::append(timeStamp, logLevel, file, line , function, category, message);
+}
+
+
+RollingFileAppender::DatePattern RollingFileAppender::datePattern() const
+{
+ QMutexLocker locker(&m_rollingMutex);
+ return m_frequency;
+}
+
+
+QString RollingFileAppender::datePatternString() const
+{
+ QMutexLocker locker(&m_rollingMutex);
+ return m_datePatternString;
+}
+
+
+void RollingFileAppender::setDatePattern(DatePattern datePattern)
+{
+ setDatePatternString(QLatin1String("'.'yyyy-MM-dd-hh-mm-zzz"));
+
+ QMutexLocker locker(&m_rollingMutex);
+ m_frequency = datePattern;
+
+ computeRollOverTime();
+}
+
+
+void RollingFileAppender::setDatePattern(const QString& datePattern)
+{
+ setDatePatternString(datePattern);
+ computeFrequency();
+
+ computeRollOverTime();
+}
+
+
+void RollingFileAppender::setDatePatternString(const QString& datePatternString)
+{
+ QMutexLocker locker(&m_rollingMutex);
+ m_datePatternString = datePatternString;
+}
+
+
+void RollingFileAppender::computeFrequency()
+{
+ QMutexLocker locker(&m_rollingMutex);
+
+ const QDateTime startTime(QDate(1999, 1, 1), QTime(0, 0));
+ const QString startString = startTime.toString(m_datePatternString);
+
+ if (startString != startTime.addSecs(60).toString(m_datePatternString))
+ m_frequency = MinutelyRollover;
+ else if (startString != startTime.addSecs(60 * 60).toString(m_datePatternString))
+ m_frequency = HourlyRollover;
+ else if (startString != startTime.addSecs(60 * 60 * 12).toString(m_datePatternString))
+ m_frequency = HalfDailyRollover;
+ else if (startString != startTime.addDays(1).toString(m_datePatternString))
+ m_frequency = DailyRollover;
+ else if (startString != startTime.addDays(7).toString(m_datePatternString))
+ m_frequency = WeeklyRollover;
+ else if (startString != startTime.addMonths(1).toString(m_datePatternString))
+ m_frequency = MonthlyRollover;
+ else
+ {
+ Q_ASSERT_X(false, "DailyRollingFileAppender::computeFrequency", "The pattern '%1' does not specify a frequency");
+ return;
+ }
+}
+
+
+void RollingFileAppender::removeOldFiles()
+{
+ if (m_logFilesLimit <= 1)
+ return;
+
+ QFileInfo fileInfo(fileName());
+ QDir logDirectory(fileInfo.absoluteDir());
+ logDirectory.setFilter(QDir::Files);
+ logDirectory.setNameFilters(QStringList() << fileInfo.fileName() + "*");
+ QFileInfoList logFiles = logDirectory.entryInfoList();
+
+ QMap<QDateTime, QString> fileDates;
+ for (int i = 0; i < logFiles.length(); ++i)
+ {
+ QString name = logFiles[i].fileName();
+ QString suffix = name.mid(name.indexOf(fileInfo.fileName()) + fileInfo.fileName().length());
+ QDateTime fileDateTime = QDateTime::fromString(suffix, datePatternString());
+
+ if (fileDateTime.isValid())
+ fileDates.insert(fileDateTime, logFiles[i].absoluteFilePath());
+ }
+
+ QList<QString> fileDateNames = fileDates.values();
+ for (int i = 0; i < fileDateNames.length() - m_logFilesLimit + 1; ++i)
+ QFile::remove(fileDateNames[i]);
+}
+
+
+void RollingFileAppender::computeRollOverTime()
+{
+ Q_ASSERT_X(!m_datePatternString.isEmpty(), "DailyRollingFileAppender::computeRollOverTime()", "No active date pattern");
+
+ QDateTime now = QDateTime::currentDateTime();
+ QDate nowDate = now.date();
+ QTime nowTime = now.time();
+ QDateTime start;
+
+ switch (m_frequency)
+ {
+ case MinutelyRollover:
+ {
+ start = QDateTime(nowDate, nowTime);
+ m_rollOverTime = start.addSecs(60);
+ }
+ break;
+ case HourlyRollover:
+ {
+ start = QDateTime(nowDate, nowTime);
+ m_rollOverTime = start.addSecs(60*60);
+ }
+ break;
+ case HalfDailyRollover:
+ {
+ int hour = nowTime.hour();
+ if (hour >= 12)
+ hour = 12;
+ else
+ hour = 0;
+ start = QDateTime(nowDate, nowTime);
+ m_rollOverTime = start.addSecs(60*60*12);
+ }
+ break;
+ case DailyRollover:
+ {
+ start = QDateTime(nowDate, nowTime);
+ m_rollOverTime = start.addDays(1);
+ }
+ break;
+ case WeeklyRollover:
+ {
+ // Qt numbers the week days 1..7. The week starts on Monday.
+ // Change it to being numbered 0..6, starting with Sunday.
+ int day = nowDate.dayOfWeek();
+ if (day == Qt::Sunday)
+ day = 0;
+ start = QDateTime(nowDate, nowTime).addDays(-1 * day);
+ m_rollOverTime = start.addDays(7);
+ }
+ break;
+ case MonthlyRollover:
+ {
+ start = QDateTime(QDate(nowDate.year(), nowDate.month(), 1), nowTime);
+ m_rollOverTime = start.addMonths(1);
+ }
+ break;
+ default:
+ Q_ASSERT_X(false, "DailyRollingFileAppender::computeInterval()", "Invalid datePattern constant");
+ m_rollOverTime = QDateTime::fromTime_t(0);
+ }
+
+ m_rollOverSuffix = start.toString(m_datePatternString);
+ Q_ASSERT_X(now.toString(m_datePatternString) == m_rollOverSuffix,
+ "DailyRollingFileAppender::computeRollOverTime()", "File name changes within interval");
+ Q_ASSERT_X(m_rollOverSuffix != m_rollOverTime.toString(m_datePatternString),
+ "DailyRollingFileAppender::computeRollOverTime()", "File name does not change with rollover");
+}
+
+
+void RollingFileAppender::rollOver()
+{
+ Q_ASSERT_X(!m_datePatternString.isEmpty(), "DailyRollingFileAppender::rollOver()", "No active date pattern");
+
+ QString rollOverSuffix = m_rollOverSuffix;
+ computeRollOverTime();
+ if (rollOverSuffix == m_rollOverSuffix)
+ return;
+
+ closeFile();
+
+ QString targetFileName = fileName() + rollOverSuffix;
+ QFile f(targetFileName);
+ if (f.exists() && !f.remove())
+ return;
+ f.setFileName(fileName());
+ if (!f.rename(targetFileName))
+ return;
+
+ openFile();
+ removeOldFiles();
+}
+
+
+void RollingFileAppender::setLogFilesLimit(int limit)
+{
+ QMutexLocker locker(&m_rollingMutex);
+ m_logFilesLimit = limit;
+}
+
+
+int RollingFileAppender::logFilesLimit() const
+{
+ QMutexLocker locker(&m_rollingMutex);
+ return m_logFilesLimit;
+}
+
+DCORE_END_NAMESPACE
--- /dev/null
+#ifndef ROLLINGFILEAPPENDER_H
+#define ROLLINGFILEAPPENDER_H
+
+#include <QDateTime>
+
+#include <FileAppender.h>
+
+DCORE_BEGIN_NAMESPACE
+
+/*!
+ * \brief The RollingFileAppender class extends FileAppender so that the underlying file is rolled over at a user chosen frequency.
+ *
+ * The class is based on Log4Qt.DailyRollingFileAppender class (http://log4qt.sourceforge.net/)
+ * and has the same date pattern format.
+ *
+ * For example, if the fileName is set to /foo/bar and the DatePattern set to the daily rollover ('.'yyyy-MM-dd'.log'), on 2014-02-16 at midnight,
+ * the logging file /foo/bar.log will be copied to /foo/bar.2014-02-16.log and logging for 2014-02-17 will continue in /foo/bar
+ * until it rolls over the next day.
+ *
+ * The logFilesLimit parameter is used to automatically delete the oldest log files in the directory during rollover
+ * (so no more than logFilesLimit recent log files exist in the directory at any moment).
+ * \sa setDatePattern(DatePattern), setLogFilesLimit(int)
+ */
+class CUTELOGGERSHARED_EXPORT RollingFileAppender : public FileAppender
+{
+ public:
+ /*!
+ * The enum DatePattern defines constants for date patterns.
+ * \sa setDatePattern(DatePattern)
+ */
+ enum DatePattern
+ {
+ /*! The minutely date pattern string is "'.'yyyy-MM-dd-hh-mm". */
+ MinutelyRollover = 0,
+ /*! The hourly date pattern string is "'.'yyyy-MM-dd-hh". */
+ HourlyRollover,
+ /*! The half-daily date pattern string is "'.'yyyy-MM-dd-a". */
+ HalfDailyRollover,
+ /*! The daily date pattern string is "'.'yyyy-MM-dd". */
+ DailyRollover,
+ /*! The weekly date pattern string is "'.'yyyy-ww". */
+ WeeklyRollover,
+ /*! The monthly date pattern string is "'.'yyyy-MM". */
+ MonthlyRollover
+ };
+ Q_ENUMS(DatePattern)
+
+ RollingFileAppender(const QString& fileName = QString());
+
+ DatePattern datePattern() const;
+ void setDatePattern(DatePattern datePattern);
+ void setDatePattern(const QString& datePattern);
+
+ QString datePatternString() const;
+
+ void setLogFilesLimit(int limit);
+ int logFilesLimit() const;
+
+ void setLogSizeLimit(int qint64);
+ qint64 logSizeLimit() const;
+
+ protected:
+ virtual void append(const QDateTime& timeStamp, Logger::LogLevel logLevel, const char* file, int line,
+ const char* function, const QString& category, const QString& message);
+
+ private:
+ void rollOver();
+ void computeRollOverTime();
+ void computeFrequency();
+ void removeOldFiles();
+ void setDatePatternString(const QString& datePatternString);
+
+ QString m_datePatternString;
+ DatePattern m_frequency;
+
+ QDateTime m_rollOverTime;
+ QString m_rollOverSuffix;
+ int m_logFilesLimit;
+ qint64 m_logSizeLimit;
+ mutable QMutex m_rollingMutex;
+};
+
+DCORE_END_NAMESPACE
+
+#endif // ROLLINGFILEAPPENDER_H
--- /dev/null
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/RollingFileAppender.h \
+ $$PWD/Logger.h \
+ $$PWD/FileAppender.h \
+ $$PWD/CuteLogger_global.h \
+ $$PWD/ConsoleAppender.h \
+ $$PWD/AbstractStringAppender.h \
+ $$PWD/AbstractAppender.h \
+ $$PWD/LogManager.h
+
+SOURCES += \
+ $$PWD/RollingFileAppender.cpp \
+ $$PWD/Logger.cpp \
+ $$PWD/FileAppender.cpp \
+ $$PWD/ConsoleAppender.cpp \
+ $$PWD/AbstractStringAppender.cpp \
+ $$PWD/AbstractAppender.cpp \
+ $$PWD/LogManager.cpp
+
+win32 {
+ SOURCES += $$PWD/OutputDebugAppender.cpp
+ HEADERS += $$PWD/OutputDebugAppender.h
+}
--- /dev/null
+include(cutelogger.pri)
$$PWD/dtkcore_global.h \
include($$PWD/base/dbase.pri)
+include($$PWD/log/log.pri)