mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
Make logger thread safe (#885)
This commit is contained in:
parent
45303e1b64
commit
4880e31562
@ -4,7 +4,6 @@
|
|||||||
requestLoggingStart();
|
requestLoggingStart();
|
||||||
|
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
|
||||||
var messages;
|
var messages;
|
||||||
var loguplmess = "";
|
var loguplmess = "";
|
||||||
var reportUrl = 'https://report.hyperion-project.org/#';
|
var reportUrl = 'https://report.hyperion-project.org/#';
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
//qt
|
//qt
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
class AuthTable;
|
class AuthTable;
|
||||||
class MetaTable;
|
class MetaTable;
|
||||||
|
@ -3,29 +3,33 @@
|
|||||||
// QT includes
|
// QT includes
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QString>
|
#include <QString>
|
||||||
|
#include <QMap>
|
||||||
|
#include <QAtomicInteger>
|
||||||
|
#include <QList>
|
||||||
|
#include <QMutex>
|
||||||
|
|
||||||
// stl includes
|
// stl includes
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <map>
|
|
||||||
#include <QVector>
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <utils/global_defines.h>
|
#include <utils/global_defines.h>
|
||||||
|
|
||||||
|
#define LOG_MESSAGE(severity, logger, ...) (logger)->Message(severity, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
|
||||||
|
|
||||||
// standard log messages
|
// standard log messages
|
||||||
#define Debug(logger, ...) (logger)->Message(Logger::DEBUG , __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
|
#define Debug(logger, ...) LOG_MESSAGE(Logger::DEBUG , logger, __VA_ARGS__)
|
||||||
#define Info(logger, ...) (logger)->Message(Logger::INFO , __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
|
#define Info(logger, ...) LOG_MESSAGE(Logger::INFO , logger, __VA_ARGS__)
|
||||||
#define Warning(logger, ...) (logger)->Message(Logger::WARNING, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
|
#define Warning(logger, ...) LOG_MESSAGE(Logger::WARNING, logger, __VA_ARGS__)
|
||||||
#define Error(logger, ...) (logger)->Message(Logger::ERRORR , __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
|
#define Error(logger, ...) LOG_MESSAGE(Logger::ERRORR , logger, __VA_ARGS__)
|
||||||
|
|
||||||
// conditional log messages
|
// conditional log messages
|
||||||
#define DebugIf(condition, logger, ...) if (condition) (logger)->Message(Logger::DEBUG , __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
|
#define DebugIf(condition, logger, ...) if (condition) Debug(logger, __VA_ARGS__)
|
||||||
#define InfoIf(condition, logger, ...) if (condition) (logger)->Message(Logger::INFO , __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
|
#define InfoIf(condition, logger, ...) if (condition) Info(logger, __VA_ARGS__)
|
||||||
#define WarningIf(condition, logger, ...) if (condition) (logger)->Message(Logger::WARNING , __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
|
#define WarningIf(condition, logger, ...) if (condition) Warning(logger, __VA_ARGS__)
|
||||||
#define ErrorIf(condition, logger, ...) if (condition) (logger)->Message(Logger::ERRORR , __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
|
#define ErrorIf(condition, logger, ...) if (condition) Error(logger, __VA_ARGS__)
|
||||||
|
|
||||||
// ================================================================
|
// ================================================================
|
||||||
|
|
||||||
@ -35,62 +39,68 @@ class Logger : public QObject
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
enum LogLevel {
|
enum LogLevel {
|
||||||
UNSET,
|
UNSET = 0,
|
||||||
DEBUG,
|
DEBUG = 1,
|
||||||
INFO,
|
INFO = 2,
|
||||||
WARNING,
|
WARNING = 3,
|
||||||
ERRORR,
|
ERRORR = 4,
|
||||||
OFF
|
OFF = 5
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef struct
|
struct T_LOG_MESSAGE
|
||||||
{
|
{
|
||||||
QString appName;
|
QString appName;
|
||||||
QString loggerName;
|
QString loggerName;
|
||||||
QString function;
|
QString function;
|
||||||
unsigned int line;
|
unsigned int line;
|
||||||
QString fileName;
|
QString fileName;
|
||||||
time_t utime;
|
uint64_t utime;
|
||||||
QString message;
|
QString message;
|
||||||
LogLevel level;
|
LogLevel level;
|
||||||
QString levelString;
|
QString levelString;
|
||||||
} T_LOG_MESSAGE;
|
};
|
||||||
|
|
||||||
static Logger* getInstance(QString name="", LogLevel minLevel=Logger::INFO);
|
static Logger* getInstance(const QString & name = "", LogLevel minLevel=Logger::INFO);
|
||||||
static void deleteInstance(QString name="");
|
static void deleteInstance(const QString & name = "");
|
||||||
static void setLogLevel(LogLevel level, QString name="");
|
static void setLogLevel(LogLevel level, const QString & name = "");
|
||||||
static LogLevel getLogLevel(QString name="");
|
static LogLevel getLogLevel(const QString & name = "");
|
||||||
|
|
||||||
void Message(LogLevel level, const char* sourceFile, const char* func, unsigned int line, const char* fmt, ...);
|
void Message(LogLevel level, const char* sourceFile, const char* func, unsigned int line, const char* fmt, ...);
|
||||||
void setMinLevel(LogLevel level) { _minLevel = level; }
|
void setMinLevel(LogLevel level) { _minLevel = static_cast<int>(level); }
|
||||||
LogLevel getMinLevel() { return _minLevel; }
|
LogLevel getMinLevel() { return static_cast<LogLevel>(int(_minLevel)); }
|
||||||
|
QString getName() const { return _name; }
|
||||||
|
QString getAppName() const { return _appname; }
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void newLogMessage(Logger::T_LOG_MESSAGE);
|
void newLogMessage(Logger::T_LOG_MESSAGE);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Logger( QString name="", LogLevel minLevel=INFO);
|
Logger(const QString & name="", LogLevel minLevel = INFO);
|
||||||
~Logger();
|
~Logger();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::map<QString,Logger*> *LoggerMap;
|
void write(const Logger::T_LOG_MESSAGE & message) const;
|
||||||
static LogLevel GLOBAL_MIN_LOG_LEVEL;
|
|
||||||
|
|
||||||
QString _name;
|
static QMutex MapLock;
|
||||||
QString _appname;
|
static QMap<QString,Logger*> LoggerMap;
|
||||||
LogLevel _minLevel;
|
static QAtomicInteger<int> GLOBAL_MIN_LOG_LEVEL;
|
||||||
bool _syslogEnabled;
|
|
||||||
unsigned _loggerId;
|
const QString _name;
|
||||||
|
const QString _appname;
|
||||||
|
const bool _syslogEnabled;
|
||||||
|
const unsigned _loggerId;
|
||||||
|
|
||||||
|
/* Only non-const member, hence the atomic */
|
||||||
|
QAtomicInteger<int> _minLevel;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class LoggerManager : public QObject
|
class LoggerManager : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static LoggerManager* getInstance();
|
static LoggerManager* getInstance();
|
||||||
QVector<Logger::T_LOG_MESSAGE>* getLogMessageBuffer() { return &_logMessageBuffer; }
|
const QList<Logger::T_LOG_MESSAGE>* getLogMessageBuffer() const { return &_logMessageBuffer; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void handleNewLogMessage(const Logger::T_LOG_MESSAGE&);
|
void handleNewLogMessage(const Logger::T_LOG_MESSAGE&);
|
||||||
@ -101,8 +111,7 @@ signals:
|
|||||||
protected:
|
protected:
|
||||||
LoggerManager();
|
LoggerManager();
|
||||||
|
|
||||||
static LoggerManager* _instance;
|
QList<Logger::T_LOG_MESSAGE> _logMessageBuffer;
|
||||||
QVector<Logger::T_LOG_MESSAGE> _logMessageBuffer;
|
|
||||||
const int _loggerMaxMsgBufferSize;
|
const int _loggerMaxMsgBufferSize;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1548,7 +1548,7 @@ void JsonAPI::incommingLogMessage(const Logger::T_LOG_MESSAGE &msg)
|
|||||||
if (!_streaming_logging_activated)
|
if (!_streaming_logging_activated)
|
||||||
{
|
{
|
||||||
_streaming_logging_activated = true;
|
_streaming_logging_activated = true;
|
||||||
QVector<Logger::T_LOG_MESSAGE> *logBuffer = LoggerManager::getInstance()->getLogMessageBuffer();
|
const QList<Logger::T_LOG_MESSAGE> *logBuffer = LoggerManager::getInstance()->getLogMessageBuffer();
|
||||||
for (int i = 0; i < logBuffer->length(); i++)
|
for (int i = 0; i < logBuffer->length(); i++)
|
||||||
{
|
{
|
||||||
message["appName"] = logBuffer->at(i).appName;
|
message["appName"] = logBuffer->at(i).appName;
|
||||||
|
@ -12,7 +12,7 @@ FlatBufferConnection::FlatBufferConnection(const QString& origin, const QString
|
|||||||
, _origin(origin)
|
, _origin(origin)
|
||||||
, _priority(priority)
|
, _priority(priority)
|
||||||
, _prevSocketState(QAbstractSocket::UnconnectedState)
|
, _prevSocketState(QAbstractSocket::UnconnectedState)
|
||||||
, _log(Logger::getInstance("FLATBUFCONNECTION"))
|
, _log(Logger::getInstance("FLATBUFCONN"))
|
||||||
, _registered(false)
|
, _registered(false)
|
||||||
{
|
{
|
||||||
QStringList parts = address.split(":");
|
QStringList parts = address.split(":");
|
||||||
|
@ -7,7 +7,7 @@ using namespace hyperion;
|
|||||||
|
|
||||||
ComponentRegister::ComponentRegister(Hyperion* hyperion)
|
ComponentRegister::ComponentRegister(Hyperion* hyperion)
|
||||||
: _hyperion(hyperion)
|
: _hyperion(hyperion)
|
||||||
, _log(Logger::getInstance("ComponentRegister"))
|
, _log(Logger::getInstance("COMPONENTREG"))
|
||||||
{
|
{
|
||||||
// init all comps to false
|
// init all comps to false
|
||||||
QVector<hyperion::Components> vect;
|
QVector<hyperion::Components> vect;
|
||||||
|
@ -14,7 +14,7 @@ Grabber::Grabber(QString grabberName, int width, int height, int cropLeft, int c
|
|||||||
, _cropTop(0)
|
, _cropTop(0)
|
||||||
, _cropBottom(0)
|
, _cropBottom(0)
|
||||||
, _enabled(true)
|
, _enabled(true)
|
||||||
, _log(Logger::getInstance(grabberName))
|
, _log(Logger::getInstance(grabberName.toUpper()))
|
||||||
{
|
{
|
||||||
Grabber::setVideoMode(VideoMode::VIDEO_2D);
|
Grabber::setVideoMode(VideoMode::VIDEO_2D);
|
||||||
Grabber::setCropping(cropLeft, cropRight, cropTop, cropBottom);
|
Grabber::setCropping(cropLeft, cropRight, cropTop, cropBottom);
|
||||||
|
@ -16,7 +16,7 @@ QJsonObject SettingsManager::schemaJson;
|
|||||||
|
|
||||||
SettingsManager::SettingsManager(const quint8& instance, QObject* parent)
|
SettingsManager::SettingsManager(const quint8& instance, QObject* parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, _log(Logger::getInstance("SettingsManager"))
|
, _log(Logger::getInstance("SETTINGSMGR"))
|
||||||
, _sTable(new SettingsTable(instance, this))
|
, _sTable(new SettingsTable(instance, this))
|
||||||
{
|
{
|
||||||
// get schema
|
// get schema
|
||||||
|
@ -16,6 +16,35 @@
|
|||||||
|
|
||||||
namespace DefaultSignalHandler
|
namespace DefaultSignalHandler
|
||||||
{
|
{
|
||||||
|
struct Signal
|
||||||
|
{
|
||||||
|
int number;
|
||||||
|
const char * name;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Signal ALL_SIGNALS[] = {
|
||||||
|
{ SIGABRT, "SIGABRT" },
|
||||||
|
{ SIGBUS, "SIGBUS" },
|
||||||
|
{ SIGFPE, "SIGFPE" },
|
||||||
|
{ SIGILL, "SIGILL" },
|
||||||
|
{ SIGSEGV, "SIGSEGV" },
|
||||||
|
{ SIGTERM, "SIGTERM" },
|
||||||
|
{ SIGHUP, "SIGHUP" },
|
||||||
|
{ SIGINT, "SIGINT" },
|
||||||
|
{ SIGPIPE, "SIGPIPE" },
|
||||||
|
};
|
||||||
|
|
||||||
|
void write_to_stderr(const char* data, size_t size)
|
||||||
|
{
|
||||||
|
int res = write(STDERR_FILENO, data, size);
|
||||||
|
|
||||||
|
Q_UNUSED(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_to_stderr(const char* data)
|
||||||
|
{
|
||||||
|
write_to_stderr(data, strlen(data));
|
||||||
|
}
|
||||||
|
|
||||||
std::string decipher_trace(const std::string &trace)
|
std::string decipher_trace(const std::string &trace)
|
||||||
{
|
{
|
||||||
@ -70,26 +99,48 @@ void print_trace()
|
|||||||
free(symbols);
|
free(symbols);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void install_default_handler(int signum)
|
||||||
|
{
|
||||||
|
struct sigaction action{};
|
||||||
|
sigemptyset(&action.sa_mask);
|
||||||
|
action.sa_handler = SIG_DFL;
|
||||||
|
(void)sigaction(signum, &action, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
/* Note that this signal handler is not async signal safe !
|
/* Note that this signal handler is not async signal safe !
|
||||||
* Ideally a signal handler should only flip a bit and defer
|
* Ideally a signal handler should only flip a bit and defer
|
||||||
* heavy work to some kind of bottom-half processing. */
|
* heavy work to some kind of bottom-half processing. */
|
||||||
void signal_handler(int signum, siginfo_t * /*info*/, void * /*context*/)
|
void signal_handler(int signum, siginfo_t * /*info*/, void * /*context*/)
|
||||||
{
|
{
|
||||||
Logger* log = Logger::getInstance("SIGNAL");
|
const char * name = "UNKNOWN SIGNAL";
|
||||||
|
|
||||||
char *name = strsignal(signum);
|
for (const auto& s : ALL_SIGNALS) {
|
||||||
if (name)
|
if (s.number == signum) {
|
||||||
{
|
name = s.name;
|
||||||
Info(log, "Signal received : %s", name);
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write_to_stderr("\n");
|
||||||
|
write_to_stderr("Hyperion caught signal :");
|
||||||
|
write_to_stderr(name);
|
||||||
|
write_to_stderr("\n");
|
||||||
|
|
||||||
|
/* Anything below here is unsafe ! */
|
||||||
|
|
||||||
switch(signum)
|
switch(signum)
|
||||||
{
|
{
|
||||||
|
case SIGBUS:
|
||||||
case SIGSEGV:
|
case SIGSEGV:
|
||||||
case SIGABRT:
|
case SIGABRT:
|
||||||
case SIGFPE :
|
case SIGFPE :
|
||||||
print_trace();
|
print_trace();
|
||||||
exit(1);
|
|
||||||
|
/* Don't catch our own signal */
|
||||||
|
install_default_handler(signum);
|
||||||
|
|
||||||
|
kill(getpid(), signum);
|
||||||
|
return;
|
||||||
case SIGINT :
|
case SIGINT :
|
||||||
case SIGTERM:
|
case SIGTERM:
|
||||||
case SIGPIPE:
|
case SIGPIPE:
|
||||||
@ -101,10 +152,7 @@ void signal_handler(int signum, siginfo_t * /*info*/, void * /*context*/)
|
|||||||
QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection);
|
QMetaObject::invokeMethod(qApp, "quit", Qt::QueuedConnection);
|
||||||
|
|
||||||
// Reset signal handler to default (in case this handler is not capable of stopping)
|
// Reset signal handler to default (in case this handler is not capable of stopping)
|
||||||
struct sigaction action{};
|
install_default_handler(signum);
|
||||||
action.sa_handler = SIG_DFL;
|
|
||||||
sigaction(signum, &action, nullptr);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,17 +164,20 @@ namespace DefaultSignalHandler
|
|||||||
void install()
|
void install()
|
||||||
{
|
{
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
struct sigaction action{};
|
Logger* log = Logger::getInstance("CORE");
|
||||||
action.sa_sigaction = signal_handler;
|
|
||||||
action.sa_flags = SA_RESTART | SA_SIGINFO;
|
|
||||||
|
|
||||||
sigaction(SIGHUP , &action, nullptr);
|
struct sigaction action{};
|
||||||
sigaction(SIGFPE , &action, nullptr);
|
sigemptyset(&action.sa_mask);
|
||||||
sigaction(SIGINT , &action, nullptr);
|
action.sa_sigaction = signal_handler;
|
||||||
sigaction(SIGTERM, &action, nullptr);
|
action.sa_flags |= SA_SIGINFO;
|
||||||
sigaction(SIGABRT, &action, nullptr);
|
|
||||||
sigaction(SIGSEGV, &action, nullptr);
|
for (const auto& s : ALL_SIGNALS)
|
||||||
sigaction(SIGPIPE, &action, nullptr);
|
{
|
||||||
|
if (sigaction(s.number, &action, nullptr)!= 0)
|
||||||
|
{
|
||||||
|
Error(log, "Failed to install handler for %s]\n", s.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif // _WIN32
|
#endif // _WIN32
|
||||||
}
|
}
|
||||||
} // namespace DefaultSignalHandler
|
} // namespace DefaultSignalHandler
|
||||||
|
@ -11,99 +11,33 @@
|
|||||||
#include <Shlwapi.h>
|
#include <Shlwapi.h>
|
||||||
#pragma comment(lib, "Shlwapi.lib")
|
#pragma comment(lib, "Shlwapi.lib")
|
||||||
#endif
|
#endif
|
||||||
|
#include <QDateTime>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
|
#include <QMutexLocker>
|
||||||
|
#include <QThreadStorage>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
static const char * LogLevelStrings[] = { "", "DEBUG", "INFO", "WARNING", "ERROR" };
|
QMutex Logger::MapLock { QMutex::Recursive };
|
||||||
|
QMap<QString,Logger*> Logger::LoggerMap { };
|
||||||
|
QAtomicInteger<int> Logger::GLOBAL_MIN_LOG_LEVEL { static_cast<int>(Logger::UNSET)};
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const char * LogLevelStrings[] = { "", "DEBUG", "INFO", "WARNING", "ERROR" };
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
static const int LogLevelSysLog[] = { LOG_DEBUG, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR };
|
const int LogLevelSysLog[] = { LOG_DEBUG, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR };
|
||||||
#endif
|
#endif
|
||||||
static unsigned int loggerCount = 0;
|
|
||||||
static unsigned int loggerId = 0;
|
|
||||||
|
|
||||||
std::map<QString,Logger*> *Logger::LoggerMap = nullptr;
|
const size_t MAX_IDENTIFICATION_LENGTH = 22;
|
||||||
Logger::LogLevel Logger::GLOBAL_MIN_LOG_LEVEL = Logger::UNSET;
|
|
||||||
LoggerManager* LoggerManager::_instance = nullptr;
|
|
||||||
int _repeatCount = 0;
|
|
||||||
Logger::T_LOG_MESSAGE _repeatMessage;
|
|
||||||
const int _maxRepeatCountSize = 200;
|
|
||||||
|
|
||||||
Logger* Logger::getInstance(QString name, Logger::LogLevel minLevel)
|
QAtomicInteger<unsigned int> LoggerCount = 0;
|
||||||
{
|
QAtomicInteger<unsigned int> LoggerId = 0;
|
||||||
qRegisterMetaType<Logger::T_LOG_MESSAGE>();
|
|
||||||
Logger* log = nullptr;
|
|
||||||
if (LoggerMap == nullptr)
|
|
||||||
{
|
|
||||||
LoggerMap = new std::map<QString,Logger*>;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LoggerMap->find(name) == LoggerMap->end())
|
const int MaxRepeatCountSize = 200;
|
||||||
{
|
QThreadStorage<int> RepeatCount;
|
||||||
log = new Logger(name,minLevel);
|
QThreadStorage<Logger::T_LOG_MESSAGE> RepeatMessage;
|
||||||
LoggerMap->insert(std::pair<QString,Logger*>(name,log)); // compat version, replace it with following line if we have 100% c++11
|
|
||||||
//LoggerMap->emplace(name,log); // not compat with older linux distro's e.g. wheezy
|
|
||||||
connect(log, &Logger::newLogMessage, LoggerManager::getInstance(), &LoggerManager::handleNewLogMessage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
log = LoggerMap->at(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return log;
|
QString getApplicationName()
|
||||||
}
|
|
||||||
|
|
||||||
void Logger::deleteInstance(QString name)
|
|
||||||
{
|
|
||||||
if (LoggerMap == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (name.isEmpty())
|
|
||||||
{
|
|
||||||
std::map<QString,Logger*>::iterator it;
|
|
||||||
for (it = LoggerMap->begin(); it != LoggerMap->end(); it++)
|
|
||||||
{
|
|
||||||
delete it->second;
|
|
||||||
}
|
|
||||||
LoggerMap->clear();
|
|
||||||
}
|
|
||||||
else if (LoggerMap->find(name) != LoggerMap->end())
|
|
||||||
{
|
|
||||||
delete LoggerMap->at(name);
|
|
||||||
LoggerMap->erase(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void Logger::setLogLevel(LogLevel level,QString name)
|
|
||||||
{
|
|
||||||
if (name.isEmpty())
|
|
||||||
{
|
|
||||||
GLOBAL_MIN_LOG_LEVEL = level;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Logger* log = Logger::getInstance(name,level);
|
|
||||||
log->setMinLevel(level);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger::LogLevel Logger::getLogLevel(QString name)
|
|
||||||
{
|
|
||||||
if (name.isEmpty())
|
|
||||||
{
|
|
||||||
return GLOBAL_MIN_LOG_LEVEL;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger* log = Logger::getInstance(name);
|
|
||||||
return log->getMinLevel();
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger::Logger (QString name, LogLevel minLevel)
|
|
||||||
: QObject()
|
|
||||||
, _name(name)
|
|
||||||
, _minLevel(minLevel)
|
|
||||||
, _syslogEnabled(true)
|
|
||||||
, _loggerId(loggerId++)
|
|
||||||
{
|
{
|
||||||
#ifdef __GLIBC__
|
#ifdef __GLIBC__
|
||||||
const char* _appname_char = program_invocation_short_name;
|
const char* _appname_char = program_invocation_short_name;
|
||||||
@ -121,32 +55,132 @@ Logger::Logger (QString name, LogLevel minLevel)
|
|||||||
else
|
else
|
||||||
_appname_char = "unknown";
|
_appname_char = "unknown";
|
||||||
#endif
|
#endif
|
||||||
_appname = QString(_appname_char).toLower();
|
return QString(_appname_char).toLower();
|
||||||
|
|
||||||
loggerCount++;
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
if (_syslogEnabled && loggerCount == 1)
|
Logger* Logger::getInstance(const QString & name, Logger::LogLevel minLevel)
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&MapLock);
|
||||||
|
|
||||||
|
Logger* log = LoggerMap.value(name, nullptr);
|
||||||
|
if (log == nullptr)
|
||||||
{
|
{
|
||||||
#ifndef _WIN32
|
log = new Logger(name, minLevel);
|
||||||
openlog (_appname_char, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL0);
|
LoggerMap.insert(name, log); // compat version, replace it with following line if we have 100% c++11
|
||||||
#endif
|
//LoggerMap.emplace(name, log); // not compat with older linux distro's e.g. wheezy
|
||||||
|
connect(log, &Logger::newLogMessage, LoggerManager::getInstance(), &LoggerManager::handleNewLogMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return log;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logger::deleteInstance(const QString & name)
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&MapLock);
|
||||||
|
|
||||||
|
if (name.isEmpty())
|
||||||
|
{
|
||||||
|
for (auto logger : LoggerMap)
|
||||||
|
delete logger;
|
||||||
|
|
||||||
|
LoggerMap.clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete LoggerMap.value(name, nullptr);
|
||||||
|
LoggerMap.remove(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Logger::setLogLevel(LogLevel level, const QString & name)
|
||||||
|
{
|
||||||
|
if (name.isEmpty())
|
||||||
|
{
|
||||||
|
GLOBAL_MIN_LOG_LEVEL = static_cast<int>(level);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger* log = Logger::getInstance(name, level);
|
||||||
|
log->setMinLevel(level);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::LogLevel Logger::getLogLevel(const QString & name)
|
||||||
|
{
|
||||||
|
if (name.isEmpty())
|
||||||
|
{
|
||||||
|
return static_cast<Logger::LogLevel>(int(GLOBAL_MIN_LOG_LEVEL));
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger* log = Logger::getInstance(name);
|
||||||
|
return log->getMinLevel();
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::Logger (const QString & name, LogLevel minLevel)
|
||||||
|
: QObject()
|
||||||
|
, _name(name)
|
||||||
|
, _appname(getApplicationName())
|
||||||
|
, _syslogEnabled(true)
|
||||||
|
, _loggerId(LoggerId++)
|
||||||
|
, _minLevel(static_cast<int>(minLevel))
|
||||||
|
{
|
||||||
|
qRegisterMetaType<Logger::T_LOG_MESSAGE>();
|
||||||
|
|
||||||
|
int count = LoggerCount.fetchAndAddOrdered(1);
|
||||||
|
|
||||||
|
if (_syslogEnabled && count == 1)
|
||||||
|
{
|
||||||
|
#ifndef _WIN32
|
||||||
|
openlog (_appname.toLocal8Bit(), LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL0);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger::~Logger()
|
Logger::~Logger()
|
||||||
{
|
{
|
||||||
//Debug(this, "logger '%s' destroyed", QSTRING_CSTR(_name) );
|
//Debug(this, "logger '%s' destroyed", QSTRING_CSTR(_name) );
|
||||||
loggerCount--;
|
|
||||||
|
int count = LoggerCount.fetchAndSubOrdered(1);
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
if (loggerCount == 0)
|
if (_syslogEnabled && count == 0)
|
||||||
closelog();
|
closelog();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Logger::write(const Logger::T_LOG_MESSAGE & message) const
|
||||||
|
{
|
||||||
|
QString location;
|
||||||
|
if (message.level == Logger::DEBUG)
|
||||||
|
{
|
||||||
|
location = QString("%1:%2:%3() | ")
|
||||||
|
.arg(message.fileName)
|
||||||
|
.arg(message.line)
|
||||||
|
.arg(message.function);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString name = message.appName + " " + message.loggerName;
|
||||||
|
name.resize(MAX_IDENTIFICATION_LENGTH, ' ');
|
||||||
|
|
||||||
|
const QDateTime timestamp = QDateTime::fromMSecsSinceEpoch(message.utime);
|
||||||
|
|
||||||
|
std::cout << QString("%1 %2 : <%3> %4%5")
|
||||||
|
.arg(timestamp.toString("yyyy-MM-ddThh:mm:ss.zzz"))
|
||||||
|
.arg(name)
|
||||||
|
.arg(LogLevelStrings[message.level])
|
||||||
|
.arg(location)
|
||||||
|
.arg(message.message)
|
||||||
|
.toStdString()
|
||||||
|
<< std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
void Logger::Message(LogLevel level, const char* sourceFile, const char* func, unsigned int line, const char* fmt, ...)
|
void Logger::Message(LogLevel level, const char* sourceFile, const char* func, unsigned int line, const char* fmt, ...)
|
||||||
{
|
{
|
||||||
if ( (GLOBAL_MIN_LOG_LEVEL == Logger::UNSET && level < _minLevel) // no global level, use level from logger
|
Logger::LogLevel globalLevel = static_cast<Logger::LogLevel>(int(GLOBAL_MIN_LOG_LEVEL));
|
||||||
|| (GLOBAL_MIN_LOG_LEVEL > Logger::UNSET && level < GLOBAL_MIN_LOG_LEVEL) ) // global level set, use global level
|
|
||||||
|
if ( (globalLevel == Logger::UNSET && level < _minLevel) // no global level, use level from logger
|
||||||
|
|| (globalLevel > Logger::UNSET && level < globalLevel) ) // global level set, use global level
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const size_t max_msg_length = 1024;
|
const size_t max_msg_length = 1024;
|
||||||
@ -156,38 +190,35 @@ void Logger::Message(LogLevel level, const char* sourceFile, const char* func, u
|
|||||||
vsnprintf (msg, max_msg_length, fmt, args);
|
vsnprintf (msg, max_msg_length, fmt, args);
|
||||||
va_end (args);
|
va_end (args);
|
||||||
|
|
||||||
auto repeatedSummary = [=]
|
const auto repeatedSummary = [&]
|
||||||
{
|
{
|
||||||
Logger::T_LOG_MESSAGE repMsg = _repeatMessage;
|
Logger::T_LOG_MESSAGE repMsg = RepeatMessage.localData();
|
||||||
repMsg.message = "Previous line repeats " + QString::number(_repeatCount) + " times";
|
repMsg.message = "Previous line repeats " + QString::number(RepeatCount.localData()) + " times";
|
||||||
|
repMsg.utime = QDateTime::currentMSecsSinceEpoch();
|
||||||
emit newLogMessage(repMsg);
|
|
||||||
|
|
||||||
std::cout << QString("[" + repMsg.appName + " " + repMsg.loggerName + "] <" + LogLevelStrings[repMsg.level] + "> " + repMsg.message).toStdString() << std::endl;
|
|
||||||
|
|
||||||
|
write(repMsg);
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
if ( _syslogEnabled && repMsg.level >= Logger::WARNING )
|
if ( _syslogEnabled && repMsg.level >= Logger::WARNING )
|
||||||
syslog (LogLevelSysLog[repMsg.level], "Previous line repeats %d times", _repeatCount);
|
syslog (LogLevelSysLog[repMsg.level], "Previous line repeats %d times", RepeatCount.localData());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
_repeatCount = 0;
|
RepeatCount.setLocalData(0);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (_repeatMessage.loggerName == _name
|
if (RepeatMessage.localData().loggerName == _name &&
|
||||||
&& _repeatMessage.function == QString(func)
|
RepeatMessage.localData().function == func &&
|
||||||
&& _repeatMessage.line == line
|
RepeatMessage.localData().message == msg &&
|
||||||
&& _repeatMessage.message == QString(msg))
|
RepeatMessage.localData().line == line)
|
||||||
{
|
{
|
||||||
if (_repeatCount >= _maxRepeatCountSize)
|
if (RepeatCount.localData() >= MaxRepeatCountSize)
|
||||||
repeatedSummary();
|
repeatedSummary();
|
||||||
else
|
else
|
||||||
_repeatCount++;
|
RepeatCount.setLocalData(RepeatCount.localData() + 1);
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_repeatCount) repeatedSummary();
|
if (RepeatCount.localData())
|
||||||
|
repeatedSummary();
|
||||||
|
|
||||||
Logger::T_LOG_MESSAGE logMsg;
|
Logger::T_LOG_MESSAGE logMsg;
|
||||||
|
|
||||||
@ -196,25 +227,17 @@ void Logger::Message(LogLevel level, const char* sourceFile, const char* func, u
|
|||||||
logMsg.function = QString(func);
|
logMsg.function = QString(func);
|
||||||
logMsg.line = line;
|
logMsg.line = line;
|
||||||
logMsg.fileName = FileUtils::getBaseName(sourceFile);
|
logMsg.fileName = FileUtils::getBaseName(sourceFile);
|
||||||
time(&(logMsg.utime));
|
logMsg.utime = QDateTime::currentMSecsSinceEpoch();
|
||||||
logMsg.message = QString(msg);
|
logMsg.message = QString(msg);
|
||||||
logMsg.level = level;
|
logMsg.level = level;
|
||||||
logMsg.levelString = LogLevelStrings[level];
|
logMsg.levelString = LogLevelStrings[level];
|
||||||
|
|
||||||
emit newLogMessage(logMsg);
|
write(logMsg);
|
||||||
|
|
||||||
QString location;
|
|
||||||
if ( level == Logger::DEBUG )
|
|
||||||
{
|
|
||||||
location = "<" + logMsg.fileName + ":" + QString::number(line)+":"+ logMsg.function + "()> ";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << QString("[" + _appname + " " + _name + "] <" + LogLevelStrings[level] + "> " + location + msg).toStdString() << std::endl;
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
if ( _syslogEnabled && level >= Logger::WARNING )
|
if ( _syslogEnabled && level >= Logger::WARNING )
|
||||||
syslog (LogLevelSysLog[level], "%s", msg);
|
syslog (LogLevelSysLog[level], "%s", msg);
|
||||||
#endif
|
#endif
|
||||||
_repeatMessage = logMsg;
|
RepeatMessage.setLocalData(logMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,14 +245,15 @@ LoggerManager::LoggerManager()
|
|||||||
: QObject()
|
: QObject()
|
||||||
, _loggerMaxMsgBufferSize(200)
|
, _loggerMaxMsgBufferSize(200)
|
||||||
{
|
{
|
||||||
|
_logMessageBuffer.reserve(_loggerMaxMsgBufferSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoggerManager::handleNewLogMessage(const Logger::T_LOG_MESSAGE &msg)
|
void LoggerManager::handleNewLogMessage(const Logger::T_LOG_MESSAGE & msg)
|
||||||
{
|
{
|
||||||
_logMessageBuffer.append(msg);
|
_logMessageBuffer.push_back(msg);
|
||||||
if (_logMessageBuffer.length() > _loggerMaxMsgBufferSize)
|
if (_logMessageBuffer.length() > _loggerMaxMsgBufferSize)
|
||||||
{
|
{
|
||||||
_logMessageBuffer.erase(_logMessageBuffer.begin());
|
_logMessageBuffer.pop_front();
|
||||||
}
|
}
|
||||||
|
|
||||||
emit newLogMessage(msg);
|
emit newLogMessage(msg);
|
||||||
@ -237,7 +261,6 @@ void LoggerManager::handleNewLogMessage(const Logger::T_LOG_MESSAGE &msg)
|
|||||||
|
|
||||||
LoggerManager* LoggerManager::getInstance()
|
LoggerManager* LoggerManager::getInstance()
|
||||||
{
|
{
|
||||||
if (_instance == nullptr)
|
static LoggerManager instance;
|
||||||
_instance = new LoggerManager();
|
return &instance;
|
||||||
return _instance;
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user