173 lines
5.5 KiB
C++
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());
|
|
}
|
|
}
|
|
//--------------------------------------------------------------
|