xref: /openbmc/bmcweb/redfish-core/src/event_log.cpp (revision 4a19a7b5e62ad2569913d944c84c19f1005c922f)
1b80ba2e4SAlexander Hansen /*
2b80ba2e4SAlexander Hansen Copyright (c) 2020 Intel Corporation
3b80ba2e4SAlexander Hansen 
4b80ba2e4SAlexander Hansen Licensed under the Apache License, Version 2.0 (the "License");
5b80ba2e4SAlexander Hansen you may not use this file except in compliance with the License.
6b80ba2e4SAlexander Hansen You may obtain a copy of the License at
7b80ba2e4SAlexander Hansen 
8b80ba2e4SAlexander Hansen       http://www.apache.org/licenses/LICENSE-2.0
9b80ba2e4SAlexander Hansen 
10b80ba2e4SAlexander Hansen Unless required by applicable law or agreed to in writing, software
11b80ba2e4SAlexander Hansen distributed under the License is distributed on an "AS IS" BASIS,
12b80ba2e4SAlexander Hansen WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b80ba2e4SAlexander Hansen See the License for the specific language governing permissions and
14b80ba2e4SAlexander Hansen limitations under the License.
15b80ba2e4SAlexander Hansen */
16b80ba2e4SAlexander Hansen #include "event_log.hpp"
17b80ba2e4SAlexander Hansen 
18b80ba2e4SAlexander Hansen #include "logging.hpp"
19b80ba2e4SAlexander Hansen #include "registries.hpp"
20b80ba2e4SAlexander Hansen #include "str_utility.hpp"
21b80ba2e4SAlexander Hansen 
22b80ba2e4SAlexander Hansen #include <nlohmann/json.hpp>
23b80ba2e4SAlexander Hansen 
24b80ba2e4SAlexander Hansen #include <cerrno>
25b80ba2e4SAlexander Hansen #include <cstddef>
26*4a19a7b5SEd Tanous #include <cstdint>
27b80ba2e4SAlexander Hansen #include <ctime>
28b80ba2e4SAlexander Hansen #include <iomanip>
29b80ba2e4SAlexander Hansen #include <span>
30b80ba2e4SAlexander Hansen #include <sstream>
31b80ba2e4SAlexander Hansen #include <string>
32b80ba2e4SAlexander Hansen #include <string_view>
33b80ba2e4SAlexander Hansen #include <utility>
34b80ba2e4SAlexander Hansen #include <vector>
35b80ba2e4SAlexander Hansen 
36b80ba2e4SAlexander Hansen namespace redfish
37b80ba2e4SAlexander Hansen {
38b80ba2e4SAlexander Hansen 
39b80ba2e4SAlexander Hansen namespace event_log
40b80ba2e4SAlexander Hansen {
41b80ba2e4SAlexander Hansen 
getUniqueEntryID(const std::string & logEntry,std::string & entryID)42b80ba2e4SAlexander Hansen bool getUniqueEntryID(const std::string& logEntry, std::string& entryID)
43b80ba2e4SAlexander Hansen {
44b80ba2e4SAlexander Hansen     static time_t prevTs = 0;
45b80ba2e4SAlexander Hansen     static int index = 0;
46b80ba2e4SAlexander Hansen 
47b80ba2e4SAlexander Hansen     // Get the entry timestamp
48b80ba2e4SAlexander Hansen     std::time_t curTs = 0;
49b80ba2e4SAlexander Hansen     std::tm timeStruct = {};
50b80ba2e4SAlexander Hansen     std::istringstream entryStream(logEntry);
51b80ba2e4SAlexander Hansen     if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
52b80ba2e4SAlexander Hansen     {
53b80ba2e4SAlexander Hansen         curTs = std::mktime(&timeStruct);
54b80ba2e4SAlexander Hansen         if (curTs == -1)
55b80ba2e4SAlexander Hansen         {
56b80ba2e4SAlexander Hansen             return false;
57b80ba2e4SAlexander Hansen         }
58b80ba2e4SAlexander Hansen     }
59b80ba2e4SAlexander Hansen     // If the timestamp isn't unique, increment the index
60b80ba2e4SAlexander Hansen     index = (curTs == prevTs) ? index + 1 : 0;
61b80ba2e4SAlexander Hansen 
62b80ba2e4SAlexander Hansen     // Save the timestamp
63b80ba2e4SAlexander Hansen     prevTs = curTs;
64b80ba2e4SAlexander Hansen 
65b80ba2e4SAlexander Hansen     entryID = std::to_string(curTs);
66b80ba2e4SAlexander Hansen     if (index > 0)
67b80ba2e4SAlexander Hansen     {
68b80ba2e4SAlexander Hansen         entryID += "_" + std::to_string(index);
69b80ba2e4SAlexander Hansen     }
70b80ba2e4SAlexander Hansen     return true;
71b80ba2e4SAlexander Hansen }
72b80ba2e4SAlexander Hansen 
getEventLogParams(const std::string & logEntry,std::string & timestamp,std::string & messageID,std::vector<std::string> & messageArgs)73b80ba2e4SAlexander Hansen int getEventLogParams(const std::string& logEntry, std::string& timestamp,
74b80ba2e4SAlexander Hansen                       std::string& messageID,
75b80ba2e4SAlexander Hansen                       std::vector<std::string>& messageArgs)
76b80ba2e4SAlexander Hansen {
77b80ba2e4SAlexander Hansen     // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
78b80ba2e4SAlexander Hansen     // First get the Timestamp
79b80ba2e4SAlexander Hansen     size_t space = logEntry.find_first_of(' ');
80b80ba2e4SAlexander Hansen     if (space == std::string::npos)
81b80ba2e4SAlexander Hansen     {
82b80ba2e4SAlexander Hansen         BMCWEB_LOG_ERROR("EventLog Params: could not find first space: {}",
83b80ba2e4SAlexander Hansen                          logEntry);
84b80ba2e4SAlexander Hansen         return -EINVAL;
85b80ba2e4SAlexander Hansen     }
86b80ba2e4SAlexander Hansen     timestamp = logEntry.substr(0, space);
87b80ba2e4SAlexander Hansen     // Then get the log contents
88b80ba2e4SAlexander Hansen     size_t entryStart = logEntry.find_first_not_of(' ', space);
89b80ba2e4SAlexander Hansen     if (entryStart == std::string::npos)
90b80ba2e4SAlexander Hansen     {
91b80ba2e4SAlexander Hansen         BMCWEB_LOG_ERROR("EventLog Params: could not find log contents: {}",
92b80ba2e4SAlexander Hansen                          logEntry);
93b80ba2e4SAlexander Hansen         return -EINVAL;
94b80ba2e4SAlexander Hansen     }
95b80ba2e4SAlexander Hansen     std::string_view entry(logEntry);
96b80ba2e4SAlexander Hansen     entry.remove_prefix(entryStart);
97b80ba2e4SAlexander Hansen     // Use split to separate the entry into its fields
98b80ba2e4SAlexander Hansen     std::vector<std::string> logEntryFields;
99b80ba2e4SAlexander Hansen     bmcweb::split(logEntryFields, entry, ',');
100b80ba2e4SAlexander Hansen     // We need at least a MessageId to be valid
101b80ba2e4SAlexander Hansen     if (logEntryFields.empty())
102b80ba2e4SAlexander Hansen     {
103b80ba2e4SAlexander Hansen         BMCWEB_LOG_ERROR("EventLog Params: could not find entry fields: {}",
104b80ba2e4SAlexander Hansen                          logEntry);
105b80ba2e4SAlexander Hansen         return -EINVAL;
106b80ba2e4SAlexander Hansen     }
107b80ba2e4SAlexander Hansen     messageID = logEntryFields[0];
108b80ba2e4SAlexander Hansen 
109b80ba2e4SAlexander Hansen     // Get the MessageArgs from the log if there are any
110b80ba2e4SAlexander Hansen     if (logEntryFields.size() > 1)
111b80ba2e4SAlexander Hansen     {
112b80ba2e4SAlexander Hansen         const std::string& messageArgsStart = logEntryFields[1];
113b80ba2e4SAlexander Hansen         // If the first string is empty, assume there are no MessageArgs
114b80ba2e4SAlexander Hansen         if (!messageArgsStart.empty())
115b80ba2e4SAlexander Hansen         {
116b80ba2e4SAlexander Hansen             messageArgs.assign(logEntryFields.begin() + 1,
117b80ba2e4SAlexander Hansen                                logEntryFields.end());
118b80ba2e4SAlexander Hansen         }
119b80ba2e4SAlexander Hansen     }
120b80ba2e4SAlexander Hansen 
121b80ba2e4SAlexander Hansen     return 0;
122b80ba2e4SAlexander Hansen }
123b80ba2e4SAlexander Hansen 
formatEventLogEntry(uint64_t eventId,const std::string & logEntryID,const std::string & messageID,const std::span<std::string_view> messageArgs,std::string timestamp,const std::string & customText,nlohmann::json::object_t & logEntryJson)124*4a19a7b5SEd Tanous int formatEventLogEntry(uint64_t eventId, const std::string& logEntryID,
125*4a19a7b5SEd Tanous                         const std::string& messageID,
126*4a19a7b5SEd Tanous                         const std::span<std::string_view> messageArgs,
127*4a19a7b5SEd Tanous                         std::string timestamp, const std::string& customText,
128*4a19a7b5SEd Tanous                         nlohmann::json::object_t& logEntryJson)
129b80ba2e4SAlexander Hansen {
130b80ba2e4SAlexander Hansen     // Get the Message from the MessageRegistry
131d109e2b6SAlexander Hansen     const registries::Message* message = registries::getMessage(messageID);
132b80ba2e4SAlexander Hansen 
133b80ba2e4SAlexander Hansen     if (message == nullptr)
134b80ba2e4SAlexander Hansen     {
1350309c216SIgor Kanyuka         BMCWEB_LOG_DEBUG(
1360309c216SIgor Kanyuka             "{}: could not find messageID '{}' for log entry {} in registry",
1370309c216SIgor Kanyuka             __func__, messageID, logEntryID);
138b80ba2e4SAlexander Hansen         return -1;
139b80ba2e4SAlexander Hansen     }
140b80ba2e4SAlexander Hansen 
141b80ba2e4SAlexander Hansen     std::string msg =
142b80ba2e4SAlexander Hansen         redfish::registries::fillMessageArgs(messageArgs, message->message);
143b80ba2e4SAlexander Hansen     if (msg.empty())
144b80ba2e4SAlexander Hansen     {
1450309c216SIgor Kanyuka         BMCWEB_LOG_DEBUG("{}: message is empty after filling fillMessageArgs",
1460309c216SIgor Kanyuka                          __func__);
147b80ba2e4SAlexander Hansen         return -1;
148b80ba2e4SAlexander Hansen     }
149b80ba2e4SAlexander Hansen 
150b80ba2e4SAlexander Hansen     // Get the Created time from the timestamp. The log timestamp is in
151b80ba2e4SAlexander Hansen     // RFC3339 format which matches the Redfish format except for the
152b80ba2e4SAlexander Hansen     // fractional seconds between the '.' and the '+', so just remove them.
153b80ba2e4SAlexander Hansen     std::size_t dot = timestamp.find_first_of('.');
154b80ba2e4SAlexander Hansen     std::size_t plus = timestamp.find_first_of('+', dot);
155b80ba2e4SAlexander Hansen     if (dot != std::string::npos && plus != std::string::npos)
156b80ba2e4SAlexander Hansen     {
157b80ba2e4SAlexander Hansen         timestamp.erase(dot, plus - dot);
158b80ba2e4SAlexander Hansen     }
159b80ba2e4SAlexander Hansen 
160b80ba2e4SAlexander Hansen     // Fill in the log entry with the gathered data
161*4a19a7b5SEd Tanous     logEntryJson["EventId"] = std::to_string(eventId);
162b80ba2e4SAlexander Hansen 
163b80ba2e4SAlexander Hansen     logEntryJson["Severity"] = message->messageSeverity;
164b80ba2e4SAlexander Hansen     logEntryJson["Message"] = std::move(msg);
165b80ba2e4SAlexander Hansen     logEntryJson["MessageId"] = messageID;
166b80ba2e4SAlexander Hansen     logEntryJson["MessageArgs"] = messageArgs;
167b80ba2e4SAlexander Hansen     logEntryJson["EventTimestamp"] = std::move(timestamp);
168b80ba2e4SAlexander Hansen     logEntryJson["Context"] = customText;
169b80ba2e4SAlexander Hansen     return 0;
170b80ba2e4SAlexander Hansen }
171b80ba2e4SAlexander Hansen 
172b80ba2e4SAlexander Hansen } // namespace event_log
173b80ba2e4SAlexander Hansen 
174b80ba2e4SAlexander Hansen } // namespace redfish
175