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