Files
kwa.fr/src/core/logger/logger.cpp
2026-03-12 16:32:03 +01:00

173 lines
5.5 KiB
C++

#include "logger.h"
#include <iostream>
#include <syncstream>
using namespace std;
using namespace core::logger;
//--------------------------------------------------------------
/* Constructor */
Logger::Logger(dBus::Bus &bus)
: Node(bus)
{
// Initialize the log printing loop
runPrintingLoop();
// Initialize the bus listener thread
runBusListener();
}
//--------------------------------------------------------------
/* Default destructor */
Logger::~Logger()
{
// Stop the bus listener thread if it's running
stopBusListener();
// Stop the log printing loop if it's running
stopPrintingLoop();
}
//--------------------------------------------------------------
/* Run the loop that periodically prints log entries to the console */
void Logger::runPrintingLoop()
{
/* Start the printing thread to periodically print log entries to the console.
* The thread will continuously wait for messages and process them until a stop is requested */
// clang-format off
m_printingThread = std::jthread([this](const std::stop_token &stopToken)
{
while (!stopToken.stop_requested())
{
// Sleep for a short duration to avoid busy waiting and reduce CPU usage
this_thread::sleep_for(100ms);
// Print the log entries after processing the message
if (m_logEntries.timer.isElapsed(LOG_PRINT_INTERVAL, true))
printLogEntries();
}
});
// clang-format on
}
//--------------------------------------------------------------
/* Stop the log printing loop */
void Logger::stopPrintingLoop()
{
// Stop printing thread loop
m_printingThread.request_stop();
// Despite a jthread will automatically join in its destructor, we
// can explicitly join here to ensure that the thread has finished
// executing before the Logger object is destroyed.
// This is especially important if the thread is still running and
// may access resources that are being cleaned up in the destructor.
// By joining the thread, we can ensure a clean shutdown of the Logger
// and avoid potential issues with dangling threads or accessing invalid resources.
if (m_printingThread.joinable())
m_printingThread.join();
}
//--------------------------------------------------------------
/* Print the log entries to the console */
void Logger::printLogEntries()
{
// Move the log entries to a local variable and clear the
// original vector to minimize the time the mutex is locked
std::vector<std::string> logEntries;
{
scoped_lock lock(m_logEntries.mtx);
logEntries = std::move(m_logEntries.entries);
m_logEntries.entries.clear();
}
for (const auto &entry : logEntries)
{
// Use osyncstream to ensure thread-safe output to the console
std::osyncstream syncOut(std::cout);
syncOut << entry << std::endl;
}
}
//--------------------------------------------------------------
/* Process a log message received from the bus */
void Logger::processUserMessage(const LogMessage_ptr &logMessage)
{
// Sanity check
if (!logMessage)
return;
// Add the log entry to the vector of log entries
std::scoped_lock lock(m_logEntries.mtx);
m_logEntries.entries.push_back(logMessage->toString());
}
//--------------------------------------------------------------
/* Process a system log message received from the bus */
void Logger::processSystemMessage(const std::string &logMessage)
{
// Sanity check
if (logMessage.empty())
return;
// Add the log entry to the vector of log entries
std::scoped_lock lock(m_logEntries.mtx);
m_logEntries.entries.push_back(logMessage);
}
//--------------------------------------------------------------
/* Run the bus listener thread */
void Logger::runBusListener()
{
// Subscribe to the bus in broadcast mode to receive all messages posted to the bus
m_bus.subscribeToBroadcast(this);
/* Start the bus listener thread to process incoming messages related to requirements.
* The thread will continuously wait for messages and process them until a stop is requested */
// clang-format off
m_busListenerThread = std::jthread([this](const std::stop_token &stopToken)
{
while (!stopToken.stop_requested())
{
// Wait for a message to be received
syncWaitForMessage();
// Process all received messages
while (getMessageCount() > 0)
{
auto message = popNextMessage();
if (message)
processMessageBus(message);
}
}
});
// clang-format on
}
//--------------------------------------------------------------
/* Stop the bus listener thread */
void Logger::stopBusListener()
{
// Stop event thread loop
m_busListenerThread.request_stop();
// Wake up event thread if waiting
notifyMessageQueue();
// Join the bus listener thread to ensure it has finished
// executing before the Logger object is destroyed
if (m_busListenerThread.joinable())
m_busListenerThread.join();
}
//--------------------------------------------------------------
/* Process a message received from the bus */
void Logger::processMessageBus(const Message_ptr &message)
{
// Check the message type and process accordingly
const auto messageType = message->getMessageTypeID();
if (messageType == dBus::makeID("log.message"))
{
// Process log message
processUserMessage(message->as<dBus::EventMessage<api::log::LogMessage>>());
}
else
{
// Process system log message (for any other message type, we will log it as a system message)
processSystemMessage(message->toString());
}
}
//--------------------------------------------------------------