blob: a4e78507bd5e3df141db5e32d3ff53bbe10499a9 [file] [log] [blame]
Alexander Hansenb80ba2e2024-11-18 12:24:35 +01001/*
2Copyright (c) 2020 Intel Corporation
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16#include "event_log.hpp"
17
Alexander Hansenb80ba2e2024-11-18 12:24:35 +010018#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>
Ed Tanous4a19a7b2025-01-27 10:44:15 -080026#include <cstdint>
Alexander Hansenb80ba2e2024-11-18 12:24:35 +010027#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
36namespace redfish
37{
38
39namespace event_log
40{
41
42bool 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
73int 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
Ed Tanous4a19a7b2025-01-27 10:44:15 -0800124int 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)
Alexander Hansenb80ba2e2024-11-18 12:24:35 +0100129{
130 // Get the Message from the MessageRegistry
Alexander Hansend109e2b2024-11-18 14:38:06 +0100131 const registries::Message* message = registries::getMessage(messageID);
Alexander Hansenb80ba2e2024-11-18 12:24:35 +0100132
133 if (message == nullptr)
134 {
Igor Kanyuka0309c212025-01-10 03:38:25 -0800135 BMCWEB_LOG_DEBUG(
136 "{}: could not find messageID '{}' for log entry {} in registry",
137 __func__, messageID, logEntryID);
Alexander Hansenb80ba2e2024-11-18 12:24:35 +0100138 return -1;
139 }
140
141 std::string msg =
142 redfish::registries::fillMessageArgs(messageArgs, message->message);
143 if (msg.empty())
144 {
Igor Kanyuka0309c212025-01-10 03:38:25 -0800145 BMCWEB_LOG_DEBUG("{}: message is empty after filling fillMessageArgs",
146 __func__);
Alexander Hansenb80ba2e2024-11-18 12:24:35 +0100147 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
Ed Tanous4a19a7b2025-01-27 10:44:15 -0800161 logEntryJson["EventId"] = std::to_string(eventId);
Alexander Hansenb80ba2e2024-11-18 12:24:35 +0100162
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