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