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