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