1*5ffbc9aeSGunnar Mills // SPDX-License-Identifier: Apache-2.0 2*5ffbc9aeSGunnar Mills // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3*5ffbc9aeSGunnar Mills // SPDX-FileCopyrightText: Copyright 2020 Intel Corporation 4b80ba2e4SAlexander Hansen #include "event_log.hpp" 5b80ba2e4SAlexander Hansen 6b80ba2e4SAlexander Hansen #include "logging.hpp" 7b80ba2e4SAlexander Hansen #include "registries.hpp" 8b80ba2e4SAlexander Hansen #include "str_utility.hpp" 9b80ba2e4SAlexander Hansen 10b80ba2e4SAlexander Hansen #include <nlohmann/json.hpp> 11b80ba2e4SAlexander Hansen 12b80ba2e4SAlexander Hansen #include <cerrno> 13b80ba2e4SAlexander Hansen #include <cstddef> 144a19a7b5SEd Tanous #include <cstdint> 15b80ba2e4SAlexander Hansen #include <ctime> 16b80ba2e4SAlexander Hansen #include <iomanip> 17b80ba2e4SAlexander Hansen #include <span> 18b80ba2e4SAlexander Hansen #include <sstream> 19b80ba2e4SAlexander Hansen #include <string> 20b80ba2e4SAlexander Hansen #include <string_view> 21b80ba2e4SAlexander Hansen #include <utility> 22b80ba2e4SAlexander Hansen #include <vector> 23b80ba2e4SAlexander Hansen 24b80ba2e4SAlexander Hansen namespace redfish 25b80ba2e4SAlexander Hansen { 26b80ba2e4SAlexander Hansen 27b80ba2e4SAlexander Hansen namespace event_log 28b80ba2e4SAlexander Hansen { 29b80ba2e4SAlexander Hansen 30b80ba2e4SAlexander Hansen bool getUniqueEntryID(const std::string& logEntry, std::string& entryID) 31b80ba2e4SAlexander Hansen { 32b80ba2e4SAlexander Hansen static time_t prevTs = 0; 33b80ba2e4SAlexander Hansen static int index = 0; 34b80ba2e4SAlexander Hansen 35b80ba2e4SAlexander Hansen // Get the entry timestamp 36b80ba2e4SAlexander Hansen std::time_t curTs = 0; 37b80ba2e4SAlexander Hansen std::tm timeStruct = {}; 38b80ba2e4SAlexander Hansen std::istringstream entryStream(logEntry); 39b80ba2e4SAlexander Hansen if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 40b80ba2e4SAlexander Hansen { 41b80ba2e4SAlexander Hansen curTs = std::mktime(&timeStruct); 42b80ba2e4SAlexander Hansen if (curTs == -1) 43b80ba2e4SAlexander Hansen { 44b80ba2e4SAlexander Hansen return false; 45b80ba2e4SAlexander Hansen } 46b80ba2e4SAlexander Hansen } 47b80ba2e4SAlexander Hansen // If the timestamp isn't unique, increment the index 48b80ba2e4SAlexander Hansen index = (curTs == prevTs) ? index + 1 : 0; 49b80ba2e4SAlexander Hansen 50b80ba2e4SAlexander Hansen // Save the timestamp 51b80ba2e4SAlexander Hansen prevTs = curTs; 52b80ba2e4SAlexander Hansen 53b80ba2e4SAlexander Hansen entryID = std::to_string(curTs); 54b80ba2e4SAlexander Hansen if (index > 0) 55b80ba2e4SAlexander Hansen { 56b80ba2e4SAlexander Hansen entryID += "_" + std::to_string(index); 57b80ba2e4SAlexander Hansen } 58b80ba2e4SAlexander Hansen return true; 59b80ba2e4SAlexander Hansen } 60b80ba2e4SAlexander Hansen 61b80ba2e4SAlexander Hansen int getEventLogParams(const std::string& logEntry, std::string& timestamp, 62b80ba2e4SAlexander Hansen std::string& messageID, 63b80ba2e4SAlexander Hansen std::vector<std::string>& messageArgs) 64b80ba2e4SAlexander Hansen { 65b80ba2e4SAlexander Hansen // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" 66b80ba2e4SAlexander Hansen // First get the Timestamp 67b80ba2e4SAlexander Hansen size_t space = logEntry.find_first_of(' '); 68b80ba2e4SAlexander Hansen if (space == std::string::npos) 69b80ba2e4SAlexander Hansen { 70b80ba2e4SAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find first space: {}", 71b80ba2e4SAlexander Hansen logEntry); 72b80ba2e4SAlexander Hansen return -EINVAL; 73b80ba2e4SAlexander Hansen } 74b80ba2e4SAlexander Hansen timestamp = logEntry.substr(0, space); 75b80ba2e4SAlexander Hansen // Then get the log contents 76b80ba2e4SAlexander Hansen size_t entryStart = logEntry.find_first_not_of(' ', space); 77b80ba2e4SAlexander Hansen if (entryStart == std::string::npos) 78b80ba2e4SAlexander Hansen { 79b80ba2e4SAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find log contents: {}", 80b80ba2e4SAlexander Hansen logEntry); 81b80ba2e4SAlexander Hansen return -EINVAL; 82b80ba2e4SAlexander Hansen } 83b80ba2e4SAlexander Hansen std::string_view entry(logEntry); 84b80ba2e4SAlexander Hansen entry.remove_prefix(entryStart); 85b80ba2e4SAlexander Hansen // Use split to separate the entry into its fields 86b80ba2e4SAlexander Hansen std::vector<std::string> logEntryFields; 87b80ba2e4SAlexander Hansen bmcweb::split(logEntryFields, entry, ','); 88b80ba2e4SAlexander Hansen // We need at least a MessageId to be valid 89b80ba2e4SAlexander Hansen if (logEntryFields.empty()) 90b80ba2e4SAlexander Hansen { 91b80ba2e4SAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find entry fields: {}", 92b80ba2e4SAlexander Hansen logEntry); 93b80ba2e4SAlexander Hansen return -EINVAL; 94b80ba2e4SAlexander Hansen } 95b80ba2e4SAlexander Hansen messageID = logEntryFields[0]; 96b80ba2e4SAlexander Hansen 97b80ba2e4SAlexander Hansen // Get the MessageArgs from the log if there are any 98b80ba2e4SAlexander Hansen if (logEntryFields.size() > 1) 99b80ba2e4SAlexander Hansen { 100b80ba2e4SAlexander Hansen const std::string& messageArgsStart = logEntryFields[1]; 101b80ba2e4SAlexander Hansen // If the first string is empty, assume there are no MessageArgs 102b80ba2e4SAlexander Hansen if (!messageArgsStart.empty()) 103b80ba2e4SAlexander Hansen { 104b80ba2e4SAlexander Hansen messageArgs.assign(logEntryFields.begin() + 1, 105b80ba2e4SAlexander Hansen logEntryFields.end()); 106b80ba2e4SAlexander Hansen } 107b80ba2e4SAlexander Hansen } 108b80ba2e4SAlexander Hansen 109b80ba2e4SAlexander Hansen return 0; 110b80ba2e4SAlexander Hansen } 111b80ba2e4SAlexander Hansen 1124a19a7b5SEd Tanous int formatEventLogEntry(uint64_t eventId, const std::string& logEntryID, 1134a19a7b5SEd Tanous const std::string& messageID, 1144a19a7b5SEd Tanous const std::span<std::string_view> messageArgs, 1154a19a7b5SEd Tanous std::string timestamp, const std::string& customText, 1164a19a7b5SEd Tanous nlohmann::json::object_t& logEntryJson) 117b80ba2e4SAlexander Hansen { 118b80ba2e4SAlexander Hansen // Get the Message from the MessageRegistry 119d109e2b6SAlexander Hansen const registries::Message* message = registries::getMessage(messageID); 120b80ba2e4SAlexander Hansen 121b80ba2e4SAlexander Hansen if (message == nullptr) 122b80ba2e4SAlexander Hansen { 1230309c216SIgor Kanyuka BMCWEB_LOG_DEBUG( 1240309c216SIgor Kanyuka "{}: could not find messageID '{}' for log entry {} in registry", 1250309c216SIgor Kanyuka __func__, messageID, logEntryID); 126b80ba2e4SAlexander Hansen return -1; 127b80ba2e4SAlexander Hansen } 128b80ba2e4SAlexander Hansen 129b80ba2e4SAlexander Hansen std::string msg = 130b80ba2e4SAlexander Hansen redfish::registries::fillMessageArgs(messageArgs, message->message); 131b80ba2e4SAlexander Hansen if (msg.empty()) 132b80ba2e4SAlexander Hansen { 1330309c216SIgor Kanyuka BMCWEB_LOG_DEBUG("{}: message is empty after filling fillMessageArgs", 1340309c216SIgor Kanyuka __func__); 135b80ba2e4SAlexander Hansen return -1; 136b80ba2e4SAlexander Hansen } 137b80ba2e4SAlexander Hansen 138b80ba2e4SAlexander Hansen // Get the Created time from the timestamp. The log timestamp is in 139b80ba2e4SAlexander Hansen // RFC3339 format which matches the Redfish format except for the 140b80ba2e4SAlexander Hansen // fractional seconds between the '.' and the '+', so just remove them. 141b80ba2e4SAlexander Hansen std::size_t dot = timestamp.find_first_of('.'); 142b80ba2e4SAlexander Hansen std::size_t plus = timestamp.find_first_of('+', dot); 143b80ba2e4SAlexander Hansen if (dot != std::string::npos && plus != std::string::npos) 144b80ba2e4SAlexander Hansen { 145b80ba2e4SAlexander Hansen timestamp.erase(dot, plus - dot); 146b80ba2e4SAlexander Hansen } 147b80ba2e4SAlexander Hansen 148b80ba2e4SAlexander Hansen // Fill in the log entry with the gathered data 1494a19a7b5SEd Tanous logEntryJson["EventId"] = std::to_string(eventId); 150b80ba2e4SAlexander Hansen 151b80ba2e4SAlexander Hansen logEntryJson["Severity"] = message->messageSeverity; 152b80ba2e4SAlexander Hansen logEntryJson["Message"] = std::move(msg); 153b80ba2e4SAlexander Hansen logEntryJson["MessageId"] = messageID; 154b80ba2e4SAlexander Hansen logEntryJson["MessageArgs"] = messageArgs; 155b80ba2e4SAlexander Hansen logEntryJson["EventTimestamp"] = std::move(timestamp); 156b80ba2e4SAlexander Hansen logEntryJson["Context"] = customText; 157b80ba2e4SAlexander Hansen return 0; 158b80ba2e4SAlexander Hansen } 159b80ba2e4SAlexander Hansen 160b80ba2e4SAlexander Hansen } // namespace event_log 161b80ba2e4SAlexander Hansen 162b80ba2e4SAlexander Hansen } // namespace redfish 163