X-Git-Url: https://vaikene.ee/gitweb/gitweb.cgi?a=blobdiff_plain;f=src%2Flibs%2FCommon%2Flogger.cpp;h=01d56876f9caa0e4bbedc53d33be751a7b0a6956;hb=f01d61fb753b347bbff2ffb7f224650ac3f9d81e;hp=0909fad56ff6e3afb18d349dcf6ce667de6b3c96;hpb=4d81227da330c21c7aa0badc88bd5ad4467067fb;p=evaf diff --git a/src/libs/Common/logger.cpp b/src/libs/Common/logger.cpp index 0909fad..01d5687 100644 --- a/src/libs/Common/logger.cpp +++ b/src/libs/Common/logger.cpp @@ -19,7 +19,8 @@ #include "logger.h" #include "iregistry.h" -#include "ienv.h" +#include "iapp.h" +#include "version.h" #include @@ -36,20 +37,7 @@ //------------------------------------------------------------------- -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"); + + 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 >::const_iterator it = mSources.constFind(source); + if (it != mSources.constEnd()) + return it->data(); + else { + // Create the new source + QExplicitlySharedDataPointer 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