xref: /openbmc/bmcweb/redfish-core/include/utils/journal_utils.hpp (revision d78572018fc2022091ff8b8eb5a7fef2172ba3d6)
1 #pragma once
2 
3 #include "bmcweb_config.h"
4 
5 #include "generated/enums/log_entry.hpp"
6 #include "logging.hpp"
7 #include "utility.hpp"
8 #include "utils/time_utils.hpp"
9 
10 #include <systemd/sd-journal.h>
11 
12 #include <nlohmann/json.hpp>
13 
14 #include <algorithm>
15 #include <charconv>
16 #include <cstddef>
17 #include <cstdint>
18 #include <format>
19 #include <memory>
20 #include <string>
21 #include <string_view>
22 #include <utility>
23 
24 namespace redfish
25 {
26 
getJournalMetadata(sd_journal * journal,const char * field,std::string_view & contents)27 inline int getJournalMetadata(sd_journal* journal, const char* field,
28                               std::string_view& contents)
29 {
30     const char* data = nullptr;
31     size_t length = 0;
32     int ret = 0;
33     // Get the metadata from the requested field of the journal entry
34     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
35     const void** dataVoid = reinterpret_cast<const void**>(&data);
36 
37     ret = sd_journal_get_data(journal, field, dataVoid, &length);
38     if (ret < 0)
39     {
40         return ret;
41     }
42     contents = std::string_view(data, length);
43     // Only use the content after the "=" character.
44     contents.remove_prefix(std::min(contents.find('=') + 1, contents.size()));
45     return ret;
46 }
47 
getJournalMetadataInt(sd_journal * journal,const char * field,long int & contents)48 inline int getJournalMetadataInt(sd_journal* journal, const char* field,
49                                  long int& contents)
50 {
51     std::string_view metadata;
52     // Get the metadata from the requested field of the journal entry
53     int ret = getJournalMetadata(journal, field, metadata);
54     if (ret < 0)
55     {
56         return ret;
57     }
58     std::from_chars_result res =
59         std::from_chars(&*metadata.begin(), &*metadata.end(), contents);
60     if (res.ec != std::error_code{} || res.ptr != &*metadata.end())
61     {
62         return -1;
63     }
64     return 0;
65 }
66 
getEntryTimestamp(sd_journal * journal,std::string & entryTimestamp)67 inline bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp)
68 {
69     int ret = 0;
70     uint64_t timestamp = 0;
71     ret = sd_journal_get_realtime_usec(journal, &timestamp);
72     if (ret < 0)
73     {
74         BMCWEB_LOG_ERROR("Failed to read entry timestamp: {}", ret);
75         return false;
76     }
77     entryTimestamp = redfish::time_utils::getDateTimeUintUs(timestamp);
78     return true;
79 }
80 
fillBMCJournalLogEntryJson(sd_journal * journal,nlohmann::json::object_t & bmcJournalLogEntryJson)81 inline bool fillBMCJournalLogEntryJson(
82     sd_journal* journal, nlohmann::json::object_t& bmcJournalLogEntryJson)
83 {
84     char* cursor = nullptr;
85     int ret = sd_journal_get_cursor(journal, &cursor);
86     if (ret < 0)
87     {
88         return false;
89     }
90     std::unique_ptr<char*> cursorptr = std::make_unique<char*>(cursor);
91     std::string bmcJournalLogEntryID(cursor);
92 
93     // Get the Log Entry contents
94     std::string message;
95     std::string_view syslogID;
96     ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
97     if (ret < 0)
98     {
99         BMCWEB_LOG_DEBUG("Failed to read SYSLOG_IDENTIFIER field: {}", ret);
100     }
101     if (!syslogID.empty())
102     {
103         message += std::string(syslogID) + ": ";
104     }
105 
106     std::string_view msg;
107     ret = getJournalMetadata(journal, "MESSAGE", msg);
108     if (ret < 0)
109     {
110         BMCWEB_LOG_ERROR("Failed to read MESSAGE field: {}", ret);
111         return false;
112     }
113     message += std::string(msg);
114 
115     // Get the severity from the PRIORITY field
116     long int severity = 8; // Default to an invalid priority
117     ret = getJournalMetadataInt(journal, "PRIORITY", severity);
118     if (ret < 0)
119     {
120         BMCWEB_LOG_DEBUG("Failed to read PRIORITY field: {}", ret);
121     }
122 
123     // Get the Created time from the timestamp
124     std::string entryTimeStr;
125     if (!getEntryTimestamp(journal, entryTimeStr))
126     {
127         return false;
128     }
129 
130     // Fill in the log entry with the gathered data
131     bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
132 
133     std::string entryIdBase64 =
134         crow::utility::base64encode(bmcJournalLogEntryID);
135 
136     bmcJournalLogEntryJson["@odata.id"] = boost_swap_impl::format(
137         "/redfish/v1/Managers/{}/LogServices/Journal/Entries/{}",
138         BMCWEB_REDFISH_MANAGER_URI_NAME, entryIdBase64);
139     bmcJournalLogEntryJson["Name"] = "BMC Journal Entry";
140     bmcJournalLogEntryJson["Id"] = entryIdBase64;
141     bmcJournalLogEntryJson["Message"] = std::move(message);
142     bmcJournalLogEntryJson["EntryType"] = log_entry::LogEntryType::Oem;
143     log_entry::EventSeverity rfseverity = log_entry::EventSeverity::OK;
144     if (severity <= 2)
145     {
146         rfseverity = log_entry::EventSeverity::Critical;
147     }
148     else if (severity <= 4)
149     {
150         rfseverity = log_entry::EventSeverity::Warning;
151     }
152 
153     bmcJournalLogEntryJson["Severity"] = rfseverity;
154     bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry";
155     bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr);
156     return true;
157 }
158 
159 } // namespace redfish
160