Merge dlog
authorIceyer <me@iceyer.net>
Tue, 25 Jul 2017 11:53:05 +0000 (19:53 +0800)
committerIceyer <me@iceyer.net>
Tue, 25 Jul 2017 11:53:05 +0000 (19:53 +0800)
25 files changed:
src/dtkcore_global.h
src/install.pri
src/log/AbstractAppender.cpp [new file with mode: 0644]
src/log/AbstractAppender.h [new file with mode: 0644]
src/log/AbstractStringAppender.cpp [new file with mode: 0644]
src/log/AbstractStringAppender.h [new file with mode: 0644]
src/log/ConsoleAppender.cpp [new file with mode: 0644]
src/log/ConsoleAppender.h [new file with mode: 0644]
src/log/CuteLogger_global.h [new file with mode: 0644]
src/log/DLog [new file with mode: 0644]
src/log/FileAppender.cpp [new file with mode: 0644]
src/log/FileAppender.h [new file with mode: 0644]
src/log/LICENSE [new file with mode: 0644]
src/log/LogManager.cpp [new file with mode: 0644]
src/log/LogManager.h [new file with mode: 0644]
src/log/Logger.cpp [new file with mode: 0644]
src/log/Logger.h [new file with mode: 0644]
src/log/OutputDebugAppender.cpp [new file with mode: 0644]
src/log/OutputDebugAppender.h [new file with mode: 0644]
src/log/README.md [new file with mode: 0644]
src/log/RollingFileAppender.cpp [new file with mode: 0644]
src/log/RollingFileAppender.h [new file with mode: 0644]
src/log/cutelogger.pri [new file with mode: 0644]
src/log/log.pri [new file with mode: 0644]
src/src.pro

index 055a47222fc1cff7a76ffad2fdc13f0d8d6a88ee..c75a656a5f0dca3acd00565de837e029666e0af6 100644 (file)
@@ -31,7 +31,7 @@
 #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
index c55d0dabc1b87dd932f8cc0fd339a35fed9974f5..17ff73bafb39b0ad9e9033cc4e01bb1f7d5463d6 100644 (file)
@@ -5,6 +5,11 @@ includes.files += \
     $$PWD/base/DObject \
     $$PWD/base/DSingleton
 
+includes.files += $$PWD/log/*.h
+includes.files += $$PWD/log/*.cpp
+includes.files += \
+    $$PWD/log/DLog
+
 INSTALLS += includes target
 
 
diff --git a/src/log/AbstractAppender.cpp b/src/log/AbstractAppender.cpp
new file mode 100644 (file)
index 0000000..2dfee02
--- /dev/null
@@ -0,0 +1,136 @@
+ // 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
diff --git a/src/log/AbstractAppender.h b/src/log/AbstractAppender.h
new file mode 100644 (file)
index 0000000..91e9479
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+  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
diff --git a/src/log/AbstractStringAppender.cpp b/src/log/AbstractStringAppender.cpp
new file mode 100644 (file)
index 0000000..1ec0be8
--- /dev/null
@@ -0,0 +1,462 @@
+/*
+  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
diff --git a/src/log/AbstractStringAppender.h b/src/log/AbstractStringAppender.h
new file mode 100644 (file)
index 0000000..b79f194
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+  Copyright (c) 2010 Boris Moiseev (cyberbobs at gmail dot com)
+
+  This program is free software: you can redistribute it and/or modify
+  it under the terms of the GNU Lesser General Public License version 2.1
+  as published by the Free Software Foundation and appearing in the file
+  LICENSE.LGPL included in the packaging of this file.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU Lesser General Public License for more details.
+*/
+#ifndef 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
diff --git a/src/log/ConsoleAppender.cpp b/src/log/ConsoleAppender.cpp
new file mode 100644 (file)
index 0000000..417e888
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+  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
diff --git a/src/log/ConsoleAppender.h b/src/log/ConsoleAppender.h
new file mode 100644 (file)
index 0000000..26fe8e5
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+  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
diff --git a/src/log/CuteLogger_global.h b/src/log/CuteLogger_global.h
new file mode 100644 (file)
index 0000000..bafb782
--- /dev/null
@@ -0,0 +1,16 @@
+#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
diff --git a/src/log/DLog b/src/log/DLog
new file mode 100644 (file)
index 0000000..774c2fc
--- /dev/null
@@ -0,0 +1,8 @@
+#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"
diff --git a/src/log/FileAppender.cpp b/src/log/FileAppender.cpp
new file mode 100644 (file)
index 0000000..b26273d
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+  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
diff --git a/src/log/FileAppender.h b/src/log/FileAppender.h
new file mode 100644 (file)
index 0000000..c122580
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+  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
diff --git a/src/log/LICENSE b/src/log/LICENSE
new file mode 100644 (file)
index 0000000..9cecc1d
--- /dev/null
@@ -0,0 +1,674 @@
+                    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>.
diff --git a/src/log/LogManager.cpp b/src/log/LogManager.cpp
new file mode 100644 (file)
index 0000000..cbbac76
--- /dev/null
@@ -0,0 +1,79 @@
+#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
diff --git a/src/log/LogManager.h b/src/log/LogManager.h
new file mode 100644 (file)
index 0000000..a3e7aa4
--- /dev/null
@@ -0,0 +1,47 @@
+#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
diff --git a/src/log/Logger.cpp b/src/log/Logger.cpp
new file mode 100644 (file)
index 0000000..1d5eda9
--- /dev/null
@@ -0,0 +1,1058 @@
+/*
+  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
diff --git a/src/log/Logger.h b/src/log/Logger.h
new file mode 100644 (file)
index 0000000..87ac2e2
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+  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
diff --git a/src/log/OutputDebugAppender.cpp b/src/log/OutputDebugAppender.cpp
new file mode 100644 (file)
index 0000000..0d8f08c
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+  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
diff --git a/src/log/OutputDebugAppender.h b/src/log/OutputDebugAppender.h
new file mode 100644 (file)
index 0000000..f12004f
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+  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
diff --git a/src/log/README.md b/src/log/README.md
new file mode 100644 (file)
index 0000000..d974b36
--- /dev/null
@@ -0,0 +1,63 @@
+# 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
+
+
diff --git a/src/log/RollingFileAppender.cpp b/src/log/RollingFileAppender.cpp
new file mode 100644 (file)
index 0000000..1f9a4af
--- /dev/null
@@ -0,0 +1,232 @@
+#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
diff --git a/src/log/RollingFileAppender.h b/src/log/RollingFileAppender.h
new file mode 100644 (file)
index 0000000..8011bef
--- /dev/null
@@ -0,0 +1,85 @@
+#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
diff --git a/src/log/cutelogger.pri b/src/log/cutelogger.pri
new file mode 100644 (file)
index 0000000..c37152c
--- /dev/null
@@ -0,0 +1,25 @@
+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
+}
diff --git a/src/log/log.pri b/src/log/log.pri
new file mode 100644 (file)
index 0000000..50b1897
--- /dev/null
@@ -0,0 +1 @@
+include(cutelogger.pri)
index 454202e5d3812fb9f085ca7693caee8d4b760be9..64425600ba04e5121e556fc22a46698e01efafe1 100644 (file)
@@ -14,3 +14,4 @@ HEADERS += \
     $$PWD/dtkcore_global.h \
 
 include($$PWD/base/dbase.pri)
+include($$PWD/log/log.pri)