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