blob: 1622426c98d03f32f675f4dedb599cee5f205e8b [file] [log] [blame]
#pragma once
#include "types.hpp"
#include <filesystem>
#include <fstream>
#include <iostream>
#include <memory>
#include <mutex>
#include <queue>
#include <source_location>
#include <string_view>
namespace vpd
{
/**
* @brief Enum class defining placeholder tags.
*
* The tag will be used by APIs to identify the endpoint for a given log
* message.
*/
enum class PlaceHolder
{
DEFAULT, /* logs to the journal */
PEL, /* Creates a PEL */
COLLECTION, /* Logs collection messages */
VPD_WRITE /* Logs VPD write details */
};
/**
* @brief Class to handle file operations w.r.t logging.
* Based on the placeholder the class will handle different file operations to
* log error messages.
*/
class ILogFileHandler
{
protected:
// absolute file path of log file
std::filesystem::path m_filePath{};
// max number of log entries in file
size_t m_maxEntries{256};
// file stream object to do file operations
std::fstream m_fileStream;
// current number of log entries in file
size_t m_currentNumEntries{0};
/**
* @brief API to rotate file.
*
* This API rotates the logs within a file by deleting specified number of
* oldest entries.
*
* @param[in] i_numEntriesToDelete - Number of entries to delete.
*
* @throw std::runtime_error
*/
virtual void rotateFile(
[[maybe_unused]] const unsigned i_numEntriesToDelete = 5);
/**
* @brief Constructor.
* Private so that can't be initialized by class(es) other than friends.
*
* @param[in] i_filePath - Absolute path of the log file.
* @param[in] i_maxEntries - Maximum number of entries in the log file after
* which the file will be rotated.
*/
ILogFileHandler(const std::filesystem::path& i_filePath,
const size_t i_maxEntries) :
m_filePath{i_filePath}, m_maxEntries{i_maxEntries}
{
// open the file in append mode
m_fileStream.open(m_filePath, std::ios::out | std::ios::app);
// enable exception mask to throw on badbit and failbit
m_fileStream.exceptions(std::ios_base::badbit | std::ios_base::failbit);
}
/**
* @brief API to generate timestamp in string format.
*
* @return Returns timestamp in string format on success, otherwise returns
* empty string in case of any error.
*/
static inline std::string timestamp() noexcept
{
try
{
const auto l_now = std::chrono::system_clock::now();
const auto l_in_time_t =
std::chrono::system_clock::to_time_t(l_now);
const auto l_ms =
std::chrono::duration_cast<std::chrono::milliseconds>(
l_now.time_since_epoch()) %
1000;
std::stringstream l_ss;
l_ss << std::put_time(std::localtime(&l_in_time_t),
"%Y-%m-%d %H:%M:%S")
<< "." << std::setfill('0') << std::setw(3) << l_ms.count();
return l_ss.str();
}
catch (const std::exception& l_ex)
{
return std::string{};
}
}
public:
// deleted methods
ILogFileHandler() = delete;
ILogFileHandler(const ILogFileHandler&) = delete;
ILogFileHandler(const ILogFileHandler&&) = delete;
ILogFileHandler operator=(const ILogFileHandler&) = delete;
ILogFileHandler operator=(const ILogFileHandler&&) = delete;
/**
* @brief API to log a message to file.
*
* @param[in] i_message - Message to log.
*
* @throw std::runtime_error
*/
virtual void logMessage(
[[maybe_unused]] const std::string_view& i_message) = 0;
// destructor
virtual ~ILogFileHandler()
{
if (m_fileStream.is_open())
{
m_fileStream.close();
}
}
};
/**
* @brief A class to handle logging messages to file synchronously
*
* This class handles logging messages to a specific file in a synchronous
* manner.
* Note: The logMessage API of this class is not multi-thread safe.
*/
class SyncFileLogger final : public ILogFileHandler
{
/**
* @brief Parameterized constructor.
* Private so that can't be initialized by class(es) other than friends.
*
* @param[in] i_filePath - Absolute path of the log file.
* @param[in] i_maxEntries - Maximum number of entries in the log file after
* which the file will be rotated.
*/
SyncFileLogger(const std::filesystem::path& i_filePath,
const size_t i_maxEntries) :
ILogFileHandler(i_filePath, i_maxEntries)
{}
public:
// Friend class Logger.
friend class Logger;
// deleted methods
SyncFileLogger() = delete;
SyncFileLogger(const SyncFileLogger&) = delete;
SyncFileLogger(const SyncFileLogger&&) = delete;
SyncFileLogger operator=(const SyncFileLogger&) = delete;
SyncFileLogger operator=(const SyncFileLogger&&) = delete;
/**
* @brief API to log a message to file
*
* This API logs messages to file in a synchronous manner.
* Note: This API is not multi-thread safe.
*
* @param[in] i_message - Message to log
*
* @throw std::runtime_error
*/
void logMessage(const std::string_view& i_message) override;
// destructor
~SyncFileLogger() = default;
};
/**
* @brief A class to handle asynchronous logging of messages to file
*
* This class implements methods to log messages asynchronously to a desired
* file in the filesystem. It uses a queue for buffering the messages from
* caller. The actual file operations are handled by a worker thread.
*/
class AsyncFileLogger final : public ILogFileHandler
{
/**
* @brief Constructor
* Private so that can't be initialized by class(es) other than friends.
*
* @param[in] i_fileName - Name of the log file
* @param[in] i_maxEntries - Maximum number of entries in the log file after
* which the file will be rotated
*/
AsyncFileLogger(const std::filesystem::path& i_fileName,
const size_t i_maxEntries) :
ILogFileHandler(i_fileName, i_maxEntries)
{
// start worker thread in detached mode
std::thread{[this]() { this->fileWorker(); }}.detach();
}
/**
* @brief Logger worker thread body
*/
void fileWorker() noexcept;
public:
// Friend class Logger.
friend class Logger;
// deleted methods
AsyncFileLogger() = delete;
AsyncFileLogger(const AsyncFileLogger&) = delete;
AsyncFileLogger(const AsyncFileLogger&&) = delete;
AsyncFileLogger operator=(const AsyncFileLogger&) = delete;
AsyncFileLogger operator=(const AsyncFileLogger&&) = delete;
/**
* @brief API to log a message to file
*
* @param[in] i_message - Message to log
*
* @throw std::runtime_error
*/
void logMessage(const std::string_view& i_message) override;
// destructor
~AsyncFileLogger()
{
/* TODO
- acquire lock
- set log stop flag to true
- notify log worker thread
*/
if (m_fileStream.is_open())
{
m_fileStream.close();
}
}
};
/**
* @brief Singleton class to handle error logging for the repository.
*/
class Logger
{
public:
/**
* @brief Deleted Methods
*/
Logger(const Logger&) = delete; // Copy constructor
Logger(const Logger&&) = delete; // Move constructor
Logger operator=(const Logger&) = delete; // Copy assignment operator
Logger operator=(const Logger&&) = delete; // Move assignment operator
/**
* @brief Method to get instance of Logger class.
*/
static std::shared_ptr<Logger> getLoggerInstance()
{
if (!m_loggerInstance)
{
m_loggerInstance = std::shared_ptr<Logger>(new Logger());
}
return m_loggerInstance;
}
/**
* @brief API to log a given error message.
*
* @param[in] i_message - Message to be logged.
* @param[in] i_placeHolder - States where the message needs to be logged.
* Default is journal.
* @param[in] i_pelTuple - A structure only required in case message needs
* to be logged as PEL.
* @param[in] i_location - Locatuon from where message needs to be logged.
*/
void logMessage(std::string_view i_message,
const PlaceHolder& i_placeHolder = PlaceHolder::DEFAULT,
const types::PelInfoTuple* i_pelTuple = nullptr,
const std::source_location& i_location =
std::source_location::current());
/**
* @brief API to initiate VPD collection logging.
*
* This API initiates VPD collection logging. It checks for existing
* collection log files and if 3 such files are found, it deletes the oldest
* file and initiates a VPD collection logger object, so that every new VPD
* collection flow always gets logged into a new file.
*/
void initiateVpdCollectionLogging() noexcept;
/**
* @brief API to terminate VPD collection logging.
*
* This API terminates the VPD collection logging by destroying the
* associated VPD collection logger object.
*/
void terminateVpdCollectionLogging() noexcept
{
m_collectionLogger.reset();
}
private:
/**
* @brief Constructor
*/
Logger() : m_vpdWriteLogger(nullptr), m_collectionLogger(nullptr)
{
m_vpdWriteLogger.reset(
new SyncFileLogger("/var/lib/vpd/vpdWrite.log", 128));
}
// Instance to the logger class.
static std::shared_ptr<Logger> m_loggerInstance;
// logger object to handle VPD write logs
std::unique_ptr<ILogFileHandler> m_vpdWriteLogger;
// logger object to handle VPD collection logs
std::unique_ptr<ILogFileHandler> m_collectionLogger;
};
/**
* @brief The namespace defines logging related methods for VPD.
* Only for backward compatibility till new logger class comes up.
*/
namespace logging
{
/**
* @brief An api to log message.
* This API should be called to log message. It will auto append information
* like file name, line and function name to the message being logged.
*
* @param[in] message - Information that we want to log.
* @param[in] location - Object of source_location class.
*/
void logMessage(std::string_view message, const std::source_location& location =
std::source_location::current());
} // namespace logging
} // namespace vpd