]> vaikene.ee Git - evaf/commitdiff
More work on the common library. The project builds not without errors, but we have...
authorEnar Väikene <enar.vaikene@mt.com>
Fri, 22 Apr 2011 12:36:35 +0000 (15:36 +0300)
committerEnar Väikene <enar.vaikene@mt.com>
Fri, 22 Apr 2011 12:36:35 +0000 (15:36 +0300)
src/libs/Common/CMakeLists.txt
src/libs/Common/globals.cpp [new file with mode: 0644]
src/libs/Common/ilogger.h
src/libs/Common/logger.cpp
src/libs/Common/logger.h
src/main/GUI/exithandler.cpp

index 1d166475d6a8dc1aa28b98d1fe1fefb9913ec692..20eca773134d7017b3a41bf1943cf667be21f2b0 100644 (file)
@@ -19,6 +19,8 @@ set(SRCS
     env.cpp
     event.cpp
     eventqueue.cpp
+    globals.cpp
+    logger.cpp
     registry.cpp
 )
 
@@ -32,6 +34,7 @@ set(MOC_HDRS
     app.h
     env.h
     eventqueue.h
+    logger.h
     registry.h
 )
 
diff --git a/src/libs/Common/globals.cpp b/src/libs/Common/globals.cpp
new file mode 100644 (file)
index 0000000..dfb1595
--- /dev/null
@@ -0,0 +1,65 @@
+/**
+ * @file Common/globals.cpp
+ * @brief Global constants and macros for eVaf
+ * @author Enar Vaikene
+ *
+ * Copyright (c) 2011 Enar Vaikene
+ *
+ * This file is part of the eVaf C++ cross-platform application development framework.
+ *
+ * This file can be used under the terms of the GNU General Public License
+ * version 3.0 as published by the Free Software Foundation and appearing in
+ * the file LICENSE included in the packaging of this file. Please review the
+ * the following information to ensure the GNU General Public License version
+ * 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
+ *
+ * Alternatively, this file may be used in accordance with the Commercial License
+ * Agreement provided with the Software.
+ */
+
+#include "globals.h"
+#include "env.h"
+#include "app.h"
+#include "logger.h"
+#include "version.h"
+#include "ilogger.h"
+
+#include <QCoreApplication>
+
+
+//-------------------------------------------------------------------
+
+bool eVaf::Common::init()
+{
+    if (QCoreApplication::instance() == 0) {
+        EVAF_FATAL_ERROR("QApplication is not instantiated");
+        return false;
+    }
+
+    EVAF_INFO("Initializing %s.Globals", VER_MODULE_NAME_STR);
+
+    // Initialize all the common interface implementations in the proper sequence
+
+    eVaf::Common::Internal::Env * env =
+        qobject_cast<eVaf::Common::Internal::Env *>(eVaf::Common::iEnv::instance());
+    if (env) {
+        if (!env->init())
+            return false;
+    }
+    eVaf::Common::Internal::Logger * logger =
+        qobject_cast<eVaf::Common::Internal::Logger *>(eVaf::Common::iLogger::instance());
+    if (logger) {
+        if (!logger->init())
+            return false;
+    }
+    eVaf::Common::Internal::App * app =
+        qobject_cast<eVaf::Common::Internal::App *>(eVaf::Common::iApp::instance());
+    if (app) {
+        if (!app->init())
+            return false;
+    }
+
+    EVAF_INFO("%s.Globals initialized", VER_MODULE_NAME_STR);
+
+    return true;
+}
index 81d80160fbcfb707648f3fce57f953731d387e72..cf8e5c74dfe9d61a6db8854ebd36383eaa8c5bb3 100644 (file)
@@ -99,7 +99,7 @@ public:
      * Returns the current severity level
      * @param source Name of the source or default if omitted.
      */
-    virtual Severity severity(QString const & source = 0) const = 0;
+    virtual Severity severity(QString const & source = 0) = 0;
 
     /**
      * Changes the current severity level.
@@ -116,7 +116,7 @@ public:
      * Returns the current maximum size of log files in KiB.
      * @param source Name of the source or default if omitted.
      */
-    virtual uint maxSize(QString const & source = 0) const = 0;
+    virtual uint maxSize(QString const & source = 0) = 0;
 
     /**
      * Changes the maximum size of log files for the given source
@@ -136,7 +136,7 @@ public:
      * Returns the maximum number of log files.
      * @param source Name of the source or default if omitted.
      */
-    virtual uint maxCount(QString const & source = 0) const = 0;
+    virtual uint maxCount(QString const & source = 0) = 0;
 
     /**
      * Changes the maximum number of log files
@@ -226,6 +226,22 @@ public:
      */
     virtual FatalMsgHandler installFatalMsgHandler(FatalMsgHandler newHandler) = 0;
 
+
+signals:
+
+    /**
+     * Logger event signal
+     * @param severity Severity of the message
+     * @param text The message
+     * @param source Source of the message
+     * @param where Where the message was output
+     *
+     * This signal is emitted for every message output with the iLogger interface. Connect
+     * your receiver to this signal if you want to add your own message handling. For example,
+     * use this signal to show messages in a log window etc.
+     */
+    void loggerEvent(Severity severity, QString const & text, QString const & source, QString const & where);
+
 };
 
 } // namespace eVaf::Common
