xref: /openbmc/bmcweb/features/redfish/src/event_log.cpp (revision d109e2b60f7bb367dc8115475c6cb86bca6e1914)
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>
26b80ba2e4SAlexander Hansen #include <ctime>
27b80ba2e4SAlexander Hansen #include <iomanip>
28b80ba2e4SAlexander Hansen #include <span>
29b80ba2e4SAlexander Hansen #include <sstream>
30b80ba2e4SAlexander Hansen #include <string>
31b80ba2e4SAlexander Hansen #include <string_view>
32b80ba2e4SAlexander Hansen #include <utility>
33b80ba2e4SAlexander Hansen #include <vector>
34b80ba2e4SAlexander Hansen 
35b80ba2e4SAlexander Hansen namespace redfish
36b80ba2e4SAlexander Hansen {
37b80ba2e4SAlexander Hansen 
38b80ba2e4SAlexander Hansen namespace event_log
39b80ba2e4SAlexander Hansen {
40b80ba2e4SAlexander Hansen 
41b80ba2e4SAlexander Hansen bool getUniqueEntryID(const std::string& logEntry, std::string& entryID)
42b80ba2e4SAlexander Hansen {
43b80ba2e4SAlexander Hansen     static time_t prevTs = 0;
44b80ba2e4SAlexander Hansen     static int index = 0;
45b80ba2e4SAlexander Hansen 
46b80ba2e4SAlexander Hansen     // Get the entry timestamp
47b80ba2e4SAlexander Hansen     std::time_t curTs = 0;
48b80ba2e4SAlexander Hansen     std::tm timeStruct = {};
49b80ba2e4SAlexander Hansen     std::istringstream entryStream(logEntry);
50b80ba2e4SAlexander Hansen     if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
51b80ba2e4SAlexander Hansen     {
52b80ba2e4SAlexander Hansen         curTs = std::mktime(&timeStruct);
53b80ba2e4SAlexander Hansen         if (curTs == -1)
54b80ba2e4SAlexander Hansen         {
55b80ba2e4SAlexander Hansen             return false;
56b80ba2e4SAlexander Hansen         }
57b80ba2e4SAlexander Hansen     }
58b80ba2e4SAlexander Hansen     // If the timestamp isn't unique, increment the index
59b80ba2e4SAlexander Hansen     index = (curTs == prevTs) ? index + 1 : 0;
60b80ba2e4SAlexander Hansen 
61b80ba2e4SAlexander Hansen     // Save the timestamp
62b80ba2e4SAlexander Hansen     prevTs = curTs;
63b80ba2e4SAlexander Hansen 
64b80ba2e4SAlexander Hansen     entryID = std::to_string(curTs);
65b80ba2e4SAlexander Hansen     if (index > 0)
66b80ba2e4SAlexander Hansen     {
67b80ba2e4SAlexander Hansen         entryID += "_" + std::to_string(index);
68b80ba2e4SAlexander Hansen     }
69b80ba2e4SAlexander Hansen     return true;
70b80ba2e4SAlexander Hansen }
71b80ba2e4SAlexander Hansen 
72b80ba2e4SAlexander Hansen int getEventLogParams(const std::string& logEntry, std::string& timestamp,
73b80ba2e4SAlexander Hansen                       std::string& messageID,
74b80ba2e4SAlexander Hansen                       std::vector<std::string>& messageArgs)
75b80ba2e4SAlexander Hansen {
76b80ba2e4SAlexander Hansen     // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
77b80ba2e4SAlexander Hansen     // First get the Timestamp
78b80ba2e4SAlexander Hansen     size_t space = logEntry.find_first_of(' ');
79b80ba2e4SAlexander Hansen     if (space == std::string::npos)
80b80ba2e4SAlexander Hansen     {
81b80ba2e4SAlexander Hansen         BMCWEB_LOG_ERROR("EventLog Params: could not find first space: {}",
82b80ba2e4SAlexander Hansen                          logEntry);
83b80ba2e4SAlexander Hansen         return -EINVAL;
84b80ba2e4SAlexander Hansen     }
85b80ba2e4SAlexander Hansen     timestamp = logEntry.substr(0, space);
86b80ba2e4SAlexander Hansen     // Then get the log contents
87b80ba2e4SAlexander Hansen     size_t entryStart = logEntry.find_first_not_of(' ', space);
88b80ba2e4SAlexander Hansen     if (entryStart == std::string::npos)
89b80ba2e4SAlexander Hansen     {
90b80ba2e4SAlexander Hansen         BMCWEB_LOG_ERROR("EventLog Params: could not find log contents: {}",
91b80ba2e4SAlexander Hansen                          logEntry);
92b80ba2e4SAlexander Hansen         return -EINVAL;
93b80ba2e4SAlexander Hansen     }
94b80ba2e4SAlexander Hansen     std::string_view entry(logEntry);
95b80ba2e4SAlexander Hansen     entry.remove_prefix(entryStart);
96b80ba2e4SAlexander Hansen     // Use split to separate the entry into its fields
97b80ba2e4SAlexander Hansen     std::vector<std::string> logEntryFields;
98b80ba2e4SAlexander Hansen     bmcweb::split(logEntryFields, entry, ',');
99b80ba2e4SAlexander Hansen     // We need at least a MessageId to be valid
100b80ba2e4SAlexander Hansen     if (logEntryFields.empty())
101b80ba2e4SAlexander Hansen     {
102b80ba2e4SAlexander Hansen         BMCWEB_LOG_ERROR("EventLog Params: could not find entry fields: {}",
103b80ba2e4SAlexander Hansen                          logEntry);
104b80ba2e4SAlexander Hansen         return -EINVAL;
105b80ba2e4SAlexander Hansen     }
106b80ba2e4SAlexander Hansen     messageID = logEntryFields[0];
107b80ba2e4SAlexander Hansen 
108b80ba2e4SAlexander Hansen     // Get the MessageArgs from the log if there are any
109b80ba2e4SAlexander Hansen     if (logEntryFields.size() > 1)
110b80ba2e4SAlexander Hansen     {
111b80ba2e4SAlexander Hansen         const std::string& messageArgsStart = logEntryFields[1];
112b80ba2e4SAlexander Hansen         // If the first string is empty, assume there are no MessageArgs
113b80ba2e4SAlexander Hansen         if (!messageArgsStart.empty())
114b80ba2e4SAlexander Hansen         {
115b80ba2e4SAlexander Hansen             messageArgs.assign(logEntryFields.begin() + 1,
116b80ba2e4SAlexander Hansen                                logEntryFields.end());
117b80ba2e4SAlexander Hansen         }
118b80ba2e4SAlexander Hansen     }
119b80ba2e4SAlexander Hansen 
120b80ba2e4SAlexander Hansen     return 0;
121b80ba2e4SAlexander Hansen }
122b80ba2e4SAlexander Hansen 
123b80ba2e4SAlexander Hansen int formatEventLogEntry(
124b80ba2e4SAlexander Hansen     const std::string& logEntryID, const std::string& messageID,
125b80ba2e4SAlexander Hansen     const std::span<std::string_view> messageArgs, std::string timestamp,
126b80ba2e4SAlexander Hansen     const std::string& customText, nlohmann::json::object_t& logEntryJson)
127b80ba2e4SAlexander Hansen {
128b80ba2e4SAlexander Hansen     // Get the Message from the MessageRegistry
129*d109e2b6SAlexander Hansen     const registries::Message* message = registries::getMessage(messageID);
130b80ba2e4SAlexander Hansen 
131b80ba2e4SAlexander Hansen     if (message == nullptr)
132b80ba2e4SAlexander Hansen     {
133b80ba2e4SAlexander Hansen         return -1;
134b80ba2e4SAlexander Hansen     }
135b80ba2e4SAlexander Hansen 
136b80ba2e4SAlexander Hansen     std::string msg =
137b80ba2e4SAlexander Hansen         redfish::registries::fillMessageArgs(messageArgs, message->message);
138b80ba2e4SAlexander Hansen     if (msg.empty())
139b80ba2e4SAlexander Hansen     {
140b80ba2e4SAlexander Hansen         return -1;
141b80ba2e4SAlexander Hansen     }
142b80ba2e4SAlexander Hansen 
143b80ba2e4SAlexander Hansen     // Get the Created time from the timestamp. The log timestamp is in
144b80ba2e4SAlexander Hansen     // RFC3339 format which matches the Redfish format except for the
145b80ba2e4SAlexander Hansen     // fractional seconds between the '.' and the '+', so just remove them.
146b80ba2e4SAlexander Hansen     std::size_t dot = timestamp.find_first_of('.');
147b80ba2e4SAlexander Hansen     std::size_t plus = timestamp.find_first_of('+', dot);
148b80ba2e4SAlexander Hansen     if (dot != std::string::npos && plus != std::string::npos)
149b80ba2e4SAlexander Hansen     {
150b80ba2e4SAlexander Hansen         timestamp.erase(dot, plus - dot);
151b80ba2e4SAlexander Hansen     }
152b80ba2e4SAlexander Hansen 
153b80ba2e4SAlexander Hansen     // Fill in the log entry with the gathered data
154b80ba2e4SAlexander Hansen     logEntryJson["EventId"] = logEntryID;
155b80ba2e4SAlexander Hansen 
156b80ba2e4SAlexander Hansen     logEntryJson["Severity"] = message->messageSeverity;
157b80ba2e4SAlexander Hansen     logEntryJson["Message"] = std::move(msg);
158b80ba2e4SAlexander Hansen     logEntryJson["MessageId"] = messageID;
159b80ba2e4SAlexander Hansen     logEntryJson["MessageArgs"] = messageArgs;
160b80ba2e4SAlexander Hansen     logEntryJson["EventTimestamp"] = std::move(timestamp);
161b80ba2e4SAlexander Hansen     logEntryJson["Context"] = customText;
162b80ba2e4SAlexander Hansen     return 0;
163b80ba2e4SAlexander Hansen }
164b80ba2e4SAlexander Hansen 
165b80ba2e4SAlexander Hansen } // namespace event_log
166b80ba2e4SAlexander Hansen 
167b80ba2e4SAlexander Hansen } // namespace redfish
168