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 return -1; 134 } 135 136 std::string msg = 137 redfish::registries::fillMessageArgs(messageArgs, message->message); 138 if (msg.empty()) 139 { 140 return -1; 141 } 142 143 // Get the Created time from the timestamp. The log timestamp is in 144 // RFC3339 format which matches the Redfish format except for the 145 // fractional seconds between the '.' and the '+', so just remove them. 146 std::size_t dot = timestamp.find_first_of('.'); 147 std::size_t plus = timestamp.find_first_of('+', dot); 148 if (dot != std::string::npos && plus != std::string::npos) 149 { 150 timestamp.erase(dot, plus - dot); 151 } 152 153 // Fill in the log entry with the gathered data 154 logEntryJson["EventId"] = logEntryID; 155 156 logEntryJson["Severity"] = message->messageSeverity; 157 logEntryJson["Message"] = std::move(msg); 158 logEntryJson["MessageId"] = messageID; 159 logEntryJson["MessageArgs"] = messageArgs; 160 logEntryJson["EventTimestamp"] = std::move(timestamp); 161 logEntryJson["Context"] = customText; 162 return 0; 163 } 164 165 } // namespace event_log 166 167 } // namespace redfish 168