index 0909fad56ff6e3afb18d349dcf6ce667de6b3c96..98d37708cc4f4630a15c8a5c6d86a92d4a268c05 100644 (file)
@@ -20,6 +20,7 @@
 #include "logger.h"
 #include "iregistry.h"
 #include "ienv.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,370 @@ 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(eVaf::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;
+    ::va_start(ap, fmt);
+#ifdef Q_OS_WIN32
+    _vsnprintf_s(str, sizeof(str), _TRUNCATE, fmt, ap);
+#else
+    if (::vasprintf(&str, fmt, ap)); // IF is needed to avoid the compiler warning
+#endif
+    ::va_end(ap);
+
+    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, iEnv::instance()->logDir(), iEnv::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
index c47be4f409d88306728d9e52fd356b19c6e07281..1dfdc62408a805f0ed985b93b36eb638898bbdc8 100644 (file)
 #include "ilogger.h"
 
 #include <QObject>
+#include <QString>
+#include <QHash>
+#include <QExplicitlySharedDataPointer>
+#include <QSharedData>
+
+class QThread;
 
 
 namespace eVaf {
@@ -96,6 +102,30 @@ public: // Members (we don't bother adding getter/setter functions)
 
 };
 
+/**
+ * Worker class for the logger.
+ *
+ * This class separates potentially expensive I/O from the iLogger interface making sure
+ * that writing to the log file does not block the main thread.
+ */
+class LoggerWorker : public QObject
+{
+    Q_OBJECT
+
+public slots:
+
+    /**
+     * Writes a message to the log file
+     * @param src The logger source
+     * @param msg The message
+     *
+     * This function writes the message to the log file. It also controls the size and
+     * number of log files.
+     */
+    void writeToLogFile(LoggerSource const & src, QString const & msg);
+
+};
+
 /**
  * iLogger interface implementation.
  *
@@ -125,48 +155,61 @@ public:
 
     virtual void setDefaultSource(QString const & source);
 
-    virtual Severity severity(QString const & source = 0) const;
+    virtual iLogger::Severity severity(QString const & source = 0);
 
-    virtual void setSeverity(Severity severity, QString const & source = 0);
+    virtual void setSeverity(iLogger::Severity severity, QString const & source = 0);
 
-    virtual uint maxSize(QString const & source = 0) const;
+    virtual uint maxSize(QString const & source = 0);
 
     virtual void setMaxSize(uint maxSize, QString const & source = 0);
 
-    virtual uint maxCount(QString const & source = 0) const;
+    virtual uint maxCount(QString const & source = 0);
 
     virtual void setMaxCount(uint maxCount, QString const & source = 0);
 
-    virtual Severity consoleSeverity() const { return mConsoleSeverity; }
+    virtual iLogger::Severity consoleSeverity() const { return mConsoleSeverity; }
 
-    virtual void setConsoleSeverity(Severity severity);
+    virtual void setConsoleSeverity(iLogger::Severity severity);
 
     virtual void write(Severity severity, QString const & msg, QString const & source = 0, QString const & where = 0);
 
     virtual QString printf(char const * const fmt, ...) const;
 
+    virtual QString printable(QByteArray const & msg) const;
+
     virtual FatalMsgHandler installFatalMsgHandler(FatalMsgHandler newHandler);
 
 
+signals:
+
+    void writeToLogFile(LoggerSource const & src, QString const & msg);
+
+
 private: // Members
 
     /// Current fatal error message handler
     FatalMsgHandler mFatalMsgHandler;
 
+    /// Console output severity level
+    iLogger::Severity mConsoleSeverity;
+
     /// Current default source (defaults to "evaf")
     QString mDefaultSource;
 
     /// Logger sources
     QHash<QString, QExplicitlySharedDataPointer<LoggerSource> > mSources;
 
+    /// Worker thread
+    QThread * mThread;
 
-private: // Methods
+    /// Worker object
+    LoggerWorker * mWorker;
 
-    /// Returns the source by the name
-    LoggerSource * getSource(QString const & name) const;
 
-    /// Creates a new source
-    LoggerSource * addSource(QString const & name);
+private: // Methods
+
+    /// Returns the source by the name. The source is created if it does not exist yet.
+    LoggerSource * getSource(QString const & name);
 
 #ifdef Q_OS_WIN32
     /// Changes text colors on the Windows console
index 99d376f54f6a036d0035365359a03561bc539451..f0ad4bd36bc8a45c53d3f42ee3d9195a1641b1e3 100644 (file)
@@ -107,9 +107,8 @@ static BOOL WINAPI signalHandler(DWORD sig)
 } // namespace eVaf::GUI
 } // namespace eVaf
 
-using namespace eVaf::GUI::Internal;
 
-bool installExitHandler()
+bool eVaf::GUI::Internal::installExitHandler()
 {
 
 #ifdef Q_OS_LINUX