1 #include "logger.hpp" 2 3 #include <regex> 4 #include <sstream> 5 6 namespace vpd 7 { 8 std::shared_ptr<Logger> Logger::m_loggerInstance; 9 10 void Logger::logMessage(std::string_view i_message, 11 const PlaceHolder& i_placeHolder, 12 const types::PelInfoTuple* i_pelTuple, 13 const std::source_location& i_location) 14 { 15 std::ostringstream l_log; 16 l_log << "FileName: " << i_location.file_name() << "," 17 << " Line: " << i_location.line() << " " << i_message; 18 19 if ((i_placeHolder == PlaceHolder::COLLECTION) && m_collectionLogger) 20 { 21 // Log it to a specific place. 22 m_collectionLogger->logMessage(l_log.str()); 23 } 24 else if (i_placeHolder == PlaceHolder::PEL) 25 { 26 if (i_pelTuple) 27 { 28 // LOG PEL 29 // This should call create PEL API from the event logger. 30 return; 31 } 32 std::cout << "Pel info tuple required to log PEL for message <" + 33 l_log.str() + ">" 34 << std::endl; 35 } 36 else if ((i_placeHolder == PlaceHolder::VPD_WRITE) && m_vpdWriteLogger) 37 { 38 m_vpdWriteLogger->logMessage(l_log.str()); 39 } 40 else 41 { 42 // Default case, let it go to journal. 43 std::cout << l_log.str() << std::endl; 44 } 45 } 46 47 void Logger::initiateVpdCollectionLogging() noexcept 48 { 49 try 50 { 51 // collection log file directory 52 const std::filesystem::path l_collectionLogDirectory{"/var/lib/vpd"}; 53 54 std::error_code l_ec; 55 if (!std::filesystem::exists(l_collectionLogDirectory, l_ec)) 56 { 57 if (l_ec) 58 { 59 throw std::runtime_error( 60 "File system call to exist failed with error = " + 61 l_ec.message()); 62 } 63 throw std::runtime_error( 64 "Directory " + l_collectionLogDirectory.string() + 65 " does not exist"); 66 } 67 68 // base name of collection log file 69 std::filesystem::path l_collectionLogFilePath{l_collectionLogDirectory}; 70 l_collectionLogFilePath /= "collection"; 71 72 unsigned l_collectionLogFileCount{0}; 73 74 std::filesystem::file_time_type l_oldestFileTime; 75 std::filesystem::path l_oldestFilePath{l_collectionLogFilePath}; 76 77 // iterate through all entries in the log directory 78 for (const auto& l_dirEntry : 79 std::filesystem::directory_iterator(l_collectionLogDirectory)) 80 { 81 // check /var/lib/vpd for number "collection.*" log file 82 const std::regex l_collectionLogFileRegex{"collection.*\\.log"}; 83 84 if (std::filesystem::is_regular_file(l_dirEntry.path()) && 85 std::regex_match(l_dirEntry.path().filename().string(), 86 l_collectionLogFileRegex)) 87 { 88 // check the write time of this file 89 const auto l_fileWriteTime = 90 std::filesystem::last_write_time(l_dirEntry.path()); 91 92 // update oldest file path if required 93 if (l_fileWriteTime < l_oldestFileTime) 94 { 95 l_oldestFileTime = l_fileWriteTime; 96 l_oldestFilePath = l_dirEntry.path(); 97 } 98 99 l_collectionLogFileCount++; 100 } 101 } 102 103 // maximum number of collection log files to maintain 104 constexpr auto l_maxCollectionLogFiles{3}; 105 106 if (l_collectionLogFileCount >= l_maxCollectionLogFiles) 107 { 108 // delete oldest collection log file 109 l_collectionLogFilePath = l_oldestFilePath; 110 111 logMessage("Deleting collection log file " + 112 l_collectionLogFilePath.string()); 113 114 std::error_code l_ec; 115 if (!std::filesystem::remove(l_collectionLogFilePath, l_ec)) 116 { 117 logMessage("Failed to delete existing collection log file " + 118 l_collectionLogFilePath.string() + 119 " Error: " + l_ec.message()); 120 } 121 } 122 else 123 { 124 l_collectionLogFilePath += 125 "_" + std::to_string(l_collectionLogFileCount) + ".log"; 126 } 127 128 // create collection logger object with collection_(n+1).log 129 m_collectionLogger.reset( 130 new AsyncFileLogger(l_collectionLogFilePath, 4096)); 131 } 132 catch (const std::exception& l_ex) 133 { 134 logMessage("Failed to initialize collection logger. Error: " + 135 std::string(l_ex.what())); 136 } 137 } 138 139 void SyncFileLogger::logMessage(const std::string_view& i_message) 140 { 141 try 142 { 143 if (m_currentNumEntries >= m_maxEntries) 144 { 145 rotateFile(); 146 } 147 148 std::string l_timeStampedMsg{ 149 timestamp() + " : " + std::string(i_message)}; 150 151 // check size of message and pad/trim as required 152 if (l_timeStampedMsg.length() > m_logEntrySize) 153 { 154 l_timeStampedMsg.resize(m_logEntrySize); 155 } 156 else if (l_timeStampedMsg.length() < m_logEntrySize) 157 { 158 constexpr char l_padChar{' '}; 159 l_timeStampedMsg.append(m_logEntrySize - l_timeStampedMsg.length(), 160 l_padChar); 161 } 162 163 // write the message to file 164 m_fileStream << l_timeStampedMsg << std::endl; 165 166 // increment number of entries only if write to file is successful 167 ++m_currentNumEntries; 168 } 169 catch (const std::exception& l_ex) 170 { 171 // log message to journal if we fail to log to file 172 auto l_logger = Logger::getLoggerInstance(); 173 l_logger->logMessage(i_message); 174 } 175 } 176 177 void AsyncFileLogger::logMessage(const std::string_view& i_message) 178 { 179 try 180 { 181 // acquire lock on queue 182 std::unique_lock<std::mutex> l_lock(m_mutex); 183 184 // push message to queue 185 m_messageQueue.emplace(timestamp() + " : " + std::string(i_message)); 186 187 // notify log worker thread 188 m_cv.notify_one(); 189 } 190 catch (const std::exception& l_ex) 191 { 192 // log message to journal if we fail to push message to queue 193 Logger::getLoggerInstance()->logMessage(i_message); 194 } 195 } 196 197 void AsyncFileLogger::fileWorker() noexcept 198 { 199 // create lock object on mutex 200 std::unique_lock<std::mutex> l_lock(m_mutex); 201 202 // infinite loop 203 while (true) 204 { 205 // check for exit conditions 206 if (!m_fileStream.is_open() || m_stopLogging) 207 { 208 break; 209 } 210 211 // wait for notification from log producer 212 m_cv.wait(l_lock, 213 [this] { return m_stopLogging || !m_messageQueue.empty(); }); 214 215 // flush the queue 216 while (!m_messageQueue.empty()) 217 { 218 // read the first message in queue 219 const auto l_logMessage = m_messageQueue.front(); 220 try 221 { 222 // pop the message from queue 223 m_messageQueue.pop(); 224 225 // unlock mutex on queue 226 l_lock.unlock(); 227 228 // flush the message to file 229 m_fileStream << l_logMessage << std::endl; 230 231 // lock mutex on queue 232 l_lock.lock(); 233 } 234 catch (const std::exception& l_ex) 235 { 236 // log message to journal if we fail to push message to queue 237 Logger::getLoggerInstance()->logMessage(l_logMessage); 238 239 // check if we need to reacquire lock before continuing to flush 240 // queue 241 if (!l_lock.owns_lock()) 242 { 243 l_lock.lock(); 244 } 245 } 246 } // queue flush loop 247 } // thread loop 248 } 249 250 ILogFileHandler::ILogFileHandler(const std::filesystem::path& i_filePath, 251 const size_t i_maxEntries) : 252 m_filePath{i_filePath}, m_maxEntries{i_maxEntries} 253 { 254 // check if log file already exists 255 std::error_code l_ec; 256 const bool l_logFileExists = std::filesystem::exists(m_filePath, l_ec); 257 if (l_ec) 258 { 259 Logger::getLoggerInstance()->logMessage( 260 "Failed to check if log file already exists. Error: " + 261 l_ec.message()); 262 } 263 264 // open the file in append mode 265 m_fileStream.open(m_filePath, std::ios::out | std::ios::ate); 266 // enable exception mask to throw on badbit and failbit 267 m_fileStream.exceptions(std::ios_base::badbit | std::ios_base::failbit); 268 269 if (l_logFileExists) 270 { 271 // log file already exists, check and update the number of entries 272 std::ifstream l_readFileStream{m_filePath}; 273 for (std::string l_line; std::getline(l_readFileStream, l_line); 274 ++m_currentNumEntries) 275 {} 276 277 l_readFileStream.close(); 278 } 279 } 280 281 namespace logging 282 { 283 void logMessage(std::string_view message, const std::source_location& location) 284 { 285 std::ostringstream log; 286 log << "FileName: " << location.file_name() << "," 287 << " Line: " << location.line() << " " << message; 288 289 std::cout << log.str() << std::endl; 290 } 291 } // namespace logging 292 } // namespace vpd 293