hyperion.ng/libsrc/utils/Logger.cpp
LordGrey 05d24b99c4
Fix Memory leaks (#1678)
* Refactor to fix #1671

* Add GUI/NonGUI mode to info page

* Do not show lock config, if in non-UI mode

* Updae Changelog

* Correct includes

* Ensure key member initialization - RGB Channels

* Ensure key member initialization - WebServer

* Update RGBChannels

* Fix initialization order

* Fix key when inserting new logger in LoggerMap,
Prepare logBuffer-JSON snapshot view in LoggerManager,
Increase buffered loglines to 500

* Fix Memory leak in GrabberWrapper

* Fix Memory leak in BlackBorderProcessor

* Fix Memory leak in BlackBorderProcessor

* use ninja generator under macos

* Fix BGEffectHandler destruction

* Fix Mdns code

* Clear list after applying qDeleteAll

* Fix deletion of CecHandler

* Fix memory leak caused by wrong buffer allocation

* Remove extra pixel consistently

* Change mDNS to Qt SmartPointers

* Correct removal

* Fix usage of _width/_height (they are the output resolution, not the screen resolution)
That avoids unnecessary resizing of the output image with every transferFrame call

* Move main non Thread Objects to Smart Pointers

* Revert "Move main non Thread Objects to Smart Pointers"

This reverts commit 26102ca963982e2fbc4ffb8d4db6139f0128a3cc.

* Add missing deletes

* Revert MdnsBrowser chnage

* Revert MdnsBrowser change

* Fix memory leaks related standalone grabber

* Address CodeQL finding

* delete pointer OsxFrameGrabber

---------

Co-authored-by: Paulchen-Panther <16664240+Paulchen-Panther@users.noreply.github.com>
2024-01-13 17:04:45 +01:00

282 lines
6.9 KiB
C++

#include <utils/Logger.h>
#include <utils/FileUtils.h>
#include <iostream>
#ifndef _WIN32
#include <syslog.h>
#elif _WIN32
#include <windows.h>
#include <Shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")
#endif
#include <QDateTime>
#include <QFileInfo>
#include <QMutexLocker>
#include <QThreadStorage>
#include <QJsonObject>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
QRecursiveMutex Logger::MapLock;
#else
QMutex Logger::MapLock{ QMutex::Recursive };
#endif
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", "OFF" };
#ifndef _WIN32
const int LogLevelSysLog[] = { LOG_DEBUG, LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_ERR };
#endif
const size_t MAX_IDENTIFICATION_LENGTH = 22;
QAtomicInteger<unsigned int> LoggerCount = 0;
QAtomicInteger<unsigned int> LoggerId = 0;
const int MAX_LOG_MSG_BUFFERED = 500;
const int MaxRepeatCountSize = 200;
QThreadStorage<int> RepeatCount;
QThreadStorage<Logger::T_LOG_MESSAGE> RepeatMessage;
} // namespace
Logger* Logger::getInstance(const QString & name, const QString & subName, Logger::LogLevel minLevel)
{
QMutexLocker lock(&MapLock);
Logger* log = LoggerMap.value(name + subName, nullptr);
if (log == nullptr)
{
log = new Logger(name, subName, minLevel);
LoggerMap.insert(name + subName, log);
connect(log, &Logger::newLogMessage, LoggerManager::getInstance(), &LoggerManager::handleNewLogMessage);
}
return log;
}
void Logger::deleteInstance(const QString & name, const QString & subName)
{
QMutexLocker lock(&MapLock);
if (name.isEmpty())
{
for (auto *logger : std::as_const(LoggerMap)) {
delete logger;
}
LoggerMap.clear();
}
else
{
delete LoggerMap.value(name + subName, nullptr);
LoggerMap.remove(name + subName);
}
}
void Logger::setLogLevel(LogLevel level, const QString & name, const QString & subName)
{
if (name.isEmpty())
{
GLOBAL_MIN_LOG_LEVEL = static_cast<int>(level);
}
else
{
Logger* log = Logger::getInstance(name, subName, level);
log->setMinLevel(level);
}
}
Logger::LogLevel Logger::getLogLevel(const QString & name, const QString & subName)
{
if (name.isEmpty())
{
return static_cast<Logger::LogLevel>(int(GLOBAL_MIN_LOG_LEVEL));
}
const Logger* log = Logger::getInstance(name + subName);
return log->getMinLevel();
}
Logger::Logger (const QString & name, const QString & subName, LogLevel minLevel)
: QObject()
, _name(name)
, _subName(subName)
, _syslogEnabled(true)
, _loggerId(LoggerId++)
, _minLevel(static_cast<int>(minLevel))
{
qRegisterMetaType<Logger::T_LOG_MESSAGE>();
if (LoggerCount.fetchAndAddOrdered(1) == 1)
{
#ifndef _WIN32
if (_syslogEnabled)
{
openlog (nullptr, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL0);
}
#endif
}
}
Logger::~Logger()
{
if (LoggerCount.fetchAndSubOrdered(1) == 0)
{
#ifndef _WIN32
if (_syslogEnabled)
{
closelog();
}
#endif
}
}
void Logger::write(const Logger::T_LOG_MESSAGE & message)
{
QString location;
if (message.level == Logger::DEBUG)
{
location = QString("%1:%2:%3() | ")
.arg(message.fileName)
.arg(message.line)
.arg(message.function);
}
QString name = "|" + message.loggerSubName + "| " + 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;
emit newLogMessage(message);
}
void Logger::Message(LogLevel level, const char* sourceFile, const char* func, unsigned int line, const char* fmt, ...)
{
Logger::LogLevel globalLevel = static_cast<Logger::LogLevel>(int(GLOBAL_MIN_LOG_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;
const size_t max_msg_length = 1024;
char msg[max_msg_length];
va_list args;
va_start (args, fmt);
vsnprintf (msg, max_msg_length, fmt, args);
va_end (args);
const auto repeatedSummary = [&]
{
Logger::T_LOG_MESSAGE repMsg = RepeatMessage.localData();
repMsg.message = "Previous line repeats " + QString::number(RepeatCount.localData()) + " times";
repMsg.utime = QDateTime::currentMSecsSinceEpoch();
write(repMsg);
#ifndef _WIN32
if ( _syslogEnabled && repMsg.level >= Logger::WARNING )
syslog (LogLevelSysLog[repMsg.level], "Previous line repeats %d times", RepeatCount.localData());
#endif
RepeatCount.setLocalData(0);
};
if (RepeatMessage.localData().loggerName == _name &&
RepeatMessage.localData().loggerSubName == _subName &&
RepeatMessage.localData().function == func &&
RepeatMessage.localData().message == msg &&
RepeatMessage.localData().line == line)
{
if (RepeatCount.localData() >= MaxRepeatCountSize)
repeatedSummary();
else
RepeatCount.setLocalData(RepeatCount.localData() + 1);
}
else
{
if (RepeatCount.localData())
repeatedSummary();
Logger::T_LOG_MESSAGE logMsg;
logMsg.loggerName = _name;
logMsg.loggerSubName = _subName;
logMsg.function = QString(func);
logMsg.line = line;
logMsg.fileName = FileUtils::getBaseName(sourceFile);
logMsg.utime = QDateTime::currentMSecsSinceEpoch();
logMsg.message = QString(msg);
logMsg.level = level;
logMsg.levelString = LogLevelStrings[level];
write(logMsg);
#ifndef _WIN32
if ( _syslogEnabled && level >= Logger::WARNING )
syslog (LogLevelSysLog[level], "%s", msg);
#endif
RepeatMessage.setLocalData(logMsg);
}
}
LoggerManager::LoggerManager()
: QObject()
, _loggerMaxMsgBufferSize(MAX_LOG_MSG_BUFFERED)
{
_logMessageBuffer.reserve(_loggerMaxMsgBufferSize);
}
QJsonArray LoggerManager::getLogMessageBuffer(Logger::LogLevel filter) const
{
QJsonArray messageArray;
{
for (const auto &logLine : std::as_const(_logMessageBuffer))
{
if (logLine.level >= filter)
{
QJsonObject message;
message["loggerName"] = logLine.loggerName;
message["loggerSubName"] = logLine.loggerSubName;
message["function"] = logLine.function;
message["line"] = QString::number(logLine.line);
message["fileName"] = logLine.fileName;
message["message"] = logLine.message;
message["levelString"] = logLine.levelString;
message["utime"] = QString::number(logLine.utime);
messageArray.append(message);
}
}
}
return messageArray;
}
void LoggerManager::handleNewLogMessage(const Logger::T_LOG_MESSAGE & msg)
{
_logMessageBuffer.push_back(msg);
if (_logMessageBuffer.length() > _loggerMaxMsgBufferSize)
{
_logMessageBuffer.pop_front();
}
emit newLogMessage(msg);
}
LoggerManager* LoggerManager::getInstance()
{
static LoggerManager instance;
return &instance;
}