1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. |
2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) |
3 | |
4 | #pragma once |
5 | |
6 | #ifndef SPDLOG_HEADER_ONLY |
7 | # include <spdlog/pattern_formatter.h> |
8 | #endif |
9 | |
10 | #include <spdlog/details/fmt_helper.h> |
11 | #include <spdlog/details/log_msg.h> |
12 | #include <spdlog/details/os.h> |
13 | #include <spdlog/fmt/fmt.h> |
14 | #include <spdlog/formatter.h> |
15 | |
16 | #include <algorithm> |
17 | #include <array> |
18 | #include <chrono> |
19 | #include <ctime> |
20 | #include <cctype> |
21 | #include <cstring> |
22 | #include <iterator> |
23 | #include <memory> |
24 | #include <mutex> |
25 | #include <string> |
26 | #include <thread> |
27 | #include <utility> |
28 | #include <vector> |
29 | |
30 | namespace spdlog { |
31 | namespace details { |
32 | |
33 | /////////////////////////////////////////////////////////////////////// |
34 | // name & level pattern appender |
35 | /////////////////////////////////////////////////////////////////////// |
36 | |
37 | class scoped_padder |
38 | { |
39 | public: |
40 | scoped_padder(size_t wrapped_size, const padding_info &padinfo, memory_buf_t &dest) |
41 | : padinfo_(padinfo) |
42 | , dest_(dest) |
43 | { |
44 | remaining_pad_ = static_cast<long>(padinfo.width_) - static_cast<long>(wrapped_size); |
45 | if (remaining_pad_ <= 0) |
46 | { |
47 | return; |
48 | } |
49 | |
50 | if (padinfo_.side_ == padding_info::pad_side::left) |
51 | { |
52 | pad_it(remaining_pad_); |
53 | remaining_pad_ = 0; |
54 | } |
55 | else if (padinfo_.side_ == padding_info::pad_side::center) |
56 | { |
57 | auto half_pad = remaining_pad_ / 2; |
58 | auto reminder = remaining_pad_ & 1; |
59 | pad_it(half_pad); |
60 | remaining_pad_ = half_pad + reminder; // for the right side |
61 | } |
62 | } |
63 | |
64 | template<typename T> |
65 | static unsigned int count_digits(T n) |
66 | { |
67 | return fmt_helper::count_digits(n); |
68 | } |
69 | |
70 | ~scoped_padder() |
71 | { |
72 | if (remaining_pad_ >= 0) |
73 | { |
74 | pad_it(remaining_pad_); |
75 | } |
76 | else if (padinfo_.truncate_) |
77 | { |
78 | long new_size = static_cast<long>(dest_.size()) + remaining_pad_; |
79 | dest_.resize(static_cast<size_t>(new_size)); |
80 | } |
81 | } |
82 | |
83 | private: |
84 | void pad_it(long count) |
85 | { |
86 | fmt_helper::append_string_view(string_view_t(spaces_.data(), static_cast<size_t>(count)), dest_); |
87 | } |
88 | |
89 | const padding_info &padinfo_; |
90 | memory_buf_t &dest_; |
91 | long remaining_pad_; |
92 | string_view_t spaces_{" " , 64}; |
93 | }; |
94 | |
95 | struct null_scoped_padder |
96 | { |
97 | null_scoped_padder(size_t /*wrapped_size*/, const padding_info & /*padinfo*/, memory_buf_t & /*dest*/) {} |
98 | |
99 | template<typename T> |
100 | static unsigned int count_digits(T /* number */) |
101 | { |
102 | return 0; |
103 | } |
104 | }; |
105 | |
106 | template<typename ScopedPadder> |
107 | class name_formatter final : public flag_formatter |
108 | { |
109 | public: |
110 | explicit name_formatter(padding_info padinfo) |
111 | : flag_formatter(padinfo) |
112 | {} |
113 | |
114 | void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override |
115 | { |
116 | ScopedPadder p(msg.logger_name.size(), padinfo_, dest); |
117 | fmt_helper::append_string_view(msg.logger_name, dest); |
118 | } |
119 | }; |
120 | |
121 | // log level appender |
122 | template<typename ScopedPadder> |
123 | class level_formatter final : public flag_formatter |
124 | { |
125 | public: |
126 | explicit level_formatter(padding_info padinfo) |
127 | : flag_formatter(padinfo) |
128 | {} |
129 | |
130 | void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override |
131 | { |
132 | const string_view_t &level_name = level::to_string_view(msg.level); |
133 | ScopedPadder p(level_name.size(), padinfo_, dest); |
134 | fmt_helper::append_string_view(level_name, dest); |
135 | } |
136 | }; |
137 | |
138 | // short log level appender |
139 | template<typename ScopedPadder> |
140 | class short_level_formatter final : public flag_formatter |
141 | { |
142 | public: |
143 | explicit short_level_formatter(padding_info padinfo) |
144 | : flag_formatter(padinfo) |
145 | {} |
146 | |
147 | void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override |
148 | { |
149 | string_view_t level_name{level::to_short_c_str(msg.level)}; |
150 | ScopedPadder p(level_name.size(), padinfo_, dest); |
151 | fmt_helper::append_string_view(level_name, dest); |
152 | } |
153 | }; |
154 | |
155 | /////////////////////////////////////////////////////////////////////// |
156 | // Date time pattern appenders |
157 | /////////////////////////////////////////////////////////////////////// |
158 | |
159 | static const char *ampm(const tm &t) |
160 | { |
161 | return t.tm_hour >= 12 ? "PM" : "AM" ; |
162 | } |
163 | |
164 | static int to12h(const tm &t) |
165 | { |
166 | return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour; |
167 | } |
168 | |
169 | // Abbreviated weekday name |
170 | static std::array<const char *, 7> days{{"Sun" , "Mon" , "Tue" , "Wed" , "Thu" , "Fri" , "Sat" }}; |
171 | |
172 | template<typename ScopedPadder> |
173 | class a_formatter final : public flag_formatter |
174 | { |
175 | public: |
176 | explicit a_formatter(padding_info padinfo) |
177 | : flag_formatter(padinfo) |
178 | {} |
179 | |
180 | void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override |
181 | { |
182 | string_view_t field_value{days[static_cast<size_t>(tm_time.tm_wday)]}; |
183 | ScopedPadder p(field_value.size(), padinfo_, dest); |
184 | fmt_helper::append_string_view(field_value, dest); |
185 | } |
186 | }; |
187 | |
188 | // Full weekday name |
189 | static std::array<const char *, 7> full_days{{"Sunday" , "Monday" , "Tuesday" , "Wednesday" , "Thursday" , "Friday" , "Saturday" }}; |
190 | |
191 | template<typename ScopedPadder> |
192 | class A_formatter : public flag_formatter |
193 | { |
194 | public: |
195 | explicit A_formatter(padding_info padinfo) |
196 | : flag_formatter(padinfo) |
197 | {} |
198 | |
199 | void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override |
200 | { |
201 | string_view_t field_value{full_days[static_cast<size_t>(tm_time.tm_wday)]}; |
202 | ScopedPadder p(field_value.size(), padinfo_, dest); |
203 | fmt_helper::append_string_view(field_value, dest); |
204 | } |
205 | }; |
206 | |
207 | // Abbreviated month |
208 | static const std::array<const char *, 12> months{{"Jan" , "Feb" , "Mar" , "Apr" , "May" , "Jun" , "Jul" , "Aug" , "Sept" , "Oct" , "Nov" , "Dec" }}; |
209 | |
210 | template<typename ScopedPadder> |
211 | class b_formatter final : public flag_formatter |
212 | { |
213 | public: |
214 | explicit b_formatter(padding_info padinfo) |
215 | : flag_formatter(padinfo) |
216 | {} |
217 | |
218 | void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override |
219 | { |
220 | string_view_t field_value{months[static_cast<size_t>(tm_time.tm_mon)]}; |
221 | ScopedPadder p(field_value.size(), padinfo_, dest); |
222 | fmt_helper::append_string_view(field_value, dest); |
223 | } |
224 | }; |
225 | |
226 | // Full month name |
227 | static const std::array<const char *, 12> full_months{ |
228 | {"January" , "February" , "March" , "April" , "May" , "June" , "July" , "August" , "September" , "October" , "November" , "December" }}; |
229 | |
230 | template<typename ScopedPadder> |
231 | class B_formatter final : public flag_formatter |
232 | { |
233 | public: |
234 | explicit B_formatter(padding_info padinfo) |
235 | : flag_formatter(padinfo) |
236 | {} |
237 | |
238 | void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override |
239 | { |
240 | string_view_t field_value{full_months[static_cast<size_t>(tm_time.tm_mon)]}; |
241 | ScopedPadder p(field_value.size(), padinfo_, dest); |
242 | fmt_helper::append_string_view(field_value, dest); |
243 | } |
244 | }; |
245 | |
246 | // Date and time representation (Thu Aug 23 15:35:46 2014) |
247 | template<typename ScopedPadder> |
248 | class c_formatter final : public flag_formatter |
249 | { |
250 | public: |
251 | explicit c_formatter(padding_info padinfo) |
252 | : flag_formatter(padinfo) |
253 | {} |
254 | |
255 | void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override |
256 | { |
257 | const size_t field_size = 24; |
258 | ScopedPadder p(field_size, padinfo_, dest); |
259 | |
260 | fmt_helper::append_string_view(days[static_cast<size_t>(tm_time.tm_wday)], dest); |
261 | dest.push_back(' '); |
262 | fmt_helper::append_string_view(months[static_cast<size_t>(tm_time.tm_mon)], dest); |
263 | dest.push_back(' '); |
264 | fmt_helper::append_int(tm_time.tm_mday, dest); |
265 | dest.push_back(' '); |
266 | // time |
267 | |
268 | fmt_helper::pad2(tm_time.tm_hour, dest); |
269 | dest.push_back(':'); |
270 | fmt_helper::pad2(tm_time.tm_min, dest); |
271 | dest.push_back(':'); |
272 | fmt_helper::pad2(tm_time.tm_sec, dest); |
273 | dest.push_back(' '); |
274 | fmt_helper::append_int(tm_time.tm_year + 1900, dest); |
275 | } |
276 | }; |
277 | |
278 | // year - 2 digit |
279 | template<typename ScopedPadder> |
280 | class C_formatter final : public flag_formatter |
281 | { |
282 | public: |
283 | explicit C_formatter(padding_info padinfo) |
284 | : flag_formatter(padinfo) |
285 | {} |
286 | |
287 | void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override |
288 | { |
289 | const size_t field_size = 2; |
290 | ScopedPadder p(field_size, padinfo_, dest); |
291 | fmt_helper::pad2(tm_time.tm_year % 100, dest); |
292 | } |
293 | }; |
294 | |
295 | // Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01 |
296 | template<typename ScopedPadder> |
297 | class D_formatter final : public flag_formatter |
298 | { |
299 | public: |
300 | explicit D_formatter(padding_info padinfo) |
301 | : flag_formatter(padinfo) |
302 | {} |
303 | |
304 | void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override |
305 | { |
306 | const size_t field_size = 10; |
307 | ScopedPadder p(field_size, padinfo_, dest); |
308 | |
309 | fmt_helper::pad2(tm_time.tm_mon + 1, dest); |
310 | dest.push_back('/'); |
311 | fmt_helper::pad2(tm_time.tm_mday, dest); |
312 | dest.push_back('/'); |
313 | fmt_helper::pad2(tm_time.tm_year % 100, dest); |
314 | } |
315 | }; |
316 | |
317 | // year - 4 digit |
318 | template<typename ScopedPadder> |
319 | class Y_formatter final : public flag_formatter |
320 | { |
321 | public: |
322 | explicit Y_formatter(padding_info padinfo) |
323 | : flag_formatter(padinfo) |
324 | {} |
325 | |
326 | void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override |
327 | { |
328 | const size_t field_size = 4; |
329 | ScopedPadder p(field_size, padinfo_, dest); |
330 | fmt_helper::append_int(tm_time.tm_year + 1900, dest); |
331 | } |
332 | }; |
333 | |
334 | // month 1-12 |
335 | template<typename ScopedPadder> |
336 | class m_formatter final : public flag_formatter |
337 | { |
338 | public: |
339 | explicit m_formatter(padding_info padinfo) |
340 | : flag_formatter(padinfo) |
341 | {} |
342 | |
343 | void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override |
344 | { |
345 | const size_t field_size = 2; |
346 | ScopedPadder p(field_size, padinfo_, dest); |
347 | fmt_helper::pad2(tm_time.tm_mon + 1, dest); |
348 | } |
349 | }; |
350 | |
351 | // day of month 1-31 |
352 | template<typename ScopedPadder> |
353 | class d_formatter final : public flag_formatter |
354 | { |
355 | public: |
356 | explicit d_formatter(padding_info padinfo) |
357 | : flag_formatter(padinfo) |
358 | {} |
359 | |
360 | void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override |
361 | { |
362 | const size_t field_size = 2; |
363 | ScopedPadder p(field_size, padinfo_, dest); |
364 | fmt_helper::pad2(tm_time.tm_mday, dest); |
365 | } |
366 | }; |
367 | |
368 | // hours in 24 format 0-23 |
369 | template<typename ScopedPadder> |
370 | class H_formatter final : public flag_formatter |
371 | { |
372 | public: |
373 | explicit H_formatter(padding_info padinfo) |
374 | : flag_formatter(padinfo) |
375 | {} |
376 | |
377 | void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override |
378 | { |
379 | const size_t field_size = 2; |
380 | ScopedPadder p(field_size, padinfo_, dest); |
381 | fmt_helper::pad2(tm_time.tm_hour, dest); |
382 | } |
383 | }; |
384 | |
385 | // hours in 12 format 1-12 |
386 | template<typename ScopedPadder> |
387 | class I_formatter final : public flag_formatter |
388 | { |
389 | public: |
390 | explicit I_formatter(padding_info padinfo) |
391 | : flag_formatter(padinfo) |
392 | {} |
393 | |
394 | void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override |
395 | { |
396 | const size_t field_size = 2; |
397 | ScopedPadder p(field_size, padinfo_, dest); |
398 | fmt_helper::pad2(to12h(tm_time), dest); |
399 | } |
400 | }; |
401 | |
402 | // minutes 0-59 |
403 | template<typename ScopedPadder> |
404 | class M_formatter final : public flag_formatter |
405 | { |
406 | public: |
407 | explicit M_formatter(padding_info padinfo) |
408 | : flag_formatter(padinfo) |
409 | {} |
410 | |
411 | void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override |
412 | { |
413 | const size_t field_size = 2; |
414 | ScopedPadder p(field_size, padinfo_, dest); |
415 | fmt_helper::pad2(tm_time.tm_min, dest); |
416 | } |
417 | }; |
418 | |
419 | // seconds 0-59 |
420 | template<typename ScopedPadder> |
421 | class S_formatter final : public flag_formatter |
422 | { |
423 | public: |
424 | explicit S_formatter(padding_info padinfo) |
425 | : flag_formatter(padinfo) |
426 | {} |
427 | |
428 | void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override |
429 | { |
430 | const size_t field_size = 2; |
431 | ScopedPadder p(field_size, padinfo_, dest); |
432 | fmt_helper::pad2(tm_time.tm_sec, dest); |
433 | } |
434 | }; |
435 | |
436 | // milliseconds |
437 | template<typename ScopedPadder> |
438 | class e_formatter final : public flag_formatter |
439 | { |
440 | public: |
441 | explicit e_formatter(padding_info padinfo) |
442 | : flag_formatter(padinfo) |
443 | {} |
444 | |
445 | void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override |
446 | { |
447 | auto millis = fmt_helper::time_fraction<std::chrono::milliseconds>(msg.time); |
448 | const size_t field_size = 3; |
449 | ScopedPadder p(field_size, padinfo_, dest); |
450 | fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest); |
451 | } |
452 | }; |
453 | |
454 | // microseconds |
455 | template<typename ScopedPadder> |
456 | class f_formatter final : public flag_formatter |
457 | { |
458 | public: |
459 | explicit f_formatter(padding_info padinfo) |
460 | : flag_formatter(padinfo) |
461 | {} |
462 | |
463 | void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override |
464 | { |
465 | auto micros = fmt_helper::time_fraction<std::chrono::microseconds>(msg.time); |
466 | |
467 | const size_t field_size = 6; |
468 | ScopedPadder p(field_size, padinfo_, dest); |
469 | fmt_helper::pad6(static_cast<size_t>(micros.count()), dest); |
470 | } |
471 | }; |
472 | |
473 | // nanoseconds |
474 | template<typename ScopedPadder> |
475 | class F_formatter final : public flag_formatter |
476 | { |
477 | public: |
478 | explicit F_formatter(padding_info padinfo) |
479 | : flag_formatter(padinfo) |
480 | {} |
481 | |
482 | void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override |
483 | { |
484 | auto ns = fmt_helper::time_fraction<std::chrono::nanoseconds>(msg.time); |
485 | const size_t field_size = 9; |
486 | ScopedPadder p(field_size, padinfo_, dest); |
487 | fmt_helper::pad9(static_cast<size_t>(ns.count()), dest); |
488 | } |
489 | }; |
490 | |
491 | // seconds since epoch |
492 | template<typename ScopedPadder> |
493 | class E_formatter final : public flag_formatter |
494 | { |
495 | public: |
496 | explicit E_formatter(padding_info padinfo) |
497 | : flag_formatter(padinfo) |
498 | {} |
499 | |
500 | void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override |
501 | { |
502 | const size_t field_size = 10; |
503 | ScopedPadder p(field_size, padinfo_, dest); |
504 | auto duration = msg.time.time_since_epoch(); |
505 | auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count(); |
506 | fmt_helper::append_int(seconds, dest); |
507 | } |
508 | }; |
509 | |
510 | // AM/PM |
511 | template<typename ScopedPadder> |
512 | class p_formatter final : public flag_formatter |
513 | { |
514 | public: |
515 | explicit p_formatter(padding_info padinfo) |
516 | : flag_formatter(padinfo) |
517 | {} |
518 | |
519 | void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override |
520 | { |
521 | const size_t field_size = 2; |
522 | ScopedPadder p(field_size, padinfo_, dest); |
523 | fmt_helper::append_string_view(ampm(tm_time), dest); |
524 | } |
525 | }; |
526 | |
527 | // 12 hour clock 02:55:02 pm |
528 | template<typename ScopedPadder> |
529 | class r_formatter final : public flag_formatter |
530 | { |
531 | public: |
532 | explicit r_formatter(padding_info padinfo) |
533 | : flag_formatter(padinfo) |
534 | {} |
535 | |
536 | void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override |
537 | { |
538 | const size_t field_size = 11; |
539 | ScopedPadder p(field_size, padinfo_, dest); |
540 | |
541 | fmt_helper::pad2(to12h(tm_time), dest); |
542 | dest.push_back(':'); |
543 | fmt_helper::pad2(tm_time.tm_min, dest); |
544 | dest.push_back(':'); |
545 | fmt_helper::pad2(tm_time.tm_sec, dest); |
546 | dest.push_back(' '); |
547 | fmt_helper::append_string_view(ampm(tm_time), dest); |
548 | } |
549 | }; |
550 | |
551 | // 24-hour HH:MM time, equivalent to %H:%M |
552 | template<typename ScopedPadder> |
553 | class R_formatter final : public flag_formatter |
554 | { |
555 | public: |
556 | explicit R_formatter(padding_info padinfo) |
557 | : flag_formatter(padinfo) |
558 | {} |
559 | |
560 | void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override |
561 | { |
562 | const size_t field_size = 5; |
563 | ScopedPadder p(field_size, padinfo_, dest); |
564 | |
565 | fmt_helper::pad2(tm_time.tm_hour, dest); |
566 | dest.push_back(':'); |
567 | fmt_helper::pad2(tm_time.tm_min, dest); |
568 | } |
569 | }; |
570 | |
571 | // ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S |
572 | template<typename ScopedPadder> |
573 | class T_formatter final : public flag_formatter |
574 | { |
575 | public: |
576 | explicit T_formatter(padding_info padinfo) |
577 | : flag_formatter(padinfo) |
578 | {} |
579 | |
580 | void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override |
581 | { |
582 | const size_t field_size = 8; |
583 | ScopedPadder p(field_size, padinfo_, dest); |
584 | |
585 | fmt_helper::pad2(tm_time.tm_hour, dest); |
586 | dest.push_back(':'); |
587 | fmt_helper::pad2(tm_time.tm_min, dest); |
588 | dest.push_back(':'); |
589 | fmt_helper::pad2(tm_time.tm_sec, dest); |
590 | } |
591 | }; |
592 | |
593 | // ISO 8601 offset from UTC in timezone (+-HH:MM) |
594 | template<typename ScopedPadder> |
595 | class z_formatter final : public flag_formatter |
596 | { |
597 | public: |
598 | explicit z_formatter(padding_info padinfo) |
599 | : flag_formatter(padinfo) |
600 | {} |
601 | |
602 | z_formatter() = default; |
603 | z_formatter(const z_formatter &) = delete; |
604 | z_formatter &operator=(const z_formatter &) = delete; |
605 | |
606 | void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) override |
607 | { |
608 | const size_t field_size = 6; |
609 | ScopedPadder p(field_size, padinfo_, dest); |
610 | |
611 | auto total_minutes = get_cached_offset(msg, tm_time); |
612 | bool is_negative = total_minutes < 0; |
613 | if (is_negative) |
614 | { |
615 | total_minutes = -total_minutes; |
616 | dest.push_back('-'); |
617 | } |
618 | else |
619 | { |
620 | dest.push_back('+'); |
621 | } |
622 | |
623 | fmt_helper::pad2(total_minutes / 60, dest); // hours |
624 | dest.push_back(':'); |
625 | fmt_helper::pad2(total_minutes % 60, dest); // minutes |
626 | } |
627 | |
628 | private: |
629 | log_clock::time_point last_update_{std::chrono::seconds(0)}; |
630 | int offset_minutes_{0}; |
631 | |
632 | int get_cached_offset(const log_msg &msg, const std::tm &tm_time) |
633 | { |
634 | // refresh every 10 seconds |
635 | if (msg.time - last_update_ >= std::chrono::seconds(10)) |
636 | { |
637 | offset_minutes_ = os::utc_minutes_offset(tm_time); |
638 | last_update_ = msg.time; |
639 | } |
640 | return offset_minutes_; |
641 | } |
642 | }; |
643 | |
644 | // Thread id |
645 | template<typename ScopedPadder> |
646 | class t_formatter final : public flag_formatter |
647 | { |
648 | public: |
649 | explicit t_formatter(padding_info padinfo) |
650 | : flag_formatter(padinfo) |
651 | {} |
652 | |
653 | void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override |
654 | { |
655 | const auto field_size = ScopedPadder::count_digits(msg.thread_id); |
656 | ScopedPadder p(field_size, padinfo_, dest); |
657 | fmt_helper::append_int(msg.thread_id, dest); |
658 | } |
659 | }; |
660 | |
661 | // Current pid |
662 | template<typename ScopedPadder> |
663 | class pid_formatter final : public flag_formatter |
664 | { |
665 | public: |
666 | explicit pid_formatter(padding_info padinfo) |
667 | : flag_formatter(padinfo) |
668 | {} |
669 | |
670 | void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override |
671 | { |
672 | const auto pid = static_cast<uint32_t>(details::os::pid()); |
673 | auto field_size = ScopedPadder::count_digits(pid); |
674 | ScopedPadder p(field_size, padinfo_, dest); |
675 | fmt_helper::append_int(pid, dest); |
676 | } |
677 | }; |
678 | |
679 | template<typename ScopedPadder> |
680 | class v_formatter final : public flag_formatter |
681 | { |
682 | public: |
683 | explicit v_formatter(padding_info padinfo) |
684 | : flag_formatter(padinfo) |
685 | {} |
686 | |
687 | void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override |
688 | { |
689 | ScopedPadder p(msg.payload.size(), padinfo_, dest); |
690 | fmt_helper::append_string_view(msg.payload, dest); |
691 | } |
692 | }; |
693 | |
694 | class ch_formatter final : public flag_formatter |
695 | { |
696 | public: |
697 | explicit ch_formatter(char ch) |
698 | : ch_(ch) |
699 | {} |
700 | |
701 | void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override |
702 | { |
703 | dest.push_back(ch_); |
704 | } |
705 | |
706 | private: |
707 | char ch_; |
708 | }; |
709 | |
710 | // aggregate user chars to display as is |
711 | class aggregate_formatter final : public flag_formatter |
712 | { |
713 | public: |
714 | aggregate_formatter() = default; |
715 | |
716 | void add_ch(char ch) |
717 | { |
718 | str_ += ch; |
719 | } |
720 | void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override |
721 | { |
722 | fmt_helper::append_string_view(str_, dest); |
723 | } |
724 | |
725 | private: |
726 | std::string str_; |
727 | }; |
728 | |
729 | // mark the color range. expect it to be in the form of "%^colored text%$" |
730 | class color_start_formatter final : public flag_formatter |
731 | { |
732 | public: |
733 | explicit color_start_formatter(padding_info padinfo) |
734 | : flag_formatter(padinfo) |
735 | {} |
736 | |
737 | void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override |
738 | { |
739 | msg.color_range_start = dest.size(); |
740 | } |
741 | }; |
742 | |
743 | class color_stop_formatter final : public flag_formatter |
744 | { |
745 | public: |
746 | explicit color_stop_formatter(padding_info padinfo) |
747 | : flag_formatter(padinfo) |
748 | {} |
749 | |
750 | void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override |
751 | { |
752 | msg.color_range_end = dest.size(); |
753 | } |
754 | }; |
755 | |
756 | // print source location |
757 | template<typename ScopedPadder> |
758 | class source_location_formatter final : public flag_formatter |
759 | { |
760 | public: |
761 | explicit source_location_formatter(padding_info padinfo) |
762 | : flag_formatter(padinfo) |
763 | {} |
764 | |
765 | void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override |
766 | { |
767 | if (msg.source.empty()) |
768 | { |
769 | ScopedPadder p(0, padinfo_, dest); |
770 | return; |
771 | } |
772 | |
773 | size_t text_size; |
774 | if (padinfo_.enabled()) |
775 | { |
776 | // calc text size for padding based on "filename:line" |
777 | text_size = std::char_traits<char>::length(msg.source.filename) + ScopedPadder::count_digits(msg.source.line) + 1; |
778 | } |
779 | else |
780 | { |
781 | text_size = 0; |
782 | } |
783 | |
784 | ScopedPadder p(text_size, padinfo_, dest); |
785 | fmt_helper::append_string_view(msg.source.filename, dest); |
786 | dest.push_back(':'); |
787 | fmt_helper::append_int(msg.source.line, dest); |
788 | } |
789 | }; |
790 | |
791 | // print source filename |
792 | template<typename ScopedPadder> |
793 | class source_filename_formatter final : public flag_formatter |
794 | { |
795 | public: |
796 | explicit source_filename_formatter(padding_info padinfo) |
797 | : flag_formatter(padinfo) |
798 | {} |
799 | |
800 | void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override |
801 | { |
802 | if (msg.source.empty()) |
803 | { |
804 | ScopedPadder p(0, padinfo_, dest); |
805 | return; |
806 | } |
807 | size_t text_size = padinfo_.enabled() ? std::char_traits<char>::length(msg.source.filename) : 0; |
808 | ScopedPadder p(text_size, padinfo_, dest); |
809 | fmt_helper::append_string_view(msg.source.filename, dest); |
810 | } |
811 | }; |
812 | |
813 | template<typename ScopedPadder> |
814 | class short_filename_formatter final : public flag_formatter |
815 | { |
816 | public: |
817 | explicit short_filename_formatter(padding_info padinfo) |
818 | : flag_formatter(padinfo) |
819 | {} |
820 | |
821 | #ifdef _MSC_VER |
822 | # pragma warning(push) |
823 | # pragma warning(disable : 4127) // consider using 'if constexpr' instead |
824 | #endif // _MSC_VER |
825 | static const char *basename(const char *filename) |
826 | { |
827 | // if the size is 2 (1 character + null terminator) we can use the more efficient strrchr |
828 | // the branch will be elided by optimizations |
829 | if (sizeof(os::folder_seps) == 2) |
830 | { |
831 | const char *rv = std::strrchr(filename, os::folder_seps[0]); |
832 | return rv != nullptr ? rv + 1 : filename; |
833 | } |
834 | else |
835 | { |
836 | const std::reverse_iterator<const char *> begin(filename + std::strlen(filename)); |
837 | const std::reverse_iterator<const char *> end(filename); |
838 | |
839 | const auto it = std::find_first_of(begin, end, std::begin(os::folder_seps), std::end(os::folder_seps) - 1); |
840 | return it != end ? it.base() : filename; |
841 | } |
842 | } |
843 | #ifdef _MSC_VER |
844 | # pragma warning(pop) |
845 | #endif // _MSC_VER |
846 | |
847 | void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override |
848 | { |
849 | if (msg.source.empty()) |
850 | { |
851 | ScopedPadder p(0, padinfo_, dest); |
852 | return; |
853 | } |
854 | auto filename = basename(msg.source.filename); |
855 | size_t text_size = padinfo_.enabled() ? std::char_traits<char>::length(filename) : 0; |
856 | ScopedPadder p(text_size, padinfo_, dest); |
857 | fmt_helper::append_string_view(filename, dest); |
858 | } |
859 | }; |
860 | |
861 | template<typename ScopedPadder> |
862 | class source_linenum_formatter final : public flag_formatter |
863 | { |
864 | public: |
865 | explicit source_linenum_formatter(padding_info padinfo) |
866 | : flag_formatter(padinfo) |
867 | {} |
868 | |
869 | void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override |
870 | { |
871 | if (msg.source.empty()) |
872 | { |
873 | ScopedPadder p(0, padinfo_, dest); |
874 | return; |
875 | } |
876 | |
877 | auto field_size = ScopedPadder::count_digits(msg.source.line); |
878 | ScopedPadder p(field_size, padinfo_, dest); |
879 | fmt_helper::append_int(msg.source.line, dest); |
880 | } |
881 | }; |
882 | |
883 | // print source funcname |
884 | template<typename ScopedPadder> |
885 | class source_funcname_formatter final : public flag_formatter |
886 | { |
887 | public: |
888 | explicit source_funcname_formatter(padding_info padinfo) |
889 | : flag_formatter(padinfo) |
890 | {} |
891 | |
892 | void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override |
893 | { |
894 | if (msg.source.empty()) |
895 | { |
896 | ScopedPadder p(0, padinfo_, dest); |
897 | return; |
898 | } |
899 | size_t text_size = padinfo_.enabled() ? std::char_traits<char>::length(msg.source.funcname) : 0; |
900 | ScopedPadder p(text_size, padinfo_, dest); |
901 | fmt_helper::append_string_view(msg.source.funcname, dest); |
902 | } |
903 | }; |
904 | |
905 | // print elapsed time since last message |
906 | template<typename ScopedPadder, typename Units> |
907 | class elapsed_formatter final : public flag_formatter |
908 | { |
909 | public: |
910 | using DurationUnits = Units; |
911 | |
912 | explicit elapsed_formatter(padding_info padinfo) |
913 | : flag_formatter(padinfo) |
914 | , last_message_time_(log_clock::now()) |
915 | {} |
916 | |
917 | void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override |
918 | { |
919 | auto delta = (std::max)(msg.time - last_message_time_, log_clock::duration::zero()); |
920 | auto delta_units = std::chrono::duration_cast<DurationUnits>(delta); |
921 | last_message_time_ = msg.time; |
922 | auto delta_count = static_cast<size_t>(delta_units.count()); |
923 | auto n_digits = static_cast<size_t>(ScopedPadder::count_digits(delta_count)); |
924 | ScopedPadder p(n_digits, padinfo_, dest); |
925 | fmt_helper::append_int(delta_count, dest); |
926 | } |
927 | |
928 | private: |
929 | log_clock::time_point last_message_time_; |
930 | }; |
931 | |
932 | // Full info formatter |
933 | // pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] [%s:%#] %v |
934 | class full_formatter final : public flag_formatter |
935 | { |
936 | public: |
937 | explicit full_formatter(padding_info padinfo) |
938 | : flag_formatter(padinfo) |
939 | {} |
940 | |
941 | void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) override |
942 | { |
943 | using std::chrono::duration_cast; |
944 | using std::chrono::milliseconds; |
945 | using std::chrono::seconds; |
946 | |
947 | // cache the date/time part for the next second. |
948 | auto duration = msg.time.time_since_epoch(); |
949 | auto secs = duration_cast<seconds>(duration); |
950 | |
951 | if (cache_timestamp_ != secs || cached_datetime_.size() == 0) |
952 | { |
953 | cached_datetime_.clear(); |
954 | cached_datetime_.push_back('['); |
955 | fmt_helper::append_int(tm_time.tm_year + 1900, cached_datetime_); |
956 | cached_datetime_.push_back('-'); |
957 | |
958 | fmt_helper::pad2(tm_time.tm_mon + 1, cached_datetime_); |
959 | cached_datetime_.push_back('-'); |
960 | |
961 | fmt_helper::pad2(tm_time.tm_mday, cached_datetime_); |
962 | cached_datetime_.push_back(' '); |
963 | |
964 | fmt_helper::pad2(tm_time.tm_hour, cached_datetime_); |
965 | cached_datetime_.push_back(':'); |
966 | |
967 | fmt_helper::pad2(tm_time.tm_min, cached_datetime_); |
968 | cached_datetime_.push_back(':'); |
969 | |
970 | fmt_helper::pad2(tm_time.tm_sec, cached_datetime_); |
971 | cached_datetime_.push_back('.'); |
972 | |
973 | cache_timestamp_ = secs; |
974 | } |
975 | dest.append(cached_datetime_.begin(), cached_datetime_.end()); |
976 | |
977 | auto millis = fmt_helper::time_fraction<milliseconds>(msg.time); |
978 | fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest); |
979 | dest.push_back(']'); |
980 | dest.push_back(' '); |
981 | |
982 | // append logger name if exists |
983 | if (msg.logger_name.size() > 0) |
984 | { |
985 | dest.push_back('['); |
986 | fmt_helper::append_string_view(msg.logger_name, dest); |
987 | dest.push_back(']'); |
988 | dest.push_back(' '); |
989 | } |
990 | |
991 | dest.push_back('['); |
992 | // wrap the level name with color |
993 | msg.color_range_start = dest.size(); |
994 | // fmt_helper::append_string_view(level::to_c_str(msg.level), dest); |
995 | fmt_helper::append_string_view(level::to_string_view(msg.level), dest); |
996 | msg.color_range_end = dest.size(); |
997 | dest.push_back(']'); |
998 | dest.push_back(' '); |
999 | |
1000 | // add source location if present |
1001 | if (!msg.source.empty()) |
1002 | { |
1003 | dest.push_back('['); |
1004 | const char *filename = details::short_filename_formatter<details::null_scoped_padder>::basename(msg.source.filename); |
1005 | fmt_helper::append_string_view(filename, dest); |
1006 | dest.push_back(':'); |
1007 | fmt_helper::append_int(msg.source.line, dest); |
1008 | dest.push_back(']'); |
1009 | dest.push_back(' '); |
1010 | } |
1011 | // fmt_helper::append_string_view(msg.msg(), dest); |
1012 | fmt_helper::append_string_view(msg.payload, dest); |
1013 | } |
1014 | |
1015 | private: |
1016 | std::chrono::seconds cache_timestamp_{0}; |
1017 | memory_buf_t cached_datetime_; |
1018 | }; |
1019 | |
1020 | } // namespace details |
1021 | |
1022 | SPDLOG_INLINE pattern_formatter::pattern_formatter( |
1023 | std::string pattern, pattern_time_type time_type, std::string eol, custom_flags custom_user_flags) |
1024 | : pattern_(std::move(pattern)) |
1025 | , eol_(std::move(eol)) |
1026 | , pattern_time_type_(time_type) |
1027 | , need_localtime_(false) |
1028 | , last_log_secs_(0) |
1029 | , custom_handlers_(std::move(custom_user_flags)) |
1030 | { |
1031 | std::memset(&cached_tm_, 0, sizeof(cached_tm_)); |
1032 | compile_pattern_(pattern_); |
1033 | } |
1034 | |
1035 | // use by default full formatter for if pattern is not given |
1036 | SPDLOG_INLINE pattern_formatter::pattern_formatter(pattern_time_type time_type, std::string eol) |
1037 | : pattern_("%+" ) |
1038 | , eol_(std::move(eol)) |
1039 | , pattern_time_type_(time_type) |
1040 | , need_localtime_(true) |
1041 | , last_log_secs_(0) |
1042 | { |
1043 | std::memset(&cached_tm_, 0, sizeof(cached_tm_)); |
1044 | formatters_.push_back(details::make_unique<details::full_formatter>(details::padding_info{})); |
1045 | } |
1046 | |
1047 | SPDLOG_INLINE std::unique_ptr<formatter> pattern_formatter::clone() const |
1048 | { |
1049 | custom_flags cloned_custom_formatters; |
1050 | for (auto &it : custom_handlers_) |
1051 | { |
1052 | cloned_custom_formatters[it.first] = it.second->clone(); |
1053 | } |
1054 | auto cloned = details::make_unique<pattern_formatter>(pattern_, pattern_time_type_, eol_, std::move(cloned_custom_formatters)); |
1055 | cloned->need_localtime(need_localtime_); |
1056 | #if defined(__GNUC__) && __GNUC__ < 5 |
1057 | return std::move(cloned); |
1058 | #else |
1059 | return cloned; |
1060 | #endif |
1061 | } |
1062 | |
1063 | SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory_buf_t &dest) |
1064 | { |
1065 | if (need_localtime_) |
1066 | { |
1067 | const auto secs = std::chrono::duration_cast<std::chrono::seconds>(msg.time.time_since_epoch()); |
1068 | if (secs != last_log_secs_) |
1069 | { |
1070 | cached_tm_ = get_time_(msg); |
1071 | last_log_secs_ = secs; |
1072 | } |
1073 | } |
1074 | |
1075 | for (auto &f : formatters_) |
1076 | { |
1077 | f->format(msg, cached_tm_, dest); |
1078 | } |
1079 | // write eol |
1080 | details::fmt_helper::append_string_view(eol_, dest); |
1081 | } |
1082 | |
1083 | SPDLOG_INLINE void pattern_formatter::set_pattern(std::string pattern) |
1084 | { |
1085 | pattern_ = std::move(pattern); |
1086 | need_localtime_ = false; |
1087 | compile_pattern_(pattern_); |
1088 | } |
1089 | |
1090 | SPDLOG_INLINE void pattern_formatter::need_localtime(bool need) |
1091 | { |
1092 | need_localtime_ = need; |
1093 | } |
1094 | |
1095 | SPDLOG_INLINE std::tm pattern_formatter::get_time_(const details::log_msg &msg) |
1096 | { |
1097 | if (pattern_time_type_ == pattern_time_type::local) |
1098 | { |
1099 | return details::os::localtime(log_clock::to_time_t(msg.time)); |
1100 | } |
1101 | return details::os::gmtime(log_clock::to_time_t(msg.time)); |
1102 | } |
1103 | |
1104 | template<typename Padder> |
1105 | SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_info padding) |
1106 | { |
1107 | // process custom flags |
1108 | auto it = custom_handlers_.find(flag); |
1109 | if (it != custom_handlers_.end()) |
1110 | { |
1111 | auto custom_handler = it->second->clone(); |
1112 | custom_handler->set_padding_info(padding); |
1113 | formatters_.push_back(std::move(custom_handler)); |
1114 | return; |
1115 | } |
1116 | |
1117 | // process built-in flags |
1118 | switch (flag) |
1119 | { |
1120 | case ('+'): // default formatter |
1121 | formatters_.push_back(details::make_unique<details::full_formatter>(padding)); |
1122 | need_localtime_ = true; |
1123 | break; |
1124 | |
1125 | case 'n': // logger name |
1126 | formatters_.push_back(details::make_unique<details::name_formatter<Padder>>(padding)); |
1127 | break; |
1128 | |
1129 | case 'l': // level |
1130 | formatters_.push_back(details::make_unique<details::level_formatter<Padder>>(padding)); |
1131 | break; |
1132 | |
1133 | case 'L': // short level |
1134 | formatters_.push_back(details::make_unique<details::short_level_formatter<Padder>>(padding)); |
1135 | break; |
1136 | |
1137 | case ('t'): // thread id |
1138 | formatters_.push_back(details::make_unique<details::t_formatter<Padder>>(padding)); |
1139 | break; |
1140 | |
1141 | case ('v'): // the message text |
1142 | formatters_.push_back(details::make_unique<details::v_formatter<Padder>>(padding)); |
1143 | break; |
1144 | |
1145 | case ('a'): // weekday |
1146 | formatters_.push_back(details::make_unique<details::a_formatter<Padder>>(padding)); |
1147 | need_localtime_ = true; |
1148 | break; |
1149 | |
1150 | case ('A'): // short weekday |
1151 | formatters_.push_back(details::make_unique<details::A_formatter<Padder>>(padding)); |
1152 | need_localtime_ = true; |
1153 | break; |
1154 | |
1155 | case ('b'): |
1156 | case ('h'): // month |
1157 | formatters_.push_back(details::make_unique<details::b_formatter<Padder>>(padding)); |
1158 | need_localtime_ = true; |
1159 | break; |
1160 | |
1161 | case ('B'): // short month |
1162 | formatters_.push_back(details::make_unique<details::B_formatter<Padder>>(padding)); |
1163 | need_localtime_ = true; |
1164 | break; |
1165 | |
1166 | case ('c'): // datetime |
1167 | formatters_.push_back(details::make_unique<details::c_formatter<Padder>>(padding)); |
1168 | need_localtime_ = true; |
1169 | break; |
1170 | |
1171 | case ('C'): // year 2 digits |
1172 | formatters_.push_back(details::make_unique<details::C_formatter<Padder>>(padding)); |
1173 | need_localtime_ = true; |
1174 | break; |
1175 | |
1176 | case ('Y'): // year 4 digits |
1177 | formatters_.push_back(details::make_unique<details::Y_formatter<Padder>>(padding)); |
1178 | need_localtime_ = true; |
1179 | break; |
1180 | |
1181 | case ('D'): |
1182 | case ('x'): // datetime MM/DD/YY |
1183 | formatters_.push_back(details::make_unique<details::D_formatter<Padder>>(padding)); |
1184 | need_localtime_ = true; |
1185 | break; |
1186 | |
1187 | case ('m'): // month 1-12 |
1188 | formatters_.push_back(details::make_unique<details::m_formatter<Padder>>(padding)); |
1189 | need_localtime_ = true; |
1190 | break; |
1191 | |
1192 | case ('d'): // day of month 1-31 |
1193 | formatters_.push_back(details::make_unique<details::d_formatter<Padder>>(padding)); |
1194 | need_localtime_ = true; |
1195 | break; |
1196 | |
1197 | case ('H'): // hours 24 |
1198 | formatters_.push_back(details::make_unique<details::H_formatter<Padder>>(padding)); |
1199 | need_localtime_ = true; |
1200 | break; |
1201 | |
1202 | case ('I'): // hours 12 |
1203 | formatters_.push_back(details::make_unique<details::I_formatter<Padder>>(padding)); |
1204 | need_localtime_ = true; |
1205 | break; |
1206 | |
1207 | case ('M'): // minutes |
1208 | formatters_.push_back(details::make_unique<details::M_formatter<Padder>>(padding)); |
1209 | need_localtime_ = true; |
1210 | break; |
1211 | |
1212 | case ('S'): // seconds |
1213 | formatters_.push_back(details::make_unique<details::S_formatter<Padder>>(padding)); |
1214 | need_localtime_ = true; |
1215 | break; |
1216 | |
1217 | case ('e'): // milliseconds |
1218 | formatters_.push_back(details::make_unique<details::e_formatter<Padder>>(padding)); |
1219 | break; |
1220 | |
1221 | case ('f'): // microseconds |
1222 | formatters_.push_back(details::make_unique<details::f_formatter<Padder>>(padding)); |
1223 | break; |
1224 | |
1225 | case ('F'): // nanoseconds |
1226 | formatters_.push_back(details::make_unique<details::F_formatter<Padder>>(padding)); |
1227 | break; |
1228 | |
1229 | case ('E'): // seconds since epoch |
1230 | formatters_.push_back(details::make_unique<details::E_formatter<Padder>>(padding)); |
1231 | break; |
1232 | |
1233 | case ('p'): // am/pm |
1234 | formatters_.push_back(details::make_unique<details::p_formatter<Padder>>(padding)); |
1235 | need_localtime_ = true; |
1236 | break; |
1237 | |
1238 | case ('r'): // 12 hour clock 02:55:02 pm |
1239 | formatters_.push_back(details::make_unique<details::r_formatter<Padder>>(padding)); |
1240 | need_localtime_ = true; |
1241 | break; |
1242 | |
1243 | case ('R'): // 24-hour HH:MM time |
1244 | formatters_.push_back(details::make_unique<details::R_formatter<Padder>>(padding)); |
1245 | need_localtime_ = true; |
1246 | break; |
1247 | |
1248 | case ('T'): |
1249 | case ('X'): // ISO 8601 time format (HH:MM:SS) |
1250 | formatters_.push_back(details::make_unique<details::T_formatter<Padder>>(padding)); |
1251 | need_localtime_ = true; |
1252 | break; |
1253 | |
1254 | case ('z'): // timezone |
1255 | formatters_.push_back(details::make_unique<details::z_formatter<Padder>>(padding)); |
1256 | need_localtime_ = true; |
1257 | break; |
1258 | |
1259 | case ('P'): // pid |
1260 | formatters_.push_back(details::make_unique<details::pid_formatter<Padder>>(padding)); |
1261 | break; |
1262 | |
1263 | case ('^'): // color range start |
1264 | formatters_.push_back(details::make_unique<details::color_start_formatter>(padding)); |
1265 | break; |
1266 | |
1267 | case ('$'): // color range end |
1268 | formatters_.push_back(details::make_unique<details::color_stop_formatter>(padding)); |
1269 | break; |
1270 | |
1271 | case ('@'): // source location (filename:filenumber) |
1272 | formatters_.push_back(details::make_unique<details::source_location_formatter<Padder>>(padding)); |
1273 | break; |
1274 | |
1275 | case ('s'): // short source filename - without directory name |
1276 | formatters_.push_back(details::make_unique<details::short_filename_formatter<Padder>>(padding)); |
1277 | break; |
1278 | |
1279 | case ('g'): // full source filename |
1280 | formatters_.push_back(details::make_unique<details::source_filename_formatter<Padder>>(padding)); |
1281 | break; |
1282 | |
1283 | case ('#'): // source line number |
1284 | formatters_.push_back(details::make_unique<details::source_linenum_formatter<Padder>>(padding)); |
1285 | break; |
1286 | |
1287 | case ('!'): // source funcname |
1288 | formatters_.push_back(details::make_unique<details::source_funcname_formatter<Padder>>(padding)); |
1289 | break; |
1290 | |
1291 | case ('%'): // % char |
1292 | formatters_.push_back(details::make_unique<details::ch_formatter>('%')); |
1293 | break; |
1294 | |
1295 | case ('u'): // elapsed time since last log message in nanos |
1296 | formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::nanoseconds>>(padding)); |
1297 | break; |
1298 | |
1299 | case ('i'): // elapsed time since last log message in micros |
1300 | formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::microseconds>>(padding)); |
1301 | break; |
1302 | |
1303 | case ('o'): // elapsed time since last log message in millis |
1304 | formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::milliseconds>>(padding)); |
1305 | break; |
1306 | |
1307 | case ('O'): // elapsed time since last log message in seconds |
1308 | formatters_.push_back(details::make_unique<details::elapsed_formatter<Padder, std::chrono::seconds>>(padding)); |
1309 | break; |
1310 | |
1311 | default: // Unknown flag appears as is |
1312 | auto unknown_flag = details::make_unique<details::aggregate_formatter>(); |
1313 | |
1314 | if (!padding.truncate_) |
1315 | { |
1316 | unknown_flag->add_ch('%'); |
1317 | unknown_flag->add_ch(flag); |
1318 | formatters_.push_back((std::move(unknown_flag))); |
1319 | } |
1320 | // fix issue #1617 (prev char was '!' and should have been treated as funcname flag instead of truncating flag) |
1321 | // spdlog::set_pattern("[%10!] %v") => "[ main] some message" |
1322 | // spdlog::set_pattern("[%3!!] %v") => "[mai] some message" |
1323 | else |
1324 | { |
1325 | padding.truncate_ = false; |
1326 | formatters_.push_back(details::make_unique<details::source_funcname_formatter<Padder>>(padding)); |
1327 | unknown_flag->add_ch(flag); |
1328 | formatters_.push_back((std::move(unknown_flag))); |
1329 | } |
1330 | |
1331 | break; |
1332 | } |
1333 | } |
1334 | |
1335 | // Extract given pad spec (e.g. %8X, %=8X, %-8!X, %8!X, %=8!X, %-8!X, %+8!X) |
1336 | // Advance the given it pass the end of the padding spec found (if any) |
1337 | // Return padding. |
1338 | SPDLOG_INLINE details::padding_info pattern_formatter::handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end) |
1339 | { |
1340 | using details::padding_info; |
1341 | using details::scoped_padder; |
1342 | const size_t max_width = 64; |
1343 | if (it == end) |
1344 | { |
1345 | return padding_info{}; |
1346 | } |
1347 | |
1348 | padding_info::pad_side side; |
1349 | switch (*it) |
1350 | { |
1351 | case '-': |
1352 | side = padding_info::pad_side::right; |
1353 | ++it; |
1354 | break; |
1355 | case '=': |
1356 | side = padding_info::pad_side::center; |
1357 | ++it; |
1358 | break; |
1359 | default: |
1360 | side = details::padding_info::pad_side::left; |
1361 | break; |
1362 | } |
1363 | |
1364 | if (it == end || !std::isdigit(static_cast<unsigned char>(*it))) |
1365 | { |
1366 | return padding_info{}; // no padding if no digit found here |
1367 | } |
1368 | |
1369 | auto width = static_cast<size_t>(*it) - '0'; |
1370 | for (++it; it != end && std::isdigit(static_cast<unsigned char>(*it)); ++it) |
1371 | { |
1372 | auto digit = static_cast<size_t>(*it) - '0'; |
1373 | width = width * 10 + digit; |
1374 | } |
1375 | |
1376 | // search for the optional truncate marker '!' |
1377 | bool truncate; |
1378 | if (it != end && *it == '!') |
1379 | { |
1380 | truncate = true; |
1381 | ++it; |
1382 | } |
1383 | else |
1384 | { |
1385 | truncate = false; |
1386 | } |
1387 | return details::padding_info{std::min<size_t>(width, max_width), side, truncate}; |
1388 | } |
1389 | |
1390 | SPDLOG_INLINE void pattern_formatter::compile_pattern_(const std::string &pattern) |
1391 | { |
1392 | auto end = pattern.end(); |
1393 | std::unique_ptr<details::aggregate_formatter> user_chars; |
1394 | formatters_.clear(); |
1395 | for (auto it = pattern.begin(); it != end; ++it) |
1396 | { |
1397 | if (*it == '%') |
1398 | { |
1399 | if (user_chars) // append user chars found so far |
1400 | { |
1401 | formatters_.push_back(std::move(user_chars)); |
1402 | } |
1403 | |
1404 | auto padding = handle_padspec_(++it, end); |
1405 | |
1406 | if (it != end) |
1407 | { |
1408 | if (padding.enabled()) |
1409 | { |
1410 | handle_flag_<details::scoped_padder>(*it, padding); |
1411 | } |
1412 | else |
1413 | { |
1414 | handle_flag_<details::null_scoped_padder>(*it, padding); |
1415 | } |
1416 | } |
1417 | else |
1418 | { |
1419 | break; |
1420 | } |
1421 | } |
1422 | else // chars not following the % sign should be displayed as is |
1423 | { |
1424 | if (!user_chars) |
1425 | { |
1426 | user_chars = details::make_unique<details::aggregate_formatter>(); |
1427 | } |
1428 | user_chars->add_ch(*it); |
1429 | } |
1430 | } |
1431 | if (user_chars) // append raw chars found so far |
1432 | { |
1433 | formatters_.push_back(std::move(user_chars)); |
1434 | } |
1435 | } |
1436 | } // namespace spdlog |
1437 | |