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 /** 53 * @brief API to rotate file. 54 * 55 * This API rotates the logs within a file by deleting specified number of 56 * oldest entries. 57 * 58 * @param[in] i_numEntriesToDelete - Number of entries to delete. 59 * 60 * @throw std::runtime_error 61 */ 62 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 */ ILogFileHandler(const std::filesystem::path & i_filePath,const size_t i_maxEntries)73 ILogFileHandler(const std::filesystem::path& i_filePath, 74 const size_t i_maxEntries) : 75 m_filePath{i_filePath}, m_maxEntries{i_maxEntries} 76 { 77 // 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); 82 } 83 84 /** 85 * @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. 89 */ timestamp()90 static inline std::string timestamp() noexcept 91 { 92 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 } 112 } 113 114 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 ~ILogFileHandler()133 virtual ~ILogFileHandler() 134 { 135 if (m_fileStream.is_open()) 136 { 137 m_fileStream.close(); 138 } 139 } 140 }; 141 142 /** 143 * @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 */ 149 class 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 */ SyncFileLogger(const std::filesystem::path & i_filePath,const size_t i_maxEntries)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 /** 192 * @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 */ 198 class AsyncFileLogger final : public ILogFileHandler 199 { 200 // 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 212 /** 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 */ AsyncFileLogger(const std::filesystem::path & i_fileName,const size_t i_maxEntries)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 * 247 * This API logs given message to a file. This API is multi-thread safe. 248 * 249 * @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 ~AsyncFileLogger()256 ~AsyncFileLogger() 257 { 258 std::unique_lock<std::mutex> l_lock(m_mutex); 259 260 m_stopLogging = true; 261 262 if (m_fileStream.is_open()) 263 { 264 m_fileStream.close(); 265 } 266 267 m_cv.notify_one(); 268 } 269 }; 270 271 /** 272 * @brief Singleton class to handle error logging for the repository. 273 */ 274 class Logger 275 { 276 public: 277 /** 278 * @brief Deleted Methods 279 */ 280 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 284 285 /** 286 * @brief Method to get instance of Logger class. 287 */ getLoggerInstance()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 313 /** 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 */ terminateVpdCollectionLogging()329 void terminateVpdCollectionLogging() noexcept 330 { 331 m_collectionLogger.reset(); 332 } 333 334 private: 335 /** 336 * @brief Constructor 337 */ Logger()338 Logger() : m_vpdWriteLogger(nullptr), m_collectionLogger(nullptr) 339 { 340 m_vpdWriteLogger.reset( 341 new SyncFileLogger("/var/lib/vpd/vpdWrite.log", 128)); 342 } 343 344 // Instance to the logger class. 345 static std::shared_ptr<Logger> m_loggerInstance; 346 347 // 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; 352 }; 353 354 /** 355 * @brief The namespace defines logging related methods for VPD. 356 * Only for backward compatibility till new logger class comes up. 357 */ 358 namespace 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 */ 369 void logMessage(std::string_view message, const std::source_location& location = 370 std::source_location::current()); 371 } // namespace logging 372 } // namespace vpd 373