|  | #define SD_JOURNAL_SUPPRESS_LOCATION | 
|  |  | 
|  | #include <systemd/sd-journal.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <bitset> | 
|  | #include <cstdarg> | 
|  | #include <cstdio> | 
|  | #include <iostream> | 
|  | #include <phosphor-logging/lg2.hpp> | 
|  | #include <vector> | 
|  |  | 
|  | namespace lg2::details | 
|  | { | 
|  | /** Convert unsigned to string using format flags. */ | 
|  | static std::string value_to_string(uint64_t f, uint64_t v) | 
|  | { | 
|  | switch (f & (hex | bin | dec).value) | 
|  | { | 
|  | // For binary, use bitset<>::to_string. | 
|  | // Treat values without a field-length format flag as 64 bit. | 
|  | case bin.value: | 
|  | { | 
|  | switch (f & (field8 | field16 | field32 | field64).value) | 
|  | { | 
|  | case field8.value: | 
|  | { | 
|  | return "0b" + std::bitset<8>(v).to_string(); | 
|  | } | 
|  | case field16.value: | 
|  | { | 
|  | return "0b" + std::bitset<16>(v).to_string(); | 
|  | } | 
|  | case field32.value: | 
|  | { | 
|  | return "0b" + std::bitset<32>(v).to_string(); | 
|  | } | 
|  | case field64.value: | 
|  | default: | 
|  | { | 
|  | return "0b" + std::bitset<64>(v).to_string(); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // For hex, use the appropriate sprintf. | 
|  | case hex.value: | 
|  | { | 
|  | char value[19]; | 
|  | const char* format = nullptr; | 
|  |  | 
|  | switch (f & (field8 | field16 | field32 | field64).value) | 
|  | { | 
|  | case field8.value: | 
|  | { | 
|  | format = "0x%02" PRIx64; | 
|  | break; | 
|  | } | 
|  |  | 
|  | case field16.value: | 
|  | { | 
|  | format = "0x%04" PRIx64; | 
|  | break; | 
|  | } | 
|  |  | 
|  | case field32.value: | 
|  | { | 
|  | format = "0x%08" PRIx64; | 
|  | break; | 
|  | } | 
|  |  | 
|  | case field64.value: | 
|  | { | 
|  | format = "0x%016" PRIx64; | 
|  | break; | 
|  | } | 
|  |  | 
|  | default: | 
|  | { | 
|  | format = "0x%" PRIx64; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | snprintf(value, sizeof(value), format, v); | 
|  | return value; | 
|  | } | 
|  |  | 
|  | // For dec, use the simple to_string. | 
|  | case dec.value: | 
|  | default: | 
|  | { | 
|  | return std::to_string(v); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /** Convert signed to string using format flags. */ | 
|  | static std::string value_to_string(uint64_t f, int64_t v) | 
|  | { | 
|  | // If hex or bin was requested just use the unsigned formatting | 
|  | // rules. (What should a negative binary number look like otherwise?) | 
|  | if (f & (hex | bin).value) | 
|  | { | 
|  | return value_to_string(f, static_cast<uint64_t>(v)); | 
|  | } | 
|  | return std::to_string(v); | 
|  | } | 
|  |  | 
|  | /** Convert float to string using format flags. */ | 
|  | static std::string value_to_string(uint64_t, double v) | 
|  | { | 
|  | // No format flags supported for floats. | 
|  | return std::to_string(v); | 
|  | } | 
|  |  | 
|  | // Positions of various strings in an iovec. | 
|  | static constexpr size_t pos_msg = 0; | 
|  | static constexpr size_t pos_fmtmsg = 1; | 
|  | static constexpr size_t pos_prio = 2; | 
|  | static constexpr size_t pos_file = 3; | 
|  | static constexpr size_t pos_line = 4; | 
|  | static constexpr size_t pos_func = 5; | 
|  | static constexpr size_t static_locs = pos_func + 1; | 
|  |  | 
|  | /** No-op output of a message. */ | 
|  | static void noop_extra_output(level, const lg2::source_location&, | 
|  | const std::string&) | 
|  | { | 
|  | } | 
|  |  | 
|  | /** std::cerr output of a message. */ | 
|  | static void cerr_extra_output(level l, const lg2::source_location& s, | 
|  | const std::string& m) | 
|  | { | 
|  | std::cerr << s.file_name() << ":" << s.line() << ":" << s.function_name() | 
|  | << "|<" << static_cast<uint64_t>(l) << "> " << m << std::endl; | 
|  | } | 
|  |  | 
|  | // Use the cerr output method if we are on a TTY. | 
|  | static auto extra_output_method = | 
|  | isatty(fileno(stderr)) ? cerr_extra_output : noop_extra_output; | 
|  |  | 
|  | // Do_log implementation. | 
|  | void do_log(level l, const lg2::source_location& s, const char* m, ...) | 
|  | { | 
|  | using namespace std::string_literals; | 
|  |  | 
|  | std::vector<std::string> strings{static_locs}; | 
|  |  | 
|  | std::string message{m}; | 
|  |  | 
|  | // Assign all the static fields. | 
|  | strings[pos_fmtmsg] = "LOG2_FMTMSG="s + m; | 
|  | strings[pos_prio] = "PRIORITY="s + std::to_string(static_cast<uint64_t>(l)); | 
|  | strings[pos_file] = "CODE_FILE="s + s.file_name(); | 
|  | strings[pos_line] = "CODE_LINE="s + std::to_string(s.line()); | 
|  | strings[pos_func] = "CODE_FUNC="s + s.function_name(); | 
|  |  | 
|  | // Handle all the va_list args. | 
|  | std::va_list args; | 
|  | va_start(args, m); | 
|  | while (true) | 
|  | { | 
|  | // Get the header out. | 
|  | auto h_ptr = va_arg(args, const char*); | 
|  | if (h_ptr == nullptr) | 
|  | { | 
|  | break; | 
|  | } | 
|  | std::string h{h_ptr}; | 
|  |  | 
|  | // Get the format flag. | 
|  | auto f = va_arg(args, uint64_t); | 
|  |  | 
|  | // Handle the value depending on which type format flag it has. | 
|  | std::string value = {}; | 
|  | switch (f & (signed_val | unsigned_val | str | floating).value) | 
|  | { | 
|  | case signed_val.value: | 
|  | { | 
|  | auto v = va_arg(args, int64_t); | 
|  | value = value_to_string(f, v); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case unsigned_val.value: | 
|  | { | 
|  | auto v = va_arg(args, uint64_t); | 
|  | value = value_to_string(f, v); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case str.value: | 
|  | { | 
|  | value = va_arg(args, const char*); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case floating.value: | 
|  | { | 
|  | auto v = va_arg(args, double); | 
|  | value = value_to_string(f, v); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Create the field for this value. | 
|  | strings.emplace_back(h + '=' + value); | 
|  |  | 
|  | // Check for {HEADER} in the message and replace with value. | 
|  | auto h_brace = '{' + h + '}'; | 
|  | if (auto start = message.find(h_brace); start != std::string::npos) | 
|  | { | 
|  | message.replace(start, h_brace.size(), value); | 
|  | } | 
|  | } | 
|  | va_end(args); | 
|  |  | 
|  | // Add the final message into the strings array. | 
|  | strings[pos_msg] = "MESSAGE="s + message.data(); | 
|  |  | 
|  | // Trasform strings -> iovec. | 
|  | std::vector<iovec> iov{}; | 
|  | std::ranges::transform(strings, std::back_inserter(iov), [](auto& s) { | 
|  | return iovec{s.data(), s.length()}; | 
|  | }); | 
|  |  | 
|  | // Output the iovec. | 
|  | sd_journal_sendv(iov.data(), strings.size()); | 
|  | extra_output_method(l, s, message); | 
|  | } | 
|  |  | 
|  | } // namespace lg2::details |