]> vaikene.ee Git - evaf/blobdiff - src/libs/Common/logger.cpp
Warning fixes and copyright update.
[evaf] / src / libs / Common / logger.cpp
index 8ccb0139fcd20a7285cb801c8c907f1f6ab8dfd8..94d9fa9eb9e7ea70136ed359960376a9ce844c68 100644 (file)
@@ -3,7 +3,7 @@
  * @brief iLogger interface implementation
  * @author Enar Vaikene
  *
- * Copyright (c) 2011 Enar Vaikene
+ * Copyright (c) 2011-2019 Enar Vaikene
  *
  * This file is part of the eVaf C++ cross-platform application development framework.
  *
 
 #include "logger.h"
 #include "iregistry.h"
-#include "ienv.h"
+#include "iapp.h"
+#include "iconfig.h"
+#include "globals.h"
+#include "inifile.h"
 #include "version.h"
 
 #include <QtCore>
@@ -37,9 +40,9 @@
 
 //-------------------------------------------------------------------
 
-void eVaf::Common::Internal::defFatalMsgHandler(QString const & msg, QString const & source, QString const & where)
+[[noreturn]] void eVaf::Common::Internal::defFatalMsgHandler(QString const & msg, QString const & source, QString const & where)
 {
-    Q_UNUSED(source);
+    Q_UNUSED(source)
 
     fprintf(stderr, "FATAL ERROR: %s (occurred in %s)\n", qPrintable(msg), qPrintable(where));
 
@@ -55,10 +58,18 @@ void eVaf::Common::Internal::defFatalMsgHandler(QString const & msg, QString con
 
 using namespace eVaf::Common;
 
+namespace
+{
+    static Internal::Logger * singleton = nullptr;
+}
+
 iLogger * iLogger::instance()
 {
-    static Internal::Logger singleton;
-    return &singleton;
+    if (nullptr == singleton)
+    {
+        singleton = new Internal::Logger;
+    }
+    return singleton;
 }
 
 
@@ -75,35 +86,61 @@ LoggerSource::LoggerSource()
 
 LoggerSource::LoggerSource(LoggerSource const & o)
     : QSharedData()
+    , name(o.name)
     , severity(o.severity)
+    , fileName(o.fileName)
     , maxSize(o.maxSize)
     , maxCount(o.maxCount)
 {}
 
-void LoggerSource::init(QString const & source, QString const & logDir, QString const & etcDir)
+void LoggerSource::init(QString const & source)
 {
-    Q_UNUSED(etcDir);
-
-    fileName = logDir + source + ".log";
+    name = source;
+    fileName = iApp::instance()->logDir() + source + ".log";
+
+    // Set default settings
+    severity = iLogger::Fatal;
+    maxSize = 100 * 1024;
+    maxCount = 3;
+
+    // Read settings from the 'logger.ini' file
+    QString confFileName = iApp::instance()->etcDir() + "logger.ini";
+    if (QFile::exists(confFileName)) {
+        IniFile ini(confFileName, QIODevice::ReadOnly);
+
+        // Default values for all sources
+        maxSize = 1024 * ini.getValue(".default/log_size", maxSize / 1024).toUInt();
+        maxCount = ini.getValue(".default/log_count", maxCount).toUInt();
+
+        // Default values for this source
+        maxSize = 1024 * ini.getValue(source.toLatin1() + "/log_size", maxSize / 1024).toUInt();
+        maxCount = ini.getValue(source.toLatin1() + "/log_count", maxCount).toUInt();
+    }
 }
 
 
 //-------------------------------------------------------------------
 
-/// Recursively renames backup files
-void renameBackupFile(QDir & dir, QString const & baseName, int idx)
+namespace
 {
-    QString f1 = QString("%1.%2").arg(baseName).arg(idx);
-    QString f2 = QString("%1.%2").arg(baseName).arg(idx + 1);
+    /// Recursively renames backup files
+    void renameBackupFile(QDir & dir, QString const & baseName, int idx)
+    {
+        QString f1 = QString("%1.%2").arg(baseName).arg(idx);
+        QString f2 = QString("%1.%2").arg(baseName).arg(idx + 1);
 
-    if (dir.exists(f2))
-        renameBackupFile(dir, baseName, idx + 1);
+        if (dir.exists(f2))
+            renameBackupFile(dir, baseName, idx + 1);
 
-    dir.rename(f1, f2);
+        dir.rename(f1, f2);
+    }
 }
 
 void LoggerWorker::writeToLogFile(LoggerSource const & src, QString const & msg)
 {
+    //::printf("writeToLogFile(\'%s\', \'%s\') fileName = \'%s\'\n", qPrintable(src.name), qPrintable(msg), qPrintable(src.fileName));
+    if (src.fileName.isEmpty())
+        return;
     QFile f(src.fileName);
     QFile::OpenMode mode;
 #ifdef Q_OS_LINUX
@@ -142,37 +179,48 @@ void LoggerWorker::writeToLogFile(LoggerSource const & src, QString const & msg)
 
 //-------------------------------------------------------------------
 
+void Logger::destroyInstance()
+{
+    if (singleton != nullptr)
+    {
+        delete singleton;
+        singleton = nullptr;
+    }
+}
+
 Logger::Logger()
     : iLogger()
+    , mReady(false)
     , mFatalMsgHandler(defFatalMsgHandler)
     , mConsoleSeverity(iLogger::Fatal)
-    , mDefaultSource("evaf")
-    , mThread(0)
-    , mWorker(0)
 {
     setObjectName(QString("%1-iLogger").arg(VER_MODULE_NAME_STR));
 
     qRegisterMetaType<LoggerSource>("LoggerSource");
 
-    write(Info, QString("%1 created").arg(objectName()), 0, printf("%s:%s:%d", __FILE__, __FUNCTION__, __LINE__));
+    // Create the default source
+    mDefaultSource = new LoggerSource;
+    mDefaultSource->name = "common";
+
+    write(Info, QString("%1 created").arg(objectName()), QString(), printf("%s:%s:%d", __FILE__, __FUNCTION__, __LINE__));
 }
 
 Logger::~Logger()
 {
     // Disconnect any potential receivers from this object
-    disconnect(this, SIGNAL(loggerEvent(eVaf::Common::iLogger::Severity,QString,QString,QString)), 0, 0);
+    disconnect(this, SIGNAL(loggerEvent(Common::iLogger::Severity,QString,QString,QString)), nullptr, nullptr);
 
     // Destroy the worker thread
     if (mWorker) {
-        delete mWorker;
+        mWorker.reset();
         if (mThread) {
             mThread->quit();
             mThread->wait();
-            delete mThread;
+            mThread.reset();
         }
     }
 
-    write(Info, QString("%1 destroyed").arg(objectName()), 0, printf("%s:%s:%d", __FILE__, __FUNCTION__, __LINE__));
+    write(Info, QString("%1 destroyed").arg(objectName()), QString(), printf("%s:%s:%d", __FILE__, __FUNCTION__, __LINE__));
 }
 
 bool Logger::init()
@@ -183,31 +231,50 @@ bool Logger::init()
     // Clear existing sources in case the application was restarted
     mSources.clear();
 
+    // Set the default source name to the name of the application
+    setDefaultSource(iApp::instance()->name());
+
+    // Read configuration parameters from the application's INI file
+    QVariant v = iConfig::instance()->getValue(QString("%1/general/log_level").arg(iApp::instance()->name()), severity());
+    if (v.isValid())
+        setSeverity(iLogger::Severity(qBound(int(iLogger::None), v.toInt(), int(iLogger::Debug))));
+    v = iConfig::instance()->getValue(QString("%1/general/log_size").arg(iApp::instance()->name()), maxSize());
+    if (v.isValid())
+        setMaxSize(v.toUInt());
+    v = iConfig::instance()->getValue(QString("%1/general/log_cnt").arg(iApp::instance()->name()), maxCount());
+    if (v.isValid())
+        setMaxCount(v.toUInt());
+
     // Destroy the previous worker thread
-    if (mWorker) {
-        delete mWorker;
-        if (mThread) {
-            mThread->quit();
-            mThread->wait();
-            delete mThread;
-        }
+    if (mThread) {
+        mThread->quit();
+        mThread->wait();
     }
 
     // Create the worker thread
-    mWorker = new LoggerWorker;
-    mThread = new QThread;
-    mWorker->moveToThread(mThread);
+    mWorker.reset(new LoggerWorker);
+    mThread.reset(new QThread);
+    mWorker->moveToThread(mThread.data());
     mThread->start(QThread::IdlePriority);
-    connect(this, SIGNAL(writeToLogFile(LoggerSource,QString)), mWorker, SLOT(writeToLogFile(LoggerSource,QString)), Qt::QueuedConnection);
+    connect(this, SIGNAL(writeToLogFile(LoggerSource,QString)), mWorker.data(), SLOT(writeToLogFile(LoggerSource,QString)), Qt::QueuedConnection);
 
-    write(Info, QString("%1 initialized").arg(objectName()), 0, printf("%s:%s:%d", __FILE__, __FUNCTION__, __LINE__));
+    mReady = true;
+
+    write(Info, QString("%1 initialized").arg(objectName()), QString(), printf("%s:%s:%d", __FILE__, __FUNCTION__, __LINE__));
 
     return true;
 }
 
+QString Logger::defaultSource() const
+{
+    return mDefaultSource->name;
+}
+
 void Logger::setDefaultSource(QString const & source)
 {
-    mDefaultSource = source;
+    LoggerSource * src = getSource();
+    if (src && src->name != source)
+        getSource(QString())->init(source);
 }
 
 iLogger::Severity Logger::severity(QString const & source)
@@ -227,7 +294,7 @@ uint Logger::maxSize(QString const & source)
 
 void Logger::setMaxSize(uint maxSize, QString const & source)
 {
-    getSource(source)->maxSize = maxSize;
+    getSource(source)->maxSize = maxSize * 1024;
 }
 
 uint Logger::maxCount(QString const & source)
@@ -262,46 +329,30 @@ void Logger::write(Severity severity, QString const & msg, QString const & sourc
         return;
 
     // Write to the log file
-    LoggerSource * src = getSource(source);
-    if (severity <= src->severity && src->severity != iLogger::None) {
-        QString buf;
-        QTextStream io(&buf);
+    if (mReady) {
+        LoggerSource * src = getSource(source);
+        if (severity <= src->severity && src->severity != iLogger::None) {
+            QString buf;
+            QTextStream io(&buf);
 
-        // Date/time stamp
-        io << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
+            // Date/time stamp
+            io << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
 
-        // Severity
-        io << " " << severityText[severity];
+            // Severity
+            io << " " << severityText[severity];
 
-        // Message
-        io << msg;
+            // Message
+            io << msg;
 
-        // Location in the source file
-        if (!where.isEmpty())
-            io << " (occurred in " << where << ")";
+            // Location in the source file
+            if (!where.isEmpty())
+                io << " (occurred in " << where << ")";
 
-        io << endl;
-        io.flush();
+            io << endl;
+            io.flush();
 
-        // If the worker is initialized, use the worker thread to do the job
-        if (mWorker) {
             emit writeToLogFile(*src, buf);
         }
-        // Otherwise we have to do it ourselves
-        else {
-            QFile f(src->fileName);
-            QFile::OpenMode mode;
-#ifdef Q_OS_LINUX
-            mode = QFile::Append | QFile::Text | QFile::Unbuffered;
-#else
-            mode = QFile::Append | QFile::Text;
-#endif
-
-            if (f.open(mode)) {
-                f.write(buf.toLocal8Bit());
-                f.close();
-            }
-        }
     }
 
     // Output to the console
@@ -309,25 +360,25 @@ void Logger::write(Severity severity, QString const & msg, QString const & sourc
         FILE * f = (severity < iLogger::Info) ? stderr : stdout;
 
         // Set text colors
-#ifdef Q_OS_LINUX
+#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
         switch (severity) {
             case iLogger::Info:
-                fprintf(f, "\e[32m"); // Green
+                fprintf(f, "\033[32m"); // Green
                 break;
             case iLogger::Warning:
-                fprintf(f, "\e[1m"); // Bold
+                fprintf(f, "\033[1m"); // Bold
                 break;
             case iLogger::Error:
-                fprintf(f, "\e[31m"); // Red
+                fprintf(f, "\033[31m"); // Red
                 break;
             case iLogger::Fatal:
-                fprintf(f, "\e[31m\e[1m"); // Bold Red
+                fprintf(f, "\033[31m\033[1m"); // Bold Red
                 break;
             default:
-                fprintf(f, "\e[34m"); // Blue
+                fprintf(f, "\033[34m"); // Blue
                 break;
         }
-#elif defined Q_OS_WIN32
+#elif defined(Q_OS_WIN32)
         switch (severity) {
             case iLogger::Info:
                 setColor(FOREGROUND_GREEN);
@@ -355,9 +406,9 @@ void Logger::write(Severity severity, QString const & msg, QString const & sourc
             fprintf(f, "\t(occurred in %s)\n\n", qPrintable(where));
 
         // Reset text colors
-#ifdef Q_OS_LINUX
-        fputs("\e[0m", f);
-#elif defined Q_OS_WIN32
+#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
+        fputs("\033[0m", f);
+#elif defined(Q_OS_WIN32)
         setColor(7);
 #endif
 
@@ -378,17 +429,21 @@ QString Logger::printf(char const * const fmt, ...) const
 #ifdef Q_OS_WIN32
     char str[4096];
 #else
-    char * str = 0;
+    char * str = nullptr;
 #endif
 
     va_list ap;
 #ifdef Q_OS_WIN32
     va_start(ap, fmt);
+#  ifdef Q_CC_GNU
+    vsnprintf(str, sizeof(str), fmt, ap);
+#  else
     _vsnprintf_s(str, sizeof(str), _TRUNCATE, fmt, ap);
+#  endif
     va_end(ap);
 #else
     ::va_start(ap, fmt);
-    if (::vasprintf(&str, fmt, ap)); // IF is needed to avoid the compiler warning
+    if (::vasprintf(&str, fmt, ap)) {} // IF is needed to avoid the compiler warning
     ::va_end(ap);
 #endif
 
@@ -436,6 +491,9 @@ FatalMsgHandler Logger::installFatalMsgHandler(FatalMsgHandler newHandler)
 
 LoggerSource * Logger::getSource(QString const & source)
 {
+    if (source.isEmpty() || source == mDefaultSource->name)
+        return mDefaultSource.data();
+
     QHash<QString, QExplicitlySharedDataPointer<LoggerSource> >::const_iterator it = mSources.constFind(source);
     if (it != mSources.constEnd())
         return it->data();
@@ -445,7 +503,7 @@ LoggerSource * Logger::getSource(QString const & source)
         mSources.insert(source, src);
 
         // Initialize the new source
-        src->init(source.isEmpty() ? mDefaultSource : source, iEnv::instance()->logDir(), iEnv::instance()->etcDir());
+        src->init(source);
 
         return src.data();
     }