1 #pragma once 2 3 #include "additional_data.hpp" 4 #include "elog_entry.hpp" 5 6 #include <phosphor-logging/log.hpp> 7 #include <sdeventplus/event.hpp> 8 #include <sdeventplus/source/event.hpp> 9 10 #include <queue> 11 #include <tuple> 12 13 namespace openpower::pels 14 { 15 16 /** 17 * @class EventLogger 18 * 19 * This class handles creating OpenBMC event logs (and thus PELs) from 20 * within the PEL extension code. 21 * 22 * The function to actually create the event log is passed in via the 23 * constructor so that different functions can be used when testing. 24 * 25 * To create the event log, call log() with the appropriate arguments 26 * and the log will be created as soon as the flow gets back to the event 27 * loop. If the queue isn't empty after a log is created, the next 28 * one will be scheduled to be created from the event loop again. 29 * 30 * This class does not allow new events to be added while inside the 31 * creation function, because if the code added an event log every time 32 * it tried to create one, it would do so infinitely. 33 */ 34 class EventLogger 35 { 36 public: 37 using ADMap = std::map<std::string, std::string>; 38 using LogFunction = std::function<void( 39 const std::string&, phosphor::logging::Entry::Level, const ADMap&)>; 40 41 static constexpr size_t msgPos = 0; 42 static constexpr size_t levelPos = 1; 43 static constexpr size_t adPos = 2; 44 using EventEntry = std::tuple<std::string, phosphor::logging::Entry::Level, 45 AdditionalData>; 46 47 EventLogger() = delete; 48 ~EventLogger() = default; 49 EventLogger(const EventLogger&) = delete; 50 EventLogger& operator=(const EventLogger&) = delete; 51 EventLogger(EventLogger&&) = delete; 52 EventLogger& operator=(EventLogger&&) = delete; 53 54 /** 55 * @brief Constructor 56 * 57 * @param[in] creator - The function to use to create the event log 58 */ EventLogger(LogFunction creator)59 explicit EventLogger(LogFunction creator) : 60 _event(sdeventplus::Event::get_default()), _creator(creator) 61 {} 62 63 /** 64 * @brief Adds an event to the queue so that it will be created 65 * as soon as the code makes it back to the event loop. 66 * 67 * Won't add it to the queue if already inside the create() 68 * callback. 69 * 70 * @param[in] message - The message property of the event log 71 * @param[in] severity - The severity level of the event log 72 * @param[in] ad - The additional data property of the event log 73 */ log(const std::string & message,phosphor::logging::Entry::Level severity,const AdditionalData & ad)74 void log(const std::string& message, 75 phosphor::logging::Entry::Level severity, const AdditionalData& ad) 76 { 77 if (!_inEventCreation) 78 { 79 _eventsToCreate.emplace(message, severity, ad); 80 81 if (!_eventSource) 82 { 83 scheduleCreate(); 84 } 85 } 86 else 87 { 88 phosphor::logging::log<phosphor::logging::level::INFO>( 89 "Already in event create callback, skipping new create", 90 phosphor::logging::entry("ERROR_NAME=%s", message.c_str())); 91 } 92 } 93 94 /** 95 * @brief Returns the event log queue size. 96 * 97 * @return size_t - The queue size 98 */ queueSize() const99 size_t queueSize() const 100 { 101 return _eventsToCreate.size(); 102 } 103 104 /** 105 * @brief Schedules the create() function to run using the 106 * 'defer' sd_event source. 107 */ scheduleCreate()108 void scheduleCreate() 109 { 110 _eventSource = std::make_unique<sdeventplus::source::Defer>( 111 _event, std::bind(std::mem_fn(&EventLogger::create), this, 112 std::placeholders::_1)); 113 } 114 115 private: 116 /** 117 * @brief Creates an event log and schedules the next one if 118 * there is one. 119 * 120 * This gets called from the event loop by the sd_event code. 121 * 122 * @param[in] source - The event source object used 123 */ create(sdeventplus::source::EventBase &)124 void create(sdeventplus::source::EventBase& /*source*/) 125 { 126 _eventSource.reset(); 127 128 if (_eventsToCreate.empty()) 129 { 130 return; 131 } 132 133 auto event = _eventsToCreate.front(); 134 _eventsToCreate.pop(); 135 136 _inEventCreation = true; 137 138 try 139 { 140 _creator(std::get<msgPos>(event), std::get<levelPos>(event), 141 std::get<adPos>(event).getData()); 142 } 143 catch (const std::exception& e) 144 { 145 phosphor::logging::log<phosphor::logging::level::ERR>( 146 "EventLogger's create function threw an exception", 147 phosphor::logging::entry("ERROR=%s", e.what())); 148 } 149 150 _inEventCreation = false; 151 152 if (!_eventsToCreate.empty()) 153 { 154 scheduleCreate(); 155 } 156 } 157 158 /** 159 * @brief The sd_event object. 160 */ 161 sdeventplus::Event _event; 162 163 /** 164 * @brief The user supplied function to create the event log. 165 */ 166 LogFunction _creator; 167 168 /** 169 * @brief Keeps track of if an event is currently being created. 170 * 171 * Guards against creating new events while creating events. 172 */ 173 bool _inEventCreation = false; 174 175 /** 176 * @brief The event source object used for scheduling. 177 */ 178 std::unique_ptr<sdeventplus::source::Defer> _eventSource; 179 180 /** 181 * @brief The queue of event logs to create. 182 */ 183 std::queue<EventEntry> _eventsToCreate; 184 }; 185 186 } // namespace openpower::pels 187