1 #pragma once 2 3 #include "types.hpp" 4 5 #include <filesystem> 6 #include <fstream> 7 #include <iostream> 8 #include <memory> 9 #include <mutex> 10 #include <queue> 11 #include <source_location> 12 #include <string_view> 13 14 namespace vpd 15 { 16 17 /** 18 * @brief Enum class defining placeholder tags. 19 * 20 * The tag will be used by APIs to identify the endpoint for a given log 21 * message. 22 */ 23 enum class PlaceHolder 24 { 25 DEFAULT, /* logs to the journal */ 26 PEL, /* Creates a PEL */ 27 COLLECTION, /* Logs collection messages */ 28 VPD_WRITE /* Logs VPD write details */ 29 }; 30 31 /** 32 * @brief Class to handle file operations w.r.t logging. 33 * Based on the placeholder the class will handle different file operations to 34 * log error messages. 35 */ 36 class ILogFileHandler 37 { 38 protected: 39 // absolute file path of log file 40 std::filesystem::path m_filePath{}; 41 42 // max number of log entries in file 43 size_t m_maxEntries{256}; 44 45 // file stream object to do file operations 46 std::fstream m_fileStream; 47 48 // current number of log entries in file 49 size_t m_currentNumEntries{0}; 50 51 /** 52 * @brief API to rotate file. 53 * 54 * This API rotates the logs within a file by deleting specified number of 55 * oldest entries. 56 * 57 * @param[in] i_numEntriesToDelete - Number of entries to delete. 58 * 59 * @throw std::runtime_error 60 */ 61 virtual void rotateFile( 62 [[maybe_unused]] const unsigned i_numEntriesToDelete = 5); 63 64 /** 65 * @brief Constructor. 66 * Private so that can't be initialized by class(es) other than friends. 67 * 68 * @param[in] i_filePath - Absolute path of the log file. 69 * @param[in] i_maxEntries - Maximum number of entries in the log file after 70 * which the file will be rotated. 71 */ 72 ILogFileHandler(const std::filesystem::path& i_filePath, 73 const size_t i_maxEntries) : 74 m_filePath{i_filePath}, m_maxEntries{i_maxEntries} 75 { 76 // open the file in append mode 77 m_fileStream.open(m_filePath, std::ios::out | std::ios::app); 78 79 // enable exception mask to throw on badbit and failbit 80 m_fileStream.exceptions(std::ios_base::badbit | std::ios_base::failbit); 81 } 82 83 /** 84 * @brief API to generate timestamp in string format. 85 * 86 * @return Returns timestamp in string format on success, otherwise returns 87 * empty string in case of any error. 88 */ 89 static inline std::string timestamp() noexcept 90 { 91 try 92 { 93 const auto l_now = std::chrono::system_clock::now(); 94 const auto l_in_time_t = 95 std::chrono::system_clock::to_time_t(l_now); 96 const auto l_ms = 97 std::chrono::duration_cast<std::chrono::milliseconds>( 98 l_now.time_since_epoch()) % 99 1000; 100 101 std::stringstream l_ss; 102 l_ss << std::put_time(std::localtime(&l_in_time_t), 103 "%Y-%m-%d %H:%M:%S") 104 << "." << std::setfill('0') << std::setw(3) << l_ms.count(); 105 return l_ss.str(); 106 } 107 catch (const std::exception& l_ex) 108 { 109 return std::string{}; 110 } 111 } 112 113 public: 114 // deleted methods 115 ILogFileHandler() = delete; 116 ILogFileHandler(const ILogFileHandler&) = delete; 117 ILogFileHandler(const ILogFileHandler&&) = delete; 118 ILogFileHandler operator=(const ILogFileHandler&) = delete; 119 ILogFileHandler operator=(const ILogFileHandler&&) = delete; 120 121 /** 122 * @brief API to log a message to file. 123 * 124 * @param[in] i_message - Message to log. 125 * 126 * @throw std::runtime_error 127 */ 128 virtual void logMessage( 129 [[maybe_unused]] const std::string_view& i_message) = 0; 130 131 // destructor 132 virtual ~ILogFileHandler() 133 { 134 if (m_fileStream.is_open()) 135 { 136 m_fileStream.close(); 137 } 138 } 139 }; 140 141 /** 142 * @brief A class to handle logging messages to file synchronously 143 * 144 * This class handles logging messages to a specific file in a synchronous 145 * manner. 146 * Note: The logMessage API of this class is not multi-thread safe. 147 */ 148 class SyncFileLogger final : public ILogFileHandler 149 { 150 /** 151 * @brief Parameterized constructor. 152 * Private so that can't be initialized by class(es) other than friends. 153 * 154 * @param[in] i_filePath - Absolute path of the log file. 155 * @param[in] i_maxEntries - Maximum number of entries in the log file after 156 * which the file will be rotated. 157 */ 158 SyncFileLogger(const std::filesystem::path& i_filePath, 159 const size_t i_maxEntries) : 160 ILogFileHandler(i_filePath, i_maxEntries) 161 {} 162 163 public: 164 // Friend class Logger. 165 friend class Logger; 166 167 // deleted methods 168 SyncFileLogger() = delete; 169 SyncFileLogger(const SyncFileLogger&) = delete; 170 SyncFileLogger(const SyncFileLogger&&) = delete; 171 SyncFileLogger operator=(const SyncFileLogger&) = delete; 172 SyncFileLogger operator=(const SyncFileLogger&&) = delete; 173 174 /** 175 * @brief API to log a message to file 176 * 177 * This API logs messages to file in a synchronous manner. 178 * Note: This API is not multi-thread safe. 179 * 180 * @param[in] i_message - Message to log 181 * 182 * @throw std::runtime_error 183 */ 184 void logMessage(const std::string_view& i_message) override; 185 186 // destructor 187 ~SyncFileLogger() = default; 188 }; 189 190 /** 191 * @brief A class to handle asynchronous logging of messages to file 192 * 193 * This class implements methods to log messages asynchronously to a desired 194 * file in the filesystem. It uses a queue for buffering the messages from 195 * caller. The actual file operations are handled by a worker thread. 196 */ 197 class AsyncFileLogger final : public ILogFileHandler 198 { 199 /** 200 * @brief Constructor 201 * Private so that can't be initialized by class(es) other than friends. 202 * 203 * @param[in] i_fileName - Name of the log file 204 * @param[in] i_maxEntries - Maximum number of entries in the log file after 205 * which the file will be rotated 206 */ 207 AsyncFileLogger(const std::filesystem::path& i_fileName, 208 const size_t i_maxEntries) : 209 ILogFileHandler(i_fileName, i_maxEntries) 210 { 211 // start worker thread in detached mode 212 std::thread{[this]() { this->fileWorker(); }}.detach(); 213 } 214 215 /** 216 * @brief Logger worker thread body 217 */ 218 void fileWorker() noexcept; 219 220 public: 221 // Friend class Logger. 222 friend class Logger; 223 224 // deleted methods 225 AsyncFileLogger() = delete; 226 AsyncFileLogger(const AsyncFileLogger&) = delete; 227 AsyncFileLogger(const AsyncFileLogger&&) = delete; 228 AsyncFileLogger operator=(const AsyncFileLogger&) = delete; 229 AsyncFileLogger operator=(const AsyncFileLogger&&) = delete; 230 231 /** 232 * @brief API to log a message to file 233 * 234 * @param[in] i_message - Message to log 235 * 236 * @throw std::runtime_error 237 */ 238 void logMessage(const std::string_view& i_message) override; 239 240 // destructor 241 ~AsyncFileLogger() 242 { 243 /* TODO 244 - acquire lock 245 - set log stop flag to true 246 - notify log worker thread 247 */ 248 if (m_fileStream.is_open()) 249 { 250 m_fileStream.close(); 251 } 252 } 253 }; 254 255 /** 256 * @brief Singleton class to handle error logging for the repository. 257 */ 258 class Logger 259 { 260 public: 261 /** 262 * @brief Deleted Methods 263 */ 264 Logger(const Logger&) = delete; // Copy constructor 265 Logger(const Logger&&) = delete; // Move constructor 266 Logger operator=(const Logger&) = delete; // Copy assignment operator 267 Logger operator=(const Logger&&) = delete; // Move assignment operator 268 269 /** 270 * @brief Method to get instance of Logger class. 271 */ 272 static std::shared_ptr<Logger> getLoggerInstance() 273 { 274 if (!m_loggerInstance) 275 { 276 m_loggerInstance = std::shared_ptr<Logger>(new Logger()); 277 } 278 return m_loggerInstance; 279 } 280 281 /** 282 * @brief API to log a given error message. 283 * 284 * @param[in] i_message - Message to be logged. 285 * @param[in] i_placeHolder - States where the message needs to be logged. 286 * Default is journal. 287 * @param[in] i_pelTuple - A structure only required in case message needs 288 * to be logged as PEL. 289 * @param[in] i_location - Locatuon from where message needs to be logged. 290 */ 291 void logMessage(std::string_view i_message, 292 const PlaceHolder& i_placeHolder = PlaceHolder::DEFAULT, 293 const types::PelInfoTuple* i_pelTuple = nullptr, 294 const std::source_location& i_location = 295 std::source_location::current()); 296 297 /** 298 * @brief API to initiate VPD collection logging. 299 * 300 * This API initiates VPD collection logging. It checks for existing 301 * collection log files and if 3 such files are found, it deletes the oldest 302 * file and initiates a VPD collection logger object, so that every new VPD 303 * collection flow always gets logged into a new file. 304 */ 305 void initiateVpdCollectionLogging() noexcept; 306 307 /** 308 * @brief API to terminate VPD collection logging. 309 * 310 * This API terminates the VPD collection logging by destroying the 311 * associated VPD collection logger object. 312 */ 313 void terminateVpdCollectionLogging() noexcept 314 { 315 m_collectionLogger.reset(); 316 } 317 318 private: 319 /** 320 * @brief Constructor 321 */ 322 Logger() : m_vpdWriteLogger(nullptr), m_collectionLogger(nullptr) 323 { 324 m_vpdWriteLogger.reset( 325 new SyncFileLogger("/var/lib/vpd/vpdWrite.log", 128)); 326 } 327 328 // Instance to the logger class. 329 static std::shared_ptr<Logger> m_loggerInstance; 330 331 // logger object to handle VPD write logs 332 std::unique_ptr<ILogFileHandler> m_vpdWriteLogger; 333 334 // logger object to handle VPD collection logs 335 std::unique_ptr<ILogFileHandler> m_collectionLogger; 336 }; 337 338 /** 339 * @brief The namespace defines logging related methods for VPD. 340 * Only for backward compatibility till new logger class comes up. 341 */ 342 namespace logging 343 { 344 345 /** 346 * @brief An api to log message. 347 * This API should be called to log message. It will auto append information 348 * like file name, line and function name to the message being logged. 349 * 350 * @param[in] message - Information that we want to log. 351 * @param[in] location - Object of source_location class. 352 */ 353 void logMessage(std::string_view message, const std::source_location& location = 354 std::source_location::current()); 355 } // namespace logging 356 } // namespace vpd 357