blob: 8d9c0341df847651be86653550ed5bf1c51c7845 [file] [log] [blame]
Patrick Williamsb2a3aa22021-07-27 13:30:52 -05001#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
14namespace lg2::details
15{
16/** Convert unsigned to string using format flags. */
17static 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. */
100static 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. */
112static 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.
119static constexpr size_t pos_msg = 0;
120static constexpr size_t pos_fmtmsg = 1;
121static constexpr size_t pos_prio = 2;
122static constexpr size_t pos_file = 3;
123static constexpr size_t pos_line = 4;
124static constexpr size_t pos_func = 5;
125static constexpr size_t static_locs = pos_func + 1;
126
127/** No-op output of a message. */
Patrick Williamsa91a62b2021-08-28 14:17:55 -0500128static void noop_extra_output(level, const lg2::source_location&,
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500129 const std::string&)
130{
131}
132
133/** std::cerr output of a message. */
Patrick Williamsa91a62b2021-08-28 14:17:55 -0500134static void cerr_extra_output(level l, const lg2::source_location& s,
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500135 const std::string& m)
136{
Jonathan Domana9e4f592021-11-19 00:05:17 -0800137 static const char* const defaultFormat = []() {
Patrick Williams5dffc2d2021-11-14 06:33:21 -0600138 const char* f = getenv("LG2_FORMAT");
139 if (nullptr == f)
140 {
141 f = "<%l> %m";
142 }
143 return f;
144 }();
145
Jonathan Domana9e4f592021-11-19 00:05:17 -0800146 const char* format = defaultFormat;
147
Patrick Williams5dffc2d2021-11-14 06:33:21 -0600148 while (*format)
149 {
150 if (*format != '%')
151 {
152 std::cerr << *format;
153 ++format;
154 continue;
155 }
156
157 ++format;
158 switch (*format)
159 {
160 case '%':
161 case '\0':
162 std::cerr << '%';
163 break;
164
165 case 'f':
166 std::cerr << s.function_name();
167 break;
168
169 case 'F':
170 std::cerr << s.file_name();
171 break;
172
173 case 'l':
174 std::cerr << static_cast<uint64_t>(l);
175 break;
176
177 case 'L':
178 std::cerr << s.line();
179 break;
180
181 case 'm':
182 std::cerr << m;
183 break;
184
185 default:
186 std::cerr << '%' << *format;
187 break;
188 }
189
190 if (*format != '\0')
191 {
192 ++format;
193 }
194 }
195
196 std::cerr << std::endl;
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500197}
198
199// Use the cerr output method if we are on a TTY.
200static auto extra_output_method =
201 isatty(fileno(stderr)) ? cerr_extra_output : noop_extra_output;
202
203// Do_log implementation.
Patrick Williamsa91a62b2021-08-28 14:17:55 -0500204void do_log(level l, const lg2::source_location& s, const char* m, ...)
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500205{
206 using namespace std::string_literals;
207
Patrick Williamsb1811b32021-08-26 12:19:01 -0500208 std::vector<std::string> strings{static_locs};
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500209
210 std::string message{m};
211
212 // Assign all the static fields.
Patrick Williamsb5988ff2021-08-26 11:58:36 -0500213 strings[pos_fmtmsg] = "LOG2_FMTMSG="s + m;
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500214 strings[pos_prio] = "PRIORITY="s + std::to_string(static_cast<uint64_t>(l));
215 strings[pos_file] = "CODE_FILE="s + s.file_name();
216 strings[pos_line] = "CODE_LINE="s + std::to_string(s.line());
217 strings[pos_func] = "CODE_FUNC="s + s.function_name();
218
219 // Handle all the va_list args.
220 std::va_list args;
Patrick Williamsb1811b32021-08-26 12:19:01 -0500221 va_start(args, m);
222 while (true)
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500223 {
224 // Get the header out.
Patrick Williamsb1811b32021-08-26 12:19:01 -0500225 auto h_ptr = va_arg(args, const char*);
226 if (h_ptr == nullptr)
227 {
228 break;
229 }
230 std::string h{h_ptr};
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500231
232 // Get the format flag.
233 auto f = va_arg(args, uint64_t);
234
235 // Handle the value depending on which type format flag it has.
236 std::string value = {};
237 switch (f & (signed_val | unsigned_val | str | floating).value)
238 {
239 case signed_val.value:
240 {
241 auto v = va_arg(args, int64_t);
242 value = value_to_string(f, v);
243 break;
244 }
245
246 case unsigned_val.value:
247 {
248 auto v = va_arg(args, uint64_t);
249 value = value_to_string(f, v);
250 break;
251 }
252
253 case str.value:
254 {
255 value = va_arg(args, const char*);
256 break;
257 }
258
259 case floating.value:
260 {
261 auto v = va_arg(args, double);
262 value = value_to_string(f, v);
263 break;
264 }
265 }
266
267 // Create the field for this value.
Patrick Williamsb1811b32021-08-26 12:19:01 -0500268 strings.emplace_back(h + '=' + value);
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500269
270 // Check for {HEADER} in the message and replace with value.
271 auto h_brace = '{' + h + '}';
272 if (auto start = message.find(h_brace); start != std::string::npos)
273 {
274 message.replace(start, h_brace.size(), value);
275 }
276 }
277 va_end(args);
278
279 // Add the final message into the strings array.
280 strings[pos_msg] = "MESSAGE="s + message.data();
281
282 // Trasform strings -> iovec.
283 std::vector<iovec> iov{};
284 std::ranges::transform(strings, std::back_inserter(iov), [](auto& s) {
285 return iovec{s.data(), s.length()};
286 });
287
288 // Output the iovec.
Patrick Williamsb1811b32021-08-26 12:19:01 -0500289 sd_journal_sendv(iov.data(), strings.size());
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500290 extra_output_method(l, s, message);
291}
292
293} // namespace lg2::details