blob: 59b7afd89b898fa3ad5f35b76e536cb5ac3bcb35 [file] [log] [blame]
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001#pragma once
2
Sunny Srivastava5779d972025-08-08 01:45:23 -05003#include "types.hpp"
4
Souvik Royf8587362025-09-25 09:56:20 +00005#include <condition_variable>
Souvik Roya8c3c092025-09-11 10:49:29 +00006#include <filesystem>
7#include <fstream>
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05008#include <iostream>
Sunny Srivastava5779d972025-08-08 01:45:23 -05009#include <memory>
Souvik Roy0fb5c3b2025-09-15 08:18:25 +000010#include <mutex>
11#include <queue>
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -050012#include <source_location>
13#include <string_view>
14
15namespace vpd
16{
Sunny Srivastava5779d972025-08-08 01:45:23 -050017
18/**
19 * @brief Enum class defining placeholder tags.
20 *
21 * The tag will be used by APIs to identify the endpoint for a given log
22 * message.
23 */
24enum class PlaceHolder
25{
Souvik Roya5e18b82025-09-25 05:59:56 +000026 DEFAULT, /* logs to the journal */
27 PEL, /* Creates a PEL */
28 COLLECTION, /* Logs collection messages */
29 VPD_WRITE /* Logs VPD write details */
Sunny Srivastava5779d972025-08-08 01:45:23 -050030};
31
32/**
33 * @brief Class to handle file operations w.r.t logging.
34 * Based on the placeholder the class will handle different file operations to
35 * log error messages.
36 */
Souvik Roya5e18b82025-09-25 05:59:56 +000037class ILogFileHandler
Sunny Srivastava5779d972025-08-08 01:45:23 -050038{
Souvik Roya8c3c092025-09-11 10:49:29 +000039 protected:
40 // absolute file path of log file
41 std::filesystem::path m_filePath{};
42
43 // max number of log entries in file
44 size_t m_maxEntries{256};
45
Souvik Roy6bd74ad2025-09-12 06:24:06 +000046 // file stream object to do file operations
47 std::fstream m_fileStream;
48
49 // current number of log entries in file
50 size_t m_currentNumEntries{0};
51
Sunny Srivastava5779d972025-08-08 01:45:23 -050052 /**
Souvik Roya8c3c092025-09-11 10:49:29 +000053 * @brief API to rotate file.
Sunny Srivastava5779d972025-08-08 01:45:23 -050054 *
Souvik Roya8c3c092025-09-11 10:49:29 +000055 * This API rotates the logs within a file by deleting specified number of
56 * oldest entries.
Sunny Srivastava5779d972025-08-08 01:45:23 -050057 *
Souvik Roya8c3c092025-09-11 10:49:29 +000058 * @param[in] i_numEntriesToDelete - Number of entries to delete.
59 *
60 * @throw std::runtime_error
Sunny Srivastava5779d972025-08-08 01:45:23 -050061 */
Souvik Roya8c3c092025-09-11 10:49:29 +000062 virtual void rotateFile(
63 [[maybe_unused]] const unsigned i_numEntriesToDelete = 5);
64
65 /**
66 * @brief Constructor.
67 * Private so that can't be initialized by class(es) other than friends.
68 *
69 * @param[in] i_filePath - Absolute path of the log file.
70 * @param[in] i_maxEntries - Maximum number of entries in the log file after
71 * which the file will be rotated.
72 */
73 ILogFileHandler(const std::filesystem::path& i_filePath,
74 const size_t i_maxEntries) :
75 m_filePath{i_filePath}, m_maxEntries{i_maxEntries}
Sunny Srivastava5779d972025-08-08 01:45:23 -050076 {
Souvik Roy6bd74ad2025-09-12 06:24:06 +000077 // open the file in append mode
78 m_fileStream.open(m_filePath, std::ios::out | std::ios::app);
79
80 // enable exception mask to throw on badbit and failbit
81 m_fileStream.exceptions(std::ios_base::badbit | std::ios_base::failbit);
Sunny Srivastava5779d972025-08-08 01:45:23 -050082 }
83
Sunny Srivastava5779d972025-08-08 01:45:23 -050084 /**
Souvik Roya8c3c092025-09-11 10:49:29 +000085 * @brief API to generate timestamp in string format.
86 *
87 * @return Returns timestamp in string format on success, otherwise returns
88 * empty string in case of any error.
Sunny Srivastava5779d972025-08-08 01:45:23 -050089 */
Souvik Roya8c3c092025-09-11 10:49:29 +000090 static inline std::string timestamp() noexcept
91 {
Souvik Roy6bd74ad2025-09-12 06:24:06 +000092 try
93 {
94 const auto l_now = std::chrono::system_clock::now();
95 const auto l_in_time_t =
96 std::chrono::system_clock::to_time_t(l_now);
97 const auto l_ms =
98 std::chrono::duration_cast<std::chrono::milliseconds>(
99 l_now.time_since_epoch()) %
100 1000;
101
102 std::stringstream l_ss;
103 l_ss << std::put_time(std::localtime(&l_in_time_t),
104 "%Y-%m-%d %H:%M:%S")
105 << "." << std::setfill('0') << std::setw(3) << l_ms.count();
106 return l_ss.str();
107 }
108 catch (const std::exception& l_ex)
109 {
110 return std::string{};
111 }
Souvik Roya8c3c092025-09-11 10:49:29 +0000112 }
Sunny Srivastava5779d972025-08-08 01:45:23 -0500113
Souvik Roya8c3c092025-09-11 10:49:29 +0000114 public:
115 // deleted methods
116 ILogFileHandler() = delete;
117 ILogFileHandler(const ILogFileHandler&) = delete;
118 ILogFileHandler(const ILogFileHandler&&) = delete;
119 ILogFileHandler operator=(const ILogFileHandler&) = delete;
120 ILogFileHandler operator=(const ILogFileHandler&&) = delete;
121
122 /**
123 * @brief API to log a message to file.
124 *
125 * @param[in] i_message - Message to log.
126 *
127 * @throw std::runtime_error
128 */
129 virtual void logMessage(
130 [[maybe_unused]] const std::string_view& i_message) = 0;
131
132 // destructor
133 virtual ~ILogFileHandler()
134 {
Souvik Roy6bd74ad2025-09-12 06:24:06 +0000135 if (m_fileStream.is_open())
136 {
137 m_fileStream.close();
138 }
Souvik Roya8c3c092025-09-11 10:49:29 +0000139 }
Sunny Srivastava5779d972025-08-08 01:45:23 -0500140};
141
142/**
Souvik Roy6bd74ad2025-09-12 06:24:06 +0000143 * @brief A class to handle logging messages to file synchronously
144 *
145 * This class handles logging messages to a specific file in a synchronous
146 * manner.
147 * Note: The logMessage API of this class is not multi-thread safe.
148 */
149class SyncFileLogger final : public ILogFileHandler
150{
151 /**
152 * @brief Parameterized constructor.
153 * Private so that can't be initialized by class(es) other than friends.
154 *
155 * @param[in] i_filePath - Absolute path of the log file.
156 * @param[in] i_maxEntries - Maximum number of entries in the log file after
157 * which the file will be rotated.
158 */
159 SyncFileLogger(const std::filesystem::path& i_filePath,
160 const size_t i_maxEntries) :
161 ILogFileHandler(i_filePath, i_maxEntries)
162 {}
163
164 public:
165 // Friend class Logger.
166 friend class Logger;
167
168 // deleted methods
169 SyncFileLogger() = delete;
170 SyncFileLogger(const SyncFileLogger&) = delete;
171 SyncFileLogger(const SyncFileLogger&&) = delete;
172 SyncFileLogger operator=(const SyncFileLogger&) = delete;
173 SyncFileLogger operator=(const SyncFileLogger&&) = delete;
174
175 /**
176 * @brief API to log a message to file
177 *
178 * This API logs messages to file in a synchronous manner.
179 * Note: This API is not multi-thread safe.
180 *
181 * @param[in] i_message - Message to log
182 *
183 * @throw std::runtime_error
184 */
185 void logMessage(const std::string_view& i_message) override;
186
187 // destructor
188 ~SyncFileLogger() = default;
189};
190
191/**
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000192 * @brief A class to handle asynchronous logging of messages to file
193 *
194 * This class implements methods to log messages asynchronously to a desired
195 * file in the filesystem. It uses a queue for buffering the messages from
196 * caller. The actual file operations are handled by a worker thread.
197 */
198class AsyncFileLogger final : public ILogFileHandler
199{
Souvik Royf8587362025-09-25 09:56:20 +0000200 // queue for log messages
201 std::queue<std::string> m_messageQueue;
202
203 // mutex to control access to log message queue
204 std::mutex m_mutex;
205
206 // flag which indicates log worker thread if logging is finished
207 std::atomic_bool m_stopLogging{false};
208
209 // conditional variable to signal log worker thread
210 std::condition_variable m_cv;
211
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000212 /**
213 * @brief Constructor
214 * Private so that can't be initialized by class(es) other than friends.
215 *
216 * @param[in] i_fileName - Name of the log file
217 * @param[in] i_maxEntries - Maximum number of entries in the log file after
218 * which the file will be rotated
219 */
220 AsyncFileLogger(const std::filesystem::path& i_fileName,
221 const size_t i_maxEntries) :
222 ILogFileHandler(i_fileName, i_maxEntries)
223 {
224 // start worker thread in detached mode
225 std::thread{[this]() { this->fileWorker(); }}.detach();
226 }
227
228 /**
229 * @brief Logger worker thread body
230 */
231 void fileWorker() noexcept;
232
233 public:
234 // Friend class Logger.
235 friend class Logger;
236
237 // deleted methods
238 AsyncFileLogger() = delete;
239 AsyncFileLogger(const AsyncFileLogger&) = delete;
240 AsyncFileLogger(const AsyncFileLogger&&) = delete;
241 AsyncFileLogger operator=(const AsyncFileLogger&) = delete;
242 AsyncFileLogger operator=(const AsyncFileLogger&&) = delete;
243
244 /**
245 * @brief API to log a message to file
246 *
Souvik Royf8587362025-09-25 09:56:20 +0000247 * This API logs given message to a file. This API is multi-thread safe.
248 *
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000249 * @param[in] i_message - Message to log
250 *
251 * @throw std::runtime_error
252 */
253 void logMessage(const std::string_view& i_message) override;
254
255 // destructor
256 ~AsyncFileLogger()
257 {
Souvik Royf8587362025-09-25 09:56:20 +0000258 std::unique_lock<std::mutex> l_lock(m_mutex);
259
260 m_stopLogging = true;
261
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000262 if (m_fileStream.is_open())
263 {
264 m_fileStream.close();
265 }
Souvik Royf8587362025-09-25 09:56:20 +0000266
267 m_cv.notify_one();
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000268 }
269};
270
271/**
Sunny Srivastava5779d972025-08-08 01:45:23 -0500272 * @brief Singleton class to handle error logging for the repository.
273 */
274class Logger
275{
276 public:
277 /**
278 * @brief Deleted Methods
279 */
Souvik Roya5e18b82025-09-25 05:59:56 +0000280 Logger(const Logger&) = delete; // Copy constructor
281 Logger(const Logger&&) = delete; // Move constructor
282 Logger operator=(const Logger&) = delete; // Copy assignment operator
283 Logger operator=(const Logger&&) = delete; // Move assignment operator
Sunny Srivastava5779d972025-08-08 01:45:23 -0500284
285 /**
286 * @brief Method to get instance of Logger class.
287 */
288 static std::shared_ptr<Logger> getLoggerInstance()
289 {
290 if (!m_loggerInstance)
291 {
292 m_loggerInstance = std::shared_ptr<Logger>(new Logger());
293 }
294 return m_loggerInstance;
295 }
296
297 /**
298 * @brief API to log a given error message.
299 *
300 * @param[in] i_message - Message to be logged.
301 * @param[in] i_placeHolder - States where the message needs to be logged.
302 * Default is journal.
303 * @param[in] i_pelTuple - A structure only required in case message needs
304 * to be logged as PEL.
305 * @param[in] i_location - Locatuon from where message needs to be logged.
306 */
307 void logMessage(std::string_view i_message,
308 const PlaceHolder& i_placeHolder = PlaceHolder::DEFAULT,
309 const types::PelInfoTuple* i_pelTuple = nullptr,
310 const std::source_location& i_location =
311 std::source_location::current());
312
Souvik Roya5e18b82025-09-25 05:59:56 +0000313 /**
314 * @brief API to initiate VPD collection logging.
315 *
316 * This API initiates VPD collection logging. It checks for existing
317 * collection log files and if 3 such files are found, it deletes the oldest
318 * file and initiates a VPD collection logger object, so that every new VPD
319 * collection flow always gets logged into a new file.
320 */
321 void initiateVpdCollectionLogging() noexcept;
322
323 /**
324 * @brief API to terminate VPD collection logging.
325 *
326 * This API terminates the VPD collection logging by destroying the
327 * associated VPD collection logger object.
328 */
329 void terminateVpdCollectionLogging() noexcept
330 {
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000331 m_collectionLogger.reset();
Souvik Roya5e18b82025-09-25 05:59:56 +0000332 }
333
Sunny Srivastava5779d972025-08-08 01:45:23 -0500334 private:
335 /**
336 * @brief Constructor
337 */
Souvik Roya5e18b82025-09-25 05:59:56 +0000338 Logger() : m_vpdWriteLogger(nullptr), m_collectionLogger(nullptr)
Sunny Srivastava5779d972025-08-08 01:45:23 -0500339 {
Souvik Roy6bd74ad2025-09-12 06:24:06 +0000340 m_vpdWriteLogger.reset(
341 new SyncFileLogger("/var/lib/vpd/vpdWrite.log", 128));
Sunny Srivastava5779d972025-08-08 01:45:23 -0500342 }
343
344 // Instance to the logger class.
345 static std::shared_ptr<Logger> m_loggerInstance;
346
Souvik Roya5e18b82025-09-25 05:59:56 +0000347 // logger object to handle VPD write logs
348 std::unique_ptr<ILogFileHandler> m_vpdWriteLogger;
349
350 // logger object to handle VPD collection logs
351 std::unique_ptr<ILogFileHandler> m_collectionLogger;
Sunny Srivastava5779d972025-08-08 01:45:23 -0500352};
353
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500354/**
355 * @brief The namespace defines logging related methods for VPD.
Sunny Srivastava5779d972025-08-08 01:45:23 -0500356 * Only for backward compatibility till new logger class comes up.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500357 */
358namespace logging
359{
360
361/**
362 * @brief An api to log message.
363 * This API should be called to log message. It will auto append information
364 * like file name, line and function name to the message being logged.
365 *
366 * @param[in] message - Information that we want to log.
367 * @param[in] location - Object of source_location class.
368 */
369void logMessage(std::string_view message, const std::source_location& location =
370 std::source_location::current());
371} // namespace logging
372} // namespace vpd