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