|  | #pragma once | 
|  |  | 
|  | #include "additional_data.hpp" | 
|  | #include "elog_entry.hpp" | 
|  |  | 
|  | #include <phosphor-logging/log.hpp> | 
|  | #include <sdeventplus/event.hpp> | 
|  | #include <sdeventplus/source/event.hpp> | 
|  |  | 
|  | #include <queue> | 
|  | #include <tuple> | 
|  |  | 
|  | 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<std::string, std::string>; | 
|  | using LogFunction = std::function<void( | 
|  | const std::string&, phosphor::logging::Entry::Level, const ADMap&)>; | 
|  |  | 
|  | static constexpr size_t msgPos = 0; | 
|  | static constexpr size_t levelPos = 1; | 
|  | static constexpr size_t adPos = 2; | 
|  | using EventEntry = std::tuple<std::string, phosphor::logging::Entry::Level, | 
|  | AdditionalData>; | 
|  |  | 
|  | 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<phosphor::logging::level::INFO>( | 
|  | "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<sdeventplus::source::Defer>( | 
|  | _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<msgPos>(event), std::get<levelPos>(event), | 
|  | std::get<adPos>(event).getData()); | 
|  | } | 
|  | catch (const std::exception& e) | 
|  | { | 
|  | phosphor::logging::log<phosphor::logging::level::ERR>( | 
|  | "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<sdeventplus::source::Defer> _eventSource; | 
|  |  | 
|  | /** | 
|  | * @brief The queue of event logs to create. | 
|  | */ | 
|  | std::queue<EventEntry> _eventsToCreate; | 
|  | }; | 
|  |  | 
|  | } // namespace openpower::pels |