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