xref: /openbmc/bmcweb/features/redfish/src/event_log.cpp (revision 5ffbc9ae424658c15c2b6d93b86b752f09d2b6dc)
1*5ffbc9aeSGunnar Mills // SPDX-License-Identifier: Apache-2.0
2*5ffbc9aeSGunnar Mills // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3*5ffbc9aeSGunnar Mills // SPDX-FileCopyrightText: Copyright 2020 Intel Corporation
4b80ba2e4SAlexander Hansen #include "event_log.hpp"
5b80ba2e4SAlexander Hansen 
6b80ba2e4SAlexander Hansen #include "logging.hpp"
7b80ba2e4SAlexander Hansen #include "registries.hpp"
8b80ba2e4SAlexander Hansen #include "str_utility.hpp"
9b80ba2e4SAlexander Hansen 
10b80ba2e4SAlexander Hansen #include <nlohmann/json.hpp>
11b80ba2e4SAlexander Hansen 
12b80ba2e4SAlexander Hansen #include <cerrno>
13b80ba2e4SAlexander Hansen #include <cstddef>
144a19a7b5SEd Tanous #include <cstdint>
15b80ba2e4SAlexander Hansen #include <ctime>
16b80ba2e4SAlexander Hansen #include <iomanip>
17b80ba2e4SAlexander Hansen #include <span>
18b80ba2e4SAlexander Hansen #include <sstream>
19b80ba2e4SAlexander Hansen #include <string>
20b80ba2e4SAlexander Hansen #include <string_view>
21b80ba2e4SAlexander Hansen #include <utility>
22b80ba2e4SAlexander Hansen #include <vector>
23b80ba2e4SAlexander Hansen 
24b80ba2e4SAlexander Hansen namespace redfish
25b80ba2e4SAlexander Hansen {
26b80ba2e4SAlexander Hansen 
27b80ba2e4SAlexander Hansen namespace event_log
28b80ba2e4SAlexander Hansen {
29b80ba2e4SAlexander Hansen 
30b80ba2e4SAlexander Hansen bool getUniqueEntryID(const std::string& logEntry, std::string& entryID)
31b80ba2e4SAlexander Hansen {
32b80ba2e4SAlexander Hansen     static time_t prevTs = 0;
33b80ba2e4SAlexander Hansen     static int index = 0;
34b80ba2e4SAlexander Hansen 
35b80ba2e4SAlexander Hansen     // Get the entry timestamp
36b80ba2e4SAlexander Hansen     std::time_t curTs = 0;
37b80ba2e4SAlexander Hansen     std::tm timeStruct = {};
38b80ba2e4SAlexander Hansen     std::istringstream entryStream(logEntry);
39b80ba2e4SAlexander Hansen     if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
40b80ba2e4SAlexander Hansen     {
41b80ba2e4SAlexander Hansen         curTs = std::mktime(&timeStruct);
42b80ba2e4SAlexander Hansen         if (curTs == -1)
43b80ba2e4SAlexander Hansen         {
44b80ba2e4SAlexander Hansen             return false;
45b80ba2e4SAlexander Hansen         }
46b80ba2e4SAlexander Hansen     }
47b80ba2e4SAlexander Hansen     // If the timestamp isn't unique, increment the index
48b80ba2e4SAlexander Hansen     index = (curTs == prevTs) ? index + 1 : 0;
49b80ba2e4SAlexander Hansen 
50b80ba2e4SAlexander Hansen     // Save the timestamp
51b80ba2e4SAlexander Hansen     prevTs = curTs;
52b80ba2e4SAlexander Hansen 
53b80ba2e4SAlexander Hansen     entryID = std::to_string(curTs);
54b80ba2e4SAlexander Hansen     if (index > 0)
55b80ba2e4SAlexander Hansen     {
56b80ba2e4SAlexander Hansen         entryID += "_" + std::to_string(index);
57b80ba2e4SAlexander Hansen     }
58b80ba2e4SAlexander Hansen     return true;
59b80ba2e4SAlexander Hansen }
60b80ba2e4SAlexander Hansen 
61b80ba2e4SAlexander Hansen int getEventLogParams(const std::string& logEntry, std::string& timestamp,
62b80ba2e4SAlexander Hansen                       std::string& messageID,
63b80ba2e4SAlexander Hansen                       std::vector<std::string>& messageArgs)
64b80ba2e4SAlexander Hansen {
65b80ba2e4SAlexander Hansen     // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
66b80ba2e4SAlexander Hansen     // First get the Timestamp
67b80ba2e4SAlexander Hansen     size_t space = logEntry.find_first_of(' ');
68b80ba2e4SAlexander Hansen     if (space == std::string::npos)
69b80ba2e4SAlexander Hansen     {
70b80ba2e4SAlexander Hansen         BMCWEB_LOG_ERROR("EventLog Params: could not find first space: {}",
71b80ba2e4SAlexander Hansen                          logEntry);
72b80ba2e4SAlexander Hansen         return -EINVAL;
73b80ba2e4SAlexander Hansen     }
74b80ba2e4SAlexander Hansen     timestamp = logEntry.substr(0, space);
75b80ba2e4SAlexander Hansen     // Then get the log contents
76b80ba2e4SAlexander Hansen     size_t entryStart = logEntry.find_first_not_of(' ', space);
77b80ba2e4SAlexander Hansen     if (entryStart == std::string::npos)
78b80ba2e4SAlexander Hansen     {
79b80ba2e4SAlexander Hansen         BMCWEB_LOG_ERROR("EventLog Params: could not find log contents: {}",
80b80ba2e4SAlexander Hansen                          logEntry);
81b80ba2e4SAlexander Hansen         return -EINVAL;
82b80ba2e4SAlexander Hansen     }
83b80ba2e4SAlexander Hansen     std::string_view entry(logEntry);
84b80ba2e4SAlexander Hansen     entry.remove_prefix(entryStart);
85b80ba2e4SAlexander Hansen     // Use split to separate the entry into its fields
86b80ba2e4SAlexander Hansen     std::vector<std::string> logEntryFields;
87b80ba2e4SAlexander Hansen     bmcweb::split(logEntryFields, entry, ',');
88b80ba2e4SAlexander Hansen     // We need at least a MessageId to be valid
89b80ba2e4SAlexander Hansen     if (logEntryFields.empty())
90b80ba2e4SAlexander Hansen     {
91b80ba2e4SAlexander Hansen         BMCWEB_LOG_ERROR("EventLog Params: could not find entry fields: {}",
92b80ba2e4SAlexander Hansen                          logEntry);
93b80ba2e4SAlexander Hansen         return -EINVAL;
94b80ba2e4SAlexander Hansen     }
95b80ba2e4SAlexander Hansen     messageID = logEntryFields[0];
96b80ba2e4SAlexander Hansen 
97b80ba2e4SAlexander Hansen     // Get the MessageArgs from the log if there are any
98b80ba2e4SAlexander Hansen     if (logEntryFields.size() > 1)
99b80ba2e4SAlexander Hansen     {
100b80ba2e4SAlexander Hansen         const std::string& messageArgsStart = logEntryFields[1];
101b80ba2e4SAlexander Hansen         // If the first string is empty, assume there are no MessageArgs
102b80ba2e4SAlexander Hansen         if (!messageArgsStart.empty())
103b80ba2e4SAlexander Hansen         {
104b80ba2e4SAlexander Hansen             messageArgs.assign(logEntryFields.begin() + 1,
105b80ba2e4SAlexander Hansen                                logEntryFields.end());
106b80ba2e4SAlexander Hansen         }
107b80ba2e4SAlexander Hansen     }
108b80ba2e4SAlexander Hansen 
109b80ba2e4SAlexander Hansen     return 0;
110b80ba2e4SAlexander Hansen }
111b80ba2e4SAlexander Hansen 
1124a19a7b5SEd Tanous int formatEventLogEntry(uint64_t eventId, const std::string& logEntryID,
1134a19a7b5SEd Tanous                         const std::string& messageID,
1144a19a7b5SEd Tanous                         const std::span<std::string_view> messageArgs,
1154a19a7b5SEd Tanous                         std::string timestamp, const std::string& customText,
1164a19a7b5SEd Tanous                         nlohmann::json::object_t& logEntryJson)
117b80ba2e4SAlexander Hansen {
118b80ba2e4SAlexander Hansen     // Get the Message from the MessageRegistry
119d109e2b6SAlexander Hansen     const registries::Message* message = registries::getMessage(messageID);
120b80ba2e4SAlexander Hansen 
121b80ba2e4SAlexander Hansen     if (message == nullptr)
122b80ba2e4SAlexander Hansen     {
1230309c216SIgor Kanyuka         BMCWEB_LOG_DEBUG(
1240309c216SIgor Kanyuka             "{}: could not find messageID '{}' for log entry {} in registry",
1250309c216SIgor Kanyuka             __func__, messageID, logEntryID);
126b80ba2e4SAlexander Hansen         return -1;
127b80ba2e4SAlexander Hansen     }
128b80ba2e4SAlexander Hansen 
129b80ba2e4SAlexander Hansen     std::string msg =
130b80ba2e4SAlexander Hansen         redfish::registries::fillMessageArgs(messageArgs, message->message);
131b80ba2e4SAlexander Hansen     if (msg.empty())
132b80ba2e4SAlexander Hansen     {
1330309c216SIgor Kanyuka         BMCWEB_LOG_DEBUG("{}: message is empty after filling fillMessageArgs",
1340309c216SIgor Kanyuka                          __func__);
135b80ba2e4SAlexander Hansen         return -1;
136b80ba2e4SAlexander Hansen     }
137b80ba2e4SAlexander Hansen 
138b80ba2e4SAlexander Hansen     // Get the Created time from the timestamp. The log timestamp is in
139b80ba2e4SAlexander Hansen     // RFC3339 format which matches the Redfish format except for the
140b80ba2e4SAlexander Hansen     // fractional seconds between the '.' and the '+', so just remove them.
141b80ba2e4SAlexander Hansen     std::size_t dot = timestamp.find_first_of('.');
142b80ba2e4SAlexander Hansen     std::size_t plus = timestamp.find_first_of('+', dot);
143b80ba2e4SAlexander Hansen     if (dot != std::string::npos && plus != std::string::npos)
144b80ba2e4SAlexander Hansen     {
145b80ba2e4SAlexander Hansen         timestamp.erase(dot, plus - dot);
146b80ba2e4SAlexander Hansen     }
147b80ba2e4SAlexander Hansen 
148b80ba2e4SAlexander Hansen     // Fill in the log entry with the gathered data
1494a19a7b5SEd Tanous     logEntryJson["EventId"] = std::to_string(eventId);
150b80ba2e4SAlexander Hansen 
151b80ba2e4SAlexander Hansen     logEntryJson["Severity"] = message->messageSeverity;
152b80ba2e4SAlexander Hansen     logEntryJson["Message"] = std::move(msg);
153b80ba2e4SAlexander Hansen     logEntryJson["MessageId"] = messageID;
154b80ba2e4SAlexander Hansen     logEntryJson["MessageArgs"] = messageArgs;
155b80ba2e4SAlexander Hansen     logEntryJson["EventTimestamp"] = std::move(timestamp);
156b80ba2e4SAlexander Hansen     logEntryJson["Context"] = customText;
157b80ba2e4SAlexander Hansen     return 0;
158b80ba2e4SAlexander Hansen }
159b80ba2e4SAlexander Hansen 
160b80ba2e4SAlexander Hansen } // namespace event_log
161b80ba2e4SAlexander Hansen 
162b80ba2e4SAlexander Hansen } // namespace redfish
163