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