blob: 5420549236b11aae5d1054f89c48b40e5b921732 [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
18#include "event_service_manager.hpp"
19#include "logging.hpp"
20#include "registries.hpp"
21#include "str_utility.hpp"
22
23#include <nlohmann/json.hpp>
24
25#include <cerrno>
26#include <cstddef>
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
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
124int formatEventLogEntry(
125 const std::string& logEntryID, const std::string& messageID,
126 const std::span<std::string_view> messageArgs, std::string timestamp,
127 const std::string& customText, nlohmann::json::object_t& logEntryJson)
128{
129 // Get the Message from the MessageRegistry
130 const registries::Message* message = registries::formatMessage(messageID);
131
132 if (message == nullptr)
133 {
134 return -1;
135 }
136
137 std::string msg =
138 redfish::registries::fillMessageArgs(messageArgs, message->message);
139 if (msg.empty())
140 {
141 return -1;
142 }
143
144 // Get the Created time from the timestamp. The log timestamp is in
145 // RFC3339 format which matches the Redfish format except for the
146 // fractional seconds between the '.' and the '+', so just remove them.
147 std::size_t dot = timestamp.find_first_of('.');
148 std::size_t plus = timestamp.find_first_of('+', dot);
149 if (dot != std::string::npos && plus != std::string::npos)
150 {
151 timestamp.erase(dot, plus - dot);
152 }
153
154 // Fill in the log entry with the gathered data
155 logEntryJson["EventId"] = logEntryID;
156
157 logEntryJson["Severity"] = message->messageSeverity;
158 logEntryJson["Message"] = std::move(msg);
159 logEntryJson["MessageId"] = messageID;
160 logEntryJson["MessageArgs"] = messageArgs;
161 logEntryJson["EventTimestamp"] = std::move(timestamp);
162 logEntryJson["Context"] = customText;
163 return 0;
164}
165
166} // namespace event_log
167
168} // namespace redfish