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