blob: 23af3feae4a47d8ac29244b507ef5d89695867bd [file] [log] [blame]
Gunnar Mills5ffbc9a2025-04-24 08:41:49 -05001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
3// SPDX-FileCopyrightText: Copyright 2020 Intel Corporation
Alexander Hansenb80ba2e2024-11-18 12:24:35 +01004#include "event_log.hpp"
5
Alexander Hansenb80ba2e2024-11-18 12:24:35 +01006#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>
Ed Tanous4a19a7b2025-01-27 10:44:15 -080014#include <cstdint>
Alexander Hansenb80ba2e2024-11-18 12:24:35 +010015#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
24namespace redfish
25{
26
27namespace event_log
28{
29
30bool 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
61int 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
Ed Tanous4a19a7b2025-01-27 10:44:15 -0800112int 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)
Alexander Hansenb80ba2e2024-11-18 12:24:35 +0100117{
118 // Get the Message from the MessageRegistry
Alexander Hansend109e2b2024-11-18 14:38:06 +0100119 const registries::Message* message = registries::getMessage(messageID);
Alexander Hansenb80ba2e2024-11-18 12:24:35 +0100120
121 if (message == nullptr)
122 {
Igor Kanyuka0309c212025-01-10 03:38:25 -0800123 BMCWEB_LOG_DEBUG(
124 "{}: could not find messageID '{}' for log entry {} in registry",
125 __func__, messageID, logEntryID);
Alexander Hansenb80ba2e2024-11-18 12:24:35 +0100126 return -1;
127 }
128
129 std::string msg =
130 redfish::registries::fillMessageArgs(messageArgs, message->message);
131 if (msg.empty())
132 {
Igor Kanyuka0309c212025-01-10 03:38:25 -0800133 BMCWEB_LOG_DEBUG("{}: message is empty after filling fillMessageArgs",
134 __func__);
Alexander Hansenb80ba2e2024-11-18 12:24:35 +0100135 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
Ed Tanous4a19a7b2025-01-27 10:44:15 -0800149 logEntryJson["EventId"] = std::to_string(eventId);
Alexander Hansenb80ba2e2024-11-18 12:24:35 +0100150
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