blob: 1af8e2eed8dfc869d140efec33947eb878555775 [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>
7#include <queue>
8#include <sdeventplus/event.hpp>
9#include <sdeventplus/source/event.hpp>
10#include <tuple>
11
12namespace 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 */
33class 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 *
Matt Spinlerf682b402019-12-18 13:48:08 -060056 * @param[in] creator - The function to use to create the event log
57 */
Matt Spinler45796e82022-07-01 11:25:27 -050058 explicit EventLogger(LogFunction creator) :
Matt Spinler104e9362020-04-02 09:34:41 -050059 _event(sdeventplus::Event::get_default()), _creator(creator)
Matt Spinlerf682b402019-12-18 13:48:08 -060060 {
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 */
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