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