487 lines
11 KiB
C++
Executable File
487 lines
11 KiB
C++
Executable File
|
|
|
|
|
|
#include "NGLLogger.h"
|
|
#include "MurmurHash2.h"
|
|
|
|
#include <cstdlib>
|
|
#include <iostream>
|
|
|
|
#ifdef HAVE_LOG4QT
|
|
#include <log4qt/logger.h>
|
|
#include <log4qt/logmanager.h>
|
|
#include <log4qt/loggerrepository.h>
|
|
#include <log4qt/propertyconfigurator.h>
|
|
#include <log4qt/rollingfileappender.h>
|
|
#include <log4qt/helpers/configuratorhelper.h>
|
|
#include <log4qt/helpers/properties.h>
|
|
#include <log4qt/helpers/factory.h>
|
|
#endif
|
|
|
|
#include <mutex>
|
|
#include <unordered_map>
|
|
|
|
#include <QtCore/QCoreApplication>
|
|
#include <QtCore/QFileInfo>
|
|
|
|
namespace NGL
|
|
{
|
|
|
|
void LtsRollingFileAppender::setLtsLogfileName(const QString <sLogfileName)
|
|
{
|
|
m_ltsLogfileName = ltsLogfileName;
|
|
}
|
|
|
|
void LtsRollingFileAppender::activateOptions()
|
|
{
|
|
if (!m_ltsLogfileName.isEmpty())
|
|
setFile(m_ltsLogfileName);
|
|
#ifdef HAVE_LOG4QT
|
|
Log4Qt::RollingFileAppender::activateOptions();
|
|
#endif
|
|
}
|
|
|
|
QString LtsRollingFileAppender::m_ltsLogfileName;
|
|
#ifdef HAVE_LOG4QT
|
|
static Log4Qt::Appender *createLtsRollingFileAppender()
|
|
{
|
|
auto ret = new LtsRollingFileAppender;
|
|
ret->setAppendFile(true);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
class CleanupHelper final
|
|
{
|
|
public:
|
|
CleanupHelper() = default;
|
|
~CleanupHelper();
|
|
std::mutex &getLoggerMapMutex();
|
|
|
|
std::unordered_map<unsigned int, NGL::NGLLogger *> m_loggermap;
|
|
protected:
|
|
std::mutex m_loggermapmutex;
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
NGL::NGLLogger *NGL::NGLLogger::rootlogger_ptr = nullptr;
|
|
|
|
NGL::CleanupHelper NGL::NGLLogger::s_cleanup;
|
|
|
|
|
|
|
|
|
|
CleanupHelper::~CleanupHelper()
|
|
{
|
|
std::lock_guard<std::mutex> lock(m_loggermapmutex);
|
|
|
|
for (auto it : m_loggermap)
|
|
delete it.second;
|
|
m_loggermap.clear();
|
|
|
|
delete NGL::NGLLogger::rootlogger_ptr;
|
|
NGL::NGLLogger::rootlogger_ptr = nullptr;
|
|
}
|
|
|
|
std::mutex &CleanupHelper::getLoggerMapMutex()
|
|
{
|
|
return m_loggermapmutex;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
NGL::NGLLogger::NGLLogger(const std::string &fullqualifiedname)
|
|
: name(fullqualifiedname)
|
|
, currentLevel(LogLevel::Undefined)
|
|
, m_bNoSpace(true)
|
|
{
|
|
#ifdef HAVE_LOG4QT
|
|
if (!fullqualifiedname.empty())
|
|
{
|
|
logger_ptr = Log4Qt::LogManager::loggerRepository()->logger(QString::fromStdString(name));
|
|
s_cleanup.m_loggermap[MurmurHash2(fullqualifiedname.c_str(), fullqualifiedname.size())] = this;
|
|
}
|
|
else
|
|
{
|
|
logger_ptr = Log4Qt::LogManager::loggerRepository()->rootLogger();
|
|
|
|
}
|
|
#endif
|
|
|
|
configChanged();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NGL::INGLLogger &NGL::NGLLogger::getNGLLogger(const char *fullqualifiedname)
|
|
{
|
|
std::lock_guard<std::mutex> lock(s_cleanup.getLoggerMapMutex());
|
|
auto it = s_cleanup.m_loggermap.find(MurmurHash2(fullqualifiedname, strlen(fullqualifiedname)));
|
|
if (it != s_cleanup.m_loggermap.end())
|
|
return *(*it).second;
|
|
|
|
return *(new NGLLogger(fullqualifiedname));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NGL::INGLLogger &NGL::NGLLogger::getNGLLogger(const std::string &fullqualifiedname)
|
|
{
|
|
std::lock_guard<std::mutex> lock(s_cleanup.getLoggerMapMutex());
|
|
auto it = s_cleanup.m_loggermap.find(MurmurHash2(fullqualifiedname.c_str(), fullqualifiedname.size()));
|
|
if (it != s_cleanup.m_loggermap.end())
|
|
return *(*it).second;
|
|
|
|
return *(new NGLLogger(fullqualifiedname));
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NGL::NGLLogger::log(LogLevel level, const INGLLogger::String &message, const char *file, int line, const char *function)
|
|
{
|
|
#ifdef HAVE_LOG4QT
|
|
logger_ptr->logWithLocation(static_cast<Log4Qt::Level::Value>(level), file, line, function, QString::fromStdString(message));
|
|
#else
|
|
// Stub implementation - use qDebug/qWarning/qCritical based on level
|
|
Q_UNUSED(file);
|
|
Q_UNUSED(line);
|
|
Q_UNUSED(function);
|
|
const QString msg = QString::fromStdString(message);
|
|
switch (level)
|
|
{
|
|
case LogLevel::Fatal:
|
|
case LogLevel::Error:
|
|
qCritical() << msg;
|
|
break;
|
|
case LogLevel::Warning:
|
|
qWarning() << msg;
|
|
break;
|
|
case LogLevel::Info:
|
|
case LogLevel::Debug:
|
|
case LogLevel::Trace:
|
|
default:
|
|
qDebug() << msg;
|
|
break;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NGL::NGLLogger::logTrace(const INGLLogger::String &message, const char *file, int line, const char *function)
|
|
{
|
|
if (currentLevel >= LogLevel::Trace)
|
|
log(LogLevel::Trace, message, file, line, function);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NGL::NGLLogger::logDebug(const INGLLogger::String &message, const char *file, int line, const char *function)
|
|
{
|
|
if (currentLevel >= LogLevel::Debug)
|
|
log(LogLevel::Debug, message, file, line, function);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NGL::NGLLogger::logInfo(const INGLLogger::String &message, const char *file, int line, const char *function)
|
|
{
|
|
if (currentLevel >= LogLevel::Info)
|
|
log(LogLevel::Info, message, file, line, function);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NGL::NGLLogger::logWarning(const INGLLogger::String &message, const char *file, int line, const char *function)
|
|
{
|
|
if (currentLevel >= LogLevel::Warning)
|
|
log(LogLevel::Warning, message, file, line, function);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NGL::NGLLogger::logError(const INGLLogger::String &message, const char *file, int line, const char *function)
|
|
{
|
|
if (currentLevel >= LogLevel::Error)
|
|
log(LogLevel::Error, message, file, line, function);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NGL::NGLLogger::logFatal(const INGLLogger::String &message, const char *file, int line, const char *function)
|
|
{
|
|
log(LogLevel::Fatal, message, file, line, function);
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifndef LTS_VERSION_MAJOR
|
|
#define LTS_VERSION_MAJOR "undefined"
|
|
#endif
|
|
#ifndef LTS_VERSION_MINOR
|
|
#define LTS_VERSION_MINOR "undefined"
|
|
#endif
|
|
#ifndef LTS_VERSION_PATCH
|
|
#define LTS_VERSION_PATCH "undefined"
|
|
#endif
|
|
#ifndef LTS_VERSION_DATE
|
|
#define LTS_VERSION_DATE "undefined"
|
|
#endif
|
|
#ifndef JENKINS_BUILD_NUMBER
|
|
#define JENKINS_BUILD_NUMBER "unknown"
|
|
#endif
|
|
static const char ver_major[] = LTS_VERSION_MAJOR;
|
|
static const char ver_minor[] = LTS_VERSION_MINOR;
|
|
static const char ver_patch[] = LTS_VERSION_PATCH;
|
|
static const char ver_date[] = LTS_VERSION_DATE;
|
|
static const char ver_jenkins[] = JENKINS_BUILD_NUMBER;
|
|
|
|
void NGL::NGLLogger::logStartup(const char *file, int line, const char *function)
|
|
{
|
|
log(LogLevel::Info, QStringLiteral("Application is started (%1.%2-%3 rev%4 from %5)")
|
|
.arg(QString::fromLatin1(ver_major),
|
|
QString::fromLatin1(ver_minor),
|
|
QString::fromLatin1(ver_jenkins),
|
|
QString::fromLatin1(ver_patch),
|
|
QString::fromLatin1(ver_date)).toUtf8().constData(), file, line, function);
|
|
}
|
|
|
|
|
|
|
|
|
|
void NGL::NGLLogger::logShutdown(const char *file, int line, const char *function)
|
|
{
|
|
log(LogLevel::Info, QStringLiteral("Application is shut down (%1.%2-%3 rev%4 from %5)")
|
|
.arg(QString::fromLatin1(ver_major),
|
|
QString::fromLatin1(ver_minor),
|
|
QString::fromLatin1(ver_jenkins),
|
|
QString::fromLatin1(ver_patch),
|
|
QString::fromLatin1(ver_date)).toUtf8().constData(), file, line, function);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
NGL::NGLLogger &NGL::NGLLogger::getNGLRootLogger()
|
|
{
|
|
if (!rootlogger_ptr) throw NotCreatedError();
|
|
return *rootlogger_ptr;
|
|
}
|
|
|
|
|
|
|
|
|
|
static QString findConfigFile(const QString &configfilename)
|
|
{
|
|
if (configfilename.isEmpty())
|
|
return QString();
|
|
|
|
|
|
QFileInfo fi(configfilename);
|
|
if (fi.isFile() && fi.isReadable())
|
|
return fi.absoluteFilePath();
|
|
|
|
|
|
QString p = QCoreApplication::applicationDirPath() + QLatin1Char('/') + configfilename;
|
|
fi.setFile(p);
|
|
if (fi.isFile() && fi.isReadable())
|
|
return fi.absoluteFilePath();
|
|
|
|
return QString();
|
|
}
|
|
|
|
|
|
|
|
|
|
NGL::NGLLogger &NGL::NGLLogger::createNGLRootLogger(const QString &configfilename,
|
|
bool ignorealreadycreated,
|
|
const QString &logOutputName,
|
|
bool quiet)
|
|
{
|
|
if (rootlogger_ptr)
|
|
{
|
|
if (!ignorealreadycreated) throw AlreadyCreatedError();
|
|
}
|
|
else
|
|
{
|
|
#ifdef HAVE_LOG4QT
|
|
Log4Qt::Factory::registerAppender("Lts::RollingFileAppender", createLtsRollingFileAppender);
|
|
LtsRollingFileAppender::setLtsLogfileName(logOutputName);
|
|
|
|
rootlogger_ptr = new NGLLogger(std::string());
|
|
const QString filename = findConfigFile(configfilename);
|
|
if (!filename.isEmpty())
|
|
{
|
|
Log4Qt::PropertyConfigurator::configureAndWatch(filename);
|
|
QObject::connect(Log4Qt::ConfiguratorHelper::instance(), &Log4Qt::ConfiguratorHelper::configurationFileChanged,
|
|
[]() { configurationFileChanged(); });
|
|
const auto l = Log4Qt::ConfiguratorHelper::instance()->configureError();
|
|
for (const auto &t : l)
|
|
std::cerr << t.message().toUtf8().data() << std::endl;
|
|
if (!l.isEmpty())
|
|
exit(-1);
|
|
}
|
|
else
|
|
{
|
|
if (!quiet)
|
|
std::cerr << "Could not find log4cxx configuration file " << configfilename << std::endl
|
|
<< "Using default logging to stdout" << std::endl;
|
|
|
|
|
|
Log4Qt::Properties props;
|
|
props.setProperty(QLatin1String("log4j.rootLogger"),
|
|
QLatin1String("WARN, stdout"));
|
|
props.setProperty(QLatin1String("log4j.appender.stdout"),
|
|
QLatin1String("org.apache.log4j.ConsoleAppender"));
|
|
props.setProperty(QLatin1String("log4j.appender.stdout.layout"),
|
|
QLatin1String("org.apache.log4j.PatternLayout"));
|
|
props.setProperty(QLatin1String("log4j.appender.stdout.layout.ConversionPattern"),
|
|
QLatin1String("%d %-5p %c |#| %m (%.50F:%L) %n"));
|
|
Log4Qt::PropertyConfigurator::configure(props);
|
|
}
|
|
rootlogger_ptr->configChanged();
|
|
#else
|
|
// Stub implementation - no logging initialization needed
|
|
Q_UNUSED(configfilename);
|
|
Q_UNUSED(ignorealreadycreated);
|
|
Q_UNUSED(logOutputName);
|
|
Q_UNUSED(quiet);
|
|
rootlogger_ptr = new NGLLogger(std::string());
|
|
#endif
|
|
}
|
|
return *rootlogger_ptr;
|
|
}
|
|
|
|
void NGL::NGLLogger::configurationFileChanged()
|
|
{
|
|
for (auto ptr : s_cleanup.m_loggermap)
|
|
ptr.second->configChanged();
|
|
}
|
|
|
|
void NGL::NGLLogger::configChanged()
|
|
{
|
|
if (logger_ptr->isTraceEnabled())
|
|
currentLevel = LogLevel::Trace;
|
|
else if (logger_ptr->isDebugEnabled())
|
|
currentLevel = LogLevel::Debug;
|
|
else if (logger_ptr->isInfoEnabled())
|
|
currentLevel = LogLevel::Info;
|
|
else if (logger_ptr->isWarnEnabled())
|
|
currentLevel = LogLevel::Warning;
|
|
else if (logger_ptr->isErrorEnabled())
|
|
currentLevel = LogLevel::Error;
|
|
else
|
|
currentLevel = LogLevel::Fatal;
|
|
}
|
|
|
|
namespace NGL
|
|
{
|
|
namespace NGLLoggerHelper
|
|
{
|
|
|
|
void hexDumpHelper(NGL::INGLLogger &logger, NGL::LogLevel level,
|
|
const void *a_data, const unsigned int a_size)
|
|
{
|
|
if (!logger.isLevelEnabled(level) || !a_data)
|
|
return;
|
|
|
|
unsigned char *data = (unsigned char *)a_data;
|
|
QByteArray baBuf;
|
|
for (unsigned int i = 0; i < a_size; i++)
|
|
{
|
|
if (0 == (i % 8))
|
|
baBuf = QByteArray::number(i, 16).rightJustified(8, '0').toUpper();
|
|
baBuf += " " + QByteArray::number(data[i], 16).rightJustified(2, '0').toUpper();
|
|
if (7 == (i % 8))
|
|
logger.log(level, baBuf.constData(), __FILE__, __LINE__);
|
|
}
|
|
if (a_size % 8)
|
|
logger.log(level, baBuf.constData(), __FILE__, __LINE__);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
NGL::INGLLogger& getMemoryLogger()
|
|
{
|
|
try
|
|
{
|
|
return NGLLogger::getNGLRootLogger().getNGLLogger("memorystate");
|
|
|
|
}
|
|
catch (const std::exception &ex)
|
|
{
|
|
fprintf(stderr, "Error retrieving logger: %s", ex.what());
|
|
abort();
|
|
}
|
|
}
|
|
|
|
int parseLine(char* line){
|
|
|
|
int i = strlen(line);
|
|
const char* p = line;
|
|
while (*p <'0' || *p > '9') p++;
|
|
line[i-3] = '\0';
|
|
i = atoi(p);
|
|
return i;
|
|
}
|
|
|
|
int getMemoryValue()
|
|
{
|
|
|
|
FILE* file = fopen("/proc/self/status", "r");
|
|
int result = -1;
|
|
char line[128];
|
|
|
|
while (fgets(line, 128, file) != NULL){
|
|
|
|
if (strncmp(line, "VmRSS:", 6) == 0){
|
|
result = parseLine(line);
|
|
break;
|
|
}
|
|
}
|
|
fclose(file);
|
|
return result;
|
|
}
|
|
|
|
std::string getMemoryData()
|
|
{
|
|
return QStringLiteral("VmRSS: %1 kB #").arg(QString::number(getMemoryValue())).toStdString();
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|