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 m_fileStream << timestamp() << " : " << i_message << std::endl; 148 } 149 catch (const std::exception& l_ex) 150 { 151 // log message to journal if we fail to log to file 152 auto l_logger = Logger::getLoggerInstance(); 153 l_logger->logMessage(i_message); 154 } 155 } 156 157 void AsyncFileLogger::logMessage(const std::string_view& i_message) 158 { 159 try 160 { 161 // acquire lock on queue 162 std::unique_lock<std::mutex> l_lock(m_mutex); 163 164 // push message to queue 165 m_messageQueue.emplace(timestamp() + " : " + std::string(i_message)); 166 167 // notify log worker thread 168 m_cv.notify_one(); 169 } 170 catch (const std::exception& l_ex) 171 { 172 // log message to journal if we fail to push message to queue 173 Logger::getLoggerInstance()->logMessage(i_message); 174 } 175 } 176 177 void AsyncFileLogger::fileWorker() noexcept 178 { 179 // create lock object on mutex 180 std::unique_lock<std::mutex> l_lock(m_mutex); 181 182 // infinite loop 183 while (true) 184 { 185 // check for exit conditions 186 if (!m_fileStream.is_open() || m_stopLogging) 187 { 188 break; 189 } 190 191 // wait for notification from log producer 192 m_cv.wait(l_lock, 193 [this] { return m_stopLogging || !m_messageQueue.empty(); }); 194 195 // flush the queue 196 while (!m_messageQueue.empty()) 197 { 198 // read the first message in queue 199 const auto l_logMessage = m_messageQueue.front(); 200 try 201 { 202 // pop the message from queue 203 m_messageQueue.pop(); 204 205 // unlock mutex on queue 206 l_lock.unlock(); 207 208 if (++m_currentNumEntries > m_maxEntries) 209 { 210 rotateFile(); 211 } 212 213 // flush the message to file 214 m_fileStream << l_logMessage << std::endl; 215 216 // lock mutex on queue 217 l_lock.lock(); 218 } 219 catch (const std::exception& l_ex) 220 { 221 // log message to journal if we fail to push message to queue 222 Logger::getLoggerInstance()->logMessage(l_logMessage); 223 224 // check if we need to reacquire lock before continuing to flush 225 // queue 226 if (!l_lock.owns_lock()) 227 { 228 l_lock.lock(); 229 } 230 } 231 } // queue flush loop 232 } // thread loop 233 } 234 235 void ILogFileHandler::rotateFile( 236 [[maybe_unused]] const unsigned i_numEntriesToDelete) 237 { 238 /* TODO: 239 - delete specified number of oldest entries from beginning of file 240 - rewrite file to move existing logs to beginning of file 241 */ 242 m_currentNumEntries = m_maxEntries - i_numEntriesToDelete; 243 } 244 namespace logging 245 { 246 void logMessage(std::string_view message, const std::source_location& location) 247 { 248 std::ostringstream log; 249 log << "FileName: " << location.file_name() << "," 250 << " Line: " << location.line() << " " << message; 251 252 std::cout << log.str() << std::endl; 253 } 254 } // namespace logging 255 } // namespace vpd 256