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/formatter.h>
9 #include <spdlog/details/log_msg.h>
10 #include <spdlog/details/os.h>
11 #include <spdlog/fmt/fmt.h>
12 
13 #include <chrono>
14 #include <ctime>
15 #include <memory>
16 #include <mutex>
17 #include <string>
18 #include <thread>
19 #include <utility>
20 #include <vector>
21 
22 namespace spdlog
23 {
24 namespace details
25 {
26 class flag_formatter
27 {
28 public:
~flag_formatter()29     virtual ~flag_formatter()
30     {}
31     virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0;
32 };
33 
34 ///////////////////////////////////////////////////////////////////////
35 // name & level pattern appenders
36 ///////////////////////////////////////////////////////////////////////
37 namespace
38 {
39 class name_formatter:public flag_formatter
40 {
format(details::log_msg & msg,const std::tm &)41     void format(details::log_msg& msg, const std::tm&) override
42     {
43         msg.formatted << *msg.logger_name;
44     }
45 };
46 }
47 
48 // log level appender
49 class level_formatter:public flag_formatter
50 {
format(details::log_msg & msg,const std::tm &)51     void format(details::log_msg& msg, const std::tm&) override
52     {
53         msg.formatted << level::to_str(msg.level);
54     }
55 };
56 
57 // short log level appender
58 class short_level_formatter:public flag_formatter
59 {
format(details::log_msg & msg,const std::tm &)60     void format(details::log_msg& msg, const std::tm&) override
61     {
62         msg.formatted << level::to_short_str(msg.level);
63     }
64 };
65 
66 ///////////////////////////////////////////////////////////////////////
67 // Date time pattern appenders
68 ///////////////////////////////////////////////////////////////////////
69 
ampm(const tm & t)70 static const char* ampm(const tm& t)
71 {
72     return t.tm_hour >= 12 ? "PM" : "AM";
73 }
74 
to12h(const tm & t)75 static int to12h(const tm& t)
76 {
77     return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour;
78 }
79 
80 //Abbreviated weekday name
81 static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
82 class a_formatter:public flag_formatter
83 {
format(details::log_msg & msg,const std::tm & tm_time)84     void format(details::log_msg& msg, const std::tm& tm_time) override
85     {
86         msg.formatted << days[tm_time.tm_wday];
87     }
88 };
89 
90 //Full weekday name
91 static const std::string full_days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
92 class A_formatter:public flag_formatter
93 {
format(details::log_msg & msg,const std::tm & tm_time)94     void format(details::log_msg& msg, const std::tm& tm_time) override
95     {
96         msg.formatted << full_days[tm_time.tm_wday];
97     }
98 };
99 
100 //Abbreviated month
101 static const std::string  months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" };
102 class b_formatter:public flag_formatter
103 {
format(details::log_msg & msg,const std::tm & tm_time)104     void format(details::log_msg& msg, const std::tm& tm_time) override
105     {
106         msg.formatted << months[tm_time.tm_mon];
107     }
108 };
109 
110 //Full month name
111 static const std::string full_months[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
112 class B_formatter:public flag_formatter
113 {
format(details::log_msg & msg,const std::tm & tm_time)114     void format(details::log_msg& msg, const std::tm& tm_time) override
115     {
116         msg.formatted << full_months[tm_time.tm_mon];
117     }
118 };
119 
120 
121 //write 2 ints seperated by sep with padding of 2
pad_n_join(fmt::MemoryWriter & w,int v1,int v2,char sep)122 static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, char sep)
123 {
124     w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0');
125     return w;
126 }
127 
128 //write 3 ints seperated by sep with padding of 2
pad_n_join(fmt::MemoryWriter & w,int v1,int v2,int v3,char sep)129 static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v3, char sep)
130 {
131     w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0');
132     return w;
133 }
134 
135 
136 //Date and time representation (Thu Aug 23 15:35:46 2014)
137 class c_formatter:public flag_formatter
138 {
format(details::log_msg & msg,const std::tm & tm_time)139     void format(details::log_msg& msg, const std::tm& tm_time) override
140     {
141         msg.formatted << days[tm_time.tm_wday] << ' ' << months[tm_time.tm_mon] << ' ' << tm_time.tm_mday << ' ';
142         pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << tm_time.tm_year + 1900;
143     }
144 };
145 
146 
147 // year - 2 digit
148 class C_formatter:public flag_formatter
149 {
format(details::log_msg & msg,const std::tm & tm_time)150     void format(details::log_msg& msg, const std::tm& tm_time) override
151     {
152         msg.formatted << fmt::pad(tm_time.tm_year % 100, 2, '0');
153     }
154 };
155 
156 
157 
158 // Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
159 class D_formatter:public flag_formatter
160 {
format(details::log_msg & msg,const std::tm & tm_time)161     void format(details::log_msg& msg, const std::tm& tm_time) override
162     {
163         pad_n_join(msg.formatted, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_year % 100, '/');
164     }
165 };
166 
167 
168 // year - 4 digit
169 class Y_formatter:public flag_formatter
170 {
format(details::log_msg & msg,const std::tm & tm_time)171     void format(details::log_msg& msg, const std::tm& tm_time) override
172     {
173         msg.formatted << tm_time.tm_year + 1900;
174     }
175 };
176 
177 // month 1-12
178 class m_formatter:public flag_formatter
179 {
format(details::log_msg & msg,const std::tm & tm_time)180     void format(details::log_msg& msg, const std::tm& tm_time) override
181     {
182         msg.formatted << fmt::pad(tm_time.tm_mon + 1, 2, '0');
183     }
184 };
185 
186 // day of month 1-31
187 class d_formatter:public flag_formatter
188 {
format(details::log_msg & msg,const std::tm & tm_time)189     void format(details::log_msg& msg, const std::tm& tm_time) override
190     {
191         msg.formatted << fmt::pad(tm_time.tm_mday, 2, '0');
192     }
193 };
194 
195 // hours in 24 format  0-23
196 class H_formatter:public flag_formatter
197 {
format(details::log_msg & msg,const std::tm & tm_time)198     void format(details::log_msg& msg, const std::tm& tm_time) override
199     {
200         msg.formatted << fmt::pad(tm_time.tm_hour, 2, '0');
201     }
202 };
203 
204 // hours in 12 format  1-12
205 class I_formatter:public flag_formatter
206 {
format(details::log_msg & msg,const std::tm & tm_time)207     void format(details::log_msg& msg, const std::tm& tm_time) override
208     {
209         msg.formatted << fmt::pad(to12h(tm_time), 2, '0');
210     }
211 };
212 
213 // minutes 0-59
214 class M_formatter:public flag_formatter
215 {
format(details::log_msg & msg,const std::tm & tm_time)216     void format(details::log_msg& msg, const std::tm& tm_time) override
217     {
218         msg.formatted << fmt::pad(tm_time.tm_min, 2, '0');
219     }
220 };
221 
222 // seconds 0-59
223 class S_formatter:public flag_formatter
224 {
format(details::log_msg & msg,const std::tm & tm_time)225     void format(details::log_msg& msg, const std::tm& tm_time) override
226     {
227         msg.formatted << fmt::pad(tm_time.tm_sec, 2, '0');
228     }
229 };
230 
231 // milliseconds
232 class e_formatter:public flag_formatter
233 {
format(details::log_msg & msg,const std::tm &)234     void format(details::log_msg& msg, const std::tm&) override
235     {
236         auto duration = msg.time.time_since_epoch();
237         auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
238         msg.formatted << fmt::pad(static_cast<int>(millis), 3, '0');
239     }
240 };
241 
242 // microseconds
243 class f_formatter:public flag_formatter
244 {
format(details::log_msg & msg,const std::tm &)245     void format(details::log_msg& msg, const std::tm&) override
246     {
247         auto duration = msg.time.time_since_epoch();
248         auto micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count() % 1000000;
249         msg.formatted << fmt::pad(static_cast<int>(micros), 6, '0');
250     }
251 };
252 
253 // nanoseconds
254 class F_formatter:public flag_formatter
255 {
format(details::log_msg & msg,const std::tm &)256     void format(details::log_msg& msg, const std::tm&) override
257     {
258         auto duration = msg.time.time_since_epoch();
259         auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % 1000000000;
260         msg.formatted << fmt::pad(static_cast<int>(ns), 9, '0');
261     }
262 };
263 
264 // AM/PM
265 class p_formatter:public flag_formatter
266 {
format(details::log_msg & msg,const std::tm & tm_time)267     void format(details::log_msg& msg, const std::tm& tm_time) override
268     {
269         msg.formatted << ampm(tm_time);
270     }
271 };
272 
273 
274 // 12 hour clock 02:55:02 pm
275 class r_formatter:public flag_formatter
276 {
format(details::log_msg & msg,const std::tm & tm_time)277     void format(details::log_msg& msg, const std::tm& tm_time) override
278     {
279         pad_n_join(msg.formatted, to12h(tm_time), tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << ampm(tm_time);
280     }
281 };
282 
283 // 24-hour HH:MM time, equivalent to %H:%M
284 class R_formatter:public flag_formatter
285 {
format(details::log_msg & msg,const std::tm & tm_time)286     void format(details::log_msg& msg, const std::tm& tm_time) override
287     {
288         pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, ':');
289     }
290 };
291 
292 // ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
293 class T_formatter:public flag_formatter
294 {
format(details::log_msg & msg,const std::tm & tm_time)295     void format(details::log_msg& msg, const std::tm& tm_time) override
296     {
297         pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':');
298     }
299 };
300 
301 
302 // ISO 8601 offset from UTC in timezone (+-HH:MM)
303 class z_formatter:public flag_formatter
304 {
305 public:
306     const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
307 
z_formatter()308     z_formatter():_last_update(std::chrono::seconds(0))
309     {}
310     z_formatter(const z_formatter&) = delete;
311     z_formatter& operator=(const z_formatter&) = delete;
312 
format(details::log_msg & msg,const std::tm & tm_time)313     void format(details::log_msg& msg, const std::tm& tm_time) override
314     {
315 #ifdef _WIN32
316         int total_minutes = get_cached_offset(msg, tm_time);
317 #else
318         // No need to chache under gcc,
319         // it is very fast (already stored in tm.tm_gmtoff)
320         int total_minutes = os::utc_minutes_offset(tm_time);
321 #endif
322         bool is_negative = total_minutes < 0;
323         char sign;
324         if (is_negative)
325         {
326             total_minutes = -total_minutes;
327             sign = '-';
328         }
329         else
330         {
331             sign = '+';
332         }
333 
334         int h = total_minutes / 60;
335         int m = total_minutes % 60;
336         msg.formatted << sign;
337         pad_n_join(msg.formatted, h, m, ':');
338     }
339 private:
340     log_clock::time_point _last_update;
341     int _offset_minutes;
342     std::mutex _mutex;
343 
get_cached_offset(const log_msg & msg,const std::tm & tm_time)344     int get_cached_offset(const log_msg& msg, const std::tm& tm_time)
345     {
346         using namespace std::chrono;
347         std::lock_guard<std::mutex> l(_mutex);
348         if (msg.time - _last_update >= cache_refresh)
349         {
350             _offset_minutes = os::utc_minutes_offset(tm_time);
351             _last_update = msg.time;
352         }
353         return _offset_minutes;
354     }
355 };
356 
357 
358 
359 //Thread id
360 class t_formatter:public flag_formatter
361 {
format(details::log_msg & msg,const std::tm &)362     void format(details::log_msg& msg, const std::tm&) override
363     {
364         msg.formatted << msg.thread_id;
365     }
366 };
367 
368 
369 class v_formatter:public flag_formatter
370 {
format(details::log_msg & msg,const std::tm &)371     void format(details::log_msg& msg, const std::tm&) override
372     {
373         msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
374     }
375 };
376 
377 class ch_formatter:public flag_formatter
378 {
379 public:
ch_formatter(char ch)380     explicit ch_formatter(char ch): _ch(ch)
381     {}
format(details::log_msg & msg,const std::tm &)382     void format(details::log_msg& msg, const std::tm&) override
383     {
384         msg.formatted << _ch;
385     }
386 private:
387     char _ch;
388 };
389 
390 
391 //aggregate user chars to display as is
392 class aggregate_formatter:public flag_formatter
393 {
394 public:
aggregate_formatter()395     aggregate_formatter()
396     {}
add_ch(char ch)397     void add_ch(char ch)
398     {
399         _str += ch;
400     }
format(details::log_msg & msg,const std::tm &)401     void format(details::log_msg& msg, const std::tm&) override
402     {
403         msg.formatted << _str;
404     }
405 private:
406     std::string _str;
407 };
408 
409 // Full info formatter
410 // pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
411 class full_formatter:public flag_formatter
412 {
format(details::log_msg & msg,const std::tm & tm_time)413     void format(details::log_msg& msg, const std::tm& tm_time) override
414     {
415 #ifndef SPDLOG_NO_DATETIME
416         auto duration = msg.time.time_since_epoch();
417         auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
418 
419         /* Slower version(while still very fast - about 3.2 million lines/sec under 10 threads),
420         msg.formatted.write("[{:d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}.{:03d}] [{}] [{}] {} ",
421         tm_time.tm_year + 1900,
422         tm_time.tm_mon + 1,
423         tm_time.tm_mday,
424         tm_time.tm_hour,
425         tm_time.tm_min,
426         tm_time.tm_sec,
427         static_cast<int>(millis),
428         msg.logger_name,
429         level::to_str(msg.level),
430         msg.raw.str());*/
431 
432 
433         // Faster (albeit uglier) way to format the line (5.6 million lines/sec under 10 threads)
434         msg.formatted << '[' << static_cast<unsigned int>(tm_time.tm_year + 1900) << '-'
435                       << fmt::pad(static_cast<unsigned int>(tm_time.tm_mon + 1), 2, '0') << '-'
436                       << fmt::pad(static_cast<unsigned int>(tm_time.tm_mday), 2, '0') << ' '
437                       << fmt::pad(static_cast<unsigned int>(tm_time.tm_hour), 2, '0') << ':'
438                       << fmt::pad(static_cast<unsigned int>(tm_time.tm_min), 2, '0') << ':'
439                       << fmt::pad(static_cast<unsigned int>(tm_time.tm_sec), 2, '0') << '.'
440                       << fmt::pad(static_cast<unsigned int>(millis), 3, '0') << "] ";
441 
442         //no datetime needed
443 #else
444         (void)tm_time;
445 #endif
446 
447 #ifndef SPDLOG_NO_NAME
448         msg.formatted << '[' << *msg.logger_name << "] ";
449 #endif
450 
451         msg.formatted << '[' << level::to_str(msg.level) << "] ";
452         msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
453     }
454 };
455 
456 }
457 }
458 ///////////////////////////////////////////////////////////////////////////////
459 // pattern_formatter inline impl
460 ///////////////////////////////////////////////////////////////////////////////
pattern_formatter(const std::string & pattern)461 inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern)
462 {
463     compile_pattern(pattern);
464 }
465 
compile_pattern(const std::string & pattern)466 inline void spdlog::pattern_formatter::compile_pattern(const std::string& pattern)
467 {
468     auto end = pattern.end();
469     std::unique_ptr<details::aggregate_formatter> user_chars;
470     for (auto it = pattern.begin(); it != end; ++it)
471     {
472         if (*it == '%')
473         {
474             if (user_chars) //append user chars found so far
475                 _formatters.push_back(std::move(user_chars));
476 
477             if (++it != end)
478                 handle_flag(*it);
479             else
480                 break;
481         }
482         else // chars not following the % sign should be displayed as is
483         {
484             if (!user_chars)
485                 user_chars = std::unique_ptr<details::aggregate_formatter>(new details::aggregate_formatter());
486             user_chars->add_ch(*it);
487         }
488     }
489     if (user_chars) //append raw chars found so far
490     {
491         _formatters.push_back(std::move(user_chars));
492     }
493 
494 }
handle_flag(char flag)495 inline void spdlog::pattern_formatter::handle_flag(char flag)
496 {
497     switch (flag)
498     {
499     // logger name
500     case 'n':
501         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::name_formatter()));
502         break;
503 
504     case 'l':
505         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::level_formatter()));
506         break;
507 
508     case 'L':
509         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::short_level_formatter()));
510         break;
511 
512     case('t'):
513         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::t_formatter()));
514         break;
515 
516     case('v'):
517         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::v_formatter()));
518         break;
519 
520     case('a'):
521         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::a_formatter()));
522         break;
523 
524     case('A'):
525         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::A_formatter()));
526         break;
527 
528     case('b'):
529     case('h'):
530         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::b_formatter()));
531         break;
532 
533     case('B'):
534         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::B_formatter()));
535         break;
536     case('c'):
537         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::c_formatter()));
538         break;
539 
540     case('C'):
541         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::C_formatter()));
542         break;
543 
544     case('Y'):
545         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::Y_formatter()));
546         break;
547 
548     case('D'):
549     case('x'):
550 
551         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::D_formatter()));
552         break;
553 
554     case('m'):
555         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::m_formatter()));
556         break;
557 
558     case('d'):
559         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::d_formatter()));
560         break;
561 
562     case('H'):
563         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::H_formatter()));
564         break;
565 
566     case('I'):
567         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::I_formatter()));
568         break;
569 
570     case('M'):
571         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::M_formatter()));
572         break;
573 
574     case('S'):
575         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::S_formatter()));
576         break;
577 
578     case('e'):
579         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::e_formatter()));
580         break;
581 
582     case('f'):
583         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::f_formatter()));
584         break;
585     case('F'):
586         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::F_formatter()));
587         break;
588 
589     case('p'):
590         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::p_formatter()));
591         break;
592 
593     case('r'):
594         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::r_formatter()));
595         break;
596 
597     case('R'):
598         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::R_formatter()));
599         break;
600 
601     case('T'):
602     case('X'):
603         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::T_formatter()));
604         break;
605 
606     case('z'):
607         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::z_formatter()));
608         break;
609 
610     case ('+'):
611         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::full_formatter()));
612         break;
613 
614     default: //Unkown flag appears as is
615         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter('%')));
616         _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter(flag)));
617         break;
618     }
619 }
620 
621 
format(details::log_msg & msg)622 inline void spdlog::pattern_formatter::format(details::log_msg& msg)
623 {
624 
625 #ifndef SPDLOG_NO_DATETIME
626     auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time));
627 #else
628     std::tm tm_time;
629 #endif
630     for (auto &f : _formatters)
631     {
632         f->format(msg, tm_time);
633     }
634     //write eol
635     msg.formatted.write(details::os::eol, details::os::eol_size);
636 }
637