blob: 81f085dbb72cafa3218428e94a61e008ec80fd5e [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
Patrick Williams2544b412022-10-04 08:41:06 -05006#include <phosphor-logging/lg2.hpp>
7
Patrick Williamsb2a3aa22021-07-27 13:30:52 -05008#include <algorithm>
9#include <bitset>
10#include <cstdarg>
11#include <cstdio>
12#include <iostream>
Patrick Williams7f5e4412023-06-23 06:46:06 -050013#include <source_location>
Patrick Williamsb2a3aa22021-07-27 13:30:52 -050014#include <vector>
15
16namespace lg2::details
17{
18/** Convert unsigned to string using format flags. */
19static std::string value_to_string(uint64_t f, uint64_t v)
20{
21 switch (f & (hex | bin | dec).value)
22 {
23 // For binary, use bitset<>::to_string.
24 // Treat values without a field-length format flag as 64 bit.
25 case bin.value:
26 {
27 switch (f & (field8 | field16 | field32 | field64).value)
28 {
29 case field8.value:
30 {
31 return "0b" + std::bitset<8>(v).to_string();
32 }
33 case field16.value:
34 {
35 return "0b" + std::bitset<16>(v).to_string();
36 }
37 case field32.value:
38 {
39 return "0b" + std::bitset<32>(v).to_string();
40 }
41 case field64.value:
42 default:
43 {
44 return "0b" + std::bitset<64>(v).to_string();
45 }
46 }
47 }
48
49 // For hex, use the appropriate sprintf.
50 case hex.value:
51 {
52 char value[19];
53 const char* format = nullptr;
54
55 switch (f & (field8 | field16 | field32 | field64).value)
56 {
57 case field8.value:
58 {
59 format = "0x%02" PRIx64;
60 break;
61 }
62
63 case field16.value:
64 {
65 format = "0x%04" PRIx64;
66 break;
67 }
68
69 case field32.value:
70 {
71 format = "0x%08" PRIx64;
72 break;
73 }
74
75 case field64.value:
76 {
77 format = "0x%016" PRIx64;
78 break;
79 }
80
81 default:
82 {
83 format = "0x%" PRIx64;
84 break;
85 }
86 }
87
88 snprintf(value, sizeof(value), format, v);
89 return value;
90 }
91
92 // For dec, use the simple to_string.
93 case dec.value:
94 default:
95 {
96 return std::to_string(v);
97 }
98 }
99}
100
101/** Convert signed to string using format flags. */
102static std::string value_to_string(uint64_t f, int64_t v)
103{
104 // If hex or bin was requested just use the unsigned formatting
105 // rules. (What should a negative binary number look like otherwise?)
106 if (f & (hex | bin).value)
107 {
108 return value_to_string(f, static_cast<uint64_t>(v));
109 }
110 return std::to_string(v);
111}
112
113/** Convert float to string using format flags. */
114static std::string value_to_string(uint64_t, double v)
115{
116 // No format flags supported for floats.
117 return std::to_string(v);
118}
119
120// Positions of various strings in an iovec.
121static constexpr size_t pos_msg = 0;
122static constexpr size_t pos_fmtmsg = 1;
123static constexpr size_t pos_prio = 2;
124static constexpr size_t pos_file = 3;
125static constexpr size_t pos_line = 4;
126static constexpr size_t pos_func = 5;
127static constexpr size_t static_locs = pos_func + 1;
128
129/** No-op output of a message. */
Patrick Williams7f5e4412023-06-23 06:46:06 -0500130static void noop_extra_output(level, const std::source_location&,
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500131 const std::string&)
Patrick Williams2544b412022-10-04 08:41:06 -0500132{}
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500133
134/** std::cerr output of a message. */
Patrick Williams7f5e4412023-06-23 06:46:06 -0500135static void cerr_extra_output(level l, const std::source_location& s,
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500136 const std::string& m)
137{
Jonathan Domana9e4f592021-11-19 00:05:17 -0800138 static const char* const defaultFormat = []() {
Patrick Williams5dffc2d2021-11-14 06:33:21 -0600139 const char* f = getenv("LG2_FORMAT");
140 if (nullptr == f)
141 {
142 f = "<%l> %m";
143 }
144 return f;
145 }();
146
Jonathan Domana9e4f592021-11-19 00:05:17 -0800147 const char* format = defaultFormat;
148
Patrick Williams5dffc2d2021-11-14 06:33:21 -0600149 while (*format)
150 {
151 if (*format != '%')
152 {
153 std::cerr << *format;
154 ++format;
155 continue;
156 }
157
158 ++format;
159 switch (*format)
160 {
161 case '%':
162 case '\0':
163 std::cerr << '%';
164 break;
165
166 case 'f':
167 std::cerr << s.function_name();
168 break;
169
170 case 'F':
171 std::cerr << s.file_name();
172 break;
173
174 case 'l':
175 std::cerr << static_cast<uint64_t>(l);
176 break;
177
178 case 'L':
179 std::cerr << s.line();
180 break;
181
182 case 'm':
183 std::cerr << m;
184 break;
185
186 default:
187 std::cerr << '%' << *format;
188 break;
189 }
190
191 if (*format != '\0')
192 {
193 ++format;
194 }
195 }
196
197 std::cerr << std::endl;
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500198}
199
Zane Shelleyea06f8e2022-03-25 16:09:23 -0500200// Use the cerr output method if we are on a TTY or if explicitly set via
201// environment variable.
Patrick Williams2544b412022-10-04 08:41:06 -0500202static auto extra_output_method = (isatty(fileno(stderr)) ||
203 nullptr != getenv("LG2_FORCE_STDERR"))
204 ? cerr_extra_output
205 : noop_extra_output;
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500206
207// Do_log implementation.
Patrick Williams7f5e4412023-06-23 06:46:06 -0500208void do_log(level l, const std::source_location& s, const char* m, ...)
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500209{
210 using namespace std::string_literals;
211
Patrick Williamsb1811b32021-08-26 12:19:01 -0500212 std::vector<std::string> strings{static_locs};
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500213
214 std::string message{m};
215
216 // Assign all the static fields.
Patrick Williamsb5988ff2021-08-26 11:58:36 -0500217 strings[pos_fmtmsg] = "LOG2_FMTMSG="s + m;
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500218 strings[pos_prio] = "PRIORITY="s + std::to_string(static_cast<uint64_t>(l));
219 strings[pos_file] = "CODE_FILE="s + s.file_name();
220 strings[pos_line] = "CODE_LINE="s + std::to_string(s.line());
221 strings[pos_func] = "CODE_FUNC="s + s.function_name();
222
223 // Handle all the va_list args.
224 std::va_list args;
Patrick Williamsb1811b32021-08-26 12:19:01 -0500225 va_start(args, m);
226 while (true)
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500227 {
228 // Get the header out.
Patrick Williamsb1811b32021-08-26 12:19:01 -0500229 auto h_ptr = va_arg(args, const char*);
230 if (h_ptr == nullptr)
231 {
232 break;
233 }
234 std::string h{h_ptr};
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500235
236 // Get the format flag.
237 auto f = va_arg(args, uint64_t);
238
239 // Handle the value depending on which type format flag it has.
240 std::string value = {};
241 switch (f & (signed_val | unsigned_val | str | floating).value)
242 {
243 case signed_val.value:
244 {
245 auto v = va_arg(args, int64_t);
246 value = value_to_string(f, v);
247 break;
248 }
249
250 case unsigned_val.value:
251 {
252 auto v = va_arg(args, uint64_t);
253 value = value_to_string(f, v);
254 break;
255 }
256
257 case str.value:
258 {
259 value = va_arg(args, const char*);
260 break;
261 }
262
263 case floating.value:
264 {
265 auto v = va_arg(args, double);
266 value = value_to_string(f, v);
267 break;
268 }
269 }
270
271 // Create the field for this value.
Patrick Williamsb1811b32021-08-26 12:19:01 -0500272 strings.emplace_back(h + '=' + value);
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500273
274 // Check for {HEADER} in the message and replace with value.
275 auto h_brace = '{' + h + '}';
276 if (auto start = message.find(h_brace); start != std::string::npos)
277 {
278 message.replace(start, h_brace.size(), value);
279 }
280 }
281 va_end(args);
282
283 // Add the final message into the strings array.
284 strings[pos_msg] = "MESSAGE="s + message.data();
285
286 // Trasform strings -> iovec.
287 std::vector<iovec> iov{};
288 std::ranges::transform(strings, std::back_inserter(iov), [](auto& s) {
289 return iovec{s.data(), s.length()};
290 });
291
292 // Output the iovec.
Patrick Williamsb1811b32021-08-26 12:19:01 -0500293 sd_journal_sendv(iov.data(), strings.size());
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500294 extra_output_method(l, s, message);
295}
296
Patrick Williamsb2a3aa22021-07-27 13:30:52 -0500297} // namespace lg2::details