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