blob: 6cbafde5801cb3f2eca1240a387b0f9613402b23 [file] [log] [blame]
Matt Spinlerf682b402019-12-18 13:48:08 -06001#pragma once
2
3#include "additional_data.hpp"
4#include "elog_entry.hpp"
5
6#include <phosphor-logging/log.hpp>
Matt Spinlerf682b402019-12-18 13:48:08 -06007#include <sdeventplus/event.hpp>
8#include <sdeventplus/source/event.hpp>
Patrick Williams2544b412022-10-04 08:41:06 -05009
10#include <queue>
Matt Spinlerf682b402019-12-18 13:48:08 -060011#include <tuple>
12
13namespace 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 */
34class 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 *
Matt Spinlerf682b402019-12-18 13:48:08 -060057 * @param[in] creator - The function to use to create the event log
58 */
Matt Spinler45796e82022-07-01 11:25:27 -050059 explicit EventLogger(LogFunction creator) :
Matt Spinler104e9362020-04-02 09:34:41 -050060 _event(sdeventplus::Event::get_default()), _creator(creator)
Patrick Williams2544b412022-10-04 08:41:06 -050061 {}
Matt Spinlerf682b402019-12-18 13:48:08 -060062
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 */
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 */
99 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 */
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 */
Patrick Williamsd26fa3e2021-04-21 15:22:23 -0500124 void create(sdeventplus::source::EventBase& /*source*/)
Matt Spinlerf682b402019-12-18 13:48:08 -0600125 {
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 }
Patrick Williams66491c62021-10-06 12:23:37 -0500143 catch (const std::exception& e)
Matt Spinlerf682b402019-12-18 13:48:08 -0600144 {
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