1 /* 2 Copyright (c) 2020 Intel Corporation 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 #include "event_log.hpp" 17 18 #include "logging.hpp" 19 #include "registries.hpp" 20 #include "str_utility.hpp" 21 22 #include <nlohmann/json.hpp> 23 24 #include <cerrno> 25 #include <cstddef> 26 #include <cstdint> 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 36 namespace redfish 37 { 38 39 namespace event_log 40 { 41 42 bool 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 73 int 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 124 int formatEventLogEntry(uint64_t eventId, const std::string& logEntryID, 125 const std::string& messageID, 126 const std::span<std::string_view> messageArgs, 127 std::string timestamp, const std::string& customText, 128 nlohmann::json::object_t& logEntryJson) 129 { 130 // Get the Message from the MessageRegistry 131 const registries::Message* message = registries::getMessage(messageID); 132 133 if (message == nullptr) 134 { 135 BMCWEB_LOG_DEBUG( 136 "{}: could not find messageID '{}' for log entry {} in registry", 137 __func__, messageID, logEntryID); 138 return -1; 139 } 140 141 std::string msg = 142 redfish::registries::fillMessageArgs(messageArgs, message->message); 143 if (msg.empty()) 144 { 145 BMCWEB_LOG_DEBUG("{}: message is empty after filling fillMessageArgs", 146 __func__); 147 return -1; 148 } 149 150 // Get the Created time from the timestamp. The log timestamp is in 151 // RFC3339 format which matches the Redfish format except for the 152 // fractional seconds between the '.' and the '+', so just remove them. 153 std::size_t dot = timestamp.find_first_of('.'); 154 std::size_t plus = timestamp.find_first_of('+', dot); 155 if (dot != std::string::npos && plus != std::string::npos) 156 { 157 timestamp.erase(dot, plus - dot); 158 } 159 160 // Fill in the log entry with the gathered data 161 logEntryJson["EventId"] = std::to_string(eventId); 162 163 logEntryJson["Severity"] = message->messageSeverity; 164 logEntryJson["Message"] = std::move(msg); 165 logEntryJson["MessageId"] = messageID; 166 logEntryJson["MessageArgs"] = messageArgs; 167 logEntryJson["EventTimestamp"] = std::move(timestamp); 168 logEntryJson["Context"] = customText; 169 return 0; 170 } 171 172 } // namespace event_log 173 174 } // namespace redfish 175