pattern_formatter-inl.h 45 KB

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