#pragma once #include "additional_data.hpp" #include "elog_entry.hpp" #include #include #include #include #include namespace openpower::pels { /** * @class EventLogger * * This class handles creating OpenBMC event logs (and thus PELs) from * within the PEL extension code. * * The function to actually create the event log is passed in via the * constructor so that different functions can be used when testing. * * To create the event log, call log() with the appropriate arguments * and the log will be created as soon as the flow gets back to the event * loop. If the queue isn't empty after a log is created, the next * one will be scheduled to be created from the event loop again. * * This class does not allow new events to be added while inside the * creation function, because if the code added an event log every time * it tried to create one, it would do so infinitely. */ class EventLogger { public: using ADMap = std::map; using LogFunction = std::function; static constexpr size_t msgPos = 0; static constexpr size_t levelPos = 1; static constexpr size_t adPos = 2; using EventEntry = std::tuple; EventLogger() = delete; ~EventLogger() = default; EventLogger(const EventLogger&) = delete; EventLogger& operator=(const EventLogger&) = delete; EventLogger(EventLogger&&) = delete; EventLogger& operator=(EventLogger&&) = delete; /** * @brief Constructor * * @param[in] creator - The function to use to create the event log */ explicit EventLogger(LogFunction creator) : _event(sdeventplus::Event::get_default()), _creator(creator) { } /** * @brief Adds an event to the queue so that it will be created * as soon as the code makes it back to the event loop. * * Won't add it to the queue if already inside the create() * callback. * * @param[in] message - The message property of the event log * @param[in] severity - The severity level of the event log * @param[in] ad - The additional data property of the event log */ void log(const std::string& message, phosphor::logging::Entry::Level severity, const AdditionalData& ad) { if (!_inEventCreation) { _eventsToCreate.emplace(message, severity, ad); if (!_eventSource) { scheduleCreate(); } } else { phosphor::logging::log( "Already in event create callback, skipping new create", phosphor::logging::entry("ERROR_NAME=%s", message.c_str())); } } /** * @brief Returns the event log queue size. * * @return size_t - The queue size */ size_t queueSize() const { return _eventsToCreate.size(); } /** * @brief Schedules the create() function to run using the * 'defer' sd_event source. */ void scheduleCreate() { _eventSource = std::make_unique( _event, std::bind(std::mem_fn(&EventLogger::create), this, std::placeholders::_1)); } private: /** * @brief Creates an event log and schedules the next one if * there is one. * * This gets called from the event loop by the sd_event code. * * @param[in] source - The event source object used */ void create(sdeventplus::source::EventBase& /*source*/) { _eventSource.reset(); if (_eventsToCreate.empty()) { return; } auto event = _eventsToCreate.front(); _eventsToCreate.pop(); _inEventCreation = true; try { _creator(std::get(event), std::get(event), std::get(event).getData()); } catch (const std::exception& e) { phosphor::logging::log( "EventLogger's create function threw an exception", phosphor::logging::entry("ERROR=%s", e.what())); } _inEventCreation = false; if (!_eventsToCreate.empty()) { scheduleCreate(); } } /** * @brief The sd_event object. */ sdeventplus::Event _event; /** * @brief The user supplied function to create the event log. */ LogFunction _creator; /** * @brief Keeps track of if an event is currently being created. * * Guards against creating new events while creating events. */ bool _inEventCreation = false; /** * @brief The event source object used for scheduling. */ std::unique_ptr _eventSource; /** * @brief The queue of event logs to create. */ std::queue _eventsToCreate; }; } // namespace openpower::pels