| // SPDX-License-Identifier: Apache-2.0 | 
 | // SPDX-FileCopyrightText: Copyright OpenBMC Authors | 
 | // SPDX-FileCopyrightText: Copyright 2020 Intel Corporation | 
 | #include "event_log.hpp" | 
 |  | 
 | #include "logging.hpp" | 
 | #include "registries.hpp" | 
 | #include "str_utility.hpp" | 
 |  | 
 | #include <nlohmann/json.hpp> | 
 |  | 
 | #include <cerrno> | 
 | #include <cstddef> | 
 | #include <cstdint> | 
 | #include <ctime> | 
 | #include <iomanip> | 
 | #include <span> | 
 | #include <sstream> | 
 | #include <string> | 
 | #include <string_view> | 
 | #include <utility> | 
 | #include <vector> | 
 |  | 
 | namespace redfish | 
 | { | 
 |  | 
 | namespace event_log | 
 | { | 
 |  | 
 | bool getUniqueEntryID(const std::string& logEntry, std::string& entryID) | 
 | { | 
 |     static time_t prevTs = 0; | 
 |     static int index = 0; | 
 |  | 
 |     // Get the entry timestamp | 
 |     std::time_t curTs = 0; | 
 |     std::tm timeStruct = {}; | 
 |     std::istringstream entryStream(logEntry); | 
 |     if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) | 
 |     { | 
 |         curTs = std::mktime(&timeStruct); | 
 |         if (curTs == -1) | 
 |         { | 
 |             return false; | 
 |         } | 
 |     } | 
 |     // If the timestamp isn't unique, increment the index | 
 |     index = (curTs == prevTs) ? index + 1 : 0; | 
 |  | 
 |     // Save the timestamp | 
 |     prevTs = curTs; | 
 |  | 
 |     entryID = std::to_string(curTs); | 
 |     if (index > 0) | 
 |     { | 
 |         entryID += "_" + std::to_string(index); | 
 |     } | 
 |     return true; | 
 | } | 
 |  | 
 | int getEventLogParams(const std::string& logEntry, std::string& timestamp, | 
 |                       std::string& messageID, | 
 |                       std::vector<std::string>& messageArgs) | 
 | { | 
 |     // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" | 
 |     // First get the Timestamp | 
 |     size_t space = logEntry.find_first_of(' '); | 
 |     if (space == std::string::npos) | 
 |     { | 
 |         BMCWEB_LOG_ERROR("EventLog Params: could not find first space: {}", | 
 |                          logEntry); | 
 |         return -EINVAL; | 
 |     } | 
 |     timestamp = logEntry.substr(0, space); | 
 |     // Then get the log contents | 
 |     size_t entryStart = logEntry.find_first_not_of(' ', space); | 
 |     if (entryStart == std::string::npos) | 
 |     { | 
 |         BMCWEB_LOG_ERROR("EventLog Params: could not find log contents: {}", | 
 |                          logEntry); | 
 |         return -EINVAL; | 
 |     } | 
 |     std::string_view entry(logEntry); | 
 |     entry.remove_prefix(entryStart); | 
 |     // Use split to separate the entry into its fields | 
 |     std::vector<std::string> logEntryFields; | 
 |     bmcweb::split(logEntryFields, entry, ','); | 
 |     // We need at least a MessageId to be valid | 
 |     if (logEntryFields.empty()) | 
 |     { | 
 |         BMCWEB_LOG_ERROR("EventLog Params: could not find entry fields: {}", | 
 |                          logEntry); | 
 |         return -EINVAL; | 
 |     } | 
 |     messageID = logEntryFields[0]; | 
 |  | 
 |     // Get the MessageArgs from the log if there are any | 
 |     if (logEntryFields.size() > 1) | 
 |     { | 
 |         const std::string& messageArgsStart = logEntryFields[1]; | 
 |         // If the first string is empty, assume there are no MessageArgs | 
 |         if (!messageArgsStart.empty()) | 
 |         { | 
 |             messageArgs.assign(logEntryFields.begin() + 1, | 
 |                                logEntryFields.end()); | 
 |         } | 
 |     } | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | int 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) | 
 | { | 
 |     // Get the Message from the MessageRegistry | 
 |     const registries::Message* message = registries::getMessage(messageID); | 
 |  | 
 |     if (message == nullptr) | 
 |     { | 
 |         BMCWEB_LOG_DEBUG( | 
 |             "{}: could not find messageID '{}' for log entry {} in registry", | 
 |             __func__, messageID, logEntryID); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     std::string msg = | 
 |         redfish::registries::fillMessageArgs(messageArgs, message->message); | 
 |     if (msg.empty()) | 
 |     { | 
 |         BMCWEB_LOG_DEBUG("{}: message is empty after filling fillMessageArgs", | 
 |                          __func__); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     // Get the Created time from the timestamp. The log timestamp is in | 
 |     // RFC3339 format which matches the Redfish format except for the | 
 |     // fractional seconds between the '.' and the '+', so just remove them. | 
 |     std::size_t dot = timestamp.find_first_of('.'); | 
 |     std::size_t plus = timestamp.find_first_of('+', dot); | 
 |     if (dot != std::string::npos && plus != std::string::npos) | 
 |     { | 
 |         timestamp.erase(dot, plus - dot); | 
 |     } | 
 |  | 
 |     // Fill in the log entry with the gathered data | 
 |     logEntryJson["EventId"] = std::to_string(eventId); | 
 |  | 
 |     logEntryJson["Severity"] = message->messageSeverity; | 
 |     logEntryJson["Message"] = std::move(msg); | 
 |     logEntryJson["MessageId"] = messageID; | 
 |     logEntryJson["MessageArgs"] = messageArgs; | 
 |     logEntryJson["EventTimestamp"] = std::move(timestamp); | 
 |     logEntryJson["Context"] = customText; | 
 |     return 0; | 
 | } | 
 |  | 
 | } // namespace event_log | 
 |  | 
 | } // namespace redfish |