1 #pragma once 2 3 #include "types.hpp" 4 5 #include <condition_variable> 6 #include <filesystem> 7 #include <fstream> 8 #include <iostream> 9 #include <memory> 10 #include <mutex> 11 #include <queue> 12 #include <source_location> 13 #include <string_view> 14 15 namespace vpd 16 { 17 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 */ 24 enum class PlaceHolder 25 { 26 DEFAULT, /* logs to the journal */ 27 PEL, /* Creates a PEL */ 28 COLLECTION, /* Logs collection messages */ 29 VPD_WRITE /* Logs VPD write details */ 30 }; 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 */ 37 class ILogFileHandler 38 { 39 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 46 // 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 52 // number of chars in a single log entry 53 size_t m_logEntrySize{512}; 54 55 /** 56 * @brief API to rotate file. 57 * 58 * 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. 61 * 62 * @throw std::ios_base::failure 63 */ rotateFile()64 virtual inline void rotateFile() 65 { 66 // reset file pointer to beginning of file 67 m_fileStream.seekp(0); 68 m_currentNumEntries = 0; 69 } 70 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, 80 const size_t i_maxEntries); 81 82 /** 83 * @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. 87 */ timestamp()88 static inline std::string timestamp() noexcept 89 { 90 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 } 110 } 111 112 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 ~ILogFileHandler()131 virtual ~ILogFileHandler() 132 { 133 if (m_fileStream.is_open()) 134 { 135 m_fileStream.close(); 136 } 137 } 138 }; 139 140 /** 141 * @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 */ 147 class 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 */ SyncFileLogger(const std::filesystem::path & i_filePath,const size_t i_maxEntries)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 /** 190 * @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 */ 196 class AsyncFileLogger final : public ILogFileHandler 197 { 198 // 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 210 /** 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 */ AsyncFileLogger(const std::filesystem::path & i_fileName,const size_t i_maxEntries)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 * 245 * This API logs given message to a file. This API is multi-thread safe. 246 * 247 * @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 ~AsyncFileLogger()254 ~AsyncFileLogger() 255 { 256 std::unique_lock<std::mutex> l_lock(m_mutex); 257 258 m_stopLogging = true; 259 260 if (m_fileStream.is_open()) 261 { 262 m_fileStream.close(); 263 } 264 265 m_cv.notify_one(); 266 } 267 }; 268 269 /** 270 * @brief Singleton class to handle error logging for the repository. 271 */ 272 class Logger 273 { 274 public: 275 /** 276 * @brief Deleted Methods 277 */ 278 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 282 283 /** 284 * @brief Method to get instance of Logger class. 285 */ getLoggerInstance()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()) noexcept; 310 311 #ifdef ENABLE_FILE_LOGGING 312 /** 313 * @brief API to terminate VPD collection logging. 314 * 315 * This API terminates the VPD collection logging by destroying the 316 * associated VPD collection logger object. 317 */ terminateVpdCollectionLogging()318 void terminateVpdCollectionLogging() noexcept 319 { 320 m_collectionLogger.reset(); 321 } 322 #endif 323 324 private: 325 /** 326 * @brief Constructor 327 */ Logger()328 Logger() : m_vpdWriteLogger(nullptr) 329 { 330 #ifdef ENABLE_FILE_LOGGING 331 m_collectionLogger = nullptr; 332 #endif 333 } 334 335 #ifdef ENABLE_FILE_LOGGING 336 /** 337 * @brief API to initiate VPD collection logging. 338 * 339 * This API initiates VPD collection logging. It checks for existing 340 * collection log files and if 3 such files are found, it deletes the oldest 341 * file and initiates a VPD collection logger object, so that every new VPD 342 * collection flow always gets logged into a new file. 343 */ 344 void initiateVpdCollectionLogging() noexcept; 345 346 // logger object to handle VPD collection logs 347 std::unique_ptr<ILogFileHandler> m_collectionLogger; 348 349 #endif 350 351 // Instance to the logger class. 352 static std::shared_ptr<Logger> m_loggerInstance; 353 354 // logger object to handle VPD write logs 355 std::unique_ptr<ILogFileHandler> m_vpdWriteLogger; 356 }; 357 358 /** 359 * @brief The namespace defines logging related methods for VPD. 360 * Only for backward compatibility till new logger class comes up. 361 */ 362 namespace logging 363 { 364 365 /** 366 * @brief An api to log message. 367 * This API should be called to log message. It will auto append information 368 * like file name, line and function name to the message being logged. 369 * 370 * @param[in] message - Information that we want to log. 371 * @param[in] location - Object of source_location class. 372 */ 373 void logMessage(std::string_view message, const std::source_location& location = 374 std::source_location::current()); 375 } // namespace logging 376 } // namespace vpd 377