blob: 9d3283eca89594f2a078893497487c88bd690d8f [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,
Souvik Roybac8ba62025-10-27 09:07:06 +000074 const size_t i_maxEntries);
Sunny Srivastava5779d972025-08-08 01:45:23 -050075
Sunny Srivastava5779d972025-08-08 01:45:23 -050076 /**
Souvik Roya8c3c092025-09-11 10:49:29 +000077 * @brief API to generate timestamp in string format.
78 *
79 * @return Returns timestamp in string format on success, otherwise returns
80 * empty string in case of any error.
Sunny Srivastava5779d972025-08-08 01:45:23 -050081 */
Souvik Roya8c3c092025-09-11 10:49:29 +000082 static inline std::string timestamp() noexcept
83 {
Souvik Roy6bd74ad2025-09-12 06:24:06 +000084 try
85 {
86 const auto l_now = std::chrono::system_clock::now();
87 const auto l_in_time_t =
88 std::chrono::system_clock::to_time_t(l_now);
89 const auto l_ms =
90 std::chrono::duration_cast<std::chrono::milliseconds>(
91 l_now.time_since_epoch()) %
92 1000;
93
94 std::stringstream l_ss;
95 l_ss << std::put_time(std::localtime(&l_in_time_t),
96 "%Y-%m-%d %H:%M:%S")
97 << "." << std::setfill('0') << std::setw(3) << l_ms.count();
98 return l_ss.str();
99 }
100 catch (const std::exception& l_ex)
101 {
102 return std::string{};
103 }
Souvik Roya8c3c092025-09-11 10:49:29 +0000104 }
Sunny Srivastava5779d972025-08-08 01:45:23 -0500105
Souvik Roya8c3c092025-09-11 10:49:29 +0000106 public:
107 // deleted methods
108 ILogFileHandler() = delete;
109 ILogFileHandler(const ILogFileHandler&) = delete;
110 ILogFileHandler(const ILogFileHandler&&) = delete;
111 ILogFileHandler operator=(const ILogFileHandler&) = delete;
112 ILogFileHandler operator=(const ILogFileHandler&&) = delete;
113
114 /**
115 * @brief API to log a message to file.
116 *
117 * @param[in] i_message - Message to log.
118 *
119 * @throw std::runtime_error
120 */
121 virtual void logMessage(
122 [[maybe_unused]] const std::string_view& i_message) = 0;
123
124 // destructor
125 virtual ~ILogFileHandler()
126 {
Souvik Roy6bd74ad2025-09-12 06:24:06 +0000127 if (m_fileStream.is_open())
128 {
129 m_fileStream.close();
130 }
Souvik Roya8c3c092025-09-11 10:49:29 +0000131 }
Sunny Srivastava5779d972025-08-08 01:45:23 -0500132};
133
134/**
Souvik Roy6bd74ad2025-09-12 06:24:06 +0000135 * @brief A class to handle logging messages to file synchronously
136 *
137 * This class handles logging messages to a specific file in a synchronous
138 * manner.
139 * Note: The logMessage API of this class is not multi-thread safe.
140 */
141class SyncFileLogger final : public ILogFileHandler
142{
143 /**
144 * @brief Parameterized constructor.
145 * Private so that can't be initialized by class(es) other than friends.
146 *
147 * @param[in] i_filePath - Absolute path of the log file.
148 * @param[in] i_maxEntries - Maximum number of entries in the log file after
149 * which the file will be rotated.
150 */
151 SyncFileLogger(const std::filesystem::path& i_filePath,
152 const size_t i_maxEntries) :
153 ILogFileHandler(i_filePath, i_maxEntries)
154 {}
155
156 public:
157 // Friend class Logger.
158 friend class Logger;
159
160 // deleted methods
161 SyncFileLogger() = delete;
162 SyncFileLogger(const SyncFileLogger&) = delete;
163 SyncFileLogger(const SyncFileLogger&&) = delete;
164 SyncFileLogger operator=(const SyncFileLogger&) = delete;
165 SyncFileLogger operator=(const SyncFileLogger&&) = delete;
166
167 /**
168 * @brief API to log a message to file
169 *
170 * This API logs messages to file in a synchronous manner.
171 * Note: This API is not multi-thread safe.
172 *
173 * @param[in] i_message - Message to log
174 *
175 * @throw std::runtime_error
176 */
177 void logMessage(const std::string_view& i_message) override;
178
179 // destructor
180 ~SyncFileLogger() = default;
181};
182
183/**
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000184 * @brief A class to handle asynchronous logging of messages to file
185 *
186 * This class implements methods to log messages asynchronously to a desired
187 * file in the filesystem. It uses a queue for buffering the messages from
188 * caller. The actual file operations are handled by a worker thread.
189 */
190class AsyncFileLogger final : public ILogFileHandler
191{
Souvik Royf8587362025-09-25 09:56:20 +0000192 // queue for log messages
193 std::queue<std::string> m_messageQueue;
194
195 // mutex to control access to log message queue
196 std::mutex m_mutex;
197
198 // flag which indicates log worker thread if logging is finished
199 std::atomic_bool m_stopLogging{false};
200
201 // conditional variable to signal log worker thread
202 std::condition_variable m_cv;
203
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000204 /**
205 * @brief Constructor
206 * Private so that can't be initialized by class(es) other than friends.
207 *
208 * @param[in] i_fileName - Name of the log file
209 * @param[in] i_maxEntries - Maximum number of entries in the log file after
210 * which the file will be rotated
211 */
212 AsyncFileLogger(const std::filesystem::path& i_fileName,
213 const size_t i_maxEntries) :
214 ILogFileHandler(i_fileName, i_maxEntries)
215 {
216 // start worker thread in detached mode
217 std::thread{[this]() { this->fileWorker(); }}.detach();
218 }
219
220 /**
221 * @brief Logger worker thread body
222 */
223 void fileWorker() noexcept;
224
225 public:
226 // Friend class Logger.
227 friend class Logger;
228
229 // deleted methods
230 AsyncFileLogger() = delete;
231 AsyncFileLogger(const AsyncFileLogger&) = delete;
232 AsyncFileLogger(const AsyncFileLogger&&) = delete;
233 AsyncFileLogger operator=(const AsyncFileLogger&) = delete;
234 AsyncFileLogger operator=(const AsyncFileLogger&&) = delete;
235
236 /**
237 * @brief API to log a message to file
238 *
Souvik Royf8587362025-09-25 09:56:20 +0000239 * This API logs given message to a file. This API is multi-thread safe.
240 *
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000241 * @param[in] i_message - Message to log
242 *
243 * @throw std::runtime_error
244 */
245 void logMessage(const std::string_view& i_message) override;
246
247 // destructor
248 ~AsyncFileLogger()
249 {
Souvik Royf8587362025-09-25 09:56:20 +0000250 std::unique_lock<std::mutex> l_lock(m_mutex);
251
252 m_stopLogging = true;
253
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000254 if (m_fileStream.is_open())
255 {
256 m_fileStream.close();
257 }
Souvik Royf8587362025-09-25 09:56:20 +0000258
259 m_cv.notify_one();
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000260 }
261};
262
263/**
Sunny Srivastava5779d972025-08-08 01:45:23 -0500264 * @brief Singleton class to handle error logging for the repository.
265 */
266class Logger
267{
268 public:
269 /**
270 * @brief Deleted Methods
271 */
Souvik Roya5e18b82025-09-25 05:59:56 +0000272 Logger(const Logger&) = delete; // Copy constructor
273 Logger(const Logger&&) = delete; // Move constructor
274 Logger operator=(const Logger&) = delete; // Copy assignment operator
275 Logger operator=(const Logger&&) = delete; // Move assignment operator
Sunny Srivastava5779d972025-08-08 01:45:23 -0500276
277 /**
278 * @brief Method to get instance of Logger class.
279 */
280 static std::shared_ptr<Logger> getLoggerInstance()
281 {
282 if (!m_loggerInstance)
283 {
284 m_loggerInstance = std::shared_ptr<Logger>(new Logger());
285 }
286 return m_loggerInstance;
287 }
288
289 /**
290 * @brief API to log a given error message.
291 *
292 * @param[in] i_message - Message to be logged.
293 * @param[in] i_placeHolder - States where the message needs to be logged.
294 * Default is journal.
295 * @param[in] i_pelTuple - A structure only required in case message needs
296 * to be logged as PEL.
297 * @param[in] i_location - Locatuon from where message needs to be logged.
298 */
299 void logMessage(std::string_view i_message,
300 const PlaceHolder& i_placeHolder = PlaceHolder::DEFAULT,
301 const types::PelInfoTuple* i_pelTuple = nullptr,
302 const std::source_location& i_location =
303 std::source_location::current());
304
Souvik Roya5e18b82025-09-25 05:59:56 +0000305 /**
306 * @brief API to initiate VPD collection logging.
307 *
308 * This API initiates VPD collection logging. It checks for existing
309 * collection log files and if 3 such files are found, it deletes the oldest
310 * file and initiates a VPD collection logger object, so that every new VPD
311 * collection flow always gets logged into a new file.
312 */
313 void initiateVpdCollectionLogging() noexcept;
314
315 /**
316 * @brief API to terminate VPD collection logging.
317 *
318 * This API terminates the VPD collection logging by destroying the
319 * associated VPD collection logger object.
320 */
321 void terminateVpdCollectionLogging() noexcept
322 {
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000323 m_collectionLogger.reset();
Souvik Roya5e18b82025-09-25 05:59:56 +0000324 }
325
Sunny Srivastava5779d972025-08-08 01:45:23 -0500326 private:
327 /**
328 * @brief Constructor
329 */
Souvik Roya5e18b82025-09-25 05:59:56 +0000330 Logger() : m_vpdWriteLogger(nullptr), m_collectionLogger(nullptr)
Sunny Srivastava5779d972025-08-08 01:45:23 -0500331 {
Souvik Roy6bd74ad2025-09-12 06:24:06 +0000332 m_vpdWriteLogger.reset(
333 new SyncFileLogger("/var/lib/vpd/vpdWrite.log", 128));
Sunny Srivastava5779d972025-08-08 01:45:23 -0500334 }
335
336 // Instance to the logger class.
337 static std::shared_ptr<Logger> m_loggerInstance;
338
Souvik Roya5e18b82025-09-25 05:59:56 +0000339 // logger object to handle VPD write logs
340 std::unique_ptr<ILogFileHandler> m_vpdWriteLogger;
341
342 // logger object to handle VPD collection logs
343 std::unique_ptr<ILogFileHandler> m_collectionLogger;
Sunny Srivastava5779d972025-08-08 01:45:23 -0500344};
345
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500346/**
347 * @brief The namespace defines logging related methods for VPD.
Sunny Srivastava5779d972025-08-08 01:45:23 -0500348 * Only for backward compatibility till new logger class comes up.
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500349 */
350namespace logging
351{
352
353/**
354 * @brief An api to log message.
355 * This API should be called to log message. It will auto append information
356 * like file name, line and function name to the message being logged.
357 *
358 * @param[in] message - Information that we want to log.
359 * @param[in] location - Object of source_location class.
360 */
361void logMessage(std::string_view message, const std::source_location& location =
362 std::source_location::current());
363} // namespace logging
364} // namespace vpd