1
0
mirror of https://github.com/f4exb/sdrangel.git synced 2024-11-25 09:18:54 -05:00

Imported Stefan Frings' logging library from QtWebApp and use it in main window

This commit is contained in:
f4exb 2017-11-11 10:29:32 +01:00
parent be77fa82a3
commit 5f22045abb
18 changed files with 1088 additions and 0 deletions

View File

@ -243,6 +243,7 @@ if (BUILD_DEBIAN)
endif (BUILD_DEBIAN) endif (BUILD_DEBIAN)
add_subdirectory(httpserver) add_subdirectory(httpserver)
add_subdirectory(logging)
add_subdirectory(devices) add_subdirectory(devices)
add_subdirectory(plugins) add_subdirectory(plugins)

View File

@ -4,6 +4,7 @@ This is the httpserver part of QtWebApp from Stefan Frings
- [Link to the main page](http://stefanfrings.de/qtwebapp/index-en.html) - [Link to the main page](http://stefanfrings.de/qtwebapp/index-en.html)
- [Link to API documentation](http://stefanfrings.de/qtwebapp/api/index.html) - [Link to API documentation](http://stefanfrings.de/qtwebapp/api/index.html)
- [Link to tutorial](http://stefanfrings.de/qtwebapp/tutorial/index.html)
Files copied over from the original 'doc' folder: Files copied over from the original 'doc' folder:

38
logging/CMakeLists.txt Normal file
View File

@ -0,0 +1,38 @@
project(logging)
set(logging_SOURCES
dualfilelogger.cpp
filelogger.cpp
logger.cpp
logmessage.cpp
)
set(httpserver_HEADERS
dualfilelogger.h
filelogger.h
logger.h
logmessage.h
logglobal.h
)
include_directories(
.
${CMAKE_CURRENT_BINARY_DIR}
)
#include(${QT_USE_FILE})
add_definitions(${QT_DEFINITIONS})
add_definitions(-DQT_SHARED)
add_library(logging SHARED
${logging_SOURCES}
${logging_HEADERS_MOC}
)
target_link_libraries(logging
${QT_LIBRARIES}
)
qt5_use_modules(logging Core Network)
install(TARGETS logging DESTINATION lib)

View File

@ -0,0 +1,27 @@
/**
@file
@author Stefan Frings
*/
#include "dualfilelogger.h"
using namespace qtwebapp;
DualFileLogger::DualFileLogger(QSettings* firstSettings, QSettings* secondSettings, const int refreshInterval, QObject* parent)
:Logger(parent)
{
firstLogger=new FileLogger(firstSettings, refreshInterval, this);
secondLogger=new FileLogger(secondSettings, refreshInterval, this);
}
void DualFileLogger::log(const QtMsgType type, const QString& message, const QString &file, const QString &function, const int line)
{
firstLogger->log(type,message,file,function,line);
secondLogger->log(type,message,file,function,line);
}
void DualFileLogger::clear(const bool buffer, const bool variables)
{
firstLogger->clear(buffer,variables);
secondLogger->clear(buffer,variables);
}

74
logging/dualfilelogger.h Normal file
View File

@ -0,0 +1,74 @@
/**
@file
@author Stefan Frings
*/
#ifndef DUALFILELOGGER_H
#define DUALFILELOGGER_H
#include <QString>
#include <QSettings>
#include <QtGlobal>
#include "logglobal.h"
#include "logger.h"
#include "filelogger.h"
namespace qtwebapp {
/**
Logs messages into two log files simultaneously.
May be used to create two logfiles with different configuration settings.
@see FileLogger for a description of the two underlying loggers.
*/
class DECLSPEC DualFileLogger : public Logger {
Q_OBJECT
Q_DISABLE_COPY(DualFileLogger)
public:
/**
Constructor.
@param firstSettings Configuration settings for the first log file, usually stored in an INI file.
Must not be 0.
Settings are read from the current group, so the caller must have called settings->beginGroup().
Because the group must not change during runtime, it is recommended to provide a
separate QSettings instance to the logger that is not used by other parts of the program.
@param secondSettings Same as firstSettings, but for the second log file.
@param refreshInterval Interval of checking for changed config settings in msec, or 0=disabled
@param parent Parent object.
*/
DualFileLogger(QSettings* firstSettings, QSettings* secondSettings, const int refreshInterval=10000, QObject *parent = 0);
/**
Decorate and log the message, if type>=minLevel.
This method is thread safe.
@param type Message type (level)
@param message Message text
@param file Name of the source file where the message was generated (usually filled with the macro __FILE__)
@param function Name of the function where the message was generated (usually filled with the macro __LINE__)
@param line Line Number of the source file, where the message was generated (usually filles with the macro __func__ or __FUNCTION__)
@see LogMessage for a description of the message decoration.
*/
virtual void log(const QtMsgType type, const QString& message, const QString &file="", const QString &function="", const int line=0);
/**
Clear the thread-local data of the current thread.
This method is thread safe.
@param buffer Whether to clear the backtrace buffer
@param variables Whether to clear the log variables
*/
virtual void clear(const bool buffer=true, const bool variables=true);
private:
/** First logger */
FileLogger* firstLogger;
/** Second logger */
FileLogger* secondLogger;
};
} // end of namespace
#endif // DUALFILELOGGER_H

198
logging/filelogger.cpp Normal file
View File

@ -0,0 +1,198 @@
/**
@file
@author Stefan Frings
*/
#include "filelogger.h"
#include <QTime>
#include <QStringList>
#include <QThread>
#include <QtGlobal>
#include <QFile>
#include <QTimerEvent>
#include <QDir>
#include <QFileInfo>
#include <stdio.h>
using namespace qtwebapp;
void FileLogger::refreshSettings()
{
mutex.lock();
// Save old file name for later comparision with new settings
QString oldFileName=fileName;
// Load new config settings
settings->sync();
fileName=settings->value("fileName").toString();
// Convert relative fileName to absolute, based on the directory of the config file.
#ifdef Q_OS_WIN32
if (QDir::isRelativePath(fileName) && settings->format()!=QSettings::NativeFormat)
#else
if (QDir::isRelativePath(fileName))
#endif
{
QFileInfo configFile(settings->fileName());
fileName=QFileInfo(configFile.absolutePath(),fileName).absoluteFilePath();
}
maxSize=settings->value("maxSize",0).toLongLong();
maxBackups=settings->value("maxBackups",0).toInt();
msgFormat=settings->value("msgFormat","{timestamp} {type} {msg}").toString();
timestampFormat=settings->value("timestampFormat","yyyy-MM-dd hh:mm:ss.zzz").toString();
minLevel=static_cast<QtMsgType>(settings->value("minLevel",0).toInt());
bufferSize=settings->value("bufferSize",0).toInt();
// Create new file if the filename has been changed
if (oldFileName!=fileName)
{
fprintf(stderr,"Logging to %s\n",qPrintable(fileName));
close();
open();
}
mutex.unlock();
}
FileLogger::FileLogger(QSettings* settings, const int refreshInterval, QObject* parent)
: Logger(parent)
{
Q_ASSERT(settings!=0);
Q_ASSERT(refreshInterval>=0);
this->settings=settings;
file=0;
if (refreshInterval>0)
{
refreshTimer.start(refreshInterval,this);
}
flushTimer.start(1000,this);
refreshSettings();
}
FileLogger::~FileLogger()
{
close();
}
void FileLogger::write(const LogMessage* logMessage)
{
// Try to write to the file
if (file)
{
// Write the message
file->write(qPrintable(logMessage->toString(msgFormat,timestampFormat)));
// Flush error messages immediately, to ensure that no important message
// gets lost when the program terinates abnormally.
if (logMessage->getType()>=QtCriticalMsg)
{
file->flush();
}
// Check for success
if (file->error())
{
close();
qWarning("Cannot write to log file %s: %s",qPrintable(fileName),qPrintable(file->errorString()));
}
}
// Fall-back to the super class method, if writing failed
if (!file)
{
Logger::write(logMessage);
}
}
void FileLogger::open()
{
if (fileName.isEmpty())
{
qWarning("Name of logFile is empty");
}
else {
file=new QFile(fileName);
if (!file->open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
{
qWarning("Cannot open log file %s: %s",qPrintable(fileName),qPrintable(file->errorString()));
file=0;
}
}
}
void FileLogger::close()
{
if (file)
{
file->close();
delete file;
file=0;
}
}
void FileLogger::rotate() {
// count current number of existing backup files
int count=0;
forever
{
QFile bakFile(QString("%1.%2").arg(fileName).arg(count+1));
if (bakFile.exists())
{
++count;
}
else
{
break;
}
}
// Remove all old backup files that exceed the maximum number
while (maxBackups>0 && count>=maxBackups)
{
QFile::remove(QString("%1.%2").arg(fileName).arg(count));
--count;
}
// Rotate backup files
for (int i=count; i>0; --i) {
QFile::rename(QString("%1.%2").arg(fileName).arg(i),QString("%1.%2").arg(fileName).arg(i+1));
}
// Backup the current logfile
QFile::rename(fileName,fileName+".1");
}
void FileLogger::timerEvent(QTimerEvent* event)
{
if (!event)
{
return;
}
else if (event->timerId()==refreshTimer.timerId())
{
refreshSettings();
}
else if (event->timerId()==flushTimer.timerId() && file)
{
mutex.lock();
// Flush the I/O buffer
file->flush();
// Rotate the file if it is too large
if (maxSize>0 && file->size()>=maxSize)
{
close();
rotate();
open();
}
mutex.unlock();
}
}

127
logging/filelogger.h Normal file
View File

@ -0,0 +1,127 @@
/**
@file
@author Stefan Frings
*/
#ifndef FILELOGGER_H
#define FILELOGGER_H
#include <QtGlobal>
#include <QSettings>
#include <QFile>
#include <QMutex>
#include <QBasicTimer>
#include "logglobal.h"
#include "logger.h"
namespace qtwebapp {
/**
Logger that uses a text file for output. Settings are read from a
config file using a QSettings object. Config settings can be changed at runtime.
<p>
Example for the configuration settings:
<code><pre>
fileName=logs/QtWebApp.log
maxSize=1000000
maxBackups=2
minLevel=0
msgformat={timestamp} {typeNr} {type} thread={thread}: {msg}
timestampFormat=dd.MM.yyyy hh:mm:ss.zzz
bufferSize=0
</pre></code>
- fileName is the name of the log file, relative to the directory of the settings file.
In case of windows, if the settings are in the registry, the path is relative to the current
working directory.
- maxSize is the maximum size of that file in bytes. The file will be backed up and
replaced by a new file if it becomes larger than this limit. Please note that
the actual file size may become a little bit larger than this limit. Default is 0=unlimited.
- maxBackups defines the number of backup files to keep. Default is 0=unlimited.
- minLevel defines the minimum type of messages that are written (together with buffered messages) into the file. Defaults is 0=debug.
- msgFormat defines the decoration of log messages, see LogMessage class. Default is "{timestamp} {type} {msg}".
- timestampFormat defines the format of timestamps, see QDateTime::toString(). Default is "yyyy-MM-dd hh:mm:ss.zzz".
- bufferSize defines the size of the buffer. Default is 0=disabled.
@see set() describes how to set logger variables
@see LogMessage for a description of the message decoration.
@see Logger for a descrition of the buffer.
*/
class DECLSPEC FileLogger : public Logger {
Q_OBJECT
Q_DISABLE_COPY(FileLogger)
public:
/**
Constructor.
@param settings Configuration settings, usually stored in an INI file. Must not be 0.
Settings are read from the current group, so the caller must have called settings->beginGroup().
Because the group must not change during runtime, it is recommended to provide a
separate QSettings instance to the logger that is not used by other parts of the program.
@param refreshInterval Interval of checking for changed config settings in msec, or 0=disabled
@param parent Parent object
*/
FileLogger(QSettings* settings, const int refreshInterval=10000, QObject* parent = 0);
/**
Destructor. Closes the file.
*/
virtual ~FileLogger();
/** Write a message to the log file */
virtual void write(const LogMessage* logMessage);
protected:
/**
Handler for timer events.
Refreshes config settings or synchronizes I/O buffer, depending on the event.
This method is thread-safe.
@param event used to distinguish between the two timers.
*/
void timerEvent(QTimerEvent* event);
private:
/** Configured name of the log file */
QString fileName;
/** Configured maximum size of the file in bytes, or 0=unlimited */
long maxSize;
/** Configured maximum number of backup files, or 0=unlimited */
int maxBackups;
/** Pointer to the configuration settings */
QSettings* settings;
/** Output file, or 0=disabled */
QFile* file;
/** Timer for refreshing configuration settings */
QBasicTimer refreshTimer;
/** Timer for flushing the file I/O buffer */
QBasicTimer flushTimer;
/** Open the output file */
void open();
/** Close the output file */
void close();
/** Rotate files and delete some backups if there are too many */
void rotate();
/**
Refreshes the configuration settings.
This method is thread-safe.
*/
void refreshSettings();
};
} // end of namespace
#endif // FILELOGGER_H

192
logging/logger.cpp Normal file
View File

@ -0,0 +1,192 @@
/**
@file
@author Stefan Frings
*/
#include "logger.h"
#include <stdio.h>
#include <stdlib.h>
#include <QMutex>
#include <QDateTime>
#include <QThread>
#include <QObject>
using namespace qtwebapp;
Logger* Logger::defaultLogger=0;
QThreadStorage<QHash<QString,QString>*> Logger::logVars;
QMutex Logger::mutex;
Logger::Logger(QObject* parent)
: QObject(parent),
msgFormat("{timestamp} {type} {msg}"),
timestampFormat("dd.MM.yyyy 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 QMutex recursiveMutex(QMutex::Recursive);
static QMutex nonRecursiveMutex(QMutex::NonRecursive);
// 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 >= 0x050000
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 >= 0x050000
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 >= 0x050000
qInstallMessageHandler(msgHandler5);
#else
qInstallMsgHandler(msgHandler4);
#endif
}
void Logger::set(const QString& name, const QString& value)
{
mutex.lock();
if (!logVars.hasLocalData())
{
logVars.setLocalData(new QHash<QString,QString>);
}
logVars.localData()->insert(name,value);
mutex.unlock();
}
void Logger::clear(const bool buffer, const bool variables)
{
mutex.lock();
if (buffer && buffers.hasLocalData())
{
QList<LogMessage*>* 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<LogMessage*>());
}
QList<LogMessage*>* 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();
}

188
logging/logger.h Normal file
View File

@ -0,0 +1,188 @@
/**
@file
@author Stefan Frings
*/
#ifndef LOGGER_H
#define LOGGER_H
#include <QtGlobal>
#include <QThreadStorage>
#include <QHash>
#include <QStringList>
#include <QMutex>
#include <QObject>
#include "logglobal.h"
#include "logmessage.h"
namespace qtwebapp {
/**
Decorates and writes log messages to the console, stderr.
<p>
The decorator uses a predefined msgFormat string to enrich log messages
with additional information (e.g. timestamp).
<p>
The msgFormat string and also the message text may contain additional
variable names in the form <i>{name}</i> that are filled by values
taken from a static thread local dictionary.
<p>
The logger keeps a configurable number of messages in a ring-buffer.
A log message with a severity >= minLevel flushes the buffer,
so the stored messages get written out. If the buffer is disabled, then
only messages with severity >= minLevel are written out.
<p>
If you enable the buffer and use minLevel=2, then the application logs
only errors together with some buffered debug messages. But as long no
error occurs, nothing gets written out.
<p>
Each thread has it's own buffer.
<p>
The logger can be registered to handle messages from
the static global functions qDebug(), qWarning(), qCritical() and qFatal().
@see set() describes how to set logger variables
@see LogMessage for a description of the message decoration.
@warning You should prefer a derived class, for example FileLogger,
because logging to the console is less useful.
*/
class DECLSPEC Logger : public QObject {
Q_OBJECT
Q_DISABLE_COPY(Logger)
public:
/**
Constructor.
Uses the same defaults as the other constructor.
@param parent Parent object
*/
Logger(QObject* parent);
/**
Constructor.
@param msgFormat Format of the decoration, e.g. "{timestamp} {type} thread={thread}: {msg}"
@param timestampFormat Format of timestamp, e.g. "dd.MM.yyyy hh:mm:ss.zzz"
@param minLevel Minimum severity that genertes an output (0=debug, 1=warning, 2=critical, 3=fatal).
@param bufferSize Size of the backtrace buffer, number of messages per thread. 0=disabled.
@param parent Parent object
@see LogMessage for a description of the message decoration.
*/
Logger(const QString msgFormat="{timestamp} {type} {msg}", const QString timestampFormat="dd.MM.yyyy hh:mm:ss.zzz", const QtMsgType minLevel=QtDebugMsg, const int bufferSize=0, QObject* parent = 0);
/** Destructor */
virtual ~Logger();
/**
Decorate and log the message, if type>=minLevel.
This method is thread safe.
@param type Message type (level)
@param message Message text
@param file Name of the source file where the message was generated (usually filled with the macro __FILE__)
@param function Name of the function where the message was generated (usually filled with the macro __LINE__)
@param line Line Number of the source file, where the message was generated (usually filles with the macro __func__ or __FUNCTION__)
@see LogMessage for a description of the message decoration.
*/
virtual void log(const QtMsgType type, const QString& message, const QString &file="", const QString &function="", const int line=0);
/**
Installs this logger as the default message handler, so it
can be used through the global static logging functions (e.g. qDebug()).
*/
void installMsgHandler();
/**
Sets a thread-local variable that may be used to decorate log messages.
This method is thread safe.
@param name Name of the variable
@param value Value of the variable
*/
static void set(const QString& name, const QString& value);
/**
Clear the thread-local data of the current thread.
This method is thread safe.
@param buffer Whether to clear the backtrace buffer
@param variables Whether to clear the log variables
*/
virtual void clear(const bool buffer=true, const bool variables=true);
protected:
/** Format string for message decoration */
QString msgFormat;
/** Format string of timestamps */
QString timestampFormat;
/** Minimum level of message types that are written out */
QtMsgType minLevel;
/** Size of backtrace buffer, number of messages per thread. 0=disabled */
int bufferSize;
/** Used to synchronize access of concurrent threads */
static QMutex mutex;
/**
Decorate and write a log message to stderr. Override this method
to provide a different output medium.
*/
virtual void write(const LogMessage* logMessage);
private:
/** Pointer to the default logger, used by msgHandler() */
static Logger* defaultLogger;
/**
Message Handler for the global static logging functions (e.g. qDebug()).
Forward calls to the default logger.
<p>
In case of a fatal message, the program will abort.
Variables in the in the message are replaced by their values.
This method is thread safe.
@param type Message type (level)
@param message Message text
@param file Name of the source file where the message was generated (usually filled with the macro __FILE__)
@param function Name of the function where the message was generated (usually filled with the macro __LINE__)
@param line Line Number of the source file, where the message was generated (usually filles with the macro __func__ or __FUNCTION__)
*/
static void msgHandler(const QtMsgType type, const QString &message, const QString &file="", const QString &function="", const int line=0);
#if QT_VERSION >= 0x050000
/**
Wrapper for QT version 5.
@param type Message type (level)
@param context Message context
@param message Message text
@see msgHandler()
*/
static void msgHandler5(const QtMsgType type, const QMessageLogContext& context, const QString &message);
#else
/**
Wrapper for QT version 4.
@param type Message type (level)
@param message Message text
@see msgHandler()
*/
static void msgHandler4(const QtMsgType type, const char * message);
#endif
/** Thread local variables to be used in log messages */
static QThreadStorage<QHash<QString,QString>*> logVars;
/** Thread local backtrace buffers */
QThreadStorage<QList<LogMessage*>*> buffers;
};
} // end of namespace
#endif // LOGGER_H

6
logging/logging.pri Normal file
View File

@ -0,0 +1,6 @@
INCLUDEPATH += $$PWD
DEPENDPATH += $$PWD
HEADERS += $$PWD/logglobal.h $$PWD/logmessage.h $$PWD/logger.h $$PWD/filelogger.h $$PWD/dualfilelogger.h
SOURCES += $$PWD/logmessage.cpp $$PWD/logger.cpp $$PWD/filelogger.cpp $$PWD/dualfilelogger.cpp

24
logging/logglobal.h Normal file
View File

@ -0,0 +1,24 @@
/**
@file
@author Stefan Frings
*/
#ifndef LOGGLOBAL_H
#define LOGGLOBAL_H
#include <QtGlobal>
// This is specific to Windows dll's
#if defined(Q_OS_WIN)
#if defined(QTWEBAPPLIB_EXPORT)
#define DECLSPEC Q_DECL_EXPORT
#elif defined(QTWEBAPPLIB_IMPORT)
#define DECLSPEC Q_DECL_IMPORT
#endif
#endif
#if !defined(DECLSPEC)
#define DECLSPEC
#endif
#endif // LOGGLOBAL_H

90
logging/logmessage.cpp Normal file
View File

@ -0,0 +1,90 @@
/**
@file
@author Stefan Frings
*/
#include "logmessage.h"
#include <QThread>
using namespace qtwebapp;
LogMessage::LogMessage(const QtMsgType type, const QString& message, QHash<QString, QString>* logVars, const QString &file, const QString &function, const int line)
{
this->type=type;
this->message=message;
this->file=file;
this->function=function;
this->line=line;
timestamp=QDateTime::currentDateTime();
threadId=QThread::currentThreadId();
// Copy the logVars if not null,
// so that later changes in the original do not affect the copy
if (logVars)
{
this->logVars=*logVars;
}
}
QString LogMessage::toString(const QString& msgFormat, const QString& timestampFormat) const
{
QString decorated=msgFormat+"\n";
decorated.replace("{msg}",message);
if (decorated.contains("{timestamp}"))
{
decorated.replace("{timestamp}",timestamp.toString(timestampFormat));
}
QString typeNr;
typeNr.setNum(type);
decorated.replace("{typeNr}",typeNr);
switch (type)
{
case QtDebugMsg:
decorated.replace("{type}","DEBUG ");
break;
case QtWarningMsg:
decorated.replace("{type}","WARNING ");
break;
case QtCriticalMsg:
decorated.replace("{type}","CRITICAL");
break;
case QtFatalMsg:
decorated.replace("{type}","FATAL ");
break;
#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
case QtInfoMsg:
decorated.replace("{type}","INFO ");
break;
#endif
default:
decorated.replace("{type}",typeNr);
}
decorated.replace("{file}",file);
decorated.replace("{function}",function);
decorated.replace("{line}",QString::number(line));
QString threadId;
threadId.setNum((unsigned long)QThread::currentThreadId());
decorated.replace("{thread}",threadId);
// Fill in variables
if (decorated.contains("{") && !logVars.isEmpty())
{
QList<QString> keys=logVars.keys();
foreach (QString key, keys)
{
decorated.replace("{"+key+"}",logVars.value(key));
}
}
return decorated;
}
QtMsgType LogMessage::getType() const
{
return type;
}

97
logging/logmessage.h Normal file
View File

@ -0,0 +1,97 @@
/**
@file
@author Stefan Frings
*/
#ifndef LOGMESSAGE_H
#define LOGMESSAGE_H
#include <QtGlobal>
#include <QDateTime>
#include <QHash>
#include "logglobal.h"
namespace qtwebapp {
/**
Represents a single log message together with some data
that are used to decorate the log message.
The following variables may be used in the message and in msgFormat:
- {timestamp} Date and time of creation
- {typeNr} Type of the message in numeric format (0-3)
- {type} Type of the message in string format (DEBUG, WARNING, CRITICAL, FATAL)
- {thread} ID number of the thread
- {msg} Message text
- {xxx} For any user-defined logger variable
Plus some new variables since QT 5.0, only filled when compiled in debug mode:
- {file} Filename where the message was generated
- {function} Function where the message was generated
- {line} Line number where the message was generated
*/
class DECLSPEC LogMessage
{
Q_DISABLE_COPY(LogMessage)
public:
/**
Constructor. All parameters are copied, so that later changes to them do not
affect this object.
@param type Type of the message
@param message Message text
@param logVars Logger variables, 0 is allowed
@param file Name of the source file where the message was generated
@param function Name of the function where the message was generated
@param line Line Number of the source file, where the message was generated
*/
LogMessage(const QtMsgType type, const QString& message, QHash<QString,QString>* logVars, const QString &file, const QString &function, const int line);
/**
Returns the log message as decorated string.
@param msgFormat Format of the decoration. May contain variables and static text,
e.g. "{timestamp} {type} thread={thread}: {msg}".
@param timestampFormat Format of timestamp, e.g. "dd.MM.yyyy hh:mm:ss.zzz", see QDateTime::toString().
@see QDatetime for a description of the timestamp format pattern
*/
QString toString(const QString& msgFormat, const QString& timestampFormat) const;
/**
Get the message type.
*/
QtMsgType getType() const;
private:
/** Logger variables */
QHash<QString,QString> logVars;
/** Date and time of creation */
QDateTime timestamp;
/** Type of the message */
QtMsgType type;
/** ID number of the thread */
Qt::HANDLE threadId;
/** Message text */
QString message;
/** Filename where the message was generated */
QString file;
/** Function name where the message was generated */
QString function;
/** Line number where the message was generated */
int line;
};
} // end of namespace
#endif // LOGMESSAGE_H

9
logging/readme.md Normal file
View File

@ -0,0 +1,9 @@
## QtWebApp logger ##
This is based on the logging part of QtWebApp from Stefan Frings
- [Link to the main page](http://stefanfrings.de/qtwebapp/index-en.html)
- [Link to API documentation](http://stefanfrings.de/qtwebapp/api/index.html)
- [Link to tutorial](http://stefanfrings.de/qtwebapp/tutorial/index.html)
Some changes have been made to support the option of having a console logging plus optional file logging

View File

@ -23,6 +23,7 @@ SUBDIRS += devices
SUBDIRS += mbelib SUBDIRS += mbelib
SUBDIRS += dsdcc SUBDIRS += dsdcc
SUBDIRS += httpserver SUBDIRS += httpserver
SUBDIRS += logging
CONFIG(MINGW64)SUBDIRS += cm256cc CONFIG(MINGW64)SUBDIRS += cm256cc
SUBDIRS += plugins/samplesource/filesource SUBDIRS += plugins/samplesource/filesource
CONFIG(MINGW64)SUBDIRS += plugins/samplesource/sdrdaemonsource CONFIG(MINGW64)SUBDIRS += plugins/samplesource/sdrdaemonsource

View File

@ -147,6 +147,7 @@ add_library(sdrgui SHARED
include_directories( include_directories(
. .
${CMAKE_SOURCE_DIR}/sdrbase ${CMAKE_SOURCE_DIR}/sdrbase
${CMAKE_SOURCE_DIR}/logging
${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}
${OPENGL_INCLUDE_DIR} ${OPENGL_INCLUDE_DIR}
) )
@ -155,6 +156,7 @@ target_link_libraries(sdrgui
${QT_LIBRARIES} ${QT_LIBRARIES}
${OPENGL_LIBRARIES} ${OPENGL_LIBRARIES}
sdrbase sdrbase
logging
) )
set_target_properties(sdrgui PROPERTIES DEFINE_SYMBOL "sdrangel_EXPORTS") set_target_properties(sdrgui PROPERTIES DEFINE_SYMBOL "sdrangel_EXPORTS")

View File

@ -50,6 +50,7 @@
#include "plugin/pluginapi.h" #include "plugin/pluginapi.h"
#include "gui/glspectrum.h" #include "gui/glspectrum.h"
#include "gui/glspectrumgui.h" #include "gui/glspectrumgui.h"
#include "logger.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
@ -71,6 +72,9 @@ MainWindow::MainWindow(QWidget* parent) :
m_centerFrequency(0), m_centerFrequency(0),
m_sampleFileName(std::string("./test.sdriq")) m_sampleFileName(std::string("./test.sdriq"))
{ {
m_logger = new qtwebapp::Logger(this);
m_logger->installMsgHandler();
qDebug() << "MainWindow::MainWindow: start"; qDebug() << "MainWindow::MainWindow: start";
m_instance = this; m_instance = this;
@ -202,6 +206,9 @@ MainWindow::~MainWindow()
delete m_showSystemWidget; delete m_showSystemWidget;
delete ui; delete ui;
qDebug() << "MainWindow::~MainWindow: end";
delete m_logger;
} }
void MainWindow::addSourceDevice() void MainWindow::addSourceDevice()

View File

@ -50,6 +50,10 @@ class DeviceUISet;
class PluginInterface; class PluginInterface;
class QWidget; class QWidget;
namespace qtwebapp {
class Logger;
}
namespace Ui { namespace Ui {
class MainWindow; class MainWindow;
} }
@ -110,6 +114,8 @@ private:
quint64 m_centerFrequency; quint64 m_centerFrequency;
std::string m_sampleFileName; std::string m_sampleFileName;
qtwebapp::Logger *m_logger;
void loadSettings(); void loadSettings();
void loadPresetSettings(const Preset* preset, int tabIndex); void loadPresetSettings(const Preset* preset, int tabIndex);
void savePresetSettings(Preset* preset, int tabIndex); void savePresetSettings(Preset* preset, int tabIndex);