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 static const char* format = []() { 138 const char* f = getenv("LG2_FORMAT"); 139 if (nullptr == f) 140 { 141 f = "<%l> %m"; 142 } 143 return f; 144 }(); 145 146 while (*format) 147 { 148 if (*format != '%') 149 { 150 std::cerr << *format; 151 ++format; 152 continue; 153 } 154 155 ++format; 156 switch (*format) 157 { 158 case '%': 159 case '\0': 160 std::cerr << '%'; 161 break; 162 163 case 'f': 164 std::cerr << s.function_name(); 165 break; 166 167 case 'F': 168 std::cerr << s.file_name(); 169 break; 170 171 case 'l': 172 std::cerr << static_cast<uint64_t>(l); 173 break; 174 175 case 'L': 176 std::cerr << s.line(); 177 break; 178 179 case 'm': 180 std::cerr << m; 181 break; 182 183 default: 184 std::cerr << '%' << *format; 185 break; 186 } 187 188 if (*format != '\0') 189 { 190 ++format; 191 } 192 } 193 194 std::cerr << std::endl; 195 } 196 197 // Use the cerr output method if we are on a TTY. 198 static auto extra_output_method = 199 isatty(fileno(stderr)) ? cerr_extra_output : noop_extra_output; 200 201 // Do_log implementation. 202 void do_log(level l, const lg2::source_location& s, const char* m, ...) 203 { 204 using namespace std::string_literals; 205 206 std::vector<std::string> strings{static_locs}; 207 208 std::string message{m}; 209 210 // Assign all the static fields. 211 strings[pos_fmtmsg] = "LOG2_FMTMSG="s + m; 212 strings[pos_prio] = "PRIORITY="s + std::to_string(static_cast<uint64_t>(l)); 213 strings[pos_file] = "CODE_FILE="s + s.file_name(); 214 strings[pos_line] = "CODE_LINE="s + std::to_string(s.line()); 215 strings[pos_func] = "CODE_FUNC="s + s.function_name(); 216 217 // Handle all the va_list args. 218 std::va_list args; 219 va_start(args, m); 220 while (true) 221 { 222 // Get the header out. 223 auto h_ptr = va_arg(args, const char*); 224 if (h_ptr == nullptr) 225 { 226 break; 227 } 228 std::string h{h_ptr}; 229 230 // Get the format flag. 231 auto f = va_arg(args, uint64_t); 232 233 // Handle the value depending on which type format flag it has. 234 std::string value = {}; 235 switch (f & (signed_val | unsigned_val | str | floating).value) 236 { 237 case signed_val.value: 238 { 239 auto v = va_arg(args, int64_t); 240 value = value_to_string(f, v); 241 break; 242 } 243 244 case unsigned_val.value: 245 { 246 auto v = va_arg(args, uint64_t); 247 value = value_to_string(f, v); 248 break; 249 } 250 251 case str.value: 252 { 253 value = va_arg(args, const char*); 254 break; 255 } 256 257 case floating.value: 258 { 259 auto v = va_arg(args, double); 260 value = value_to_string(f, v); 261 break; 262 } 263 } 264 265 // Create the field for this value. 266 strings.emplace_back(h + '=' + value); 267 268 // Check for {HEADER} in the message and replace with value. 269 auto h_brace = '{' + h + '}'; 270 if (auto start = message.find(h_brace); start != std::string::npos) 271 { 272 message.replace(start, h_brace.size(), value); 273 } 274 } 275 va_end(args); 276 277 // Add the final message into the strings array. 278 strings[pos_msg] = "MESSAGE="s + message.data(); 279 280 // Trasform strings -> iovec. 281 std::vector<iovec> iov{}; 282 std::ranges::transform(strings, std::back_inserter(iov), [](auto& s) { 283 return iovec{s.data(), s.length()}; 284 }); 285 286 // Output the iovec. 287 sd_journal_sendv(iov.data(), strings.size()); 288 extra_output_method(l, s, message); 289 } 290 291 } // namespace lg2::details 292