blob: b4df0d47aa84e6cf5d00004c7989d41b3a605248 [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 *
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