blob: cbf385a11a8a1681cc045e188c4b5203462b4dbb [file] [log] [blame]
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05001#include "logger.hpp"
2
Souvik Roy8042bbf2025-10-06 09:16:50 +00003#include <regex>
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -05004#include <sstream>
5
6namespace vpd
7{
Sunny Srivastava5779d972025-08-08 01:45:23 -05008std::shared_ptr<Logger> Logger::m_loggerInstance;
9
10void 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
Alpana Kumari138489f2025-11-10 08:59:20 -060019 if (i_placeHolder == PlaceHolder::COLLECTION)
Sunny Srivastava5779d972025-08-08 01:45:23 -050020 {
Alpana Kumari37ce6e32025-10-23 06:37:59 -050021#ifdef ENABLE_FILE_LOGGING
Alpana Kumari138489f2025-11-10 08:59:20 -060022 if (m_collectionLogger.get() == nullptr)
23 {
24 initiateVpdCollectionLogging();
25
26 if (m_collectionLogger.get() != nullptr)
27 {
28 // Log it to a specific place.
29 m_collectionLogger->logMessage(l_log.str());
30 }
31 else
32 {
33 std::cout << l_log.str() << std::endl;
34 }
35 }
36 else
37 {
38 m_collectionLogger->logMessage(l_log.str());
39 }
Alpana Kumari37ce6e32025-10-23 06:37:59 -050040#else
41 std::cout << l_log.str() << std::endl;
42#endif
Sunny Srivastava5779d972025-08-08 01:45:23 -050043 }
44 else if (i_placeHolder == PlaceHolder::PEL)
45 {
46 if (i_pelTuple)
47 {
48 // LOG PEL
49 // This should call create PEL API from the event logger.
50 return;
51 }
52 std::cout << "Pel info tuple required to log PEL for message <" +
53 l_log.str() + ">"
54 << std::endl;
55 }
Souvik Royebca7b12025-11-10 06:27:49 +000056 else if (i_placeHolder == PlaceHolder::VPD_WRITE)
Souvik Roya5e18b82025-09-25 05:59:56 +000057 {
Souvik Royebca7b12025-11-10 06:27:49 +000058 if (!m_vpdWriteLogger)
59 {
60 m_vpdWriteLogger.reset(
61 new SyncFileLogger("/var/lib/vpd/vpdWrite.log", 128));
62 }
Souvik Roya8c3c092025-09-11 10:49:29 +000063 m_vpdWriteLogger->logMessage(l_log.str());
Souvik Roya5e18b82025-09-25 05:59:56 +000064 }
Sunny Srivastava5779d972025-08-08 01:45:23 -050065 else
66 {
67 // Default case, let it go to journal.
68 std::cout << l_log.str() << std::endl;
69 }
70}
71
Alpana Kumari138489f2025-11-10 08:59:20 -060072#ifdef ENABLE_FILE_LOGGING
Souvik Roya5e18b82025-09-25 05:59:56 +000073void Logger::initiateVpdCollectionLogging() noexcept
74{
75 try
76 {
Souvik Roy8042bbf2025-10-06 09:16:50 +000077 // collection log file directory
78 const std::filesystem::path l_collectionLogDirectory{"/var/lib/vpd"};
79
80 std::error_code l_ec;
81 if (!std::filesystem::exists(l_collectionLogDirectory, l_ec))
82 {
83 if (l_ec)
84 {
85 throw std::runtime_error(
86 "File system call to exist failed with error = " +
87 l_ec.message());
88 }
89 throw std::runtime_error(
90 "Directory " + l_collectionLogDirectory.string() +
91 " does not exist");
92 }
93
94 // base name of collection log file
95 std::filesystem::path l_collectionLogFilePath{l_collectionLogDirectory};
96 l_collectionLogFilePath /= "collection";
97
98 unsigned l_collectionLogFileCount{0};
99
100 std::filesystem::file_time_type l_oldestFileTime;
101 std::filesystem::path l_oldestFilePath{l_collectionLogFilePath};
102
103 // iterate through all entries in the log directory
104 for (const auto& l_dirEntry :
105 std::filesystem::directory_iterator(l_collectionLogDirectory))
106 {
107 // check /var/lib/vpd for number "collection.*" log file
108 const std::regex l_collectionLogFileRegex{"collection.*\\.log"};
109
110 if (std::filesystem::is_regular_file(l_dirEntry.path()) &&
111 std::regex_match(l_dirEntry.path().filename().string(),
112 l_collectionLogFileRegex))
113 {
114 // check the write time of this file
115 const auto l_fileWriteTime =
116 std::filesystem::last_write_time(l_dirEntry.path());
117
118 // update oldest file path if required
119 if (l_fileWriteTime < l_oldestFileTime)
120 {
121 l_oldestFileTime = l_fileWriteTime;
122 l_oldestFilePath = l_dirEntry.path();
123 }
124
125 l_collectionLogFileCount++;
126 }
127 }
128
129 // maximum number of collection log files to maintain
130 constexpr auto l_maxCollectionLogFiles{3};
131
132 if (l_collectionLogFileCount >= l_maxCollectionLogFiles)
133 {
134 // delete oldest collection log file
135 l_collectionLogFilePath = l_oldestFilePath;
136
137 logMessage("Deleting collection log file " +
138 l_collectionLogFilePath.string());
139
140 std::error_code l_ec;
141 if (!std::filesystem::remove(l_collectionLogFilePath, l_ec))
142 {
143 logMessage("Failed to delete existing collection log file " +
144 l_collectionLogFilePath.string() +
145 " Error: " + l_ec.message());
146 }
147 }
148 else
149 {
150 l_collectionLogFilePath +=
151 "_" + std::to_string(l_collectionLogFileCount) + ".log";
152 }
153
154 // create collection logger object with collection_(n+1).log
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000155 m_collectionLogger.reset(
Souvik Roy8042bbf2025-10-06 09:16:50 +0000156 new AsyncFileLogger(l_collectionLogFilePath, 4096));
Souvik Roya5e18b82025-09-25 05:59:56 +0000157 }
158 catch (const std::exception& l_ex)
159 {
160 logMessage("Failed to initialize collection logger. Error: " +
161 std::string(l_ex.what()));
162 }
163}
Alpana Kumari138489f2025-11-10 08:59:20 -0600164#endif
Souvik Roya5e18b82025-09-25 05:59:56 +0000165
Souvik Roy6bd74ad2025-09-12 06:24:06 +0000166void SyncFileLogger::logMessage(const std::string_view& i_message)
167{
168 try
169 {
Souvik Royd834b122025-10-28 06:37:51 +0000170 if (m_currentNumEntries >= m_maxEntries)
Souvik Roy6bd74ad2025-09-12 06:24:06 +0000171 {
172 rotateFile();
173 }
Souvik Royd834b122025-10-28 06:37:51 +0000174
175 std::string l_timeStampedMsg{
176 timestamp() + " : " + std::string(i_message)};
177
178 // check size of message and pad/trim as required
179 if (l_timeStampedMsg.length() > m_logEntrySize)
180 {
181 l_timeStampedMsg.resize(m_logEntrySize);
182 }
183 else if (l_timeStampedMsg.length() < m_logEntrySize)
184 {
185 constexpr char l_padChar{' '};
186 l_timeStampedMsg.append(m_logEntrySize - l_timeStampedMsg.length(),
187 l_padChar);
188 }
189
190 // write the message to file
191 m_fileStream << l_timeStampedMsg << std::endl;
192
193 // increment number of entries only if write to file is successful
194 ++m_currentNumEntries;
Souvik Roy6bd74ad2025-09-12 06:24:06 +0000195 }
196 catch (const std::exception& l_ex)
197 {
198 // log message to journal if we fail to log to file
199 auto l_logger = Logger::getLoggerInstance();
200 l_logger->logMessage(i_message);
201 }
202}
203
Souvik Royf8587362025-09-25 09:56:20 +0000204void AsyncFileLogger::logMessage(const std::string_view& i_message)
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000205{
206 try
207 {
Souvik Royf8587362025-09-25 09:56:20 +0000208 // acquire lock on queue
209 std::unique_lock<std::mutex> l_lock(m_mutex);
210
211 // push message to queue
212 m_messageQueue.emplace(timestamp() + " : " + std::string(i_message));
213
214 // notify log worker thread
215 m_cv.notify_one();
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000216 }
217 catch (const std::exception& l_ex)
218 {
Souvik Royf8587362025-09-25 09:56:20 +0000219 // log message to journal if we fail to push message to queue
220 Logger::getLoggerInstance()->logMessage(i_message);
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000221 }
222}
223
224void AsyncFileLogger::fileWorker() noexcept
225{
Souvik Royf8587362025-09-25 09:56:20 +0000226 // create lock object on mutex
227 std::unique_lock<std::mutex> l_lock(m_mutex);
228
229 // infinite loop
230 while (true)
231 {
232 // check for exit conditions
233 if (!m_fileStream.is_open() || m_stopLogging)
234 {
235 break;
236 }
237
238 // wait for notification from log producer
239 m_cv.wait(l_lock,
240 [this] { return m_stopLogging || !m_messageQueue.empty(); });
241
242 // flush the queue
243 while (!m_messageQueue.empty())
244 {
245 // read the first message in queue
246 const auto l_logMessage = m_messageQueue.front();
247 try
248 {
249 // pop the message from queue
250 m_messageQueue.pop();
251
252 // unlock mutex on queue
253 l_lock.unlock();
254
Souvik Royf8587362025-09-25 09:56:20 +0000255 // flush the message to file
256 m_fileStream << l_logMessage << std::endl;
257
258 // lock mutex on queue
259 l_lock.lock();
260 }
261 catch (const std::exception& l_ex)
262 {
263 // log message to journal if we fail to push message to queue
264 Logger::getLoggerInstance()->logMessage(l_logMessage);
265
266 // check if we need to reacquire lock before continuing to flush
267 // queue
268 if (!l_lock.owns_lock())
269 {
270 l_lock.lock();
271 }
272 }
273 } // queue flush loop
274 } // thread loop
Souvik Roy0fb5c3b2025-09-15 08:18:25 +0000275}
276
Souvik Roybac8ba62025-10-27 09:07:06 +0000277ILogFileHandler::ILogFileHandler(const std::filesystem::path& i_filePath,
278 const size_t i_maxEntries) :
279 m_filePath{i_filePath}, m_maxEntries{i_maxEntries}
280{
281 // check if log file already exists
282 std::error_code l_ec;
283 const bool l_logFileExists = std::filesystem::exists(m_filePath, l_ec);
284 if (l_ec)
285 {
286 Logger::getLoggerInstance()->logMessage(
287 "Failed to check if log file already exists. Error: " +
288 l_ec.message());
289 }
290
Souvik Royb89c2c72025-11-12 06:07:49 +0000291 if (!l_logFileExists)
292 {
293 l_ec.clear();
294
295 // check if the parent directory of the file exists
296 if (!std::filesystem::exists(m_filePath.parent_path(), l_ec))
297 {
298 if (l_ec)
299 {
300 Logger::getLoggerInstance()->logMessage(
301 "Failed to check if log file parent directory [" +
302 m_filePath.parent_path().string() +
303 "] exists. Error: " + l_ec.message());
304
305 l_ec.clear();
306 }
307
308 // create parent directories
309 if (!std::filesystem::create_directories(m_filePath.parent_path(),
310 l_ec))
311 {
312 if (l_ec)
313 {
314 throw std::runtime_error(
315 "Failed to create parent directory of log file path:[" +
316 m_filePath.string() + "]. Error: " + l_ec.message());
317 }
318 }
319 }
320 }
321
Souvik Roybac8ba62025-10-27 09:07:06 +0000322 // open the file in append mode
Souvik Royd834b122025-10-28 06:37:51 +0000323 m_fileStream.open(m_filePath, std::ios::out | std::ios::ate);
Souvik Roybac8ba62025-10-27 09:07:06 +0000324 // enable exception mask to throw on badbit and failbit
325 m_fileStream.exceptions(std::ios_base::badbit | std::ios_base::failbit);
326
327 if (l_logFileExists)
328 {
329 // log file already exists, check and update the number of entries
330 std::ifstream l_readFileStream{m_filePath};
331 for (std::string l_line; std::getline(l_readFileStream, l_line);
332 ++m_currentNumEntries)
333 {}
334
335 l_readFileStream.close();
336 }
337}
338
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500339namespace logging
340{
341void logMessage(std::string_view message, const std::source_location& location)
342{
343 std::ostringstream log;
344 log << "FileName: " << location.file_name() << ","
345 << " Line: " << location.line() << " " << message;
346
Sunny Srivastavafa5e4d32023-03-12 11:59:49 -0500347 std::cout << log.str() << std::endl;
348}
349} // namespace logging
350} // namespace vpd