blob: 6cbafde5801cb3f2eca1240a387b0f9613402b23 [file] [log] [blame]
#pragma once
#include "additional_data.hpp"
#include "elog_entry.hpp"
#include <phosphor-logging/log.hpp>
#include <sdeventplus/event.hpp>
#include <sdeventplus/source/event.hpp>
#include <queue>
#include <tuple>
namespace openpower::pels
{
/**
* @class EventLogger
*
* This class handles creating OpenBMC event logs (and thus PELs) from
* within the PEL extension code.
*
* The function to actually create the event log is passed in via the
* constructor so that different functions can be used when testing.
*
* To create the event log, call log() with the appropriate arguments
* and the log will be created as soon as the flow gets back to the event
* loop. If the queue isn't empty after a log is created, the next
* one will be scheduled to be created from the event loop again.
*
* This class does not allow new events to be added while inside the
* creation function, because if the code added an event log every time
* it tried to create one, it would do so infinitely.
*/
class EventLogger
{
public:
using ADMap = std::map<std::string, std::string>;
using LogFunction = std::function<void(
const std::string&, phosphor::logging::Entry::Level, const ADMap&)>;
static constexpr size_t msgPos = 0;
static constexpr size_t levelPos = 1;
static constexpr size_t adPos = 2;
using EventEntry = std::tuple<std::string, phosphor::logging::Entry::Level,
AdditionalData>;
EventLogger() = delete;
~EventLogger() = default;
EventLogger(const EventLogger&) = delete;
EventLogger& operator=(const EventLogger&) = delete;
EventLogger(EventLogger&&) = delete;
EventLogger& operator=(EventLogger&&) = delete;
/**
* @brief Constructor
*
* @param[in] creator - The function to use to create the event log
*/
explicit EventLogger(LogFunction creator) :
_event(sdeventplus::Event::get_default()), _creator(creator)
{}
/**
* @brief Adds an event to the queue so that it will be created
* as soon as the code makes it back to the event loop.
*
* Won't add it to the queue if already inside the create()
* callback.
*
* @param[in] message - The message property of the event log
* @param[in] severity - The severity level of the event log
* @param[in] ad - The additional data property of the event log
*/
void log(const std::string& message,
phosphor::logging::Entry::Level severity, const AdditionalData& ad)
{
if (!_inEventCreation)
{
_eventsToCreate.emplace(message, severity, ad);
if (!_eventSource)
{
scheduleCreate();
}
}
else
{
phosphor::logging::log<phosphor::logging::level::INFO>(
"Already in event create callback, skipping new create",
phosphor::logging::entry("ERROR_NAME=%s", message.c_str()));
}
}
/**
* @brief Returns the event log queue size.
*
* @return size_t - The queue size
*/
size_t queueSize() const
{
return _eventsToCreate.size();
}
/**
* @brief Schedules the create() function to run using the
* 'defer' sd_event source.
*/
void scheduleCreate()
{
_eventSource = std::make_unique<sdeventplus::source::Defer>(
_event, std::bind(std::mem_fn(&EventLogger::create), this,
std::placeholders::_1));
}
private:
/**
* @brief Creates an event log and schedules the next one if
* there is one.
*
* This gets called from the event loop by the sd_event code.
*
* @param[in] source - The event source object used
*/
void create(sdeventplus::source::EventBase& /*source*/)
{
_eventSource.reset();
if (_eventsToCreate.empty())
{
return;
}
auto event = _eventsToCreate.front();
_eventsToCreate.pop();
_inEventCreation = true;
try
{
_creator(std::get<msgPos>(event), std::get<levelPos>(event),
std::get<adPos>(event).getData());
}
catch (const std::exception& e)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"EventLogger's create function threw an exception",
phosphor::logging::entry("ERROR=%s", e.what()));
}
_inEventCreation = false;
if (!_eventsToCreate.empty())
{
scheduleCreate();
}
}
/**
* @brief The sd_event object.
*/
sdeventplus::Event _event;
/**
* @brief The user supplied function to create the event log.
*/
LogFunction _creator;
/**
* @brief Keeps track of if an event is currently being created.
*
* Guards against creating new events while creating events.
*/
bool _inEventCreation = false;
/**
* @brief The event source object used for scheduling.
*/
std::unique_ptr<sdeventplus::source::Defer> _eventSource;
/**
* @brief The queue of event logs to create.
*/
std::queue<EventEntry> _eventsToCreate;
};
} // namespace openpower::pels