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