xref: /openbmc/openpower-vpd-parser/vpd-manager/src/logger.cpp (revision 8042bbf27edb16536e05ba4788b22acf8aee289b)
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 
logMessage(std::string_view i_message,const PlaceHolder & i_placeHolder,const types::PelInfoTuple * i_pelTuple,const std::source_location & i_location)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 
initiateVpdCollectionLogging()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 
logMessage(const std::string_view & i_message)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 
logMessage(const std::string_view & i_message)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 
fileWorker()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 
rotateFile(const unsigned i_numEntriesToDelete)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 {
logMessage(std::string_view message,const std::source_location & location)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