1*b80ba2e4SAlexander Hansen /* 2*b80ba2e4SAlexander Hansen Copyright (c) 2020 Intel Corporation 3*b80ba2e4SAlexander Hansen 4*b80ba2e4SAlexander Hansen Licensed under the Apache License, Version 2.0 (the "License"); 5*b80ba2e4SAlexander Hansen you may not use this file except in compliance with the License. 6*b80ba2e4SAlexander Hansen You may obtain a copy of the License at 7*b80ba2e4SAlexander Hansen 8*b80ba2e4SAlexander Hansen http://www.apache.org/licenses/LICENSE-2.0 9*b80ba2e4SAlexander Hansen 10*b80ba2e4SAlexander Hansen Unless required by applicable law or agreed to in writing, software 11*b80ba2e4SAlexander Hansen distributed under the License is distributed on an "AS IS" BASIS, 12*b80ba2e4SAlexander Hansen WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*b80ba2e4SAlexander Hansen See the License for the specific language governing permissions and 14*b80ba2e4SAlexander Hansen limitations under the License. 15*b80ba2e4SAlexander Hansen */ 16*b80ba2e4SAlexander Hansen #include "event_log.hpp" 17*b80ba2e4SAlexander Hansen 18*b80ba2e4SAlexander Hansen #include "event_service_manager.hpp" 19*b80ba2e4SAlexander Hansen #include "logging.hpp" 20*b80ba2e4SAlexander Hansen #include "registries.hpp" 21*b80ba2e4SAlexander Hansen #include "str_utility.hpp" 22*b80ba2e4SAlexander Hansen 23*b80ba2e4SAlexander Hansen #include <nlohmann/json.hpp> 24*b80ba2e4SAlexander Hansen 25*b80ba2e4SAlexander Hansen #include <cerrno> 26*b80ba2e4SAlexander Hansen #include <cstddef> 27*b80ba2e4SAlexander Hansen #include <ctime> 28*b80ba2e4SAlexander Hansen #include <iomanip> 29*b80ba2e4SAlexander Hansen #include <span> 30*b80ba2e4SAlexander Hansen #include <sstream> 31*b80ba2e4SAlexander Hansen #include <string> 32*b80ba2e4SAlexander Hansen #include <string_view> 33*b80ba2e4SAlexander Hansen #include <utility> 34*b80ba2e4SAlexander Hansen #include <vector> 35*b80ba2e4SAlexander Hansen 36*b80ba2e4SAlexander Hansen namespace redfish 37*b80ba2e4SAlexander Hansen { 38*b80ba2e4SAlexander Hansen 39*b80ba2e4SAlexander Hansen namespace event_log 40*b80ba2e4SAlexander Hansen { 41*b80ba2e4SAlexander Hansen 42*b80ba2e4SAlexander Hansen bool getUniqueEntryID(const std::string& logEntry, std::string& entryID) 43*b80ba2e4SAlexander Hansen { 44*b80ba2e4SAlexander Hansen static time_t prevTs = 0; 45*b80ba2e4SAlexander Hansen static int index = 0; 46*b80ba2e4SAlexander Hansen 47*b80ba2e4SAlexander Hansen // Get the entry timestamp 48*b80ba2e4SAlexander Hansen std::time_t curTs = 0; 49*b80ba2e4SAlexander Hansen std::tm timeStruct = {}; 50*b80ba2e4SAlexander Hansen std::istringstream entryStream(logEntry); 51*b80ba2e4SAlexander Hansen if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S")) 52*b80ba2e4SAlexander Hansen { 53*b80ba2e4SAlexander Hansen curTs = std::mktime(&timeStruct); 54*b80ba2e4SAlexander Hansen if (curTs == -1) 55*b80ba2e4SAlexander Hansen { 56*b80ba2e4SAlexander Hansen return false; 57*b80ba2e4SAlexander Hansen } 58*b80ba2e4SAlexander Hansen } 59*b80ba2e4SAlexander Hansen // If the timestamp isn't unique, increment the index 60*b80ba2e4SAlexander Hansen index = (curTs == prevTs) ? index + 1 : 0; 61*b80ba2e4SAlexander Hansen 62*b80ba2e4SAlexander Hansen // Save the timestamp 63*b80ba2e4SAlexander Hansen prevTs = curTs; 64*b80ba2e4SAlexander Hansen 65*b80ba2e4SAlexander Hansen entryID = std::to_string(curTs); 66*b80ba2e4SAlexander Hansen if (index > 0) 67*b80ba2e4SAlexander Hansen { 68*b80ba2e4SAlexander Hansen entryID += "_" + std::to_string(index); 69*b80ba2e4SAlexander Hansen } 70*b80ba2e4SAlexander Hansen return true; 71*b80ba2e4SAlexander Hansen } 72*b80ba2e4SAlexander Hansen 73*b80ba2e4SAlexander Hansen int getEventLogParams(const std::string& logEntry, std::string& timestamp, 74*b80ba2e4SAlexander Hansen std::string& messageID, 75*b80ba2e4SAlexander Hansen std::vector<std::string>& messageArgs) 76*b80ba2e4SAlexander Hansen { 77*b80ba2e4SAlexander Hansen // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>" 78*b80ba2e4SAlexander Hansen // First get the Timestamp 79*b80ba2e4SAlexander Hansen size_t space = logEntry.find_first_of(' '); 80*b80ba2e4SAlexander Hansen if (space == std::string::npos) 81*b80ba2e4SAlexander Hansen { 82*b80ba2e4SAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find first space: {}", 83*b80ba2e4SAlexander Hansen logEntry); 84*b80ba2e4SAlexander Hansen return -EINVAL; 85*b80ba2e4SAlexander Hansen } 86*b80ba2e4SAlexander Hansen timestamp = logEntry.substr(0, space); 87*b80ba2e4SAlexander Hansen // Then get the log contents 88*b80ba2e4SAlexander Hansen size_t entryStart = logEntry.find_first_not_of(' ', space); 89*b80ba2e4SAlexander Hansen if (entryStart == std::string::npos) 90*b80ba2e4SAlexander Hansen { 91*b80ba2e4SAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find log contents: {}", 92*b80ba2e4SAlexander Hansen logEntry); 93*b80ba2e4SAlexander Hansen return -EINVAL; 94*b80ba2e4SAlexander Hansen } 95*b80ba2e4SAlexander Hansen std::string_view entry(logEntry); 96*b80ba2e4SAlexander Hansen entry.remove_prefix(entryStart); 97*b80ba2e4SAlexander Hansen // Use split to separate the entry into its fields 98*b80ba2e4SAlexander Hansen std::vector<std::string> logEntryFields; 99*b80ba2e4SAlexander Hansen bmcweb::split(logEntryFields, entry, ','); 100*b80ba2e4SAlexander Hansen // We need at least a MessageId to be valid 101*b80ba2e4SAlexander Hansen if (logEntryFields.empty()) 102*b80ba2e4SAlexander Hansen { 103*b80ba2e4SAlexander Hansen BMCWEB_LOG_ERROR("EventLog Params: could not find entry fields: {}", 104*b80ba2e4SAlexander Hansen logEntry); 105*b80ba2e4SAlexander Hansen return -EINVAL; 106*b80ba2e4SAlexander Hansen } 107*b80ba2e4SAlexander Hansen messageID = logEntryFields[0]; 108*b80ba2e4SAlexander Hansen 109*b80ba2e4SAlexander Hansen // Get the MessageArgs from the log if there are any 110*b80ba2e4SAlexander Hansen if (logEntryFields.size() > 1) 111*b80ba2e4SAlexander Hansen { 112*b80ba2e4SAlexander Hansen const std::string& messageArgsStart = logEntryFields[1]; 113*b80ba2e4SAlexander Hansen // If the first string is empty, assume there are no MessageArgs 114*b80ba2e4SAlexander Hansen if (!messageArgsStart.empty()) 115*b80ba2e4SAlexander Hansen { 116*b80ba2e4SAlexander Hansen messageArgs.assign(logEntryFields.begin() + 1, 117*b80ba2e4SAlexander Hansen logEntryFields.end()); 118*b80ba2e4SAlexander Hansen } 119*b80ba2e4SAlexander Hansen } 120*b80ba2e4SAlexander Hansen 121*b80ba2e4SAlexander Hansen return 0; 122*b80ba2e4SAlexander Hansen } 123*b80ba2e4SAlexander Hansen 124*b80ba2e4SAlexander Hansen int formatEventLogEntry( 125*b80ba2e4SAlexander Hansen const std::string& logEntryID, const std::string& messageID, 126*b80ba2e4SAlexander Hansen const std::span<std::string_view> messageArgs, std::string timestamp, 127*b80ba2e4SAlexander Hansen const std::string& customText, nlohmann::json::object_t& logEntryJson) 128*b80ba2e4SAlexander Hansen { 129*b80ba2e4SAlexander Hansen // Get the Message from the MessageRegistry 130*b80ba2e4SAlexander Hansen const registries::Message* message = registries::formatMessage(messageID); 131*b80ba2e4SAlexander Hansen 132*b80ba2e4SAlexander Hansen if (message == nullptr) 133*b80ba2e4SAlexander Hansen { 134*b80ba2e4SAlexander Hansen return -1; 135*b80ba2e4SAlexander Hansen } 136*b80ba2e4SAlexander Hansen 137*b80ba2e4SAlexander Hansen std::string msg = 138*b80ba2e4SAlexander Hansen redfish::registries::fillMessageArgs(messageArgs, message->message); 139*b80ba2e4SAlexander Hansen if (msg.empty()) 140*b80ba2e4SAlexander Hansen { 141*b80ba2e4SAlexander Hansen return -1; 142*b80ba2e4SAlexander Hansen } 143*b80ba2e4SAlexander Hansen 144*b80ba2e4SAlexander Hansen // Get the Created time from the timestamp. The log timestamp is in 145*b80ba2e4SAlexander Hansen // RFC3339 format which matches the Redfish format except for the 146*b80ba2e4SAlexander Hansen // fractional seconds between the '.' and the '+', so just remove them. 147*b80ba2e4SAlexander Hansen std::size_t dot = timestamp.find_first_of('.'); 148*b80ba2e4SAlexander Hansen std::size_t plus = timestamp.find_first_of('+', dot); 149*b80ba2e4SAlexander Hansen if (dot != std::string::npos && plus != std::string::npos) 150*b80ba2e4SAlexander Hansen { 151*b80ba2e4SAlexander Hansen timestamp.erase(dot, plus - dot); 152*b80ba2e4SAlexander Hansen } 153*b80ba2e4SAlexander Hansen 154*b80ba2e4SAlexander Hansen // Fill in the log entry with the gathered data 155*b80ba2e4SAlexander Hansen logEntryJson["EventId"] = logEntryID; 156*b80ba2e4SAlexander Hansen 157*b80ba2e4SAlexander Hansen logEntryJson["Severity"] = message->messageSeverity; 158*b80ba2e4SAlexander Hansen logEntryJson["Message"] = std::move(msg); 159*b80ba2e4SAlexander Hansen logEntryJson["MessageId"] = messageID; 160*b80ba2e4SAlexander Hansen logEntryJson["MessageArgs"] = messageArgs; 161*b80ba2e4SAlexander Hansen logEntryJson["EventTimestamp"] = std::move(timestamp); 162*b80ba2e4SAlexander Hansen logEntryJson["Context"] = customText; 163*b80ba2e4SAlexander Hansen return 0; 164*b80ba2e4SAlexander Hansen } 165*b80ba2e4SAlexander Hansen 166*b80ba2e4SAlexander Hansen } // namespace event_log 167*b80ba2e4SAlexander Hansen 168*b80ba2e4SAlexander Hansen } // namespace redfish 169