1 #define SD_JOURNAL_SUPPRESS_LOCATION 2 3 #include <systemd/sd-journal.h> 4 #include <unistd.h> 5 6 #include <algorithm> 7 #include <bitset> 8 #include <cstdarg> 9 #include <cstdio> 10 #include <iostream> 11 #include <phosphor-logging/lg2.hpp> 12 #include <vector> 13 14 namespace lg2::details 15 { 16 /** Convert unsigned to string using format flags. */ 17 static std::string value_to_string(uint64_t f, uint64_t v) 18 { 19 switch (f & (hex | bin | dec).value) 20 { 21 // For binary, use bitset<>::to_string. 22 // Treat values without a field-length format flag as 64 bit. 23 case bin.value: 24 { 25 switch (f & (field8 | field16 | field32 | field64).value) 26 { 27 case field8.value: 28 { 29 return "0b" + std::bitset<8>(v).to_string(); 30 } 31 case field16.value: 32 { 33 return "0b" + std::bitset<16>(v).to_string(); 34 } 35 case field32.value: 36 { 37 return "0b" + std::bitset<32>(v).to_string(); 38 } 39 case field64.value: 40 default: 41 { 42 return "0b" + std::bitset<64>(v).to_string(); 43 } 44 } 45 } 46 47 // For hex, use the appropriate sprintf. 48 case hex.value: 49 { 50 char value[19]; 51 const char* format = nullptr; 52 53 switch (f & (field8 | field16 | field32 | field64).value) 54 { 55 case field8.value: 56 { 57 format = "0x%02" PRIx64; 58 break; 59 } 60 61 case field16.value: 62 { 63 format = "0x%04" PRIx64; 64 break; 65 } 66 67 case field32.value: 68 { 69 format = "0x%08" PRIx64; 70 break; 71 } 72 73 case field64.value: 74 { 75 format = "0x%016" PRIx64; 76 break; 77 } 78 79 default: 80 { 81 format = "0x%" PRIx64; 82 break; 83 } 84 } 85 86 snprintf(value, sizeof(value), format, v); 87 return value; 88 } 89 90 // For dec, use the simple to_string. 91 case dec.value: 92 default: 93 { 94 return std::to_string(v); 95 } 96 } 97 } 98 99 /** Convert signed to string using format flags. */ 100 static std::string value_to_string(uint64_t f, int64_t v) 101 { 102 // If hex or bin was requested just use the unsigned formatting 103 // rules. (What should a negative binary number look like otherwise?) 104 if (f & (hex | bin).value) 105 { 106 return value_to_string(f, static_cast<uint64_t>(v)); 107 } 108 return std::to_string(v); 109 } 110 111 /** Convert float to string using format flags. */ 112 static std::string value_to_string(uint64_t, double v) 113 { 114 // No format flags supported for floats. 115 return std::to_string(v); 116 } 117 118 // Positions of various strings in an iovec. 119 static constexpr size_t pos_msg = 0; 120 static constexpr size_t pos_fmtmsg = 1; 121 static constexpr size_t pos_prio = 2; 122 static constexpr size_t pos_file = 3; 123 static constexpr size_t pos_line = 4; 124 static constexpr size_t pos_func = 5; 125 static constexpr size_t static_locs = pos_func + 1; 126 127 /** No-op output of a message. */ 128 static void noop_extra_output(level, const lg2::source_location&, 129 const std::string&) 130 { 131 } 132 133 /** std::cerr output of a message. */ 134 static void cerr_extra_output(level l, const lg2::source_location& s, 135 const std::string& m) 136 { 137 std::cerr << s.file_name() << ":" << s.line() << ":" << s.function_name() 138 << "|<" << static_cast<uint64_t>(l) << "> " << m << std::endl; 139 } 140 141 // Use the cerr output method if we are on a TTY. 142 static auto extra_output_method = 143 isatty(fileno(stderr)) ? cerr_extra_output : noop_extra_output; 144 145 // Do_log implementation. 146 void do_log(level l, const lg2::source_location& s, const char* m, ...) 147 { 148 using namespace std::string_literals; 149 150 std::vector<std::string> strings{static_locs}; 151 152 std::string message{m}; 153 154 // Assign all the static fields. 155 strings[pos_fmtmsg] = "LOG2_FMTMSG="s + m; 156 strings[pos_prio] = "PRIORITY="s + std::to_string(static_cast<uint64_t>(l)); 157 strings[pos_file] = "CODE_FILE="s + s.file_name(); 158 strings[pos_line] = "CODE_LINE="s + std::to_string(s.line()); 159 strings[pos_func] = "CODE_FUNC="s + s.function_name(); 160 161 // Handle all the va_list args. 162 std::va_list args; 163 va_start(args, m); 164 while (true) 165 { 166 // Get the header out. 167 auto h_ptr = va_arg(args, const char*); 168 if (h_ptr == nullptr) 169 { 170 break; 171 } 172 std::string h{h_ptr}; 173 174 // Get the format flag. 175 auto f = va_arg(args, uint64_t); 176 177 // Handle the value depending on which type format flag it has. 178 std::string value = {}; 179 switch (f & (signed_val | unsigned_val | str | floating).value) 180 { 181 case signed_val.value: 182 { 183 auto v = va_arg(args, int64_t); 184 value = value_to_string(f, v); 185 break; 186 } 187 188 case unsigned_val.value: 189 { 190 auto v = va_arg(args, uint64_t); 191 value = value_to_string(f, v); 192 break; 193 } 194 195 case str.value: 196 { 197 value = va_arg(args, const char*); 198 break; 199 } 200 201 case floating.value: 202 { 203 auto v = va_arg(args, double); 204 value = value_to_string(f, v); 205 break; 206 } 207 } 208 209 // Create the field for this value. 210 strings.emplace_back(h + '=' + value); 211 212 // Check for {HEADER} in the message and replace with value. 213 auto h_brace = '{' + h + '}'; 214 if (auto start = message.find(h_brace); start != std::string::npos) 215 { 216 message.replace(start, h_brace.size(), value); 217 } 218 } 219 va_end(args); 220 221 // Add the final message into the strings array. 222 strings[pos_msg] = "MESSAGE="s + message.data(); 223 224 // Trasform strings -> iovec. 225 std::vector<iovec> iov{}; 226 std::ranges::transform(strings, std::back_inserter(iov), [](auto& s) { 227 return iovec{s.data(), s.length()}; 228 }); 229 230 // Output the iovec. 231 sd_journal_sendv(iov.data(), strings.size()); 232 extra_output_method(l, s, message); 233 } 234 235 } // namespace lg2::details 236