blob: 8dc8b556e672263d58c7d35ae17dd3ebb4f86c05 [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
Souvik Royd834b122025-10-28 06:37:51 +000052 // number of chars in a single log entry
53 size_t m_logEntrySize{512};
54
Sunny Srivastava5779d972025-08-08 01:45:23 -050055 /**
Souvik Roya8c3c092025-09-11 10:49:29 +000056 * @brief API to rotate file.
Sunny Srivastava5779d972025-08-08 01:45:23 -050057 *
Souvik Royd834b122025-10-28 06:37:51 +000058 * This API rotates the logs within a file by repositioning the write file
59 * pointer to beginning of file. Rotation is achieved by overwriting the
60 * oldest log entries starting from the top of the file.
Sunny Srivastava5779d972025-08-08 01:45:23 -050061 *
Souvik Royd834b122025-10-28 06:37:51 +000062 * @throw std::ios_base::failure
Sunny Srivastava5779d972025-08-08 01:45:23 -050063 */
Souvik Royd834b122025-10-28 06:37:51 +000064 virtual inline void rotateFile()
65 {
66 // reset file pointer to beginning of file
67 m_fileStream.seekp(0);
68 m_currentNumEntries = 0;
69 }
Souvik Roya8c3c092025-09-11 10:49:29 +000070
71 /**
72 * @brief Constructor.
73 * Private so that can't be initialized by class(es) other than friends.
74 *
75 * @param[in] i_filePath - Absolute path of the log file.
76 * @param[in] i_maxEntries - Maximum number of entries in the log file after
77 * which the file will be rotated.
78 */
79 ILogFileHandler(const std::filesystem::path& i_filePath,
Souvik Roybac8ba62025-10-27 09:07:06 +000080 const size_t i_maxEntries);
Sunny Srivastava5779d972025-08-08 01:45:23 -050081
Sunny Srivastava5779d972025-08-08 01:45:23 -050082 /**
Souvik Roya8c3c092025-09-11 10:49:29 +000083 * @brief API to generate timestamp in string format.
84 *
85 * @return Returns timestamp in string format on success, otherwise returns
86 * empty string in case of any error.
Sunny Srivastava5779d972025-08-08 01:45:23 -050087 */
Souvik Roya8c3c092025-09-11 10:49:29 +000088 static inline std::string timestamp() noexcept
89 {
Souvik Roy6bd74ad2025-09-12 06:24:06 +000090 try
91 {
92 const auto l_now = std::chrono::system_clock::now();
93 const auto l_in_time_t =
94 std::chrono::system_clock::to_time_t(l_now);
95 const auto l_ms =
96 std::chrono::duration_cast<std::chrono::milliseconds>(
97 l_now.time_since_epoch()) %
98 1000;
99
100 std::stringstream l_ss;
101 l_ss << std::put_time(std::localtime(&l_in_time_t),
102 "%Y-%m-%d %H:%M:%S")
103 << "." << std::setfill('0') << std::setw(3) << l_ms.count();
104 return l_ss.str();
105 }
106 catch (const std::exception& l_ex)
107 {
108 return std::string{};
109 }
Souvik Roya8c3c092025-09-11 10:49:29 +0000110 }
Sunny Srivastava5779d972025-08-08 01:45:23 -0500111
Souvik Roya8c3c092025-09-11 10:49:29 +0000112 public:
113 // deleted methods
114 ILogFileHandler() = delete;
115 ILogFileHandler(const ILogFileHandler&) = delete;
116 ILogFileHandler(const ILogFileHandler&&) = delete;
117 ILogFileHandler operator=(const ILogFileHandler&) = delete;
118 ILogFileHandler operator=(const ILogFileHandler&&) = delete;
119
120 /**
121 * @brief API to log a message to file.
122 *
123 * @param[in] i_message - Message to log.
124 *
125 * @throw std::runtime_error
126 */
127 virtual void logMessage(
128 [[maybe_unused]] const std::string_view& i_message) = 0;
129
130 // destructor
131 virtual ~ILogFileHandler()
132 {
Souvik Roy6bd74ad2025-09-12 06:24:06 +0000133 if (m_fileStream.is_open())
134 {
135 m_fileStream.close();
136 }
Souvik Roya8c3c092025-09-11 10:49:29 +0000137 }
Sunny Srivastava5779d972025-08-08 01:45:23 -0500138};
139
140/**
Souvik Roy6bd74ad2025-09-12 06:24:06 +0000141 * @brief A class to handle logging messages to file synchronously
142 *
143 * This class handles logging messages to a specific file in a synchronous
144 * manner.
145 * Note: The logMessage API of this class is not multi-thread safe.
146 */
147class SyncFileLogger final : public ILogFileHandler
148{
149 /**
150 * @brief Parameterized constructor.
151 * Private so that can't be initialized by class(es) other than friends.
152 *
153 * @param[in] i_filePath - Absolute path of the log file.
154 * @param[in] i_maxEntries - Maximum number of entries in the log file after
155 * which the file will be rotated.
156 */
157 SyncFileLogger(const std::filesystem::path& i_filePath,
158 const size_t i_maxEntries) :
159 ILogFileHandler(i_filePath, i_maxEntries)
160 {}
161
162 public:
163 // Friend class Logger.
164 friend class Logger;
165
166 // deleted methods
167 SyncFileLogger() = delete;
168 SyncFileLogger(const SyncFileLogger&) = delete;
169 SyncFileLogger(const SyncFileLogger&&) = delete;
170 SyncFileLogger operator=(const SyncFileLogger&) = delete;
171 SyncFileLogger operator=(const SyncFileLogger&&) = delete;
172
173 /**
174 * @brief API to log a message to file
175 *
176 * This API logs messages to file in a synchronous manner.
177 * Note: This API is not multi-thread safe.
178 *
179 * @param[in] i_message - Message to log
180 *
181 * @throw std::runtime_error
182 */
183 void logMessage(const std::string_view& i_message) override;
184
185 // destructor
186 ~SyncFileLogger() = default;
187};
188
189/**
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000190 * @brief A class to handle asynchronous logging of messages to file
191 *
192 * This class implements methods to log messages asynchronously to a desired
193 * file in the filesystem. It uses a queue for buffering the messages from
194 * caller. The actual file operations are handled by a worker thread.
195 */
196class AsyncFileLogger final : public ILogFileHandler
197{
Souvik Royf8587362025-09-25 09:56:20 +0000198 // queue for log messages
199 std::queue<std::string> m_messageQueue;
200
201 // mutex to control access to log message queue
202 std::mutex m_mutex;
203
204 // flag which indicates log worker thread if logging is finished
205 std::atomic_bool m_stopLogging{false};
206
207 // conditional variable to signal log worker thread
208 std::condition_variable m_cv;
209
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000210 /**
211 * @brief Constructor
212 * Private so that can't be initialized by class(es) other than friends.
213 *
214 * @param[in] i_fileName - Name of the log file
215 * @param[in] i_maxEntries - Maximum number of entries in the log file after
216 * which the file will be rotated
217 */
218 AsyncFileLogger(const std::filesystem::path& i_fileName,
219 const size_t i_maxEntries) :
220 ILogFileHandler(i_fileName, i_maxEntries)
221 {
222 // start worker thread in detached mode
223 std::thread{[this]() { this->fileWorker(); }}.detach();
224 }
225
226 /**
227 * @brief Logger worker thread body
228 */
229 void fileWorker() noexcept;
230
231 public:
232 // Friend class Logger.
233 friend class Logger;
234
235 // deleted methods
236 AsyncFileLogger() = delete;
237 AsyncFileLogger(const AsyncFileLogger&) = delete;
238 AsyncFileLogger(const AsyncFileLogger&&) = delete;
239 AsyncFileLogger operator=(const AsyncFileLogger&) = delete;
240 AsyncFileLogger operator=(const AsyncFileLogger&&) = delete;
241
242 /**
243 * @brief API to log a message to file
244 *
Souvik Royf8587362025-09-25 09:56:20 +0000245 * This API logs given message to a file. This API is multi-thread safe.
246 *
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000247 * @param[in] i_message - Message to log
248 *
249 * @throw std::runtime_error
250 */
251 void logMessage(const std::string_view& i_message) override;
252
253 // destructor
254 ~AsyncFileLogger()
255 {
Souvik Royf8587362025-09-25 09:56:20 +0000256 std::unique_lock<std::mutex> l_lock(m_mutex);
257
258 m_stopLogging = true;
259
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000260 if (m_fileStream.is_open())
261 {
262 m_fileStream.close();
263 }
Souvik Royf8587362025-09-25 09:56:20 +0000264
265 m_cv.notify_one();
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000266 }
267};
268
269/**
Sunny Srivastava5779d972025-08-08 01:45:23 -0500270 * @brief Singleton class to handle error logging for the repository.
271 */
272class Logger
273{
274 public:
275 /**
276 * @brief Deleted Methods
277 */
Souvik Roya5e18b82025-09-25 05:59:56 +0000278 Logger(const Logger&) = delete; // Copy constructor
279 Logger(const Logger&&) = delete; // Move constructor
280 Logger operator=(const Logger&) = delete; // Copy assignment operator
281 Logger operator=(const Logger&&) = delete; // Move assignment operator
Sunny Srivastava5779d972025-08-08 01:45:23 -0500282
283 /**
284 * @brief Method to get instance of Logger class.
285 */
286 static std::shared_ptr<Logger> getLoggerInstance()
287 {
288 if (!m_loggerInstance)
289 {
290 m_loggerInstance = std::shared_ptr<Logger>(new Logger());
291 }
292 return m_loggerInstance;
293 }
294
295 /**
296 * @brief API to log a given error message.
297 *
298 * @param[in] i_message - Message to be logged.
299 * @param[in] i_placeHolder - States where the message needs to be logged.
300 * Default is journal.
301 * @param[in] i_pelTuple - A structure only required in case message needs
302 * to be logged as PEL.
303 * @param[in] i_location - Locatuon from where message needs to be logged.
304 */
305 void logMessage(std::string_view i_message,
306 const PlaceHolder& i_placeHolder = PlaceHolder::DEFAULT,
307 const types::PelInfoTuple* i_pelTuple = nullptr,
308 const std::source_location& i_location =
309 std::source_location::current());
310
Souvik Roya5e18b82025-09-25 05:59:56 +0000311 /**
312 * @brief API to initiate VPD collection logging.
313 *
314 * This API initiates VPD collection logging. It checks for existing
315 * collection log files and if 3 such files are found, it deletes the oldest
316 * file and initiates a VPD collection logger object, so that every new VPD
317 * collection flow always gets logged into a new file.
318 */
319 void initiateVpdCollectionLogging() noexcept;
320
321 /**
322 * @brief API to terminate VPD collection logging.
323 *
324 * This API terminates the VPD collection logging by destroying the
325 * associated VPD collection logger object.
326 */
327 void terminateVpdCollectionLogging() noexcept
328 {
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000329 m_collectionLogger.reset();
Souvik Roya5e18b82025-09-25 05:59:56 +0000330 }
331
Sunny Srivastava5779d972025-08-08 01:45:23 -0500332 private:
333 /**
334 * @brief Constructor
335 */
Souvik Roya5e18b82025-09-25 05:59:56 +0000336 Logger() : m_vpdWriteLogger(nullptr), m_collectionLogger(nullptr)
Sunny Srivastava5779d972025-08-08 01:45:23 -0500337 {
Souvik Roy6bd74ad2025-09-12 06:24:06 +0000338 m_vpdWriteLogger.reset(
339 new SyncFileLogger("/var/lib/vpd/vpdWrite.log", 128));
Sunny Srivastava5779d972025-08-08 01:45:23 -0500340 }
341
342 // Instance to the logger class.
343 static std::shared_ptr<Logger> m_loggerInstance;
344
Souvik Roya5e18b82025-09-25 05:59:56 +0000345 // logger object to handle VPD write logs
346 std::unique_ptr<ILogFileHandler> m_vpdWriteLogger;
347
348 // logger object to handle VPD collection logs
349 std::unique_ptr<ILogFileHandler> m_collectionLogger;
Sunny Srivastava5779d972025-08-08 01:45:23 -0500350};
351
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500352/**
353 * @brief The namespace defines logging related methods for VPD.
Sunny Srivastava5779d972025-08-08 01:45:23 -0500354 * Only for backward compatibility till new logger class comes up.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500355 */
356namespace logging
357{
358
359/**
360 * @brief An api to log message.
361 * This API should be called to log message. It will auto append information
362 * like file name, line and function name to the message being logged.
363 *
364 * @param[in] message - Information that we want to log.
365 * @param[in] location - Object of source_location class.
366 */
367void logMessage(std::string_view message, const std::source_location& location =
368 std::source_location::current());
369} // namespace logging
370} // namespace vpd