]> vaikene.ee Git - evaf/blobdiff - src/libs/Common/logger.cpp
Unified signal signatures so that the eVaf namespace is not included in the signature.
[evaf] / src / libs / Common / logger.cpp
index 0909fad56ff6e3afb18d349dcf6ce667de6b3c96..01d56876f9caa0e4bbedc53d33be751a7b0a6956 100644 (file)
@@ -19,7 +19,8 @@
 
 #include "logger.h"
 #include "iregistry.h"
-#include "ienv.h"
+#include "iapp.h"
+#include "version.h"
 
 #include <QtCore>
 
 
 //-------------------------------------------------------------------
 
-using namespace eVaf::Common;
-
-iLogger * iLogger::instance()
-{
-    static Internal::Logger singleton;
-    return &singleton;
-}
-
-
-//-------------------------------------------------------------------
-
-using namespace eVaf::Common::Internal;
-
-void defFatalMsgHandler(QString const & msg, QString const & source, QString const & where)
+void eVaf::Common::Internal::defFatalMsgHandler(QString const & msg, QString const & source, QString const & where)
 {
     Q_UNUSED(source);
 
@@ -65,6 +53,19 @@ void defFatalMsgHandler(QString const & msg, QString const & source, QString con
 
 //-------------------------------------------------------------------
 
+using namespace eVaf::Common;
+
+iLogger * iLogger::instance()
+{
+    static Internal::Logger singleton;
+    return &singleton;
+}
+
+
+//-------------------------------------------------------------------
+
+using namespace eVaf::Common::Internal;
+
 LoggerSource::LoggerSource()
     : QSharedData()
     , severity(iLogger::Fatal)
@@ -88,3 +89,372 @@ void LoggerSource::init(QString const & source, QString const & logDir, QString
 
 
 //-------------------------------------------------------------------
+
+/// 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);
+
+    dir.rename(f1, f2);
+}
+
+void LoggerWorker::writeToLogFile(LoggerSource const & src, QString const & msg)
+{
+    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
+
+    // Open the log file
+    if (f.open(mode)) {
+
+        // Write to the log file and then close the file
+        f.write(msg.toLocal8Bit());
+        f.close();
+
+        // Check the file size
+        if (src.maxSize > 0 && f.size() > src.maxSize) {
+
+            QDir dir(QFileInfo(src.fileName).dir());
+            QString baseName = QFileInfo(src.fileName).fileName();
+
+            // Delete the oldest backup file if the number of files has reached the maximum
+            if (src.maxCount > 0 && dir.exists(QString("%1.%2").arg(baseName).arg(src.maxCount - 1)))
+                dir.remove(QString("%1.%2").arg(baseName).arg(src.maxCount - 1));
+
+            // Shift backup files (.0 -> .1, .1 -> .2 etc)
+            renameBackupFile(dir, baseName, 0);
+
+            // Rename the current log file to the backup #0 file
+            dir.rename(baseName, baseName + ".0");
+
+        }
+    }
+}
+
+
+//-------------------------------------------------------------------
+
+Logger::Logger()
+    : iLogger()
+    , 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__));
+}
+
+Logger::~Logger()
+{
+    // Disconnect any potential receivers from this object
+    disconnect(this, SIGNAL(loggerEvent(Common::iLogger::Severity,QString,QString,QString)), 0, 0);
+
+    // Destroy the worker thread
+    if (mWorker) {
+        delete mWorker;
+        if (mThread) {
+            mThread->quit();
+            mThread->wait();
+            delete mThread;
+        }
+    }
+
+    write(Info, QString("%1 destroyed").arg(objectName()), 0, printf("%s:%s:%d", __FILE__, __FUNCTION__, __LINE__));
+}
+
+bool Logger::init()
+{
+    // Register our interface
+    iRegistry::instance()->registerInterface("iLogger", this);
+
+    // Clear existing sources in case the application was restarted
+    mSources.clear();
+
+    // Destroy the previous worker thread
+    if (mWorker) {
+        delete mWorker;
+        if (mThread) {
+            mThread->quit();
+            mThread->wait();
+            delete mThread;
+        }
+    }
+
+    // Create the worker thread
+    mWorker = new LoggerWorker;
+    mThread = new QThread;
+    mWorker->moveToThread(mThread);
+    mThread->start(QThread::IdlePriority);
+    connect(this, SIGNAL(writeToLogFile(LoggerSource,QString)), mWorker, SLOT(writeToLogFile(LoggerSource,QString)), Qt::QueuedConnection);
+
+    write(Info, QString("%1 initialized").arg(objectName()), 0, printf("%s:%s:%d", __FILE__, __FUNCTION__, __LINE__));
+
+    return true;
+}
+
+void Logger::setDefaultSource(QString const & source)
+{
+    mDefaultSource = source;
+}
+
+iLogger::Severity Logger::severity(QString const & source)
+{
+    return getSource(source)->severity;
+}
+
+void Logger::setSeverity(iLogger::Severity severity, QString const & source)
+{
+    getSource(source)->severity = severity;
+}
+
+uint Logger::maxSize(QString const & source)
+{
+    return getSource(source)->maxSize;
+}
+
+void Logger::setMaxSize(uint maxSize, QString const & source)
+{
+    getSource(source)->maxSize = maxSize;
+}
+
+uint Logger::maxCount(QString const & source)
+{
+    return getSource(source)->maxCount;
+}
+
+void Logger::setMaxCount(uint maxCount, QString const & source)
+{
+    getSource(source)->maxCount = maxCount;
+}
+
+void Logger::setConsoleSeverity(iLogger::Severity severity)
+{
+    mConsoleSeverity = severity;
+}
+
+void Logger::write(Severity severity, QString const & msg, QString const & source, QString const & where)
+{
+    static char const * const severityText[] =
+    {
+        "[NONE]   : ",
+        "[FATAL]  : ",
+        "[ERROR]  : ",
+        "[WARNING]: ",
+        "[INFO]   : ",
+        "[DEBUG]  : "
+    };
+
+    // Make sure that we don't output messages with the None severity
+    if (severity == iLogger::None)
+        return;
+
+    // Write to the log file
+    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");
+
+        // Severity
+        io << " " << severityText[severity];
+
+        // Message
+        io << msg;
+
+        // Location in the source file
+        if (!where.isEmpty())
+            io << " (occurred in " << where << ")";
+
+        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
+    if (source.isEmpty() && severity <= mConsoleSeverity && mConsoleSeverity != iLogger::None) {
+        FILE * f = (severity < iLogger::Info) ? stderr : stdout;
+
+        // Set text colors
+#ifdef Q_OS_LINUX
+        switch (severity) {
+            case iLogger::Info:
+                fprintf(f, "\e[32m"); // Green
+                break;
+            case iLogger::Warning:
+                fprintf(f, "\e[1m"); // Bold
+                break;
+            case iLogger::Error:
+                fprintf(f, "\e[31m"); // Red
+                break;
+            case iLogger::Fatal:
+                fprintf(f, "\e[31m\e[1m"); // Bold Red
+                break;
+            default:
+                fprintf(f, "\e[34m"); // Blue
+                break;
+        }
+#elif defined Q_OS_WIN32
+        switch (severity) {
+            case iLogger::Info:
+                setColor(FOREGROUND_GREEN);
+                break;
+            case iLogger::Warning:
+                setColor(FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);
+                break;
+            case iLogger::Error:
+                setColor(FOREGROUND_RED | FOREGROUND_INTENSITY);
+                break;
+            case iLogger::Fatal:
+                setColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY | BACKGROUND_RED);
+                break;
+            default:
+                setColor(FOREGROUND_BLUE);
+                break;
+        }
+#endif
+
+        // Output the message
+        fprintf(f, "%s %s\n", severityText[severity], qPrintable(msg));
+
+        // Add the location
+        if (!where.isEmpty())
+            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
+        setColor(7);
+#endif
+
+        // Flush the output
+        fflush(f);
+    }
+
+    // Inform others
+    emit loggerEvent(severity, msg, source, where);
+
+    // Handle fatal error messages
+    if (severity == iLogger::Fatal && mFatalMsgHandler)
+        mFatalMsgHandler(msg, source, where);
+}
+
+QString Logger::printf(char const * const fmt, ...) const
+{
+#ifdef Q_OS_WIN32
+    char str[4096];
+#else
+    char * str = 0;
+#endif
+
+    va_list ap;
+#ifdef Q_OS_WIN32
+    va_start(ap, fmt);
+    _vsnprintf_s(str, sizeof(str), _TRUNCATE, fmt, ap);
+    va_end(ap);
+#else
+    ::va_start(ap, fmt);
+    if (::vasprintf(&str, fmt, ap)); // IF is needed to avoid the compiler warning
+    ::va_end(ap);
+#endif
+
+    QString rval(str);
+
+#ifndef Q_OS_WIN32
+    ::free(str);
+#endif
+
+    return rval;
+}
+
+QString Logger::printable(QByteArray const & msg) const
+{
+    static char const * const ctrlChars[] = {
+        "NUL", "SOH", "STX", "ETX", "EOT", "ENW", "ACK", "BEL",
+        "BS",  "HT",  "LF",  "VT",  "FF",  "CR",  "SO",  "SI",
+        "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
+        "CAN", "EM",  "SUB", "ESC", "FS",  "GS",  "RS",  "US"
+    };
+
+    QString rval;
+    int sz = msg.size();
+    for (int i = 0; i < sz; ++i) {
+        uchar ch = uchar(msg.at(i));
+        if (ch < 32)
+            rval.append(QString("[%1]").arg(ctrlChars[ch]));
+        else if (ch < 127)
+            rval.append(msg.at(i));
+        else if (ch == 127)
+            rval.append("[DEL]");
+        else
+            rval.append(QString("[\\x%1]").arg(ch, 2, 16, QChar('0')));
+    }
+
+    return rval;
+}
+
+FatalMsgHandler Logger::installFatalMsgHandler(FatalMsgHandler newHandler)
+{
+    FatalMsgHandler oldHandler = mFatalMsgHandler;
+    mFatalMsgHandler = newHandler;
+    return oldHandler;
+}
+
+LoggerSource * Logger::getSource(QString const & source)
+{
+    QHash<QString, QExplicitlySharedDataPointer<LoggerSource> >::const_iterator it = mSources.constFind(source);
+    if (it != mSources.constEnd())
+        return it->data();
+    else {
+        // Create the new source
+        QExplicitlySharedDataPointer<LoggerSource> src(new LoggerSource);
+        mSources.insert(source, src);
+
+        // Initialize the new source
+        src->init(source.isEmpty() ? mDefaultSource : source, iApp::instance()->logDir(), iApp::instance()->etcDir());
+
+        return src.data();
+    }
+}
+
+#ifdef Q_OS_WIN32
+void Logger::setColor(short int c)
+{
+    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
+    SetConsoleTextAttribute(handle, c);
+}
+#endif