pattern_formatter-inl.h 46 KB

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