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