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 <ctime> 27 #include <iomanip> 28 #include <span> 29 #include <sstream> 30 #include <string> 31 #include <string_view> 32 #include <utility> 33 #include <vector> 34 35 namespace redfish 36 { 37 38 namespace event_log 39 { 40 41 bool getUniqueEntryID(const std::string& logEntry, std::string& entryID) 42 { 43 static time_t prevTs = 0; 44 static int index = 0; 45 46 // Get the entry timestamp 47 std::time_t curTs = 0; 48 std::tm timeStruct = {}; 49 std::istringstream entryStream(logEntry); 50 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 51 { 52 curTs = std::mktime(&timeStruct); 53 if (curTs == -1) 54 { 55 return false; 56 } 57 } 58 // If the timestamp isn't unique, increment the index 59 index = (curTs == prevTs) ? index + 1 : 0; 60 61 // Save the timestamp 62 prevTs = curTs; 63 64 entryID = std::to_string(curTs); 65 if (index > 0) 66 { 67 entryID += "_" + std::to_string(index); 68 } 69 return true; 70 } 71 72 int getEventLogParams(const std::string& logEntry, std::string& timestamp, 73 std::string& messageID, 74 std::vector<std::string>& messageArgs) 75 { 76 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" 77 // First get the Timestamp 78 size_t space = logEntry.find_first_of(' '); 79 if (space == std::string::npos) 80 { 81 BMCWEB_LOG_ERROR("EventLog Params: could not find first space: {}", 82 logEntry); 83 return -EINVAL; 84 } 85 timestamp = logEntry.substr(0, space); 86 // Then get the log contents 87 size_t entryStart = logEntry.find_first_not_of(' ', space); 88 if (entryStart == std::string::npos) 89 { 90 BMCWEB_LOG_ERROR("EventLog Params: could not find log contents: {}", 91 logEntry); 92 return -EINVAL; 93 } 94 std::string_view entry(logEntry); 95 entry.remove_prefix(entryStart); 96 // Use split to separate the entry into its fields 97 std::vector<std::string> logEntryFields; 98 bmcweb::split(logEntryFields, entry, ','); 99 // We need at least a MessageId to be valid 100 if (logEntryFields.empty()) 101 { 102 BMCWEB_LOG_ERROR("EventLog Params: could not find entry fields: {}", 103 logEntry); 104 return -EINVAL; 105 } 106 messageID = logEntryFields[0]; 107 108 // Get the MessageArgs from the log if there are any 109 if (logEntryFields.size() > 1) 110 { 111 const std::string& messageArgsStart = logEntryFields[1]; 112 // If the first string is empty, assume there are no MessageArgs 113 if (!messageArgsStart.empty()) 114 { 115 messageArgs.assign(logEntryFields.begin() + 1, 116 logEntryFields.end()); 117 } 118 } 119 120 return 0; 121 } 122 123 int formatEventLogEntry( 124 const std::string& logEntryID, const std::string& messageID, 125 const std::span<std::string_view> messageArgs, std::string timestamp, 126 const std::string& customText, nlohmann::json::object_t& logEntryJson) 127 { 128 // Get the Message from the MessageRegistry 129 const registries::Message* message = registries::getMessage(messageID); 130 131 if (message == nullptr) 132 { 133 BMCWEB_LOG_DEBUG( 134 "{}: could not find messageID '{}' for log entry {} in registry", 135 __func__, messageID, logEntryID); 136 return -1; 137 } 138 139 std::string msg = 140 redfish::registries::fillMessageArgs(messageArgs, message->message); 141 if (msg.empty()) 142 { 143 BMCWEB_LOG_DEBUG("{}: message is empty after filling fillMessageArgs", 144 __func__); 145 return -1; 146 } 147 148 // Get the Created time from the timestamp. The log timestamp is in 149 // RFC3339 format which matches the Redfish format except for the 150 // fractional seconds between the '.' and the '+', so just remove them. 151 std::size_t dot = timestamp.find_first_of('.'); 152 std::size_t plus = timestamp.find_first_of('+', dot); 153 if (dot != std::string::npos && plus != std::string::npos) 154 { 155 timestamp.erase(dot, plus - dot); 156 } 157 158 // Fill in the log entry with the gathered data 159 logEntryJson["EventId"] = logEntryID; 160 161 logEntryJson["Severity"] = message->messageSeverity; 162 logEntryJson["Message"] = std::move(msg); 163 logEntryJson["MessageId"] = messageID; 164 logEntryJson["MessageArgs"] = messageArgs; 165 logEntryJson["EventTimestamp"] = std::move(timestamp); 166 logEntryJson["Context"] = customText; 167 return 0; 168 } 169 170 } // namespace event_log 171 172 } // namespace redfish 173