xref: /openbmc/openpower-vpd-parser/vpd-manager/src/logger.cpp (revision b5fab80a2955b33d56c07f47bf5c20057b3d1de3)
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