#include "logger.h" #include #include 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 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>()); } else { // Process system log message (for any other message type, we will log it as a system message) processSystemMessage(message->toString()); } } //--------------------------------------------------------------