/** @file @author Stefan Frings */ #include "logger.h" #include #include #include #include #include #include #include using namespace qtwebapp; Logger* Logger::defaultLogger=0; QThreadStorage*> Logger::logVars; QRecursiveMutex Logger::mutex; Logger::Logger(QObject* parent) : QObject(parent), msgFormat("{timestamp} {type} {msg}"), timestampFormat("yyyy-MM-dd HH:mm:ss.zzz"), minLevel(QtDebugMsg), bufferSize(0) {} Logger::Logger(const QString msgFormat, const QString timestampFormat, const QtMsgType minLevel, const int bufferSize, QObject* parent) :QObject(parent) { this->msgFormat=msgFormat; this->timestampFormat=timestampFormat; this->minLevel=minLevel; this->bufferSize=bufferSize; } void Logger::msgHandler(const QtMsgType type, const QString &message, const QString &file, const QString &function, const int line) { static QRecursiveMutex recursiveMutex; static QMutex nonRecursiveMutex; // Prevent multiple threads from calling this method simultaneoulsy. // But allow recursive calls, which is required to prevent a deadlock // if the logger itself produces an error message. recursiveMutex.lock(); // Fall back to stderr when this method has been called recursively. if (defaultLogger && nonRecursiveMutex.tryLock()) { defaultLogger->log(type, message, file, function, line); nonRecursiveMutex.unlock(); } else { fputs(qPrintable(message),stderr); fflush(stderr); } // Abort the program after logging a fatal message if (type==QtFatalMsg) { abort(); } recursiveMutex.unlock(); } #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) void Logger::msgHandler5(const QtMsgType type, const QMessageLogContext &context, const QString &message) { (void)(context); // suppress "unused parameter" warning msgHandler(type,message,context.file,context.function,context.line); } #else void Logger::msgHandler4(const QtMsgType type, const char* message) { msgHandler(type,message); } #endif Logger::~Logger() { if (defaultLogger==this) { #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) qInstallMessageHandler(0); #else qInstallMsgHandler(0); #endif defaultLogger=0; } } void Logger::write(const LogMessage* logMessage) { fputs(qPrintable(logMessage->toString(msgFormat,timestampFormat)),stderr); fflush(stderr); } void Logger::installMsgHandler() { defaultLogger=this; #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) qInstallMessageHandler(msgHandler5); #else qInstallMsgHandler(msgHandler4); #endif } void Logger::set(const QString& name, const QString& value) { mutex.lock(); if (!logVars.hasLocalData()) { logVars.setLocalData(new QHash); } logVars.localData()->insert(name,value); mutex.unlock(); } void Logger::clear(const bool buffer, const bool variables) { mutex.lock(); if (buffer && buffers.hasLocalData()) { QList* buffer=buffers.localData(); while (buffer && !buffer->isEmpty()) { LogMessage* logMessage=buffer->takeLast(); delete logMessage; } } if (variables && logVars.hasLocalData()) { logVars.localData()->clear(); } mutex.unlock(); } void Logger::log(const QtMsgType type, const QString& message, const QString &file, const QString &function, const int line) { mutex.lock(); // If the buffer is enabled, write the message into it if (bufferSize>0) { // Create new thread local buffer, if necessary if (!buffers.hasLocalData()) { buffers.setLocalData(new QList()); } QList* buffer=buffers.localData(); // Append the decorated log message LogMessage* logMessage=new LogMessage(type,message,logVars.localData(),file,function,line); buffer->append(logMessage); // Delete oldest message if the buffer became too large if (buffer->size()>bufferSize) { delete buffer->takeFirst(); } // If the type of the message is high enough, print the whole buffer if (type>=minLevel) { while (!buffer->isEmpty()) { LogMessage* logMessage=buffer->takeFirst(); write(logMessage); delete logMessage; } } } // Buffer is disabled, print the message if the type is high enough else { if (type>=minLevel) { LogMessage logMessage(type,message,logVars.localData(),file,function,line); write(&logMessage); } } mutex.unlock(); }