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