// 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 #include #include #include #include #include #include #include #include #include #include #include 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& messageArgs) { // The redfish log format is " ," // 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 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 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