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