mirror of
https://github.com/gabime/spdlog.git
synced 2026-04-10 11:34:29 +08:00
Compare commits
1060 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
10578ff08c | ||
|
|
1f0513cf4e | ||
|
|
647470f3ae | ||
|
|
efd0dbe5c2 | ||
|
|
bd2fe64bf1 | ||
|
|
7153db954f | ||
|
|
3b425affd3 | ||
|
|
d5a79ad5d7 | ||
|
|
7951338d27 | ||
|
|
90801267ee | ||
|
|
8d57823e51 | ||
|
|
277ccc5e18 | ||
|
|
cff9db5044 | ||
|
|
216f905670 | ||
|
|
53b2308011 | ||
|
|
c368500efd | ||
|
|
2fed68a73b | ||
|
|
e7ab49c973 | ||
|
|
5496491aa4 | ||
|
|
53ca5b2870 | ||
|
|
6aced26c35 | ||
|
|
2331750b58 | ||
|
|
b3fb4c1265 | ||
|
|
3ad7b9b117 | ||
|
|
5721debdf1 | ||
|
|
c1c23d1e7b | ||
|
|
9605641982 | ||
|
|
e52672c263 | ||
|
|
50f070980e | ||
|
|
7733849478 | ||
|
|
4bbc8a89a0 | ||
|
|
c87882e82f | ||
|
|
bd4301b2c1 | ||
|
|
e771f4e75e | ||
|
|
35835469d7 | ||
|
|
0d6992fcdd | ||
|
|
29b3f471cf | ||
|
|
4985875a15 | ||
|
|
4fffd3a111 | ||
|
|
590749e8be | ||
|
|
27cc76766c | ||
|
|
d52cf87d71 | ||
|
|
2ddd6895e1 | ||
|
|
545e7d2de8 | ||
|
|
a9ed6b352b | ||
|
|
523eebe47d | ||
|
|
b303d8bc40 | ||
|
|
68118f4233 | ||
|
|
fcc6b97f88 | ||
|
|
adc4398cc5 | ||
|
|
c53d26cfca | ||
|
|
c188bee229 | ||
|
|
7f1a89e3f6 | ||
|
|
5d46f3fcab | ||
|
|
b55d95d365 | ||
|
|
494cc8bace | ||
|
|
03e8c0f45c | ||
|
|
b6388a15ff | ||
|
|
45a18a61c6 | ||
|
|
1857a44c7c | ||
|
|
bd9e1475e2 | ||
|
|
6883267996 | ||
|
|
b88c784634 | ||
|
|
31020f9eea | ||
|
|
e89d59995a | ||
|
|
bf324a11cd | ||
|
|
e149433a80 | ||
|
|
65d02e495e | ||
|
|
f196a9fd27 | ||
|
|
7f0398ca25 | ||
|
|
d7f05722d4 | ||
|
|
26377a2195 | ||
|
|
aa4eaa16bf | ||
|
|
abc7bfe5c9 | ||
|
|
f0f4499540 | ||
|
|
dae4f9fef6 | ||
|
|
4c45c6fbd8 | ||
|
|
172cf26d77 | ||
|
|
feefb7e7e2 | ||
|
|
ced44a15ea | ||
|
|
5c2855e1c1 | ||
|
|
433785dc64 | ||
|
|
28845b96bd | ||
|
|
98ec35cee1 | ||
|
|
f795297e15 | ||
|
|
3fd3c47e6d | ||
|
|
153c25dbb3 | ||
|
|
a1a6b7e64f | ||
|
|
3ea7fb18d6 | ||
|
|
6ff52332a8 | ||
|
|
5e75b104d6 | ||
|
|
dc893701f9 | ||
|
|
e6b0aaf94a | ||
|
|
e754cbf763 | ||
|
|
5988895d69 | ||
|
|
2af5eea2c6 | ||
|
|
554acb7429 | ||
|
|
9c5869ce5a | ||
|
|
e641ff64fd | ||
|
|
aa731e3297 | ||
|
|
ac6407bb8e | ||
|
|
baf08eee09 | ||
|
|
04a43cd6a1 | ||
|
|
ed8d099607 | ||
|
|
b693d0cd91 | ||
|
|
fafedd2d59 | ||
|
|
f3a7ef1199 | ||
|
|
cb890c96b9 | ||
|
|
37bfa092a5 | ||
|
|
c517cb64ae | ||
|
|
51e09fa504 | ||
|
|
691172e28b | ||
|
|
6cf6d2159b | ||
|
|
17f0b417d5 | ||
|
|
d89baf4c5b | ||
|
|
2eb52cd047 | ||
|
|
f5492aed12 | ||
|
|
c2efd6ee58 | ||
|
|
be507bf1cc | ||
|
|
f11f3ce8b7 | ||
|
|
b2a3e930c1 | ||
|
|
147bf04d08 | ||
|
|
f4d3616c4b | ||
|
|
c97c025adb | ||
|
|
c55336e78d | ||
|
|
13e9135935 | ||
|
|
5c1e44a93d | ||
|
|
75adf9e75e | ||
|
|
0fa09f6af4 | ||
|
|
011ed270e8 | ||
|
|
d7e58ce10e | ||
|
|
813536d4c6 | ||
|
|
b89023efa1 | ||
|
|
b155347560 | ||
|
|
15faf742f1 | ||
|
|
2ba7d1639e | ||
|
|
a2de7cf070 | ||
|
|
c6d558b6f2 | ||
|
|
d1b97c0ba9 | ||
|
|
755ce0a016 | ||
|
|
79334ca5ab | ||
|
|
11e9752536 | ||
|
|
72b0f9e8f7 | ||
|
|
408a162044 | ||
|
|
7d6444491c | ||
|
|
7bfb6d6b76 | ||
|
|
e1be7f3d6f | ||
|
|
04a8485b17 | ||
|
|
f330dd210e | ||
|
|
97dc27b5fa | ||
|
|
1fd43fe673 | ||
|
|
29e21cc7f3 | ||
|
|
292fc153ef | ||
|
|
25d3c83d3b | ||
|
|
6b7f3db28e | ||
|
|
eec6e28b19 | ||
|
|
f3e379cf78 | ||
|
|
0258c47774 | ||
|
|
f63df65245 | ||
|
|
099137fe9a | ||
|
|
36f253893e | ||
|
|
8280c0d64c | ||
|
|
4f98b000eb | ||
|
|
b5d61b963a | ||
|
|
a7f7984c4a | ||
|
|
dd33c16aae | ||
|
|
e0bf0c0301 | ||
|
|
8d8aacf5e9 | ||
|
|
e085ba7fcc | ||
|
|
33f881ac8b | ||
|
|
b24ef39b9d | ||
|
|
a6d8b52686 | ||
|
|
65407539bb | ||
|
|
543060683b | ||
|
|
2848e51755 | ||
|
|
0db4978899 | ||
|
|
0284a23d0a | ||
|
|
7e728869cc | ||
|
|
a19d93e1a2 | ||
|
|
5aefa1af3d | ||
|
|
f1718fb5b3 | ||
|
|
6b527a50dd | ||
|
|
74df115fc1 | ||
|
|
3adfeeec3e | ||
|
|
c4df94a1d9 | ||
|
|
da1d98d603 | ||
|
|
6683418983 | ||
|
|
2c1d97f1ad | ||
|
|
2f854428bc | ||
|
|
c1a524a969 | ||
|
|
23807e12e8 | ||
|
|
87ec1ab97b | ||
|
|
b057b979fa | ||
|
|
7dc378e296 | ||
|
|
6d8cc30f12 | ||
|
|
0335e3fcc0 | ||
|
|
76aa1059cd | ||
|
|
b0a25f0183 | ||
|
|
db1babab5e | ||
|
|
7ea951613d | ||
|
|
6506b73523 | ||
|
|
639029007d | ||
|
|
01eb59ca9b | ||
|
|
a8b5e3da29 | ||
|
|
8cc0997f79 | ||
|
|
ffb7c317b5 | ||
|
|
bb7420fc22 | ||
|
|
0df9164e7c | ||
|
|
dcd590b9de | ||
|
|
8dc3a66688 | ||
|
|
88b4adebdc | ||
|
|
01f2438c1f | ||
|
|
eb51f37c67 | ||
|
|
4ef4d0659d | ||
|
|
2ce9a3f70f | ||
|
|
59cbdaaf49 | ||
|
|
e0cf16b7e9 | ||
|
|
1cdf09e9dd | ||
|
|
9966a6a4b7 | ||
|
|
20a1d1c519 | ||
|
|
313ec87dc1 | ||
|
|
a7ba6e447d | ||
|
|
baa978ab0b | ||
|
|
9f1b4fc9e7 | ||
|
|
38e5dbd866 | ||
|
|
50ed27946d | ||
|
|
856ac7d773 | ||
|
|
81f12df8b5 | ||
|
|
9eca3234e8 | ||
|
|
5a540bdd42 | ||
|
|
6d394b132d | ||
|
|
96a317ce68 | ||
|
|
bca8945c26 | ||
|
|
a4b108334f | ||
|
|
4f72cf9744 | ||
|
|
3c30f77d31 | ||
|
|
bcb6484062 | ||
|
|
11472eddbc | ||
|
|
12470f6221 | ||
|
|
a82d0e2f57 | ||
|
|
dca1d1e0d1 | ||
|
|
0cef8f3d26 | ||
|
|
fbde18fc02 | ||
|
|
b640c59087 | ||
|
|
1f3dea60d3 | ||
|
|
a7c06eadd0 | ||
|
|
39910f5137 | ||
|
|
6fc4eb92db | ||
|
|
81e82fb2d3 | ||
|
|
c817254495 | ||
|
|
4578b0ad11 | ||
|
|
9fbf82b603 | ||
|
|
4b0267910c | ||
|
|
54456aee9e | ||
|
|
2a31cdcded | ||
|
|
d3f31c6038 | ||
|
|
59dd9f6203 | ||
|
|
05cac05c06 | ||
|
|
724713ac80 | ||
|
|
72f3d5291c | ||
|
|
c138685364 | ||
|
|
4180d00a6c | ||
|
|
2512ac1e3c | ||
|
|
121fc0a273 | ||
|
|
4d9281018f | ||
|
|
3a94a60537 | ||
|
|
161e6fb8fb | ||
|
|
29fa474e4a | ||
|
|
ddb19f4a4f | ||
|
|
789fb1e7c9 | ||
|
|
521c5317a2 | ||
|
|
e0d85e60a3 | ||
|
|
ac7821f9bf | ||
|
|
84809db955 | ||
|
|
e6cecd97ac | ||
|
|
60e7deaaf5 | ||
|
|
23b07d8cb6 | ||
|
|
5f27697198 | ||
|
|
261d2c5ae4 | ||
|
|
847f7de003 | ||
|
|
dffc8df3e0 | ||
|
|
330d491eba | ||
|
|
db103ff340 | ||
|
|
1ac46bacfe | ||
|
|
c27a4ee61f | ||
|
|
2d8c4b1c88 | ||
|
|
d969f8621d | ||
|
|
a4ec91fd06 | ||
|
|
fd53472238 | ||
|
|
b3ddef2fc2 | ||
|
|
07d753176f | ||
|
|
8d758add63 | ||
|
|
506ab1c735 | ||
|
|
945020e505 | ||
|
|
5a7bcd0a4f | ||
|
|
ae92279f5c | ||
|
|
be33f5eb89 | ||
|
|
717a582085 | ||
|
|
ee87aee4dd | ||
|
|
2d6afeebe1 | ||
|
|
49bc58da04 | ||
|
|
f5831d5132 | ||
|
|
517ccc4088 | ||
|
|
90dd56b839 | ||
|
|
d1794f4c1b | ||
|
|
75bb4346b2 | ||
|
|
13477e5478 | ||
|
|
1093897838 | ||
|
|
4d27419d7c | ||
|
|
89e6d66872 | ||
|
|
b97c16a636 | ||
|
|
751ff59e2a | ||
|
|
64a549d051 | ||
|
|
7a686d4d21 | ||
|
|
7b218737cc | ||
|
|
b1520a87c3 | ||
|
|
2a2a34601c | ||
|
|
3c64b3da97 | ||
|
|
e7889e9ce2 | ||
|
|
7c8f45747c | ||
|
|
d37def7a72 | ||
|
|
452770e374 | ||
|
|
54e44ab477 | ||
|
|
6012d52fdb | ||
|
|
7ffa0766b4 | ||
|
|
d8e17111b9 | ||
|
|
9e602a491b | ||
|
|
f529afa625 | ||
|
|
5a4deb6e88 | ||
|
|
3bcd3cef2f | ||
|
|
fbe6f945f3 | ||
|
|
bb0f3839c1 | ||
|
|
af4026104c | ||
|
|
822aee2b4f | ||
|
|
f09334dc6f | ||
|
|
d1d2609f49 | ||
|
|
9aa6cdc494 | ||
|
|
8b403081c1 | ||
|
|
dc054c3f8a | ||
|
|
94c2810b0a | ||
|
|
6e83abdbf2 | ||
|
|
f03eaaaf33 | ||
|
|
71162ebdbb | ||
|
|
c75549f6db | ||
|
|
2ebc96d8eb | ||
|
|
29f2eeea31 | ||
|
|
a13981ffe4 | ||
|
|
cf152e6030 | ||
|
|
b279196af2 | ||
|
|
98e151fda7 | ||
|
|
7f3b5fb84d | ||
|
|
3d069f7b46 | ||
|
|
65c4f955a6 | ||
|
|
246b4b01c5 | ||
|
|
a680b71dc7 | ||
|
|
d0b5b09318 | ||
|
|
67f3a83c31 | ||
|
|
5dd260c336 | ||
|
|
ee6f165a1f | ||
|
|
0cc2ff83ed | ||
|
|
a9e92d6c5c | ||
|
|
ea5f07110b | ||
|
|
59746c2e36 | ||
|
|
6399e05209 | ||
|
|
08de642536 | ||
|
|
ab9e1b3aa7 | ||
|
|
af6744b112 | ||
|
|
1d86803e38 | ||
|
|
b12c19162b | ||
|
|
220608e52a | ||
|
|
06fb5c7c69 | ||
|
|
8970fd5d2f | ||
|
|
67d5f65507 | ||
|
|
7d678be07a | ||
|
|
74e2aa9c66 | ||
|
|
b9cc158e52 | ||
|
|
e68cf1c9ed | ||
|
|
f0fcc73f92 | ||
|
|
a340b3812c | ||
|
|
78c833a09f | ||
|
|
38888ba5b3 | ||
|
|
99e519cf0f | ||
|
|
09cb45001b | ||
|
|
9d3aa5a253 | ||
|
|
314308f975 | ||
|
|
b658ff2124 | ||
|
|
c844ea4423 | ||
|
|
db5af8ead1 | ||
|
|
c09dee7717 | ||
|
|
352281313f | ||
|
|
8afe18f148 | ||
|
|
90f348d26a | ||
|
|
8d3d06b7a0 | ||
|
|
c56ee8ec03 | ||
|
|
76f6c10434 | ||
|
|
cf64f2baca | ||
|
|
68a0193d95 | ||
|
|
80740f0e46 | ||
|
|
c60f790793 | ||
|
|
e0b4ec54bd | ||
|
|
d1bed6bf45 | ||
|
|
b82966e775 | ||
|
|
19a9d87486 | ||
|
|
3448e5867e | ||
|
|
e013d6b98c | ||
|
|
d392739049 | ||
|
|
12266ad004 | ||
|
|
e03c160e27 | ||
|
|
03f0e2196e | ||
|
|
34ea38c12e | ||
|
|
a33de607df | ||
|
|
e39959a132 | ||
|
|
33a42202c7 | ||
|
|
efc358da9f | ||
|
|
c1b39eb2ce | ||
|
|
12e30f0eb4 | ||
|
|
94bf971f72 | ||
|
|
bcfa9241b8 | ||
|
|
eea9d6136f | ||
|
|
c35f33e61a | ||
|
|
78eeba940a | ||
|
|
b3ed5f77f2 | ||
|
|
38f6b5ea71 | ||
|
|
c000a6164c | ||
|
|
cb6208a6fa | ||
|
|
b540558db6 | ||
|
|
e70b2dfbb2 | ||
|
|
f64cad89c4 | ||
|
|
935b3de2ad | ||
|
|
abc0359522 | ||
|
|
fcd48d9b2d | ||
|
|
97a79a4511 | ||
|
|
31172b0ecc | ||
|
|
74bd1613bd | ||
|
|
1fb3f95fdb | ||
|
|
ca571e7a7a | ||
|
|
52b6be0dfe | ||
|
|
abd6a6784e | ||
|
|
978df46611 | ||
|
|
31c428cece | ||
|
|
67c892991a | ||
|
|
5743adc467 | ||
|
|
76fc166e11 | ||
|
|
459cd21070 | ||
|
|
fc53e3339f | ||
|
|
9b788a882d | ||
|
|
5a3e0d5e85 | ||
|
|
ce4da69cc0 | ||
|
|
1a779077db | ||
|
|
e79531b292 | ||
|
|
7fea60183f | ||
|
|
41df6c4df2 | ||
|
|
b6a28b497b | ||
|
|
c75fbabc0f | ||
|
|
4cc0876efa | ||
|
|
abe1e37253 | ||
|
|
4dec965569 | ||
|
|
f478eaa98e | ||
|
|
1e105f88ca | ||
|
|
6586f3ed29 | ||
|
|
ba7019de8a | ||
|
|
9562ce3b87 | ||
|
|
a48fe674ee | ||
|
|
d439f75491 | ||
|
|
c559067f77 | ||
|
|
30bd80bd85 | ||
|
|
5709cb10d1 | ||
|
|
9329f8d3cd | ||
|
|
884c23a9c9 | ||
|
|
7a3a560c44 | ||
|
|
2963da1392 | ||
|
|
54f1941691 | ||
|
|
ca14ae19db | ||
|
|
6636ae6e63 | ||
|
|
2e75f42c69 | ||
|
|
c9547f383a | ||
|
|
65576707bf | ||
|
|
6ec8a06a09 | ||
|
|
9205c9d031 | ||
|
|
1ef80d6330 | ||
|
|
322665a22f | ||
|
|
cfa6d12691 | ||
|
|
c264c3e2dd | ||
|
|
d21bcd2f87 | ||
|
|
84f25b9f18 | ||
|
|
576fec4c36 | ||
|
|
48acafd10d | ||
|
|
a532a072ce | ||
|
|
2cd53c6ff1 | ||
|
|
18ccd55725 | ||
|
|
87eb569929 | ||
|
|
92387b1527 | ||
|
|
dd2f293f33 | ||
|
|
24e4f0aa87 | ||
|
|
6fe899af10 | ||
|
|
107fe0a142 | ||
|
|
b021be29e5 | ||
|
|
55e7844ca0 | ||
|
|
8dd85285e7 | ||
|
|
dbcbeb7a57 | ||
|
|
a9aee1c5b3 | ||
|
|
b3fe4b54c8 | ||
|
|
872ea6bf09 | ||
|
|
32fb9d51b9 | ||
|
|
ce637440bb | ||
|
|
61e4597488 | ||
|
|
26a064ed2d | ||
|
|
d5c9bac3c7 | ||
|
|
1665006401 | ||
|
|
5220ac4a9e | ||
|
|
ee0fdf016a | ||
|
|
8b4eedb594 | ||
|
|
01f5efa1d9 | ||
|
|
130bc26b9a | ||
|
|
09e83937de | ||
|
|
42e30468a9 | ||
|
|
3834acad5b | ||
|
|
654f7eceee | ||
|
|
ca9c8ae5fb | ||
|
|
1752086cfd | ||
|
|
369b2f7cd2 | ||
|
|
8b244ca988 | ||
|
|
fb9e51d943 | ||
|
|
bb3dc87953 | ||
|
|
6bcb422c80 | ||
|
|
540f865355 | ||
|
|
46ef71e3ec | ||
|
|
005450ff13 | ||
|
|
f809427575 | ||
|
|
9564eb2edb | ||
|
|
49708f209b | ||
|
|
14381fe8d0 | ||
|
|
17bec5c3ce | ||
|
|
2b90ab496a | ||
|
|
74dbf4cf70 | ||
|
|
e504aceeb5 | ||
|
|
3ce9ac74a6 | ||
|
|
3b0e7b4d0d | ||
|
|
5e856c6b4d | ||
|
|
6651a48c4d | ||
|
|
c031ae2aab | ||
|
|
1ac6c9f9c2 | ||
|
|
5d0eb6dda5 | ||
|
|
29c949ab03 | ||
|
|
576e389788 | ||
|
|
7b15a3d345 | ||
|
|
eedb43d756 | ||
|
|
338125b93a | ||
|
|
3ecc3ab798 | ||
|
|
de1cdb2dbe | ||
|
|
c9887874bc | ||
|
|
69fcaf14e5 | ||
|
|
f414198fee | ||
|
|
2de924a187 | ||
|
|
c1c2ff2d07 | ||
|
|
ff89f1476d | ||
|
|
e8d99cee70 | ||
|
|
ccfa3f03b0 | ||
|
|
baefe0b3f6 | ||
|
|
3669351427 | ||
|
|
79938b98da | ||
|
|
411d588fea | ||
|
|
7e63d773ef | ||
|
|
3e378f009d | ||
|
|
13db9d9452 | ||
|
|
c1c6e6265c | ||
|
|
a984b1b073 | ||
|
|
215b6aea95 | ||
|
|
96b7214ae2 | ||
|
|
1b0752b0a9 | ||
|
|
02329f61e3 | ||
|
|
7e29c48379 | ||
|
|
ad63efdaf7 | ||
|
|
6bec53dcd2 | ||
|
|
fef405ac98 | ||
|
|
97f9cc4bc0 | ||
|
|
7ab6fd9db6 | ||
|
|
b7ecec0c23 | ||
|
|
d12a858897 | ||
|
|
9716ff69c4 | ||
|
|
4dd1a24d0b | ||
|
|
c69c49047b | ||
|
|
bfbb4e4050 | ||
|
|
2aceb13f3e | ||
|
|
e9f34fbd26 | ||
|
|
17f9cdd401 | ||
|
|
156b856a80 | ||
|
|
e2e3df9013 | ||
|
|
ef8773a89b | ||
|
|
536f5d8203 | ||
|
|
631416d54a | ||
|
|
d366a06461 | ||
|
|
7bf8f14879 | ||
|
|
cd65d6de69 | ||
|
|
b57d514b1e | ||
|
|
f36be4d5e4 | ||
|
|
c2b0e223fa | ||
|
|
e32c856a04 | ||
|
|
bc7cd2ccc2 | ||
|
|
1842669104 | ||
|
|
c7535a91a6 | ||
|
|
99a5484dfb | ||
|
|
b78ae5ab10 | ||
|
|
f74d3e7e94 | ||
|
|
eba37e8fbe | ||
|
|
84fb11599e | ||
|
|
099812d219 | ||
|
|
c4291510e8 | ||
|
|
775a411215 | ||
|
|
59b4dd4c46 | ||
|
|
2f907e3a92 | ||
|
|
9971fd2864 | ||
|
|
23a394d1fc | ||
|
|
91d450df4e | ||
|
|
ea3943a87a | ||
|
|
019eda5b0c | ||
|
|
5056437ca1 | ||
|
|
90c912a5e2 | ||
|
|
9219613957 | ||
|
|
9858f2e499 | ||
|
|
6b0bf33f8e | ||
|
|
9ea6079072 | ||
|
|
478f16234d | ||
|
|
57a312cb1a | ||
|
|
bb88a74f92 | ||
|
|
4a07ce5fae | ||
|
|
043c4acc7e | ||
|
|
eb478e38b2 | ||
|
|
bf307e24c5 | ||
|
|
4a00590a1b | ||
|
|
4ccca079a5 | ||
|
|
9e5d1b3ba5 | ||
|
|
240f13a0a6 | ||
|
|
221ce33eb5 | ||
|
|
722943000e | ||
|
|
509a503761 | ||
|
|
bac1e4a850 | ||
|
|
934cc892eb | ||
|
|
b1c90f08cc | ||
|
|
680f19a424 | ||
|
|
621e0a8330 | ||
|
|
4449bf6f83 | ||
|
|
569c62e528 | ||
|
|
4c240edf94 | ||
|
|
0e977d66c1 | ||
|
|
3dee10772d | ||
|
|
1d72edcc4f | ||
|
|
c29b7d22d9 | ||
|
|
ee502aed49 | ||
|
|
414ff25564 | ||
|
|
b084b8b1d8 | ||
|
|
3ca19a8580 | ||
|
|
9c12a44d6e | ||
|
|
4706b0ada4 | ||
|
|
cc98e9850d | ||
|
|
af80db8c22 | ||
|
|
053d5ad24d | ||
|
|
e2805ac68a | ||
|
|
1caf05cc52 | ||
|
|
63cfb7db25 | ||
|
|
bdfc7d2a5a | ||
|
|
0ccbdcdd1f | ||
|
|
c598b2fa2d | ||
|
|
4f86448bd4 | ||
|
|
d235e7d46f | ||
|
|
e25b323d1b | ||
|
|
42093c48b2 | ||
|
|
9fca0b20f0 | ||
|
|
a6229d9e87 | ||
|
|
c443896644 | ||
|
|
79f11bd655 | ||
|
|
c5552dac1f | ||
|
|
734af31c13 | ||
|
|
2d96896fae | ||
|
|
20a0f82701 | ||
|
|
e4b7dbce7f | ||
|
|
0f128fd561 | ||
|
|
ac8a7bc12d | ||
|
|
90a299f424 | ||
|
|
00a7f5d75d | ||
|
|
2463fe92bd | ||
|
|
4f65fcd7b1 | ||
|
|
e41b92c55a | ||
|
|
3925f8fa16 | ||
|
|
cce1e36e26 | ||
|
|
3466c9c8cb | ||
|
|
6e2dadc63a | ||
|
|
00e89a23f6 | ||
|
|
a29e518cfe | ||
|
|
a7148b718e | ||
|
|
23fdc0eae4 | ||
|
|
8cb1bc89f1 | ||
|
|
1798a1fa12 | ||
|
|
f4c737ef42 | ||
|
|
611df4964d | ||
|
|
0a4ccf22da | ||
|
|
4fe98bf6e6 | ||
|
|
57c3023881 | ||
|
|
4408e079ff | ||
|
|
2991057aef | ||
|
|
6b4fea39ab | ||
|
|
a08ffcff50 | ||
|
|
6bd9f4a13a | ||
|
|
32420b77c8 | ||
|
|
0b8a84f536 | ||
|
|
f18a55831c | ||
|
|
58fb0decbf | ||
|
|
2124b7bf64 | ||
|
|
5b273a33b4 | ||
|
|
fb702f989f | ||
|
|
0203a0fdaf | ||
|
|
112a7ada74 | ||
|
|
452ba76507 | ||
|
|
2ac42c0d14 | ||
|
|
0955ea5b85 | ||
|
|
084bc72d90 | ||
|
|
57e2193432 | ||
|
|
ce8cf1e152 | ||
|
|
3da189f7c0 | ||
|
|
058d2d1bd4 | ||
|
|
f70f2f8c62 | ||
|
|
8d6086da48 | ||
|
|
bd6d88b884 | ||
|
|
4003218ceb | ||
|
|
ec3f2b76b0 | ||
|
|
fcb661d0e9 | ||
|
|
d8eb0558e9 | ||
|
|
5191948b64 | ||
|
|
7442d720f4 | ||
|
|
bbc859ca19 | ||
|
|
7275fb6f52 | ||
|
|
2d50202b2d | ||
|
|
f5dc16603e | ||
|
|
85b4d7c8d6 | ||
|
|
f0c962d274 | ||
|
|
486b6937d3 | ||
|
|
a6152ebadd | ||
|
|
63a475d88c | ||
|
|
3eba3224c8 | ||
|
|
247c4e55e7 | ||
|
|
26d7c27bee | ||
|
|
b492642282 | ||
|
|
cff78f5833 | ||
|
|
4ba19821ce | ||
|
|
1e385851d7 | ||
|
|
4643f74a03 | ||
|
|
6453d396bf | ||
|
|
92921f767e | ||
|
|
c251c4ccbb | ||
|
|
0ce670e45a | ||
|
|
2671b48a6c | ||
|
|
382478259f | ||
|
|
e3c333be47 | ||
|
|
a1a463787f | ||
|
|
a16ff07a06 | ||
|
|
3218caf34a | ||
|
|
01583ef540 | ||
|
|
dc13700094 | ||
|
|
1293af093c | ||
|
|
2998815166 | ||
|
|
9484c4dc05 | ||
|
|
521b0733d4 | ||
|
|
a463989278 | ||
|
|
a31719b546 | ||
|
|
f97cb00737 | ||
|
|
f2305fe5bf | ||
|
|
216cd6935f | ||
|
|
3a8f9484d2 | ||
|
|
3fa76b2d8f | ||
|
|
50648553cf | ||
|
|
70d03fd9c3 | ||
|
|
0a8cce6984 | ||
|
|
fb1a3a3a12 | ||
|
|
52e2722412 | ||
|
|
b64e4464a7 | ||
|
|
f1ab6feba2 | ||
|
|
e751461ff1 | ||
|
|
c7f42d1a4a | ||
|
|
6232ec78f7 | ||
|
|
f09d0f2301 | ||
|
|
14a071c478 | ||
|
|
e601ebe19b | ||
|
|
7068c45115 | ||
|
|
3bfcb0468e | ||
|
|
b2735eb30c | ||
|
|
552416bda4 | ||
|
|
b522413085 | ||
|
|
8a0fc92f20 | ||
|
|
4a34cd0662 | ||
|
|
11d83515dd | ||
|
|
7ce8ae72e8 | ||
|
|
cc7e122915 | ||
|
|
da84893921 | ||
|
|
1d5b6d7ae6 | ||
|
|
314991ac60 | ||
|
|
9b5b4cd505 | ||
|
|
c5069135d7 | ||
|
|
971c1f46b0 | ||
|
|
34c60e5486 | ||
|
|
526f21ae7f | ||
|
|
48597a94e8 | ||
|
|
0e77c3391b | ||
|
|
f1e79bde2e | ||
|
|
7b6849578b | ||
|
|
617fcc92cf | ||
|
|
18f0e4ba1a | ||
|
|
6fedffe6d6 | ||
|
|
d54e302a28 | ||
|
|
d99179f822 | ||
|
|
a6fbb3ef4c | ||
|
|
65cff673b8 | ||
|
|
dc166cad92 | ||
|
|
7d7ccac416 | ||
|
|
e933c5f481 | ||
|
|
2ba4b23b85 | ||
|
|
ba4ed0eb7f | ||
|
|
3cdf2b7f04 | ||
|
|
99b68c8352 | ||
|
|
a446f187c1 | ||
|
|
3ff541cf77 | ||
|
|
10895796b2 | ||
|
|
fbc58ebef8 | ||
|
|
5c54414be7 | ||
|
|
4df28728e2 | ||
|
|
f14a4c0b18 | ||
|
|
f5a27250b1 | ||
|
|
f95b189fe3 | ||
|
|
1aace95c8d | ||
|
|
7844471971 | ||
|
|
794a636dd3 | ||
|
|
e35414a0f1 | ||
|
|
0d0706a204 | ||
|
|
80f19324bc | ||
|
|
1b04c222cf | ||
|
|
5d04848886 | ||
|
|
fc14e831eb | ||
|
|
04d577262a | ||
|
|
7be3d2afe9 | ||
|
|
d1237d8984 | ||
|
|
1b391ccd06 | ||
|
|
7377bfcf07 | ||
|
|
665b708e6e | ||
|
|
f87049370f | ||
|
|
47948a34dd | ||
|
|
121a7dcedf | ||
|
|
240a58fd6e | ||
|
|
99e23b41eb | ||
|
|
cadb3d7da2 | ||
|
|
392d126372 | ||
|
|
871cca2401 | ||
|
|
1bdd556d3b | ||
|
|
9daad800a8 | ||
|
|
8d2c956563 | ||
|
|
0584d6d89b | ||
|
|
5763733490 | ||
|
|
894438d5fb | ||
|
|
2ad191aeba | ||
|
|
dd9d7e62d5 | ||
|
|
4e3e80109a | ||
|
|
70bef682b0 | ||
|
|
c2a9bf9974 | ||
|
|
23da9f13b0 | ||
|
|
a5a39c52b0 | ||
|
|
6355e9895d | ||
|
|
abf4af2645 | ||
|
|
cb71fea0f6 | ||
|
|
3e2d593dde | ||
|
|
4b66e94ecf | ||
|
|
40143cae1e | ||
|
|
b1277caeeb | ||
|
|
13cc6478fb | ||
|
|
c465250c21 | ||
|
|
815b52b8fb | ||
|
|
275167d1b0 | ||
|
|
45717147f7 | ||
|
|
f2f9f324ec | ||
|
|
8131d3e127 | ||
|
|
226d5a1d36 | ||
|
|
312fe4775d | ||
|
|
b368d18b0f | ||
|
|
8e4996baf4 | ||
|
|
b7cd502054 | ||
|
|
53ac379bc5 | ||
|
|
10e809cf64 | ||
|
|
3079551d30 | ||
|
|
f4c5c5a367 | ||
|
|
2a7b995723 | ||
|
|
d0beac70bd | ||
|
|
cbf66ac653 | ||
|
|
98f9cb8c1f | ||
|
|
8bd4c87d2f | ||
|
|
c88b568685 | ||
|
|
c83c9a3193 | ||
|
|
410c46f1ab | ||
|
|
1b8bf35acc | ||
|
|
aa47ac85c9 | ||
|
|
3eadda9466 | ||
|
|
dea6a7c217 | ||
|
|
887a104dd0 | ||
|
|
1808e3c4c8 | ||
|
|
1f4cae4bf7 | ||
|
|
3b009f5aa6 | ||
|
|
36112371c0 | ||
|
|
2fa538779f | ||
|
|
b7a6659451 | ||
|
|
102c31a04c | ||
|
|
f01da91abf | ||
|
|
10000c383a | ||
|
|
8b42b7d269 | ||
|
|
17702969fa | ||
|
|
cc3613e012 | ||
|
|
0258418a99 | ||
|
|
397c2a934f | ||
|
|
796986f38c | ||
|
|
c5011181bb | ||
|
|
dace099348 | ||
|
|
0876e39c4f | ||
|
|
0b516733db | ||
|
|
e15deead32 | ||
|
|
18df6138a7 | ||
|
|
8c125ed009 | ||
|
|
4720b703f4 | ||
|
|
cd8e15dcd1 | ||
|
|
a06d32ae19 | ||
|
|
7af3f014af | ||
|
|
8e80081f99 | ||
|
|
14c0417f3e | ||
|
|
0879dea444 | ||
|
|
a8c4aef6bd | ||
|
|
669a66f18a | ||
|
|
e8dae26176 | ||
|
|
e3a66473b2 | ||
|
|
7704e41336 | ||
|
|
a74bbe7381 | ||
|
|
729ec21629 | ||
|
|
b393715bee | ||
|
|
5ec4e60424 | ||
|
|
5cd24f3033 | ||
|
|
27a03c5cec | ||
|
|
5d34d21f40 | ||
|
|
ca8accbaa8 | ||
|
|
65defd3806 | ||
|
|
be7e7237e9 | ||
|
|
234cb2dfba | ||
|
|
b922ae0fb8 | ||
|
|
8649fb5118 | ||
|
|
a4bae6aba9 | ||
|
|
808bc1f4ed | ||
|
|
41d879e292 | ||
|
|
2e7b3cae2a | ||
|
|
a0ae62a733 | ||
|
|
06eb69b93a | ||
|
|
7025ff4280 | ||
|
|
2fa7410c0e | ||
|
|
3771d12992 | ||
|
|
f4ac67ae1c | ||
|
|
d48d6939c2 | ||
|
|
188cff7d65 | ||
|
|
75925762e8 | ||
|
|
f2ac7d730c | ||
|
|
c5c1c5458b | ||
|
|
1649597eae | ||
|
|
22f85deb2c | ||
|
|
1cb49bfe72 | ||
|
|
1aa4b657d6 | ||
|
|
52a01b2cf2 | ||
|
|
d803e7003f | ||
|
|
18efcd62ff | ||
|
|
9fda1cb421 | ||
|
|
bcc6db4a06 | ||
|
|
37cd707294 | ||
|
|
3698c1d2f1 | ||
|
|
00acb8ba41 | ||
|
|
a6ee1cf590 | ||
|
|
ebce97947d | ||
|
|
f4bbe8b2b3 | ||
|
|
e52e258f15 | ||
|
|
679fcd787f | ||
|
|
f3798159e7 | ||
|
|
c4de214cea | ||
|
|
6c5bbca0c1 | ||
|
|
447a6a15d9 | ||
|
|
5d7845c138 | ||
|
|
91d8869f36 | ||
|
|
1f8b2cbb8b | ||
|
|
d0cfca0820 | ||
|
|
f6049cd333 | ||
|
|
a25fd62349 | ||
|
|
39492436ec | ||
|
|
df962e5c53 | ||
|
|
2990126054 | ||
|
|
3edc7f1d18 | ||
|
|
2870afdeae | ||
|
|
d3c1ad29a0 | ||
|
|
23db7a213d | ||
|
|
3151081ff3 | ||
|
|
0758b39061 | ||
|
|
45d3c8341c | ||
|
|
8418131ae3 | ||
|
|
9ad9cfb898 | ||
|
|
a281d21fbf | ||
|
|
af5962450e | ||
|
|
3b3af1ab1e | ||
|
|
acd7a88bf2 | ||
|
|
176cab4fee | ||
|
|
28435dc736 | ||
|
|
a58d7594cb | ||
|
|
06181720fb | ||
|
|
b51c8cfd0f | ||
|
|
b6b9d835c5 | ||
|
|
ebea09c8b4 | ||
|
|
137f801ec7 | ||
|
|
3d58f8d471 | ||
|
|
b962fbb15c | ||
|
|
e8927dc75f | ||
|
|
fb37585bc1 | ||
|
|
4a871b9792 | ||
|
|
057bf1b92d | ||
|
|
750b520f41 | ||
|
|
c23f36c734 | ||
|
|
4eb80dd8d2 | ||
|
|
c543985cf4 | ||
|
|
863f704f47 | ||
|
|
54896763ab | ||
|
|
0272bd2846 | ||
|
|
70f3ed66f4 | ||
|
|
1dba3162c4 | ||
|
|
cb0d8cfbbd | ||
|
|
08064716b3 | ||
|
|
dca20731a2 | ||
|
|
530e209f66 | ||
|
|
566df7e826 | ||
|
|
56b3a17e56 | ||
|
|
d6cc5847fa | ||
|
|
1d672d39cf | ||
|
|
aefde13858 | ||
|
|
607779cccf | ||
|
|
da2af6ea2e | ||
|
|
ba337d1393 | ||
|
|
6ae240c0b6 | ||
|
|
05d6960ebc | ||
|
|
4866f2ac05 | ||
|
|
4456f96ae3 | ||
|
|
8008d7fe53 | ||
|
|
0a585092dc | ||
|
|
387ccae7d8 | ||
|
|
d951ea32a6 | ||
|
|
da30e2ef18 | ||
|
|
cb299375f6 | ||
|
|
4534d5239f | ||
|
|
af5a516443 | ||
|
|
368b3699d0 | ||
|
|
49d663f6c8 | ||
|
|
7e32ccbd8f | ||
|
|
f8f2d7b950 | ||
|
|
769f11109d | ||
|
|
85a2bf1c17 | ||
|
|
a2fa7a833c | ||
|
|
8179b26388 | ||
|
|
12bbef308b |
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
* text=false
|
||||||
19
.gitignore
vendored
19
.gitignore
vendored
@@ -1,5 +1,5 @@
|
|||||||
# Auto generated files
|
# Auto generated files
|
||||||
build/*
|
build/*
|
||||||
*.slo
|
*.slo
|
||||||
*.lo
|
*.lo
|
||||||
*.o
|
*.o
|
||||||
@@ -34,6 +34,9 @@ build/*
|
|||||||
# Codelite
|
# Codelite
|
||||||
.codelite
|
.codelite
|
||||||
|
|
||||||
|
# KDevelop
|
||||||
|
*.kdev4
|
||||||
|
|
||||||
# .orig files
|
# .orig files
|
||||||
*.orig
|
*.orig
|
||||||
|
|
||||||
@@ -46,6 +49,7 @@ example/*
|
|||||||
!example/example.sln
|
!example/example.sln
|
||||||
!example/example.vcxproj
|
!example/example.vcxproj
|
||||||
!example/CMakeLists.txt
|
!example/CMakeLists.txt
|
||||||
|
!example/meson.build
|
||||||
!example/multisink.cpp
|
!example/multisink.cpp
|
||||||
!example/jni
|
!example/jni
|
||||||
|
|
||||||
@@ -65,4 +69,15 @@ install_manifest.txt
|
|||||||
/tests/logs/*
|
/tests/logs/*
|
||||||
|
|
||||||
# idea
|
# idea
|
||||||
.idea/
|
.idea/
|
||||||
|
cmake-build-*/
|
||||||
|
*.db
|
||||||
|
*.ipch
|
||||||
|
*.filters
|
||||||
|
*.db-wal
|
||||||
|
*.opendb
|
||||||
|
*.db-shm
|
||||||
|
*.vcxproj
|
||||||
|
*.tcl
|
||||||
|
*.user
|
||||||
|
*.sln
|
||||||
|
|||||||
202
.travis.yml
202
.travis.yml
@@ -1,91 +1,111 @@
|
|||||||
# Adapted from various sources, including:
|
# Adapted from various sources, including:
|
||||||
# - Louis Dionne's Hana: https://github.com/ldionne/hana
|
# - Louis Dionne's Hana: https://github.com/ldionne/hana
|
||||||
# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit
|
# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit
|
||||||
# - Eric Niebler's range-v3: https://github.com/ericniebler/range-v3
|
# - Eric Niebler's range-v3: https://github.com/ericniebler/range-v3
|
||||||
language: cpp
|
sudo: required
|
||||||
|
language: cpp
|
||||||
# Test matrix:
|
|
||||||
# - Build matrix per compiler: C++11/C++14 + Debug/Release
|
# gcc 4.8
|
||||||
# - Optionally: AddressSanitizer (ASAN)
|
addons: &gcc48
|
||||||
# - Valgrind: all release builds are also tested with valgrind
|
apt:
|
||||||
# - clang 3.4, 3.5, 3.6, trunk
|
packages:
|
||||||
# - Note: 3.4 and trunk are tested with/without ASAN,
|
- g++-4.8
|
||||||
# the rest is only tested with ASAN=On.
|
sources:
|
||||||
# - gcc 4.9, 5.0
|
- ubuntu-toolchain-r-test
|
||||||
#
|
|
||||||
matrix:
|
# gcc 7.0
|
||||||
include:
|
addons: &gcc7
|
||||||
|
apt:
|
||||||
# Test gcc-4.8: C++11, Build=Debug/Release, ASAN=Off
|
packages:
|
||||||
- env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11 ASAN=Off LIBCXX=Off
|
- g++-7
|
||||||
os: linux
|
sources:
|
||||||
addons: &gcc48
|
- ubuntu-toolchain-r-test
|
||||||
apt:
|
|
||||||
packages:
|
# Clang 3.5
|
||||||
- g++-4.8
|
addons: &clang35
|
||||||
- valgrind
|
apt:
|
||||||
sources:
|
packages:
|
||||||
- ubuntu-toolchain-r-test
|
- clang-3.5
|
||||||
|
sources:
|
||||||
- env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=Off
|
- ubuntu-toolchain-r-test
|
||||||
os: linux
|
- llvm-toolchain-precise-3.5
|
||||||
addons: *gcc48
|
|
||||||
|
# Clang 7.0
|
||||||
# Test gcc-4.9: C++11, Build=Debug/Release, ASAN=Off
|
addons: &clang70
|
||||||
- env: GCC_VERSION=4.9 BUILD_TYPE=Debug CPP=11 ASAN=Off LIBCXX=Off
|
apt:
|
||||||
os: linux
|
packages:
|
||||||
addons: &gcc49
|
- clang-7
|
||||||
apt:
|
sources:
|
||||||
packages:
|
- ubuntu-toolchain-r-test
|
||||||
- g++-4.9
|
- llvm-toolchain-trusty-7
|
||||||
- valgrind
|
|
||||||
sources:
|
|
||||||
- ubuntu-toolchain-r-test
|
|
||||||
|
matrix:
|
||||||
- env: GCC_VERSION=4.9 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=Off
|
include:
|
||||||
os: linux
|
# Test gcc-4.8: C++11, Build=Debug/Release
|
||||||
addons: *gcc49
|
- env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11
|
||||||
|
os: linux
|
||||||
# Install dependencies
|
addons: *gcc48
|
||||||
before_install:
|
|
||||||
- export CHECKOUT_PATH=`pwd`;
|
- env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11
|
||||||
- if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi
|
os: linux
|
||||||
- if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi
|
addons: *gcc48
|
||||||
- if [ "$CLANG_VERSION" == "3.4" ]; then export CXX="/usr/local/clang-3.4/bin/clang++" CC="/usr/local/clang-3.4/bin/clang"; fi
|
|
||||||
- which $CXX
|
- env: GCC_VERSION=7 BUILD_TYPE=Release CPP=11
|
||||||
- which $CC
|
os: linux
|
||||||
- which valgrind
|
addons: *gcc7
|
||||||
- if [ -n "$CLANG_VERSION" ]; then sudo CXX=$CXX CC=$CC ./tests/install_libcxx.sh; fi
|
|
||||||
|
# Test clang-3.5: C++11, Build=Debug/Release
|
||||||
install:
|
- env: CLANG_VERSION=3.5 BUILD_TYPE=Debug CPP=11
|
||||||
- cd $CHECKOUT_PATH
|
os: linux
|
||||||
|
addons: *clang35
|
||||||
# Workaround for valgrind bug: https://bugs.kde.org/show_bug.cgi?id=326469.
|
|
||||||
# It is fixed in valgrind 3.10 so this won't be necessary if someone
|
- env: CLANG_VERSION=3.5 BUILD_TYPE=Release CPP=11
|
||||||
# replaces the current valgrind (3.7) with valgrind-3.10
|
os: linux
|
||||||
- sed -i 's/march=native/msse4.2/' example/Makefile
|
addons: *clang35
|
||||||
|
|
||||||
- if [ ! -d build ]; then mkdir build; fi
|
# Test clang-7.0: C++11, Build=Debug, ASAN=On
|
||||||
- export CXX_FLAGS="-I${CHECKOUT_PATH}/include"
|
- env: CLANG_VERSION=7 BUILD_TYPE=Debug CPP=11 ASAN=On TSAN=Off
|
||||||
- export CXX_LINKER_FLAGS=""
|
dist: bionic
|
||||||
- if [ -z "$BUILD_TYPE" ]; then export BUILD_TYPE=Release; fi
|
|
||||||
- if [ "$ASAN" == "On"]; then export CXX_FLAGS="${CXX_FLAGS} -fsanitize=address,undefined,integer -fno-omit-frame-pointer -fno-sanitize=unsigned-integer-overflow"; fi
|
- env: CLANG_VERSION=7 BUILD_TYPE=Release CPP=11 ASAN=On TSAN=Off
|
||||||
- if [ -n "$CLANG_VERSION" ]; then CXX_FLAGS="${CXX_FLAGS} -D__extern_always_inline=inline"; fi
|
dist: bionic
|
||||||
- if [ "$LIBCXX" == "On" ]; then CXX_FLAGS="${CXX_FLAGS} -stdlib=libc++ -I/usr/include/c++/v1/"; fi
|
|
||||||
- if [ "$LIBCXX" == "On" ]; then CXX_LINKER_FLAGS="${CXX_FLAGS} -L/usr/lib/ -lc++"; fi
|
# osx
|
||||||
- CXX_FLAGS="${CXX_FLAGS} -std=c++${CPP}"
|
- env: BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=Off
|
||||||
|
os: osx
|
||||||
# Build examples
|
|
||||||
- cd example
|
|
||||||
- if [ "$BUILD_TYPE" == "Release" ]; then make rebuild CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example; fi
|
|
||||||
- if [ "$BUILD_TYPE" == "Debug" ]; then make rebuild debug CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example-debug; fi
|
before_script:
|
||||||
|
- if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi
|
||||||
|
- if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi
|
||||||
script:
|
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export CXX="clang++" CC="clang"; fi
|
||||||
- ./"${BIN}"
|
- which $CXX
|
||||||
- valgrind --trace-children=yes --leak-check=full ./"${BIN}"
|
- which $CC
|
||||||
- cd $CHECKOUT_PATH/tests; make rebuild; ./tests
|
- $CXX --version
|
||||||
- cd $CHECKOUT_PATH/tests; STYLE=printf make rebuild; ./tests
|
- cmake --version
|
||||||
|
|
||||||
notifications:
|
script:
|
||||||
email: false
|
- cd ${TRAVIS_BUILD_DIR}
|
||||||
|
- mkdir -p build && cd build
|
||||||
|
- |
|
||||||
|
cmake .. \
|
||||||
|
--warn-uninitialized \
|
||||||
|
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
||||||
|
-DCMAKE_CXX_STANDARD=$CPP \
|
||||||
|
-DSPDLOG_BUILD_EXAMPLE=ON \
|
||||||
|
-DSPDLOG_BUILD_EXAMPLE_HO=ON \
|
||||||
|
-DSPDLOG_BUILD_BENCH=OFF \
|
||||||
|
-DSPDLOG_BUILD_TESTS=ON \
|
||||||
|
-DSPDLOG_BUILD_TESTS_HO=OFf \
|
||||||
|
-DSPDLOG_SANITIZE_ADDRESS=$ASAN
|
||||||
|
|
||||||
|
- make VERBOSE=1 -j2
|
||||||
|
- ctest -j2 --output-on-failure
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email: false
|
||||||
|
|||||||
267
CMakeLists.txt
267
CMakeLists.txt
@@ -1,115 +1,210 @@
|
|||||||
#
|
# Copyright(c) 2019 spdlog authors
|
||||||
# Copyright(c) 2015 Ruslan Baratov.
|
|
||||||
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
#
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.1)
|
cmake_minimum_required(VERSION 3.2)
|
||||||
project(spdlog VERSION 1.0.0 LANGUAGES CXX)
|
|
||||||
include(CTest)
|
|
||||||
include(CMakeDependentOption)
|
|
||||||
include(GNUInstallDirs)
|
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------------
|
||||||
# compiler config
|
# Start spdlog project
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
include(GNUInstallDirs)
|
||||||
|
include(cmake/utils.cmake)
|
||||||
|
include(cmake/ide.cmake)
|
||||||
|
|
||||||
|
spdlog_extract_version()
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
# Set default build to release
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
project(spdlog VERSION ${SPDLOG_VERSION} LANGUAGES CXX)
|
||||||
|
message(STATUS "Build spdlog: ${SPDLOG_VERSION}")
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
# Compiler config
|
||||||
#---------------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------------
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
#---------------------------------------------------------------------------------------
|
||||||
set(CMAKE_CXX_FLAGS "-Wall -O3 ${CMAKE_CXX_FLAGS}")
|
# Set SPDLOG_MASTER_PROJECT to ON if we are building spdlog
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
# Check if spdlog is being used directly or via add_subdirectory, but allow overriding
|
||||||
|
if (NOT DEFINED SPDLOG_MASTER_PROJECT)
|
||||||
|
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
||||||
|
set(SPDLOG_MASTER_PROJECT ON)
|
||||||
|
else()
|
||||||
|
set(SPDLOG_MASTER_PROJECT OFF)
|
||||||
|
endif()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
# build shared option
|
||||||
|
if(NOT WIN32)
|
||||||
|
option(SPDLOG_BUILD_SHARED "Build shared library" OFF)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# example options
|
||||||
|
option(SPDLOG_BUILD_EXAMPLE "Build example" ${SPDLOG_MASTER_PROJECT})
|
||||||
|
option(SPDLOG_BUILD_EXAMPLE_HO "Build header only example" OFF)
|
||||||
|
|
||||||
|
# testing options
|
||||||
|
option(SPDLOG_BUILD_TESTS "Build tests" ${SPDLOG_MASTER_PROJECT})
|
||||||
|
option(SPDLOG_BUILD_TESTS_HO "Build tests using the header only version" OFF)
|
||||||
|
|
||||||
|
# bench options
|
||||||
|
option(SPDLOG_BUILD_BENCH "Build benchmarks (Requires https://github.com/google/benchmark.git to be installed)" OFF)
|
||||||
|
|
||||||
|
# sanitizer options
|
||||||
|
option(SPDLOG_SANITIZE_ADDRESS "Enable address sanitizer in tests" OFF)
|
||||||
|
|
||||||
|
# install options
|
||||||
|
option(SPDLOG_INSTALL "Generate the install target" ${SPDLOG_MASTER_PROJECT})
|
||||||
|
option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF)
|
||||||
|
|
||||||
|
if(WIN32)
|
||||||
|
option(SPDLOG_WCHAR_SUPPORT "Support wchar api" OFF)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
option(SPDLOG_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF)
|
||||||
|
|
||||||
|
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
|
message(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------------
|
||||||
# spdlog target
|
# Static/Shared library (shared not supported in windows yet)
|
||||||
#---------------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------------
|
||||||
add_library(spdlog INTERFACE)
|
set(SPDLOG_SRCS
|
||||||
|
src/spdlog.cpp
|
||||||
|
src/stdout_sinks.cpp
|
||||||
|
src/fmt.cpp
|
||||||
|
src/color_sinks.cpp
|
||||||
|
src/file_sinks.cpp
|
||||||
|
src/async.cpp)
|
||||||
|
|
||||||
option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF)
|
|
||||||
cmake_dependent_option(SPDLOG_BUILD_TESTING
|
|
||||||
"Build spdlog tests" ON
|
|
||||||
"BUILD_TESTING" OFF
|
|
||||||
)
|
|
||||||
|
|
||||||
target_include_directories(
|
if (SPDLOG_BUILD_SHARED)
|
||||||
spdlog
|
if(WIN32)
|
||||||
INTERFACE
|
message(FATAL_ERROR "spdlog shared lib is not yet supported under windows")
|
||||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
endif()
|
||||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
add_library(spdlog SHARED ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
|
||||||
)
|
else()
|
||||||
|
add_library(spdlog STATIC ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
|
||||||
|
endif()
|
||||||
|
|
||||||
set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
add_library(spdlog::spdlog ALIAS spdlog)
|
||||||
|
|
||||||
if(SPDLOG_BUILD_EXAMPLES)
|
target_compile_definitions(spdlog PUBLIC SPDLOG_COMPILED_LIB)
|
||||||
|
target_include_directories(spdlog PUBLIC
|
||||||
|
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
||||||
|
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
|
||||||
|
target_link_libraries(spdlog PUBLIC Threads::Threads)
|
||||||
|
spdlog_enable_warnings(spdlog)
|
||||||
|
|
||||||
|
set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION ${SPDLOG_VERSION_MAJOR})
|
||||||
|
set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX d)
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
# Header only version
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
add_library(spdlog_header_only INTERFACE)
|
||||||
|
add_library(spdlog::spdlog_header_only ALIAS spdlog_header_only)
|
||||||
|
|
||||||
|
target_include_directories(spdlog_header_only INTERFACE
|
||||||
|
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
||||||
|
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
|
||||||
|
target_link_libraries(spdlog_header_only INTERFACE Threads::Threads)
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
# Use fmt package if using exertnal fmt
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
if(SPDLOG_FMT_EXTERNAL)
|
||||||
|
if (NOT TARGET fmt::fmt)
|
||||||
|
find_package(fmt REQUIRED)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
target_compile_definitions(spdlog PUBLIC SPDLOG_FMT_EXTERNAL)
|
||||||
|
target_link_libraries(spdlog PUBLIC fmt::fmt)
|
||||||
|
|
||||||
|
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FMT_EXTERNAL)
|
||||||
|
target_link_libraries(spdlog_header_only INTERFACE fmt::fmt)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(SPDLOG_WCHAR_SUPPORT)
|
||||||
|
target_compile_definitions(spdlog PUBLIC SPDLOG_WCHAR_TO_UTF8_SUPPORT)
|
||||||
|
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_WCHAR_TO_UTF8_SUPPORT)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(SPDLOG_NO_EXCEPTIONS)
|
||||||
|
target_compile_definitions(spdlog PUBLIC SPDLOG_NO_EXCEPTIONS)
|
||||||
|
|
||||||
|
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_EXCEPTIONS)
|
||||||
|
|
||||||
|
if(NOT MSVC)
|
||||||
|
target_compile_options(spdlog PRIVATE -fno-exceptions)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
# Build binaries
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
if(SPDLOG_BUILD_EXAMPLE OR SPDLOG_BUILD_EXAMPLE_HO)
|
||||||
|
message(STATUS "Generating examples")
|
||||||
add_subdirectory(example)
|
add_subdirectory(example)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(SPDLOG_BUILD_TESTING)
|
if(SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_TESTS_HO)
|
||||||
|
message(STATUS "Generating tests")
|
||||||
|
enable_testing()
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(SPDLOG_BUILD_BENCH)
|
||||||
|
message(STATUS "Generating benchmarks")
|
||||||
|
add_subdirectory(bench)
|
||||||
|
endif()
|
||||||
|
|
||||||
#---------------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------------
|
||||||
# Install/export targets and files
|
# Install
|
||||||
#---------------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------------
|
||||||
# set files and directories
|
if (SPDLOG_INSTALL)
|
||||||
set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
message(STATUS "Generating install")
|
||||||
set(include_install_dir "${CMAKE_INSTALL_INCLUDEDIR}")
|
set(project_config_in "${CMAKE_CURRENT_LIST_DIR}/cmake/spdlogConfig.cmake.in")
|
||||||
set(pkgconfig_install_dir "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
set(project_config_out "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfig.cmake")
|
||||||
set(version_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
|
set(config_targets_file "spdlogConfigTargets.cmake")
|
||||||
set(project_config "${PROJECT_NAME}Config.cmake")
|
set(version_config_file "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfigVersion.cmake")
|
||||||
set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc")
|
set(export_dest_dir "${CMAKE_INSTALL_LIBDIR}/spdlog/cmake")
|
||||||
set(targets_export_name "${PROJECT_NAME}Targets")
|
|
||||||
set(namespace "${PROJECT_NAME}::")
|
|
||||||
|
|
||||||
# generate package version file
|
#---------------------------------------------------------------------------------------
|
||||||
include(CMakePackageConfigHelpers)
|
# Include files
|
||||||
write_basic_package_version_file(
|
#---------------------------------------------------------------------------------------
|
||||||
"${version_config}" COMPATIBILITY SameMajorVersion
|
install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||||
)
|
install(TARGETS spdlog spdlog_header_only EXPORT spdlog DESTINATION "${CMAKE_INSTALL_LIBDIR}/spdlog")
|
||||||
|
|
||||||
# configure pkg config file
|
#---------------------------------------------------------------------------------------
|
||||||
configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY)
|
# Package and version files
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
install(EXPORT spdlog
|
||||||
|
DESTINATION ${export_dest_dir}
|
||||||
|
NAMESPACE spdlog::
|
||||||
|
FILE ${config_targets_file})
|
||||||
|
|
||||||
# install targets
|
include(CMakePackageConfigHelpers)
|
||||||
install(
|
configure_file("${project_config_in}" "${project_config_out}" @ONLY)
|
||||||
TARGETS spdlog
|
write_basic_package_version_file("${version_config_file}" COMPATIBILITY SameMajorVersion)
|
||||||
EXPORT "${targets_export_name}"
|
install(FILES
|
||||||
)
|
"${project_config_out}"
|
||||||
|
"${version_config_file}" DESTINATION "${export_dest_dir}")
|
||||||
|
|
||||||
# install headers
|
#---------------------------------------------------------------------------------------
|
||||||
install(
|
# Support creation of installable packages
|
||||||
DIRECTORY "${HEADER_BASE}/${PROJECT_NAME}"
|
#---------------------------------------------------------------------------------------
|
||||||
DESTINATION "${include_install_dir}"
|
include(cmake/spdlogCPack.cmake)
|
||||||
)
|
|
||||||
|
|
||||||
# install project version file
|
endif ()
|
||||||
install(
|
|
||||||
FILES "${version_config}"
|
|
||||||
DESTINATION "${config_install_dir}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# install pkg config file
|
|
||||||
install(
|
|
||||||
FILES "${pkg_config}"
|
|
||||||
DESTINATION "${pkgconfig_install_dir}"
|
|
||||||
)
|
|
||||||
|
|
||||||
# install project config file
|
|
||||||
install(
|
|
||||||
EXPORT "${targets_export_name}"
|
|
||||||
NAMESPACE "${namespace}"
|
|
||||||
DESTINATION "${config_install_dir}"
|
|
||||||
FILE ${project_config}
|
|
||||||
)
|
|
||||||
|
|
||||||
# export build directory config file
|
|
||||||
export(
|
|
||||||
EXPORT ${targets_export_name}
|
|
||||||
NAMESPACE "${namespace}"
|
|
||||||
FILE ${project_config}
|
|
||||||
)
|
|
||||||
|
|
||||||
# register project in CMake user registry
|
|
||||||
export(PACKAGE ${PROJECT_NAME})
|
|
||||||
|
|
||||||
file(GLOB_RECURSE spdlog_include_SRCS "${HEADER_BASE}/*.h")
|
|
||||||
add_custom_target(spdlog_headers_for_ide SOURCES ${spdlog_include_SRCS})
|
|
||||||
|
|||||||
21
INSTALL
21
INSTALL
@@ -1,13 +1,24 @@
|
|||||||
spdlog is header only library.
|
Header only version:
|
||||||
Just copy the files to your build tree and use a C++11 compiler
|
==================================================================
|
||||||
|
Just copy the files to your build tree and use a C++11 compiler.
|
||||||
|
Or use CMake:
|
||||||
|
add_executable(example_header_only example.cpp)
|
||||||
|
target_link_libraries(example_header_only spdlog::spdlog_header_only)
|
||||||
|
|
||||||
|
|
||||||
|
Compiled library version:
|
||||||
|
==================================================================
|
||||||
|
CMake:
|
||||||
|
add_executable(example example.cpp)
|
||||||
|
target_link_libraries(example spdlog::spdlog)
|
||||||
|
|
||||||
|
Or copy src/spdlog.cpp to your build tree and pass the -DSPDLOG_COMPILED_LIB to the compiler.
|
||||||
|
|
||||||
Tested on:
|
Tested on:
|
||||||
gcc 4.8.1 and above
|
gcc 4.8.1 and above
|
||||||
clang 3.5
|
clang 3.5
|
||||||
Visual Studio 2013
|
Visual Studio 2013
|
||||||
|
|
||||||
gcc 4.8 flags: --std==c++11 -pthread -O3 -flto -Wl,--no-as-needed
|
|
||||||
gcc 4.9 flags: --std=c++11 -pthread -O3 -flto
|
|
||||||
|
|
||||||
|
|
||||||
see the makefile in the example folder
|
|
||||||
|
|||||||
282
README.md
282
README.md
@@ -1,37 +1,43 @@
|
|||||||
# spdlog
|
# spdlog
|
||||||
|
|
||||||
Very fast, header only, C++ logging library. [](https://travis-ci.org/gabime/spdlog) [](https://ci.appveyor.com/project/gabime/spdlog)
|
Very fast, header-only/compiled, C++ logging library. [](https://travis-ci.org/gabime/spdlog) [](https://ci.appveyor.com/project/gabime/spdlog)
|
||||||
|
|
||||||
|
|
||||||
## Install
|
|
||||||
#### Just copy the headers:
|
|
||||||
|
|
||||||
* Copy the source [folder](https://github.com/gabime/spdlog/tree/v1.x/include/spdlog) to your build tree and use a C++11 compiler.
|
## Install
|
||||||
|
#### Header only version
|
||||||
|
Copy the source [folder](https://github.com/gabime/spdlog/tree/v1.x/include/spdlog) to your build tree and use a C++11 compiler.
|
||||||
|
|
||||||
#### Or use your favorite package manager:
|
#### Static lib version (recommended - much faster compile times, v1.4.0)
|
||||||
|
```console
|
||||||
|
$ git clone https://github.com/gabime/spdlog.git
|
||||||
|
$ cd spdlog && mkdir build && cd build
|
||||||
|
$ cmake .. && make -j
|
||||||
|
```
|
||||||
|
|
||||||
|
see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v1.x/example/CMakeLists.txt) on how to use.
|
||||||
|
|
||||||
* Ubuntu: `apt-get install libspdlog-dev`
|
## Platforms
|
||||||
|
* Linux, FreeBSD, OpenBSD, Solaris, AIX
|
||||||
|
* Windows (msvc 2013+, cygwin)
|
||||||
|
* macOS (clang 3.5+)
|
||||||
|
* Android
|
||||||
|
|
||||||
|
## Package managers:
|
||||||
* Homebrew: `brew install spdlog`
|
* Homebrew: `brew install spdlog`
|
||||||
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
|
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
|
||||||
* Fedora: `yum install spdlog`
|
* Fedora: `yum install spdlog`
|
||||||
* Gentoo: `emerge dev-libs/spdlog`
|
* Gentoo: `emerge dev-libs/spdlog`
|
||||||
* Arch Linux: `yaourt -S spdlog-git`
|
* Arch Linux: `yaourt -S spdlog-git`
|
||||||
* vcpkg: `vcpkg install spdlog`
|
* vcpkg: `vcpkg install spdlog`
|
||||||
|
|
||||||
|
|
||||||
## Platforms
|
|
||||||
* Linux, FreeBSD, Solaris, AIX
|
|
||||||
* Windows (vc 2013+, cygwin)
|
|
||||||
* Mac OSX (clang 3.5+)
|
|
||||||
* Android
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
|
* Very fast (see [benchmarks](#benchmarks) below).
|
||||||
* Headers only, just copy and use.
|
* Headers only, just copy and use. Or use as a compiled library.
|
||||||
* Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library.
|
* Feature rich formatting, using the excellent [fmt](https://github.com/fmtlib/fmt) library.
|
||||||
|
* **New!** [Backtrace](#backtrace-support) support - store debug or other messages in a ring buffer and display later on demand.
|
||||||
* Fast asynchronous mode (optional)
|
* Fast asynchronous mode (optional)
|
||||||
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
|
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
|
||||||
* Conditional Logging
|
|
||||||
* Multi/Single threaded loggers.
|
* Multi/Single threaded loggers.
|
||||||
* Various log targets:
|
* Various log targets:
|
||||||
* Rotating log files.
|
* Rotating log files.
|
||||||
@@ -42,78 +48,51 @@ Very fast, header only, C++ logging library. [ interface).
|
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
|
||||||
* Severity based filtering - threshold levels can be modified in runtime as well as in compile time.
|
* Severity based filtering - threshold levels can be modified in runtime as well as in compile time.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Benchmarks
|
|
||||||
|
|
||||||
Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/bench.cpp) done in Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
|
|
||||||
|
|
||||||
#### Synchronous mode
|
|
||||||
```
|
|
||||||
*******************************************************************************
|
|
||||||
Single thread, 1,000,000 iterations
|
|
||||||
*******************************************************************************
|
|
||||||
basic_st... Elapsed: 0.231041 4,328,228/sec
|
|
||||||
rotating... Elapsed: 0.233466 4,283,282/sec
|
|
||||||
daily_st... Elapsed: 0.244491 4,090,136/sec
|
|
||||||
null_st... Elapsed: 0.162708 6,145,995/sec
|
|
||||||
|
|
||||||
*******************************************************************************
|
|
||||||
10 threads sharing same logger, 1,000,000 iterations
|
|
||||||
*******************************************************************************
|
|
||||||
basic_mt... Elapsed: 0.854029 1,170,920/sec
|
|
||||||
rotating_mt Elapsed: 0.867038 1,153,351/sec
|
|
||||||
daily_mt... Elapsed: 0.869593 1,149,963/sec
|
|
||||||
null_mt... Elapsed: 0.171215 2,033,537/sec
|
|
||||||
```
|
|
||||||
#### Asynchronous mode
|
|
||||||
```
|
|
||||||
*******************************************************************************
|
|
||||||
10 threads sharing same logger, 1,000,000 iterations
|
|
||||||
*******************************************************************************
|
|
||||||
async... Elapsed: 0.442731 2,258,706/sec
|
|
||||||
async... Elapsed: 0.427072 2,341,527/sec
|
|
||||||
async... Elapsed: 0.449768 2,223,369/sec
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage samples
|
## Usage samples
|
||||||
|
|
||||||
|
#### Basic usage
|
||||||
|
```c++
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
spdlog::info("Welcome to spdlog!");
|
||||||
|
spdlog::error("Some error message with arg: {}", 1);
|
||||||
|
|
||||||
|
spdlog::warn("Easy padding in numbers like {:08d}", 12);
|
||||||
|
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||||
|
spdlog::info("Support for floats {:03.2f}", 1.23456);
|
||||||
|
spdlog::info("Positional args are {1} {0}..", "too", "supported");
|
||||||
|
spdlog::info("{:<30}", "left aligned");
|
||||||
|
|
||||||
|
spdlog::set_level(spdlog::level::debug); // Set global log level to debug
|
||||||
|
spdlog::debug("This message should be displayed..");
|
||||||
|
|
||||||
|
// change log pattern
|
||||||
|
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
|
||||||
|
|
||||||
|
// Compile time log levels
|
||||||
|
// define SPDLOG_ACTIVE_LEVEL to desired level
|
||||||
|
SPDLOG_TRACE("Some trace message with param {}", {});
|
||||||
|
SPDLOG_DEBUG("Some debug message");
|
||||||
|
|
||||||
|
// Set the default logger to file logger
|
||||||
|
auto file_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic.txt");
|
||||||
|
spdlog::set_default_logger(file_logger);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
#### create stdout/stderr logger object
|
||||||
```c++
|
```c++
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
void stdout_example()
|
void stdout_example()
|
||||||
{
|
{
|
||||||
// create color multi threaded logger
|
// create color multi threaded logger
|
||||||
auto console = spdlog::stdout_color_mt("console");
|
auto console = spdlog::stdout_color_mt("console");
|
||||||
console->info("Welcome to spdlog!");
|
auto err_logger = spdlog::stderr_color_mt("stderr");
|
||||||
console->error("Some error message with arg: {}", 1);
|
|
||||||
|
|
||||||
auto err_logger = spdlog::stderr_color_mt("stderr");
|
|
||||||
err_logger->error("Some error message");
|
|
||||||
|
|
||||||
// Formatting examples
|
|
||||||
console->warn("Easy padding in numbers like {:08d}", 12);
|
|
||||||
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
|
||||||
console->info("Support for floats {:03.2f}", 1.23456);
|
|
||||||
console->info("Positional args are {1} {0}..", "too", "supported");
|
|
||||||
console->info("{:<30}", "left aligned");
|
|
||||||
|
|
||||||
spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
|
spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
|
||||||
|
|
||||||
// Runtime log levels
|
|
||||||
spdlog::set_level(spdlog::level::info); // Set global log level to info
|
|
||||||
console->debug("This message should not be displayed!");
|
|
||||||
console->set_level(spdlog::level::trace); // Set specific logger's log level
|
|
||||||
console->debug("This message should be displayed..");
|
|
||||||
|
|
||||||
// Customize msg format for all loggers
|
|
||||||
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
|
|
||||||
console->info("This an info message with custom format");
|
|
||||||
|
|
||||||
// Compile time log levels
|
|
||||||
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
|
|
||||||
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
|
|
||||||
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
---
|
---
|
||||||
@@ -129,7 +108,6 @@ void basic_logfile_example()
|
|||||||
catch (const spdlog::spdlog_ex &ex)
|
catch (const spdlog::spdlog_ex &ex)
|
||||||
{
|
{
|
||||||
std::cout << "Log init failed: " << ex.what() << std::endl;
|
std::cout << "Log init failed: " << ex.what() << std::endl;
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -157,6 +135,23 @@ void daily_example()
|
|||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
#### Backtrace support
|
||||||
|
```c++
|
||||||
|
// Loggers can store in a ring buffer all messages (including debug/trace) and display later on demand.
|
||||||
|
// When needed, call dump_backtrace() to see them
|
||||||
|
spdlog::enable_backtrace(32); // create ring buffer with capacity of 32 messages
|
||||||
|
// or my_logger->enable_backtrace(32)..
|
||||||
|
for(int i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
spdlog::debug("Backtrace message {}", i); // not logged yet..
|
||||||
|
}
|
||||||
|
// e.g. if some error happened:
|
||||||
|
spdlog::dump_backtrace(); // log them now! show the last 32 messages
|
||||||
|
|
||||||
|
// or my_logger->dump_backtrace(32)..
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
#### Periodic flush
|
#### Periodic flush
|
||||||
```c++
|
```c++
|
||||||
@@ -167,21 +162,34 @@ spdlog::flush_every(std::chrono::seconds(3));
|
|||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
#### Asynchronous logging
|
#### Log binary data in hex
|
||||||
```c++
|
```c++
|
||||||
#include "spdlog/async.h"
|
// many types of std::container<char> types can be used.
|
||||||
void async_example()
|
// ranges are supported too.
|
||||||
|
// format flags:
|
||||||
|
// {:X} - print in uppercase.
|
||||||
|
// {:s} - don't separate each byte with space.
|
||||||
|
// {:p} - don't print the position on each line start.
|
||||||
|
// {:n} - don't split the output to lines.
|
||||||
|
|
||||||
|
#include "spdlog/fmt/bin_to_hex.h"
|
||||||
|
|
||||||
|
void binary_example()
|
||||||
{
|
{
|
||||||
// default thread pool settings can be modified *before* creating the async logger:
|
auto console = spdlog::get("console");
|
||||||
// spdlog::init_thread_pool(8192, 1); // queue with 8k items and 1 backing thread.
|
std::array<char, 80> buf;
|
||||||
auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
|
console->info("Binary example: {}", spdlog::to_hex(buf));
|
||||||
// alternatively:
|
console->info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10));
|
||||||
// auto async_file = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");
|
// more examples:
|
||||||
|
// logger->info("uppercase: {:X}", spdlog::to_hex(buf));
|
||||||
|
// logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf));
|
||||||
|
// logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
#### Logger with multi targets - each with different format and log level
|
#### Logger with multi sinks - each with different format and log level
|
||||||
```c++
|
```c++
|
||||||
|
|
||||||
// create logger with 2 targets with different log levels and formats.
|
// create logger with 2 targets with different log levels and formats.
|
||||||
@@ -201,6 +209,40 @@ void multi_sink_example()
|
|||||||
logger.info("this message should not appear in the console, only in the file");
|
logger.info("this message should not appear in the console, only in the file");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
#### Asynchronous logging
|
||||||
|
```c++
|
||||||
|
#include "spdlog/async.h"
|
||||||
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
|
void async_example()
|
||||||
|
{
|
||||||
|
// default thread pool settings can be modified *before* creating the async logger:
|
||||||
|
// spdlog::init_thread_pool(8192, 1); // queue with 8k items and 1 backing thread.
|
||||||
|
auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
|
||||||
|
// alternatively:
|
||||||
|
// auto async_file = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
#### Asynchronous logger with multi sinks
|
||||||
|
```c++
|
||||||
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
|
#include "spdlog/sinks/rotating_file_sink.h"
|
||||||
|
|
||||||
|
void multi_sink_example2()
|
||||||
|
{
|
||||||
|
spdlog::init_thread_pool(8192, 1);
|
||||||
|
auto stdout_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt >();
|
||||||
|
auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("mylog.txt", 1024*1024*10, 3);
|
||||||
|
std::vector<spdlog::sink_ptr> sinks {stdout_sink, rotating_sink};
|
||||||
|
auto logger = std::make_shared<spdlog::async_logger>("loggername", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block);
|
||||||
|
spdlog::register_logger(logger);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
#### User defined types
|
#### User defined types
|
||||||
```c++
|
```c++
|
||||||
@@ -240,21 +282,75 @@ void err_handler_example()
|
|||||||
void syslog_example()
|
void syslog_example()
|
||||||
{
|
{
|
||||||
std::string ident = "spdlog-example";
|
std::string ident = "spdlog-example";
|
||||||
auto syslog_logger = spdlog::syslog_logger("syslog", ident, LOG_PID);
|
auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID);
|
||||||
syslog_logger->warn("This is warning that will end up in syslog.");
|
syslog_logger->warn("This is warning that will end up in syslog.");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
---
|
---
|
||||||
#### Android example
|
#### Android example
|
||||||
```c++
|
```c++
|
||||||
#incude "spdlog/sinks/android_sink.h"
|
#include "spdlog/sinks/android_sink.h"
|
||||||
void android_example()
|
void android_example()
|
||||||
{
|
{
|
||||||
std::string tag = "spdlog-android";
|
std::string tag = "spdlog-android";
|
||||||
auto android_logger = spdlog::android_logger("android", tag);
|
auto android_logger = spdlog::android_logger_mt("android", tag);
|
||||||
android_logger->critical("Use \"adb shell logcat\" to view this message.");
|
android_logger->critical("Use \"adb shell logcat\" to view this message.");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Benchmarks
|
||||||
|
|
||||||
|
Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/bench.cpp) done in Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
|
||||||
|
|
||||||
|
#### Synchronous mode
|
||||||
|
```
|
||||||
|
[info] **************************************************************
|
||||||
|
[info] Single thread, 1,000,000 iterations
|
||||||
|
[info] **************************************************************
|
||||||
|
[info] basic_st Elapsed: 0.17 secs 5,777,626/sec
|
||||||
|
[info] rotating_st Elapsed: 0.18 secs 5,475,894/sec
|
||||||
|
[info] daily_st Elapsed: 0.20 secs 5,062,659/sec
|
||||||
|
[info] empty_logger Elapsed: 0.07 secs 14,127,300/sec
|
||||||
|
[info] **************************************************************
|
||||||
|
[info] C-string (400 bytes). Single thread, 1,000,000 iterations
|
||||||
|
[info] **************************************************************
|
||||||
|
[info] basic_st Elapsed: 0.41 secs 2,412,483/sec
|
||||||
|
[info] rotating_st Elapsed: 0.72 secs 1,389,196/sec
|
||||||
|
[info] daily_st Elapsed: 0.42 secs 2,393,298/sec
|
||||||
|
[info] null_st Elapsed: 0.04 secs 27,446,957/sec
|
||||||
|
[info] **************************************************************
|
||||||
|
[info] 10 threads sharing same logger, 1,000,000 iterations
|
||||||
|
[info] **************************************************************
|
||||||
|
[info] basic_mt Elapsed: 0.60 secs 1,659,613/sec
|
||||||
|
[info] rotating_mt Elapsed: 0.62 secs 1,612,493/sec
|
||||||
|
[info] daily_mt Elapsed: 0.61 secs 1,638,305/sec
|
||||||
|
[info] null_mt Elapsed: 0.16 secs 6,272,758/sec
|
||||||
|
```
|
||||||
|
#### ASynchronous mode
|
||||||
|
```
|
||||||
|
[info] -------------------------------------------------
|
||||||
|
[info] Messages : 1,000,000
|
||||||
|
[info] Threads : 10
|
||||||
|
[info] Queue : 8,192 slots
|
||||||
|
[info] Queue memory : 8,192 x 272 = 2,176 KB
|
||||||
|
[info] Total iters : 3
|
||||||
|
[info] -------------------------------------------------
|
||||||
|
[info]
|
||||||
|
[info] *********************************
|
||||||
|
[info] Queue Overflow Policy: block
|
||||||
|
[info] *********************************
|
||||||
|
[info] Elapsed: 1.70784 secs 585,535/sec
|
||||||
|
[info] Elapsed: 1.69805 secs 588,910/sec
|
||||||
|
[info] Elapsed: 1.7026 secs 587,337/sec
|
||||||
|
[info]
|
||||||
|
[info] *********************************
|
||||||
|
[info] Queue Overflow Policy: overrun
|
||||||
|
[info] *********************************
|
||||||
|
[info] Elapsed: 0.372816 secs 2,682,285/sec
|
||||||
|
[info] Elapsed: 0.379758 secs 2,633,255/sec
|
||||||
|
[info] Elapsed: 0.373532 secs 2,677,147/sec
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages.
|
Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages.
|
||||||
|
|||||||
@@ -26,7 +26,9 @@ build_script:
|
|||||||
|
|
||||||
set PATH=C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin;%PATH%
|
set PATH=C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin;%PATH%
|
||||||
|
|
||||||
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE%
|
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DSPDLOG_BUILD_EXAMPLE=ON -DSPDLOG_BUILD_EXAMPLE_HO=ON -DSPDLOG_BUILD_TESTS=ON -DSPDLOG_BUILD_TESTS_HO=OFF
|
||||||
|
|
||||||
cmake --build . --config %BUILD_TYPE%
|
cmake --build . --config %BUILD_TYPE%
|
||||||
test: off
|
|
||||||
|
test_script:
|
||||||
|
- ctest -VV -C "%BUILD_TYPE%"
|
||||||
|
|||||||
28
bench/CMakeLists.txt
Normal file
28
bench/CMakeLists.txt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
# Copyright(c) 2019 spdlog authors
|
||||||
|
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.1)
|
||||||
|
project(spdlog_bench CXX)
|
||||||
|
|
||||||
|
if(NOT TARGET spdlog)
|
||||||
|
# Stand-alone build
|
||||||
|
find_package(spdlog CONFIG REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
find_package(benchmark CONFIG REQUIRED)
|
||||||
|
|
||||||
|
add_executable(bench bench.cpp)
|
||||||
|
spdlog_enable_warnings(bench)
|
||||||
|
target_link_libraries(bench PRIVATE spdlog::spdlog)
|
||||||
|
|
||||||
|
add_executable(async_bench async_bench.cpp)
|
||||||
|
target_link_libraries(async_bench PRIVATE spdlog::spdlog)
|
||||||
|
|
||||||
|
add_executable(latency latency.cpp)
|
||||||
|
target_link_libraries(latency PRIVATE benchmark::benchmark spdlog::spdlog)
|
||||||
|
|
||||||
|
add_executable(formatter-bench formatter-bench.cpp)
|
||||||
|
target_link_libraries(formatter-bench PRIVATE benchmark::benchmark spdlog::spdlog)
|
||||||
|
|
||||||
|
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
CXX ?= g++
|
|
||||||
CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1
|
|
||||||
CXX_RELEASE_FLAGS = -Ofast -flto -Wl,--no-as-needed
|
|
||||||
|
|
||||||
|
|
||||||
binaries=bench latency async_bench
|
|
||||||
|
|
||||||
all: $(binaries)
|
|
||||||
|
|
||||||
bench: bench.cpp
|
|
||||||
$(CXX) bench.cpp -o bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
|
||||||
|
|
||||||
|
|
||||||
async_bench: async_bench.cpp
|
|
||||||
$(CXX) async_bench.cpp -o async_bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
|
||||||
|
|
||||||
|
|
||||||
latency: latency.cpp
|
|
||||||
$(CXX) latency.cpp -o latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
|
||||||
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f *.o logs/* $(binaries)
|
|
||||||
|
|
||||||
rebuild: clean all
|
|
||||||
@@ -6,10 +6,11 @@
|
|||||||
//
|
//
|
||||||
// bench.cpp : spdlog benchmarks
|
// bench.cpp : spdlog benchmarks
|
||||||
//
|
//
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
#include "spdlog/async.h"
|
#include "spdlog/async.h"
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@@ -25,6 +26,11 @@ using namespace utils;
|
|||||||
|
|
||||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4996) // disable fopen warning under msvc
|
||||||
|
#endif // _MSC_VER
|
||||||
|
|
||||||
int count_lines(const char *filename)
|
int count_lines(const char *filename)
|
||||||
{
|
{
|
||||||
int counter = 0;
|
int counter = 0;
|
||||||
@@ -35,24 +41,41 @@ int count_lines(const char *filename)
|
|||||||
if ('\n' == ch)
|
if ('\n' == ch)
|
||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
|
fclose(infile);
|
||||||
|
|
||||||
return counter;
|
return counter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void verify_file(const char *filename, int expected_count)
|
||||||
|
{
|
||||||
|
spdlog::info("Verifying {} to contain {:n} line..", filename, expected_count);
|
||||||
|
auto count = count_lines(filename);
|
||||||
|
if (count != expected_count)
|
||||||
|
{
|
||||||
|
spdlog::error("Test failed. {} has {:n} lines instead of {:n}", filename, count, expected_count);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
spdlog::info("Line count OK ({:n})\n", count);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
|
||||||
int howmany = 1000000;
|
int howmany = 1000000;
|
||||||
int queue_size = howmany + 2;
|
int queue_size = std::min(howmany + 2, 8192);
|
||||||
int threads = 10;
|
int threads = 10;
|
||||||
int iters = 3;
|
int iters = 3;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto console = spdlog::stdout_color_mt("console");
|
spdlog::set_pattern("[%^%l%$] %v");
|
||||||
if (argc == 1)
|
if (argc == 1)
|
||||||
{
|
{
|
||||||
console->set_pattern("%v");
|
spdlog::info("Usage: {} <message_count> <threads> <q_size> <iterations>", argv[0]);
|
||||||
console->info("Usage: {} <message_count> <threads> <q_size> <iterations>", argv[0]);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,38 +84,56 @@ int main(int argc, char *argv[])
|
|||||||
if (argc > 2)
|
if (argc > 2)
|
||||||
threads = atoi(argv[2]);
|
threads = atoi(argv[2]);
|
||||||
if (argc > 3)
|
if (argc > 3)
|
||||||
|
{
|
||||||
queue_size = atoi(argv[3]);
|
queue_size = atoi(argv[3]);
|
||||||
|
if (queue_size > 500000)
|
||||||
|
{
|
||||||
|
spdlog::error("Max queue size allowed: 500,000");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (argc > 4)
|
if (argc > 4)
|
||||||
iters = atoi(argv[4]);
|
iters = atoi(argv[4]);
|
||||||
|
|
||||||
console->info("-------------------------------------------------");
|
auto slot_size = sizeof(spdlog::details::async_msg);
|
||||||
console->info("Messages: {:14n}", howmany);
|
spdlog::info("-------------------------------------------------");
|
||||||
console->info("Threads : {:14n}", threads);
|
spdlog::info("Messages : {:n}", howmany);
|
||||||
console->info("Queue : {:14n}", queue_size);
|
spdlog::info("Threads : {:n}", threads);
|
||||||
console->info("Iters : {:>14n}", iters);
|
spdlog::info("Queue : {:n} slots", queue_size);
|
||||||
console->info("-------------------------------------------------");
|
spdlog::info("Queue memory : {:n} x {} = {:n} KB ", queue_size, slot_size, (queue_size * slot_size) / 1024);
|
||||||
|
spdlog::info("Total iters : {:n}", iters);
|
||||||
|
spdlog::info("-------------------------------------------------");
|
||||||
|
|
||||||
const char *filename = "logs/basic_async.log";
|
const char *filename = "logs/basic_async.log";
|
||||||
|
spdlog::info("");
|
||||||
|
spdlog::info("*********************************");
|
||||||
|
spdlog::info("Queue Overflow Policy: block");
|
||||||
|
spdlog::info("*********************************");
|
||||||
for (int i = 0; i < iters; i++)
|
for (int i = 0; i < iters; i++)
|
||||||
{
|
{
|
||||||
auto tp = std::make_shared<details::thread_pool>(queue_size, 1);
|
auto tp = std::make_shared<details::thread_pool>(queue_size, 1);
|
||||||
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
|
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
|
||||||
auto logger = std::make_shared<async_logger>("async_logger", std::move(file_sink), std::move(tp), async_overflow_policy::block);
|
auto logger = std::make_shared<async_logger>("async_logger", std::move(file_sink), std::move(tp), async_overflow_policy::block);
|
||||||
bench_mt(howmany, std::move(logger), threads);
|
bench_mt(howmany, std::move(logger), threads);
|
||||||
auto count = count_lines(filename);
|
// verify_file(filename, howmany);
|
||||||
|
|
||||||
if (count != howmany)
|
|
||||||
{
|
|
||||||
console->error("Test failed. {} has {:n} lines instead of {:n}", filename, count, howmany);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
console->info("Line count OK ({:n})\n", count);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spdlog::info("");
|
||||||
|
spdlog::info("*********************************");
|
||||||
|
spdlog::info("Queue Overflow Policy: overrun");
|
||||||
|
spdlog::info("*********************************");
|
||||||
|
// do same test but discard oldest if queue is full instead of blocking
|
||||||
|
filename = "logs/basic_async-overrun.log";
|
||||||
|
for (int i = 0; i < iters; i++)
|
||||||
|
{
|
||||||
|
auto tp = std::make_shared<details::thread_pool>(queue_size, 1);
|
||||||
|
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
|
||||||
|
auto logger =
|
||||||
|
std::make_shared<async_logger>("async_logger", std::move(file_sink), std::move(tp), async_overflow_policy::overrun_oldest);
|
||||||
|
bench_mt(howmany, std::move(logger), threads);
|
||||||
|
}
|
||||||
|
spdlog::shutdown();
|
||||||
}
|
}
|
||||||
catch (std::exception &ex)
|
catch (std::exception &ex)
|
||||||
{
|
{
|
||||||
@@ -134,5 +175,5 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> logger, int thread_co
|
|||||||
|
|
||||||
auto delta = high_resolution_clock::now() - start;
|
auto delta = high_resolution_clock::now() - start;
|
||||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
spdlog::get("console")->info("Elapsed: {} secs\t {:n}/sec", delta_d, int(howmany / delta_d));
|
spdlog::info("Elapsed: {} secs\t {:n}/sec", delta_d, int(howmany / delta_d));
|
||||||
}
|
}
|
||||||
|
|||||||
198
bench/bench.cpp
198
bench/bench.cpp
@@ -6,16 +6,15 @@
|
|||||||
//
|
//
|
||||||
// bench.cpp : spdlog benchmarks
|
// bench.cpp : spdlog benchmarks
|
||||||
//
|
//
|
||||||
#include "spdlog/async.h"
|
#include "spdlog/spdlog.h"
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
#include "spdlog/sinks/daily_file_sink.h"
|
#include "spdlog/sinks/daily_file_sink.h"
|
||||||
#include "spdlog/sinks/null_sink.h"
|
#include "spdlog/sinks/null_sink.h"
|
||||||
#include "spdlog/sinks/rotating_file_sink.h"
|
#include "spdlog/sinks/rotating_file_sink.h"
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstdlib> // EXIT_FAILURE
|
#include <cstdlib> // EXIT_FAILURE
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@@ -28,77 +27,106 @@ using namespace utils;
|
|||||||
|
|
||||||
void bench(int howmany, std::shared_ptr<spdlog::logger> log);
|
void bench(int howmany, std::shared_ptr<spdlog::logger> log);
|
||||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
||||||
|
void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log);
|
||||||
|
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log);
|
||||||
|
|
||||||
|
static size_t file_size = 30 * 1024 * 1024;
|
||||||
|
static size_t rotating_files = 5;
|
||||||
|
|
||||||
|
void bench_threaded_logging(int threads, int iters)
|
||||||
|
{
|
||||||
|
spdlog::info("**************************************************************");
|
||||||
|
spdlog::info("Multi threaded: {:n} threads, {:n} messages", threads, iters);
|
||||||
|
spdlog::info("**************************************************************");
|
||||||
|
|
||||||
|
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
|
||||||
|
bench_mt(iters, std::move(basic_mt), threads);
|
||||||
|
auto basic_mt_tracing = spdlog::basic_logger_mt("basic_mt/backtrace-on", "logs/basic_mt.log", true);
|
||||||
|
basic_mt_tracing->enable_backtrace(32);
|
||||||
|
bench_mt(iters, std::move(basic_mt_tracing), threads);
|
||||||
|
|
||||||
|
spdlog::info("");
|
||||||
|
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files);
|
||||||
|
bench_mt(iters, std::move(rotating_mt), threads);
|
||||||
|
auto rotating_mt_tracing = spdlog::rotating_logger_mt("rotating_mt/backtrace-on", "logs/rotating_mt.log", file_size, rotating_files);
|
||||||
|
rotating_mt_tracing->enable_backtrace(32);
|
||||||
|
bench_mt(iters, std::move(rotating_mt_tracing), threads);
|
||||||
|
|
||||||
|
spdlog::info("");
|
||||||
|
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
|
||||||
|
bench_mt(iters, std::move(daily_mt), threads);
|
||||||
|
auto daily_mt_tracing = spdlog::daily_logger_mt("daily_mt/backtrace-on", "logs/daily_mt.log");
|
||||||
|
daily_mt_tracing->enable_backtrace(32);
|
||||||
|
bench_mt(iters, std::move(daily_mt_tracing), threads);
|
||||||
|
|
||||||
|
spdlog::info("");
|
||||||
|
auto empty_logger = std::make_shared<spdlog::logger>("level-off");
|
||||||
|
empty_logger->set_level(spdlog::level::off);
|
||||||
|
bench(iters, empty_logger);
|
||||||
|
auto empty_logger_tracing = std::make_shared<spdlog::logger>("level-off/backtrace-on");
|
||||||
|
empty_logger_tracing->set_level(spdlog::level::off);
|
||||||
|
empty_logger_tracing->enable_backtrace(32);
|
||||||
|
bench(iters, empty_logger_tracing);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_single_threaded(int iters)
|
||||||
|
{
|
||||||
|
spdlog::info("**************************************************************");
|
||||||
|
spdlog::info("Single threaded: {:n} messages", iters);
|
||||||
|
spdlog::info("**************************************************************");
|
||||||
|
|
||||||
|
auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true);
|
||||||
|
bench(iters, std::move(basic_st));
|
||||||
|
|
||||||
|
auto basic_st_tracing = spdlog::basic_logger_st("basic_st/backtrace-on", "logs/basic_st.log", true);
|
||||||
|
bench(iters, std::move(basic_st_tracing));
|
||||||
|
|
||||||
|
spdlog::info("");
|
||||||
|
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
|
||||||
|
bench(iters, std::move(rotating_st));
|
||||||
|
auto rotating_st_tracing = spdlog::rotating_logger_st("rotating_st/backtrace-on", "logs/rotating_st.log", file_size, rotating_files);
|
||||||
|
rotating_st_tracing->enable_backtrace(32);
|
||||||
|
bench(iters, std::move(rotating_st_tracing));
|
||||||
|
|
||||||
|
spdlog::info("");
|
||||||
|
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
|
||||||
|
bench(iters, std::move(daily_st));
|
||||||
|
auto daily_st_tracing = spdlog::daily_logger_st("daily_st/backtrace-on", "logs/daily_st.log");
|
||||||
|
daily_st_tracing->enable_backtrace(32);
|
||||||
|
bench(iters, std::move(daily_st_tracing));
|
||||||
|
|
||||||
|
spdlog::info("");
|
||||||
|
auto empty_logger = std::make_shared<spdlog::logger>("level-off");
|
||||||
|
empty_logger->set_level(spdlog::level::off);
|
||||||
|
bench(iters, empty_logger);
|
||||||
|
|
||||||
|
auto empty_logger_tracing = std::make_shared<spdlog::logger>("level-off/backtrace-on");
|
||||||
|
empty_logger_tracing->set_level(spdlog::level::off);
|
||||||
|
empty_logger_tracing->enable_backtrace(32);
|
||||||
|
bench(iters, empty_logger_tracing);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
spdlog::set_automatic_registration(false);
|
||||||
int howmany = 1000000;
|
spdlog::default_logger()->set_pattern("[%^%l%$] %v");
|
||||||
int queue_size = howmany + 2;
|
int iters = 250000;
|
||||||
int threads = 10;
|
int threads = 4;
|
||||||
int file_size = 30 * 1024 * 1024;
|
|
||||||
int rotating_files = 5;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
if (argc > 1)
|
if (argc > 1)
|
||||||
howmany = atoi(argv[1]);
|
iters = atoi(argv[1]);
|
||||||
if (argc > 2)
|
if (argc > 2)
|
||||||
threads = atoi(argv[2]);
|
threads = atoi(argv[2]);
|
||||||
if (argc > 3)
|
|
||||||
queue_size = atoi(argv[3]);
|
|
||||||
|
|
||||||
cout << "******************************************************************"
|
bench_single_threaded(iters);
|
||||||
"*************\n";
|
bench_threaded_logging(1, iters);
|
||||||
cout << "Single thread, " << format(howmany) << " iterations" << endl;
|
bench_threaded_logging(threads, iters);
|
||||||
cout << "******************************************************************"
|
|
||||||
"*************\n";
|
|
||||||
|
|
||||||
auto basic_st = spdlog::basic_logger_mt("basic_st", "logs/basic_st.log", true);
|
|
||||||
bench(howmany, basic_st);
|
|
||||||
|
|
||||||
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
|
|
||||||
bench(howmany, rotating_st);
|
|
||||||
|
|
||||||
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
|
|
||||||
bench(howmany, daily_st);
|
|
||||||
|
|
||||||
bench(howmany, spdlog::create<null_sink_st>("null_st"));
|
|
||||||
|
|
||||||
cout << "\n****************************************************************"
|
|
||||||
"***************\n";
|
|
||||||
cout << threads << " threads sharing same logger, " << format(howmany) << " iterations" << endl;
|
|
||||||
cout << "******************************************************************"
|
|
||||||
"*************\n";
|
|
||||||
|
|
||||||
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
|
|
||||||
bench_mt(howmany, basic_mt, threads);
|
|
||||||
|
|
||||||
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files);
|
|
||||||
bench_mt(howmany, rotating_mt, threads);
|
|
||||||
|
|
||||||
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
|
|
||||||
bench_mt(howmany, daily_mt, threads);
|
|
||||||
bench_mt(howmany, spdlog::create<null_sink_mt>("null_mt"), threads);
|
|
||||||
|
|
||||||
cout << "\n****************************************************************"
|
|
||||||
"***************\n";
|
|
||||||
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations " << endl;
|
|
||||||
cout << "******************************************************************"
|
|
||||||
"*************\n";
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; ++i)
|
|
||||||
{
|
|
||||||
spdlog::init_thread_pool(queue_size, 1);
|
|
||||||
auto as = spdlog::basic_logger_mt<spdlog::async_factory>("async", "logs/basic_async.log", true);
|
|
||||||
bench_mt(howmany, as, threads);
|
|
||||||
spdlog::drop("async");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (std::exception &ex)
|
catch (std::exception &ex)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: " << ex.what() << std::endl;
|
spdlog::error(ex.what());
|
||||||
perror("Last error");
|
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
@@ -107,7 +135,6 @@ int main(int argc, char *argv[])
|
|||||||
void bench(int howmany, std::shared_ptr<spdlog::logger> log)
|
void bench(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||||
{
|
{
|
||||||
using std::chrono::high_resolution_clock;
|
using std::chrono::high_resolution_clock;
|
||||||
cout << log->name() << "...\t\t" << flush;
|
|
||||||
auto start = high_resolution_clock::now();
|
auto start = high_resolution_clock::now();
|
||||||
for (auto i = 0; i < howmany; ++i)
|
for (auto i = 0; i < howmany; ++i)
|
||||||
{
|
{
|
||||||
@@ -117,14 +144,13 @@ void bench(int howmany, std::shared_ptr<spdlog::logger> log)
|
|||||||
auto delta = high_resolution_clock::now() - start;
|
auto delta = high_resolution_clock::now() - start;
|
||||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
|
|
||||||
cout << "Elapsed: " << delta_d << "\t" << format(int(howmany / delta_d)) << "/sec" << endl;
|
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||||
spdlog::drop(log->name());
|
spdlog::drop(log->name());
|
||||||
}
|
}
|
||||||
|
|
||||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
|
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
|
||||||
{
|
{
|
||||||
using std::chrono::high_resolution_clock;
|
using std::chrono::high_resolution_clock;
|
||||||
cout << log->name() << "...\t\t" << flush;
|
|
||||||
vector<thread> threads;
|
vector<thread> threads;
|
||||||
auto start = high_resolution_clock::now();
|
auto start = high_resolution_clock::now();
|
||||||
for (int t = 0; t < thread_count; ++t)
|
for (int t = 0; t < thread_count; ++t)
|
||||||
@@ -144,5 +170,47 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count
|
|||||||
|
|
||||||
auto delta = high_resolution_clock::now() - start;
|
auto delta = high_resolution_clock::now() - start;
|
||||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
cout << "Elapsed: " << delta_d << "\t" << format(int(howmany / delta_d)) << "/sec" << endl;
|
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||||
|
spdlog::drop(log->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||||
|
{
|
||||||
|
using std::chrono::high_resolution_clock;
|
||||||
|
auto orig_default = spdlog::default_logger();
|
||||||
|
spdlog::set_default_logger(log);
|
||||||
|
auto start = high_resolution_clock::now();
|
||||||
|
for (auto i = 0; i < howmany; ++i)
|
||||||
|
{
|
||||||
|
spdlog::info("Hello logger: msg number {}", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto delta = high_resolution_clock::now() - start;
|
||||||
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
|
spdlog::drop(log->name());
|
||||||
|
spdlog::set_default_logger(std::move(orig_default));
|
||||||
|
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||||
|
{
|
||||||
|
const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus "
|
||||||
|
"lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus volutpat mi, eu consequat sem "
|
||||||
|
"libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam non dapibus eros. Donec fringilla dui sed "
|
||||||
|
"augue pretium, nec scelerisque est maximus. Nullam convallis, sem nec blandit maximus, nisi turpis ornare "
|
||||||
|
"nisl, sit amet volutpat neque massa eu odio. Maecenas malesuada quam ex, posuere congue nibh turpis duis.";
|
||||||
|
using std::chrono::high_resolution_clock;
|
||||||
|
auto orig_default = spdlog::default_logger();
|
||||||
|
spdlog::set_default_logger(log);
|
||||||
|
auto start = high_resolution_clock::now();
|
||||||
|
for (auto i = 0; i < howmany; ++i)
|
||||||
|
{
|
||||||
|
spdlog::log(level::info, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto delta = high_resolution_clock::now() - start;
|
||||||
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
|
spdlog::drop(log->name());
|
||||||
|
spdlog::set_default_logger(std::move(orig_default));
|
||||||
|
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||||
}
|
}
|
||||||
|
|||||||
80
bench/formatter-bench.cpp
Normal file
80
bench/formatter-bench.cpp
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2018 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
#include "spdlog/details/pattern_formatter.h"
|
||||||
|
|
||||||
|
void bench_formatter(benchmark::State &state, std::string pattern)
|
||||||
|
{
|
||||||
|
auto formatter = spdlog::details::make_unique<spdlog::pattern_formatter>(pattern);
|
||||||
|
spdlog::memory_buf_t dest;
|
||||||
|
std::string logger_name = "logger-name";
|
||||||
|
const char *text = "Hello. This is some message with length of 80 ";
|
||||||
|
|
||||||
|
spdlog::source_loc source_loc{"a/b/c/d/myfile.cpp", 123, "some_func()"};
|
||||||
|
spdlog::details::log_msg msg(source_loc, logger_name, spdlog::level::info, text);
|
||||||
|
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
dest.clear();
|
||||||
|
formatter->format(msg, dest);
|
||||||
|
benchmark::DoNotOptimize(dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_formatters()
|
||||||
|
{
|
||||||
|
// basic patterns(single flag)
|
||||||
|
std::string all_flags = "+vtPnlLaAbBcCYDmdHIMSefFprRTXzEisg@luioO%";
|
||||||
|
std::vector<std::string> basic_patterns;
|
||||||
|
for (auto &flag : all_flags)
|
||||||
|
{
|
||||||
|
auto pattern = std::string("%") + flag;
|
||||||
|
benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
||||||
|
|
||||||
|
// pattern = std::string("%16") + flag;
|
||||||
|
// benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
||||||
|
//
|
||||||
|
// // bench center padding
|
||||||
|
// pattern = std::string("%=16") + flag;
|
||||||
|
// benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
// complex patterns
|
||||||
|
std::vector<std::string> patterns = {
|
||||||
|
"[%D %X] [%l] [%n] %v",
|
||||||
|
"[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] %v",
|
||||||
|
"[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] [%t] %v",
|
||||||
|
};
|
||||||
|
for (auto &pattern : patterns)
|
||||||
|
{
|
||||||
|
benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern)->Iterations(2500000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
|
||||||
|
spdlog::set_pattern("[%^%l%$] %v");
|
||||||
|
if (argc != 2)
|
||||||
|
{
|
||||||
|
spdlog::error("Usage: {} <pattern> (or \"all\" to bench all)", argv[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string pattern = argv[1];
|
||||||
|
if (pattern == "all")
|
||||||
|
{
|
||||||
|
bench_formatters();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
||||||
|
}
|
||||||
|
benchmark::Initialize(&argc, argv);
|
||||||
|
benchmark::RunSpecifiedBenchmarks();
|
||||||
|
}
|
||||||
@@ -6,146 +6,164 @@
|
|||||||
//
|
//
|
||||||
// latency.cpp : spdlog latency benchmarks
|
// latency.cpp : spdlog latency benchmarks
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
#include "spdlog/async.h"
|
#include "spdlog/async.h"
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
#include "spdlog/sinks/daily_file_sink.h"
|
#include "spdlog/sinks/daily_file_sink.h"
|
||||||
#include "spdlog/sinks/null_sink.h"
|
#include "spdlog/sinks/null_sink.h"
|
||||||
#include "spdlog/sinks/rotating_file_sink.h"
|
#include "spdlog/sinks/rotating_file_sink.h"
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#include "utils.h"
|
|
||||||
#include <atomic>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
using namespace std;
|
void prepare_logdir()
|
||||||
using namespace std::chrono;
|
|
||||||
using namespace spdlog;
|
|
||||||
using namespace spdlog::sinks;
|
|
||||||
using namespace utils;
|
|
||||||
|
|
||||||
void bench(int howmany, std::shared_ptr<spdlog::logger> log);
|
|
||||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
|
||||||
|
|
||||||
int main(int, char *[])
|
|
||||||
{
|
{
|
||||||
std::srand(static_cast<unsigned>(std::time(nullptr))); // use current time as seed for random generator
|
spdlog::info("Preparing latency_logs directory..");
|
||||||
int howmany = 1000000;
|
#ifdef _WIN32
|
||||||
int queue_size = howmany + 2;
|
system("if not exist logs mkdir latency_logs");
|
||||||
int threads = 10;
|
system("del /F /Q logs\\*");
|
||||||
int file_size = 30 * 1024 * 1024;
|
#else
|
||||||
int rotating_files = 5;
|
auto rv = system("mkdir -p latency_logs");
|
||||||
|
if (rv != 0)
|
||||||
try
|
|
||||||
{
|
{
|
||||||
|
throw std::runtime_error("Failed to mkdir -p latency_logs");
|
||||||
cout << "******************************************************************"
|
|
||||||
"*************\n";
|
|
||||||
cout << "Single thread\n";
|
|
||||||
cout << "******************************************************************"
|
|
||||||
"*************\n";
|
|
||||||
|
|
||||||
auto basic_st = spdlog::basic_logger_mt("basic_st", "logs/basic_st.log", true);
|
|
||||||
bench(howmany, basic_st);
|
|
||||||
|
|
||||||
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
|
|
||||||
bench(howmany, rotating_st);
|
|
||||||
|
|
||||||
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
|
|
||||||
bench(howmany, daily_st);
|
|
||||||
|
|
||||||
bench(howmany, spdlog::create<null_sink_st>("null_st"));
|
|
||||||
|
|
||||||
cout << "\n****************************************************************"
|
|
||||||
"***************\n";
|
|
||||||
cout << threads << " threads sharing same logger\n";
|
|
||||||
cout << "******************************************************************"
|
|
||||||
"*************\n";
|
|
||||||
|
|
||||||
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
|
|
||||||
bench_mt(howmany, basic_mt, threads);
|
|
||||||
|
|
||||||
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files);
|
|
||||||
bench_mt(howmany, rotating_mt, threads);
|
|
||||||
|
|
||||||
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
|
|
||||||
bench_mt(howmany, daily_mt, threads);
|
|
||||||
bench(howmany, spdlog::create<null_sink_st>("null_mt"));
|
|
||||||
|
|
||||||
cout << "\n****************************************************************"
|
|
||||||
"***************\n";
|
|
||||||
cout << "async logging.. " << threads << " threads sharing same logger\n";
|
|
||||||
cout << "******************************************************************"
|
|
||||||
"*************\n";
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; ++i)
|
|
||||||
{
|
|
||||||
spdlog::init_thread_pool(queue_size, 1);
|
|
||||||
auto as = spdlog::basic_logger_mt<spdlog::async_factory>("async", "logs/basic_async.log", true);
|
|
||||||
bench_mt(howmany, as, threads);
|
|
||||||
spdlog::drop("async");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (std::exception &ex)
|
rv = system("rm -f latency_logs/*");
|
||||||
|
if (rv != 0)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: " << ex.what() << std::endl;
|
throw std::runtime_error("Failed to rm -f latency_logs/*");
|
||||||
perror("Last error");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
}
|
||||||
return EXIT_SUCCESS;
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void bench(int howmany, std::shared_ptr<spdlog::logger> log)
|
void bench_c_string(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
|
||||||
{
|
{
|
||||||
using namespace std::chrono;
|
const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus "
|
||||||
using chrono::high_resolution_clock;
|
"lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus volutpat mi, eu consequat sem "
|
||||||
using chrono::milliseconds;
|
"libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam non dapibus eros. Donec fringilla dui sed "
|
||||||
using chrono::nanoseconds;
|
"augue pretium, nec scelerisque est maximus. Nullam convallis, sem nec blandit maximus, nisi turpis ornare "
|
||||||
|
"nisl, sit amet volutpat neque massa eu odio. Maecenas malesuada quam ex, posuere congue nibh turpis duis.";
|
||||||
|
|
||||||
cout << log->name() << "...\t\t" << flush;
|
for (auto _ : state)
|
||||||
nanoseconds total_nanos = nanoseconds::zero();
|
|
||||||
for (auto i = 0; i < howmany; ++i)
|
|
||||||
{
|
{
|
||||||
auto start = high_resolution_clock::now();
|
logger->info(msg);
|
||||||
log->info("Hello logger: msg number {}", i);
|
|
||||||
auto delta_nanos = chrono::duration_cast<nanoseconds>(high_resolution_clock::now() - start);
|
|
||||||
total_nanos += delta_nanos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto avg = total_nanos.count() / howmany;
|
|
||||||
cout << format(avg) << " ns/call" << endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
|
void bench_logger(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
|
||||||
{
|
{
|
||||||
using namespace std::chrono;
|
int i = 0;
|
||||||
using chrono::high_resolution_clock;
|
for (auto _ : state)
|
||||||
using chrono::milliseconds;
|
|
||||||
using chrono::nanoseconds;
|
|
||||||
|
|
||||||
cout << log->name() << "...\t\t" << flush;
|
|
||||||
vector<thread> threads;
|
|
||||||
std::atomic<nanoseconds::rep> total_nanos{0};
|
|
||||||
for (int t = 0; t < thread_count; ++t)
|
|
||||||
{
|
{
|
||||||
threads.push_back(std::thread([&]() {
|
logger->info("Hello logger: msg number {}...............", ++i);
|
||||||
for (int j = 0; j < howmany / thread_count; j++)
|
|
||||||
{
|
|
||||||
auto start = high_resolution_clock::now();
|
|
||||||
log->info("Hello logger: msg number {}", j);
|
|
||||||
auto delta_nanos = chrono::duration_cast<nanoseconds>(high_resolution_clock::now() - start);
|
|
||||||
total_nanos += delta_nanos.count();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
for (auto &t : threads)
|
|
||||||
{
|
void bench_disabled_macro(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
|
||||||
t.join();
|
{
|
||||||
};
|
int i = 0;
|
||||||
|
benchmark::DoNotOptimize(i); // prevent unused warnings
|
||||||
auto avg = total_nanos / howmany;
|
benchmark::DoNotOptimize(logger); // prevent unused warnings
|
||||||
cout << format(avg) << " ns/call" << endl;
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
SPDLOG_LOGGER_DEBUG(logger, "Hello logger: msg number {}...............", i++);
|
||||||
|
SPDLOG_DEBUG("Hello logger: msg number {}...............", i++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
|
||||||
|
using spdlog::sinks::basic_file_sink_mt;
|
||||||
|
using spdlog::sinks::basic_file_sink_st;
|
||||||
|
using spdlog::sinks::null_sink_mt;
|
||||||
|
using spdlog::sinks::null_sink_st;
|
||||||
|
|
||||||
|
size_t file_size = 30 * 1024 * 1024;
|
||||||
|
size_t rotating_files = 5;
|
||||||
|
int n_threads = benchmark::CPUInfo::Get().num_cpus;
|
||||||
|
|
||||||
|
prepare_logdir();
|
||||||
|
|
||||||
|
// disabled loggers
|
||||||
|
auto disabled_logger = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>());
|
||||||
|
disabled_logger->set_level(spdlog::level::off);
|
||||||
|
benchmark::RegisterBenchmark("disabled-at-compile-time", bench_disabled_macro, disabled_logger);
|
||||||
|
benchmark::RegisterBenchmark("disabled-at-runtime", bench_logger, disabled_logger);
|
||||||
|
// with backtrace of 64
|
||||||
|
auto tracing_disabled_logger = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>());
|
||||||
|
tracing_disabled_logger->enable_backtrace(64);
|
||||||
|
benchmark::RegisterBenchmark("disabled-at-runtime/backtrace", bench_logger, tracing_disabled_logger);
|
||||||
|
|
||||||
|
auto null_logger_st = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_st>());
|
||||||
|
benchmark::RegisterBenchmark("null_sink_st (500_bytes c_str)", bench_c_string, std::move(null_logger_st));
|
||||||
|
benchmark::RegisterBenchmark("null_sink_st", bench_logger, null_logger_st);
|
||||||
|
// with backtrace of 64
|
||||||
|
auto tracing_null_logger_st = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_st>());
|
||||||
|
tracing_null_logger_st->enable_backtrace(64);
|
||||||
|
benchmark::RegisterBenchmark("null_sink_st/backtrace", bench_logger, tracing_null_logger_st);
|
||||||
|
|
||||||
|
// basic_st
|
||||||
|
auto basic_st = spdlog::basic_logger_st("basic_st", "latency_logs/basic_st.log", true);
|
||||||
|
benchmark::RegisterBenchmark("basic_st", bench_logger, std::move(basic_st))->UseRealTime();
|
||||||
|
spdlog::drop("basic_st");
|
||||||
|
// with backtrace of 64
|
||||||
|
auto tracing_basic_st = spdlog::basic_logger_st("tracing_basic_st", "latency_logs/tracing_basic_st.log", true);
|
||||||
|
tracing_basic_st->enable_backtrace(64);
|
||||||
|
benchmark::RegisterBenchmark("basic_st/backtrace", bench_logger, std::move(tracing_basic_st))->UseRealTime();
|
||||||
|
spdlog::drop("tracing_basic_st");
|
||||||
|
|
||||||
|
// rotating st
|
||||||
|
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "latency_logs/rotating_st.log", file_size, rotating_files);
|
||||||
|
benchmark::RegisterBenchmark("rotating_st", bench_logger, std::move(rotating_st))->UseRealTime();
|
||||||
|
spdlog::drop("rotating_st");
|
||||||
|
// with backtrace of 64
|
||||||
|
auto tracing_rotating_st =
|
||||||
|
spdlog::rotating_logger_st("tracing_rotating_st", "latency_logs/tracing_rotating_st.log", file_size, rotating_files);
|
||||||
|
benchmark::RegisterBenchmark("rotating_st/backtrace", bench_logger, std::move(tracing_rotating_st))->UseRealTime();
|
||||||
|
spdlog::drop("tracing_rotating_st");
|
||||||
|
|
||||||
|
// daily st
|
||||||
|
auto daily_st = spdlog::daily_logger_mt("daily_st", "latency_logs/daily_st.log");
|
||||||
|
benchmark::RegisterBenchmark("daily_st", bench_logger, std::move(daily_st))->UseRealTime();
|
||||||
|
spdlog::drop("daily_st");
|
||||||
|
auto tracing_daily_st = spdlog::daily_logger_mt("tracing_daily_st", "latency_logs/daily_st.log");
|
||||||
|
benchmark::RegisterBenchmark("daily_st/backtrace", bench_logger, std::move(tracing_daily_st))->UseRealTime();
|
||||||
|
spdlog::drop("tracing_daily_st");
|
||||||
|
|
||||||
|
//
|
||||||
|
// Multi threaded bench, 10 loggers using same logger concurrently
|
||||||
|
//
|
||||||
|
auto null_logger_mt = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>());
|
||||||
|
benchmark::RegisterBenchmark("null_sink_mt", bench_logger, null_logger_mt)->Threads(n_threads)->UseRealTime();
|
||||||
|
|
||||||
|
// basic_mt
|
||||||
|
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "latency_logs/basic_mt.log", true);
|
||||||
|
benchmark::RegisterBenchmark("basic_mt", bench_logger, std::move(basic_mt))->Threads(n_threads)->UseRealTime();
|
||||||
|
spdlog::drop("basic_mt");
|
||||||
|
|
||||||
|
// rotating mt
|
||||||
|
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "latency_logs/rotating_mt.log", file_size, rotating_files);
|
||||||
|
benchmark::RegisterBenchmark("rotating_mt", bench_logger, std::move(rotating_mt))->Threads(n_threads)->UseRealTime();
|
||||||
|
spdlog::drop("rotating_mt");
|
||||||
|
|
||||||
|
// daily mt
|
||||||
|
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "latency_logs/daily_mt.log");
|
||||||
|
benchmark::RegisterBenchmark("daily_mt", bench_logger, std::move(daily_mt))->Threads(n_threads)->UseRealTime();
|
||||||
|
spdlog::drop("daily_mt");
|
||||||
|
|
||||||
|
// async
|
||||||
|
auto queue_size = 1024 * 1024 * 3;
|
||||||
|
auto tp = std::make_shared<spdlog::details::thread_pool>(queue_size, 1);
|
||||||
|
auto async_logger = std::make_shared<spdlog::async_logger>(
|
||||||
|
"async_logger", std::make_shared<null_sink_mt>(), std::move(tp), spdlog::async_overflow_policy::overrun_oldest);
|
||||||
|
benchmark::RegisterBenchmark("async_logger", bench_logger, async_logger)->Threads(n_threads)->UseRealTime();
|
||||||
|
|
||||||
|
auto async_logger_tracing = std::make_shared<spdlog::async_logger>(
|
||||||
|
"async_logger_tracing", std::make_shared<null_sink_mt>(), std::move(tp), spdlog::async_overflow_policy::overrun_oldest);
|
||||||
|
async_logger_tracing->enable_backtrace(32);
|
||||||
|
benchmark::RegisterBenchmark("async_logger/tracing", bench_logger, async_logger_tracing)->Threads(n_threads)->UseRealTime();
|
||||||
|
|
||||||
|
benchmark::Initialize(&argc, argv);
|
||||||
|
benchmark::RunSpecifiedBenchmarks();
|
||||||
}
|
}
|
||||||
|
|||||||
4
bench/logs/.gitignore
vendored
4
bench/logs/.gitignore
vendored
@@ -1,4 +0,0 @@
|
|||||||
# Ignore everything in this directory
|
|
||||||
*
|
|
||||||
# Except this file
|
|
||||||
!.gitignore
|
|
||||||
19
bench/mem
19
bench/mem
@@ -1,19 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
if [ $# -lt 1 ]; then
|
|
||||||
echo "usage: $0 <program>"
|
|
||||||
fi
|
|
||||||
|
|
||||||
PROG=$1
|
|
||||||
|
|
||||||
if [ ! -x "$PROG" ]; then
|
|
||||||
echo $PROG not found or not executable.
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
$* &
|
|
||||||
PID=$!
|
|
||||||
|
|
||||||
while `kill -0 $PID 2>/dev/null`; do
|
|
||||||
ps -eo size,pid,user,pcpu,command --sort -size | awk '{ line=1 ; hr=$1/1024 ; printf("%13.2f Mb ",hr); } { for ( x=4 ; x<=NF ; x++ ) { printf("%s ",$x) } print "" }' | grep -v grep | grep -v $0 | grep $PROG
|
|
||||||
done
|
|
||||||
15
bench/meson.build
Normal file
15
bench/meson.build
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
benchmark = dependency('benchmark')
|
||||||
|
|
||||||
|
bench_matrix = [
|
||||||
|
['bench', [spdlog_dep], []],
|
||||||
|
['async_bench', [spdlog_dep], []],
|
||||||
|
['formatter-bench', [spdlog_dep, benchmark], ['all']],
|
||||||
|
['latency', [spdlog_dep, benchmark], []],
|
||||||
|
]
|
||||||
|
|
||||||
|
foreach i : bench_matrix
|
||||||
|
bench_exe = executable(i[0], i[0] + '.cpp', dependencies: i[1])
|
||||||
|
benchmark('bench_' + i[0], bench_exe, args: i[2])
|
||||||
|
endforeach
|
||||||
|
|
||||||
|
run_command(find_program('mkdir'), meson.current_build_dir() + '/logs')
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <iostream>
|
|
||||||
#include <thread>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "spdlog/async.h"
|
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
using namespace std::chrono;
|
|
||||||
using clock = steady_clock;
|
|
||||||
|
|
||||||
int thread_count = 10;
|
|
||||||
if (argc > 1)
|
|
||||||
thread_count = std::atoi(argv[1]);
|
|
||||||
|
|
||||||
int howmany = 1000000;
|
|
||||||
spdlog::init_thread_pool(howmany, 1);
|
|
||||||
|
|
||||||
auto logger = spdlog::create_async_logger<spdlog::sinks::basic_file_sink_mt>("file_logger", "logs/spdlog-bench-async.log", false);
|
|
||||||
logger->set_pattern("[%Y-%m-%d %T.%F]: %L %t %v");
|
|
||||||
|
|
||||||
std::cout << "To stop, press <Enter>" << std::endl;
|
|
||||||
std::atomic<bool> run{true};
|
|
||||||
std::thread stoper(std::thread([&run]() {
|
|
||||||
std::cin.get();
|
|
||||||
run = false;
|
|
||||||
}));
|
|
||||||
|
|
||||||
while (run)
|
|
||||||
{
|
|
||||||
std::atomic<int> msg_counter{0};
|
|
||||||
std::vector<std::thread> threads;
|
|
||||||
|
|
||||||
auto start = clock::now();
|
|
||||||
for (int t = 0; t < thread_count; ++t)
|
|
||||||
{
|
|
||||||
threads.push_back(std::thread([&]() {
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
int counter = ++msg_counter;
|
|
||||||
if (counter > howmany)
|
|
||||||
break;
|
|
||||||
logger->info("spdlog message #{}: This is some text for your pleasure", counter);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &t : threads)
|
|
||||||
{
|
|
||||||
t.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
duration<float> delta = clock::now() - start;
|
|
||||||
float deltaf = delta.count();
|
|
||||||
auto rate = howmany / deltaf;
|
|
||||||
|
|
||||||
std::cout << "Total: " << howmany << std::endl;
|
|
||||||
std::cout << "Threads: " << thread_count << std::endl;
|
|
||||||
std::cout << "Delta = " << std::fixed << deltaf << " seconds" << std::endl;
|
|
||||||
std::cout << "Rate = " << std::fixed << rate << "/sec" << std::endl;
|
|
||||||
} // while
|
|
||||||
|
|
||||||
stoper.join();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
# *************************************************************************/
|
|
||||||
# * Copyright (c) 2015 Ruslan Baratov. */
|
|
||||||
# * */
|
|
||||||
# * Permission is hereby granted, free of charge, to any person obtaining */
|
|
||||||
# * a copy of this software and associated documentation files (the */
|
|
||||||
# * "Software"), to deal in the Software without restriction, including */
|
|
||||||
# * without limitation the rights to use, copy, modify, merge, publish, */
|
|
||||||
# * distribute, sublicense, and/or sell copies of the Software, and to */
|
|
||||||
# * permit persons to whom the Software is furnished to do so, subject to */
|
|
||||||
# * the following conditions: */
|
|
||||||
# * */
|
|
||||||
# * The above copyright notice and this permission notice shall be */
|
|
||||||
# * included in all copies or substantial portions of the Software. */
|
|
||||||
# * */
|
|
||||||
# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
|
||||||
# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
|
||||||
# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
|
||||||
# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
|
||||||
# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
|
||||||
# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
|
||||||
# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
||||||
# *************************************************************************/
|
|
||||||
|
|
||||||
include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
|
|
||||||
18
cmake/ide.cmake
Normal file
18
cmake/ide.cmake
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
# IDE support for headers
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
set(SPDLOG_HEADERS_DIR "${CMAKE_CURRENT_LIST_DIR}/../include")
|
||||||
|
|
||||||
|
file(GLOB SPDLOG_TOP_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/*.h")
|
||||||
|
file(GLOB SPDLOG_DETAILS_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/details/*.h")
|
||||||
|
file(GLOB SPDLOG_SINKS_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/sinks/*.h")
|
||||||
|
file(GLOB SPDLOG_FMT_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/fmt/*.h")
|
||||||
|
file(GLOB SPDLOG_FMT_BUNDELED_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/fmt/bundled/*.h")
|
||||||
|
set(SPDLOG_ALL_HEADERS ${SPDLOG_TOP_HEADERS} ${SPDLOG_DETAILS_HEADERS} ${SPDLOG_SINKS_HEADERS} ${SPDLOG_FMT_HEADERS} ${SPDLOG_FMT_BUNDELED_HEADERS})
|
||||||
|
|
||||||
|
source_group("Header Files\\spdlog" FILES ${SPDLOG_TOP_HEADERS})
|
||||||
|
source_group("Header Files\\spdlog\\details" FILES ${SPDLOG_DETAILS_HEADERS})
|
||||||
|
source_group("Header Files\\spdlog\\sinks" FILES ${SPDLOG_SINKS_HEADERS})
|
||||||
|
source_group("Header Files\\spdlog\\fmt" FILES ${SPDLOG_FMT_HEADERS})
|
||||||
|
source_group("Header Files\\spdlog\\fmt\\bundled\\" FILES ${SPDLOG_FMT_BUNDELED_HEADERS})
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
prefix=@CMAKE_INSTALL_PREFIX@
|
|
||||||
includedir=${prefix}/include
|
|
||||||
|
|
||||||
Name: @PROJECT_NAME@
|
|
||||||
Description: Super fast C++ logging library.
|
|
||||||
Version: @PROJECT_VERSION@
|
|
||||||
26
cmake/spdlogCPack.cmake
Normal file
26
cmake/spdlogCPack.cmake
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
set(CPACK_GENERATOR
|
||||||
|
TGZ
|
||||||
|
ZIP
|
||||||
|
)
|
||||||
|
|
||||||
|
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0)
|
||||||
|
set(CPACK_INSTALL_CMAKE_PROJECTS
|
||||||
|
"${CMAKE_BINARY_DIR}"
|
||||||
|
"${PROJECT_NAME}"
|
||||||
|
ALL
|
||||||
|
.
|
||||||
|
)
|
||||||
|
|
||||||
|
set(CPACK_PROJECT_URL "https://github.com/gabime/spdlog")
|
||||||
|
set(CPACK_PACKAGE_VENDOR "Gabi Melman")
|
||||||
|
set(CPACK_PACKAGE_CONTACT "Gabi Melman <gmelman1@gmail.com>")
|
||||||
|
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
|
||||||
|
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
|
||||||
|
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
|
||||||
|
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
|
||||||
|
if (PROJECT_VERSION_TWEAK)
|
||||||
|
set(CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION}.${PROJECT_VERSION_TWEAK})
|
||||||
|
endif ()
|
||||||
|
set(CPACK_PACKAGE_RELOCATABLE ON)
|
||||||
|
|
||||||
|
include(CPack)
|
||||||
15
cmake/spdlogConfig.cmake.in
Normal file
15
cmake/spdlogConfig.cmake.in
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Copyright(c) 2019 spdlog authors
|
||||||
|
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
|
set(SPDLOG_FMT_EXTERNAL @SPDLOG_FMT_EXTERNAL@)
|
||||||
|
set(config_targets_file @config_targets_file@)
|
||||||
|
|
||||||
|
if(SPDLOG_FMT_EXTERNAL)
|
||||||
|
include(CMakeFindDependencyMacro)
|
||||||
|
find_dependency(fmt CONFIG)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/${config_targets_file}")
|
||||||
47
cmake/utils.cmake
Normal file
47
cmake/utils.cmake
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
# Get spdlog version from include/spdlog/version.h and put it in SPDLOG_VERSION
|
||||||
|
function(spdlog_extract_version)
|
||||||
|
file(READ "${CMAKE_CURRENT_LIST_DIR}/include/spdlog/version.h" file_contents)
|
||||||
|
string(REGEX MATCH "SPDLOG_VER_MAJOR ([0-9]+)" _ "${file_contents}")
|
||||||
|
if(NOT CMAKE_MATCH_COUNT EQUAL 1)
|
||||||
|
message(FATAL_ERROR "Could not extract major version number from spdlog/version.h")
|
||||||
|
endif()
|
||||||
|
set(ver_major ${CMAKE_MATCH_1})
|
||||||
|
|
||||||
|
string(REGEX MATCH "SPDLOG_VER_MINOR ([0-9]+)" _ "${file_contents}")
|
||||||
|
if(NOT CMAKE_MATCH_COUNT EQUAL 1)
|
||||||
|
message(FATAL_ERROR "Could not extract minor version number from spdlog/version.h")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(ver_minor ${CMAKE_MATCH_1})
|
||||||
|
string(REGEX MATCH "SPDLOG_VER_PATCH ([0-9]+)" _ "${file_contents}")
|
||||||
|
if(NOT CMAKE_MATCH_COUNT EQUAL 1)
|
||||||
|
message(FATAL_ERROR "Could not extract patch version number from spdlog/version.h")
|
||||||
|
endif()
|
||||||
|
set(ver_patch ${CMAKE_MATCH_1})
|
||||||
|
|
||||||
|
set(SPDLOG_VERSION_MAJOR ${ver_major} PARENT_SCOPE)
|
||||||
|
set (SPDLOG_VERSION "${ver_major}.${ver_minor}.${ver_patch}" PARENT_SCOPE)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
|
||||||
|
# Turn on warnings on the given target
|
||||||
|
function(spdlog_enable_warnings target_name)
|
||||||
|
target_compile_options(${target_name} PRIVATE
|
||||||
|
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
|
||||||
|
-Wall -Wextra -Wconversion -pedantic -Wfatal-errors>
|
||||||
|
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
|
||||||
|
# Enable address sanitizer (gcc/clang only)
|
||||||
|
function(spdlog_enable_sanitizer target_name)
|
||||||
|
if (NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||||
|
message(FATAL_ERROR "Sanitizer supported only for gcc/clang")
|
||||||
|
endif()
|
||||||
|
message(STATUS "Address sanitizer enabled")
|
||||||
|
target_compile_options(${target_name} PRIVATE -fsanitize=address,undefined)
|
||||||
|
target_compile_options(${target_name} PRIVATE -fno-sanitize=signed-integer-overflow)
|
||||||
|
target_compile_options(${target_name} PRIVATE -fno-sanitize-recover=all)
|
||||||
|
target_compile_options(${target_name} PRIVATE -fno-omit-frame-pointer)
|
||||||
|
target_link_libraries(${target_name} PRIVATE -fsanitize=address,undefined -fuse-ld=gold)
|
||||||
|
endfunction()
|
||||||
@@ -1,49 +1,29 @@
|
|||||||
# *************************************************************************/
|
# Copyright(c) 2019 spdlog authors
|
||||||
# * Copyright (c) 2015 Ruslan Baratov. */
|
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
# * */
|
|
||||||
# * Permission is hereby granted, free of charge, to any person obtaining */
|
|
||||||
# * a copy of this software and associated documentation files (the */
|
|
||||||
# * "Software"), to deal in the Software without restriction, including */
|
|
||||||
# * without limitation the rights to use, copy, modify, merge, publish, */
|
|
||||||
# * distribute, sublicense, and/or sell copies of the Software, and to */
|
|
||||||
# * permit persons to whom the Software is furnished to do so, subject to */
|
|
||||||
# * the following conditions: */
|
|
||||||
# * */
|
|
||||||
# * The above copyright notice and this permission notice shall be */
|
|
||||||
# * included in all copies or substantial portions of the Software. */
|
|
||||||
# * */
|
|
||||||
# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
|
||||||
# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
|
||||||
# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
|
||||||
# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
|
||||||
# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
|
||||||
# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
|
||||||
# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
||||||
# *************************************************************************/
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.1)
|
cmake_minimum_required(VERSION 3.1)
|
||||||
project(SpdlogExamples CXX)
|
project(spdlog_examples CXX)
|
||||||
|
|
||||||
if(TARGET spdlog)
|
if(NOT TARGET spdlog)
|
||||||
# Part of the main project
|
# Stand-alone build
|
||||||
add_library(spdlog::spdlog ALIAS spdlog)
|
find_package(spdlog REQUIRED)
|
||||||
else()
|
|
||||||
# Stand-alone build
|
|
||||||
find_package(spdlog CONFIG REQUIRED)
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
#---------------------------------------------------------------------------------------
|
||||||
|
# Example of using pre-compiled library
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
add_executable(example example.cpp)
|
add_executable(example example.cpp)
|
||||||
target_link_libraries(example spdlog::spdlog Threads::Threads)
|
spdlog_enable_warnings(example)
|
||||||
|
target_link_libraries(example PRIVATE spdlog::spdlog)
|
||||||
|
|
||||||
add_executable(benchmark bench.cpp)
|
#---------------------------------------------------------------------------------------
|
||||||
target_link_libraries(benchmark spdlog::spdlog Threads::Threads)
|
# Example of using header-only library
|
||||||
|
#---------------------------------------------------------------------------------------
|
||||||
|
if(SPDLOG_BUILD_EXAMPLE_HO)
|
||||||
|
add_executable(example_header_only example.cpp)
|
||||||
|
spdlog_enable_warnings(example_header_only)
|
||||||
|
target_link_libraries(example_header_only PRIVATE spdlog::spdlog_header_only)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(multisink multisink.cpp)
|
# Create logs directory
|
||||||
target_link_libraries(multisink spdlog::spdlog Threads::Threads)
|
|
||||||
|
|
||||||
enable_testing()
|
|
||||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
|
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
|
||||||
add_test(NAME RunExample COMMAND example)
|
|
||||||
add_test(NAME RunBenchmark COMMAND benchmark)
|
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
CXX ?= g++
|
|
||||||
CXX_FLAGS = -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1
|
|
||||||
CXX_RELEASE_FLAGS = -O3 -march=native
|
|
||||||
CXX_DEBUG_FLAGS= -g
|
|
||||||
|
|
||||||
all: example
|
|
||||||
debug: example-debug
|
|
||||||
|
|
||||||
example: example.cpp
|
|
||||||
$(CXX) example.cpp -o example $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS)
|
|
||||||
|
|
||||||
|
|
||||||
example-debug: example.cpp
|
|
||||||
$(CXX) example.cpp -o example-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS)
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f *.o logs/*.txt example example-debug
|
|
||||||
|
|
||||||
|
|
||||||
rebuild: clean all
|
|
||||||
rebuild-debug: clean debug
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
CXX = clang++
|
|
||||||
CXXFLAGS = -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -I../include
|
|
||||||
CXX_RELEASE_FLAGS = -O2
|
|
||||||
CXX_DEBUG_FLAGS= -g
|
|
||||||
|
|
||||||
|
|
||||||
all: example
|
|
||||||
debug: example-debug
|
|
||||||
|
|
||||||
example: example.cpp
|
|
||||||
$(CXX) example.cpp -o example-clang $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
example-debug: example.cpp
|
|
||||||
$(CXX) example.cpp -o example-clang-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
|
|
||||||
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f *.o logs/*.txt example-clang example-clang-debug
|
|
||||||
|
|
||||||
|
|
||||||
rebuild: clean all
|
|
||||||
rebuild-debug: clean debug
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
CXX ?= g++
|
|
||||||
CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -pedantic -std=gnu++0x -pthread -Wl,--no-as-needed -I../include
|
|
||||||
CXX_RELEASE_FLAGS = -O3
|
|
||||||
CXX_DEBUG_FLAGS= -g
|
|
||||||
|
|
||||||
|
|
||||||
all: example
|
|
||||||
debug: example-debug
|
|
||||||
|
|
||||||
example: example.cpp
|
|
||||||
$(CXX) example.cpp -o example $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
|
||||||
|
|
||||||
|
|
||||||
example-debug: example.cpp
|
|
||||||
$(CXX) example.cpp -o example-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
|
|
||||||
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -f *.o logs/*.txt example example-debug
|
|
||||||
|
|
||||||
|
|
||||||
rebuild: clean all
|
|
||||||
rebuild-debug: clean debug
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,19 +1,18 @@
|
|||||||
//
|
//
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
//
|
|
||||||
// spdlog usage example
|
// spdlog usage example
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <iostream>
|
#include <cstdio>
|
||||||
|
|
||||||
void stdout_example();
|
void stdout_logger_example();
|
||||||
void basic_example();
|
void basic_example();
|
||||||
void rotating_example();
|
void rotating_example();
|
||||||
void daily_example();
|
void daily_example();
|
||||||
void async_example();
|
void async_example();
|
||||||
|
void binary_example();
|
||||||
|
void trace_example();
|
||||||
void multi_sink_example();
|
void multi_sink_example();
|
||||||
void user_defined_example();
|
void user_defined_example();
|
||||||
void err_handler_example();
|
void err_handler_example();
|
||||||
@@ -23,109 +22,104 @@ void syslog_example();
|
|||||||
|
|
||||||
int main(int, char *[])
|
int main(int, char *[])
|
||||||
{
|
{
|
||||||
|
spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH);
|
||||||
|
spdlog::warn("Easy padding in numbers like {:08d}", 12);
|
||||||
|
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||||
|
spdlog::info("Support for floats {:03.2f}", 1.23456);
|
||||||
|
spdlog::info("Positional args are {1} {0}..", "too", "supported");
|
||||||
|
spdlog::info("{:>8} aligned, {:<8} aligned", "right", "left");
|
||||||
|
|
||||||
|
// Runtime log levels
|
||||||
|
spdlog::set_level(spdlog::level::info); // Set global log level to info
|
||||||
|
spdlog::debug("This message should not be displayed!");
|
||||||
|
spdlog::set_level(spdlog::level::trace); // Set specific logger's log level
|
||||||
|
spdlog::debug("This message should be displayed..");
|
||||||
|
|
||||||
|
// Customize msg format for all loggers
|
||||||
|
spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [thread %t] %v");
|
||||||
|
spdlog::info("This an info message with custom format");
|
||||||
|
spdlog::set_pattern("%+"); // back to default format
|
||||||
|
spdlog::set_level(spdlog::level::info);
|
||||||
|
|
||||||
|
// Backtrace support
|
||||||
|
// Loggers can store in a ring buffer all messages (including debug/trace) for later inspection.
|
||||||
|
// When needed, call dump_backtrace() to see what happened:
|
||||||
|
spdlog::enable_backtrace(10); // create ring buffer with capacity of 10 messages
|
||||||
|
for (int i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
spdlog::debug("Backtrace message {}", i); // not logged..
|
||||||
|
}
|
||||||
|
// e.g. if some error happened:
|
||||||
|
spdlog::dump_backtrace(); // log them now!
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// console logging example
|
stdout_logger_example();
|
||||||
stdout_example();
|
|
||||||
|
|
||||||
// various file loggers
|
|
||||||
basic_example();
|
basic_example();
|
||||||
rotating_example();
|
rotating_example();
|
||||||
daily_example();
|
daily_example();
|
||||||
|
|
||||||
// async logging using a backing thread pool
|
|
||||||
async_example();
|
async_example();
|
||||||
|
binary_example();
|
||||||
// a logger can have multiple targets with different formats
|
|
||||||
multi_sink_example();
|
multi_sink_example();
|
||||||
|
|
||||||
// user defined types logging by implementing operator<<
|
|
||||||
user_defined_example();
|
user_defined_example();
|
||||||
|
|
||||||
// custom error handler
|
|
||||||
err_handler_example();
|
err_handler_example();
|
||||||
|
trace_example();
|
||||||
|
|
||||||
// flush all *registered* loggers using a worker thread every 3 seconds.
|
// Flush all *registered* loggers using a worker thread every 3 seconds.
|
||||||
// note: registered loggers *must* be thread safe for this to work correctly!
|
// note: registered loggers *must* be thread safe for this to work correctly!
|
||||||
spdlog::flush_every(std::chrono::seconds(3));
|
spdlog::flush_every(std::chrono::seconds(3));
|
||||||
|
|
||||||
// apply some function on all registered loggers
|
// Apply some function on all registered loggers
|
||||||
spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); });
|
spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); });
|
||||||
|
|
||||||
// release any threads created by spdlog, and drop all loggers in the registry.
|
// Release all spdlog resources, and drop all loggers in the registry.
|
||||||
|
// This is optional (only mandatory if using windows + async log).
|
||||||
spdlog::shutdown();
|
spdlog::shutdown();
|
||||||
}
|
}
|
||||||
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
|
|
||||||
|
// Exceptions will only be thrown upon failed logger or sink construction (not during logging).
|
||||||
catch (const spdlog::spdlog_ex &ex)
|
catch (const spdlog::spdlog_ex &ex)
|
||||||
{
|
{
|
||||||
std::cout << "Log init failed: " << ex.what() << std::endl;
|
std::printf("Log initialization failed: %s\n", ex.what());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
// or #include "spdlog/sinks/stdout_sinks.h" if no colors needed
|
// or #include "spdlog/sinks/stdout_sinks.h" if no colors needed.
|
||||||
void stdout_example()
|
void stdout_logger_example()
|
||||||
{
|
{
|
||||||
// create color multi threaded logger
|
// Create color multi threaded logger.
|
||||||
auto console = spdlog::stdout_color_mt("console");
|
auto console = spdlog::stdout_color_mt("console");
|
||||||
console->info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH);
|
// or for stderr:
|
||||||
console->error("Some error message with arg: {}", 1);
|
// auto console = spdlog::stderr_color_mt("error-logger");
|
||||||
|
|
||||||
auto err_logger = spdlog::stderr_color_mt("stderr");
|
|
||||||
err_logger->error("Some error message");
|
|
||||||
|
|
||||||
// Formatting examples
|
|
||||||
console->warn("Easy padding in numbers like {:08d}", 12);
|
|
||||||
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
|
||||||
console->info("Support for floats {:03.2f}", 1.23456);
|
|
||||||
console->info("Positional args are {1} {0}..", "too", "supported");
|
|
||||||
console->info("{:<30}", "left aligned");
|
|
||||||
|
|
||||||
spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
|
|
||||||
|
|
||||||
// Runtime log levels
|
|
||||||
spdlog::set_level(spdlog::level::info); // Set global log level to info
|
|
||||||
console->debug("This message should not be displayed!");
|
|
||||||
console->set_level(spdlog::level::trace); // Set specific logger's log level
|
|
||||||
console->debug("This message should be displayed..");
|
|
||||||
|
|
||||||
// Customize msg format for all loggers
|
|
||||||
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
|
|
||||||
console->info("This an info message with custom format");
|
|
||||||
|
|
||||||
// Compile time log levels
|
|
||||||
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
|
|
||||||
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
|
|
||||||
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
void basic_example()
|
void basic_example()
|
||||||
{
|
{
|
||||||
// Create basic file logger (not rotated)
|
// Create basic file logger (not rotated).
|
||||||
auto my_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
|
auto my_logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "spdlog/sinks/rotating_file_sink.h"
|
#include "spdlog/sinks/rotating_file_sink.h"
|
||||||
void rotating_example()
|
void rotating_example()
|
||||||
{
|
{
|
||||||
// Create a file rotating logger with 5mb size max and 3 rotated files
|
// Create a file rotating logger with 5mb size max and 3 rotated files.
|
||||||
auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
|
auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "spdlog/sinks/daily_file_sink.h"
|
#include "spdlog/sinks/daily_file_sink.h"
|
||||||
void daily_example()
|
void daily_example()
|
||||||
{
|
{
|
||||||
// Create a daily logger - a new file is created every day on 2:30am
|
// Create a daily logger - a new file is created every day on 2:30am.
|
||||||
auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "spdlog/async.h"
|
#include "spdlog/async.h"
|
||||||
void async_example()
|
void async_example()
|
||||||
{
|
{
|
||||||
// default thread pool settings can be modified *before* creating the async logger:
|
// Default thread pool settings can be modified *before* creating the async logger:
|
||||||
// spdlog::init_thread_pool(32768, 1); // queue with max 32k items 1 backing thread.
|
// spdlog::init_thread_pool(32768, 1); // queue with max 32k items 1 backing thread.
|
||||||
auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
|
auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
|
||||||
// alternatively:
|
// alternatively:
|
||||||
@@ -137,9 +131,46 @@ void async_example()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// create logger with 2 targets with different log levels and formats
|
// Log binary data as hex.
|
||||||
// the console will show only warnings or errors, while the file will log all
|
// Many types of std::container<char> types can be used.
|
||||||
|
// Iterator ranges are supported too.
|
||||||
|
// Format flags:
|
||||||
|
// {:X} - print in uppercase.
|
||||||
|
// {:s} - don't separate each byte with space.
|
||||||
|
// {:p} - don't print the position on each line start.
|
||||||
|
// {:n} - don't split the output to lines.
|
||||||
|
|
||||||
|
#include "spdlog/fmt/bin_to_hex.h"
|
||||||
|
void binary_example()
|
||||||
|
{
|
||||||
|
std::vector<char> buf;
|
||||||
|
for (int i = 0; i < 80; i++)
|
||||||
|
{
|
||||||
|
buf.push_back(static_cast<char>(i & 0xff));
|
||||||
|
}
|
||||||
|
spdlog::info("Binary example: {}", spdlog::to_hex(buf));
|
||||||
|
spdlog::info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10));
|
||||||
|
// more examples:
|
||||||
|
// logger->info("uppercase: {:X}", spdlog::to_hex(buf));
|
||||||
|
// logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf));
|
||||||
|
// logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile time log levels.
|
||||||
|
// define SPDLOG_ACTIVE_LEVEL to required level (e.g. SPDLOG_LEVEL_TRACE)
|
||||||
|
void trace_example()
|
||||||
|
{
|
||||||
|
// trace from default logger
|
||||||
|
SPDLOG_TRACE("Some trace message.. {} ,{}", 1, 3.23);
|
||||||
|
// debug from default logger
|
||||||
|
SPDLOG_DEBUG("Some debug message.. {} ,{}", 1, 3.23);
|
||||||
|
|
||||||
|
// trace from logger object
|
||||||
|
auto logger = spdlog::get("file_logger");
|
||||||
|
SPDLOG_LOGGER_TRACE(logger, "another trace message");
|
||||||
|
}
|
||||||
|
|
||||||
|
// A logger with multiple sinks (stdout and file) - each with a different format and log level.
|
||||||
void multi_sink_example()
|
void multi_sink_example()
|
||||||
{
|
{
|
||||||
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
||||||
@@ -154,7 +185,8 @@ void multi_sink_example()
|
|||||||
logger.warn("this should appear in both console and file");
|
logger.warn("this should appear in both console and file");
|
||||||
logger.info("this message should not appear in the console, only in the file");
|
logger.info("this message should not appear in the console, only in the file");
|
||||||
}
|
}
|
||||||
// user defined types logging by implementing operator<<
|
|
||||||
|
// User defined types logging by implementing operator<<
|
||||||
#include "spdlog/fmt/ostr.h" // must be included
|
#include "spdlog/fmt/ostr.h" // must be included
|
||||||
struct my_type
|
struct my_type
|
||||||
{
|
{
|
||||||
@@ -168,17 +200,14 @@ struct my_type
|
|||||||
|
|
||||||
void user_defined_example()
|
void user_defined_example()
|
||||||
{
|
{
|
||||||
spdlog::get("console")->info("user defined type: {}", my_type{14});
|
spdlog::info("user defined type: {}", my_type{14});
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// Custom error handler. Will be triggered on log failure.
|
||||||
// custom error handler
|
|
||||||
//
|
|
||||||
void err_handler_example()
|
void err_handler_example()
|
||||||
{
|
{
|
||||||
// can be set globally or per logger(logger->set_error_handler(..))
|
// can be set globally or per logger(logger->set_error_handler(..))
|
||||||
spdlog::set_error_handler([](const std::string &msg) { spdlog::get("console")->error("*** ERROR HANDLER EXAMPLE ***: {}", msg); });
|
spdlog::set_error_handler([](const std::string &msg) { printf("*** Custom log error handler: %s ***\n", msg.c_str()); });
|
||||||
spdlog::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// syslog example (linux/osx/freebsd)
|
// syslog example (linux/osx/freebsd)
|
||||||
@@ -192,7 +221,7 @@ void syslog_example()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Android example
|
// Android example.
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
#include "spdlog/sinks/android_sink.h"
|
#include "spdlog/sinks/android_sink.h"
|
||||||
void android_example()
|
void android_example()
|
||||||
|
|||||||
@@ -1,106 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio 15
|
|
||||||
VisualStudioVersion = 15.0.27703.2018
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example", "example.vcxproj", "{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "spdlog", "spdlog", "{7FC6AB76-AD88-4135-888C-0568E81475AF}"
|
|
||||||
ProjectSection(SolutionItems) = preProject
|
|
||||||
..\include\spdlog\async.h = ..\include\spdlog\async.h
|
|
||||||
..\include\spdlog\async_logger.h = ..\include\spdlog\async_logger.h
|
|
||||||
..\include\spdlog\common.h = ..\include\spdlog\common.h
|
|
||||||
..\include\spdlog\formatter.h = ..\include\spdlog\formatter.h
|
|
||||||
..\include\spdlog\logger.h = ..\include\spdlog\logger.h
|
|
||||||
..\include\spdlog\spdlog.h = ..\include\spdlog\spdlog.h
|
|
||||||
..\include\spdlog\tweakme.h = ..\include\spdlog\tweakme.h
|
|
||||||
..\include\spdlog\version.h = ..\include\spdlog\version.h
|
|
||||||
EndProjectSection
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "details", "details", "{08E93803-E650-42D9-BBB4-3C16979F850E}"
|
|
||||||
ProjectSection(SolutionItems) = preProject
|
|
||||||
..\include\spdlog\details\async_logger_impl.h = ..\include\spdlog\details\async_logger_impl.h
|
|
||||||
..\include\spdlog\details\circular_q.h = ..\include\spdlog\details\circular_q.h
|
|
||||||
..\include\spdlog\details\console_globals.h = ..\include\spdlog\details\console_globals.h
|
|
||||||
..\include\spdlog\details\file_helper.h = ..\include\spdlog\details\file_helper.h
|
|
||||||
..\include\spdlog\details\fmt_helper.h = ..\include\spdlog\details\fmt_helper.h
|
|
||||||
..\include\spdlog\details\log_msg.h = ..\include\spdlog\details\log_msg.h
|
|
||||||
..\include\spdlog\details\logger_impl.h = ..\include\spdlog\details\logger_impl.h
|
|
||||||
..\include\spdlog\details\mpmc_blocking_q.h = ..\include\spdlog\details\mpmc_blocking_q.h
|
|
||||||
..\include\spdlog\details\null_mutex.h = ..\include\spdlog\details\null_mutex.h
|
|
||||||
..\include\spdlog\details\os.h = ..\include\spdlog\details\os.h
|
|
||||||
..\include\spdlog\details\pattern_formatter.h = ..\include\spdlog\details\pattern_formatter.h
|
|
||||||
..\include\spdlog\details\periodic_worker.h = ..\include\spdlog\details\periodic_worker.h
|
|
||||||
..\include\spdlog\details\registry.h = ..\include\spdlog\details\registry.h
|
|
||||||
..\include\spdlog\details\spdlog_impl.h = ..\include\spdlog\details\spdlog_impl.h
|
|
||||||
..\include\spdlog\details\thread_pool.h = ..\include\spdlog\details\thread_pool.h
|
|
||||||
EndProjectSection
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fmt", "fmt", "{82378DE1-8463-4F91-91A0-C2C40E2AEA2A}"
|
|
||||||
ProjectSection(SolutionItems) = preProject
|
|
||||||
..\include\spdlog\fmt\fmt.h = ..\include\spdlog\fmt\fmt.h
|
|
||||||
..\include\spdlog\fmt\ostr.h = ..\include\spdlog\fmt\ostr.h
|
|
||||||
EndProjectSection
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "bundled", "bundled", "{D9CA4494-80D1-48D1-A897-D3564F7B27FF}"
|
|
||||||
ProjectSection(SolutionItems) = preProject
|
|
||||||
..\include\spdlog\fmt\bundled\format.cc = ..\include\spdlog\fmt\bundled\format.cc
|
|
||||||
..\include\spdlog\fmt\bundled\format.h = ..\include\spdlog\fmt\bundled\format.h
|
|
||||||
..\include\spdlog\fmt\bundled\LICENSE.rst = ..\include\spdlog\fmt\bundled\LICENSE.rst
|
|
||||||
..\include\spdlog\fmt\bundled\ostream.cc = ..\include\spdlog\fmt\bundled\ostream.cc
|
|
||||||
..\include\spdlog\fmt\bundled\ostream.h = ..\include\spdlog\fmt\bundled\ostream.h
|
|
||||||
..\include\spdlog\fmt\bundled\posix.cc = ..\include\spdlog\fmt\bundled\posix.cc
|
|
||||||
..\include\spdlog\fmt\bundled\posix.h = ..\include\spdlog\fmt\bundled\posix.h
|
|
||||||
..\include\spdlog\fmt\bundled\printf.cc = ..\include\spdlog\fmt\bundled\printf.cc
|
|
||||||
..\include\spdlog\fmt\bundled\printf.h = ..\include\spdlog\fmt\bundled\printf.h
|
|
||||||
..\include\spdlog\fmt\bundled\time.h = ..\include\spdlog\fmt\bundled\time.h
|
|
||||||
EndProjectSection
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sinks", "sinks", "{27D16BB9-2B81-4F61-80EC-0C7A777248E4}"
|
|
||||||
ProjectSection(SolutionItems) = preProject
|
|
||||||
..\include\spdlog\sinks\android_sink.h = ..\include\spdlog\sinks\android_sink.h
|
|
||||||
..\include\spdlog\sinks\ansicolor_sink.h = ..\include\spdlog\sinks\ansicolor_sink.h
|
|
||||||
..\include\spdlog\sinks\base_sink.h = ..\include\spdlog\sinks\base_sink.h
|
|
||||||
..\include\spdlog\sinks\dist_sink.h = ..\include\spdlog\sinks\dist_sink.h
|
|
||||||
..\include\spdlog\sinks\file_sinks.h = ..\include\spdlog\sinks\file_sinks.h
|
|
||||||
..\include\spdlog\sinks\msvc_sink.h = ..\include\spdlog\sinks\msvc_sink.h
|
|
||||||
..\include\spdlog\sinks\null_sink.h = ..\include\spdlog\sinks\null_sink.h
|
|
||||||
..\include\spdlog\sinks\ostream_sink.h = ..\include\spdlog\sinks\ostream_sink.h
|
|
||||||
..\include\spdlog\sinks\sink.h = ..\include\spdlog\sinks\sink.h
|
|
||||||
..\include\spdlog\sinks\stdout_color_sinks.h = ..\include\spdlog\sinks\stdout_color_sinks.h
|
|
||||||
..\include\spdlog\sinks\stdout_sinks.h = ..\include\spdlog\sinks\stdout_sinks.h
|
|
||||||
..\include\spdlog\sinks\syslog_sink.h = ..\include\spdlog\sinks\syslog_sink.h
|
|
||||||
..\include\spdlog\sinks\wincolor_sink.h = ..\include\spdlog\sinks\wincolor_sink.h
|
|
||||||
..\include\spdlog\sinks\windebug_sink.h = ..\include\spdlog\sinks\windebug_sink.h
|
|
||||||
EndProjectSection
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Win32 = Debug|Win32
|
|
||||||
Debug|x64 = Debug|x64
|
|
||||||
Release|Win32 = Release|Win32
|
|
||||||
Release|x64 = Release|x64
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.ActiveCfg = Debug|Win32
|
|
||||||
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.Build.0 = Debug|Win32
|
|
||||||
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|x64.ActiveCfg = Debug|x64
|
|
||||||
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|x64.Build.0 = Debug|x64
|
|
||||||
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.ActiveCfg = Release|Win32
|
|
||||||
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.Build.0 = Release|Win32
|
|
||||||
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|x64.ActiveCfg = Release|x64
|
|
||||||
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|x64.Build.0 = Release|x64
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(NestedProjects) = preSolution
|
|
||||||
{08E93803-E650-42D9-BBB4-3C16979F850E} = {7FC6AB76-AD88-4135-888C-0568E81475AF}
|
|
||||||
{82378DE1-8463-4F91-91A0-C2C40E2AEA2A} = {7FC6AB76-AD88-4135-888C-0568E81475AF}
|
|
||||||
{D9CA4494-80D1-48D1-A897-D3564F7B27FF} = {82378DE1-8463-4F91-91A0-C2C40E2AEA2A}
|
|
||||||
{27D16BB9-2B81-4F61-80EC-0C7A777248E4} = {7FC6AB76-AD88-4135-888C-0568E81475AF}
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {1BF53532-C5DC-4236-B195-9E17CBE40A48}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
|
||||||
<ItemGroup Label="ProjectConfigurations">
|
|
||||||
<ProjectConfiguration Include="Debug|Win32">
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Debug|x64">
|
|
||||||
<Configuration>Debug</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release|Win32">
|
|
||||||
<Configuration>Release</Configuration>
|
|
||||||
<Platform>Win32</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
<ProjectConfiguration Include="Release|x64">
|
|
||||||
<Configuration>Release</Configuration>
|
|
||||||
<Platform>x64</Platform>
|
|
||||||
</ProjectConfiguration>
|
|
||||||
</ItemGroup>
|
|
||||||
<ItemGroup>
|
|
||||||
<ClCompile Include="example.cpp" />
|
|
||||||
</ItemGroup>
|
|
||||||
<PropertyGroup Label="Globals">
|
|
||||||
<ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid>
|
|
||||||
<Keyword>Win32Proj</Keyword>
|
|
||||||
<RootNamespace>.</RootNamespace>
|
|
||||||
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>true</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
|
||||||
<ConfigurationType>Application</ConfigurationType>
|
|
||||||
<UseDebugLibraries>false</UseDebugLibraries>
|
|
||||||
<PlatformToolset>v141</PlatformToolset>
|
|
||||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
|
||||||
<CharacterSet>Unicode</CharacterSet>
|
|
||||||
</PropertyGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
|
||||||
<ImportGroup Label="ExtensionSettings">
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
|
||||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
|
||||||
</ImportGroup>
|
|
||||||
<PropertyGroup Label="UserMacros" />
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<LinkIncremental>true</LinkIncremental>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<LinkIncremental>true</LinkIncremental>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<LinkIncremental>false</LinkIncremental>
|
|
||||||
</PropertyGroup>
|
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<LinkIncremental>false</LinkIncremental>
|
|
||||||
</PropertyGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<PrecompiledHeader>
|
|
||||||
</PrecompiledHeader>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>Disabled</Optimization>
|
|
||||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|
||||||
<PrecompiledHeaderFile />
|
|
||||||
<PrecompiledHeaderOutputFile />
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<PrecompiledHeader>
|
|
||||||
</PrecompiledHeader>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<Optimization>Disabled</Optimization>
|
|
||||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|
||||||
<PrecompiledHeaderFile>
|
|
||||||
</PrecompiledHeaderFile>
|
|
||||||
<PrecompiledHeaderOutputFile>
|
|
||||||
</PrecompiledHeaderOutputFile>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<PrecompiledHeader>
|
|
||||||
</PrecompiledHeader>
|
|
||||||
<Optimization>MaxSpeed</Optimization>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|
||||||
<PrecompiledHeaderFile />
|
|
||||||
<PrecompiledHeaderOutputFile />
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|
||||||
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
|
||||||
<ClCompile>
|
|
||||||
<WarningLevel>Level3</WarningLevel>
|
|
||||||
<PrecompiledHeader>
|
|
||||||
</PrecompiledHeader>
|
|
||||||
<Optimization>MaxSpeed</Optimization>
|
|
||||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
|
||||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
|
||||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
|
||||||
<AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
|
||||||
<PrecompiledHeaderFile>
|
|
||||||
</PrecompiledHeaderFile>
|
|
||||||
<PrecompiledHeaderOutputFile>
|
|
||||||
</PrecompiledHeaderOutputFile>
|
|
||||||
</ClCompile>
|
|
||||||
<Link>
|
|
||||||
<SubSystem>Console</SubSystem>
|
|
||||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
|
||||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
|
||||||
<OptimizeReferences>true</OptimizeReferences>
|
|
||||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
|
||||||
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
|
||||||
</Link>
|
|
||||||
</ItemDefinitionGroup>
|
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
|
||||||
<ImportGroup Label="ExtensionTargets">
|
|
||||||
</ImportGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# Setup a project
|
|
||||||
LOCAL_PATH := $(call my-dir)
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
|
|
||||||
LOCAL_MODULE := example
|
|
||||||
LOCAL_SRC_FILES := example.cpp
|
|
||||||
LOCAL_CPPFLAGS += -Wall -Wshadow -Wextra -pedantic -std=c++11 -fPIE -pie
|
|
||||||
LOCAL_LDFLAGS += -fPIE -pie
|
|
||||||
|
|
||||||
# Add exception support and set path for spdlog's headers
|
|
||||||
LOCAL_CPPFLAGS += -fexceptions -I../include
|
|
||||||
# Use android's log library
|
|
||||||
LOCAL_LDFLAGS += -llog
|
|
||||||
|
|
||||||
include $(BUILD_EXECUTABLE)
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# Exceptions are used in spdlog. Link to an exception-ready C++ runtime.
|
|
||||||
APP_STL = gnustl_static
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// spdlog usage example
|
|
||||||
//
|
|
||||||
//
|
|
||||||
|
|
||||||
#define SPDLOG_TRACE_ON
|
|
||||||
#define SPDLOG_DEBUG_ON
|
|
||||||
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
void async_example();
|
|
||||||
void syslog_example();
|
|
||||||
void android_example();
|
|
||||||
void user_defined_example();
|
|
||||||
void err_handler_example();
|
|
||||||
|
|
||||||
namespace spd = spdlog;
|
|
||||||
int main(int, char *[])
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Console logger with color
|
|
||||||
auto console = spd::stdout_color_mt("console");
|
|
||||||
console->info("Welcome to spdlog!");
|
|
||||||
console->error("Some error message with arg{}..", 1);
|
|
||||||
|
|
||||||
// Formatting examples
|
|
||||||
console->warn("Easy padding in numbers like {:08d}", 12);
|
|
||||||
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
|
||||||
console->info("Support for floats {:03.2f}", 1.23456);
|
|
||||||
console->info("Positional args are {1} {0}..", "too", "supported");
|
|
||||||
console->info("{:<30}", "left aligned");
|
|
||||||
|
|
||||||
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
|
|
||||||
|
|
||||||
// Create basic file logger (not rotated)
|
|
||||||
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt");
|
|
||||||
my_logger->info("Some log message");
|
|
||||||
|
|
||||||
// Create a file rotating logger with 5mb size max and 3 rotated files
|
|
||||||
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
|
|
||||||
for (int i = 0; i < 10; ++i)
|
|
||||||
rotating_logger->info("{} * {} equals {:>10}", i, i, i * i);
|
|
||||||
|
|
||||||
// Create a daily logger - a new file is created every day on 2:30am
|
|
||||||
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
|
||||||
// trigger flush if the log severity is error or higher
|
|
||||||
daily_logger->flush_on(spd::level::err);
|
|
||||||
daily_logger->info(123.44);
|
|
||||||
|
|
||||||
// Customize msg format for all messages
|
|
||||||
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
|
|
||||||
rotating_logger->info("This is another message with custom format");
|
|
||||||
|
|
||||||
// Runtime log levels
|
|
||||||
spd::set_level(spd::level::info); // Set global log level to info
|
|
||||||
console->debug("This message should not be displayed!");
|
|
||||||
console->set_level(spd::level::debug); // Set specific logger's log level
|
|
||||||
console->debug("This message should be displayed..");
|
|
||||||
|
|
||||||
// Compile time log levels
|
|
||||||
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
|
|
||||||
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
|
|
||||||
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
|
|
||||||
|
|
||||||
// Asynchronous logging is very fast..
|
|
||||||
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
|
|
||||||
async_example();
|
|
||||||
|
|
||||||
// syslog example. linux/osx only
|
|
||||||
syslog_example();
|
|
||||||
|
|
||||||
// android example. compile with NDK
|
|
||||||
android_example();
|
|
||||||
|
|
||||||
// Log user-defined types example
|
|
||||||
user_defined_example();
|
|
||||||
|
|
||||||
// Change default log error handler
|
|
||||||
err_handler_example();
|
|
||||||
|
|
||||||
// Apply a function on all registered loggers
|
|
||||||
spd::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); });
|
|
||||||
|
|
||||||
// Release and close all loggers
|
|
||||||
spdlog::drop_all();
|
|
||||||
}
|
|
||||||
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
|
|
||||||
catch (const spd::spdlog_ex &ex)
|
|
||||||
{
|
|
||||||
std::cout << "Log init failed: " << ex.what() << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void async_example()
|
|
||||||
{
|
|
||||||
size_t q_size = 4096; // queue size must be power of 2
|
|
||||||
spdlog::set_async_mode(q_size);
|
|
||||||
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
|
|
||||||
for (int i = 0; i < 100; ++i)
|
|
||||||
async_file->info("Async message #{}", i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// syslog example (linux/osx/freebsd)
|
|
||||||
void syslog_example()
|
|
||||||
{
|
|
||||||
#ifdef SPDLOG_ENABLE_SYSLOG
|
|
||||||
std::string ident = "spdlog-example";
|
|
||||||
auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID);
|
|
||||||
syslog_logger->warn("This is warning that will end up in syslog.");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Android example
|
|
||||||
void android_example()
|
|
||||||
{
|
|
||||||
#if defined(__ANDROID__)
|
|
||||||
std::string tag = "spdlog-android";
|
|
||||||
auto android_logger = spd::android_logger("android", tag);
|
|
||||||
android_logger->critical("Use \"adb shell logcat\" to view this message.");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// user defined types logging by implementing operator<<
|
|
||||||
struct my_type
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
template<typename OStream>
|
|
||||||
friend OStream &operator<<(OStream &os, const my_type &c)
|
|
||||||
{
|
|
||||||
return os << "[my_type i=" << c.i << "]";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#include "spdlog/fmt/ostr.h" // must be included
|
|
||||||
void user_defined_example()
|
|
||||||
{
|
|
||||||
spd::get("console")->info("user defined type: {}", my_type{14});
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// custom error handler
|
|
||||||
//
|
|
||||||
void err_handler_example()
|
|
||||||
{
|
|
||||||
// can be set globaly or per logger(logger->set_error_handler(..))
|
|
||||||
spdlog::set_error_handler([](const std::string &msg) { std::cerr << "my err handler: " << msg << std::endl; });
|
|
||||||
spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
|
|
||||||
}
|
|
||||||
1
example/logs/.gitignore
vendored
1
example/logs/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
*.txt
|
|
||||||
5
example/meson.build
Normal file
5
example/meson.build
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
executable('example', 'example.cpp', dependencies: spdlog_dep)
|
||||||
|
executable('example_header_only', 'example.cpp', dependencies: spdlog_headeronly_dep)
|
||||||
|
run_command(find_program('mkdir'), meson.current_build_dir() + '/logs')
|
||||||
|
|
||||||
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
#include "spdlog/sinks/file_sinks.h"
|
|
||||||
#include "spdlog/sinks/stdout_sinks.h"
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace spd = spdlog;
|
|
||||||
int main(int, char *[])
|
|
||||||
{
|
|
||||||
bool enable_debug = true;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// This other example use a single logger with multiple sinks.
|
|
||||||
// This means that the same log_msg is forwarded to multiple sinks;
|
|
||||||
// Each sink can have it's own log level and a message will be logged.
|
|
||||||
std::vector<spdlog::sink_ptr> sinks;
|
|
||||||
sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_mt>());
|
|
||||||
sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>("./log_regular_file.txt"));
|
|
||||||
sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>("./log_debug_file.txt"));
|
|
||||||
|
|
||||||
spdlog::logger console_multisink("multisink", sinks.begin(), sinks.end());
|
|
||||||
console_multisink.set_level(spdlog::level::warn);
|
|
||||||
|
|
||||||
sinks[0]->set_level(spdlog::level::trace); // console. Allow everything. Default value
|
|
||||||
sinks[1]->set_level(spdlog::level::trace); // regular file. Allow everything. Default value
|
|
||||||
sinks[2]->set_level(spdlog::level::off); // regular file. Ignore everything.
|
|
||||||
|
|
||||||
console_multisink.warn("warn: will print only on console and regular file");
|
|
||||||
|
|
||||||
if (enable_debug)
|
|
||||||
{
|
|
||||||
console_multisink.set_level(spdlog::level::debug); // level of the logger
|
|
||||||
sinks[1]->set_level(spdlog::level::debug); // regular file
|
|
||||||
sinks[2]->set_level(spdlog::level::debug); // debug file
|
|
||||||
}
|
|
||||||
console_multisink.debug("Debug: you should see this on console and both files");
|
|
||||||
|
|
||||||
// Release and close all loggers
|
|
||||||
spdlog::drop_all();
|
|
||||||
}
|
|
||||||
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
|
|
||||||
catch (const spd::spdlog_ex &ex)
|
|
||||||
{
|
|
||||||
std::cout << "Log init failed: " << ex.what() << std::endl;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <iomanip>
|
|
||||||
#include <locale>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace utils {
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline std::string format(const T &value)
|
|
||||||
{
|
|
||||||
static std::locale loc("");
|
|
||||||
std::stringstream ss;
|
|
||||||
ss.imbue(loc);
|
|
||||||
ss << value;
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
template<>
|
|
||||||
inline std::string format(const double &value)
|
|
||||||
{
|
|
||||||
static std::locale loc("");
|
|
||||||
std::stringstream ss;
|
|
||||||
ss.imbue(loc);
|
|
||||||
ss << std::fixed << std::setprecision(1) << value;
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace utils
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
echo -n "Running dos2unix "
|
|
||||||
find . -name "*\.h" -o -name "*\.cpp"|xargs -I {} sh -c "dos2unix '{}' 2>/dev/null; echo -n '.'"
|
|
||||||
echo
|
|
||||||
echo -n "Running clang-format "
|
|
||||||
find . -name "*\.h" -o -name "*\.cpp"|xargs -I {} sh -c "clang-format -i {}; echo -n '.'"
|
|
||||||
echo
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
//
|
|
||||||
// Copyright(c) 2018 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@@ -23,6 +20,7 @@
|
|||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
|
|
||||||
@@ -37,12 +35,14 @@ template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
|
|||||||
struct async_factory_impl
|
struct async_factory_impl
|
||||||
{
|
{
|
||||||
template<typename Sink, typename... SinkArgs>
|
template<typename Sink, typename... SinkArgs>
|
||||||
static std::shared_ptr<async_logger> create(const std::string &logger_name, SinkArgs &&... args)
|
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&... args)
|
||||||
{
|
{
|
||||||
auto ®istry_inst = details::registry::instance();
|
auto ®istry_inst = details::registry::instance();
|
||||||
|
|
||||||
// create global thread pool if not already exists..
|
// create global thread pool if not already exists..
|
||||||
std::lock_guard<std::recursive_mutex>(registry_inst.tp_mutex());
|
|
||||||
|
auto &mutex = registry_inst.tp_mutex();
|
||||||
|
std::lock_guard<std::recursive_mutex> tp_lock(mutex);
|
||||||
auto tp = registry_inst.get_tp();
|
auto tp = registry_inst.get_tp();
|
||||||
if (tp == nullptr)
|
if (tp == nullptr)
|
||||||
{
|
{
|
||||||
@@ -51,8 +51,8 @@ struct async_factory_impl
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
||||||
auto new_logger = std::make_shared<async_logger>(logger_name, std::move(sink), std::move(tp), OverflowPolicy);
|
auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy);
|
||||||
registry_inst.register_and_init(new_logger);
|
registry_inst.initialize_logger(new_logger);
|
||||||
return new_logger;
|
return new_logger;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -61,22 +61,28 @@ using async_factory = async_factory_impl<async_overflow_policy::block>;
|
|||||||
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
|
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
|
||||||
|
|
||||||
template<typename Sink, typename... SinkArgs>
|
template<typename Sink, typename... SinkArgs>
|
||||||
inline std::shared_ptr<spdlog::logger> create_async(const std::string &logger_name, SinkArgs &&... sink_args)
|
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&... sink_args)
|
||||||
{
|
{
|
||||||
return async_factory::create<Sink>(logger_name, std::forward<SinkArgs>(sink_args)...);
|
return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Sink, typename... SinkArgs>
|
template<typename Sink, typename... SinkArgs>
|
||||||
inline std::shared_ptr<spdlog::logger> create_async_nb(const std::string &logger_name, SinkArgs &&... sink_args)
|
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&... sink_args)
|
||||||
{
|
{
|
||||||
return async_factory_nonblock::create<Sink>(logger_name, std::forward<SinkArgs>(sink_args)...);
|
return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set global thread pool.
|
||||||
|
inline void init_thread_pool(size_t q_size, size_t thread_count, std::function<void()> on_thread_start)
|
||||||
|
{
|
||||||
|
auto tp = std::make_shared<details::thread_pool>(q_size, thread_count, on_thread_start);
|
||||||
|
details::registry::instance().set_tp(std::move(tp));
|
||||||
}
|
}
|
||||||
|
|
||||||
// set global thread pool.
|
// set global thread pool.
|
||||||
inline void init_thread_pool(size_t q_size, size_t thread_count)
|
inline void init_thread_pool(size_t q_size, size_t thread_count)
|
||||||
{
|
{
|
||||||
auto tp = std::make_shared<details::thread_pool>(q_size, thread_count);
|
init_thread_pool(q_size, thread_count, [] {});
|
||||||
details::registry::instance().set_tp(std::move(tp));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the global thread pool.
|
// get the global thread pool.
|
||||||
|
|||||||
92
include/spdlog/async_logger-inl.h
Normal file
92
include/spdlog/async_logger-inl.h
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include "spdlog/async_logger.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "spdlog/sinks/sink.h"
|
||||||
|
#include "spdlog/details/thread_pool.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
SPDLOG_INLINE spdlog::async_logger::async_logger(
|
||||||
|
std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
||||||
|
: async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), std::move(tp), overflow_policy)
|
||||||
|
{}
|
||||||
|
|
||||||
|
SPDLOG_INLINE spdlog::async_logger::async_logger(
|
||||||
|
std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
||||||
|
: async_logger(std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// send the log message to the thread pool
|
||||||
|
SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
if (auto pool_ptr = thread_pool_.lock())
|
||||||
|
{
|
||||||
|
pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SPDLOG_THROW(spdlog_ex("async log: thread pool doesn't exist anymore"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// send flush request to the thread pool
|
||||||
|
SPDLOG_INLINE void spdlog::async_logger::flush_()
|
||||||
|
{
|
||||||
|
if (auto pool_ptr = thread_pool_.lock())
|
||||||
|
{
|
||||||
|
pool_ptr->post_flush(shared_from_this(), overflow_policy_);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SPDLOG_THROW(spdlog_ex("async flush: thread pool doesn't exist anymore"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// backend functions - called from the thread pool to do the actual job
|
||||||
|
//
|
||||||
|
SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
for (auto &sink : sinks_)
|
||||||
|
{
|
||||||
|
if (sink->should_log(msg.level))
|
||||||
|
{
|
||||||
|
SPDLOG_TRY
|
||||||
|
{
|
||||||
|
sink->log(msg);
|
||||||
|
}
|
||||||
|
SPDLOG_LOGGER_CATCH()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_flush_(msg))
|
||||||
|
{
|
||||||
|
backend_flush_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void spdlog::async_logger::backend_flush_()
|
||||||
|
{
|
||||||
|
for (auto &sink : sinks_)
|
||||||
|
{
|
||||||
|
SPDLOG_TRY
|
||||||
|
{
|
||||||
|
sink->flush();
|
||||||
|
}
|
||||||
|
SPDLOG_LOGGER_CATCH()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::shared_ptr<spdlog::logger> spdlog::async_logger::clone(std::string new_name)
|
||||||
|
{
|
||||||
|
auto cloned = std::make_shared<spdlog::async_logger>(*this);
|
||||||
|
cloned->name_ = std::move(new_name);
|
||||||
|
return cloned;
|
||||||
|
}
|
||||||
@@ -1,31 +1,21 @@
|
|||||||
//
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Very fast asynchronous logger (millions of logs per second on an average
|
// Fast asynchronous logger.
|
||||||
// desktop)
|
// Uses pre allocated queue.
|
||||||
// Uses pre allocated lockfree queue for maximum throughput even under large
|
|
||||||
// number of threads.
|
|
||||||
// Creates a single back thread to pop messages from the queue and log them.
|
// Creates a single back thread to pop messages from the queue and log them.
|
||||||
//
|
//
|
||||||
// Upon each log write the logger:
|
// Upon each log write the logger:
|
||||||
// 1. Checks if its log level is enough to log the message
|
// 1. Checks if its log level is enough to log the message
|
||||||
// 2. Push a new copy of the message to a queue (or block the caller until
|
// 2. Push a new copy of the message to a queue (or block the caller until
|
||||||
// space is available in the queue)
|
// space is available in the queue)
|
||||||
// 3. will throw spdlog_ex upon log exceptions
|
|
||||||
// Upon destruction, logs all remaining messages in the queue before
|
// Upon destruction, logs all remaining messages in the queue before
|
||||||
// destructing..
|
// destructing..
|
||||||
|
|
||||||
#include "spdlog/common.h"
|
|
||||||
#include "spdlog/logger.h"
|
#include "spdlog/logger.h"
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
|
|
||||||
// Async overflow policy - block by default.
|
// Async overflow policy - block by default.
|
||||||
@@ -40,26 +30,31 @@ namespace details {
|
|||||||
class thread_pool;
|
class thread_pool;
|
||||||
}
|
}
|
||||||
|
|
||||||
class async_logger SPDLOG_FINAL : public std::enable_shared_from_this<async_logger>, public logger
|
class async_logger final : public std::enable_shared_from_this<async_logger>, public logger
|
||||||
{
|
{
|
||||||
friend class details::thread_pool;
|
friend class details::thread_pool;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template<typename It>
|
template<typename It>
|
||||||
async_logger(std::string logger_name, const It &begin, const It &end, std::weak_ptr<details::thread_pool> tp,
|
async_logger(std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp,
|
||||||
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
async_overflow_policy overflow_policy = async_overflow_policy::block)
|
||||||
|
: logger(std::move(logger_name), begin, end)
|
||||||
|
, thread_pool_(std::move(tp))
|
||||||
|
, overflow_policy_(overflow_policy)
|
||||||
|
{}
|
||||||
|
|
||||||
async_logger(std::string logger_name, sinks_init_list sinks, std::weak_ptr<details::thread_pool> tp,
|
async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp,
|
||||||
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
||||||
|
|
||||||
async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp,
|
async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp,
|
||||||
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
||||||
|
|
||||||
protected:
|
std::shared_ptr<logger> clone(std::string new_name) override;
|
||||||
void sink_it_(details::log_msg &msg) override;
|
|
||||||
void flush_() override;
|
|
||||||
|
|
||||||
void backend_log_(details::log_msg &incoming_log_msg);
|
protected:
|
||||||
|
void sink_it_(const details::log_msg &msg) override;
|
||||||
|
void flush_() override;
|
||||||
|
void backend_sink_it_(const details::log_msg &incoming_log_msg);
|
||||||
void backend_flush_();
|
void backend_flush_();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -68,4 +63,6 @@ private:
|
|||||||
};
|
};
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
#include "details/async_logger_impl.h"
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "async_logger-inl.h"
|
||||||
|
#endif
|
||||||
|
|||||||
57
include/spdlog/common-inl.h
Normal file
57
include/spdlog/common-inl.h
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include "spdlog/common.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace level {
|
||||||
|
static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
|
||||||
|
|
||||||
|
static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES;
|
||||||
|
|
||||||
|
SPDLOG_INLINE string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
return level_string_views[l];
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
return short_level_names[l];
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
int level = 0;
|
||||||
|
for (const auto &level_str : level_string_views)
|
||||||
|
{
|
||||||
|
if (level_str == name)
|
||||||
|
{
|
||||||
|
return static_cast<level::level_enum>(level);
|
||||||
|
}
|
||||||
|
level++;
|
||||||
|
}
|
||||||
|
return level::off;
|
||||||
|
}
|
||||||
|
} // namespace level
|
||||||
|
|
||||||
|
SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg)
|
||||||
|
: msg_(std::move(msg))
|
||||||
|
{}
|
||||||
|
|
||||||
|
SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno)
|
||||||
|
{
|
||||||
|
memory_buf_t outbuf;
|
||||||
|
fmt::format_system_error(outbuf, last_errno, msg);
|
||||||
|
msg_ = fmt::to_string(outbuf);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
return msg_.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spdlog
|
||||||
@@ -1,44 +1,51 @@
|
|||||||
//
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "spdlog/tweakme.h"
|
#include "spdlog/tweakme.h"
|
||||||
|
#include "spdlog/details/null_mutex.h"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <functional>
|
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <exception>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <type_traits>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
|
#ifdef _WIN32
|
||||||
#include <codecvt>
|
#ifndef NOMINMAX
|
||||||
#include <locale>
|
#define NOMINMAX // prevent windows redefining min/max
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#endif //_WIN32
|
||||||
|
|
||||||
|
#ifdef SPDLOG_COMPILED_LIB
|
||||||
|
#undef SPDLOG_HEADER_ONLY
|
||||||
|
#define SPDLOG_INLINE
|
||||||
|
#else
|
||||||
|
#define SPDLOG_HEADER_ONLY
|
||||||
|
#define SPDLOG_INLINE inline
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "spdlog/fmt/fmt.h"
|
||||||
|
|
||||||
// visual studio upto 2013 does not support noexcept nor constexpr
|
// visual studio upto 2013 does not support noexcept nor constexpr
|
||||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||||
#define SPDLOG_NOEXCEPT throw()
|
#define SPDLOG_NOEXCEPT _NOEXCEPT
|
||||||
#define SPDLOG_CONSTEXPR
|
#define SPDLOG_CONSTEXPR
|
||||||
#else
|
#else
|
||||||
#define SPDLOG_NOEXCEPT noexcept
|
#define SPDLOG_NOEXCEPT noexcept
|
||||||
#define SPDLOG_CONSTEXPR constexpr
|
#define SPDLOG_CONSTEXPR constexpr
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// final keyword support. On by default. See tweakme.h
|
|
||||||
#if defined(SPDLOG_NO_FINAL)
|
|
||||||
#define SPDLOG_FINAL
|
|
||||||
#else
|
|
||||||
#define SPDLOG_FINAL final
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
#define SPDLOG_DEPRECATED __attribute__((deprecated))
|
#define SPDLOG_DEPRECATED __attribute__((deprecated))
|
||||||
#elif defined(_MSC_VER)
|
#elif defined(_MSC_VER)
|
||||||
@@ -47,7 +54,31 @@
|
|||||||
#define SPDLOG_DEPRECATED
|
#define SPDLOG_DEPRECATED
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "spdlog/fmt/fmt.h"
|
// disable thread local on msvc 2013
|
||||||
|
#ifndef SPDLOG_NO_TLS
|
||||||
|
#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
|
||||||
|
#define SPDLOG_NO_TLS 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SPDLOG_FUNCTION
|
||||||
|
#define SPDLOG_FUNCTION __FUNCTION__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SPDLOG_NO_EXCEPTIONS
|
||||||
|
#define SPDLOG_TRY
|
||||||
|
#define SPDLOG_THROW(ex) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
printf("spdlog fatal error: %s\n", ex.what()); \
|
||||||
|
std::abort(); \
|
||||||
|
} while (0)
|
||||||
|
#define SPDLOG_CATCH_ALL()
|
||||||
|
#else
|
||||||
|
#define SPDLOG_TRY try
|
||||||
|
#define SPDLOG_THROW(ex) throw(ex)
|
||||||
|
#define SPDLOG_CATCH_ALL() catch (...)
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
|
|
||||||
@@ -57,10 +88,36 @@ namespace sinks {
|
|||||||
class sink;
|
class sink;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
|
using filename_t = std::wstring;
|
||||||
|
#define SPDLOG_FILENAME_T(s) L##s
|
||||||
|
#else
|
||||||
|
using filename_t = std::string;
|
||||||
|
#define SPDLOG_FILENAME_T(s) s
|
||||||
|
#endif
|
||||||
|
|
||||||
using log_clock = std::chrono::system_clock;
|
using log_clock = std::chrono::system_clock;
|
||||||
using sink_ptr = std::shared_ptr<sinks::sink>;
|
using sink_ptr = std::shared_ptr<sinks::sink>;
|
||||||
using sinks_init_list = std::initializer_list<sink_ptr>;
|
using sinks_init_list = std::initializer_list<sink_ptr>;
|
||||||
using log_err_handler = std::function<void(const std::string &err_msg)>;
|
using err_handler = std::function<void(const std::string &err_msg)>;
|
||||||
|
using string_view_t = fmt::basic_string_view<char>;
|
||||||
|
using memory_buf_t = fmt::basic_memory_buffer<char, 250>;
|
||||||
|
|
||||||
|
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
|
#ifndef _WIN32
|
||||||
|
#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
|
||||||
|
#else
|
||||||
|
using wstring_view_t = basic_string_view_t<wchar_t>;
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct is_convertible_to_wstring_view : std::is_convertible<T, wstring_view_t>
|
||||||
|
{};
|
||||||
|
#endif // _WIN32
|
||||||
|
#else
|
||||||
|
template<typename>
|
||||||
|
struct is_convertible_to_wstring_view : std::false_type
|
||||||
|
{};
|
||||||
|
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
|
|
||||||
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
|
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
|
||||||
using level_t = details::null_atomic_int;
|
using level_t = details::null_atomic_int;
|
||||||
@@ -68,17 +125,29 @@ using level_t = details::null_atomic_int;
|
|||||||
using level_t = std::atomic<int>;
|
using level_t = std::atomic<int>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define SPDLOG_LEVEL_TRACE 0
|
||||||
|
#define SPDLOG_LEVEL_DEBUG 1
|
||||||
|
#define SPDLOG_LEVEL_INFO 2
|
||||||
|
#define SPDLOG_LEVEL_WARN 3
|
||||||
|
#define SPDLOG_LEVEL_ERROR 4
|
||||||
|
#define SPDLOG_LEVEL_CRITICAL 5
|
||||||
|
#define SPDLOG_LEVEL_OFF 6
|
||||||
|
|
||||||
|
#if !defined(SPDLOG_ACTIVE_LEVEL)
|
||||||
|
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
|
||||||
|
#endif
|
||||||
|
|
||||||
// Log level enum
|
// Log level enum
|
||||||
namespace level {
|
namespace level {
|
||||||
enum level_enum
|
enum level_enum
|
||||||
{
|
{
|
||||||
trace = 0,
|
trace = SPDLOG_LEVEL_TRACE,
|
||||||
debug = 1,
|
debug = SPDLOG_LEVEL_DEBUG,
|
||||||
info = 2,
|
info = SPDLOG_LEVEL_INFO,
|
||||||
warn = 3,
|
warn = SPDLOG_LEVEL_WARN,
|
||||||
err = 4,
|
err = SPDLOG_LEVEL_ERROR,
|
||||||
critical = 5,
|
critical = SPDLOG_LEVEL_CRITICAL,
|
||||||
off = 6
|
off = SPDLOG_LEVEL_OFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
#if !defined(SPDLOG_LEVEL_NAMES)
|
#if !defined(SPDLOG_LEVEL_NAMES)
|
||||||
@@ -87,37 +156,32 @@ enum level_enum
|
|||||||
"trace", "debug", "info", "warning", "error", "critical", "off" \
|
"trace", "debug", "info", "warning", "error", "critical", "off" \
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
static const char *level_names[] SPDLOG_LEVEL_NAMES;
|
|
||||||
|
|
||||||
static const char *short_level_names[]{"T", "D", "I", "W", "E", "C", "O"};
|
#if !defined(SPDLOG_SHORT_LEVEL_NAMES)
|
||||||
|
|
||||||
inline const char *to_c_str(spdlog::level::level_enum l)
|
#define SPDLOG_SHORT_LEVEL_NAMES \
|
||||||
{
|
{ \
|
||||||
return level_names[l];
|
"T", "D", "I", "W", "E", "C", "O" \
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
inline const char *to_short_c_str(spdlog::level::level_enum l)
|
string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
||||||
{
|
const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
||||||
return short_level_names[l];
|
spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
|
||||||
}
|
|
||||||
inline spdlog::level::level_enum from_str(const std::string &name)
|
|
||||||
{
|
|
||||||
static std::unordered_map<std::string, level_enum> name_to_level = // map string->level
|
|
||||||
{{level_names[0], level::trace}, // trace
|
|
||||||
{level_names[1], level::debug}, // debug
|
|
||||||
{level_names[2], level::info}, // info
|
|
||||||
{level_names[3], level::warn}, // warn
|
|
||||||
{level_names[4], level::err}, // err
|
|
||||||
{level_names[5], level::critical}, // critical
|
|
||||||
{level_names[6], level::off}}; // off
|
|
||||||
|
|
||||||
auto lvl_it = name_to_level.find(name);
|
|
||||||
return lvl_it != name_to_level.end() ? lvl_it->second : level::off;
|
|
||||||
}
|
|
||||||
|
|
||||||
using level_hasher = std::hash<int>;
|
using level_hasher = std::hash<int>;
|
||||||
} // namespace level
|
} // namespace level
|
||||||
|
|
||||||
|
//
|
||||||
|
// Color mode used by sinks with color support.
|
||||||
|
//
|
||||||
|
enum class color_mode
|
||||||
|
{
|
||||||
|
always,
|
||||||
|
automatic,
|
||||||
|
never
|
||||||
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
// Pattern time - specific time getting to use for pattern_formatter.
|
// Pattern time - specific time getting to use for pattern_formatter.
|
||||||
// local time by default
|
// local time by default
|
||||||
@@ -131,53 +195,52 @@ enum class pattern_time_type
|
|||||||
//
|
//
|
||||||
// Log exception
|
// Log exception
|
||||||
//
|
//
|
||||||
class spdlog_ex : public std::runtime_error
|
class spdlog_ex : public std::exception
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit spdlog_ex(const std::string &msg)
|
explicit spdlog_ex(std::string msg);
|
||||||
: runtime_error(msg)
|
spdlog_ex(const std::string &msg, int last_errno);
|
||||||
{
|
const char *what() const SPDLOG_NOEXCEPT override;
|
||||||
}
|
|
||||||
spdlog_ex(std::string msg, int last_errno)
|
|
||||||
: runtime_error(std::move(msg))
|
|
||||||
, last_errno_(last_errno)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
const char *what() const SPDLOG_NOEXCEPT override
|
|
||||||
{
|
|
||||||
if (last_errno_)
|
|
||||||
{
|
|
||||||
fmt::memory_buffer buf;
|
|
||||||
std::string msg(runtime_error::what());
|
|
||||||
fmt::format_system_error(buf, last_errno_, msg);
|
|
||||||
return fmt::to_string(buf).c_str();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return runtime_error::what();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int last_errno_{0};
|
std::string msg_;
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
struct source_loc
|
||||||
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
|
{
|
||||||
//
|
SPDLOG_CONSTEXPR source_loc() = default;
|
||||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
SPDLOG_CONSTEXPR source_loc(const char *filename_in, int line_in, const char *funcname_in)
|
||||||
using filename_t = std::wstring;
|
: filename{filename_in}
|
||||||
#else
|
, line{line_in}
|
||||||
using filename_t = std::string;
|
, funcname{funcname_in}
|
||||||
#endif
|
{}
|
||||||
|
|
||||||
#define SPDLOG_CATCH_AND_HANDLE \
|
SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT
|
||||||
catch (const std::exception &ex) \
|
{
|
||||||
{ \
|
return line == 0;
|
||||||
err_handler_(ex.what()); \
|
|
||||||
} \
|
|
||||||
catch (...) \
|
|
||||||
{ \
|
|
||||||
err_handler_("Unknown exeption in logger"); \
|
|
||||||
}
|
}
|
||||||
|
const char *filename{nullptr};
|
||||||
|
int line{0};
|
||||||
|
const char *funcname{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
// make_unique support for pre c++14
|
||||||
|
|
||||||
|
#if __cplusplus >= 201402L // C++14 and beyond
|
||||||
|
using std::make_unique;
|
||||||
|
#else
|
||||||
|
template<typename T, typename... Args>
|
||||||
|
std::unique_ptr<T> make_unique(Args &&... args)
|
||||||
|
{
|
||||||
|
static_assert(!std::is_array<T>::value, "arrays not supported");
|
||||||
|
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "common-inl.h"
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,100 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// async logger implementation
|
|
||||||
// uses a thread pool to perform the actual logging
|
|
||||||
|
|
||||||
#include "spdlog/details/thread_pool.h"
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
template<typename It>
|
|
||||||
inline spdlog::async_logger::async_logger(
|
|
||||||
std::string logger_name, const It &begin, const It &end, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
|
||||||
: logger(std::move(logger_name), begin, end)
|
|
||||||
, thread_pool_(tp)
|
|
||||||
, overflow_policy_(overflow_policy)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
inline spdlog::async_logger::async_logger(
|
|
||||||
std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
|
||||||
: async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), tp, overflow_policy)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
inline spdlog::async_logger::async_logger(
|
|
||||||
std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
|
||||||
: async_logger(std::move(logger_name), {single_sink}, tp, overflow_policy)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// send the log message to the thread pool
|
|
||||||
inline void spdlog::async_logger::sink_it_(details::log_msg &msg)
|
|
||||||
{
|
|
||||||
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
|
|
||||||
incr_msg_counter_(msg);
|
|
||||||
#endif
|
|
||||||
if (auto pool_ptr = thread_pool_.lock())
|
|
||||||
{
|
|
||||||
pool_ptr->post_log(shared_from_this(), std::move(msg), overflow_policy_);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw spdlog_ex("async log: thread pool doens't exist anymore");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// send flush request to the thread pool
|
|
||||||
inline void spdlog::async_logger::flush_()
|
|
||||||
{
|
|
||||||
if (auto pool_ptr = thread_pool_.lock())
|
|
||||||
{
|
|
||||||
pool_ptr->post_flush(shared_from_this(), overflow_policy_);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw spdlog_ex("async flush: thread pool doesn't exist anymore");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// backend functions - called from the thread pool to do the actual job
|
|
||||||
//
|
|
||||||
inline void spdlog::async_logger::backend_log_(details::log_msg &incoming_log_msg)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for (auto &s : sinks_)
|
|
||||||
{
|
|
||||||
if (s->should_log(incoming_log_msg.level))
|
|
||||||
{
|
|
||||||
s->log(incoming_log_msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SPDLOG_CATCH_AND_HANDLE
|
|
||||||
|
|
||||||
if (should_flush_(incoming_log_msg))
|
|
||||||
{
|
|
||||||
backend_flush_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::async_logger::backend_flush_()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
sink->flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SPDLOG_CATCH_AND_HANDLE
|
|
||||||
}
|
|
||||||
74
include/spdlog/details/backtracer-inl.h
Normal file
74
include/spdlog/details/backtracer-inl.h
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include "spdlog/details/backtracer.h"
|
||||||
|
#endif
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
SPDLOG_INLINE backtracer::backtracer(const backtracer &other)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(other.mutex_);
|
||||||
|
enabled_ = other.enabled();
|
||||||
|
messages_ = other.messages_;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(other.mutex_);
|
||||||
|
enabled_ = other.enabled();
|
||||||
|
messages_ = std::move(other.messages_);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
enabled_ = other.enabled();
|
||||||
|
messages_ = other.messages_;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void backtracer::enable(size_t size)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
enabled_.store(true, std::memory_order_relaxed);
|
||||||
|
messages_ = circular_q<log_msg_buffer>{size};
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void backtracer::disable()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
enabled_.store(false, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE bool backtracer::enabled() const
|
||||||
|
{
|
||||||
|
return enabled_.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE backtracer::operator bool() const
|
||||||
|
{
|
||||||
|
return enabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void backtracer::push_back(const log_msg &msg)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
messages_.push_back(log_msg_buffer{msg});
|
||||||
|
}
|
||||||
|
|
||||||
|
// pop all items in the q and apply the given fun on each of them.
|
||||||
|
SPDLOG_INLINE void backtracer::foreach_pop(std::function<void(const details::log_msg &)> fun)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock{mutex_};
|
||||||
|
while (!messages_.empty())
|
||||||
|
{
|
||||||
|
auto &front_msg = messages_.front();
|
||||||
|
fun(front_msg);
|
||||||
|
messages_.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
46
include/spdlog/details/backtracer.h
Normal file
46
include/spdlog/details/backtracer.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "spdlog/details/log_msg_buffer.h"
|
||||||
|
#include "spdlog/details/circular_q.h"
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <mutex>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
// Store log messages in circular buffer.
|
||||||
|
// Useful for storing debug data in case of error/warning happens.
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
class backtracer
|
||||||
|
{
|
||||||
|
mutable std::mutex mutex_;
|
||||||
|
std::atomic<bool> enabled_{false};
|
||||||
|
circular_q<log_msg_buffer> messages_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
backtracer() = default;
|
||||||
|
backtracer(const backtracer &other);
|
||||||
|
|
||||||
|
backtracer(backtracer &&other) SPDLOG_NOEXCEPT;
|
||||||
|
backtracer &operator=(backtracer other);
|
||||||
|
|
||||||
|
void enable(size_t size);
|
||||||
|
void disable();
|
||||||
|
bool enabled() const;
|
||||||
|
explicit operator bool() const;
|
||||||
|
void push_back(const log_msg &msg);
|
||||||
|
|
||||||
|
// pop all items in the q and apply the given fun on each of them.
|
||||||
|
void foreach_pop(std::function<void(const details::log_msg &)> fun);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "backtracer-inl.h"
|
||||||
|
#endif
|
||||||
@@ -1,62 +1,119 @@
|
|||||||
//
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
// Copyright(c) 2018 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
// cirucal q view of std::vector.
|
// cirucal q view of std::vector.
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace details {
|
namespace details {
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class circular_q
|
class circular_q
|
||||||
{
|
{
|
||||||
|
size_t max_items_ = 0;
|
||||||
|
typename std::vector<T>::size_type head_ = 0;
|
||||||
|
typename std::vector<T>::size_type tail_ = 0;
|
||||||
|
size_t overrun_counter_ = 0;
|
||||||
|
std::vector<T> v_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using item_type = T;
|
using value_type = T;
|
||||||
|
|
||||||
|
// empty ctor - create a disabled queue with no elements allocated at all
|
||||||
|
circular_q() = default;
|
||||||
|
|
||||||
explicit circular_q(size_t max_items)
|
explicit circular_q(size_t max_items)
|
||||||
: max_items_(max_items + 1) // one item is reserved as marker for full q
|
: max_items_(max_items + 1) // one item is reserved as marker for full q
|
||||||
, v_(max_items_)
|
, v_(max_items_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
circular_q(const circular_q &) = default;
|
||||||
|
circular_q &operator=(const circular_q &) = default;
|
||||||
|
|
||||||
|
// move cannot be default,
|
||||||
|
// since we need to reset head_, tail_, etc to zero in the moved object
|
||||||
|
circular_q(circular_q &&other) SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
|
copy_moveable(std::move(other));
|
||||||
|
}
|
||||||
|
|
||||||
|
circular_q &operator=(circular_q &&other) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
copy_moveable(std::move(other));
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// push back, overrun (oldest) item if no room left
|
// push back, overrun (oldest) item if no room left
|
||||||
void push_back(T &&item)
|
void push_back(T &&item)
|
||||||
{
|
{
|
||||||
v_[tail_] = std::move(item);
|
if (max_items_ > 0)
|
||||||
tail_ = (tail_ + 1) % max_items_;
|
|
||||||
|
|
||||||
if (tail_ == head_) // overrun last item if full
|
|
||||||
{
|
{
|
||||||
head_ = (head_ + 1) % max_items_;
|
v_[tail_] = std::move(item);
|
||||||
|
tail_ = (tail_ + 1) % max_items_;
|
||||||
|
|
||||||
|
if (tail_ == head_) // overrun last item if full
|
||||||
|
{
|
||||||
|
head_ = (head_ + 1) % max_items_;
|
||||||
|
++overrun_counter_;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return reference to the front item.
|
||||||
|
// If there are no elements in the container, the behavior is undefined.
|
||||||
|
const T& front() const
|
||||||
|
{
|
||||||
|
return v_[head_];
|
||||||
|
}
|
||||||
|
|
||||||
|
T& front()
|
||||||
|
{
|
||||||
|
return v_[head_];
|
||||||
|
}
|
||||||
|
|
||||||
// Pop item from front.
|
// Pop item from front.
|
||||||
// If there are no elements in the container, the behavior is undefined.
|
// If there are no elements in the container, the behavior is undefined.
|
||||||
void pop_front(T &popped_item)
|
void pop_front()
|
||||||
{
|
{
|
||||||
popped_item = std::move(v_[head_]);
|
|
||||||
head_ = (head_ + 1) % max_items_;
|
head_ = (head_ + 1) % max_items_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool empty()
|
bool empty() const
|
||||||
{
|
{
|
||||||
return tail_ == head_;
|
return tail_ == head_;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool full()
|
bool full() const
|
||||||
{
|
{
|
||||||
// head is ahead of the tail by 1
|
// head is ahead of the tail by 1
|
||||||
return ((tail_ + 1) % max_items_) == head_;
|
if(max_items_ > 0)
|
||||||
|
{
|
||||||
|
return ((tail_ + 1) % max_items_) == head_;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t overrun_counter() const
|
||||||
|
{
|
||||||
|
return overrun_counter_;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t max_items_;
|
// copy from other&& and reset it to disabled state
|
||||||
typename std::vector<T>::size_type head_ = 0;
|
void copy_moveable(circular_q &&other) SPDLOG_NOEXCEPT
|
||||||
typename std::vector<T>::size_type tail_ = 0;
|
{
|
||||||
|
max_items_ = other.max_items_;
|
||||||
|
head_ = other.head_;
|
||||||
|
tail_ = other.tail_;
|
||||||
|
overrun_counter_ = other.overrun_counter_;
|
||||||
|
v_ = std::move(other.v_);
|
||||||
|
|
||||||
std::vector<T> v_;
|
// put &&other in disabled, but valid state
|
||||||
|
other.max_items_ = 0;
|
||||||
|
other.head_ = other.tail_ = 0;
|
||||||
|
other.overrun_counter_ = 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // namespace details
|
} // namespace details
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|||||||
@@ -1,42 +1,13 @@
|
|||||||
#pragma once
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
//
|
|
||||||
// Copyright(c) 2018 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
#include "spdlog/details/null_mutex.h"
|
||||||
#include "stdio.h"
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace details {
|
namespace details {
|
||||||
struct console_stdout
|
|
||||||
{
|
|
||||||
static FILE *stream()
|
|
||||||
{
|
|
||||||
return stdout;
|
|
||||||
}
|
|
||||||
#ifdef _WIN32
|
|
||||||
static HANDLE handle()
|
|
||||||
{
|
|
||||||
return ::GetStdHandle(STD_OUTPUT_HANDLE);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
struct console_stderr
|
|
||||||
{
|
|
||||||
static FILE *stream()
|
|
||||||
{
|
|
||||||
return stderr;
|
|
||||||
}
|
|
||||||
#ifdef _WIN32
|
|
||||||
static HANDLE handle()
|
|
||||||
{
|
|
||||||
return ::GetStdHandle(STD_ERROR_HANDLE);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
struct console_mutex
|
struct console_mutex
|
||||||
{
|
{
|
||||||
|
|||||||
133
include/spdlog/details/file_helper-inl.h
Normal file
133
include/spdlog/details/file_helper-inl.h
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include "spdlog/details/file_helper.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "spdlog/details/os.h"
|
||||||
|
#include "spdlog/common.h"
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
SPDLOG_INLINE file_helper::~file_helper()
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
|
||||||
|
_filename = fname;
|
||||||
|
for (int tries = 0; tries < open_tries; ++tries)
|
||||||
|
{
|
||||||
|
if (!os::fopen_s(&fd_, fname, mode))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
details::os::sleep_for_millis(open_interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_THROW(spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void file_helper::reopen(bool truncate)
|
||||||
|
{
|
||||||
|
if (_filename.empty())
|
||||||
|
{
|
||||||
|
SPDLOG_THROW(spdlog_ex("Failed re opening file - was not opened before"));
|
||||||
|
}
|
||||||
|
open(_filename, truncate);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void file_helper::flush()
|
||||||
|
{
|
||||||
|
std::fflush(fd_);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void file_helper::close()
|
||||||
|
{
|
||||||
|
if (fd_ != nullptr)
|
||||||
|
{
|
||||||
|
std::fclose(fd_);
|
||||||
|
fd_ = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf)
|
||||||
|
{
|
||||||
|
size_t msg_size = buf.size();
|
||||||
|
auto data = buf.data();
|
||||||
|
if (std::fwrite(data, 1, msg_size, fd_) != msg_size)
|
||||||
|
{
|
||||||
|
SPDLOG_THROW(spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE size_t file_helper::size() const
|
||||||
|
{
|
||||||
|
if (fd_ == nullptr)
|
||||||
|
{
|
||||||
|
SPDLOG_THROW(spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename)));
|
||||||
|
}
|
||||||
|
return os::filesize(fd_);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE const filename_t &file_helper::filename() const
|
||||||
|
{
|
||||||
|
return _filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE bool file_helper::file_exists(const filename_t &fname)
|
||||||
|
{
|
||||||
|
return os::file_exists(fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// return file path and its extension:
|
||||||
|
//
|
||||||
|
// "mylog.txt" => ("mylog", ".txt")
|
||||||
|
// "mylog" => ("mylog", "")
|
||||||
|
// "mylog." => ("mylog.", "")
|
||||||
|
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
|
||||||
|
//
|
||||||
|
// the starting dot in filenames is ignored (hidden files):
|
||||||
|
//
|
||||||
|
// ".mylog" => (".mylog". "")
|
||||||
|
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
||||||
|
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
||||||
|
SPDLOG_INLINE std::tuple<filename_t, filename_t> file_helper::split_by_extension(const filename_t &fname)
|
||||||
|
{
|
||||||
|
auto ext_index = fname.rfind('.');
|
||||||
|
|
||||||
|
// no valid extension found - return whole path and empty string as
|
||||||
|
// extension
|
||||||
|
if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
|
||||||
|
{
|
||||||
|
return std::make_tuple(fname, filename_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
// treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
|
||||||
|
auto folder_index = fname.rfind(details::os::folder_sep);
|
||||||
|
if (folder_index != filename_t::npos && folder_index >= ext_index - 1)
|
||||||
|
{
|
||||||
|
return std::make_tuple(fname, filename_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally - return a valid base and extension tuple
|
||||||
|
return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
|
||||||
|
}
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
@@ -1,114 +1,35 @@
|
|||||||
//
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Helper class for file sink
|
#include "spdlog/common.h"
|
||||||
// When failing to open a file, retry several times(5) with small delay between
|
|
||||||
// the tries(10 ms)
|
|
||||||
// Throw spdlog_ex exception on errors
|
|
||||||
|
|
||||||
#include "../details/log_msg.h"
|
|
||||||
#include "../details/os.h"
|
|
||||||
|
|
||||||
#include <cerrno>
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace details {
|
namespace details {
|
||||||
|
|
||||||
|
// Helper class for file sinks.
|
||||||
|
// When failing to open a file, retry several times(5) with a delay interval(10 ms).
|
||||||
|
// Throw spdlog_ex exception on errors.
|
||||||
|
|
||||||
class file_helper
|
class file_helper
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const int open_tries = 5;
|
|
||||||
const int open_interval = 10;
|
|
||||||
|
|
||||||
explicit file_helper() = default;
|
explicit file_helper() = default;
|
||||||
|
|
||||||
file_helper(const file_helper &) = delete;
|
file_helper(const file_helper &) = delete;
|
||||||
file_helper &operator=(const file_helper &) = delete;
|
file_helper &operator=(const file_helper &) = delete;
|
||||||
|
~file_helper();
|
||||||
|
|
||||||
~file_helper()
|
void open(const filename_t &fname, bool truncate = false);
|
||||||
{
|
void reopen(bool truncate);
|
||||||
close();
|
void flush();
|
||||||
}
|
void close();
|
||||||
|
void write(const memory_buf_t &buf);
|
||||||
void open(const filename_t &fname, bool truncate = false)
|
size_t size() const;
|
||||||
{
|
const filename_t &filename() const;
|
||||||
close();
|
static bool file_exists(const filename_t &fname);
|
||||||
auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
|
|
||||||
_filename = fname;
|
|
||||||
for (int tries = 0; tries < open_tries; ++tries)
|
|
||||||
{
|
|
||||||
if (!os::fopen_s(&fd_, fname, mode))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
details::os::sleep_for_millis(open_interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
void reopen(bool truncate)
|
|
||||||
{
|
|
||||||
if (_filename.empty())
|
|
||||||
{
|
|
||||||
throw spdlog_ex("Failed re opening file - was not opened before");
|
|
||||||
}
|
|
||||||
open(_filename, truncate);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush()
|
|
||||||
{
|
|
||||||
std::fflush(fd_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void close()
|
|
||||||
{
|
|
||||||
if (fd_ != nullptr)
|
|
||||||
{
|
|
||||||
std::fclose(fd_);
|
|
||||||
fd_ = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void write(const fmt::memory_buffer &buf)
|
|
||||||
{
|
|
||||||
size_t msg_size = buf.size();
|
|
||||||
auto data = buf.data();
|
|
||||||
if (std::fwrite(data, 1, msg_size, fd_) != msg_size)
|
|
||||||
{
|
|
||||||
throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t size() const
|
|
||||||
{
|
|
||||||
if (fd_ == nullptr)
|
|
||||||
{
|
|
||||||
throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
|
|
||||||
}
|
|
||||||
return os::filesize(fd_);
|
|
||||||
}
|
|
||||||
|
|
||||||
const filename_t &filename() const
|
|
||||||
{
|
|
||||||
return _filename;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool file_exists(const filename_t &fname)
|
|
||||||
{
|
|
||||||
return os::file_exists(fname);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// return file path and its extension:
|
// return file path and its extension:
|
||||||
@@ -123,31 +44,17 @@ public:
|
|||||||
// ".mylog" => (".mylog". "")
|
// ".mylog" => (".mylog". "")
|
||||||
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
||||||
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
||||||
static std::tuple<filename_t, filename_t> split_by_extenstion(const spdlog::filename_t &fname)
|
static std::tuple<filename_t, filename_t> split_by_extension(const filename_t &fname);
|
||||||
{
|
|
||||||
auto ext_index = fname.rfind('.');
|
|
||||||
|
|
||||||
// no valid extension found - return whole path and empty string as
|
|
||||||
// extension
|
|
||||||
if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
|
|
||||||
{
|
|
||||||
return std::make_tuple(fname, spdlog::filename_t());
|
|
||||||
}
|
|
||||||
|
|
||||||
// treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
|
|
||||||
auto folder_index = fname.rfind(details::os::folder_sep);
|
|
||||||
if (folder_index != fname.npos && folder_index >= ext_index - 1)
|
|
||||||
{
|
|
||||||
return std::make_tuple(fname, spdlog::filename_t());
|
|
||||||
}
|
|
||||||
|
|
||||||
// finally - return a valid base and extension tuple
|
|
||||||
return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
FILE *fd_{nullptr};
|
const int open_tries = 5;
|
||||||
|
const int open_interval = 10;
|
||||||
|
std::FILE *fd_{nullptr};
|
||||||
filename_t _filename;
|
filename_t _filename;
|
||||||
};
|
};
|
||||||
} // namespace details
|
} // namespace details
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "file_helper-inl.h"
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,125 +1,106 @@
|
|||||||
//
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
// Created by gabi on 6/15/18.
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "chrono"
|
#include <chrono>
|
||||||
|
#include <type_traits>
|
||||||
#include "spdlog/fmt/fmt.h"
|
#include "spdlog/fmt/fmt.h"
|
||||||
|
#include "spdlog/common.h"
|
||||||
|
|
||||||
// Some fmt helpers to efficiently format and pad ints and strings
|
// Some fmt helpers to efficiently format and pad ints and strings
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace details {
|
namespace details {
|
||||||
namespace fmt_helper {
|
namespace fmt_helper {
|
||||||
|
|
||||||
template<size_t Buffer_Size>
|
inline spdlog::string_view_t to_string_view(const memory_buf_t &buf) SPDLOG_NOEXCEPT
|
||||||
inline void append_str(const std::string &str, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
|
||||||
{
|
{
|
||||||
auto *str_ptr = str.data();
|
return spdlog::string_view_t{buf.data(), buf.size()};
|
||||||
dest.append(str_ptr, str_ptr + str.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t Buffer_Size>
|
inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest)
|
||||||
inline void append_c_str(const char *c_str, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
|
||||||
{
|
{
|
||||||
char ch;
|
auto *buf_ptr = view.data();
|
||||||
while ((ch = *c_str) != '\0')
|
if (buf_ptr != nullptr)
|
||||||
{
|
{
|
||||||
dest.push_back(ch);
|
dest.append(buf_ptr, buf_ptr + view.size());
|
||||||
++c_str;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t Buffer_Size1, size_t Buffer_Size2>
|
template<typename T>
|
||||||
inline void append_buf(const fmt::basic_memory_buffer<char, Buffer_Size1> &buf, fmt::basic_memory_buffer<char, Buffer_Size2> &dest)
|
inline void append_int(T n, memory_buf_t &dest)
|
||||||
{
|
|
||||||
auto *buf_ptr = buf.data();
|
|
||||||
dest.append(buf_ptr, buf_ptr + buf.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T, size_t Buffer_Size>
|
|
||||||
inline void append_int(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
|
||||||
{
|
{
|
||||||
fmt::format_int i(n);
|
fmt::format_int i(n);
|
||||||
dest.append(i.data(), i.data() + i.size());
|
dest.append(i.data(), i.data() + i.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t Buffer_Size>
|
template<typename T>
|
||||||
inline void pad2(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
inline unsigned count_digits(T n)
|
||||||
|
{
|
||||||
|
using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
|
||||||
|
return static_cast<unsigned>(fmt::internal::count_digits(static_cast<count_type>(n)));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void pad2(int n, memory_buf_t &dest)
|
||||||
{
|
{
|
||||||
if (n > 99)
|
if (n > 99)
|
||||||
{
|
{
|
||||||
append_int(n, dest);
|
append_int(n, dest);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (n > 9) // 10-99
|
else if (n > 9) // 10-99
|
||||||
{
|
{
|
||||||
dest.push_back('0' + static_cast<char>(n / 10));
|
dest.push_back(static_cast<char>('0' + n / 10));
|
||||||
dest.push_back('0' + static_cast<char>(n % 10));
|
dest.push_back(static_cast<char>('0' + n % 10));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (n >= 0) // 0-9
|
else if (n >= 0) // 0-9
|
||||||
{
|
{
|
||||||
dest.push_back('0');
|
dest.push_back('0');
|
||||||
dest.push_back('0' + static_cast<char>(n));
|
dest.push_back(static_cast<char>('0' + n));
|
||||||
return;
|
}
|
||||||
|
else // negatives (unlikely, but just in case, let fmt deal with it)
|
||||||
|
{
|
||||||
|
fmt::format_to(dest, "{:02}", n);
|
||||||
}
|
}
|
||||||
// negatives (unlikely, but just in case, let fmt deal with it)
|
|
||||||
fmt::format_to(dest, "{:02}", n);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t Buffer_Size>
|
template<typename T>
|
||||||
inline void pad3(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
inline void pad_uint(T n, unsigned int width, memory_buf_t &dest)
|
||||||
{
|
{
|
||||||
if (n > 999)
|
static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
|
||||||
|
auto digits = count_digits(n);
|
||||||
|
if (width > digits)
|
||||||
{
|
{
|
||||||
append_int(n, dest);
|
const char *zeroes = "0000000000000000000";
|
||||||
return;
|
dest.append(zeroes, zeroes + width - digits);
|
||||||
}
|
}
|
||||||
|
append_int(n, dest);
|
||||||
if (n > 99) // 100-999
|
|
||||||
{
|
|
||||||
append_int(n / 100, dest);
|
|
||||||
pad2(n % 100, dest);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (n > 9) // 10-99
|
|
||||||
{
|
|
||||||
dest.push_back('0');
|
|
||||||
dest.push_back('0' + static_cast<char>(n / 10));
|
|
||||||
dest.push_back('0' + static_cast<char>(n % 10));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (n >= 0)
|
|
||||||
{
|
|
||||||
dest.push_back('0');
|
|
||||||
dest.push_back('0');
|
|
||||||
dest.push_back('0' + static_cast<char>(n));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// negatives (unlikely, but just in case let fmt deal with it)
|
|
||||||
fmt::format_to(dest, "{:03}", n);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t Buffer_Size>
|
template<typename T>
|
||||||
inline void pad6(size_t n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
inline void pad3(T n, memory_buf_t &dest)
|
||||||
{
|
{
|
||||||
if (n > 99999)
|
pad_uint(n, 3, dest);
|
||||||
{
|
}
|
||||||
append_int(n, dest);
|
|
||||||
return;
|
template<typename T>
|
||||||
}
|
inline void pad6(T n, memory_buf_t &dest)
|
||||||
pad3(static_cast<int>(n / 1000), dest);
|
{
|
||||||
pad3(static_cast<int>(n % 1000), dest);
|
pad_uint(n, 6, dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void pad9(T n, memory_buf_t &dest)
|
||||||
|
{
|
||||||
|
pad_uint(n, 9, dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
// return fraction of a second of the given time_point.
|
// return fraction of a second of the given time_point.
|
||||||
// e.g.
|
// e.g.
|
||||||
// fraction<std::milliseconds>(tp) -> will return the millis part of the second
|
// fraction<std::milliseconds>(tp) -> will return the millis part of the second
|
||||||
template<typename ToDuration>
|
template<typename ToDuration>
|
||||||
inline ToDuration time_fraction(const log_clock::time_point &tp)
|
inline ToDuration time_fraction(log_clock::time_point tp)
|
||||||
{
|
{
|
||||||
using namespace std::chrono;
|
using std::chrono::duration_cast;
|
||||||
|
using std::chrono::seconds;
|
||||||
auto duration = tp.time_since_epoch();
|
auto duration = tp.time_since_epoch();
|
||||||
auto secs = duration_cast<seconds>(duration);
|
auto secs = duration_cast<seconds>(duration);
|
||||||
return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);
|
return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);
|
||||||
@@ -127,4 +108,4 @@ inline ToDuration time_fraction(const log_clock::time_point &tp)
|
|||||||
|
|
||||||
} // namespace fmt_helper
|
} // namespace fmt_helper
|
||||||
} // namespace details
|
} // namespace details
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|||||||
34
include/spdlog/details/log_msg-inl.h
Normal file
34
include/spdlog/details/log_msg-inl.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include "spdlog/details/log_msg.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "spdlog/details/os.h"
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
SPDLOG_INLINE log_msg::log_msg(spdlog::source_loc loc, string_view_t logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
|
||||||
|
: logger_name(logger_name)
|
||||||
|
, level(lvl)
|
||||||
|
#ifndef SPDLOG_NO_DATETIME
|
||||||
|
, time(os::now())
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SPDLOG_NO_THREAD_ID
|
||||||
|
, thread_id(os::thread_id())
|
||||||
|
#endif
|
||||||
|
, source(loc)
|
||||||
|
, payload(msg)
|
||||||
|
{}
|
||||||
|
|
||||||
|
SPDLOG_INLINE log_msg::log_msg(string_view_t logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
|
||||||
|
: log_msg(source_loc{}, logger_name, lvl, msg)
|
||||||
|
{}
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
@@ -1,47 +1,35 @@
|
|||||||
//
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "spdlog/common.h"
|
#include "spdlog/common.h"
|
||||||
#include "spdlog/details/os.h"
|
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace details {
|
namespace details {
|
||||||
struct log_msg
|
struct log_msg
|
||||||
{
|
{
|
||||||
log_msg() = default;
|
log_msg() = default;
|
||||||
log_msg(const std::string *loggers_name, level::level_enum lvl)
|
log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
||||||
: logger_name(loggers_name)
|
log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
||||||
, level(lvl)
|
log_msg(const log_msg &other) = default;
|
||||||
{
|
|
||||||
#ifndef SPDLOG_NO_DATETIME
|
|
||||||
time = os::now();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SPDLOG_NO_THREAD_ID
|
string_view_t logger_name;
|
||||||
thread_id = os::thread_id();
|
level::level_enum level{level::off};
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
log_msg(const log_msg &other) = delete;
|
|
||||||
log_msg(log_msg &&other) = delete;
|
|
||||||
log_msg &operator=(log_msg &&other) = delete;
|
|
||||||
|
|
||||||
const std::string *logger_name{nullptr};
|
|
||||||
level::level_enum level;
|
|
||||||
log_clock::time_point time;
|
log_clock::time_point time;
|
||||||
size_t thread_id;
|
size_t thread_id{0};
|
||||||
fmt::memory_buffer raw;
|
|
||||||
size_t msg_id{0};
|
// wrapping the formatted text with color (updated by pattern_formatter).
|
||||||
// info about wrapping the formatted text with color
|
|
||||||
mutable size_t color_range_start{0};
|
mutable size_t color_range_start{0};
|
||||||
mutable size_t color_range_end{0};
|
mutable size_t color_range_end{0};
|
||||||
|
|
||||||
|
source_loc source;
|
||||||
|
string_view_t payload;
|
||||||
};
|
};
|
||||||
} // namespace details
|
} // namespace details
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "log_msg-inl.h"
|
||||||
|
#endif
|
||||||
|
|||||||
60
include/spdlog/details/log_msg_buffer-inl.h
Normal file
60
include/spdlog/details/log_msg_buffer-inl.h
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include "spdlog/details/log_msg_buffer.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg &orig_msg)
|
||||||
|
: log_msg{orig_msg}
|
||||||
|
{
|
||||||
|
buffer.append(logger_name.begin(), logger_name.end());
|
||||||
|
buffer.append(payload.begin(), payload.end());
|
||||||
|
update_string_views();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other)
|
||||||
|
: log_msg{other}
|
||||||
|
{
|
||||||
|
buffer.append(logger_name.begin(), logger_name.end());
|
||||||
|
buffer.append(payload.begin(), payload.end());
|
||||||
|
update_string_views();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE log_msg_buffer:: log_msg_buffer(log_msg_buffer &&other)
|
||||||
|
: log_msg{std::move(other)}
|
||||||
|
, buffer{std::move(other.buffer)}
|
||||||
|
{
|
||||||
|
update_string_views();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &other)
|
||||||
|
{
|
||||||
|
log_msg::operator=(other);
|
||||||
|
buffer.clear();
|
||||||
|
buffer.append(other.buffer.data(), other.buffer.data() + other.buffer.size());
|
||||||
|
update_string_views();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other)
|
||||||
|
{
|
||||||
|
log_msg::operator=(std::move(other));
|
||||||
|
buffer = std::move(other.buffer);
|
||||||
|
update_string_views();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void log_msg_buffer::update_string_views()
|
||||||
|
{
|
||||||
|
logger_name = string_view_t{buffer.data(), logger_name.size()};
|
||||||
|
payload = string_view_t{buffer.data()+logger_name.size(), payload.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
34
include/spdlog/details/log_msg_buffer.h
Normal file
34
include/spdlog/details/log_msg_buffer.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "spdlog/details/log_msg.h"
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
// Extend log_msg with internal buffer to store its payload.
|
||||||
|
// THis is needed since log_msg holds string_views that points to stack data.
|
||||||
|
|
||||||
|
class log_msg_buffer : public log_msg
|
||||||
|
{
|
||||||
|
memory_buf_t buffer;
|
||||||
|
void update_string_views();
|
||||||
|
public:
|
||||||
|
log_msg_buffer() = default;
|
||||||
|
explicit log_msg_buffer(const log_msg &orig_msg);
|
||||||
|
log_msg_buffer(const log_msg_buffer &other);
|
||||||
|
log_msg_buffer(log_msg_buffer &&other);
|
||||||
|
log_msg_buffer &operator=(const log_msg_buffer &other);
|
||||||
|
log_msg_buffer &operator=(log_msg_buffer &&other);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "log_msg_buffer-inl.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
@@ -1,345 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// create logger with given name, sinks and the default pattern formatter
|
|
||||||
// all other ctors will call this one
|
|
||||||
template<typename It>
|
|
||||||
inline spdlog::logger::logger(std::string logger_name, const It &begin, const It &end)
|
|
||||||
: name_(std::move(logger_name))
|
|
||||||
, sinks_(begin, end)
|
|
||||||
, level_(level::info)
|
|
||||||
, flush_level_(level::off)
|
|
||||||
, last_err_time_(0)
|
|
||||||
, msg_counter_(1) // message counter will start from 1. 0-message id will be
|
|
||||||
// reserved for controll messages
|
|
||||||
{
|
|
||||||
err_handler_ = [this](const std::string &msg) { this->default_err_handler_(msg); };
|
|
||||||
}
|
|
||||||
|
|
||||||
// ctor with sinks as init list
|
|
||||||
inline spdlog::logger::logger(std::string logger_name, sinks_init_list sinks_list)
|
|
||||||
: logger(std::move(logger_name), sinks_list.begin(), sinks_list.end())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// ctor with single sink
|
|
||||||
inline spdlog::logger::logger(std::string logger_name, spdlog::sink_ptr single_sink)
|
|
||||||
: logger(std::move(logger_name), {std::move(single_sink)})
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
inline spdlog::logger::~logger() = default;
|
|
||||||
|
|
||||||
inline void spdlog::logger::set_formatter(std::unique_ptr<spdlog::formatter> f)
|
|
||||||
{
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
sink->set_formatter(f->clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::logger::set_pattern(std::string pattern, pattern_time_type time_type)
|
|
||||||
{
|
|
||||||
set_formatter(std::unique_ptr<spdlog::formatter>(new pattern_formatter(std::move(pattern), time_type)));
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
if (!should_log(lvl))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
details::log_msg log_msg(&name_, lvl);
|
|
||||||
fmt::format_to(log_msg.raw, fmt, args...);
|
|
||||||
sink_it_(log_msg);
|
|
||||||
}
|
|
||||||
SPDLOG_CATCH_AND_HANDLE
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::log(level::level_enum lvl, const char *msg)
|
|
||||||
{
|
|
||||||
if (!should_log(lvl))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
details::log_msg log_msg(&name_, lvl);
|
|
||||||
fmt::format_to(log_msg.raw, "{}", msg);
|
|
||||||
sink_it_(log_msg);
|
|
||||||
}
|
|
||||||
SPDLOG_CATCH_AND_HANDLE
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
|
|
||||||
{
|
|
||||||
if (!should_log(lvl))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
details::log_msg log_msg(&name_, lvl);
|
|
||||||
fmt::format_to(log_msg.raw, "{}", msg);
|
|
||||||
sink_it_(log_msg);
|
|
||||||
}
|
|
||||||
SPDLOG_CATCH_AND_HANDLE
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::trace(const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::trace, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::debug(const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::debug, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::info(const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::info, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::warn(const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::warn, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::error(const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::err, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::critical(const char *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::critical, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void spdlog::logger::trace(const T &msg)
|
|
||||||
{
|
|
||||||
log(level::trace, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void spdlog::logger::debug(const T &msg)
|
|
||||||
{
|
|
||||||
log(level::debug, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void spdlog::logger::info(const T &msg)
|
|
||||||
{
|
|
||||||
log(level::info, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void spdlog::logger::warn(const T &msg)
|
|
||||||
{
|
|
||||||
log(level::warn, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void spdlog::logger::error(const T &msg)
|
|
||||||
{
|
|
||||||
log(level::err, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline void spdlog::logger::critical(const T &msg)
|
|
||||||
{
|
|
||||||
log(level::critical, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
if (!should_log(lvl))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
decltype(wstring_converter_)::byte_string utf8_string;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(wstring_converter_mutex_);
|
|
||||||
utf8_string = wstring_converter_.to_bytes(fmt);
|
|
||||||
}
|
|
||||||
log(lvl, utf8_string.c_str(), args...);
|
|
||||||
}
|
|
||||||
SPDLOG_CATCH_AND_HANDLE
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::trace(const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::trace, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::debug(const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::debug, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::info(const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::info, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::warn(const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::warn, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::error(const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::err, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void spdlog::logger::critical(const wchar_t *fmt, const Args &... args)
|
|
||||||
{
|
|
||||||
log(level::critical, fmt, args...);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
|
||||||
|
|
||||||
//
|
|
||||||
// name and level
|
|
||||||
//
|
|
||||||
inline const std::string &spdlog::logger::name() const
|
|
||||||
{
|
|
||||||
return name_;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::logger::set_level(spdlog::level::level_enum log_level)
|
|
||||||
{
|
|
||||||
level_.store(log_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler)
|
|
||||||
{
|
|
||||||
err_handler_ = std::move(err_handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline spdlog::log_err_handler spdlog::logger::error_handler()
|
|
||||||
{
|
|
||||||
return err_handler_;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::logger::flush()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
flush_();
|
|
||||||
}
|
|
||||||
SPDLOG_CATCH_AND_HANDLE
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::logger::flush_on(level::level_enum log_level)
|
|
||||||
{
|
|
||||||
flush_level_.store(log_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool spdlog::logger::should_flush_(const details::log_msg &msg)
|
|
||||||
{
|
|
||||||
auto flush_level = flush_level_.load(std::memory_order_relaxed);
|
|
||||||
return (msg.level >= flush_level) && (msg.level != level::off);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline spdlog::level::level_enum spdlog::logger::level() const
|
|
||||||
{
|
|
||||||
return static_cast<spdlog::level::level_enum>(level_.load(std::memory_order_relaxed));
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const
|
|
||||||
{
|
|
||||||
return msg_level >= level_.load(std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// protected virtual called at end of each user log call (if enabled) by the
|
|
||||||
// line_logger
|
|
||||||
//
|
|
||||||
inline void spdlog::logger::sink_it_(details::log_msg &msg)
|
|
||||||
{
|
|
||||||
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
|
|
||||||
incr_msg_counter_(msg);
|
|
||||||
#endif
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
if (sink->should_log(msg.level))
|
|
||||||
{
|
|
||||||
sink->log(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (should_flush_(msg))
|
|
||||||
{
|
|
||||||
flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::logger::flush_()
|
|
||||||
{
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
sink->flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::logger::default_err_handler_(const std::string &msg)
|
|
||||||
{
|
|
||||||
auto now = time(nullptr);
|
|
||||||
if (now - last_err_time_ < 60)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
last_err_time_ = now;
|
|
||||||
auto tm_time = details::os::localtime(now);
|
|
||||||
char date_buf[100];
|
|
||||||
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
|
|
||||||
fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void spdlog::logger::incr_msg_counter_(details::log_msg &msg)
|
|
||||||
{
|
|
||||||
msg.msg_id = msg_counter_.fetch_add(1, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline const std::vector<spdlog::sink_ptr> &spdlog::logger::sinks() const
|
|
||||||
{
|
|
||||||
return sinks_;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::vector<spdlog::sink_ptr> &spdlog::logger::sinks()
|
|
||||||
{
|
|
||||||
return sinks_;
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,7 @@
|
|||||||
#pragma once
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
|
||||||
//
|
|
||||||
// Copyright(c) 2018 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
// multi producer-multi consumer blocking queue.
|
// multi producer-multi consumer blocking queue.
|
||||||
// enqueue(..) - will block until room found to put the new message.
|
// enqueue(..) - will block until room found to put the new message.
|
||||||
@@ -27,8 +25,7 @@ public:
|
|||||||
using item_type = T;
|
using item_type = T;
|
||||||
explicit mpmc_blocking_queue(size_t max_items)
|
explicit mpmc_blocking_queue(size_t max_items)
|
||||||
: q_(max_items)
|
: q_(max_items)
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef __MINGW32__
|
#ifndef __MINGW32__
|
||||||
// try to enqueue and block if no room left
|
// try to enqueue and block if no room left
|
||||||
@@ -62,7 +59,8 @@ public:
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
q_.pop_front(popped_item);
|
popped_item = std::move(q_.front());
|
||||||
|
q_.pop_front();
|
||||||
}
|
}
|
||||||
pop_cv_.notify_one();
|
pop_cv_.notify_one();
|
||||||
return true;
|
return true;
|
||||||
@@ -72,7 +70,7 @@ public:
|
|||||||
// apparently mingw deadlocks if the mutex is released before cv.notify_one(),
|
// apparently mingw deadlocks if the mutex is released before cv.notify_one(),
|
||||||
// so release the mutex at the very end each function.
|
// so release the mutex at the very end each function.
|
||||||
|
|
||||||
// try to enqueue and block if no room left
|
// try to enqueue and block if no room left
|
||||||
void enqueue(T &&item)
|
void enqueue(T &&item)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
@@ -87,6 +85,7 @@ public:
|
|||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
q_.push_back(std::move(item));
|
q_.push_back(std::move(item));
|
||||||
push_cv_.notify_one();
|
push_cv_.notify_one();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// try to dequeue item. if no item found. wait upto timeout and try again
|
// try to dequeue item. if no item found. wait upto timeout and try again
|
||||||
@@ -98,13 +97,20 @@ public:
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
q_.pop_front(popped_item);
|
popped_item = std::move(q_.front());
|
||||||
|
q_.pop_front();
|
||||||
pop_cv_.notify_one();
|
pop_cv_.notify_one();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
size_t overrun_counter()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
|
return q_.overrun_counter();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::mutex queue_mutex_;
|
std::mutex queue_mutex_;
|
||||||
std::condition_variable push_cv_;
|
std::condition_variable push_cv_;
|
||||||
|
|||||||
@@ -1,20 +1,19 @@
|
|||||||
//
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
#include <utility>
|
||||||
// null, no cost dummy "mutex" and dummy "atomic" int
|
// null, no cost dummy "mutex" and dummy "atomic" int
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace details {
|
namespace details {
|
||||||
struct null_mutex
|
struct null_mutex
|
||||||
{
|
{
|
||||||
void lock() {}
|
void lock() const {}
|
||||||
void unlock() {}
|
void unlock() const {}
|
||||||
bool try_lock()
|
bool try_lock() const
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -25,19 +24,24 @@ struct null_atomic_int
|
|||||||
int value;
|
int value;
|
||||||
null_atomic_int() = default;
|
null_atomic_int() = default;
|
||||||
|
|
||||||
explicit null_atomic_int(int val)
|
explicit null_atomic_int(int new_value)
|
||||||
: value(val)
|
: value(new_value)
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
int load(std::memory_order) const
|
int load(std::memory_order = std::memory_order_relaxed) const
|
||||||
{
|
{
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void store(int val)
|
void store(int new_value, std::memory_order = std::memory_order_relaxed)
|
||||||
{
|
{
|
||||||
value = val;
|
value = new_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int exchange(int new_value, std::memory_order = std::memory_order_relaxed)
|
||||||
|
{
|
||||||
|
std::swap(new_value, value);
|
||||||
|
return new_value; // return value before the call
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
465
include/spdlog/details/os-inl.h
Normal file
465
include/spdlog/details/os-inl.h
Normal file
@@ -0,0 +1,465 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include "spdlog/details/os.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "spdlog/common.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
#include <ctime>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <array>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
|
||||||
|
#ifndef NOMINMAX
|
||||||
|
#define NOMINMAX // prevent windows redefining min/max
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
#include <io.h> // _get_osfhandle and _isatty support
|
||||||
|
#include <process.h> // _get_pid support
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
#include <share.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
|
#include <limits>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else // unix
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
|
||||||
|
|
||||||
|
#elif defined(_AIX)
|
||||||
|
#include <pthread.h> // for pthread_getthreadid_np
|
||||||
|
|
||||||
|
#elif defined(__DragonFly__) || defined(__FreeBSD__)
|
||||||
|
#include <pthread_np.h> // for pthread_getthreadid_np
|
||||||
|
|
||||||
|
#elif defined(__NetBSD__)
|
||||||
|
#include <lwp.h> // for _lwp_self
|
||||||
|
|
||||||
|
#elif defined(__sun)
|
||||||
|
#include <thread.h> // for thr_self
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // unix
|
||||||
|
|
||||||
|
#ifndef __has_feature // Clang - feature checking macros.
|
||||||
|
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
namespace os {
|
||||||
|
|
||||||
|
SPDLOG_INLINE spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
|
||||||
|
#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
|
||||||
|
timespec ts;
|
||||||
|
::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
|
||||||
|
return std::chrono::time_point<log_clock, typename log_clock::duration>(
|
||||||
|
std::chrono::duration_cast<typename log_clock::duration>(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
|
||||||
|
|
||||||
|
#else
|
||||||
|
return log_clock::now();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
SPDLOG_INLINE std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::tm tm;
|
||||||
|
localtime_s(&tm, &time_tt);
|
||||||
|
#else
|
||||||
|
std::tm tm;
|
||||||
|
localtime_r(&time_tt, &tm);
|
||||||
|
#endif
|
||||||
|
return tm;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
std::time_t now_t = time(nullptr);
|
||||||
|
return localtime(now_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
std::tm tm;
|
||||||
|
gmtime_s(&tm, &time_tt);
|
||||||
|
#else
|
||||||
|
std::tm tm;
|
||||||
|
gmtime_r(&time_tt, &tm);
|
||||||
|
#endif
|
||||||
|
return tm;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
std::time_t now_t = time(nullptr);
|
||||||
|
return gmtime(now_t);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void prevent_child_fd(FILE *f)
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#if !defined(__cplusplus_winrt)
|
||||||
|
auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(_fileno(f)));
|
||||||
|
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
|
||||||
|
SPDLOG_THROW(spdlog_ex("SetHandleInformation failed", errno));
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
auto fd = fileno(f);
|
||||||
|
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
|
||||||
|
{
|
||||||
|
SPDLOG_THROW(spdlog_ex("fcntl with FD_CLOEXEC failed", errno));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// fopen_s on non windows for writing
|
||||||
|
SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||||
|
*fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
|
||||||
|
#else
|
||||||
|
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
|
||||||
|
#endif
|
||||||
|
#else // unix
|
||||||
|
*fp = fopen((filename.c_str()), mode.c_str());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SPDLOG_PREVENT_CHILD_FD
|
||||||
|
if (*fp != nullptr)
|
||||||
|
{
|
||||||
|
prevent_child_fd(*fp);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return *fp == nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
|
return _wremove(filename.c_str());
|
||||||
|
#else
|
||||||
|
return std::remove(filename.c_str());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
return file_exists(filename) ? remove(filename) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
|
return _wrename(filename1.c_str(), filename2.c_str());
|
||||||
|
#else
|
||||||
|
return std::rename(filename1.c_str(), filename2.c_str());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if file exists
|
||||||
|
SPDLOG_INLINE bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||||
|
auto attribs = GetFileAttributesW(filename.c_str());
|
||||||
|
#else
|
||||||
|
auto attribs = GetFileAttributesA(filename.c_str());
|
||||||
|
#endif
|
||||||
|
return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
|
||||||
|
#else // common linux/unix all have the stat system call
|
||||||
|
struct stat buffer;
|
||||||
|
return (::stat(filename.c_str(), &buffer) == 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return file size according to open FILE* object
|
||||||
|
SPDLOG_INLINE size_t filesize(FILE *f)
|
||||||
|
{
|
||||||
|
if (f == nullptr)
|
||||||
|
{
|
||||||
|
SPDLOG_THROW(spdlog_ex("Failed getting file size. fd is null"));
|
||||||
|
}
|
||||||
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||||
|
int fd = _fileno(f);
|
||||||
|
#if _WIN64 // 64 bits
|
||||||
|
__int64 ret = _filelengthi64(fd);
|
||||||
|
if (ret >= 0)
|
||||||
|
{
|
||||||
|
return static_cast<size_t>(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else // windows 32 bits
|
||||||
|
long ret = _filelength(fd);
|
||||||
|
if (ret >= 0)
|
||||||
|
{
|
||||||
|
return static_cast<size_t>(ret);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#else // unix
|
||||||
|
int fd = fileno(f);
|
||||||
|
// 64 bits(but not in osx or cygwin, where fstat64 is deprecated)
|
||||||
|
#if (defined(__linux__) || defined(__sun) || defined(_AIX)) && (defined(__LP64__) || defined(_LP64))
|
||||||
|
struct stat64 st;
|
||||||
|
if (::fstat64(fd, &st) == 0)
|
||||||
|
{
|
||||||
|
return static_cast<size_t>(st.st_size);
|
||||||
|
}
|
||||||
|
#else // unix 32 bits or cygwin
|
||||||
|
struct stat st;
|
||||||
|
|
||||||
|
if (::fstat(fd, &st) == 0)
|
||||||
|
{
|
||||||
|
return static_cast<size_t>(st.st_size);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
SPDLOG_THROW(spdlog_ex("Failed getting file size from fd", errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return utc offset in minutes or throw spdlog_ex on failure
|
||||||
|
SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#if _WIN32_WINNT < _WIN32_WINNT_WS08
|
||||||
|
TIME_ZONE_INFORMATION tzinfo;
|
||||||
|
auto rv = GetTimeZoneInformation(&tzinfo);
|
||||||
|
#else
|
||||||
|
DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
|
||||||
|
auto rv = GetDynamicTimeZoneInformation(&tzinfo);
|
||||||
|
#endif
|
||||||
|
if (rv == TIME_ZONE_ID_INVALID)
|
||||||
|
SPDLOG_THROW(spdlog::spdlog_ex("Failed getting timezone info. ", errno));
|
||||||
|
|
||||||
|
int offset = -tzinfo.Bias;
|
||||||
|
if (tm.tm_isdst)
|
||||||
|
{
|
||||||
|
offset -= tzinfo.DaylightBias;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
offset -= tzinfo.StandardBias;
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
#else
|
||||||
|
|
||||||
|
#if defined(sun) || defined(__sun) || defined(_AIX)
|
||||||
|
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
|
||||||
|
struct helper
|
||||||
|
{
|
||||||
|
static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), const std::tm &gmtm = details::os::gmtime())
|
||||||
|
{
|
||||||
|
int local_year = localtm.tm_year + (1900 - 1);
|
||||||
|
int gmt_year = gmtm.tm_year + (1900 - 1);
|
||||||
|
|
||||||
|
long int days = (
|
||||||
|
// difference in day of year
|
||||||
|
localtm.tm_yday -
|
||||||
|
gmtm.tm_yday
|
||||||
|
|
||||||
|
// + intervening leap days
|
||||||
|
+ ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
|
||||||
|
((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
|
||||||
|
|
||||||
|
// + difference in years * 365 */
|
||||||
|
+ (long int)(local_year - gmt_year) * 365);
|
||||||
|
|
||||||
|
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
|
||||||
|
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
|
||||||
|
long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
|
||||||
|
|
||||||
|
return secs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto offset_seconds = helper::calculate_gmt_offset(tm);
|
||||||
|
#else
|
||||||
|
auto offset_seconds = tm.tm_gmtoff;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return static_cast<int>(offset_seconds / 60);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return current thread id as size_t
|
||||||
|
// It exists because the std::this_thread::get_id() is much slower(especially
|
||||||
|
// under VS 2013)
|
||||||
|
SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
return static_cast<size_t>(::GetCurrentThreadId());
|
||||||
|
#elif defined(__linux__)
|
||||||
|
#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
|
||||||
|
#define SYS_gettid __NR_gettid
|
||||||
|
#endif
|
||||||
|
return static_cast<size_t>(syscall(SYS_gettid));
|
||||||
|
#elif defined(_AIX) || defined(__DragonFly__) || defined(__FreeBSD__)
|
||||||
|
return static_cast<size_t>(pthread_getthreadid_np());
|
||||||
|
#elif defined(__NetBSD__)
|
||||||
|
return static_cast<size_t>(_lwp_self());
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
return static_cast<size_t>(getthrid());
|
||||||
|
#elif defined(__sun)
|
||||||
|
return static_cast<size_t>(thr_self());
|
||||||
|
#elif __APPLE__
|
||||||
|
uint64_t tid;
|
||||||
|
pthread_threadid_np(nullptr, &tid);
|
||||||
|
return static_cast<size_t>(tid);
|
||||||
|
#else // Default to standard C++11 (other Unix)
|
||||||
|
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return current thread id as size_t (from thread local storage)
|
||||||
|
SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#if defined(SPDLOG_NO_TLS)
|
||||||
|
return _thread_id();
|
||||||
|
#else // cache thread id in tls
|
||||||
|
static thread_local const size_t tid = _thread_id();
|
||||||
|
return tid;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is avoid msvc issue in sleep_for that happens if the clock changes.
|
||||||
|
// See https://github.com/gabime/spdlog/issues/609
|
||||||
|
SPDLOG_INLINE void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#if defined(_WIN32)
|
||||||
|
::Sleep(milliseconds);
|
||||||
|
#else
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
|
||||||
|
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
|
SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
|
||||||
|
{
|
||||||
|
memory_buf_t buf;
|
||||||
|
wstr_to_utf8buf(filename, buf);
|
||||||
|
return fmt::to_string(buf);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
SPDLOG_INLINE std::string filename_to_str(const filename_t &filename)
|
||||||
|
{
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
return static_cast<int>(::GetCurrentProcessId());
|
||||||
|
#else
|
||||||
|
return static_cast<int>(::getpid());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if the terminal supports colors
|
||||||
|
// Source: https://github.com/agauniyal/rang/
|
||||||
|
SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
static constexpr std::array<const char *, 14> Terms = {{
|
||||||
|
"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"}};
|
||||||
|
|
||||||
|
const char *env_p = std::getenv("TERM");
|
||||||
|
if (env_p == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const bool result =
|
||||||
|
std::any_of(std::begin(Terms), std::end(Terms), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
|
||||||
|
return result;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detrmine if the terminal attached
|
||||||
|
// Source: https://github.com/agauniyal/rang/
|
||||||
|
SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
return _isatty(_fileno(file)) != 0;
|
||||||
|
#else
|
||||||
|
return isatty(fileno(file)) != 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||||
|
SPDLOG_INLINE void wstr_to_utf8buf(basic_string_view_t<wchar_t> wstr, memory_buf_t &target)
|
||||||
|
{
|
||||||
|
if (wstr.size() > static_cast<size_t>(std::numeric_limits<int>::max()))
|
||||||
|
{
|
||||||
|
SPDLOG_THROW(spdlog::spdlog_ex("UTF-16 string is too big to be converted to UTF-8"));
|
||||||
|
}
|
||||||
|
|
||||||
|
int wstr_size = static_cast<int>(wstr.size());
|
||||||
|
if (wstr_size == 0)
|
||||||
|
{
|
||||||
|
target.resize(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result_size = static_cast<int>(target.capacity());
|
||||||
|
if ((wstr_size + 1) * 2 > result_size)
|
||||||
|
{
|
||||||
|
result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, NULL, 0, NULL, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result_size > 0)
|
||||||
|
{
|
||||||
|
target.resize(result_size);
|
||||||
|
result_size = ::WideCharToMultiByte(CP_UTF8, 0, wstr.data(), wstr_size, target.data(), result_size, NULL, NULL);
|
||||||
|
|
||||||
|
if (result_size > 0)
|
||||||
|
{
|
||||||
|
target.resize(result_size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_THROW(spdlog::spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError())));
|
||||||
|
}
|
||||||
|
#endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||||
|
|
||||||
|
} // namespace os
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
@@ -1,122 +1,24 @@
|
|||||||
//
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "../common.h"
|
#include "spdlog/common.h"
|
||||||
|
#include <ctime> // std::time_t
|
||||||
#include <algorithm>
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
#include <ctime>
|
|
||||||
#include <functional>
|
|
||||||
#include <string>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
|
|
||||||
#ifndef NOMINMAX
|
|
||||||
#define NOMINMAX // prevent windows redefining min/max
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef WIN32_LEAN_AND_MEAN
|
|
||||||
#define WIN32_LEAN_AND_MEAN
|
|
||||||
#endif
|
|
||||||
#include <io.h> // _get_osfhandle and _isatty support
|
|
||||||
#include <process.h> // _get_pid support
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
#ifdef __MINGW32__
|
|
||||||
#include <share.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#else // unix
|
|
||||||
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
|
|
||||||
|
|
||||||
#elif __FreeBSD__
|
|
||||||
#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // unix
|
|
||||||
|
|
||||||
#ifndef __has_feature // Clang - feature checking macros.
|
|
||||||
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace details {
|
namespace details {
|
||||||
namespace os {
|
namespace os {
|
||||||
|
|
||||||
inline spdlog::log_clock::time_point now()
|
spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT;
|
||||||
{
|
|
||||||
|
|
||||||
#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
|
std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
|
||||||
timespec ts;
|
|
||||||
::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
|
|
||||||
return std::chrono::time_point<log_clock, typename log_clock::duration>(
|
|
||||||
std::chrono::duration_cast<typename log_clock::duration>(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
|
|
||||||
|
|
||||||
#else
|
std::tm localtime() SPDLOG_NOEXCEPT;
|
||||||
return log_clock::now();
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
inline std::tm localtime(const std::time_t &time_tt)
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
|
||||||
std::tm tm;
|
|
||||||
localtime_s(&tm, &time_tt);
|
|
||||||
#else
|
|
||||||
std::tm tm;
|
|
||||||
localtime_r(&time_tt, &tm);
|
|
||||||
#endif
|
|
||||||
return tm;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::tm localtime()
|
std::tm gmtime() SPDLOG_NOEXCEPT;
|
||||||
{
|
|
||||||
std::time_t now_t = time(nullptr);
|
|
||||||
return localtime(now_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::tm gmtime(const std::time_t &time_tt)
|
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
std::tm tm;
|
|
||||||
gmtime_s(&tm, &time_tt);
|
|
||||||
#else
|
|
||||||
std::tm tm;
|
|
||||||
gmtime_r(&time_tt, &tm);
|
|
||||||
#endif
|
|
||||||
return tm;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::tm gmtime()
|
|
||||||
{
|
|
||||||
std::time_t now_t = time(nullptr);
|
|
||||||
return gmtime(now_t);
|
|
||||||
}
|
|
||||||
inline bool operator==(const std::tm &tm1, const std::tm &tm2)
|
|
||||||
{
|
|
||||||
return (tm1.tm_sec == tm2.tm_sec && tm1.tm_min == tm2.tm_min && tm1.tm_hour == tm2.tm_hour && tm1.tm_mday == tm2.tm_mday &&
|
|
||||||
tm1.tm_mon == tm2.tm_mon && tm1.tm_year == tm2.tm_year && tm1.tm_isdst == tm2.tm_isdst);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool operator!=(const std::tm &tm1, const std::tm &tm2)
|
|
||||||
{
|
|
||||||
return !(tm1 == tm2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eol definition
|
// eol definition
|
||||||
#if !defined(SPDLOG_EOL)
|
#if !defined(SPDLOG_EOL)
|
||||||
@@ -131,302 +33,66 @@ SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
|
|||||||
|
|
||||||
// folder separator
|
// folder separator
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
SPDLOG_CONSTEXPR static const char folder_sep = '\\';
|
const char folder_sep = '\\';
|
||||||
#else
|
#else
|
||||||
SPDLOG_CONSTEXPR static const char folder_sep = '/';
|
SPDLOG_CONSTEXPR static const char folder_sep = '/';
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
inline void prevent_child_fd(FILE *f)
|
void prevent_child_fd(FILE *f);
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#if !defined(__cplusplus_winrt)
|
|
||||||
auto file_handle = (HANDLE)_get_osfhandle(_fileno(f));
|
|
||||||
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
|
|
||||||
throw spdlog_ex("SetHandleInformation failed", errno);
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
auto fd = fileno(f);
|
|
||||||
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
|
|
||||||
{
|
|
||||||
throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// fopen_s on non windows for writing
|
// fopen_s on non windows for writing
|
||||||
inline bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
|
bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
#ifdef SPDLOG_WCHAR_FILENAMES
|
|
||||||
*fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
|
|
||||||
#else
|
|
||||||
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
|
|
||||||
#endif
|
|
||||||
#else // unix
|
|
||||||
*fp = fopen((filename.c_str()), mode.c_str());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef SPDLOG_PREVENT_CHILD_FD
|
// Remove filename. return 0 on success
|
||||||
if (*fp != nullptr)
|
int remove(const filename_t &filename) SPDLOG_NOEXCEPT;
|
||||||
{
|
|
||||||
prevent_child_fd(*fp);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return *fp == nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int remove(const filename_t &filename)
|
// Remove file if exists. return 0 on success
|
||||||
{
|
// Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread)
|
||||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
|
||||||
return _wremove(filename.c_str());
|
|
||||||
#else
|
|
||||||
return std::remove(filename.c_str());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int rename(const filename_t &filename1, const filename_t &filename2)
|
int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT;
|
||||||
{
|
|
||||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
|
||||||
return _wrename(filename1.c_str(), filename2.c_str());
|
|
||||||
#else
|
|
||||||
return std::rename(filename1.c_str(), filename2.c_str());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return if file exists
|
// Return if file exists.
|
||||||
inline bool file_exists(const filename_t &filename)
|
bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
#ifdef SPDLOG_WCHAR_FILENAMES
|
|
||||||
auto attribs = GetFileAttributesW(filename.c_str());
|
|
||||||
#else
|
|
||||||
auto attribs = GetFileAttributesA(filename.c_str());
|
|
||||||
#endif
|
|
||||||
return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
|
|
||||||
#else // common linux/unix all have the stat system call
|
|
||||||
struct stat buffer;
|
|
||||||
return (stat(filename.c_str(), &buffer) == 0);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return file size according to open FILE* object
|
// Return file size according to open FILE* object
|
||||||
inline size_t filesize(FILE *f)
|
size_t filesize(FILE *f);
|
||||||
{
|
|
||||||
if (f == nullptr)
|
|
||||||
{
|
|
||||||
throw spdlog_ex("Failed getting file size. fd is null");
|
|
||||||
}
|
|
||||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
|
||||||
int fd = _fileno(f);
|
|
||||||
#if _WIN64 // 64 bits
|
|
||||||
struct _stat64 st;
|
|
||||||
if (_fstat64(fd, &st) == 0)
|
|
||||||
{
|
|
||||||
return st.st_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else // windows 32 bits
|
|
||||||
long ret = _filelength(fd);
|
|
||||||
if (ret >= 0)
|
|
||||||
{
|
|
||||||
return static_cast<size_t>(ret);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#else // unix
|
|
||||||
int fd = fileno(f);
|
|
||||||
// 64 bits(but not in osx or cygwin, where fstat64 is deprecated)
|
|
||||||
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__)
|
|
||||||
struct stat64 st;
|
|
||||||
if (fstat64(fd, &st) == 0)
|
|
||||||
{
|
|
||||||
return static_cast<size_t>(st.st_size);
|
|
||||||
}
|
|
||||||
#else // unix 32 bits or cygwin
|
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
if (fstat(fd, &st) == 0)
|
|
||||||
{
|
|
||||||
return static_cast<size_t>(st.st_size);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
throw spdlog_ex("Failed getting file size from fd", errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return utc offset in minutes or throw spdlog_ex on failure
|
// Return utc offset in minutes or throw spdlog_ex on failure
|
||||||
inline int utc_minutes_offset(const std::tm &tm = details::os::localtime())
|
int utc_minutes_offset(const std::tm &tm = details::os::localtime());
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
#if _WIN32_WINNT < _WIN32_WINNT_WS08
|
|
||||||
TIME_ZONE_INFORMATION tzinfo;
|
|
||||||
auto rv = GetTimeZoneInformation(&tzinfo);
|
|
||||||
#else
|
|
||||||
DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
|
|
||||||
auto rv = GetDynamicTimeZoneInformation(&tzinfo);
|
|
||||||
#endif
|
|
||||||
if (rv == TIME_ZONE_ID_INVALID)
|
|
||||||
throw spdlog::spdlog_ex("Failed getting timezone info. ", errno);
|
|
||||||
|
|
||||||
int offset = -tzinfo.Bias;
|
|
||||||
if (tm.tm_isdst)
|
|
||||||
{
|
|
||||||
offset -= tzinfo.DaylightBias;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
offset -= tzinfo.StandardBias;
|
|
||||||
}
|
|
||||||
return offset;
|
|
||||||
#else
|
|
||||||
|
|
||||||
#if defined(sun) || defined(__sun) || defined(_AIX)
|
|
||||||
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
|
|
||||||
struct helper
|
|
||||||
{
|
|
||||||
static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), const std::tm &gmtm = details::os::gmtime())
|
|
||||||
{
|
|
||||||
int local_year = localtm.tm_year + (1900 - 1);
|
|
||||||
int gmt_year = gmtm.tm_year + (1900 - 1);
|
|
||||||
|
|
||||||
long int days = (
|
|
||||||
// difference in day of year
|
|
||||||
localtm.tm_yday -
|
|
||||||
gmtm.tm_yday
|
|
||||||
|
|
||||||
// + intervening leap days
|
|
||||||
+ ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
|
|
||||||
((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
|
|
||||||
|
|
||||||
// + difference in years * 365 */
|
|
||||||
+ (long int)(local_year - gmt_year) * 365);
|
|
||||||
|
|
||||||
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
|
|
||||||
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
|
|
||||||
long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
|
|
||||||
|
|
||||||
return secs;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
auto offset_seconds = helper::calculate_gmt_offset(tm);
|
|
||||||
#else
|
|
||||||
auto offset_seconds = tm.tm_gmtoff;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return static_cast<int>(offset_seconds / 60);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return current thread id as size_t
|
// Return current thread id as size_t
|
||||||
// It exists because the std::this_thread::get_id() is much slower(especially
|
// It exists because the std::this_thread::get_id() is much slower(especially
|
||||||
// under VS 2013)
|
// under VS 2013)
|
||||||
inline size_t _thread_id()
|
size_t _thread_id() SPDLOG_NOEXCEPT;
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
return static_cast<size_t>(::GetCurrentThreadId());
|
|
||||||
#elif __linux__
|
|
||||||
#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
|
|
||||||
#define SYS_gettid __NR_gettid
|
|
||||||
#endif
|
|
||||||
return static_cast<size_t>(syscall(SYS_gettid));
|
|
||||||
#elif __FreeBSD__
|
|
||||||
long tid;
|
|
||||||
thr_self(&tid);
|
|
||||||
return static_cast<size_t>(tid);
|
|
||||||
#elif __APPLE__
|
|
||||||
uint64_t tid;
|
|
||||||
pthread_threadid_np(nullptr, &tid);
|
|
||||||
return static_cast<size_t>(tid);
|
|
||||||
#else // Default to standard C++11 (other Unix)
|
|
||||||
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return current thread id as size_t (from thread local storage)
|
// Return current thread id as size_t (from thread local storage)
|
||||||
inline size_t thread_id()
|
size_t thread_id() SPDLOG_NOEXCEPT;
|
||||||
{
|
|
||||||
#if defined(SPDLOG_DISABLE_TID_CACHING) || (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt) || \
|
|
||||||
(defined(__clang__) && !__has_feature(cxx_thread_local))
|
|
||||||
return _thread_id();
|
|
||||||
#else // cache thread id in tls
|
|
||||||
static thread_local const size_t tid = _thread_id();
|
|
||||||
return tid;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is avoid msvc issue in sleep_for that happens if the clock changes.
|
// This is avoid msvc issue in sleep_for that happens if the clock changes.
|
||||||
// See https://github.com/gabime/spdlog/issues/609
|
// See https://github.com/gabime/spdlog/issues/609
|
||||||
inline void sleep_for_millis(int milliseconds)
|
void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT;
|
||||||
{
|
|
||||||
#if defined(_WIN32)
|
|
||||||
::Sleep(milliseconds);
|
|
||||||
#else
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
|
std::string filename_to_str(const filename_t &filename);
|
||||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
|
||||||
#define SPDLOG_FILENAME_T(s) L##s
|
|
||||||
inline std::string filename_to_str(const filename_t &filename)
|
|
||||||
{
|
|
||||||
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c;
|
|
||||||
return c.to_bytes(filename);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
#define SPDLOG_FILENAME_T(s) s
|
|
||||||
inline std::string filename_to_str(const filename_t &filename)
|
|
||||||
{
|
|
||||||
return filename;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
inline int pid()
|
int pid() SPDLOG_NOEXCEPT;
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
return static_cast<int>(::GetCurrentProcessId());
|
|
||||||
#else
|
|
||||||
return static_cast<int>(::getpid());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if the terminal supports colors
|
// Determine if the terminal supports colors
|
||||||
// Source: https://github.com/agauniyal/rang/
|
// Source: https://github.com/agauniyal/rang/
|
||||||
inline bool is_color_terminal()
|
bool is_color_terminal() SPDLOG_NOEXCEPT;
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
return true;
|
|
||||||
#else
|
|
||||||
static constexpr const char *Terms[] = {
|
|
||||||
"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"};
|
|
||||||
|
|
||||||
const char *env_p = std::getenv("TERM");
|
|
||||||
if (env_p == nullptr)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const bool result =
|
|
||||||
std::any_of(std::begin(Terms), std::end(Terms), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
|
|
||||||
return result;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detrmine if the terminal attached
|
// Detrmine if the terminal attached
|
||||||
// Source: https://github.com/agauniyal/rang/
|
// Source: https://github.com/agauniyal/rang/
|
||||||
inline bool in_terminal(FILE *file)
|
bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
|
||||||
{
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||||
return _isatty(_fileno(file)) != 0;
|
void wstr_to_utf8buf(basic_string_view_t<wchar_t> wstr, memory_buf_t &target);
|
||||||
#else
|
|
||||||
return isatty(fileno(file)) != 0;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
|
||||||
} // namespace os
|
} // namespace os
|
||||||
} // namespace details
|
} // namespace details
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "os-inl.h"
|
||||||
|
#endif
|
||||||
|
|||||||
1317
include/spdlog/details/pattern_formatter-inl.h
Normal file
1317
include/spdlog/details/pattern_formatter-inl.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,572 +1,77 @@
|
|||||||
//
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "spdlog/details/fmt_helper.h"
|
#include "spdlog/common.h"
|
||||||
#include "spdlog/details/log_msg.h"
|
#include "spdlog/details/log_msg.h"
|
||||||
#include "spdlog/details/os.h"
|
#include "spdlog/details/os.h"
|
||||||
#include "spdlog/fmt/fmt.h"
|
|
||||||
#include "spdlog/formatter.h"
|
#include "spdlog/formatter.h"
|
||||||
|
|
||||||
#include <array>
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace details {
|
namespace details {
|
||||||
|
|
||||||
|
// padding information.
|
||||||
|
struct padding_info
|
||||||
|
{
|
||||||
|
enum pad_side
|
||||||
|
{
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
center
|
||||||
|
};
|
||||||
|
|
||||||
|
padding_info() = default;
|
||||||
|
padding_info(size_t width, padding_info::pad_side side)
|
||||||
|
: width_(width)
|
||||||
|
, side_(side)
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool enabled() const
|
||||||
|
{
|
||||||
|
return width_ != 0;
|
||||||
|
}
|
||||||
|
const size_t width_ = 0;
|
||||||
|
const pad_side side_ = left;
|
||||||
|
};
|
||||||
|
|
||||||
class flag_formatter
|
class flag_formatter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
explicit flag_formatter(padding_info padinfo)
|
||||||
|
: padinfo_(padinfo)
|
||||||
|
{}
|
||||||
|
flag_formatter() = default;
|
||||||
virtual ~flag_formatter() = default;
|
virtual ~flag_formatter() = default;
|
||||||
virtual void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) = 0;
|
virtual void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) = 0;
|
||||||
};
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
protected:
|
||||||
// name & level pattern appenders
|
padding_info padinfo_;
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
class name_formatter : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::append_str(*msg.logger_name, dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// log level appender
|
|
||||||
class level_formatter : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::append_c_str(level::to_c_str(msg.level), dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// short log level appender
|
|
||||||
class short_level_formatter : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::append_c_str(level::to_short_c_str(msg.level), dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// Date time pattern appenders
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
static const char *ampm(const tm &t)
|
|
||||||
{
|
|
||||||
return t.tm_hour >= 12 ? "PM" : "AM";
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned int to12h(const tm &t)
|
|
||||||
{
|
|
||||||
return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Abbreviated weekday name
|
|
||||||
static const char *days[]{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
|
|
||||||
class a_formatter : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::append_c_str(days[tm_time.tm_wday], dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Full weekday name
|
|
||||||
static const char *full_days[]{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
|
|
||||||
class A_formatter : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::append_c_str(full_days[tm_time.tm_wday], dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Abbreviated month
|
|
||||||
static const char *months[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"};
|
|
||||||
class b_formatter : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::append_c_str(months[tm_time.tm_mon], dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Full month name
|
|
||||||
static const char *full_months[]{
|
|
||||||
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
|
|
||||||
class B_formatter : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::append_c_str(full_months[tm_time.tm_mon], dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Date and time representation (Thu Aug 23 15:35:46 2014)
|
|
||||||
class c_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
// fmt::format_to(dest, "{} {} {} ", days[tm_time.tm_wday],
|
|
||||||
// months[tm_time.tm_mon], tm_time.tm_mday);
|
|
||||||
// date
|
|
||||||
fmt_helper::append_str(days[tm_time.tm_wday], dest);
|
|
||||||
dest.push_back(' ');
|
|
||||||
fmt_helper::append_str(months[tm_time.tm_mon], dest);
|
|
||||||
dest.push_back(' ');
|
|
||||||
fmt_helper::append_int(tm_time.tm_mday, dest);
|
|
||||||
dest.push_back(' ');
|
|
||||||
// time
|
|
||||||
|
|
||||||
fmt_helper::pad2(tm_time.tm_hour, dest);
|
|
||||||
dest.push_back(':');
|
|
||||||
fmt_helper::pad2(tm_time.tm_min, dest);
|
|
||||||
dest.push_back(':');
|
|
||||||
fmt_helper::pad2(tm_time.tm_sec, dest);
|
|
||||||
dest.push_back(' ');
|
|
||||||
fmt_helper::append_int(tm_time.tm_year + 1900, dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// year - 2 digit
|
|
||||||
class C_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::pad2(tm_time.tm_year % 100, dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
|
|
||||||
class D_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::pad2(tm_time.tm_mon + 1, dest);
|
|
||||||
dest.push_back('/');
|
|
||||||
fmt_helper::pad2(tm_time.tm_mday, dest);
|
|
||||||
dest.push_back('/');
|
|
||||||
fmt_helper::pad2(tm_time.tm_year % 100, dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// year - 4 digit
|
|
||||||
class Y_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::append_int(tm_time.tm_year + 1900, dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// month 1-12
|
|
||||||
class m_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::pad2(tm_time.tm_mon + 1, dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// day of month 1-31
|
|
||||||
class d_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::pad2(tm_time.tm_mday, dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// hours in 24 format 0-23
|
|
||||||
class H_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::pad2(tm_time.tm_hour, dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// hours in 12 format 1-12
|
|
||||||
class I_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::pad2(to12h(tm_time), dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// minutes 0-59
|
|
||||||
class M_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::pad2(tm_time.tm_min, dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// seconds 0-59
|
|
||||||
class S_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::pad2(tm_time.tm_sec, dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// milliseconds
|
|
||||||
class e_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
auto millis = fmt_helper::time_fraction<std::chrono::milliseconds>(msg.time);
|
|
||||||
fmt_helper::pad3(static_cast<int>(millis.count()), dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// microseconds
|
|
||||||
class f_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
auto micros = fmt_helper::time_fraction<std::chrono::microseconds>(msg.time);
|
|
||||||
fmt_helper::pad6(static_cast<int>(micros.count()), dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// nanoseconds
|
|
||||||
class F_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
auto ns = fmt_helper::time_fraction<std::chrono::nanoseconds>(msg.time);
|
|
||||||
fmt::format_to(dest, "{:09}", ns.count());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// seconds since epoch
|
|
||||||
class E_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
auto duration = msg.time.time_since_epoch();
|
|
||||||
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
|
|
||||||
fmt_helper::append_int(seconds, dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// AM/PM
|
|
||||||
class p_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::append_c_str(ampm(tm_time), dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 12 hour clock 02:55:02 pm
|
|
||||||
class r_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::pad2(to12h(tm_time), dest);
|
|
||||||
dest.push_back(':');
|
|
||||||
fmt_helper::pad2(tm_time.tm_min, dest);
|
|
||||||
dest.push_back(':');
|
|
||||||
fmt_helper::pad2(tm_time.tm_sec, dest);
|
|
||||||
dest.push_back(' ');
|
|
||||||
fmt_helper::append_c_str(ampm(tm_time), dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 24-hour HH:MM time, equivalent to %H:%M
|
|
||||||
class R_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::pad2(tm_time.tm_hour, dest);
|
|
||||||
dest.push_back(':');
|
|
||||||
fmt_helper::pad2(tm_time.tm_min, dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
|
|
||||||
class T_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
// fmt::format_to(dest, "{:02}:{:02}:{:02}", tm_time.tm_hour,
|
|
||||||
// tm_time.tm_min, tm_time.tm_sec);
|
|
||||||
fmt_helper::pad2(tm_time.tm_hour, dest);
|
|
||||||
dest.push_back(':');
|
|
||||||
fmt_helper::pad2(tm_time.tm_min, dest);
|
|
||||||
dest.push_back(':');
|
|
||||||
fmt_helper::pad2(tm_time.tm_sec, dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// ISO 8601 offset from UTC in timezone (+-HH:MM)
|
|
||||||
class z_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
|
|
||||||
|
|
||||||
z_formatter() = default;
|
|
||||||
z_formatter(const z_formatter &) = delete;
|
|
||||||
z_formatter &operator=(const z_formatter &) = delete;
|
|
||||||
|
|
||||||
void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
#ifdef _WIN32
|
|
||||||
int total_minutes = get_cached_offset(msg, tm_time);
|
|
||||||
#else
|
|
||||||
// No need to chache under gcc,
|
|
||||||
// it is very fast (already stored in tm.tm_gmtoff)
|
|
||||||
(void)(msg);
|
|
||||||
int total_minutes = os::utc_minutes_offset(tm_time);
|
|
||||||
#endif
|
|
||||||
bool is_negative = total_minutes < 0;
|
|
||||||
if (is_negative)
|
|
||||||
{
|
|
||||||
total_minutes = -total_minutes;
|
|
||||||
dest.push_back('-');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
dest.push_back('+');
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt_helper::pad2(total_minutes / 60, dest); // hours
|
|
||||||
dest.push_back(':');
|
|
||||||
fmt_helper::pad2(total_minutes % 60, dest); // minutes
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
log_clock::time_point last_update_{std::chrono::seconds(0)};
|
|
||||||
#ifdef _WIN32
|
|
||||||
int offset_minutes_{0};
|
|
||||||
|
|
||||||
int get_cached_offset(const log_msg &msg, const std::tm &tm_time)
|
|
||||||
{
|
|
||||||
if (msg.time - last_update_ >= cache_refresh)
|
|
||||||
{
|
|
||||||
offset_minutes_ = os::utc_minutes_offset(tm_time);
|
|
||||||
last_update_ = msg.time;
|
|
||||||
}
|
|
||||||
return offset_minutes_;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
// Thread id
|
|
||||||
class t_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::pad6(msg.thread_id, dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Current pid
|
|
||||||
class pid_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::append_int(details::os::pid(), dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// message counter formatter
|
|
||||||
class i_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::pad6(msg.msg_id, dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class v_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::append_buf(msg.raw, dest);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class ch_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit ch_formatter(char ch)
|
|
||||||
: ch_(ch)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
dest.push_back(ch_);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
char ch_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// aggregate user chars to display as is
|
|
||||||
class aggregate_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
aggregate_formatter() = default;
|
|
||||||
|
|
||||||
void add_ch(char ch)
|
|
||||||
{
|
|
||||||
str_ += ch;
|
|
||||||
}
|
|
||||||
void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
fmt_helper::append_str(str_, dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::string str_;
|
|
||||||
};
|
|
||||||
|
|
||||||
// mark the color range. expect it to be in the form of "%^colored text%$"
|
|
||||||
class color_start_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
msg.color_range_start = dest.size();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
class color_stop_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
msg.color_range_end = dest.size();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Full info formatter
|
|
||||||
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
|
|
||||||
class full_formatter SPDLOG_FINAL : public flag_formatter
|
|
||||||
{
|
|
||||||
void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
using namespace std::chrono;
|
|
||||||
#ifndef SPDLOG_NO_DATETIME
|
|
||||||
|
|
||||||
// cache the date/time part for the next second.
|
|
||||||
auto duration = msg.time.time_since_epoch();
|
|
||||||
auto secs = duration_cast<seconds>(duration);
|
|
||||||
|
|
||||||
if (cache_timestamp_ != secs || cached_datetime_.size() == 0)
|
|
||||||
{
|
|
||||||
cached_datetime_.resize(0);
|
|
||||||
cached_datetime_.push_back('[');
|
|
||||||
fmt_helper::append_int(tm_time.tm_year + 1900, cached_datetime_);
|
|
||||||
cached_datetime_.push_back('-');
|
|
||||||
|
|
||||||
fmt_helper::pad2(tm_time.tm_mon + 1, cached_datetime_);
|
|
||||||
cached_datetime_.push_back('-');
|
|
||||||
|
|
||||||
fmt_helper::pad2(tm_time.tm_mday, cached_datetime_);
|
|
||||||
cached_datetime_.push_back(' ');
|
|
||||||
|
|
||||||
fmt_helper::pad2(tm_time.tm_hour, cached_datetime_);
|
|
||||||
cached_datetime_.push_back(':');
|
|
||||||
|
|
||||||
fmt_helper::pad2(tm_time.tm_min, cached_datetime_);
|
|
||||||
cached_datetime_.push_back(':');
|
|
||||||
|
|
||||||
fmt_helper::pad2(tm_time.tm_sec, cached_datetime_);
|
|
||||||
cached_datetime_.push_back('.');
|
|
||||||
|
|
||||||
cache_timestamp_ = secs;
|
|
||||||
}
|
|
||||||
fmt_helper::append_buf(cached_datetime_, dest);
|
|
||||||
|
|
||||||
auto millis = fmt_helper::time_fraction<milliseconds>(msg.time);
|
|
||||||
fmt_helper::pad3(static_cast<int>(millis.count()), dest);
|
|
||||||
dest.push_back(']');
|
|
||||||
dest.push_back(' ');
|
|
||||||
|
|
||||||
#else // no datetime needed
|
|
||||||
(void)tm_time;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SPDLOG_NO_NAME
|
|
||||||
dest.push_back('[');
|
|
||||||
fmt_helper::append_str(*msg.logger_name, dest);
|
|
||||||
dest.push_back(']');
|
|
||||||
dest.push_back(' ');
|
|
||||||
#endif
|
|
||||||
|
|
||||||
dest.push_back('[');
|
|
||||||
// wrap the level name with color
|
|
||||||
msg.color_range_start = dest.size();
|
|
||||||
fmt_helper::append_c_str(level::to_c_str(msg.level), dest);
|
|
||||||
msg.color_range_end = dest.size();
|
|
||||||
dest.push_back(']');
|
|
||||||
dest.push_back(' ');
|
|
||||||
fmt_helper::append_buf(msg.raw, dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::chrono::seconds cache_timestamp_{0};
|
|
||||||
fmt::basic_memory_buffer<char, 128> cached_datetime_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace details
|
} // namespace details
|
||||||
|
|
||||||
class pattern_formatter SPDLOG_FINAL : public formatter
|
class pattern_formatter final : public formatter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit pattern_formatter(
|
explicit pattern_formatter(
|
||||||
std::string pattern, pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol)
|
std::string pattern, pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol);
|
||||||
: pattern_(std::move(pattern))
|
|
||||||
, eol_(std::move(eol))
|
// use default pattern is not given
|
||||||
, pattern_time_type_(time_type)
|
explicit pattern_formatter(pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol);
|
||||||
, last_log_secs_(0)
|
|
||||||
{
|
|
||||||
std::memset(&cached_tm_, 0, sizeof(cached_tm_));
|
|
||||||
compile_pattern_(pattern_);
|
|
||||||
}
|
|
||||||
|
|
||||||
pattern_formatter(const pattern_formatter &other) = delete;
|
pattern_formatter(const pattern_formatter &other) = delete;
|
||||||
pattern_formatter &operator=(const pattern_formatter &other) = delete;
|
pattern_formatter &operator=(const pattern_formatter &other) = delete;
|
||||||
|
|
||||||
virtual std::unique_ptr<formatter> clone() const override
|
std::unique_ptr<formatter> clone() const override;
|
||||||
{
|
void format(const details::log_msg &msg, memory_buf_t &dest) override;
|
||||||
return std::unique_ptr<formatter>(new pattern_formatter(pattern_, pattern_time_type_, eol_));
|
|
||||||
}
|
|
||||||
|
|
||||||
void format(const details::log_msg &msg, fmt::memory_buffer &dest) override
|
|
||||||
{
|
|
||||||
#ifndef SPDLOG_NO_DATETIME
|
|
||||||
auto secs = std::chrono::duration_cast<std::chrono::seconds>(msg.time.time_since_epoch());
|
|
||||||
if (secs != last_log_secs_)
|
|
||||||
{
|
|
||||||
cached_tm_ = get_time_(msg);
|
|
||||||
last_log_secs_ = secs;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
for (auto &f : formatters_)
|
|
||||||
{
|
|
||||||
f->format(msg, cached_tm_, dest);
|
|
||||||
}
|
|
||||||
// write eol
|
|
||||||
details::fmt_helper::append_str(eol_, dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string pattern_;
|
std::string pattern_;
|
||||||
@@ -574,199 +79,21 @@ private:
|
|||||||
pattern_time_type pattern_time_type_;
|
pattern_time_type pattern_time_type_;
|
||||||
std::tm cached_tm_;
|
std::tm cached_tm_;
|
||||||
std::chrono::seconds last_log_secs_;
|
std::chrono::seconds last_log_secs_;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<details::flag_formatter>> formatters_;
|
std::vector<std::unique_ptr<details::flag_formatter>> formatters_;
|
||||||
|
|
||||||
std::tm get_time_(const details::log_msg &msg)
|
std::tm get_time_(const details::log_msg &msg);
|
||||||
{
|
template<typename Padder>
|
||||||
if (pattern_time_type_ == pattern_time_type::local)
|
void handle_flag_(char flag, details::padding_info padding);
|
||||||
{
|
|
||||||
return details::os::localtime(log_clock::to_time_t(msg.time));
|
|
||||||
}
|
|
||||||
return details::os::gmtime(log_clock::to_time_t(msg.time));
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_flag_(char flag)
|
// Extract given pad spec (e.g. %8X)
|
||||||
{
|
// Advance the given it pass the end of the padding spec found (if any)
|
||||||
switch (flag)
|
// Return padding.
|
||||||
{
|
details::padding_info handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end);
|
||||||
// logger name
|
|
||||||
case 'n':
|
|
||||||
formatters_.emplace_back(new details::name_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'l':
|
void compile_pattern_(const std::string &pattern);
|
||||||
formatters_.emplace_back(new details::level_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'L':
|
|
||||||
formatters_.emplace_back(new details::short_level_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('t'):
|
|
||||||
formatters_.emplace_back(new details::t_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('v'):
|
|
||||||
formatters_.emplace_back(new details::v_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('a'):
|
|
||||||
formatters_.emplace_back(new details::a_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('A'):
|
|
||||||
formatters_.emplace_back(new details::A_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('b'):
|
|
||||||
case ('h'):
|
|
||||||
formatters_.emplace_back(new details::b_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('B'):
|
|
||||||
formatters_.emplace_back(new details::B_formatter());
|
|
||||||
break;
|
|
||||||
case ('c'):
|
|
||||||
formatters_.emplace_back(new details::c_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('C'):
|
|
||||||
formatters_.emplace_back(new details::C_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('Y'):
|
|
||||||
formatters_.emplace_back(new details::Y_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('D'):
|
|
||||||
case ('x'):
|
|
||||||
formatters_.emplace_back(new details::D_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('m'):
|
|
||||||
formatters_.emplace_back(new details::m_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('d'):
|
|
||||||
formatters_.emplace_back(new details::d_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('H'):
|
|
||||||
formatters_.emplace_back(new details::H_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('I'):
|
|
||||||
formatters_.emplace_back(new details::I_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('M'):
|
|
||||||
formatters_.emplace_back(new details::M_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('S'):
|
|
||||||
formatters_.emplace_back(new details::S_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('e'):
|
|
||||||
formatters_.emplace_back(new details::e_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('f'):
|
|
||||||
formatters_.emplace_back(new details::f_formatter());
|
|
||||||
break;
|
|
||||||
case ('F'):
|
|
||||||
formatters_.emplace_back(new details::F_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('E'):
|
|
||||||
formatters_.emplace_back(new details::E_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('p'):
|
|
||||||
formatters_.emplace_back(new details::p_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('r'):
|
|
||||||
formatters_.emplace_back(new details::r_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('R'):
|
|
||||||
formatters_.emplace_back(new details::R_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('T'):
|
|
||||||
case ('X'):
|
|
||||||
formatters_.emplace_back(new details::T_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('z'):
|
|
||||||
formatters_.emplace_back(new details::z_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('+'):
|
|
||||||
formatters_.emplace_back(new details::full_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('P'):
|
|
||||||
formatters_.emplace_back(new details::pid_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('i'):
|
|
||||||
formatters_.emplace_back(new details::i_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('^'):
|
|
||||||
formatters_.emplace_back(new details::color_start_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ('$'):
|
|
||||||
formatters_.emplace_back(new details::color_stop_formatter());
|
|
||||||
break;
|
|
||||||
|
|
||||||
default: // Unknown flag appears as is
|
|
||||||
formatters_.emplace_back(new details::ch_formatter('%'));
|
|
||||||
formatters_.emplace_back(new details::ch_formatter(flag));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void compile_pattern_(const std::string &pattern)
|
|
||||||
{
|
|
||||||
auto end = pattern.end();
|
|
||||||
std::unique_ptr<details::aggregate_formatter> user_chars;
|
|
||||||
formatters_.clear();
|
|
||||||
for (auto it = pattern.begin(); it != end; ++it)
|
|
||||||
{
|
|
||||||
if (*it == '%')
|
|
||||||
{
|
|
||||||
if (user_chars) // append user chars found so far
|
|
||||||
{
|
|
||||||
formatters_.push_back(std::move(user_chars));
|
|
||||||
}
|
|
||||||
// if(
|
|
||||||
if (++it != end)
|
|
||||||
{
|
|
||||||
handle_flag_(*it);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // chars not following the % sign should be displayed as is
|
|
||||||
{
|
|
||||||
if (!user_chars)
|
|
||||||
{
|
|
||||||
user_chars = std::unique_ptr<details::aggregate_formatter>(new details::aggregate_formatter());
|
|
||||||
}
|
|
||||||
user_chars->add_ch(*it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (user_chars) // append raw chars found so far
|
|
||||||
{
|
|
||||||
formatters_.push_back(std::move(user_chars));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "pattern_formatter-inl.h"
|
||||||
|
#endif
|
||||||
|
|||||||
49
include/spdlog/details/periodic_worker-inl.h
Normal file
49
include/spdlog/details/periodic_worker-inl.h
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include "spdlog/details/periodic_worker.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
SPDLOG_INLINE periodic_worker::periodic_worker(const std::function<void()> &callback_fun, std::chrono::seconds interval)
|
||||||
|
{
|
||||||
|
active_ = (interval > std::chrono::seconds::zero());
|
||||||
|
if (!active_)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
worker_thread_ = std::thread([this, callback_fun, interval]() {
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(this->mutex_);
|
||||||
|
if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; }))
|
||||||
|
{
|
||||||
|
return; // active_ == false, so exit this thread
|
||||||
|
}
|
||||||
|
callback_fun();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop the worker thread and join it
|
||||||
|
SPDLOG_INLINE periodic_worker::~periodic_worker()
|
||||||
|
{
|
||||||
|
if (worker_thread_.joinable())
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(mutex_);
|
||||||
|
active_ = false;
|
||||||
|
}
|
||||||
|
cv_.notify_one();
|
||||||
|
worker_thread_.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
@@ -1,8 +1,5 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
//
|
|
||||||
// Copyright(c) 2018 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@@ -10,7 +7,7 @@
|
|||||||
//
|
//
|
||||||
// RAII over the owned thread:
|
// RAII over the owned thread:
|
||||||
// creates the thread on construction.
|
// creates the thread on construction.
|
||||||
// stops and joins the thread on destruction.
|
// stops and joins the thread on destruction (if the thread is executing a callback, wait for it to finish first).
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
@@ -23,43 +20,11 @@ namespace details {
|
|||||||
class periodic_worker
|
class periodic_worker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
periodic_worker(std::function<void()> callback_fun, std::chrono::seconds interval)
|
periodic_worker(const std::function<void()> &callback_fun, std::chrono::seconds interval);
|
||||||
{
|
|
||||||
active_ = (interval > std::chrono::seconds::zero());
|
|
||||||
if (!active_)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
worker_thread_ = std::thread([this, callback_fun, interval]() {
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(this->mutex_);
|
|
||||||
if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; }))
|
|
||||||
{
|
|
||||||
return; // active_ == false, so exit this thread
|
|
||||||
}
|
|
||||||
callback_fun();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
periodic_worker(const periodic_worker &) = delete;
|
periodic_worker(const periodic_worker &) = delete;
|
||||||
periodic_worker &operator=(const periodic_worker &) = delete;
|
periodic_worker &operator=(const periodic_worker &) = delete;
|
||||||
|
|
||||||
// stop the worker thread and join it
|
// stop the worker thread and join it
|
||||||
~periodic_worker()
|
~periodic_worker();
|
||||||
{
|
|
||||||
if (worker_thread_.joinable())
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
|
||||||
active_ = false;
|
|
||||||
}
|
|
||||||
cv_.notify_one();
|
|
||||||
worker_thread_.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool active_;
|
bool active_;
|
||||||
@@ -69,3 +34,7 @@ private:
|
|||||||
};
|
};
|
||||||
} // namespace details
|
} // namespace details
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "periodic_worker-inl.h"
|
||||||
|
#endif
|
||||||
|
|||||||
284
include/spdlog/details/registry-inl.h
Normal file
284
include/spdlog/details/registry-inl.h
Normal file
@@ -0,0 +1,284 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include "spdlog/details/registry.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "spdlog/common.h"
|
||||||
|
#include "spdlog/details/periodic_worker.h"
|
||||||
|
#include "spdlog/logger.h"
|
||||||
|
#include "spdlog/details/pattern_formatter.h"
|
||||||
|
|
||||||
|
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||||
|
// support for the default stdout color logger
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "spdlog/sinks/wincolor_sink.h"
|
||||||
|
#else
|
||||||
|
#include "spdlog/sinks/ansicolor_sink.h"
|
||||||
|
#endif
|
||||||
|
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
SPDLOG_INLINE registry::registry()
|
||||||
|
: formatter_(new pattern_formatter())
|
||||||
|
{
|
||||||
|
|
||||||
|
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||||
|
// create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows).
|
||||||
|
#ifdef _WIN32
|
||||||
|
auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>();
|
||||||
|
#else
|
||||||
|
auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char *default_logger_name = "";
|
||||||
|
default_logger_ = std::make_shared<spdlog::logger>(default_logger_name, std::move(color_sink));
|
||||||
|
loggers_[default_logger_name] = default_logger_;
|
||||||
|
|
||||||
|
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||||
|
}
|
||||||
|
SPDLOG_INLINE void registry::register_logger(std::shared_ptr<logger> new_logger)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
register_logger_(std::move(new_logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logger)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
new_logger->set_formatter(formatter_->clone());
|
||||||
|
|
||||||
|
if (err_handler_)
|
||||||
|
{
|
||||||
|
new_logger->set_error_handler(err_handler_);
|
||||||
|
}
|
||||||
|
|
||||||
|
new_logger->set_level(level_);
|
||||||
|
new_logger->flush_on(flush_level_);
|
||||||
|
|
||||||
|
if (backtrace_n_messages_ > 0)
|
||||||
|
{
|
||||||
|
new_logger->enable_backtrace(backtrace_n_messages_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (automatic_registration_)
|
||||||
|
{
|
||||||
|
register_logger_(std::move(new_logger));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::shared_ptr<logger> registry::get(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
auto found = loggers_.find(logger_name);
|
||||||
|
return found == loggers_.end() ? nullptr : found->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::shared_ptr<logger> registry::default_logger()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
return default_logger_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return raw ptr to the default logger.
|
||||||
|
// To be used directly by the spdlog default api (e.g. spdlog::info)
|
||||||
|
// This make the default API faster, but cannot be used concurrently with set_default_logger().
|
||||||
|
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
|
||||||
|
SPDLOG_INLINE logger *registry::get_default_raw()
|
||||||
|
{
|
||||||
|
return default_logger_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set default logger.
|
||||||
|
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
|
||||||
|
SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr<logger> new_default_logger)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
// remove previous default logger from the map
|
||||||
|
if (default_logger_ != nullptr)
|
||||||
|
{
|
||||||
|
loggers_.erase(default_logger_->name());
|
||||||
|
}
|
||||||
|
if (new_default_logger != nullptr)
|
||||||
|
{
|
||||||
|
loggers_[new_default_logger->name()] = new_default_logger;
|
||||||
|
}
|
||||||
|
default_logger_ = std::move(new_default_logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::set_tp(std::shared_ptr<thread_pool> tp)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
||||||
|
tp_ = std::move(tp);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::shared_ptr<thread_pool> registry::get_tp()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
||||||
|
return tp_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set global formatter. Each sink in each logger will get a clone of this object
|
||||||
|
SPDLOG_INLINE void registry::set_formatter(std::unique_ptr<formatter> formatter)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
formatter_ = std::move(formatter);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->set_formatter(formatter_->clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::enable_backtrace(size_t n_messages)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
backtrace_n_messages_ = n_messages;
|
||||||
|
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->enable_backtrace(n_messages);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::disable_backtrace()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
backtrace_n_messages_ = 0;
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->disable_backtrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::set_level(level::level_enum log_level)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->set_level(log_level);
|
||||||
|
}
|
||||||
|
level_ = log_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->flush_on(log_level);
|
||||||
|
}
|
||||||
|
flush_level_ = log_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::flush_every(std::chrono::seconds interval)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
||||||
|
std::function<void()> clbk = std::bind(®istry::flush_all, this);
|
||||||
|
periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::set_error_handler(void (*handler)(const std::string &msg))
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->set_error_handler(handler);
|
||||||
|
}
|
||||||
|
err_handler_ = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
fun(l.second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::flush_all()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
for (auto &l : loggers_)
|
||||||
|
{
|
||||||
|
l.second->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::drop(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
loggers_.erase(logger_name);
|
||||||
|
if (default_logger_ && default_logger_->name() == logger_name)
|
||||||
|
{
|
||||||
|
default_logger_.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::drop_all()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
loggers_.clear();
|
||||||
|
default_logger_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean all resources and threads started by the registry
|
||||||
|
SPDLOG_INLINE void registry::shutdown()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
||||||
|
periodic_flusher_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_all();
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
||||||
|
tp_.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::recursive_mutex ®istry::tp_mutex()
|
||||||
|
{
|
||||||
|
return tp_mutex_;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_regsistration)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
automatic_registration_ = automatic_regsistration;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE registry ®istry::instance()
|
||||||
|
{
|
||||||
|
static registry s_instance;
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
if (loggers_.find(logger_name) != loggers_.end())
|
||||||
|
{
|
||||||
|
SPDLOG_THROW(spdlog_ex("logger with name '" + logger_name + "' already exists"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void registry::register_logger_(std::shared_ptr<logger> new_logger)
|
||||||
|
{
|
||||||
|
auto logger_name = new_logger->name();
|
||||||
|
throw_if_exists_(logger_name);
|
||||||
|
loggers_[logger_name] = std::move(new_logger);
|
||||||
|
}
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
@@ -1,28 +1,28 @@
|
|||||||
//
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Loggers registy of unique name->logger pointer
|
// Loggers registry of unique name->logger pointer
|
||||||
// An attempt to create a logger with an already existing name will be ignored
|
// An attempt to create a logger with an already existing name will result with spdlog_ex exception.
|
||||||
// If user requests a non existing logger, nullptr will be returned
|
// If user requests a non existing logger, nullptr will be returned
|
||||||
// This class is thread safe
|
// This class is thread safe
|
||||||
|
|
||||||
#include "spdlog/common.h"
|
#include "spdlog/common.h"
|
||||||
#include "spdlog/details/periodic_worker.h"
|
|
||||||
#include "spdlog/logger.h"
|
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
|
class logger;
|
||||||
|
|
||||||
namespace details {
|
namespace details {
|
||||||
class thread_pool;
|
class thread_pool;
|
||||||
|
class periodic_worker;
|
||||||
|
|
||||||
class registry
|
class registry
|
||||||
{
|
{
|
||||||
@@ -30,189 +30,80 @@ public:
|
|||||||
registry(const registry &) = delete;
|
registry(const registry &) = delete;
|
||||||
registry &operator=(const registry &) = delete;
|
registry &operator=(const registry &) = delete;
|
||||||
|
|
||||||
void register_logger(std::shared_ptr<logger> new_logger)
|
void register_logger(std::shared_ptr<logger> new_logger);
|
||||||
{
|
void initialize_logger(std::shared_ptr<logger> new_logger);
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
std::shared_ptr<logger> get(const std::string &logger_name);
|
||||||
auto logger_name = new_logger->name();
|
std::shared_ptr<logger> default_logger();
|
||||||
throw_if_exists_(logger_name);
|
|
||||||
loggers_[logger_name] = new_logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
void register_and_init(std::shared_ptr<logger> new_logger)
|
// Return raw ptr to the default logger.
|
||||||
{
|
// To be used directly by the spdlog default api (e.g. spdlog::info)
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
// This make the default API faster, but cannot be used concurrently with set_default_logger().
|
||||||
auto logger_name = new_logger->name();
|
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
|
||||||
throw_if_exists_(logger_name);
|
logger *get_default_raw();
|
||||||
|
|
||||||
// set the global formatter pattern
|
// set default logger.
|
||||||
new_logger->set_formatter(formatter_->clone());
|
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
|
||||||
|
void set_default_logger(std::shared_ptr<logger> new_default_logger);
|
||||||
|
|
||||||
if (err_handler_)
|
void set_tp(std::shared_ptr<thread_pool> tp);
|
||||||
{
|
|
||||||
new_logger->set_error_handler(err_handler_);
|
|
||||||
}
|
|
||||||
|
|
||||||
new_logger->set_level(level_);
|
std::shared_ptr<thread_pool> get_tp();
|
||||||
new_logger->flush_on(flush_level_);
|
|
||||||
|
|
||||||
// add to registry
|
|
||||||
loggers_[logger_name] = new_logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<logger> get(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
auto found = loggers_.find(logger_name);
|
|
||||||
return found == loggers_.end() ? nullptr : found->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_tp(std::shared_ptr<thread_pool> tp)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
|
||||||
tp_ = std::move(tp);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<thread_pool> get_tp()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
|
||||||
return tp_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set global formatter. Each sink in each logger will get a clone of this object
|
// Set global formatter. Each sink in each logger will get a clone of this object
|
||||||
void set_formatter(std::unique_ptr<formatter> formatter)
|
void set_formatter(std::unique_ptr<formatter> formatter);
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
formatter_ = std::move(formatter);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
l.second->set_formatter(formatter_->clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_level(level::level_enum log_level)
|
void enable_backtrace(size_t n_messages);
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
l.second->set_level(log_level);
|
|
||||||
}
|
|
||||||
level_ = log_level;
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_on(level::level_enum log_level)
|
void disable_backtrace();
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
l.second->flush_on(log_level);
|
|
||||||
}
|
|
||||||
flush_level_ = log_level;
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_every(std::chrono::seconds interval)
|
void set_level(level::level_enum log_level);
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
|
||||||
std::function<void()> clbk(std::bind(®istry::flush_all, this));
|
|
||||||
periodic_flusher_.reset(new periodic_worker(clbk, interval));
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_error_handler(log_err_handler handler)
|
void flush_on(level::level_enum log_level);
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
l.second->set_error_handler(handler);
|
|
||||||
}
|
|
||||||
err_handler_ = handler;
|
|
||||||
}
|
|
||||||
|
|
||||||
void apply_all(std::function<void(std::shared_ptr<logger>)> fun)
|
void flush_every(std::chrono::seconds interval);
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
fun(l.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_all()
|
void set_error_handler(void (*handler)(const std::string &msg));
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
for (auto &l : loggers_)
|
|
||||||
{
|
|
||||||
l.second->flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void drop(const std::string &logger_name)
|
void apply_all(const std::function<void(const std::shared_ptr<logger>)> &fun);
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
loggers_.erase(logger_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void drop_all()
|
void flush_all();
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
|
||||||
loggers_.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// clean all reasources and threads started by the registry
|
void drop(const std::string &logger_name);
|
||||||
void shutdown()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
|
||||||
periodic_flusher_.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
drop_all();
|
void drop_all();
|
||||||
|
|
||||||
{
|
// clean all resources and threads started by the registry
|
||||||
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
void shutdown();
|
||||||
tp_.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::recursive_mutex &tp_mutex()
|
std::recursive_mutex &tp_mutex();
|
||||||
{
|
|
||||||
return tp_mutex_;
|
|
||||||
}
|
|
||||||
|
|
||||||
static registry &instance()
|
void set_automatic_registration(bool automatic_regsistration);
|
||||||
{
|
|
||||||
static registry s_instance;
|
static registry &instance();
|
||||||
return s_instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
registry()
|
registry();
|
||||||
: formatter_(new pattern_formatter("%+"))
|
~registry() = default;
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~registry()
|
|
||||||
{
|
|
||||||
/*std::lock_guard<std::mutex> lock(flusher_mutex_);
|
|
||||||
periodic_flusher_.reset();*/
|
|
||||||
}
|
|
||||||
|
|
||||||
void throw_if_exists_(const std::string &logger_name)
|
|
||||||
{
|
|
||||||
if (loggers_.find(logger_name) != loggers_.end())
|
|
||||||
{
|
|
||||||
throw spdlog_ex("logger with name '" + logger_name + "' already exists");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
void throw_if_exists_(const std::string &logger_name);
|
||||||
|
void register_logger_(std::shared_ptr<logger> new_logger);
|
||||||
std::mutex logger_map_mutex_, flusher_mutex_;
|
std::mutex logger_map_mutex_, flusher_mutex_;
|
||||||
std::recursive_mutex tp_mutex_;
|
std::recursive_mutex tp_mutex_;
|
||||||
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
|
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
|
||||||
std::unique_ptr<formatter> formatter_;
|
std::unique_ptr<formatter> formatter_;
|
||||||
level::level_enum level_ = level::info;
|
level::level_enum level_ = level::info;
|
||||||
level::level_enum flush_level_ = level::off;
|
level::level_enum flush_level_ = level::off;
|
||||||
log_err_handler err_handler_;
|
void (*err_handler_)(const std::string &msg);
|
||||||
std::shared_ptr<thread_pool> tp_;
|
std::shared_ptr<thread_pool> tp_;
|
||||||
std::unique_ptr<periodic_worker> periodic_flusher_;
|
std::unique_ptr<periodic_worker> periodic_flusher_;
|
||||||
|
std::shared_ptr<logger> default_logger_;
|
||||||
|
bool automatic_registration_ = true;
|
||||||
|
size_t backtrace_n_messages_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace details
|
} // namespace details
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "registry-inl.h"
|
||||||
|
#endif
|
||||||
|
|||||||
24
include/spdlog/details/synchronous_factory.h
Normal file
24
include/spdlog/details/synchronous_factory.h
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "registry.h"
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
|
||||||
|
// Default logger factory- creates synchronous loggers
|
||||||
|
class logger;
|
||||||
|
|
||||||
|
struct synchronous_factory
|
||||||
|
{
|
||||||
|
template<typename Sink, typename... SinkArgs>
|
||||||
|
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... args)
|
||||||
|
{
|
||||||
|
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
||||||
|
auto new_logger = std::make_shared<spdlog::logger>(std::move(logger_name), std::move(sink));
|
||||||
|
details::registry::instance().initialize_logger(new_logger);
|
||||||
|
return new_logger;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace spdlog
|
||||||
127
include/spdlog/details/thread_pool-inl.h
Normal file
127
include/spdlog/details/thread_pool-inl.h
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include "spdlog/details/thread_pool.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "spdlog/common.h"
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start)
|
||||||
|
: q_(q_max_items)
|
||||||
|
{
|
||||||
|
if (threads_n == 0 || threads_n > 1000)
|
||||||
|
{
|
||||||
|
SPDLOG_THROW(spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid "
|
||||||
|
"range is 1-1000)"));
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < threads_n; i++)
|
||||||
|
{
|
||||||
|
threads_.emplace_back([this, on_thread_start] {
|
||||||
|
on_thread_start();
|
||||||
|
this->thread_pool::worker_loop_();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n)
|
||||||
|
: thread_pool(q_max_items, threads_n, [] {})
|
||||||
|
{}
|
||||||
|
|
||||||
|
// message all threads to terminate gracefully join them
|
||||||
|
SPDLOG_INLINE thread_pool::~thread_pool()
|
||||||
|
{
|
||||||
|
SPDLOG_TRY
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < threads_.size(); i++)
|
||||||
|
{
|
||||||
|
post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads_)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SPDLOG_CATCH_ALL() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPDLOG_INLINE thread_pool::post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy)
|
||||||
|
{
|
||||||
|
async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg);
|
||||||
|
post_async_msg_(std::move(async_m), overflow_policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPDLOG_INLINE thread_pool::post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy)
|
||||||
|
{
|
||||||
|
post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t SPDLOG_INLINE thread_pool::overrun_counter()
|
||||||
|
{
|
||||||
|
return q_.overrun_counter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
|
||||||
|
{
|
||||||
|
if (overflow_policy == async_overflow_policy::block)
|
||||||
|
{
|
||||||
|
q_.enqueue(std::move(new_msg));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
q_.enqueue_nowait(std::move(new_msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SPDLOG_INLINE thread_pool::worker_loop_()
|
||||||
|
{
|
||||||
|
while (process_next_msg_()) {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// process next message in the queue
|
||||||
|
// return true if this thread should still be active (while no terminate msg
|
||||||
|
// was received)
|
||||||
|
bool SPDLOG_INLINE thread_pool::process_next_msg_()
|
||||||
|
{
|
||||||
|
async_msg incoming_async_msg;
|
||||||
|
bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10));
|
||||||
|
if (!dequeued)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (incoming_async_msg.msg_type)
|
||||||
|
{
|
||||||
|
case async_msg_type::log:
|
||||||
|
{
|
||||||
|
incoming_async_msg.worker_ptr->backend_sink_it_(incoming_async_msg);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case async_msg_type::flush:
|
||||||
|
{
|
||||||
|
incoming_async_msg.worker_ptr->backend_flush_();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case async_msg_type::terminate:
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
assert(false && "Unexpected async_msg_type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
@@ -1,6 +1,9 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "spdlog/details/log_msg.h"
|
#include "spdlog/details/log_msg_buffer.h"
|
||||||
#include "spdlog/details/mpmc_blocking_q.h"
|
#include "spdlog/details/mpmc_blocking_q.h"
|
||||||
#include "spdlog/details/os.h"
|
#include "spdlog/details/os.h"
|
||||||
|
|
||||||
@@ -8,8 +11,11 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
|
class async_logger;
|
||||||
|
|
||||||
namespace details {
|
namespace details {
|
||||||
|
|
||||||
using async_logger_ptr = std::shared_ptr<spdlog::async_logger>;
|
using async_logger_ptr = std::shared_ptr<spdlog::async_logger>;
|
||||||
@@ -21,17 +27,12 @@ enum class async_msg_type
|
|||||||
terminate
|
terminate
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#include "spdlog/details/log_msg_buffer.h"
|
||||||
// Async msg to move to/from the queue
|
// Async msg to move to/from the queue
|
||||||
// Movable only. should never be copied
|
// Movable only. should never be copied
|
||||||
struct async_msg
|
struct async_msg : log_msg_buffer
|
||||||
{
|
{
|
||||||
async_msg_type msg_type;
|
async_msg_type msg_type{async_msg_type::log};
|
||||||
level::level_enum level;
|
|
||||||
log_clock::time_point time;
|
|
||||||
size_t thread_id;
|
|
||||||
fmt::basic_memory_buffer<char, 176> raw;
|
|
||||||
|
|
||||||
size_t msg_id;
|
|
||||||
async_logger_ptr worker_ptr;
|
async_logger_ptr worker_ptr;
|
||||||
|
|
||||||
async_msg() = default;
|
async_msg() = default;
|
||||||
@@ -42,66 +43,40 @@ struct async_msg
|
|||||||
|
|
||||||
// support for vs2013 move
|
// support for vs2013 move
|
||||||
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||||
async_msg(async_msg &&other) SPDLOG_NOEXCEPT : msg_type(other.msg_type),
|
async_msg(async_msg &&other)
|
||||||
level(other.level),
|
: log_msg_buffer(std::move(other))
|
||||||
time(other.time),
|
, msg_type(other.msg_type)
|
||||||
thread_id(other.thread_id),
|
, worker_ptr(std::move(other.worker_ptr))
|
||||||
raw(move(other.raw)),
|
{}
|
||||||
msg_id(other.msg_id),
|
|
||||||
worker_ptr(std::move(other.worker_ptr))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
async_msg &operator=(async_msg &&other) SPDLOG_NOEXCEPT
|
async_msg &operator=(async_msg &&other)
|
||||||
{
|
{
|
||||||
|
*static_cast<log_msg_buffer *>(this) = std::move(other);
|
||||||
msg_type = other.msg_type;
|
msg_type = other.msg_type;
|
||||||
level = other.level;
|
|
||||||
time = other.time;
|
|
||||||
thread_id = other.thread_id;
|
|
||||||
raw = std::move(other.raw);
|
|
||||||
msg_id = other.msg_id;
|
|
||||||
worker_ptr = std::move(other.worker_ptr);
|
worker_ptr = std::move(other.worker_ptr);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
#else // (_MSC_VER) && _MSC_VER <= 1800
|
#else // (_MSC_VER) && _MSC_VER <= 1800
|
||||||
async_msg(async_msg &&other) = default;
|
async_msg(async_msg &&) = default;
|
||||||
async_msg &operator=(async_msg &&other) = default;
|
async_msg &operator=(async_msg &&) = default;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// construct from log_msg with given type
|
// construct from log_msg with given type
|
||||||
async_msg(async_logger_ptr &&worker, async_msg_type the_type, details::log_msg &&m)
|
async_msg(async_logger_ptr &&worker, async_msg_type the_type, const details::log_msg &m)
|
||||||
: msg_type(the_type)
|
: log_msg_buffer{m}
|
||||||
, level(m.level)
|
, msg_type{the_type}
|
||||||
, time(m.time)
|
, worker_ptr{std::move(worker)}
|
||||||
, thread_id(m.thread_id)
|
{}
|
||||||
, msg_id(m.msg_id)
|
|
||||||
, worker_ptr(std::forward<async_logger_ptr>(worker))
|
|
||||||
{
|
|
||||||
fmt_helper::append_buf(m.raw, raw);
|
|
||||||
}
|
|
||||||
|
|
||||||
async_msg(async_logger_ptr &&worker, async_msg_type the_type)
|
async_msg(async_logger_ptr &&worker, async_msg_type the_type)
|
||||||
: async_msg(std::forward<async_logger_ptr>(worker), the_type, details::log_msg())
|
: log_msg_buffer{}
|
||||||
{
|
, msg_type{the_type}
|
||||||
}
|
, worker_ptr{std::move(worker)}
|
||||||
|
{}
|
||||||
|
|
||||||
explicit async_msg(async_msg_type the_type)
|
explicit async_msg(async_msg_type the_type)
|
||||||
: async_msg(nullptr, the_type, details::log_msg())
|
: async_msg{nullptr, the_type}
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
// copy into log_msg
|
|
||||||
void to_log_msg(log_msg &msg)
|
|
||||||
{
|
|
||||||
msg.logger_name = &worker_ptr->name();
|
|
||||||
msg.level = level;
|
|
||||||
msg.time = time;
|
|
||||||
msg.thread_id = thread_id;
|
|
||||||
fmt_helper::append_buf(raw, msg.raw);
|
|
||||||
msg.msg_id = msg_id;
|
|
||||||
msg.color_range_start = 0;
|
|
||||||
msg.color_range_end = 0;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class thread_pool
|
class thread_pool
|
||||||
@@ -110,111 +85,36 @@ public:
|
|||||||
using item_type = async_msg;
|
using item_type = async_msg;
|
||||||
using q_type = details::mpmc_blocking_queue<item_type>;
|
using q_type = details::mpmc_blocking_queue<item_type>;
|
||||||
|
|
||||||
thread_pool(size_t q_max_items, size_t threads_n)
|
thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start);
|
||||||
: q_(q_max_items)
|
thread_pool(size_t q_max_items, size_t threads_n);
|
||||||
{
|
|
||||||
// std::cout << "thread_pool() q_size_bytes: " << q_size_bytes <<
|
|
||||||
// "\tthreads_n: " << threads_n << std::endl;
|
|
||||||
if (threads_n == 0 || threads_n > 1000)
|
|
||||||
{
|
|
||||||
throw spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid "
|
|
||||||
"range is 1-1000)");
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < threads_n; i++)
|
|
||||||
{
|
|
||||||
threads_.emplace_back(std::bind(&thread_pool::worker_loop_, this));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// message all threads to terminate gracefully join them
|
// message all threads to terminate gracefully join them
|
||||||
~thread_pool()
|
~thread_pool();
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for (size_t i = 0; i < threads_.size(); i++)
|
|
||||||
{
|
|
||||||
post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &t : threads_)
|
thread_pool(const thread_pool &) = delete;
|
||||||
{
|
thread_pool &operator=(thread_pool &&) = delete;
|
||||||
t.join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void post_log(async_logger_ptr &&worker_ptr, details::log_msg &&msg, async_overflow_policy overflow_policy)
|
void post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy);
|
||||||
{
|
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
|
||||||
async_msg async_m(std::forward<async_logger_ptr>(worker_ptr), async_msg_type::log, std::forward<log_msg>(msg));
|
size_t overrun_counter();
|
||||||
post_async_msg_(std::move(async_m), overflow_policy);
|
|
||||||
}
|
|
||||||
|
|
||||||
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy)
|
|
||||||
{
|
|
||||||
post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
q_type q_;
|
q_type q_;
|
||||||
|
|
||||||
std::vector<std::thread> threads_;
|
std::vector<std::thread> threads_;
|
||||||
|
|
||||||
void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
|
void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy);
|
||||||
{
|
void worker_loop_();
|
||||||
if (overflow_policy == async_overflow_policy::block)
|
|
||||||
{
|
|
||||||
q_.enqueue(std::move(new_msg));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
q_.enqueue_nowait(std::move(new_msg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void worker_loop_()
|
|
||||||
{
|
|
||||||
while (process_next_msg_()) {};
|
|
||||||
}
|
|
||||||
|
|
||||||
// process next message in the queue
|
// process next message in the queue
|
||||||
// return true if this thread should still be active (while no terminate msg
|
// return true if this thread should still be active (while no terminate msg
|
||||||
// was received)
|
// was received)
|
||||||
bool process_next_msg_()
|
bool process_next_msg_();
|
||||||
{
|
|
||||||
async_msg incoming_async_msg;
|
|
||||||
bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10));
|
|
||||||
if (!dequeued)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (incoming_async_msg.msg_type)
|
|
||||||
{
|
|
||||||
case async_msg_type::flush:
|
|
||||||
{
|
|
||||||
incoming_async_msg.worker_ptr->backend_flush_();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
case async_msg_type::terminate:
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
log_msg msg;
|
|
||||||
incoming_async_msg.to_log_msg(msg);
|
|
||||||
incoming_async_msg.worker_ptr->backend_log_(msg);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true; // should not be reached
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace details
|
} // namespace details
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "thread_pool-inl.h"
|
||||||
|
#endif
|
||||||
|
|||||||
175
include/spdlog/fmt/bin_to_hex.h
Normal file
175
include/spdlog/fmt/bin_to_hex.h
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
//
|
||||||
|
// Support for logging binary data as hex
|
||||||
|
// format flags:
|
||||||
|
// {:X} - print in uppercase.
|
||||||
|
// {:s} - don't separate each byte with space.
|
||||||
|
// {:p} - don't print the position on each line start.
|
||||||
|
// {:n} - don't split the output to lines.
|
||||||
|
|
||||||
|
//
|
||||||
|
// Examples:
|
||||||
|
//
|
||||||
|
// std::vector<char> v(200, 0x0b);
|
||||||
|
// logger->info("Some buffer {}", spdlog::to_hex(v));
|
||||||
|
// char buf[128];
|
||||||
|
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
template<typename It>
|
||||||
|
class bytes_range
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
bytes_range(It range_begin, It range_end)
|
||||||
|
: begin_(range_begin)
|
||||||
|
, end_(range_end)
|
||||||
|
{}
|
||||||
|
|
||||||
|
It begin() const
|
||||||
|
{
|
||||||
|
return begin_;
|
||||||
|
}
|
||||||
|
It end() const
|
||||||
|
{
|
||||||
|
return end_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
It begin_, end_;
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
// create a bytes_range that wraps the given container
|
||||||
|
template<typename Container>
|
||||||
|
inline details::bytes_range<typename Container::const_iterator> to_hex(const Container &container)
|
||||||
|
{
|
||||||
|
static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
|
||||||
|
using Iter = typename Container::const_iterator;
|
||||||
|
return details::bytes_range<Iter>(std::begin(container), std::end(container));
|
||||||
|
}
|
||||||
|
|
||||||
|
// create bytes_range from ranges
|
||||||
|
template<typename It>
|
||||||
|
inline details::bytes_range<It> to_hex(const It range_begin, const It range_end)
|
||||||
|
{
|
||||||
|
return details::bytes_range<It>(range_begin, range_end);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
namespace fmt {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct formatter<spdlog::details::bytes_range<T>>
|
||||||
|
{
|
||||||
|
const std::size_t line_size = 100;
|
||||||
|
const char delimiter = ' ';
|
||||||
|
|
||||||
|
bool put_newlines = true;
|
||||||
|
bool put_delimiters = true;
|
||||||
|
bool use_uppercase = false;
|
||||||
|
bool put_positions = true; // position on start of each line
|
||||||
|
|
||||||
|
// parse the format string flags
|
||||||
|
template<typename ParseContext>
|
||||||
|
auto parse(ParseContext &ctx) -> decltype(ctx.begin())
|
||||||
|
{
|
||||||
|
auto it = ctx.begin();
|
||||||
|
while (*it && *it != '}')
|
||||||
|
{
|
||||||
|
switch (*it)
|
||||||
|
{
|
||||||
|
case 'X':
|
||||||
|
use_uppercase = true;
|
||||||
|
break;
|
||||||
|
case 's':
|
||||||
|
put_delimiters = false;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
put_positions = false;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
put_newlines = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
// format the given bytes range as hex
|
||||||
|
template<typename FormatContext, typename Container>
|
||||||
|
auto format(const spdlog::details::bytes_range<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
|
||||||
|
{
|
||||||
|
SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
|
||||||
|
SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
|
||||||
|
const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
|
||||||
|
|
||||||
|
std::size_t pos = 0;
|
||||||
|
std::size_t column = line_size;
|
||||||
|
#if FMT_VERSION < 60000
|
||||||
|
auto inserter = ctx.begin();
|
||||||
|
#else
|
||||||
|
auto inserter = ctx.out();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (auto &item : the_range)
|
||||||
|
{
|
||||||
|
auto ch = static_cast<unsigned char>(item);
|
||||||
|
pos++;
|
||||||
|
|
||||||
|
if (put_newlines && column >= line_size)
|
||||||
|
{
|
||||||
|
column = put_newline(inserter, pos);
|
||||||
|
|
||||||
|
// put first byte without delimiter in front of it
|
||||||
|
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
|
||||||
|
*inserter++ = hex_chars[ch & 0x0f];
|
||||||
|
column += 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (put_delimiters)
|
||||||
|
{
|
||||||
|
*inserter++ = delimiter;
|
||||||
|
++column;
|
||||||
|
}
|
||||||
|
|
||||||
|
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
|
||||||
|
*inserter++ = hex_chars[ch & 0x0f];
|
||||||
|
column += 2;
|
||||||
|
}
|
||||||
|
return inserter;
|
||||||
|
}
|
||||||
|
|
||||||
|
// put newline(and position header)
|
||||||
|
// return the next column
|
||||||
|
template<typename It>
|
||||||
|
std::size_t put_newline(It inserter, std::size_t pos)
|
||||||
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
*inserter++ = '\r';
|
||||||
|
#endif
|
||||||
|
*inserter++ = '\n';
|
||||||
|
|
||||||
|
if (put_positions)
|
||||||
|
{
|
||||||
|
fmt::format_to(inserter, "{:<04X}: ", pos - 1);
|
||||||
|
return 7;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace fmt
|
||||||
@@ -1,23 +1,27 @@
|
|||||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
|
||||||
All rights reserved.
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
The above copyright notice and this permission notice shall be
|
||||||
modification, are permitted provided that the following conditions are met:
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
list of conditions and the following disclaimer.
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
this list of conditions and the following disclaimer in the documentation
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
and/or other materials provided with the distribution.
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
--- Optional exception to the license ---
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
As an exception, if, as a result of your compiling your source code, portions
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
of this Software are embedded into a machine-executable object form of such
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
source code, you may redistribute such embedded portions in such object form
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
without including the above copyright and permission notices.
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
||||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
|
|||||||
829
include/spdlog/fmt/bundled/chrono.h
Normal file
829
include/spdlog/fmt/bundled/chrono.h
Normal file
@@ -0,0 +1,829 @@
|
|||||||
|
// Formatting library for C++ - chrono support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_CHRONO_H_
|
||||||
|
#define FMT_CHRONO_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include "locale.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <ctime>
|
||||||
|
#include <locale>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
// enable safe chrono durations, unless explicitly disabled
|
||||||
|
#ifndef FMT_SAFE_DURATION_CAST
|
||||||
|
# define FMT_SAFE_DURATION_CAST 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if FMT_SAFE_DURATION_CAST
|
||||||
|
# include "safe-duration-cast.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
// Prevents expansion of a preceding token as a function-style macro.
|
||||||
|
// Usage: f FMT_NOMACRO()
|
||||||
|
#define FMT_NOMACRO
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
|
||||||
|
inline null<> localtime_s(...) { return null<>(); }
|
||||||
|
inline null<> gmtime_r(...) { return null<>(); }
|
||||||
|
inline null<> gmtime_s(...) { return null<>(); }
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Thread-safe replacement for std::localtime
|
||||||
|
inline std::tm localtime(std::time_t time) {
|
||||||
|
struct dispatcher {
|
||||||
|
std::time_t time_;
|
||||||
|
std::tm tm_;
|
||||||
|
|
||||||
|
dispatcher(std::time_t t) : time_(t) {}
|
||||||
|
|
||||||
|
bool run() {
|
||||||
|
using namespace fmt::internal;
|
||||||
|
return handle(localtime_r(&time_, &tm_));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handle(std::tm* tm) { return tm != nullptr; }
|
||||||
|
|
||||||
|
bool handle(internal::null<>) {
|
||||||
|
using namespace fmt::internal;
|
||||||
|
return fallback(localtime_s(&tm_, &time_));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fallback(int res) { return res == 0; }
|
||||||
|
|
||||||
|
#if !FMT_MSC_VER
|
||||||
|
bool fallback(internal::null<>) {
|
||||||
|
using namespace fmt::internal;
|
||||||
|
std::tm* tm = std::localtime(&time_);
|
||||||
|
if (tm) tm_ = *tm;
|
||||||
|
return tm != nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
dispatcher lt(time);
|
||||||
|
// Too big time values may be unsupported.
|
||||||
|
if (!lt.run()) FMT_THROW(format_error("time_t value out of range"));
|
||||||
|
return lt.tm_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thread-safe replacement for std::gmtime
|
||||||
|
inline std::tm gmtime(std::time_t time) {
|
||||||
|
struct dispatcher {
|
||||||
|
std::time_t time_;
|
||||||
|
std::tm tm_;
|
||||||
|
|
||||||
|
dispatcher(std::time_t t) : time_(t) {}
|
||||||
|
|
||||||
|
bool run() {
|
||||||
|
using namespace fmt::internal;
|
||||||
|
return handle(gmtime_r(&time_, &tm_));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool handle(std::tm* tm) { return tm != nullptr; }
|
||||||
|
|
||||||
|
bool handle(internal::null<>) {
|
||||||
|
using namespace fmt::internal;
|
||||||
|
return fallback(gmtime_s(&tm_, &time_));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fallback(int res) { return res == 0; }
|
||||||
|
|
||||||
|
#if !FMT_MSC_VER
|
||||||
|
bool fallback(internal::null<>) {
|
||||||
|
std::tm* tm = std::gmtime(&time_);
|
||||||
|
if (tm) tm_ = *tm;
|
||||||
|
return tm != nullptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
dispatcher gt(time);
|
||||||
|
// Too big time values may be unsupported.
|
||||||
|
if (!gt.run()) FMT_THROW(format_error("time_t value out of range"));
|
||||||
|
return gt.tm_;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
inline std::size_t strftime(char* str, std::size_t count, const char* format,
|
||||||
|
const std::tm* time) {
|
||||||
|
return std::strftime(str, count, format, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::size_t strftime(wchar_t* str, std::size_t count,
|
||||||
|
const wchar_t* format, const std::tm* time) {
|
||||||
|
return std::wcsftime(str, count, format, time);
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename Char> struct formatter<std::tm, Char> {
|
||||||
|
template <typename ParseContext>
|
||||||
|
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
auto it = ctx.begin();
|
||||||
|
if (it != ctx.end() && *it == ':') ++it;
|
||||||
|
auto end = it;
|
||||||
|
while (end != ctx.end() && *end != '}') ++end;
|
||||||
|
tm_format.reserve(internal::to_unsigned(end - it + 1));
|
||||||
|
tm_format.append(it, end);
|
||||||
|
tm_format.push_back('\0');
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
|
basic_memory_buffer<Char> buf;
|
||||||
|
std::size_t start = buf.size();
|
||||||
|
for (;;) {
|
||||||
|
std::size_t size = buf.capacity() - start;
|
||||||
|
std::size_t count =
|
||||||
|
internal::strftime(&buf[start], size, &tm_format[0], &tm);
|
||||||
|
if (count != 0) {
|
||||||
|
buf.resize(start + count);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (size >= tm_format.size() * 256) {
|
||||||
|
// If the buffer is 256 times larger than the format string, assume
|
||||||
|
// that `strftime` gives an empty result. There doesn't seem to be a
|
||||||
|
// better way to distinguish the two cases:
|
||||||
|
// https://github.com/fmtlib/fmt/issues/367
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const std::size_t MIN_GROWTH = 10;
|
||||||
|
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
||||||
|
}
|
||||||
|
return std::copy(buf.begin(), buf.end(), ctx.out());
|
||||||
|
}
|
||||||
|
|
||||||
|
basic_memory_buffer<Char> tm_format;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
template <typename Period> FMT_CONSTEXPR const char* get_units() {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::atto>() { return "as"; }
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::femto>() { return "fs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::pico>() { return "ps"; }
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::nano>() { return "ns"; }
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::micro>() { return "µs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::milli>() { return "ms"; }
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::centi>() { return "cs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::deci>() { return "ds"; }
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::ratio<1>>() { return "s"; }
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::deca>() { return "das"; }
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::hecto>() { return "hs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::kilo>() { return "ks"; }
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::mega>() { return "Ms"; }
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::giga>() { return "Gs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::tera>() { return "Ts"; }
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::peta>() { return "Ps"; }
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::exa>() { return "Es"; }
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::ratio<60>>() {
|
||||||
|
return "m";
|
||||||
|
}
|
||||||
|
template <> FMT_CONSTEXPR const char* get_units<std::ratio<3600>>() {
|
||||||
|
return "h";
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class numeric_system {
|
||||||
|
standard,
|
||||||
|
// Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
|
||||||
|
alternative
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parses a put_time-like format string and invokes handler actions.
|
||||||
|
template <typename Char, typename Handler>
|
||||||
|
FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
|
||||||
|
const Char* end,
|
||||||
|
Handler&& handler) {
|
||||||
|
auto ptr = begin;
|
||||||
|
while (ptr != end) {
|
||||||
|
auto c = *ptr;
|
||||||
|
if (c == '}') break;
|
||||||
|
if (c != '%') {
|
||||||
|
++ptr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (begin != ptr) handler.on_text(begin, ptr);
|
||||||
|
++ptr; // consume '%'
|
||||||
|
if (ptr == end) FMT_THROW(format_error("invalid format"));
|
||||||
|
c = *ptr++;
|
||||||
|
switch (c) {
|
||||||
|
case '%':
|
||||||
|
handler.on_text(ptr - 1, ptr);
|
||||||
|
break;
|
||||||
|
case 'n': {
|
||||||
|
const char newline[] = "\n";
|
||||||
|
handler.on_text(newline, newline + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 't': {
|
||||||
|
const char tab[] = "\t";
|
||||||
|
handler.on_text(tab, tab + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Day of the week:
|
||||||
|
case 'a':
|
||||||
|
handler.on_abbr_weekday();
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
handler.on_full_weekday();
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
handler.on_dec0_weekday(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
handler.on_dec1_weekday(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
// Month:
|
||||||
|
case 'b':
|
||||||
|
handler.on_abbr_month();
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
handler.on_full_month();
|
||||||
|
break;
|
||||||
|
// Hour, minute, second:
|
||||||
|
case 'H':
|
||||||
|
handler.on_24_hour(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
handler.on_12_hour(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
handler.on_minute(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
handler.on_second(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
// Other:
|
||||||
|
case 'c':
|
||||||
|
handler.on_datetime(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
handler.on_loc_date(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
handler.on_loc_time(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
handler.on_us_date();
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
handler.on_iso_date();
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
handler.on_12_hour_time();
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
handler.on_24_hour_time();
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
handler.on_iso_time();
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
handler.on_am_pm();
|
||||||
|
break;
|
||||||
|
case 'Q':
|
||||||
|
handler.on_duration_value();
|
||||||
|
break;
|
||||||
|
case 'q':
|
||||||
|
handler.on_duration_unit();
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
handler.on_utc_offset();
|
||||||
|
break;
|
||||||
|
case 'Z':
|
||||||
|
handler.on_tz_name();
|
||||||
|
break;
|
||||||
|
// Alternative representation:
|
||||||
|
case 'E': {
|
||||||
|
if (ptr == end) FMT_THROW(format_error("invalid format"));
|
||||||
|
c = *ptr++;
|
||||||
|
switch (c) {
|
||||||
|
case 'c':
|
||||||
|
handler.on_datetime(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
handler.on_loc_date(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
handler.on_loc_time(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
FMT_THROW(format_error("invalid format"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'O':
|
||||||
|
if (ptr == end) FMT_THROW(format_error("invalid format"));
|
||||||
|
c = *ptr++;
|
||||||
|
switch (c) {
|
||||||
|
case 'w':
|
||||||
|
handler.on_dec0_weekday(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
handler.on_dec1_weekday(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
handler.on_24_hour(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
handler.on_12_hour(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
handler.on_minute(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
handler.on_second(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
FMT_THROW(format_error("invalid format"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
FMT_THROW(format_error("invalid format"));
|
||||||
|
}
|
||||||
|
begin = ptr;
|
||||||
|
}
|
||||||
|
if (begin != ptr) handler.on_text(begin, ptr);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct chrono_format_checker {
|
||||||
|
FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); }
|
||||||
|
|
||||||
|
template <typename Char> void on_text(const Char*, const Char*) {}
|
||||||
|
FMT_NORETURN void on_abbr_weekday() { report_no_date(); }
|
||||||
|
FMT_NORETURN void on_full_weekday() { report_no_date(); }
|
||||||
|
FMT_NORETURN void on_dec0_weekday(numeric_system) { report_no_date(); }
|
||||||
|
FMT_NORETURN void on_dec1_weekday(numeric_system) { report_no_date(); }
|
||||||
|
FMT_NORETURN void on_abbr_month() { report_no_date(); }
|
||||||
|
FMT_NORETURN void on_full_month() { report_no_date(); }
|
||||||
|
void on_24_hour(numeric_system) {}
|
||||||
|
void on_12_hour(numeric_system) {}
|
||||||
|
void on_minute(numeric_system) {}
|
||||||
|
void on_second(numeric_system) {}
|
||||||
|
FMT_NORETURN void on_datetime(numeric_system) { report_no_date(); }
|
||||||
|
FMT_NORETURN void on_loc_date(numeric_system) { report_no_date(); }
|
||||||
|
FMT_NORETURN void on_loc_time(numeric_system) { report_no_date(); }
|
||||||
|
FMT_NORETURN void on_us_date() { report_no_date(); }
|
||||||
|
FMT_NORETURN void on_iso_date() { report_no_date(); }
|
||||||
|
void on_12_hour_time() {}
|
||||||
|
void on_24_hour_time() {}
|
||||||
|
void on_iso_time() {}
|
||||||
|
void on_am_pm() {}
|
||||||
|
void on_duration_value() {}
|
||||||
|
void on_duration_unit() {}
|
||||||
|
FMT_NORETURN void on_utc_offset() { report_no_date(); }
|
||||||
|
FMT_NORETURN void on_tz_name() { report_no_date(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
|
inline bool isnan(T) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||||
|
inline bool isnan(T value) {
|
||||||
|
return std::isnan(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
|
inline bool isfinite(T) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||||
|
inline bool isfinite(T value) {
|
||||||
|
return std::isfinite(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convers value to int and checks that it's in the range [0, upper).
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
|
inline int to_nonnegative_int(T value, int upper) {
|
||||||
|
FMT_ASSERT(value >= 0 && value <= upper, "invalid value");
|
||||||
|
(void)upper;
|
||||||
|
return static_cast<int>(value);
|
||||||
|
}
|
||||||
|
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||||
|
inline int to_nonnegative_int(T value, int upper) {
|
||||||
|
FMT_ASSERT(
|
||||||
|
std::isnan(value) || (value >= 0 && value <= static_cast<T>(upper)),
|
||||||
|
"invalid value");
|
||||||
|
(void)upper;
|
||||||
|
return static_cast<int>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||||
|
inline T mod(T x, int y) {
|
||||||
|
return x % y;
|
||||||
|
}
|
||||||
|
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||||
|
inline T mod(T x, int y) {
|
||||||
|
return std::fmod(x, static_cast<T>(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If T is an integral type, maps T to its unsigned counterpart, otherwise
|
||||||
|
// leaves it unchanged (unlike std::make_unsigned).
|
||||||
|
template <typename T, bool INTEGRAL = std::is_integral<T>::value>
|
||||||
|
struct make_unsigned_or_unchanged {
|
||||||
|
using type = T;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> struct make_unsigned_or_unchanged<T, true> {
|
||||||
|
using type = typename std::make_unsigned<T>::type;
|
||||||
|
};
|
||||||
|
|
||||||
|
#if FMT_SAFE_DURATION_CAST
|
||||||
|
// throwing version of safe_duration_cast
|
||||||
|
template <typename To, typename FromRep, typename FromPeriod>
|
||||||
|
To fmt_safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from) {
|
||||||
|
int ec;
|
||||||
|
To to = safe_duration_cast::safe_duration_cast<To>(from, ec);
|
||||||
|
if (ec) FMT_THROW(format_error("cannot format duration"));
|
||||||
|
return to;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename Rep, typename Period,
|
||||||
|
FMT_ENABLE_IF(std::is_integral<Rep>::value)>
|
||||||
|
inline std::chrono::duration<Rep, std::milli> get_milliseconds(
|
||||||
|
std::chrono::duration<Rep, Period> d) {
|
||||||
|
// this may overflow and/or the result may not fit in the
|
||||||
|
// target type.
|
||||||
|
#if FMT_SAFE_DURATION_CAST
|
||||||
|
using CommonSecondsType =
|
||||||
|
typename std::common_type<decltype(d), std::chrono::seconds>::type;
|
||||||
|
const auto d_as_common = fmt_safe_duration_cast<CommonSecondsType>(d);
|
||||||
|
const auto d_as_whole_seconds =
|
||||||
|
fmt_safe_duration_cast<std::chrono::seconds>(d_as_common);
|
||||||
|
// this conversion should be nonproblematic
|
||||||
|
const auto diff = d_as_common - d_as_whole_seconds;
|
||||||
|
const auto ms =
|
||||||
|
fmt_safe_duration_cast<std::chrono::duration<Rep, std::milli>>(diff);
|
||||||
|
return ms;
|
||||||
|
#else
|
||||||
|
auto s = std::chrono::duration_cast<std::chrono::seconds>(d);
|
||||||
|
return std::chrono::duration_cast<std::chrono::milliseconds>(d - s);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Rep, typename Period,
|
||||||
|
FMT_ENABLE_IF(std::is_floating_point<Rep>::value)>
|
||||||
|
inline std::chrono::duration<Rep, std::milli> get_milliseconds(
|
||||||
|
std::chrono::duration<Rep, Period> d) {
|
||||||
|
using common_type = typename std::common_type<Rep, std::intmax_t>::type;
|
||||||
|
auto ms = mod(d.count() * static_cast<common_type>(Period::num) /
|
||||||
|
static_cast<common_type>(Period::den) * 1000,
|
||||||
|
1000);
|
||||||
|
return std::chrono::duration<Rep, std::milli>(static_cast<Rep>(ms));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Rep, typename OutputIt>
|
||||||
|
OutputIt format_chrono_duration_value(OutputIt out, Rep val, int precision) {
|
||||||
|
if (precision >= 0) return format_to(out, "{:.{}f}", val, precision);
|
||||||
|
return format_to(out, std::is_floating_point<Rep>::value ? "{:g}" : "{}",
|
||||||
|
val);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Period, typename OutputIt>
|
||||||
|
static OutputIt format_chrono_duration_unit(OutputIt out) {
|
||||||
|
if (const char* unit = get_units<Period>()) return format_to(out, "{}", unit);
|
||||||
|
if (Period::den == 1) return format_to(out, "[{}]s", Period::num);
|
||||||
|
return format_to(out, "[{}/{}]s", Period::num, Period::den);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext, typename OutputIt, typename Rep,
|
||||||
|
typename Period>
|
||||||
|
struct chrono_formatter {
|
||||||
|
FormatContext& context;
|
||||||
|
OutputIt out;
|
||||||
|
int precision;
|
||||||
|
// rep is unsigned to avoid overflow.
|
||||||
|
using rep =
|
||||||
|
conditional_t<std::is_integral<Rep>::value && sizeof(Rep) < sizeof(int),
|
||||||
|
unsigned, typename make_unsigned_or_unchanged<Rep>::type>;
|
||||||
|
rep val;
|
||||||
|
using seconds = std::chrono::duration<rep>;
|
||||||
|
seconds s;
|
||||||
|
using milliseconds = std::chrono::duration<rep, std::milli>;
|
||||||
|
bool negative;
|
||||||
|
|
||||||
|
using char_type = typename FormatContext::char_type;
|
||||||
|
|
||||||
|
explicit chrono_formatter(FormatContext& ctx, OutputIt o,
|
||||||
|
std::chrono::duration<Rep, Period> d)
|
||||||
|
: context(ctx), out(o), val(d.count()), negative(false) {
|
||||||
|
if (d.count() < 0) {
|
||||||
|
val = 0 - val;
|
||||||
|
negative = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this may overflow and/or the result may not fit in the
|
||||||
|
// target type.
|
||||||
|
#if FMT_SAFE_DURATION_CAST
|
||||||
|
// might need checked conversion (rep!=Rep)
|
||||||
|
auto tmpval = std::chrono::duration<rep, Period>(val);
|
||||||
|
s = fmt_safe_duration_cast<seconds>(tmpval);
|
||||||
|
#else
|
||||||
|
s = std::chrono::duration_cast<seconds>(
|
||||||
|
std::chrono::duration<rep, Period>(val));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns true if nan or inf, writes to out.
|
||||||
|
bool handle_nan_inf() {
|
||||||
|
if (isfinite(val)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (isnan(val)) {
|
||||||
|
write_nan();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// must be +-inf
|
||||||
|
if (val > 0) {
|
||||||
|
write_pinf();
|
||||||
|
} else {
|
||||||
|
write_ninf();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rep hour() const { return static_cast<Rep>(mod((s.count() / 3600), 24)); }
|
||||||
|
|
||||||
|
Rep hour12() const {
|
||||||
|
Rep hour = static_cast<Rep>(mod((s.count() / 3600), 12));
|
||||||
|
return hour <= 0 ? 12 : hour;
|
||||||
|
}
|
||||||
|
|
||||||
|
Rep minute() const { return static_cast<Rep>(mod((s.count() / 60), 60)); }
|
||||||
|
Rep second() const { return static_cast<Rep>(mod(s.count(), 60)); }
|
||||||
|
|
||||||
|
std::tm time() const {
|
||||||
|
auto time = std::tm();
|
||||||
|
time.tm_hour = to_nonnegative_int(hour(), 24);
|
||||||
|
time.tm_min = to_nonnegative_int(minute(), 60);
|
||||||
|
time.tm_sec = to_nonnegative_int(second(), 60);
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_sign() {
|
||||||
|
if (negative) {
|
||||||
|
*out++ = '-';
|
||||||
|
negative = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(Rep value, int width) {
|
||||||
|
write_sign();
|
||||||
|
if (isnan(value)) return write_nan();
|
||||||
|
uint32_or_64_t<int> n = to_unsigned(
|
||||||
|
to_nonnegative_int(value, (std::numeric_limits<int>::max)()));
|
||||||
|
int num_digits = internal::count_digits(n);
|
||||||
|
if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
|
||||||
|
out = format_decimal<char_type>(out, n, num_digits);
|
||||||
|
}
|
||||||
|
|
||||||
|
void write_nan() { std::copy_n("nan", 3, out); }
|
||||||
|
void write_pinf() { std::copy_n("inf", 3, out); }
|
||||||
|
void write_ninf() { std::copy_n("-inf", 4, out); }
|
||||||
|
|
||||||
|
void format_localized(const tm& time, const char* format) {
|
||||||
|
if (isnan(val)) return write_nan();
|
||||||
|
auto locale = context.locale().template get<std::locale>();
|
||||||
|
auto& facet = std::use_facet<std::time_put<char_type>>(locale);
|
||||||
|
std::basic_ostringstream<char_type> os;
|
||||||
|
os.imbue(locale);
|
||||||
|
facet.put(os, os, ' ', &time, format, format + std::strlen(format));
|
||||||
|
auto str = os.str();
|
||||||
|
std::copy(str.begin(), str.end(), out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_text(const char_type* begin, const char_type* end) {
|
||||||
|
std::copy(begin, end, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are not implemented because durations don't have date information.
|
||||||
|
void on_abbr_weekday() {}
|
||||||
|
void on_full_weekday() {}
|
||||||
|
void on_dec0_weekday(numeric_system) {}
|
||||||
|
void on_dec1_weekday(numeric_system) {}
|
||||||
|
void on_abbr_month() {}
|
||||||
|
void on_full_month() {}
|
||||||
|
void on_datetime(numeric_system) {}
|
||||||
|
void on_loc_date(numeric_system) {}
|
||||||
|
void on_loc_time(numeric_system) {}
|
||||||
|
void on_us_date() {}
|
||||||
|
void on_iso_date() {}
|
||||||
|
void on_utc_offset() {}
|
||||||
|
void on_tz_name() {}
|
||||||
|
|
||||||
|
void on_24_hour(numeric_system ns) {
|
||||||
|
if (handle_nan_inf()) return;
|
||||||
|
|
||||||
|
if (ns == numeric_system::standard) return write(hour(), 2);
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_hour = to_nonnegative_int(hour(), 24);
|
||||||
|
format_localized(time, "%OH");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_12_hour(numeric_system ns) {
|
||||||
|
if (handle_nan_inf()) return;
|
||||||
|
|
||||||
|
if (ns == numeric_system::standard) return write(hour12(), 2);
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_hour = to_nonnegative_int(hour12(), 12);
|
||||||
|
format_localized(time, "%OI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_minute(numeric_system ns) {
|
||||||
|
if (handle_nan_inf()) return;
|
||||||
|
|
||||||
|
if (ns == numeric_system::standard) return write(minute(), 2);
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_min = to_nonnegative_int(minute(), 60);
|
||||||
|
format_localized(time, "%OM");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_second(numeric_system ns) {
|
||||||
|
if (handle_nan_inf()) return;
|
||||||
|
|
||||||
|
if (ns == numeric_system::standard) {
|
||||||
|
write(second(), 2);
|
||||||
|
#if FMT_SAFE_DURATION_CAST
|
||||||
|
// convert rep->Rep
|
||||||
|
using duration_rep = std::chrono::duration<rep, Period>;
|
||||||
|
using duration_Rep = std::chrono::duration<Rep, Period>;
|
||||||
|
auto tmpval = fmt_safe_duration_cast<duration_Rep>(duration_rep{val});
|
||||||
|
#else
|
||||||
|
auto tmpval = std::chrono::duration<Rep, Period>(val);
|
||||||
|
#endif
|
||||||
|
auto ms = get_milliseconds(tmpval);
|
||||||
|
if (ms != std::chrono::milliseconds(0)) {
|
||||||
|
*out++ = '.';
|
||||||
|
write(ms.count(), 3);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_sec = to_nonnegative_int(second(), 60);
|
||||||
|
format_localized(time, "%OS");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_12_hour_time() {
|
||||||
|
if (handle_nan_inf()) return;
|
||||||
|
|
||||||
|
format_localized(time(), "%r");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_24_hour_time() {
|
||||||
|
if (handle_nan_inf()) {
|
||||||
|
*out++ = ':';
|
||||||
|
handle_nan_inf();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
write(hour(), 2);
|
||||||
|
*out++ = ':';
|
||||||
|
write(minute(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_iso_time() {
|
||||||
|
on_24_hour_time();
|
||||||
|
*out++ = ':';
|
||||||
|
if (handle_nan_inf()) return;
|
||||||
|
write(second(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_am_pm() {
|
||||||
|
if (handle_nan_inf()) return;
|
||||||
|
format_localized(time(), "%p");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_duration_value() {
|
||||||
|
if (handle_nan_inf()) return;
|
||||||
|
write_sign();
|
||||||
|
out = format_chrono_duration_value(out, val, precision);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_duration_unit() { out = format_chrono_duration_unit<Period>(out); }
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename Rep, typename Period, typename Char>
|
||||||
|
struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
||||||
|
private:
|
||||||
|
basic_format_specs<Char> specs;
|
||||||
|
int precision;
|
||||||
|
using arg_ref_type = internal::arg_ref<Char>;
|
||||||
|
arg_ref_type width_ref;
|
||||||
|
arg_ref_type precision_ref;
|
||||||
|
mutable basic_string_view<Char> format_str;
|
||||||
|
using duration = std::chrono::duration<Rep, Period>;
|
||||||
|
|
||||||
|
struct spec_handler {
|
||||||
|
formatter& f;
|
||||||
|
basic_parse_context<Char>& context;
|
||||||
|
basic_string_view<Char> format_str;
|
||||||
|
|
||||||
|
template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
|
||||||
|
context.check_arg_id(arg_id);
|
||||||
|
return arg_ref_type(arg_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR arg_ref_type make_arg_ref(basic_string_view<Char> arg_id) {
|
||||||
|
context.check_arg_id(arg_id);
|
||||||
|
const auto str_val = internal::string_view_metadata(format_str, arg_id);
|
||||||
|
return arg_ref_type(str_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
|
||||||
|
return arg_ref_type(context.next_arg_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
|
||||||
|
void on_fill(Char fill) { f.specs.fill[0] = fill; }
|
||||||
|
void on_align(align_t align) { f.specs.align = align; }
|
||||||
|
void on_width(unsigned width) { f.specs.width = width; }
|
||||||
|
void on_precision(unsigned precision) { f.precision = precision; }
|
||||||
|
void end_precision() {}
|
||||||
|
|
||||||
|
template <typename Id> void on_dynamic_width(Id arg_id) {
|
||||||
|
f.width_ref = make_arg_ref(arg_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Id> void on_dynamic_precision(Id arg_id) {
|
||||||
|
f.precision_ref = make_arg_ref(arg_id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using iterator = typename basic_parse_context<Char>::iterator;
|
||||||
|
struct parse_range {
|
||||||
|
iterator begin;
|
||||||
|
iterator end;
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_CONSTEXPR parse_range do_parse(basic_parse_context<Char>& ctx) {
|
||||||
|
auto begin = ctx.begin(), end = ctx.end();
|
||||||
|
if (begin == end || *begin == '}') return {begin, begin};
|
||||||
|
spec_handler handler{*this, ctx, format_str};
|
||||||
|
begin = internal::parse_align(begin, end, handler);
|
||||||
|
if (begin == end) return {begin, begin};
|
||||||
|
begin = internal::parse_width(begin, end, handler);
|
||||||
|
if (begin == end) return {begin, begin};
|
||||||
|
if (*begin == '.') {
|
||||||
|
if (std::is_floating_point<Rep>::value)
|
||||||
|
begin = internal::parse_precision(begin, end, handler);
|
||||||
|
else
|
||||||
|
handler.on_error("precision not allowed for this argument type");
|
||||||
|
}
|
||||||
|
end = parse_chrono_format(begin, end, internal::chrono_format_checker());
|
||||||
|
return {begin, end};
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
formatter() : precision(-1) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR auto parse(basic_parse_context<Char>& ctx)
|
||||||
|
-> decltype(ctx.begin()) {
|
||||||
|
auto range = do_parse(ctx);
|
||||||
|
format_str = basic_string_view<Char>(
|
||||||
|
&*range.begin, internal::to_unsigned(range.end - range.begin));
|
||||||
|
return range.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const duration& d, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
|
auto begin = format_str.begin(), end = format_str.end();
|
||||||
|
// As a possible future optimization, we could avoid extra copying if width
|
||||||
|
// is not specified.
|
||||||
|
basic_memory_buffer<Char> buf;
|
||||||
|
auto out = std::back_inserter(buf);
|
||||||
|
using range = internal::output_range<decltype(ctx.out()), Char>;
|
||||||
|
internal::basic_writer<range> w(range(ctx.out()));
|
||||||
|
internal::handle_dynamic_spec<internal::width_checker>(
|
||||||
|
specs.width, width_ref, ctx, format_str.begin());
|
||||||
|
internal::handle_dynamic_spec<internal::precision_checker>(
|
||||||
|
precision, precision_ref, ctx, format_str.begin());
|
||||||
|
if (begin == end || *begin == '}') {
|
||||||
|
out = internal::format_chrono_duration_value(out, d.count(), precision);
|
||||||
|
internal::format_chrono_duration_unit<Period>(out);
|
||||||
|
} else {
|
||||||
|
internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
|
||||||
|
ctx, out, d);
|
||||||
|
f.precision = precision;
|
||||||
|
parse_chrono_format(begin, end, f);
|
||||||
|
}
|
||||||
|
w.write(buf.data(), buf.size(), specs);
|
||||||
|
return w.out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_CHRONO_H_
|
||||||
585
include/spdlog/fmt/bundled/color.h
Normal file
585
include/spdlog/fmt/bundled/color.h
Normal file
@@ -0,0 +1,585 @@
|
|||||||
|
// Formatting library for C++ - color support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_COLOR_H_
|
||||||
|
#define FMT_COLOR_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
enum class color : uint32_t {
|
||||||
|
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||||
|
antique_white = 0xFAEBD7, // rgb(250,235,215)
|
||||||
|
aqua = 0x00FFFF, // rgb(0,255,255)
|
||||||
|
aquamarine = 0x7FFFD4, // rgb(127,255,212)
|
||||||
|
azure = 0xF0FFFF, // rgb(240,255,255)
|
||||||
|
beige = 0xF5F5DC, // rgb(245,245,220)
|
||||||
|
bisque = 0xFFE4C4, // rgb(255,228,196)
|
||||||
|
black = 0x000000, // rgb(0,0,0)
|
||||||
|
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
|
||||||
|
blue = 0x0000FF, // rgb(0,0,255)
|
||||||
|
blue_violet = 0x8A2BE2, // rgb(138,43,226)
|
||||||
|
brown = 0xA52A2A, // rgb(165,42,42)
|
||||||
|
burly_wood = 0xDEB887, // rgb(222,184,135)
|
||||||
|
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
|
||||||
|
chartreuse = 0x7FFF00, // rgb(127,255,0)
|
||||||
|
chocolate = 0xD2691E, // rgb(210,105,30)
|
||||||
|
coral = 0xFF7F50, // rgb(255,127,80)
|
||||||
|
cornflower_blue = 0x6495ED, // rgb(100,149,237)
|
||||||
|
cornsilk = 0xFFF8DC, // rgb(255,248,220)
|
||||||
|
crimson = 0xDC143C, // rgb(220,20,60)
|
||||||
|
cyan = 0x00FFFF, // rgb(0,255,255)
|
||||||
|
dark_blue = 0x00008B, // rgb(0,0,139)
|
||||||
|
dark_cyan = 0x008B8B, // rgb(0,139,139)
|
||||||
|
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
|
||||||
|
dark_gray = 0xA9A9A9, // rgb(169,169,169)
|
||||||
|
dark_green = 0x006400, // rgb(0,100,0)
|
||||||
|
dark_khaki = 0xBDB76B, // rgb(189,183,107)
|
||||||
|
dark_magenta = 0x8B008B, // rgb(139,0,139)
|
||||||
|
dark_olive_green = 0x556B2F, // rgb(85,107,47)
|
||||||
|
dark_orange = 0xFF8C00, // rgb(255,140,0)
|
||||||
|
dark_orchid = 0x9932CC, // rgb(153,50,204)
|
||||||
|
dark_red = 0x8B0000, // rgb(139,0,0)
|
||||||
|
dark_salmon = 0xE9967A, // rgb(233,150,122)
|
||||||
|
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
|
||||||
|
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
|
||||||
|
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
|
||||||
|
dark_turquoise = 0x00CED1, // rgb(0,206,209)
|
||||||
|
dark_violet = 0x9400D3, // rgb(148,0,211)
|
||||||
|
deep_pink = 0xFF1493, // rgb(255,20,147)
|
||||||
|
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
|
||||||
|
dim_gray = 0x696969, // rgb(105,105,105)
|
||||||
|
dodger_blue = 0x1E90FF, // rgb(30,144,255)
|
||||||
|
fire_brick = 0xB22222, // rgb(178,34,34)
|
||||||
|
floral_white = 0xFFFAF0, // rgb(255,250,240)
|
||||||
|
forest_green = 0x228B22, // rgb(34,139,34)
|
||||||
|
fuchsia = 0xFF00FF, // rgb(255,0,255)
|
||||||
|
gainsboro = 0xDCDCDC, // rgb(220,220,220)
|
||||||
|
ghost_white = 0xF8F8FF, // rgb(248,248,255)
|
||||||
|
gold = 0xFFD700, // rgb(255,215,0)
|
||||||
|
golden_rod = 0xDAA520, // rgb(218,165,32)
|
||||||
|
gray = 0x808080, // rgb(128,128,128)
|
||||||
|
green = 0x008000, // rgb(0,128,0)
|
||||||
|
green_yellow = 0xADFF2F, // rgb(173,255,47)
|
||||||
|
honey_dew = 0xF0FFF0, // rgb(240,255,240)
|
||||||
|
hot_pink = 0xFF69B4, // rgb(255,105,180)
|
||||||
|
indian_red = 0xCD5C5C, // rgb(205,92,92)
|
||||||
|
indigo = 0x4B0082, // rgb(75,0,130)
|
||||||
|
ivory = 0xFFFFF0, // rgb(255,255,240)
|
||||||
|
khaki = 0xF0E68C, // rgb(240,230,140)
|
||||||
|
lavender = 0xE6E6FA, // rgb(230,230,250)
|
||||||
|
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
|
||||||
|
lawn_green = 0x7CFC00, // rgb(124,252,0)
|
||||||
|
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
|
||||||
|
light_blue = 0xADD8E6, // rgb(173,216,230)
|
||||||
|
light_coral = 0xF08080, // rgb(240,128,128)
|
||||||
|
light_cyan = 0xE0FFFF, // rgb(224,255,255)
|
||||||
|
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
|
||||||
|
light_gray = 0xD3D3D3, // rgb(211,211,211)
|
||||||
|
light_green = 0x90EE90, // rgb(144,238,144)
|
||||||
|
light_pink = 0xFFB6C1, // rgb(255,182,193)
|
||||||
|
light_salmon = 0xFFA07A, // rgb(255,160,122)
|
||||||
|
light_sea_green = 0x20B2AA, // rgb(32,178,170)
|
||||||
|
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
|
||||||
|
light_slate_gray = 0x778899, // rgb(119,136,153)
|
||||||
|
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
|
||||||
|
light_yellow = 0xFFFFE0, // rgb(255,255,224)
|
||||||
|
lime = 0x00FF00, // rgb(0,255,0)
|
||||||
|
lime_green = 0x32CD32, // rgb(50,205,50)
|
||||||
|
linen = 0xFAF0E6, // rgb(250,240,230)
|
||||||
|
magenta = 0xFF00FF, // rgb(255,0,255)
|
||||||
|
maroon = 0x800000, // rgb(128,0,0)
|
||||||
|
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
|
||||||
|
medium_blue = 0x0000CD, // rgb(0,0,205)
|
||||||
|
medium_orchid = 0xBA55D3, // rgb(186,85,211)
|
||||||
|
medium_purple = 0x9370DB, // rgb(147,112,219)
|
||||||
|
medium_sea_green = 0x3CB371, // rgb(60,179,113)
|
||||||
|
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
|
||||||
|
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
|
||||||
|
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
|
||||||
|
medium_violet_red = 0xC71585, // rgb(199,21,133)
|
||||||
|
midnight_blue = 0x191970, // rgb(25,25,112)
|
||||||
|
mint_cream = 0xF5FFFA, // rgb(245,255,250)
|
||||||
|
misty_rose = 0xFFE4E1, // rgb(255,228,225)
|
||||||
|
moccasin = 0xFFE4B5, // rgb(255,228,181)
|
||||||
|
navajo_white = 0xFFDEAD, // rgb(255,222,173)
|
||||||
|
navy = 0x000080, // rgb(0,0,128)
|
||||||
|
old_lace = 0xFDF5E6, // rgb(253,245,230)
|
||||||
|
olive = 0x808000, // rgb(128,128,0)
|
||||||
|
olive_drab = 0x6B8E23, // rgb(107,142,35)
|
||||||
|
orange = 0xFFA500, // rgb(255,165,0)
|
||||||
|
orange_red = 0xFF4500, // rgb(255,69,0)
|
||||||
|
orchid = 0xDA70D6, // rgb(218,112,214)
|
||||||
|
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
|
||||||
|
pale_green = 0x98FB98, // rgb(152,251,152)
|
||||||
|
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
|
||||||
|
pale_violet_red = 0xDB7093, // rgb(219,112,147)
|
||||||
|
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
|
||||||
|
peach_puff = 0xFFDAB9, // rgb(255,218,185)
|
||||||
|
peru = 0xCD853F, // rgb(205,133,63)
|
||||||
|
pink = 0xFFC0CB, // rgb(255,192,203)
|
||||||
|
plum = 0xDDA0DD, // rgb(221,160,221)
|
||||||
|
powder_blue = 0xB0E0E6, // rgb(176,224,230)
|
||||||
|
purple = 0x800080, // rgb(128,0,128)
|
||||||
|
rebecca_purple = 0x663399, // rgb(102,51,153)
|
||||||
|
red = 0xFF0000, // rgb(255,0,0)
|
||||||
|
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
|
||||||
|
royal_blue = 0x4169E1, // rgb(65,105,225)
|
||||||
|
saddle_brown = 0x8B4513, // rgb(139,69,19)
|
||||||
|
salmon = 0xFA8072, // rgb(250,128,114)
|
||||||
|
sandy_brown = 0xF4A460, // rgb(244,164,96)
|
||||||
|
sea_green = 0x2E8B57, // rgb(46,139,87)
|
||||||
|
sea_shell = 0xFFF5EE, // rgb(255,245,238)
|
||||||
|
sienna = 0xA0522D, // rgb(160,82,45)
|
||||||
|
silver = 0xC0C0C0, // rgb(192,192,192)
|
||||||
|
sky_blue = 0x87CEEB, // rgb(135,206,235)
|
||||||
|
slate_blue = 0x6A5ACD, // rgb(106,90,205)
|
||||||
|
slate_gray = 0x708090, // rgb(112,128,144)
|
||||||
|
snow = 0xFFFAFA, // rgb(255,250,250)
|
||||||
|
spring_green = 0x00FF7F, // rgb(0,255,127)
|
||||||
|
steel_blue = 0x4682B4, // rgb(70,130,180)
|
||||||
|
tan = 0xD2B48C, // rgb(210,180,140)
|
||||||
|
teal = 0x008080, // rgb(0,128,128)
|
||||||
|
thistle = 0xD8BFD8, // rgb(216,191,216)
|
||||||
|
tomato = 0xFF6347, // rgb(255,99,71)
|
||||||
|
turquoise = 0x40E0D0, // rgb(64,224,208)
|
||||||
|
violet = 0xEE82EE, // rgb(238,130,238)
|
||||||
|
wheat = 0xF5DEB3, // rgb(245,222,179)
|
||||||
|
white = 0xFFFFFF, // rgb(255,255,255)
|
||||||
|
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
||||||
|
yellow = 0xFFFF00, // rgb(255,255,0)
|
||||||
|
yellow_green = 0x9ACD32 // rgb(154,205,50)
|
||||||
|
}; // enum class color
|
||||||
|
|
||||||
|
enum class terminal_color : uint8_t {
|
||||||
|
black = 30,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
yellow,
|
||||||
|
blue,
|
||||||
|
magenta,
|
||||||
|
cyan,
|
||||||
|
white,
|
||||||
|
bright_black = 90,
|
||||||
|
bright_red,
|
||||||
|
bright_green,
|
||||||
|
bright_yellow,
|
||||||
|
bright_blue,
|
||||||
|
bright_magenta,
|
||||||
|
bright_cyan,
|
||||||
|
bright_white
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class emphasis : uint8_t {
|
||||||
|
bold = 1,
|
||||||
|
italic = 1 << 1,
|
||||||
|
underline = 1 << 2,
|
||||||
|
strikethrough = 1 << 3
|
||||||
|
};
|
||||||
|
|
||||||
|
// rgb is a struct for red, green and blue colors.
|
||||||
|
// Using the name "rgb" makes some editors show the color in a tooltip.
|
||||||
|
struct rgb {
|
||||||
|
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
|
||||||
|
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
|
||||||
|
FMT_CONSTEXPR rgb(uint32_t hex)
|
||||||
|
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
|
||||||
|
FMT_CONSTEXPR rgb(color hex)
|
||||||
|
: r((uint32_t(hex) >> 16) & 0xFF),
|
||||||
|
g((uint32_t(hex) >> 8) & 0xFF),
|
||||||
|
b(uint32_t(hex) & 0xFF) {}
|
||||||
|
uint8_t r;
|
||||||
|
uint8_t g;
|
||||||
|
uint8_t b;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// color is a struct of either a rgb color or a terminal color.
|
||||||
|
struct color_type {
|
||||||
|
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
|
||||||
|
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
|
||||||
|
value{} {
|
||||||
|
value.rgb_color = static_cast<uint32_t>(rgb_color);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} {
|
||||||
|
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
|
||||||
|
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
|
||||||
|
value{} {
|
||||||
|
value.term_color = static_cast<uint8_t>(term_color);
|
||||||
|
}
|
||||||
|
bool is_rgb;
|
||||||
|
union color_union {
|
||||||
|
uint8_t term_color;
|
||||||
|
uint32_t rgb_color;
|
||||||
|
} value;
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Experimental text formatting support.
|
||||||
|
class text_style {
|
||||||
|
public:
|
||||||
|
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
|
||||||
|
: set_foreground_color(),
|
||||||
|
set_background_color(),
|
||||||
|
ems(em) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
|
||||||
|
if (!set_foreground_color) {
|
||||||
|
set_foreground_color = rhs.set_foreground_color;
|
||||||
|
foreground_color = rhs.foreground_color;
|
||||||
|
} else if (rhs.set_foreground_color) {
|
||||||
|
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||||
|
FMT_THROW(format_error("can't OR a terminal color"));
|
||||||
|
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_background_color) {
|
||||||
|
set_background_color = rhs.set_background_color;
|
||||||
|
background_color = rhs.background_color;
|
||||||
|
} else if (rhs.set_background_color) {
|
||||||
|
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||||
|
FMT_THROW(format_error("can't OR a terminal color"));
|
||||||
|
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
|
||||||
|
static_cast<uint8_t>(rhs.ems));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
|
||||||
|
const text_style& rhs) {
|
||||||
|
return lhs |= rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) {
|
||||||
|
if (!set_foreground_color) {
|
||||||
|
set_foreground_color = rhs.set_foreground_color;
|
||||||
|
foreground_color = rhs.foreground_color;
|
||||||
|
} else if (rhs.set_foreground_color) {
|
||||||
|
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||||
|
FMT_THROW(format_error("can't AND a terminal color"));
|
||||||
|
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_background_color) {
|
||||||
|
set_background_color = rhs.set_background_color;
|
||||||
|
background_color = rhs.background_color;
|
||||||
|
} else if (rhs.set_background_color) {
|
||||||
|
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||||
|
FMT_THROW(format_error("can't AND a terminal color"));
|
||||||
|
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
|
||||||
|
static_cast<uint8_t>(rhs.ems));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR text_style operator&(text_style lhs,
|
||||||
|
const text_style& rhs) {
|
||||||
|
return lhs &= rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
|
||||||
|
return set_foreground_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
|
||||||
|
return set_background_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
|
||||||
|
return static_cast<uint8_t>(ems) != 0;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
|
||||||
|
assert(has_foreground() && "no foreground specified for this style");
|
||||||
|
return foreground_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
|
||||||
|
assert(has_background() && "no background specified for this style");
|
||||||
|
return background_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
|
||||||
|
assert(has_emphasis() && "no emphasis specified for this style");
|
||||||
|
return ems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||||
|
internal::color_type text_color) FMT_NOEXCEPT
|
||||||
|
: set_foreground_color(),
|
||||||
|
set_background_color(),
|
||||||
|
ems() {
|
||||||
|
if (is_foreground) {
|
||||||
|
foreground_color = text_color;
|
||||||
|
set_foreground_color = true;
|
||||||
|
} else {
|
||||||
|
background_color = text_color;
|
||||||
|
set_background_color = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
internal::color_type foreground_color;
|
||||||
|
internal::color_type background_color;
|
||||||
|
bool set_foreground_color;
|
||||||
|
bool set_background_color;
|
||||||
|
emphasis ems;
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
|
||||||
|
return text_style(/*is_foreground=*/true, foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
|
||||||
|
return text_style(/*is_foreground=*/false, background);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
|
||||||
|
return text_style(lhs) | rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename Char> struct ansi_color_escape {
|
||||||
|
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
|
||||||
|
const char* esc) FMT_NOEXCEPT {
|
||||||
|
// If we have a terminal color, we need to output another escape code
|
||||||
|
// sequence.
|
||||||
|
if (!text_color.is_rgb) {
|
||||||
|
bool is_background = esc == internal::data::background_color;
|
||||||
|
uint32_t value = text_color.value.term_color;
|
||||||
|
// Background ASCII codes are the same as the foreground ones but with
|
||||||
|
// 10 more.
|
||||||
|
if (is_background) value += 10u;
|
||||||
|
|
||||||
|
std::size_t index = 0;
|
||||||
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
|
buffer[index++] = static_cast<Char>('[');
|
||||||
|
|
||||||
|
if (value >= 100u) {
|
||||||
|
buffer[index++] = static_cast<Char>('1');
|
||||||
|
value %= 100u;
|
||||||
|
}
|
||||||
|
buffer[index++] = static_cast<Char>('0' + value / 10u);
|
||||||
|
buffer[index++] = static_cast<Char>('0' + value % 10u);
|
||||||
|
|
||||||
|
buffer[index++] = static_cast<Char>('m');
|
||||||
|
buffer[index++] = static_cast<Char>('\0');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
buffer[i] = static_cast<Char>(esc[i]);
|
||||||
|
}
|
||||||
|
rgb color(text_color.value.rgb_color);
|
||||||
|
to_esc(color.r, buffer + 7, ';');
|
||||||
|
to_esc(color.g, buffer + 11, ';');
|
||||||
|
to_esc(color.b, buffer + 15, 'm');
|
||||||
|
buffer[19] = static_cast<Char>(0);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
|
||||||
|
uint8_t em_codes[4] = {};
|
||||||
|
uint8_t em_bits = static_cast<uint8_t>(em);
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
|
||||||
|
em_codes[3] = 9;
|
||||||
|
|
||||||
|
std::size_t index = 0;
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
if (!em_codes[i]) continue;
|
||||||
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
|
buffer[index++] = static_cast<Char>('[');
|
||||||
|
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
|
||||||
|
buffer[index++] = static_cast<Char>('m');
|
||||||
|
}
|
||||||
|
buffer[index++] = static_cast<Char>(0);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
|
||||||
|
|
||||||
|
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
|
||||||
|
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
|
||||||
|
return buffer + std::strlen(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Char buffer[7u + 3u * 4u + 1u];
|
||||||
|
|
||||||
|
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
|
||||||
|
char delimiter) FMT_NOEXCEPT {
|
||||||
|
out[0] = static_cast<Char>('0' + c / 100);
|
||||||
|
out[1] = static_cast<Char>('0' + c / 10 % 10);
|
||||||
|
out[2] = static_cast<Char>('0' + c % 10);
|
||||||
|
out[3] = static_cast<Char>(delimiter);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
|
||||||
|
internal::color_type foreground) FMT_NOEXCEPT {
|
||||||
|
return ansi_color_escape<Char>(foreground, internal::data::foreground_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
|
||||||
|
internal::color_type background) FMT_NOEXCEPT {
|
||||||
|
return ansi_color_escape<Char>(background, internal::data::background_color);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
|
||||||
|
return ansi_color_escape<Char>(em);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
|
||||||
|
std::fputs(chars, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
|
||||||
|
std::fputws(chars, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
|
||||||
|
fputs(internal::data::reset_color, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
||||||
|
fputs(internal::data::wreset_color, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
|
||||||
|
const char* begin = data::reset_color;
|
||||||
|
const char* end = begin + sizeof(data::reset_color) - 1;
|
||||||
|
buffer.append(begin, end);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
std::basic_string<Char> vformat(const text_style& ts,
|
||||||
|
basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<Char> > args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
bool has_style = false;
|
||||||
|
if (ts.has_emphasis()) {
|
||||||
|
has_style = true;
|
||||||
|
ansi_color_escape<Char> escape = make_emphasis<Char>(ts.get_emphasis());
|
||||||
|
buffer.append(escape.begin(), escape.end());
|
||||||
|
}
|
||||||
|
if (ts.has_foreground()) {
|
||||||
|
has_style = true;
|
||||||
|
ansi_color_escape<Char> escape =
|
||||||
|
make_foreground_color<Char>(ts.get_foreground());
|
||||||
|
buffer.append(escape.begin(), escape.end());
|
||||||
|
}
|
||||||
|
if (ts.has_background()) {
|
||||||
|
has_style = true;
|
||||||
|
ansi_color_escape<Char> escape =
|
||||||
|
make_background_color<Char>(ts.get_background());
|
||||||
|
buffer.append(escape.begin(), escape.end());
|
||||||
|
}
|
||||||
|
internal::vformat_to(buffer, format_str, args);
|
||||||
|
if (has_style) {
|
||||||
|
reset_color<Char>(buffer);
|
||||||
|
}
|
||||||
|
return fmt::to_string(buffer);
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename S, typename Char = char_t<S> >
|
||||||
|
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
||||||
|
basic_format_args<buffer_context<Char> > args) {
|
||||||
|
bool has_style = false;
|
||||||
|
if (ts.has_emphasis()) {
|
||||||
|
has_style = true;
|
||||||
|
internal::fputs<Char>(internal::make_emphasis<Char>(ts.get_emphasis()), f);
|
||||||
|
}
|
||||||
|
if (ts.has_foreground()) {
|
||||||
|
has_style = true;
|
||||||
|
internal::fputs<Char>(
|
||||||
|
internal::make_foreground_color<Char>(ts.get_foreground()), f);
|
||||||
|
}
|
||||||
|
if (ts.has_background()) {
|
||||||
|
has_style = true;
|
||||||
|
internal::fputs<Char>(
|
||||||
|
internal::make_background_color<Char>(ts.get_background()), f);
|
||||||
|
}
|
||||||
|
vprint(f, format, args);
|
||||||
|
if (has_style) {
|
||||||
|
internal::reset_color<Char>(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats a string and prints it to the specified file stream using ANSI
|
||||||
|
escape sequences to specify text formatting.
|
||||||
|
Example:
|
||||||
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
||||||
|
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
||||||
|
const Args&... args) {
|
||||||
|
internal::check_format_string<Args...>(format_str);
|
||||||
|
using context = buffer_context<char_t<S> >;
|
||||||
|
format_arg_store<context, Args...> as{args...};
|
||||||
|
vprint(f, ts, format_str, basic_format_args<context>(as));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||||
|
specify text formatting.
|
||||||
|
Example:
|
||||||
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(internal::is_string<S>::value)>
|
||||||
|
void print(const text_style& ts, const S& format_str, const Args&... args) {
|
||||||
|
return print(stdout, ts, format_str, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename Char = char_t<S> >
|
||||||
|
inline std::basic_string<Char> vformat(
|
||||||
|
const text_style& ts, const S& format_str,
|
||||||
|
basic_format_args<buffer_context<Char> > args) {
|
||||||
|
return internal::vformat(ts, to_string_view(format_str), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Formats arguments and returns the result as a string using ANSI
|
||||||
|
escape sequences to specify text formatting.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
#include <fmt/color.h>
|
||||||
|
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
|
"The answer is {}", 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename S, typename... Args, typename Char = char_t<S> >
|
||||||
|
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
||||||
|
const Args&... args) {
|
||||||
|
return internal::vformat(ts, to_string_view(format_str),
|
||||||
|
{internal::make_args_checked(format_str, args...)});
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_COLOR_H_
|
||||||
@@ -1,257 +0,0 @@
|
|||||||
// Formatting library for C++ - the core API
|
|
||||||
//
|
|
||||||
// Copyright (c) 2012 - present, Victor Zverovich
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// For the license information refer to format.h.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
|
|
||||||
// All Rights Reserved
|
|
||||||
// {fmt} support for rgb color output.
|
|
||||||
|
|
||||||
#ifndef FMT_COLORS_H_
|
|
||||||
#define FMT_COLORS_H_
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
// rgb is a struct for red, green and blue colors.
|
|
||||||
// We use rgb as name because some editors will show it as color direct in the
|
|
||||||
// editor.
|
|
||||||
struct rgb
|
|
||||||
{
|
|
||||||
FMT_CONSTEXPR_DECL rgb()
|
|
||||||
: r(0)
|
|
||||||
, g(0)
|
|
||||||
, b(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_)
|
|
||||||
: r(r_)
|
|
||||||
, g(g_)
|
|
||||||
, b(b_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
FMT_CONSTEXPR_DECL rgb(uint32_t hex)
|
|
||||||
: r((hex >> 16) & 0xFF)
|
|
||||||
, g((hex >> 8) & 0xFF)
|
|
||||||
, b((hex)&0xFF)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
uint8_t r;
|
|
||||||
uint8_t g;
|
|
||||||
uint8_t b;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
FMT_CONSTEXPR inline void to_esc(uint8_t c, char out[], int offset)
|
|
||||||
{
|
|
||||||
out[offset + 0] = static_cast<char>('0' + c / 100);
|
|
||||||
out[offset + 1] = static_cast<char>('0' + c / 10 % 10);
|
|
||||||
out[offset + 2] = static_cast<char>('0' + c % 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
FMT_FUNC void vprint_rgb(rgb fd, string_view format, format_args args)
|
|
||||||
{
|
|
||||||
char escape_fd[] = "\x1b[38;2;000;000;000m";
|
|
||||||
static FMT_CONSTEXPR_DECL const char RESET_COLOR[] = "\x1b[0m";
|
|
||||||
internal::to_esc(fd.r, escape_fd, 7);
|
|
||||||
internal::to_esc(fd.g, escape_fd, 11);
|
|
||||||
internal::to_esc(fd.b, escape_fd, 15);
|
|
||||||
|
|
||||||
std::fputs(escape_fd, stdout);
|
|
||||||
vprint(format, args);
|
|
||||||
std::fputs(RESET_COLOR, stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC void vprint_rgb(rgb fd, rgb bg, string_view format, format_args args)
|
|
||||||
{
|
|
||||||
char escape_fd[] = "\x1b[38;2;000;000;000m"; // foreground color
|
|
||||||
char escape_bg[] = "\x1b[48;2;000;000;000m"; // background color
|
|
||||||
static FMT_CONSTEXPR_DECL const char RESET_COLOR[] = "\x1b[0m";
|
|
||||||
internal::to_esc(fd.r, escape_fd, 7);
|
|
||||||
internal::to_esc(fd.g, escape_fd, 11);
|
|
||||||
internal::to_esc(fd.b, escape_fd, 15);
|
|
||||||
|
|
||||||
internal::to_esc(bg.r, escape_bg, 7);
|
|
||||||
internal::to_esc(bg.g, escape_bg, 11);
|
|
||||||
internal::to_esc(bg.b, escape_bg, 15);
|
|
||||||
|
|
||||||
std::fputs(escape_fd, stdout);
|
|
||||||
std::fputs(escape_bg, stdout);
|
|
||||||
vprint(format, args);
|
|
||||||
std::fputs(RESET_COLOR, stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void print_rgb(rgb fd, string_view format_str, const Args &... args)
|
|
||||||
{
|
|
||||||
vprint_rgb(fd, format_str, make_format_args(args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
// rgb foreground color
|
|
||||||
template<typename... Args>
|
|
||||||
inline void print(rgb fd, string_view format_str, const Args &... args)
|
|
||||||
{
|
|
||||||
vprint_rgb(fd, format_str, make_format_args(args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
// rgb foreground color and background color
|
|
||||||
template<typename... Args>
|
|
||||||
inline void print(rgb fd, rgb bg, string_view format_str, const Args &... args)
|
|
||||||
{
|
|
||||||
vprint_rgb(fd, bg, format_str, make_format_args(args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class color : uint32_t
|
|
||||||
{
|
|
||||||
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
|
||||||
antique_white = 0xFAEBD7, // rgb(250,235,215)
|
|
||||||
aqua = 0x00FFFF, // rgb(0,255,255)
|
|
||||||
aquamarine = 0x7FFFD4, // rgb(127,255,212)
|
|
||||||
azure = 0xF0FFFF, // rgb(240,255,255)
|
|
||||||
beige = 0xF5F5DC, // rgb(245,245,220)
|
|
||||||
bisque = 0xFFE4C4, // rgb(255,228,196)
|
|
||||||
black = 0x000000, // rgb(0,0,0)
|
|
||||||
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
|
|
||||||
blue = 0x0000FF, // rgb(0,0,255)
|
|
||||||
blue_violet = 0x8A2BE2, // rgb(138,43,226)
|
|
||||||
brown = 0xA52A2A, // rgb(165,42,42)
|
|
||||||
burly_wood = 0xDEB887, // rgb(222,184,135)
|
|
||||||
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
|
|
||||||
chartreuse = 0x7FFF00, // rgb(127,255,0)
|
|
||||||
chocolate = 0xD2691E, // rgb(210,105,30)
|
|
||||||
coral = 0xFF7F50, // rgb(255,127,80)
|
|
||||||
cornflower_blue = 0x6495ED, // rgb(100,149,237)
|
|
||||||
cornsilk = 0xFFF8DC, // rgb(255,248,220)
|
|
||||||
crimson = 0xDC143C, // rgb(220,20,60)
|
|
||||||
cyan = 0x00FFFF, // rgb(0,255,255)
|
|
||||||
dark_blue = 0x00008B, // rgb(0,0,139)
|
|
||||||
dark_cyan = 0x008B8B, // rgb(0,139,139)
|
|
||||||
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
|
|
||||||
dark_gray = 0xA9A9A9, // rgb(169,169,169)
|
|
||||||
dark_green = 0x006400, // rgb(0,100,0)
|
|
||||||
dark_khaki = 0xBDB76B, // rgb(189,183,107)
|
|
||||||
dark_magenta = 0x8B008B, // rgb(139,0,139)
|
|
||||||
dark_olive_green = 0x556B2F, // rgb(85,107,47)
|
|
||||||
dark_orange = 0xFF8C00, // rgb(255,140,0)
|
|
||||||
dark_orchid = 0x9932CC, // rgb(153,50,204)
|
|
||||||
dark_red = 0x8B0000, // rgb(139,0,0)
|
|
||||||
dark_salmon = 0xE9967A, // rgb(233,150,122)
|
|
||||||
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
|
|
||||||
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
|
|
||||||
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
|
|
||||||
dark_turquoise = 0x00CED1, // rgb(0,206,209)
|
|
||||||
dark_violet = 0x9400D3, // rgb(148,0,211)
|
|
||||||
deep_pink = 0xFF1493, // rgb(255,20,147)
|
|
||||||
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
|
|
||||||
dim_gray = 0x696969, // rgb(105,105,105)
|
|
||||||
dodger_blue = 0x1E90FF, // rgb(30,144,255)
|
|
||||||
fire_brick = 0xB22222, // rgb(178,34,34)
|
|
||||||
floral_white = 0xFFFAF0, // rgb(255,250,240)
|
|
||||||
forest_green = 0x228B22, // rgb(34,139,34)
|
|
||||||
fuchsia = 0xFF00FF, // rgb(255,0,255)
|
|
||||||
gainsboro = 0xDCDCDC, // rgb(220,220,220)
|
|
||||||
ghost_white = 0xF8F8FF, // rgb(248,248,255)
|
|
||||||
gold = 0xFFD700, // rgb(255,215,0)
|
|
||||||
golden_rod = 0xDAA520, // rgb(218,165,32)
|
|
||||||
gray = 0x808080, // rgb(128,128,128)
|
|
||||||
green = 0x008000, // rgb(0,128,0)
|
|
||||||
green_yellow = 0xADFF2F, // rgb(173,255,47)
|
|
||||||
honey_dew = 0xF0FFF0, // rgb(240,255,240)
|
|
||||||
hot_pink = 0xFF69B4, // rgb(255,105,180)
|
|
||||||
indian_red = 0xCD5C5C, // rgb(205,92,92)
|
|
||||||
indigo = 0x4B0082, // rgb(75,0,130)
|
|
||||||
ivory = 0xFFFFF0, // rgb(255,255,240)
|
|
||||||
khaki = 0xF0E68C, // rgb(240,230,140)
|
|
||||||
lavender = 0xE6E6FA, // rgb(230,230,250)
|
|
||||||
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
|
|
||||||
lawn_green = 0x7CFC00, // rgb(124,252,0)
|
|
||||||
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
|
|
||||||
light_blue = 0xADD8E6, // rgb(173,216,230)
|
|
||||||
light_coral = 0xF08080, // rgb(240,128,128)
|
|
||||||
light_cyan = 0xE0FFFF, // rgb(224,255,255)
|
|
||||||
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
|
|
||||||
light_gray = 0xD3D3D3, // rgb(211,211,211)
|
|
||||||
light_green = 0x90EE90, // rgb(144,238,144)
|
|
||||||
light_pink = 0xFFB6C1, // rgb(255,182,193)
|
|
||||||
light_salmon = 0xFFA07A, // rgb(255,160,122)
|
|
||||||
light_sea_green = 0x20B2AA, // rgb(32,178,170)
|
|
||||||
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
|
|
||||||
light_slate_gray = 0x778899, // rgb(119,136,153)
|
|
||||||
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
|
|
||||||
light_yellow = 0xFFFFE0, // rgb(255,255,224)
|
|
||||||
lime = 0x00FF00, // rgb(0,255,0)
|
|
||||||
lime_green = 0x32CD32, // rgb(50,205,50)
|
|
||||||
linen = 0xFAF0E6, // rgb(250,240,230)
|
|
||||||
magenta = 0xFF00FF, // rgb(255,0,255)
|
|
||||||
maroon = 0x800000, // rgb(128,0,0)
|
|
||||||
medium_aqua_marine = 0x66CDAA, // rgb(102,205,170)
|
|
||||||
medium_blue = 0x0000CD, // rgb(0,0,205)
|
|
||||||
medium_orchid = 0xBA55D3, // rgb(186,85,211)
|
|
||||||
medium_purple = 0x9370DB, // rgb(147,112,219)
|
|
||||||
medium_sea_green = 0x3CB371, // rgb(60,179,113)
|
|
||||||
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
|
|
||||||
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
|
|
||||||
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
|
|
||||||
medium_violet_red = 0xC71585, // rgb(199,21,133)
|
|
||||||
midnight_blue = 0x191970, // rgb(25,25,112)
|
|
||||||
mint_cream = 0xF5FFFA, // rgb(245,255,250)
|
|
||||||
misty_rose = 0xFFE4E1, // rgb(255,228,225)
|
|
||||||
moccasin = 0xFFE4B5, // rgb(255,228,181)
|
|
||||||
navajo_white = 0xFFDEAD, // rgb(255,222,173)
|
|
||||||
navy = 0x000080, // rgb(0,0,128)
|
|
||||||
old_lace = 0xFDF5E6, // rgb(253,245,230)
|
|
||||||
olive = 0x808000, // rgb(128,128,0)
|
|
||||||
olive_drab = 0x6B8E23, // rgb(107,142,35)
|
|
||||||
orange = 0xFFA500, // rgb(255,165,0)
|
|
||||||
orange_red = 0xFF4500, // rgb(255,69,0)
|
|
||||||
orchid = 0xDA70D6, // rgb(218,112,214)
|
|
||||||
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
|
|
||||||
pale_green = 0x98FB98, // rgb(152,251,152)
|
|
||||||
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
|
|
||||||
pale_violet_red = 0xDB7093, // rgb(219,112,147)
|
|
||||||
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
|
|
||||||
peach_puff = 0xFFDAB9, // rgb(255,218,185)
|
|
||||||
peru = 0xCD853F, // rgb(205,133,63)
|
|
||||||
pink = 0xFFC0CB, // rgb(255,192,203)
|
|
||||||
plum = 0xDDA0DD, // rgb(221,160,221)
|
|
||||||
powder_blue = 0xB0E0E6, // rgb(176,224,230)
|
|
||||||
purple = 0x800080, // rgb(128,0,128)
|
|
||||||
rebecca_purple = 0x663399, // rgb(102,51,153)
|
|
||||||
red = 0xFF0000, // rgb(255,0,0)
|
|
||||||
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
|
|
||||||
royal_blue = 0x4169E1, // rgb(65,105,225)
|
|
||||||
saddle_brown = 0x8B4513, // rgb(139,69,19)
|
|
||||||
salmon = 0xFA8072, // rgb(250,128,114)
|
|
||||||
sandy_brown = 0xF4A460, // rgb(244,164,96)
|
|
||||||
sea_green = 0x2E8B57, // rgb(46,139,87)
|
|
||||||
sea_shell = 0xFFF5EE, // rgb(255,245,238)
|
|
||||||
sienna = 0xA0522D, // rgb(160,82,45)
|
|
||||||
silver = 0xC0C0C0, // rgb(192,192,192)
|
|
||||||
sky_blue = 0x87CEEB, // rgb(135,206,235)
|
|
||||||
slate_blue = 0x6A5ACD, // rgb(106,90,205)
|
|
||||||
slate_gray = 0x708090, // rgb(112,128,144)
|
|
||||||
snow = 0xFFFAFA, // rgb(255,250,250)
|
|
||||||
spring_green = 0x00FF7F, // rgb(0,255,127)
|
|
||||||
steel_blue = 0x4682B4, // rgb(70,130,180)
|
|
||||||
tan = 0xD2B48C, // rgb(210,180,140)
|
|
||||||
teal = 0x008080, // rgb(0,128,128)
|
|
||||||
thistle = 0xD8BFD8, // rgb(216,191,216)
|
|
||||||
tomato = 0xFF6347, // rgb(255,99,71)
|
|
||||||
turquoise = 0x40E0D0, // rgb(64,224,208)
|
|
||||||
violet = 0xEE82EE, // rgb(238,130,238)
|
|
||||||
wheat = 0xF5DEB3, // rgb(245,222,179)
|
|
||||||
white = 0xFFFFFF, // rgb(255,255,255)
|
|
||||||
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
|
||||||
yellow = 0xFFFF00, // rgb(255,255,0)
|
|
||||||
yellow_green = 0x9ACD32, // rgb(154,205,50)
|
|
||||||
}; // enum class colors
|
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
|
||||||
|
|
||||||
#endif // FMT_COLORS_H_
|
|
||||||
466
include/spdlog/fmt/bundled/compile.h
Normal file
466
include/spdlog/fmt/bundled/compile.h
Normal file
@@ -0,0 +1,466 @@
|
|||||||
|
// Formatting library for C++ - experimental format string compilation
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_COMPILE_H_
|
||||||
|
#define FMT_COMPILE_H_
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename Char> struct format_part {
|
||||||
|
public:
|
||||||
|
struct named_argument_id {
|
||||||
|
FMT_CONSTEXPR named_argument_id(internal::string_view_metadata id)
|
||||||
|
: id(id) {}
|
||||||
|
internal::string_view_metadata id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct argument_id {
|
||||||
|
FMT_CONSTEXPR argument_id() : argument_id(0u) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR argument_id(unsigned id)
|
||||||
|
: which(which_arg_id::index), val(id) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR argument_id(internal::string_view_metadata id)
|
||||||
|
: which(which_arg_id::named_index), val(id) {}
|
||||||
|
|
||||||
|
enum class which_arg_id { index, named_index };
|
||||||
|
|
||||||
|
which_arg_id which;
|
||||||
|
|
||||||
|
union value {
|
||||||
|
FMT_CONSTEXPR value() : index(0u) {}
|
||||||
|
FMT_CONSTEXPR value(unsigned id) : index(id) {}
|
||||||
|
FMT_CONSTEXPR value(internal::string_view_metadata id)
|
||||||
|
: named_index(id) {}
|
||||||
|
|
||||||
|
unsigned index;
|
||||||
|
internal::string_view_metadata named_index;
|
||||||
|
} val;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct specification {
|
||||||
|
FMT_CONSTEXPR specification() : arg_id(0u) {}
|
||||||
|
FMT_CONSTEXPR specification(unsigned id) : arg_id(id) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR specification(internal::string_view_metadata id)
|
||||||
|
: arg_id(id) {}
|
||||||
|
|
||||||
|
argument_id arg_id;
|
||||||
|
internal::dynamic_format_specs<Char> parsed_specs;
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_CONSTEXPR format_part()
|
||||||
|
: which(kind::argument_id), end_of_argument_id(0u), val(0u) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR format_part(internal::string_view_metadata text)
|
||||||
|
: which(kind::text), end_of_argument_id(0u), val(text) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR format_part(unsigned id)
|
||||||
|
: which(kind::argument_id), end_of_argument_id(0u), val(id) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR format_part(named_argument_id arg_id)
|
||||||
|
: which(kind::named_argument_id), end_of_argument_id(0u), val(arg_id) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR format_part(specification spec)
|
||||||
|
: which(kind::specification), end_of_argument_id(0u), val(spec) {}
|
||||||
|
|
||||||
|
enum class kind { argument_id, named_argument_id, text, specification };
|
||||||
|
|
||||||
|
kind which;
|
||||||
|
std::size_t end_of_argument_id;
|
||||||
|
union value {
|
||||||
|
FMT_CONSTEXPR value() : arg_id(0u) {}
|
||||||
|
FMT_CONSTEXPR value(unsigned id) : arg_id(id) {}
|
||||||
|
FMT_CONSTEXPR value(named_argument_id named_id)
|
||||||
|
: named_arg_id(named_id.id) {}
|
||||||
|
FMT_CONSTEXPR value(internal::string_view_metadata t) : text(t) {}
|
||||||
|
FMT_CONSTEXPR value(specification s) : spec(s) {}
|
||||||
|
unsigned arg_id;
|
||||||
|
internal::string_view_metadata named_arg_id;
|
||||||
|
internal::string_view_metadata text;
|
||||||
|
specification spec;
|
||||||
|
} val;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char, typename PartsContainer>
|
||||||
|
class format_preparation_handler : public internal::error_handler {
|
||||||
|
private:
|
||||||
|
using part = format_part<Char>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using iterator = typename basic_string_view<Char>::iterator;
|
||||||
|
|
||||||
|
FMT_CONSTEXPR format_preparation_handler(basic_string_view<Char> format,
|
||||||
|
PartsContainer& parts)
|
||||||
|
: parts_(parts), format_(format), parse_context_(format) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
|
||||||
|
if (begin == end) return;
|
||||||
|
const auto offset = begin - format_.data();
|
||||||
|
const auto size = end - begin;
|
||||||
|
parts_.push_back(part(string_view_metadata(offset, size)));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_arg_id() {
|
||||||
|
parts_.push_back(part(parse_context_.next_arg_id()));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_arg_id(unsigned id) {
|
||||||
|
parse_context_.check_arg_id(id);
|
||||||
|
parts_.push_back(part(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
|
||||||
|
const auto view = string_view_metadata(format_, id);
|
||||||
|
const auto arg_id = typename part::named_argument_id(view);
|
||||||
|
parts_.push_back(part(arg_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
|
||||||
|
parts_.back().end_of_argument_id = ptr - format_.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
|
||||||
|
const Char* end) {
|
||||||
|
const auto specs_offset = to_unsigned(begin - format_.begin());
|
||||||
|
|
||||||
|
using parse_context = basic_parse_context<Char>;
|
||||||
|
internal::dynamic_format_specs<Char> parsed_specs;
|
||||||
|
dynamic_specs_handler<parse_context> handler(parsed_specs, parse_context_);
|
||||||
|
begin = parse_format_specs(begin, end, handler);
|
||||||
|
|
||||||
|
if (*begin != '}') on_error("missing '}' in format string");
|
||||||
|
|
||||||
|
auto& last_part = parts_.back();
|
||||||
|
auto specs = last_part.which == part::kind::argument_id
|
||||||
|
? typename part::specification(last_part.val.arg_id)
|
||||||
|
: typename part::specification(last_part.val.named_arg_id);
|
||||||
|
specs.parsed_specs = parsed_specs;
|
||||||
|
last_part = part(specs);
|
||||||
|
last_part.end_of_argument_id = specs_offset;
|
||||||
|
return begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
PartsContainer& parts_;
|
||||||
|
basic_string_view<Char> format_;
|
||||||
|
basic_parse_context<Char> parse_context_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Format, typename PreparedPartsProvider, typename... Args>
|
||||||
|
class prepared_format {
|
||||||
|
public:
|
||||||
|
using char_type = char_t<Format>;
|
||||||
|
using format_part_t = format_part<char_type>;
|
||||||
|
|
||||||
|
constexpr prepared_format(Format f)
|
||||||
|
: format_(std::move(f)), parts_provider_(to_string_view(format_)) {}
|
||||||
|
|
||||||
|
prepared_format() = delete;
|
||||||
|
|
||||||
|
using context = buffer_context<char_type>;
|
||||||
|
|
||||||
|
template <typename Range, typename Context>
|
||||||
|
auto vformat_to(Range out, basic_format_args<Context> args) const ->
|
||||||
|
typename Context::iterator {
|
||||||
|
const auto format_view = internal::to_string_view(format_);
|
||||||
|
basic_parse_context<char_type> parse_ctx(format_view);
|
||||||
|
Context ctx(out.begin(), args);
|
||||||
|
|
||||||
|
const auto& parts = parts_provider_.parts();
|
||||||
|
for (auto part_it = parts.begin(); part_it != parts.end(); ++part_it) {
|
||||||
|
const auto& part = *part_it;
|
||||||
|
const auto& value = part.val;
|
||||||
|
|
||||||
|
switch (part.which) {
|
||||||
|
case format_part_t::kind::text: {
|
||||||
|
const auto text = value.text.to_view(format_view.data());
|
||||||
|
auto output = ctx.out();
|
||||||
|
auto&& it = internal::reserve(output, text.size());
|
||||||
|
it = std::copy_n(text.begin(), text.size(), it);
|
||||||
|
ctx.advance_to(output);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case format_part_t::kind::argument_id: {
|
||||||
|
advance_parse_context_to_specification(parse_ctx, part);
|
||||||
|
format_arg<Range>(parse_ctx, ctx, value.arg_id);
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case format_part_t::kind::named_argument_id: {
|
||||||
|
advance_parse_context_to_specification(parse_ctx, part);
|
||||||
|
const auto named_arg_id =
|
||||||
|
value.named_arg_id.to_view(format_view.data());
|
||||||
|
format_arg<Range>(parse_ctx, ctx, named_arg_id);
|
||||||
|
} break;
|
||||||
|
case format_part_t::kind::specification: {
|
||||||
|
const auto& arg_id_value = value.spec.arg_id.val;
|
||||||
|
const auto arg = value.spec.arg_id.which ==
|
||||||
|
format_part_t::argument_id::which_arg_id::index
|
||||||
|
? ctx.arg(arg_id_value.index)
|
||||||
|
: ctx.arg(arg_id_value.named_index.to_view(
|
||||||
|
to_string_view(format_).data()));
|
||||||
|
|
||||||
|
auto specs = value.spec.parsed_specs;
|
||||||
|
|
||||||
|
handle_dynamic_spec<internal::width_checker>(
|
||||||
|
specs.width, specs.width_ref, ctx, format_view.begin());
|
||||||
|
handle_dynamic_spec<internal::precision_checker>(
|
||||||
|
specs.precision, specs.precision_ref, ctx, format_view.begin());
|
||||||
|
|
||||||
|
check_prepared_specs(specs, arg.type());
|
||||||
|
advance_parse_context_to_specification(parse_ctx, part);
|
||||||
|
ctx.advance_to(
|
||||||
|
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctx.out();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void advance_parse_context_to_specification(
|
||||||
|
basic_parse_context<char_type>& parse_ctx,
|
||||||
|
const format_part_t& part) const {
|
||||||
|
const auto view = to_string_view(format_);
|
||||||
|
const auto specification_begin = view.data() + part.end_of_argument_id;
|
||||||
|
advance_to(parse_ctx, specification_begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Range, typename Context, typename Id>
|
||||||
|
void format_arg(basic_parse_context<char_type>& parse_ctx, Context& ctx,
|
||||||
|
Id arg_id) const {
|
||||||
|
parse_ctx.check_arg_id(arg_id);
|
||||||
|
const auto stopped_at =
|
||||||
|
visit_format_arg(arg_formatter<Range>(ctx), ctx.arg(arg_id));
|
||||||
|
ctx.advance_to(stopped_at);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
void check_prepared_specs(const basic_format_specs<Char>& specs,
|
||||||
|
internal::type arg_type) const {
|
||||||
|
internal::error_handler h;
|
||||||
|
numeric_specs_checker<internal::error_handler> checker(h, arg_type);
|
||||||
|
if (specs.align == align::numeric) checker.require_numeric_argument();
|
||||||
|
if (specs.sign != sign::none) checker.check_sign();
|
||||||
|
if (specs.alt) checker.require_numeric_argument();
|
||||||
|
if (specs.precision >= 0) checker.check_precision();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Format format_;
|
||||||
|
PreparedPartsProvider parts_provider_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char> struct part_counter {
|
||||||
|
unsigned num_parts = 0;
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
|
||||||
|
if (begin != end) ++num_parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_arg_id() { ++num_parts; }
|
||||||
|
FMT_CONSTEXPR void on_arg_id(unsigned) { ++num_parts; }
|
||||||
|
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
|
||||||
|
const Char* end) {
|
||||||
|
// Find the matching brace.
|
||||||
|
unsigned braces_counter = 0;
|
||||||
|
for (; begin != end; ++begin) {
|
||||||
|
if (*begin == '{') {
|
||||||
|
++braces_counter;
|
||||||
|
} else if (*begin == '}') {
|
||||||
|
if (braces_counter == 0u) break;
|
||||||
|
--braces_counter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return begin;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void on_error(const char*) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Format> class compiletime_prepared_parts_type_provider {
|
||||||
|
private:
|
||||||
|
using char_type = char_t<Format>;
|
||||||
|
|
||||||
|
static FMT_CONSTEXPR unsigned count_parts() {
|
||||||
|
FMT_CONSTEXPR_DECL const auto text = to_string_view(Format{});
|
||||||
|
part_counter<char_type> counter;
|
||||||
|
internal::parse_format_string</*IS_CONSTEXPR=*/true>(text, counter);
|
||||||
|
return counter.num_parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workaround for old compilers. Compiletime parts preparation will not be
|
||||||
|
// performed with them anyway.
|
||||||
|
#if FMT_USE_CONSTEXPR
|
||||||
|
static FMT_CONSTEXPR_DECL const unsigned number_of_format_parts =
|
||||||
|
compiletime_prepared_parts_type_provider::count_parts();
|
||||||
|
#else
|
||||||
|
static const unsigned number_of_format_parts = 0u;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <unsigned N> struct format_parts_array {
|
||||||
|
using value_type = format_part<char_type>;
|
||||||
|
|
||||||
|
FMT_CONSTEXPR format_parts_array() : arr{} {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR value_type& operator[](unsigned ind) { return arr[ind]; }
|
||||||
|
|
||||||
|
FMT_CONSTEXPR const value_type* begin() const { return arr; }
|
||||||
|
FMT_CONSTEXPR const value_type* end() const { return begin() + N; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
value_type arr[N];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct empty {
|
||||||
|
// Parts preparator will search for it
|
||||||
|
using value_type = format_part<char_type>;
|
||||||
|
};
|
||||||
|
|
||||||
|
using type = conditional_t<number_of_format_parts != 0,
|
||||||
|
format_parts_array<number_of_format_parts>, empty>;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Parts> class compiletime_prepared_parts_collector {
|
||||||
|
private:
|
||||||
|
using format_part = typename Parts::value_type;
|
||||||
|
|
||||||
|
public:
|
||||||
|
FMT_CONSTEXPR explicit compiletime_prepared_parts_collector(Parts& parts)
|
||||||
|
: parts_{parts}, counter_{0u} {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR void push_back(format_part part) { parts_[counter_++] = part; }
|
||||||
|
|
||||||
|
FMT_CONSTEXPR format_part& back() { return parts_[counter_ - 1]; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Parts& parts_;
|
||||||
|
unsigned counter_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename PartsContainer, typename Char>
|
||||||
|
FMT_CONSTEXPR PartsContainer prepare_parts(basic_string_view<Char> format) {
|
||||||
|
PartsContainer parts;
|
||||||
|
internal::parse_format_string</*IS_CONSTEXPR=*/false>(
|
||||||
|
format, format_preparation_handler<Char, PartsContainer>(format, parts));
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PartsContainer, typename Char>
|
||||||
|
FMT_CONSTEXPR PartsContainer
|
||||||
|
prepare_compiletime_parts(basic_string_view<Char> format) {
|
||||||
|
using collector = compiletime_prepared_parts_collector<PartsContainer>;
|
||||||
|
|
||||||
|
PartsContainer parts;
|
||||||
|
collector c(parts);
|
||||||
|
internal::parse_format_string</*IS_CONSTEXPR=*/true>(
|
||||||
|
format, format_preparation_handler<Char, collector>(format, c));
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PartsContainer> class runtime_parts_provider {
|
||||||
|
public:
|
||||||
|
runtime_parts_provider() = delete;
|
||||||
|
template <typename Char>
|
||||||
|
runtime_parts_provider(basic_string_view<Char> format)
|
||||||
|
: parts_(prepare_parts<PartsContainer>(format)) {}
|
||||||
|
|
||||||
|
const PartsContainer& parts() const { return parts_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
PartsContainer parts_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Format, typename PartsContainer>
|
||||||
|
struct compiletime_parts_provider {
|
||||||
|
compiletime_parts_provider() = delete;
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR compiletime_parts_provider(basic_string_view<Char>) {}
|
||||||
|
|
||||||
|
const PartsContainer& parts() const {
|
||||||
|
static FMT_CONSTEXPR_DECL const PartsContainer prepared_parts =
|
||||||
|
prepare_compiletime_parts<PartsContainer>(
|
||||||
|
internal::to_string_view(Format{}));
|
||||||
|
|
||||||
|
return prepared_parts;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
#if FMT_USE_CONSTEXPR
|
||||||
|
template <typename... Args, typename S,
|
||||||
|
FMT_ENABLE_IF(is_compile_string<S>::value)>
|
||||||
|
FMT_CONSTEXPR auto compile(S format_str) -> internal::prepared_format<
|
||||||
|
S,
|
||||||
|
internal::compiletime_parts_provider<
|
||||||
|
S,
|
||||||
|
typename internal::compiletime_prepared_parts_type_provider<S>::type>,
|
||||||
|
Args...> {
|
||||||
|
return format_str;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <typename... Args, typename Char, size_t N>
|
||||||
|
auto compile(const Char (&format_str)[N]) -> internal::prepared_format<
|
||||||
|
std::basic_string<Char>,
|
||||||
|
internal::runtime_parts_provider<std::vector<internal::format_part<Char>>>,
|
||||||
|
Args...> {
|
||||||
|
return std::basic_string<Char>(format_str, N - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CompiledFormat, typename... Args,
|
||||||
|
typename Char = typename CompiledFormat::char_type>
|
||||||
|
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
using range = internal::buffer_range<Char>;
|
||||||
|
using context = buffer_context<Char>;
|
||||||
|
cf.template vformat_to<range, context>(range(buffer),
|
||||||
|
{make_format_args<context>(args...)});
|
||||||
|
return to_string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename CompiledFormat, typename... Args>
|
||||||
|
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||||
|
const Args&... args) {
|
||||||
|
using char_type = typename CompiledFormat::char_type;
|
||||||
|
using range = internal::output_range<OutputIt, char_type>;
|
||||||
|
using context = format_context_t<OutputIt, char_type>;
|
||||||
|
return cf.template vformat_to<range, context>(
|
||||||
|
range(out), {make_format_args<context>(args...)});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||||
|
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
|
||||||
|
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
||||||
|
const CompiledFormat& cf,
|
||||||
|
const Args&... args) {
|
||||||
|
auto it =
|
||||||
|
format_to(internal::truncating_iterator<OutputIt>(out, n), cf, args...);
|
||||||
|
return {it.base(), it.count()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename CompiledFormat, typename... Args>
|
||||||
|
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
||||||
|
return fmt::format_to(
|
||||||
|
internal::counting_iterator<typename CompiledFormat::char_type>(),
|
||||||
|
cf, args...)
|
||||||
|
.count();
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_COMPILE_H_
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,27 +1,77 @@
|
|||||||
// Formatting library for C++ - locale support
|
// Formatting library for C++ - std::locale support
|
||||||
//
|
//
|
||||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// For the license information refer to format.h.
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
#include "format.h"
|
#ifndef FMT_LOCALE_H_
|
||||||
|
#define FMT_LOCALE_H_
|
||||||
|
|
||||||
#include <locale>
|
#include <locale>
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
namespace fmt {
|
FMT_BEGIN_NAMESPACE
|
||||||
class locale
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
std::locale locale_;
|
|
||||||
|
|
||||||
public:
|
namespace internal {
|
||||||
explicit locale(std::locale loc = std::locale())
|
template <typename Char>
|
||||||
: locale_(loc)
|
typename buffer_context<Char>::iterator vformat_to(
|
||||||
{
|
const std::locale& loc, buffer<Char>& buf,
|
||||||
}
|
basic_string_view<Char> format_str,
|
||||||
std::locale get()
|
basic_format_args<buffer_context<Char>> args) {
|
||||||
{
|
using range = buffer_range<Char>;
|
||||||
return locale_;
|
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
|
||||||
}
|
internal::locale_ref(loc));
|
||||||
};
|
}
|
||||||
} // namespace fmt
|
|
||||||
|
template <typename Char>
|
||||||
|
std::basic_string<Char> vformat(const std::locale& loc,
|
||||||
|
basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<Char>> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
internal::vformat_to(loc, buffer, format_str, args);
|
||||||
|
return fmt::to_string(buffer);
|
||||||
|
}
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename S, typename Char = char_t<S>>
|
||||||
|
inline std::basic_string<Char> vformat(
|
||||||
|
const std::locale& loc, const S& format_str,
|
||||||
|
basic_format_args<buffer_context<Char>> args) {
|
||||||
|
return internal::vformat(loc, to_string_view(format_str), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
|
inline std::basic_string<Char> format(const std::locale& loc,
|
||||||
|
const S& format_str, Args&&... args) {
|
||||||
|
return internal::vformat(
|
||||||
|
loc, to_string_view(format_str),
|
||||||
|
{internal::make_args_checked<Args...>(format_str, args...)});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename OutputIt, typename... Args,
|
||||||
|
typename Char = enable_if_t<
|
||||||
|
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
|
||||||
|
inline OutputIt vformat_to(OutputIt out, const std::locale& loc,
|
||||||
|
const S& format_str,
|
||||||
|
format_args_t<OutputIt, Char> args) {
|
||||||
|
using range = internal::output_range<OutputIt, Char>;
|
||||||
|
return vformat_to<arg_formatter<range>>(
|
||||||
|
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
|
||||||
|
internal::is_string<S>::value)>
|
||||||
|
inline OutputIt format_to(OutputIt out, const std::locale& loc,
|
||||||
|
const S& format_str, Args&&... args) {
|
||||||
|
internal::check_format_string<Args...>(format_str);
|
||||||
|
using context = format_context_t<OutputIt, char_t<S>>;
|
||||||
|
format_arg_store<context, Args...> as{args...};
|
||||||
|
return vformat_to(out, loc, to_string_view(format_str),
|
||||||
|
basic_format_args<context>(as));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_LOCALE_H_
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// Formatting library for C++ - std::ostream support
|
// Formatting library for C++ - std::ostream support
|
||||||
//
|
//
|
||||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// For the license information refer to format.h.
|
// For the license information refer to format.h.
|
||||||
@@ -8,146 +8,114 @@
|
|||||||
#ifndef FMT_OSTREAM_H_
|
#ifndef FMT_OSTREAM_H_
|
||||||
#define FMT_OSTREAM_H_
|
#define FMT_OSTREAM_H_
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
template<class Char>
|
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
||||||
class formatbuf : public std::basic_streambuf<Char>
|
private:
|
||||||
{
|
using int_type = typename std::basic_streambuf<Char>::int_type;
|
||||||
private:
|
using traits_type = typename std::basic_streambuf<Char>::traits_type;
|
||||||
typedef typename std::basic_streambuf<Char>::int_type int_type;
|
|
||||||
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
|
|
||||||
|
|
||||||
basic_buffer<Char> &buffer_;
|
buffer<Char>& buffer_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
formatbuf(basic_buffer<Char> &buffer)
|
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
|
||||||
: buffer_(buffer)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// The put-area is actually always empty. This makes the implementation
|
// The put-area is actually always empty. This makes the implementation
|
||||||
// simpler and has the advantage that the streambuf and the buffer are always
|
// simpler and has the advantage that the streambuf and the buffer are always
|
||||||
// in sync and sputc never writes into uninitialized memory. The obvious
|
// in sync and sputc never writes into uninitialized memory. The obvious
|
||||||
// disadvantage is that each call to sputc always results in a (virtual) call
|
// disadvantage is that each call to sputc always results in a (virtual) call
|
||||||
// to overflow. There is no disadvantage here for sputn since this always
|
// to overflow. There is no disadvantage here for sputn since this always
|
||||||
// results in a call to xsputn.
|
// results in a call to xsputn.
|
||||||
|
|
||||||
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE
|
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
|
||||||
{
|
if (!traits_type::eq_int_type(ch, traits_type::eof()))
|
||||||
if (!traits_type::eq_int_type(ch, traits_type::eof()))
|
buffer_.push_back(static_cast<Char>(ch));
|
||||||
buffer_.push_back(static_cast<Char>(ch));
|
return ch;
|
||||||
return ch;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE
|
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
|
||||||
{
|
buffer_.append(s, s + count);
|
||||||
buffer_.append(s, s + count);
|
return count;
|
||||||
return count;
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Char>
|
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
||||||
struct test_stream : std::basic_ostream<Char>
|
private:
|
||||||
{
|
struct null;
|
||||||
private:
|
// Hide all operator<< from std::basic_ostream<Char>.
|
||||||
struct null;
|
void operator<<(null);
|
||||||
// Hide all operator<< from std::basic_ostream<Char>.
|
|
||||||
void operator<<(null);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Checks if T has a user-defined operator<< (e.g. not a member of
|
// Checks if T has a user-defined operator<< (e.g. not a member of
|
||||||
// std::ostream).
|
// std::ostream).
|
||||||
template<typename T, typename Char>
|
template <typename T, typename Char> class is_streamable {
|
||||||
class is_streamable
|
private:
|
||||||
{
|
template <typename U>
|
||||||
private:
|
static decltype((void)(std::declval<test_stream<Char>&>()
|
||||||
template<typename U>
|
<< std::declval<U>()),
|
||||||
static decltype(internal::declval<test_stream<Char> &>() << internal::declval<U>(), std::true_type()) test(int);
|
std::true_type())
|
||||||
|
test(int);
|
||||||
|
|
||||||
template<typename>
|
template <typename> static std::false_type test(...);
|
||||||
static std::false_type test(...);
|
|
||||||
|
|
||||||
typedef decltype(test<T>(0)) result;
|
using result = decltype(test<T>(0));
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// std::string operator<< is not considered user-defined because we handle
|
static const bool value = result::value;
|
||||||
// strings
|
|
||||||
// specially.
|
|
||||||
static const bool value = result::value && !std::is_same<T, std::string>::value;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Disable conversion to int if T has an overloaded operator<< which is a free
|
|
||||||
// function (not a member of std::ostream).
|
|
||||||
template<typename T, typename Char>
|
|
||||||
class convert_to_int<T, Char, true>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
static const bool value = convert_to_int<T, Char, false>::value && !is_streamable<T, Char>::value;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Write the content of buf to os.
|
// Write the content of buf to os.
|
||||||
template<typename Char>
|
template <typename Char>
|
||||||
void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf)
|
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||||
{
|
const Char* buf_data = buf.data();
|
||||||
const Char *data = buf.data();
|
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
|
||||||
typedef std::make_unsigned<std::streamsize>::type UnsignedStreamSize;
|
unsigned_streamsize size = buf.size();
|
||||||
UnsignedStreamSize size = buf.size();
|
unsigned_streamsize max_size =
|
||||||
UnsignedStreamSize max_size = internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
|
to_unsigned((std::numeric_limits<std::streamsize>::max)());
|
||||||
do
|
do {
|
||||||
{
|
unsigned_streamsize n = size <= max_size ? size : max_size;
|
||||||
UnsignedStreamSize n = size <= max_size ? size : max_size;
|
os.write(buf_data, static_cast<std::streamsize>(n));
|
||||||
os.write(data, static_cast<std::streamsize>(n));
|
buf_data += n;
|
||||||
data += n;
|
size -= n;
|
||||||
size -= n;
|
} while (size != 0);
|
||||||
} while (size != 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Char, typename T>
|
template <typename Char, typename T>
|
||||||
void format_value(basic_buffer<Char> &buffer, const T &value)
|
void format_value(buffer<Char>& buf, const T& value) {
|
||||||
{
|
formatbuf<Char> format_buf(buf);
|
||||||
internal::formatbuf<Char> format_buf(buffer);
|
std::basic_ostream<Char> output(&format_buf);
|
||||||
std::basic_ostream<Char> output(&format_buf);
|
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
output << value;
|
||||||
output << value;
|
buf.resize(buf.size());
|
||||||
buffer.resize(buffer.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable builtin formatting of enums and use operator<< instead.
|
|
||||||
template<typename T>
|
|
||||||
struct format_enum<T, typename std::enable_if<std::is_enum<T>::value>::type> : std::false_type
|
|
||||||
{
|
|
||||||
};
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||||
template<typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
struct formatter<T, Char, typename std::enable_if<internal::is_streamable<T, Char>::value>::type> : formatter<basic_string_view<Char>, Char>
|
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||||
{
|
: formatter<basic_string_view<Char>, Char> {
|
||||||
|
template <typename Context>
|
||||||
template<typename Context>
|
auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
|
||||||
auto format(const T &value, Context &ctx) -> decltype(ctx.out())
|
|
||||||
{
|
|
||||||
basic_memory_buffer<Char> buffer;
|
|
||||||
internal::format_value(buffer, value);
|
|
||||||
basic_string_view<Char> str(buffer.data(), buffer.size());
|
|
||||||
formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
|
||||||
return ctx.out();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename Char>
|
|
||||||
inline void vprint(
|
|
||||||
std::basic_ostream<Char> &os, basic_string_view<Char> format_str, basic_format_args<typename buffer_context<Char>::type> args)
|
|
||||||
{
|
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
vformat_to(buffer, format_str, args);
|
format_value(buffer, value);
|
||||||
internal::write(os, buffer);
|
basic_string_view<Char> str(buffer.data(), buffer.size());
|
||||||
|
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<Char>> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
internal::vformat_to(buffer, format_str, args);
|
||||||
|
internal::write(os, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Prints formatted data to the stream *os*.
|
Prints formatted data to the stream *os*.
|
||||||
@@ -157,17 +125,12 @@ inline void vprint(
|
|||||||
fmt::print(cerr, "Don't {}!", "panic");
|
fmt::print(cerr, "Don't {}!", "panic");
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template<typename... Args>
|
template <typename S, typename... Args,
|
||||||
inline void print(std::ostream &os, string_view format_str, const Args &... args)
|
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
|
||||||
{
|
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
||||||
vprint<char>(os, format_str, make_format_args<format_context>(args...));
|
vprint(os, to_string_view(format_str),
|
||||||
}
|
{internal::make_args_checked<Args...>(format_str, args...)});
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void print(std::wostream &os, wstring_view format_str, const Args &... args)
|
|
||||||
{
|
|
||||||
vprint<wchar_t>(os, format_str, make_format_args<wformat_context>(args...));
|
|
||||||
}
|
}
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_OSTREAM_H_
|
#endif // FMT_OSTREAM_H_
|
||||||
|
|||||||
@@ -10,55 +10,54 @@
|
|||||||
|
|
||||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||||
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
|
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
|
||||||
#undef __STRICT_ANSI__
|
# undef __STRICT_ANSI__
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h> // for O_RDONLY
|
#include <fcntl.h> // for O_RDONLY
|
||||||
#include <locale.h> // for locale_t
|
#include <locale.h> // for locale_t
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h> // for strtod_l
|
#include <stdlib.h> // for strtod_l
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
|
||||||
#if defined __APPLE__ || defined(__FreeBSD__)
|
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||||
#include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
#ifndef FMT_POSIX
|
#ifndef FMT_POSIX
|
||||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||||
// Fix warnings about deprecated symbols.
|
// Fix warnings about deprecated symbols.
|
||||||
#define FMT_POSIX(call) _##call
|
# define FMT_POSIX(call) _##call
|
||||||
#else
|
# else
|
||||||
#define FMT_POSIX(call) call
|
# define FMT_POSIX(call) call
|
||||||
#endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
|
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
|
||||||
#ifdef FMT_SYSTEM
|
#ifdef FMT_SYSTEM
|
||||||
#define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
||||||
#else
|
#else
|
||||||
#define FMT_SYSTEM(call) call
|
# define FMT_SYSTEM(call) call
|
||||||
#ifdef _WIN32
|
# ifdef _WIN32
|
||||||
// Fix warnings about deprecated symbols.
|
// Fix warnings about deprecated symbols.
|
||||||
#define FMT_POSIX_CALL(call) ::_##call
|
# define FMT_POSIX_CALL(call) ::_##call
|
||||||
#else
|
# else
|
||||||
#define FMT_POSIX_CALL(call) ::call
|
# define FMT_POSIX_CALL(call) ::call
|
||||||
#endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Retries the expression while it evaluates to error_result and errno
|
// Retries the expression while it evaluates to error_result and errno
|
||||||
// equals to EINTR.
|
// equals to EINTR.
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#define FMT_RETRY_VAL(result, expression, error_result) \
|
# define FMT_RETRY_VAL(result, expression, error_result) \
|
||||||
do \
|
do { \
|
||||||
{ \
|
result = (expression); \
|
||||||
result = (expression); \
|
|
||||||
} while (result == error_result && errno == EINTR)
|
} while (result == error_result && errno == EINTR)
|
||||||
#else
|
#else
|
||||||
#define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
|
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||||
@@ -70,7 +69,7 @@ FMT_BEGIN_NAMESPACE
|
|||||||
A reference to a null-terminated string. It can be constructed from a C
|
A reference to a null-terminated string. It can be constructed from a C
|
||||||
string or ``std::string``.
|
string or ``std::string``.
|
||||||
|
|
||||||
You can use one of the following typedefs for common character types:
|
You can use one of the following type aliases for common character types:
|
||||||
|
|
||||||
+---------------+-----------------------------+
|
+---------------+-----------------------------+
|
||||||
| Type | Definition |
|
| Type | Definition |
|
||||||
@@ -90,167 +89,92 @@ FMT_BEGIN_NAMESPACE
|
|||||||
format(std::string("{}"), 42);
|
format(std::string("{}"), 42);
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template<typename Char>
|
template <typename Char> class basic_cstring_view {
|
||||||
class basic_cstring_view
|
private:
|
||||||
{
|
const Char* data_;
|
||||||
private:
|
|
||||||
const Char *data_;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/** Constructs a string reference object from a C string. */
|
/** Constructs a string reference object from a C string. */
|
||||||
basic_cstring_view(const Char *s)
|
basic_cstring_view(const Char* s) : data_(s) {}
|
||||||
: data_(s)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Constructs a string reference from an ``std::string`` object.
|
Constructs a string reference from an ``std::string`` object.
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
basic_cstring_view(const std::basic_string<Char> &s)
|
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
|
||||||
: data_(s.c_str())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Returns the pointer to a C string. */
|
/** Returns the pointer to a C string. */
|
||||||
const Char *c_str() const
|
const Char* c_str() const { return data_; }
|
||||||
{
|
|
||||||
return data_;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef basic_cstring_view<char> cstring_view;
|
using cstring_view = basic_cstring_view<char>;
|
||||||
typedef basic_cstring_view<wchar_t> wcstring_view;
|
using wcstring_view = basic_cstring_view<wchar_t>;
|
||||||
|
|
||||||
// An error code.
|
// An error code.
|
||||||
class error_code
|
class error_code {
|
||||||
{
|
private:
|
||||||
private:
|
int value_;
|
||||||
int value_;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
||||||
|
|
||||||
int get() const FMT_NOEXCEPT
|
int get() const FMT_NOEXCEPT { return value_; }
|
||||||
{
|
|
||||||
return value_;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// A buffered file.
|
// A buffered file.
|
||||||
class buffered_file
|
class buffered_file {
|
||||||
{
|
private:
|
||||||
private:
|
FILE* file_;
|
||||||
FILE *file_;
|
|
||||||
|
|
||||||
friend class file;
|
friend class file;
|
||||||
|
|
||||||
explicit buffered_file(FILE *f)
|
explicit buffered_file(FILE* f) : file_(f) {}
|
||||||
: file_(f)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Constructs a buffered_file object which doesn't represent any file.
|
// Constructs a buffered_file object which doesn't represent any file.
|
||||||
buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {}
|
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
|
||||||
|
|
||||||
// Destroys the object closing the file it represents if any.
|
// Destroys the object closing the file it represents if any.
|
||||||
FMT_API ~buffered_file() FMT_DTOR_NOEXCEPT;
|
FMT_API ~buffered_file() FMT_NOEXCEPT;
|
||||||
|
|
||||||
#if !FMT_USE_RVALUE_REFERENCES
|
private:
|
||||||
// Emulate a move constructor and a move assignment operator if rvalue
|
buffered_file(const buffered_file&) = delete;
|
||||||
// references are not supported.
|
void operator=(const buffered_file&) = delete;
|
||||||
|
|
||||||
private:
|
public:
|
||||||
// A proxy object to emulate a move constructor.
|
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
|
||||||
// It is private to make it impossible call operator Proxy directly.
|
other.file_ = nullptr;
|
||||||
struct Proxy
|
}
|
||||||
{
|
|
||||||
FILE *file;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
buffered_file& operator=(buffered_file&& other) {
|
||||||
// A "move constructor" for moving from a temporary.
|
close();
|
||||||
buffered_file(Proxy p) FMT_NOEXCEPT : file_(p.file) {}
|
file_ = other.file_;
|
||||||
|
other.file_ = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
// A "move constructor" for moving from an lvalue.
|
// Opens a file.
|
||||||
buffered_file(buffered_file &f) FMT_NOEXCEPT : file_(f.file_)
|
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||||
{
|
|
||||||
f.file_ = FMT_NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A "move assignment operator" for moving from a temporary.
|
// Closes the file.
|
||||||
buffered_file &operator=(Proxy p)
|
FMT_API void close();
|
||||||
{
|
|
||||||
close();
|
|
||||||
file_ = p.file;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A "move assignment operator" for moving from an lvalue.
|
// Returns the pointer to a FILE object representing this file.
|
||||||
buffered_file &operator=(buffered_file &other)
|
FILE* get() const FMT_NOEXCEPT { return file_; }
|
||||||
{
|
|
||||||
close();
|
|
||||||
file_ = other.file_;
|
|
||||||
other.file_ = FMT_NULL;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a proxy object for moving from a temporary:
|
// We place parentheses around fileno to workaround a bug in some versions
|
||||||
// buffered_file file = buffered_file(...);
|
// of MinGW that define fileno as a macro.
|
||||||
operator Proxy() FMT_NOEXCEPT
|
FMT_API int(fileno)() const;
|
||||||
{
|
|
||||||
Proxy p = {file_};
|
|
||||||
file_ = FMT_NULL;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
void vprint(string_view format_str, format_args args) {
|
||||||
private:
|
fmt::vprint(file_, format_str, args);
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(buffered_file);
|
}
|
||||||
|
|
||||||
public:
|
template <typename... Args>
|
||||||
buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_)
|
inline void print(string_view format_str, const Args&... args) {
|
||||||
{
|
vprint(format_str, make_format_args(args...));
|
||||||
other.file_ = FMT_NULL;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
buffered_file &operator=(buffered_file &&other)
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
file_ = other.file_;
|
|
||||||
other.file_ = FMT_NULL;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Opens a file.
|
|
||||||
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
|
||||||
|
|
||||||
// Closes the file.
|
|
||||||
FMT_API void close();
|
|
||||||
|
|
||||||
// Returns the pointer to a FILE object representing this file.
|
|
||||||
FILE *get() const FMT_NOEXCEPT
|
|
||||||
{
|
|
||||||
return file_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We place parentheses around fileno to workaround a bug in some versions
|
|
||||||
// of MinGW that define fileno as a macro.
|
|
||||||
FMT_API int(fileno)() const;
|
|
||||||
|
|
||||||
void vprint(string_view format_str, format_args args)
|
|
||||||
{
|
|
||||||
fmt::vprint(file_, format_str, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void print(string_view format_str, const Args &... args)
|
|
||||||
{
|
|
||||||
vprint(format_str, make_format_args(args...));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// A file. Closed file is represented by a file object with descriptor -1.
|
// A file. Closed file is represented by a file object with descriptor -1.
|
||||||
@@ -259,226 +183,129 @@ public:
|
|||||||
// closing the file multiple times will cause a crash on Windows rather
|
// closing the file multiple times will cause a crash on Windows rather
|
||||||
// than an exception. You can get standard behavior by overriding the
|
// than an exception. You can get standard behavior by overriding the
|
||||||
// invalid parameter handler with _set_invalid_parameter_handler.
|
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||||
class file
|
class file {
|
||||||
{
|
private:
|
||||||
private:
|
int fd_; // File descriptor.
|
||||||
int fd_; // File descriptor.
|
|
||||||
|
|
||||||
// Constructs a file object with a given descriptor.
|
// Constructs a file object with a given descriptor.
|
||||||
explicit file(int fd)
|
explicit file(int fd) : fd_(fd) {}
|
||||||
: fd_(fd)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Possible values for the oflag argument to the constructor.
|
// Possible values for the oflag argument to the constructor.
|
||||||
enum
|
enum {
|
||||||
{
|
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||||
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||||
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
|
||||||
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
|
};
|
||||||
};
|
|
||||||
|
|
||||||
// Constructs a file object which doesn't represent any file.
|
// Constructs a file object which doesn't represent any file.
|
||||||
file() FMT_NOEXCEPT : fd_(-1) {}
|
file() FMT_NOEXCEPT : fd_(-1) {}
|
||||||
|
|
||||||
// Opens a file and constructs a file object representing this file.
|
// Opens a file and constructs a file object representing this file.
|
||||||
FMT_API file(cstring_view path, int oflag);
|
FMT_API file(cstring_view path, int oflag);
|
||||||
|
|
||||||
#if !FMT_USE_RVALUE_REFERENCES
|
private:
|
||||||
// Emulate a move constructor and a move assignment operator if rvalue
|
file(const file&) = delete;
|
||||||
// references are not supported.
|
void operator=(const file&) = delete;
|
||||||
|
|
||||||
private:
|
public:
|
||||||
// A proxy object to emulate a move constructor.
|
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
|
||||||
// It is private to make it impossible call operator Proxy directly.
|
|
||||||
struct Proxy
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
file& operator=(file&& other) {
|
||||||
// A "move constructor" for moving from a temporary.
|
close();
|
||||||
file(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {}
|
fd_ = other.fd_;
|
||||||
|
other.fd_ = -1;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
// A "move constructor" for moving from an lvalue.
|
// Destroys the object closing the file it represents if any.
|
||||||
file(file &other) FMT_NOEXCEPT : fd_(other.fd_)
|
FMT_API ~file() FMT_NOEXCEPT;
|
||||||
{
|
|
||||||
other.fd_ = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A "move assignment operator" for moving from a temporary.
|
// Returns the file descriptor.
|
||||||
file &operator=(Proxy p)
|
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
||||||
{
|
|
||||||
close();
|
|
||||||
fd_ = p.fd;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A "move assignment operator" for moving from an lvalue.
|
// Closes the file.
|
||||||
file &operator=(file &other)
|
FMT_API void close();
|
||||||
{
|
|
||||||
close();
|
|
||||||
fd_ = other.fd_;
|
|
||||||
other.fd_ = -1;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns a proxy object for moving from a temporary:
|
// Returns the file size. The size has signed type for consistency with
|
||||||
// file f = file(...);
|
// stat::st_size.
|
||||||
operator Proxy() FMT_NOEXCEPT
|
FMT_API long long size() const;
|
||||||
{
|
|
||||||
Proxy p = {fd_};
|
|
||||||
fd_ = -1;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
// Attempts to read count bytes from the file into the specified buffer.
|
||||||
private:
|
FMT_API std::size_t read(void* buffer, std::size_t count);
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(file);
|
|
||||||
|
|
||||||
public:
|
// Attempts to write count bytes from the specified buffer to the file.
|
||||||
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_)
|
FMT_API std::size_t write(const void* buffer, std::size_t count);
|
||||||
{
|
|
||||||
other.fd_ = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
file &operator=(file &&other)
|
// Duplicates a file descriptor with the dup function and returns
|
||||||
{
|
// the duplicate as a file object.
|
||||||
close();
|
FMT_API static file dup(int fd);
|
||||||
fd_ = other.fd_;
|
|
||||||
other.fd_ = -1;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Destroys the object closing the file it represents if any.
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
FMT_API ~file() FMT_DTOR_NOEXCEPT;
|
// necessary.
|
||||||
|
FMT_API void dup2(int fd);
|
||||||
|
|
||||||
// Returns the file descriptor.
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
int descriptor() const FMT_NOEXCEPT
|
// necessary.
|
||||||
{
|
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
|
||||||
return fd_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Closes the file.
|
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||||
FMT_API void close();
|
// and writing respectively.
|
||||||
|
FMT_API static void pipe(file& read_end, file& write_end);
|
||||||
|
|
||||||
// Returns the file size. The size has signed type for consistency with
|
// Creates a buffered_file object associated with this file and detaches
|
||||||
// stat::st_size.
|
// this file object from the file.
|
||||||
FMT_API long long size() const;
|
FMT_API buffered_file fdopen(const char* mode);
|
||||||
|
|
||||||
// Attempts to read count bytes from the file into the specified buffer.
|
|
||||||
FMT_API std::size_t read(void *buffer, std::size_t count);
|
|
||||||
|
|
||||||
// Attempts to write count bytes from the specified buffer to the file.
|
|
||||||
FMT_API std::size_t write(const void *buffer, std::size_t count);
|
|
||||||
|
|
||||||
// Duplicates a file descriptor with the dup function and returns
|
|
||||||
// the duplicate as a file object.
|
|
||||||
FMT_API static file dup(int fd);
|
|
||||||
|
|
||||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
|
||||||
// necessary.
|
|
||||||
FMT_API void dup2(int fd);
|
|
||||||
|
|
||||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
|
||||||
// necessary.
|
|
||||||
FMT_API void dup2(int fd, error_code &ec) FMT_NOEXCEPT;
|
|
||||||
|
|
||||||
// Creates a pipe setting up read_end and write_end file objects for reading
|
|
||||||
// and writing respectively.
|
|
||||||
FMT_API static void pipe(file &read_end, file &write_end);
|
|
||||||
|
|
||||||
// Creates a buffered_file object associated with this file and detaches
|
|
||||||
// this file object from the file.
|
|
||||||
FMT_API buffered_file fdopen(const char *mode);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns the memory page size.
|
// Returns the memory page size.
|
||||||
long getpagesize();
|
long getpagesize();
|
||||||
|
|
||||||
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && !defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__)
|
|
||||||
#define FMT_LOCALE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef FMT_LOCALE
|
#ifdef FMT_LOCALE
|
||||||
// A "C" numeric locale.
|
// A "C" numeric locale.
|
||||||
class Locale
|
class Locale {
|
||||||
{
|
private:
|
||||||
private:
|
# ifdef _WIN32
|
||||||
#ifdef _MSC_VER
|
using locale_t = _locale_t;
|
||||||
typedef _locale_t locale_t;
|
|
||||||
|
|
||||||
enum
|
enum { LC_NUMERIC_MASK = LC_NUMERIC };
|
||||||
{
|
|
||||||
LC_NUMERIC_MASK = LC_NUMERIC
|
|
||||||
};
|
|
||||||
|
|
||||||
static locale_t newlocale(int category_mask, const char *locale, locale_t)
|
static locale_t newlocale(int category_mask, const char* locale, locale_t) {
|
||||||
{
|
return _create_locale(category_mask, locale);
|
||||||
return _create_locale(category_mask, locale);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static void freelocale(locale_t locale)
|
static void freelocale(locale_t locale) { _free_locale(locale); }
|
||||||
{
|
|
||||||
_free_locale(locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
static double strtod_l(const char *nptr, char **endptr, _locale_t locale)
|
static double strtod_l(const char* nptr, char** endptr, _locale_t locale) {
|
||||||
{
|
return _strtod_l(nptr, endptr, locale);
|
||||||
return _strtod_l(nptr, endptr, locale);
|
}
|
||||||
}
|
# endif
|
||||||
#endif
|
|
||||||
|
|
||||||
locale_t locale_;
|
locale_t locale_;
|
||||||
|
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
|
Locale(const Locale&) = delete;
|
||||||
|
void operator=(const Locale&) = delete;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef locale_t Type;
|
using type = locale_t;
|
||||||
|
|
||||||
Locale()
|
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
|
||||||
: locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL))
|
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
|
||||||
{
|
}
|
||||||
if (!locale_)
|
~Locale() { freelocale(locale_); }
|
||||||
FMT_THROW(system_error(errno, "cannot create locale"));
|
|
||||||
}
|
|
||||||
~Locale()
|
|
||||||
{
|
|
||||||
freelocale(locale_);
|
|
||||||
}
|
|
||||||
|
|
||||||
Type get() const
|
type get() const { return locale_; }
|
||||||
{
|
|
||||||
return locale_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Converts string to floating-point number and advances str past the end
|
// Converts string to floating-point number and advances str past the end
|
||||||
// of the parsed input.
|
// of the parsed input.
|
||||||
double strtod(const char *&str) const
|
double strtod(const char*& str) const {
|
||||||
{
|
char* end = nullptr;
|
||||||
char *end = FMT_NULL;
|
double result = strtod_l(str, &end, locale_);
|
||||||
double result = strtod_l(str, &end, locale_);
|
str = end;
|
||||||
str = end;
|
return result;
|
||||||
return result;
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
#endif // FMT_LOCALE
|
#endif // FMT_LOCALE
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#if !FMT_USE_RVALUE_REFERENCES
|
#endif // FMT_POSIX_H_
|
||||||
namespace std {
|
|
||||||
// For compatibility with C++98.
|
|
||||||
inline fmt::buffered_file &move(fmt::buffered_file &f)
|
|
||||||
{
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
inline fmt::file &move(fmt::file &f)
|
|
||||||
{
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
} // namespace std
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif // FMT_POSIX_H_
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
|||||||
// Formatting library for C++ - the core API
|
// Formatting library for C++ - experimental range support
|
||||||
//
|
//
|
||||||
// Copyright (c) 2012 - present, Victor Zverovich
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
@@ -12,333 +12,277 @@
|
|||||||
#ifndef FMT_RANGES_H_
|
#ifndef FMT_RANGES_H_
|
||||||
#define FMT_RANGES_H_
|
#define FMT_RANGES_H_
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
// output only up to N items from the range.
|
// output only up to N items from the range.
|
||||||
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
|
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
|
||||||
#define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
|
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
template<typename Char>
|
template <typename Char> struct formatting_base {
|
||||||
struct formatting_base
|
template <typename ParseContext>
|
||||||
{
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
template<typename ParseContext>
|
return ctx.begin();
|
||||||
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
|
}
|
||||||
{
|
|
||||||
return ctx.begin();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Char, typename Enable = void>
|
template <typename Char, typename Enable = void>
|
||||||
struct formatting_range : formatting_base<Char>
|
struct formatting_range : formatting_base<Char> {
|
||||||
{
|
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
|
||||||
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit = FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
|
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
|
||||||
// range.
|
// range.
|
||||||
Char prefix;
|
Char prefix;
|
||||||
Char delimiter;
|
Char delimiter;
|
||||||
Char postfix;
|
Char postfix;
|
||||||
formatting_range()
|
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
|
||||||
: prefix('{')
|
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||||
, delimiter(',')
|
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||||
, postfix('}')
|
|
||||||
{
|
|
||||||
}
|
|
||||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
|
||||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename Char, typename Enable = void>
|
template <typename Char, typename Enable = void>
|
||||||
struct formatting_tuple : formatting_base<Char>
|
struct formatting_tuple : formatting_base<Char> {
|
||||||
{
|
Char prefix;
|
||||||
Char prefix;
|
Char delimiter;
|
||||||
Char delimiter;
|
Char postfix;
|
||||||
Char postfix;
|
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
|
||||||
formatting_tuple()
|
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||||
: prefix('(')
|
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||||
, delimiter(',')
|
|
||||||
, postfix(')')
|
|
||||||
{
|
|
||||||
}
|
|
||||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
|
||||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
template<typename RangeT, typename OutputIterator>
|
template <typename RangeT, typename OutputIterator>
|
||||||
void copy(const RangeT &range, OutputIterator out)
|
OutputIterator copy(const RangeT& range, OutputIterator out) {
|
||||||
{
|
for (auto it = range.begin(), end = range.end(); it != end; ++it)
|
||||||
for (auto it = range.begin(), end = range.end(); it != end; ++it)
|
*out++ = *it;
|
||||||
*out++ = *it;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename OutputIterator>
|
template <typename OutputIterator>
|
||||||
void copy(const char *str, OutputIterator out)
|
OutputIterator copy(const char* str, OutputIterator out) {
|
||||||
{
|
while (*str) *out++ = *str++;
|
||||||
const char *p_curr = str;
|
return out;
|
||||||
while (*p_curr)
|
|
||||||
{
|
|
||||||
*out++ = *p_curr++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename OutputIterator>
|
template <typename OutputIterator>
|
||||||
void copy(char ch, OutputIterator out)
|
OutputIterator copy(char ch, OutputIterator out) {
|
||||||
{
|
*out++ = ch;
|
||||||
*out++ = ch;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true value if T has std::string interface, like std::string_view.
|
/// Return true value if T has std::string interface, like std::string_view.
|
||||||
template<typename T>
|
template <typename T> class is_like_std_string {
|
||||||
class is_like_std_string
|
template <typename U>
|
||||||
{
|
static auto check(U* p)
|
||||||
template<typename U>
|
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
||||||
static auto check(U *p) -> decltype(p->find('a'), p->length(), p->data(), int());
|
template <typename> static void check(...);
|
||||||
template<typename>
|
|
||||||
static void check(...);
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static FMT_CONSTEXPR_DECL const bool value = !std::is_void<decltype(check<T>(FMT_NULL))>::value;
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename... Ts>
|
template <typename Char>
|
||||||
struct conditional_helper
|
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T, typename _ = void>
|
template <typename... Ts> struct conditional_helper {};
|
||||||
struct is_range_ : std::false_type
|
|
||||||
{
|
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
||||||
};
|
|
||||||
|
|
||||||
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
|
||||||
template<typename T>
|
template <typename T>
|
||||||
struct is_range_<T, typename std::conditional<false,
|
struct is_range_<
|
||||||
conditional_helper<decltype(internal::declval<T>().begin()), decltype(internal::declval<T>().end())>, void>::type>
|
T, conditional_t<false,
|
||||||
: std::true_type
|
conditional_helper<decltype(std::declval<T>().begin()),
|
||||||
{
|
decltype(std::declval<T>().end())>,
|
||||||
};
|
void>> : std::true_type {};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// tuple_size and tuple_element check.
|
/// tuple_size and tuple_element check.
|
||||||
template<typename T>
|
template <typename T> class is_tuple_like_ {
|
||||||
class is_tuple_like_
|
template <typename U>
|
||||||
{
|
static auto check(U* p)
|
||||||
template<typename U>
|
-> decltype(std::tuple_size<U>::value,
|
||||||
static auto check(U *p) -> decltype(std::tuple_size<U>::value, internal::declval<typename std::tuple_element<0, U>::type>(), int());
|
(void)std::declval<typename std::tuple_element<0, U>::type>(),
|
||||||
template<typename>
|
int());
|
||||||
static void check(...);
|
template <typename> static void check(...);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static FMT_CONSTEXPR_DECL const bool value = !std::is_void<decltype(check<T>(FMT_NULL))>::value;
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
|
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check for integer_sequence
|
// Check for integer_sequence
|
||||||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
|
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
|
||||||
template<typename T, T... N>
|
template <typename T, T... N>
|
||||||
using integer_sequence = std::integer_sequence<T, N...>;
|
using integer_sequence = std::integer_sequence<T, N...>;
|
||||||
template<std::size_t... N>
|
template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
|
||||||
using index_sequence = std::index_sequence<N...>;
|
template <std::size_t N>
|
||||||
template<std::size_t N>
|
|
||||||
using make_index_sequence = std::make_index_sequence<N>;
|
using make_index_sequence = std::make_index_sequence<N>;
|
||||||
#else
|
#else
|
||||||
template<typename T, T... N>
|
template <typename T, T... N> struct integer_sequence {
|
||||||
struct integer_sequence
|
using value_type = T;
|
||||||
{
|
|
||||||
typedef T value_type;
|
|
||||||
|
|
||||||
static FMT_CONSTEXPR std::size_t size()
|
static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
|
||||||
{
|
|
||||||
return sizeof...(N);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<std::size_t... N>
|
template <std::size_t... N>
|
||||||
using index_sequence = integer_sequence<std::size_t, N...>;
|
using index_sequence = integer_sequence<std::size_t, N...>;
|
||||||
|
|
||||||
template<typename T, std::size_t N, T... Ns>
|
template <typename T, std::size_t N, T... Ns>
|
||||||
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...>
|
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
|
||||||
{
|
template <typename T, T... Ns>
|
||||||
};
|
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
|
||||||
template<typename T, T... Ns>
|
|
||||||
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...>
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template<std::size_t N>
|
template <std::size_t N>
|
||||||
using make_index_sequence = make_integer_sequence<std::size_t, N>;
|
using make_index_sequence = make_integer_sequence<std::size_t, N>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template<class Tuple, class F, size_t... Is>
|
template <class Tuple, class F, size_t... Is>
|
||||||
void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT
|
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
|
||||||
{
|
using std::get;
|
||||||
using std::get;
|
// using free function get<I>(T) now.
|
||||||
// using free function get<I>(T) now.
|
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
|
||||||
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
|
(void)_; // blocks warnings
|
||||||
(void)_; // blocks warnings
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class T>
|
template <class T>
|
||||||
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(T const &)
|
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
|
||||||
{
|
T const&) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Tuple, class F>
|
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
|
||||||
void for_each(Tuple &&tup, F &&f)
|
const auto indexes = get_indexes(tup);
|
||||||
{
|
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||||
const auto indexes = get_indexes(tup);
|
|
||||||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Arg>
|
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
|
||||||
FMT_CONSTEXPR const char *format_str_quoted(
|
typename std::decay<Arg>::type>::value)>
|
||||||
bool add_space, const Arg &, typename std::enable_if<!is_like_std_string<typename std::decay<Arg>::type>::value>::type * = nullptr)
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||||
{
|
return add_space ? " {}" : "{}";
|
||||||
return add_space ? " {}" : "{}";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Arg>
|
template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
|
||||||
FMT_CONSTEXPR const char *format_str_quoted(
|
typename std::decay<Arg>::type>::value)>
|
||||||
bool add_space, const Arg &, typename std::enable_if<is_like_std_string<typename std::decay<Arg>::type>::value>::type * = nullptr)
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||||
{
|
return add_space ? " \"{}\"" : "\"{}\"";
|
||||||
return add_space ? " \"{}\"" : "\"{}\"";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR const char *format_str_quoted(bool add_space, const char *)
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
|
||||||
{
|
return add_space ? " \"{}\"" : "\"{}\"";
|
||||||
return add_space ? " \"{}\"" : "\"{}\"";
|
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR const wchar_t *format_str_quoted(bool add_space, const wchar_t *)
|
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
|
||||||
{
|
return add_space ? L" \"{}\"" : L"\"{}\"";
|
||||||
return add_space ? L" \"{}\"" : L"\"{}\"";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_CONSTEXPR const char *format_str_quoted(bool add_space, const char)
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
|
||||||
{
|
return add_space ? " '{}'" : "'{}'";
|
||||||
return add_space ? " '{}'" : "'{}'";
|
|
||||||
}
|
}
|
||||||
FMT_CONSTEXPR const wchar_t *format_str_quoted(bool add_space, const wchar_t)
|
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
||||||
{
|
return add_space ? L" '{}'" : L"'{}'";
|
||||||
return add_space ? L" '{}'" : L"'{}'";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> struct is_tuple_like {
|
||||||
struct is_tuple_like
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
{
|
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
|
||||||
static FMT_CONSTEXPR_DECL const bool value = internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename TupleT, typename Char>
|
template <typename TupleT, typename Char>
|
||||||
struct formatter<TupleT, Char, typename std::enable_if<fmt::is_tuple_like<TupleT>::value>::type>
|
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
||||||
{
|
private:
|
||||||
private:
|
// C++11 generic lambda for format()
|
||||||
// C++11 generic lambda for format()
|
template <typename FormatContext> struct format_each {
|
||||||
template<typename FormatContext>
|
template <typename T> void operator()(const T& v) {
|
||||||
struct format_each
|
if (i > 0) {
|
||||||
{
|
if (formatting.add_prepostfix_space) {
|
||||||
template<typename T>
|
*out++ = ' ';
|
||||||
void operator()(const T &v)
|
|
||||||
{
|
|
||||||
if (i > 0)
|
|
||||||
{
|
|
||||||
if (formatting.add_prepostfix_space)
|
|
||||||
{
|
|
||||||
*out++ = ' ';
|
|
||||||
}
|
|
||||||
internal::copy(formatting.delimiter, out);
|
|
||||||
}
|
|
||||||
format_to(out, internal::format_str_quoted((formatting.add_delimiter_spaces && i > 0), v), v);
|
|
||||||
++i;
|
|
||||||
}
|
}
|
||||||
|
out = internal::copy(formatting.delimiter, out);
|
||||||
formatting_tuple<Char> &formatting;
|
}
|
||||||
std::size_t &i;
|
out = format_to(out,
|
||||||
typename std::add_lvalue_reference<decltype(std::declval<FormatContext>().out())>::type out;
|
internal::format_str_quoted(
|
||||||
};
|
(formatting.add_delimiter_spaces && i > 0), v),
|
||||||
|
v);
|
||||||
public:
|
++i;
|
||||||
formatting_tuple<Char> formatting;
|
|
||||||
|
|
||||||
template<typename ParseContext>
|
|
||||||
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
|
|
||||||
{
|
|
||||||
return formatting.parse(ctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatContext = format_context>
|
formatting_tuple<Char>& formatting;
|
||||||
auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out())
|
std::size_t& i;
|
||||||
{
|
typename std::add_lvalue_reference<decltype(
|
||||||
auto out = ctx.out();
|
std::declval<FormatContext>().out())>::type out;
|
||||||
std::size_t i = 0;
|
};
|
||||||
internal::copy(formatting.prefix, out);
|
|
||||||
|
|
||||||
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
|
public:
|
||||||
if (formatting.add_prepostfix_space)
|
formatting_tuple<Char> formatting;
|
||||||
{
|
|
||||||
*out++ = ' ';
|
|
||||||
}
|
|
||||||
internal::copy(formatting.postfix, out);
|
|
||||||
|
|
||||||
return ctx.out();
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return formatting.parse(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext = format_context>
|
||||||
|
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
|
auto out = ctx.out();
|
||||||
|
std::size_t i = 0;
|
||||||
|
internal::copy(formatting.prefix, out);
|
||||||
|
|
||||||
|
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
|
||||||
|
if (formatting.add_prepostfix_space) {
|
||||||
|
*out++ = ' ';
|
||||||
}
|
}
|
||||||
|
internal::copy(formatting.postfix, out);
|
||||||
|
|
||||||
|
return ctx.out();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template <typename T, typename Char> struct is_range {
|
||||||
struct is_range
|
static FMT_CONSTEXPR_DECL const bool value =
|
||||||
{
|
internal::is_range_<T>::value &&
|
||||||
static FMT_CONSTEXPR_DECL const bool value = internal::is_range_<T>::value && !internal::is_like_std_string<T>::value;
|
!internal::is_like_std_string<T>::value &&
|
||||||
|
!std::is_convertible<T, std::basic_string<Char>>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename RangeT, typename Char>
|
template <typename RangeT, typename Char>
|
||||||
struct formatter<RangeT, Char, typename std::enable_if<fmt::is_range<RangeT>::value>::type>
|
struct formatter<RangeT, Char,
|
||||||
{
|
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
|
||||||
|
formatting_range<Char> formatting;
|
||||||
|
|
||||||
formatting_range<Char> formatting;
|
template <typename ParseContext>
|
||||||
|
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||||
|
return formatting.parse(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename ParseContext>
|
template <typename FormatContext>
|
||||||
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
|
typename FormatContext::iterator format(const RangeT& values,
|
||||||
{
|
FormatContext& ctx) {
|
||||||
return formatting.parse(ctx);
|
auto out = internal::copy(formatting.prefix, ctx.out());
|
||||||
}
|
std::size_t i = 0;
|
||||||
|
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
|
||||||
template<typename FormatContext>
|
if (i > 0) {
|
||||||
typename FormatContext::iterator format(const RangeT &values, FormatContext &ctx)
|
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||||
{
|
out = internal::copy(formatting.delimiter, out);
|
||||||
auto out = ctx.out();
|
}
|
||||||
internal::copy(formatting.prefix, out);
|
out = format_to(out,
|
||||||
std::size_t i = 0;
|
internal::format_str_quoted(
|
||||||
for (auto it = values.begin(), end = values.end(); it != end; ++it)
|
(formatting.add_delimiter_spaces && i > 0), *it),
|
||||||
{
|
*it);
|
||||||
if (i > 0)
|
if (++i > formatting.range_length_limit) {
|
||||||
{
|
out = format_to(out, " ... <other elements>");
|
||||||
if (formatting.add_prepostfix_space)
|
break;
|
||||||
{
|
}
|
||||||
*out++ = ' ';
|
|
||||||
}
|
|
||||||
internal::copy(formatting.delimiter, out);
|
|
||||||
}
|
|
||||||
format_to(out, internal::format_str_quoted((formatting.add_delimiter_spaces && i > 0), *it), *it);
|
|
||||||
if (++i > formatting.range_length_limit)
|
|
||||||
{
|
|
||||||
format_to(out, " ... <other elements>");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (formatting.add_prepostfix_space)
|
|
||||||
{
|
|
||||||
*out++ = ' ';
|
|
||||||
}
|
|
||||||
internal::copy(formatting.postfix, out);
|
|
||||||
return ctx.out();
|
|
||||||
}
|
}
|
||||||
|
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||||
|
return internal::copy(formatting.postfix, out);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_RANGES_H_
|
#endif // FMT_RANGES_H_
|
||||||
|
|||||||
293
include/spdlog/fmt/bundled/safe-duration-cast.h
Normal file
293
include/spdlog/fmt/bundled/safe-duration-cast.h
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
/*
|
||||||
|
* For conversion between std::chrono::durations without undefined
|
||||||
|
* behaviour or erroneous results.
|
||||||
|
* This is a stripped down version of duration_cast, for inclusion in fmt.
|
||||||
|
* See https://github.com/pauldreik/safe_duration_cast
|
||||||
|
*
|
||||||
|
* Copyright Paul Dreik 2019
|
||||||
|
*
|
||||||
|
* This file is licensed under the fmt license, see format.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <cmath>
|
||||||
|
#include <limits>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace safe_duration_cast {
|
||||||
|
|
||||||
|
template <typename To, typename From,
|
||||||
|
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
|
||||||
|
std::numeric_limits<From>::is_signed ==
|
||||||
|
std::numeric_limits<To>::is_signed)>
|
||||||
|
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
|
||||||
|
ec = 0;
|
||||||
|
using F = std::numeric_limits<From>;
|
||||||
|
using T = std::numeric_limits<To>;
|
||||||
|
static_assert(F::is_integer, "From must be integral");
|
||||||
|
static_assert(T::is_integer, "To must be integral");
|
||||||
|
|
||||||
|
// A and B are both signed, or both unsigned.
|
||||||
|
if (F::digits <= T::digits) {
|
||||||
|
// From fits in To without any problem.
|
||||||
|
} else {
|
||||||
|
// From does not always fit in To, resort to a dynamic check.
|
||||||
|
if (from < T::min() || from > T::max()) {
|
||||||
|
// outside range.
|
||||||
|
ec = 1;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return static_cast<To>(from);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* converts From to To, without loss. If the dynamic value of from
|
||||||
|
* can't be converted to To without loss, ec is set.
|
||||||
|
*/
|
||||||
|
template <typename To, typename From,
|
||||||
|
FMT_ENABLE_IF(!std::is_same<From, To>::value &&
|
||||||
|
std::numeric_limits<From>::is_signed !=
|
||||||
|
std::numeric_limits<To>::is_signed)>
|
||||||
|
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
|
||||||
|
ec = 0;
|
||||||
|
using F = std::numeric_limits<From>;
|
||||||
|
using T = std::numeric_limits<To>;
|
||||||
|
static_assert(F::is_integer, "From must be integral");
|
||||||
|
static_assert(T::is_integer, "To must be integral");
|
||||||
|
|
||||||
|
if (F::is_signed && !T::is_signed) {
|
||||||
|
// From may be negative, not allowed!
|
||||||
|
if (from < 0) {
|
||||||
|
ec = 1;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// From is positive. Can it always fit in To?
|
||||||
|
if (F::digits <= T::digits) {
|
||||||
|
// yes, From always fits in To.
|
||||||
|
} else {
|
||||||
|
// from may not fit in To, we have to do a dynamic check
|
||||||
|
if (from > static_cast<From>(T::max())) {
|
||||||
|
ec = 1;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!F::is_signed && T::is_signed) {
|
||||||
|
// can from be held in To?
|
||||||
|
if (F::digits < T::digits) {
|
||||||
|
// yes, From always fits in To.
|
||||||
|
} else {
|
||||||
|
// from may not fit in To, we have to do a dynamic check
|
||||||
|
if (from > static_cast<From>(T::max())) {
|
||||||
|
// outside range.
|
||||||
|
ec = 1;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// reaching here means all is ok for lossless conversion.
|
||||||
|
return static_cast<To>(from);
|
||||||
|
|
||||||
|
} // function
|
||||||
|
|
||||||
|
template <typename To, typename From,
|
||||||
|
FMT_ENABLE_IF(std::is_same<From, To>::value)>
|
||||||
|
FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
|
||||||
|
ec = 0;
|
||||||
|
return from;
|
||||||
|
} // function
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
/**
|
||||||
|
* converts From to To if possible, otherwise ec is set.
|
||||||
|
*
|
||||||
|
* input | output
|
||||||
|
* ---------------------------------|---------------
|
||||||
|
* NaN | NaN
|
||||||
|
* Inf | Inf
|
||||||
|
* normal, fits in output | converted (possibly lossy)
|
||||||
|
* normal, does not fit in output | ec is set
|
||||||
|
* subnormal | best effort
|
||||||
|
* -Inf | -Inf
|
||||||
|
*/
|
||||||
|
// clang-format on
|
||||||
|
template <typename To, typename From,
|
||||||
|
FMT_ENABLE_IF(!std::is_same<From, To>::value)>
|
||||||
|
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
|
||||||
|
ec = 0;
|
||||||
|
using T = std::numeric_limits<To>;
|
||||||
|
static_assert(std::is_floating_point<From>::value, "From must be floating");
|
||||||
|
static_assert(std::is_floating_point<To>::value, "To must be floating");
|
||||||
|
|
||||||
|
// catch the only happy case
|
||||||
|
if (std::isfinite(from)) {
|
||||||
|
if (from >= T::lowest() && from <= T::max()) {
|
||||||
|
return static_cast<To>(from);
|
||||||
|
}
|
||||||
|
// not within range.
|
||||||
|
ec = 1;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// nan and inf will be preserved
|
||||||
|
return static_cast<To>(from);
|
||||||
|
} // function
|
||||||
|
|
||||||
|
template <typename To, typename From,
|
||||||
|
FMT_ENABLE_IF(std::is_same<From, To>::value)>
|
||||||
|
FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
|
||||||
|
ec = 0;
|
||||||
|
static_assert(std::is_floating_point<From>::value, "From must be floating");
|
||||||
|
return from;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe duration cast between integral durations
|
||||||
|
*/
|
||||||
|
template <typename To, typename FromRep, typename FromPeriod,
|
||||||
|
FMT_ENABLE_IF(std::is_integral<FromRep>::value),
|
||||||
|
FMT_ENABLE_IF(std::is_integral<typename To::rep>::value)>
|
||||||
|
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
||||||
|
int& ec) {
|
||||||
|
using From = std::chrono::duration<FromRep, FromPeriod>;
|
||||||
|
ec = 0;
|
||||||
|
// the basic idea is that we need to convert from count() in the from type
|
||||||
|
// to count() in the To type, by multiplying it with this:
|
||||||
|
using Factor = std::ratio_divide<typename From::period, typename To::period>;
|
||||||
|
|
||||||
|
static_assert(Factor::num > 0, "num must be positive");
|
||||||
|
static_assert(Factor::den > 0, "den must be positive");
|
||||||
|
|
||||||
|
// the conversion is like this: multiply from.count() with Factor::num
|
||||||
|
// /Factor::den and convert it to To::rep, all this without
|
||||||
|
// overflow/underflow. let's start by finding a suitable type that can hold
|
||||||
|
// both To, From and Factor::num
|
||||||
|
using IntermediateRep =
|
||||||
|
typename std::common_type<typename From::rep, typename To::rep,
|
||||||
|
decltype(Factor::num)>::type;
|
||||||
|
|
||||||
|
// safe conversion to IntermediateRep
|
||||||
|
IntermediateRep count =
|
||||||
|
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
|
||||||
|
if (ec) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
// multiply with Factor::num without overflow or underflow
|
||||||
|
if (Factor::num != 1) {
|
||||||
|
constexpr auto max1 =
|
||||||
|
std::numeric_limits<IntermediateRep>::max() / Factor::num;
|
||||||
|
if (count > max1) {
|
||||||
|
ec = 1;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
constexpr auto min1 =
|
||||||
|
std::numeric_limits<IntermediateRep>::min() / Factor::num;
|
||||||
|
if (count < min1) {
|
||||||
|
ec = 1;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
count *= Factor::num;
|
||||||
|
}
|
||||||
|
|
||||||
|
// this can't go wrong, right? den>0 is checked earlier.
|
||||||
|
if (Factor::den != 1) {
|
||||||
|
count /= Factor::den;
|
||||||
|
}
|
||||||
|
// convert to the to type, safely
|
||||||
|
using ToRep = typename To::rep;
|
||||||
|
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
|
||||||
|
if (ec) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return To{tocount};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* safe duration_cast between floating point durations
|
||||||
|
*/
|
||||||
|
template <typename To, typename FromRep, typename FromPeriod,
|
||||||
|
FMT_ENABLE_IF(std::is_floating_point<FromRep>::value),
|
||||||
|
FMT_ENABLE_IF(std::is_floating_point<typename To::rep>::value)>
|
||||||
|
To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
||||||
|
int& ec) {
|
||||||
|
using From = std::chrono::duration<FromRep, FromPeriod>;
|
||||||
|
ec = 0;
|
||||||
|
if (std::isnan(from.count())) {
|
||||||
|
// nan in, gives nan out. easy.
|
||||||
|
return To{std::numeric_limits<typename To::rep>::quiet_NaN()};
|
||||||
|
}
|
||||||
|
// maybe we should also check if from is denormal, and decide what to do about
|
||||||
|
// it.
|
||||||
|
|
||||||
|
// +-inf should be preserved.
|
||||||
|
if (std::isinf(from.count())) {
|
||||||
|
return To{from.count()};
|
||||||
|
}
|
||||||
|
|
||||||
|
// the basic idea is that we need to convert from count() in the from type
|
||||||
|
// to count() in the To type, by multiplying it with this:
|
||||||
|
using Factor = std::ratio_divide<typename From::period, typename To::period>;
|
||||||
|
|
||||||
|
static_assert(Factor::num > 0, "num must be positive");
|
||||||
|
static_assert(Factor::den > 0, "den must be positive");
|
||||||
|
|
||||||
|
// the conversion is like this: multiply from.count() with Factor::num
|
||||||
|
// /Factor::den and convert it to To::rep, all this without
|
||||||
|
// overflow/underflow. let's start by finding a suitable type that can hold
|
||||||
|
// both To, From and Factor::num
|
||||||
|
using IntermediateRep =
|
||||||
|
typename std::common_type<typename From::rep, typename To::rep,
|
||||||
|
decltype(Factor::num)>::type;
|
||||||
|
|
||||||
|
// force conversion of From::rep -> IntermediateRep to be safe,
|
||||||
|
// even if it will never happen be narrowing in this context.
|
||||||
|
IntermediateRep count =
|
||||||
|
safe_float_conversion<IntermediateRep>(from.count(), ec);
|
||||||
|
if (ec) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// multiply with Factor::num without overflow or underflow
|
||||||
|
if (Factor::num != 1) {
|
||||||
|
constexpr auto max1 = std::numeric_limits<IntermediateRep>::max() /
|
||||||
|
static_cast<IntermediateRep>(Factor::num);
|
||||||
|
if (count > max1) {
|
||||||
|
ec = 1;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
constexpr auto min1 = std::numeric_limits<IntermediateRep>::lowest() /
|
||||||
|
static_cast<IntermediateRep>(Factor::num);
|
||||||
|
if (count < min1) {
|
||||||
|
ec = 1;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
count *= static_cast<IntermediateRep>(Factor::num);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this can't go wrong, right? den>0 is checked earlier.
|
||||||
|
if (Factor::den != 1) {
|
||||||
|
using common_t = typename std::common_type<IntermediateRep, intmax_t>::type;
|
||||||
|
count /= static_cast<common_t>(Factor::den);
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert to the to type, safely
|
||||||
|
using ToRep = typename To::rep;
|
||||||
|
|
||||||
|
const ToRep tocount = safe_float_conversion<ToRep>(count, ec);
|
||||||
|
if (ec) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return To{tocount};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace safe_duration_cast
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
@@ -1,199 +0,0 @@
|
|||||||
// Formatting library for C++ - time formatting
|
|
||||||
//
|
|
||||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// For the license information refer to format.h.
|
|
||||||
|
|
||||||
#ifndef FMT_TIME_H_
|
|
||||||
#define FMT_TIME_H_
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
#include <ctime>
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
inline null<> localtime_r(...)
|
|
||||||
{
|
|
||||||
return null<>();
|
|
||||||
}
|
|
||||||
inline null<> localtime_s(...)
|
|
||||||
{
|
|
||||||
return null<>();
|
|
||||||
}
|
|
||||||
inline null<> gmtime_r(...)
|
|
||||||
{
|
|
||||||
return null<>();
|
|
||||||
}
|
|
||||||
inline null<> gmtime_s(...)
|
|
||||||
{
|
|
||||||
return null<>();
|
|
||||||
}
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
// Thread-safe replacement for std::localtime
|
|
||||||
inline std::tm localtime(std::time_t time)
|
|
||||||
{
|
|
||||||
struct dispatcher
|
|
||||||
{
|
|
||||||
std::time_t time_;
|
|
||||||
std::tm tm_;
|
|
||||||
|
|
||||||
dispatcher(std::time_t t)
|
|
||||||
: time_(t)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool run()
|
|
||||||
{
|
|
||||||
using namespace fmt::internal;
|
|
||||||
return handle(localtime_r(&time_, &tm_));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool handle(std::tm *tm)
|
|
||||||
{
|
|
||||||
return tm != FMT_NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool handle(internal::null<>)
|
|
||||||
{
|
|
||||||
using namespace fmt::internal;
|
|
||||||
return fallback(localtime_s(&tm_, &time_));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fallback(int res)
|
|
||||||
{
|
|
||||||
return res == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fallback(internal::null<>)
|
|
||||||
{
|
|
||||||
using namespace fmt::internal;
|
|
||||||
std::tm *tm = std::localtime(&time_);
|
|
||||||
if (tm)
|
|
||||||
tm_ = *tm;
|
|
||||||
return tm != FMT_NULL;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
dispatcher lt(time);
|
|
||||||
if (lt.run())
|
|
||||||
return lt.tm_;
|
|
||||||
// Too big time values may be unsupported.
|
|
||||||
FMT_THROW(format_error("time_t value out of range"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thread-safe replacement for std::gmtime
|
|
||||||
inline std::tm gmtime(std::time_t time)
|
|
||||||
{
|
|
||||||
struct dispatcher
|
|
||||||
{
|
|
||||||
std::time_t time_;
|
|
||||||
std::tm tm_;
|
|
||||||
|
|
||||||
dispatcher(std::time_t t)
|
|
||||||
: time_(t)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool run()
|
|
||||||
{
|
|
||||||
using namespace fmt::internal;
|
|
||||||
return handle(gmtime_r(&time_, &tm_));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool handle(std::tm *tm)
|
|
||||||
{
|
|
||||||
return tm != FMT_NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool handle(internal::null<>)
|
|
||||||
{
|
|
||||||
using namespace fmt::internal;
|
|
||||||
return fallback(gmtime_s(&tm_, &time_));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fallback(int res)
|
|
||||||
{
|
|
||||||
return res == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fallback(internal::null<>)
|
|
||||||
{
|
|
||||||
std::tm *tm = std::gmtime(&time_);
|
|
||||||
if (tm)
|
|
||||||
tm_ = *tm;
|
|
||||||
return tm != FMT_NULL;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
dispatcher gt(time);
|
|
||||||
if (gt.run())
|
|
||||||
return gt.tm_;
|
|
||||||
// Too big time values may be unsupported.
|
|
||||||
FMT_THROW(format_error("time_t value out of range"));
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
inline std::size_t strftime(char *str, std::size_t count, const char *format, const std::tm *time)
|
|
||||||
{
|
|
||||||
return std::strftime(str, count, format, time);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline std::size_t strftime(wchar_t *str, std::size_t count, const wchar_t *format, const std::tm *time)
|
|
||||||
{
|
|
||||||
return std::wcsftime(str, count, format, time);
|
|
||||||
}
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
template<typename Char>
|
|
||||||
struct formatter<std::tm, Char>
|
|
||||||
{
|
|
||||||
template<typename ParseContext>
|
|
||||||
auto parse(ParseContext &ctx) -> decltype(ctx.begin())
|
|
||||||
{
|
|
||||||
auto it = internal::null_terminating_iterator<Char>(ctx);
|
|
||||||
if (*it == ':')
|
|
||||||
++it;
|
|
||||||
auto end = it;
|
|
||||||
while (*end && *end != '}')
|
|
||||||
++end;
|
|
||||||
tm_format.reserve(end - it + 1);
|
|
||||||
using internal::pointer_from;
|
|
||||||
tm_format.append(pointer_from(it), pointer_from(end));
|
|
||||||
tm_format.push_back('\0');
|
|
||||||
return pointer_from(end);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename FormatContext>
|
|
||||||
auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out())
|
|
||||||
{
|
|
||||||
internal::basic_buffer<Char> &buf = internal::get_container(ctx.out());
|
|
||||||
std::size_t start = buf.size();
|
|
||||||
for (;;)
|
|
||||||
{
|
|
||||||
std::size_t size = buf.capacity() - start;
|
|
||||||
std::size_t count = internal::strftime(&buf[start], size, &tm_format[0], &tm);
|
|
||||||
if (count != 0)
|
|
||||||
{
|
|
||||||
buf.resize(start + count);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (size >= tm_format.size() * 256)
|
|
||||||
{
|
|
||||||
// If the buffer is 256 times larger than the format string, assume
|
|
||||||
// that `strftime` gives an empty result. There doesn't seem to be a
|
|
||||||
// better way to distinguish the two cases:
|
|
||||||
// https://github.com/fmtlib/fmt/issues/367
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const std::size_t MIN_GROWTH = 10;
|
|
||||||
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
|
||||||
}
|
|
||||||
return ctx.out();
|
|
||||||
}
|
|
||||||
|
|
||||||
basic_memory_buffer<Char> tm_format;
|
|
||||||
};
|
|
||||||
FMT_END_NAMESPACE
|
|
||||||
|
|
||||||
#endif // FMT_TIME_H_
|
|
||||||
@@ -11,15 +11,17 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#if !defined(SPDLOG_FMT_EXTERNAL)
|
#if !defined(SPDLOG_FMT_EXTERNAL)
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
#ifndef FMT_HEADER_ONLY
|
#ifndef FMT_HEADER_ONLY
|
||||||
#define FMT_HEADER_ONLY
|
#define FMT_HEADER_ONLY
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
#ifndef FMT_USE_WINDOWS_H
|
#ifndef FMT_USE_WINDOWS_H
|
||||||
#define FMT_USE_WINDOWS_H 0
|
#define FMT_USE_WINDOWS_H 0
|
||||||
#endif
|
#endif
|
||||||
#include "bundled/core.h"
|
#include "bundled/core.h"
|
||||||
#include "bundled/format.h"
|
#include "bundled/format.h"
|
||||||
#else // external fmtlib
|
#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib
|
||||||
#include <fmt/core.h>
|
#include "fmt/core.h"
|
||||||
#include <fmt/format.h>
|
#include "fmt/format.h"
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
//
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@@ -14,7 +12,7 @@ class formatter
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~formatter() = default;
|
virtual ~formatter() = default;
|
||||||
virtual void format(const details::log_msg &msg, fmt::memory_buffer &dest) = 0;
|
virtual void format(const details::log_msg &msg, memory_buf_t &dest) = 0;
|
||||||
virtual std::unique_ptr<formatter> clone() const = 0;
|
virtual std::unique_ptr<formatter> clone() const = 0;
|
||||||
};
|
};
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|||||||
246
include/spdlog/logger-inl.h
Normal file
246
include/spdlog/logger-inl.h
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include "spdlog/logger.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "spdlog/sinks/sink.h"
|
||||||
|
#include "spdlog/details/backtracer.h"
|
||||||
|
#include "spdlog/details/pattern_formatter.h"
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
|
||||||
|
// public methods
|
||||||
|
SPDLOG_INLINE logger::logger(const logger &other)
|
||||||
|
: name_(other.name_)
|
||||||
|
, sinks_(other.sinks_)
|
||||||
|
, level_(other.level_.load(std::memory_order_relaxed))
|
||||||
|
, flush_level_(other.flush_level_.load(std::memory_order_relaxed))
|
||||||
|
, custom_err_handler_(other.custom_err_handler_)
|
||||||
|
, tracer_(other.tracer_)
|
||||||
|
{}
|
||||||
|
|
||||||
|
SPDLOG_INLINE logger::logger(logger &&other) SPDLOG_NOEXCEPT : name_(std::move(other.name_)),
|
||||||
|
sinks_(std::move(other.sinks_)),
|
||||||
|
level_(other.level_.load(std::memory_order_relaxed)),
|
||||||
|
flush_level_(other.flush_level_.load(std::memory_order_relaxed)),
|
||||||
|
custom_err_handler_(std::move(other.custom_err_handler_)),
|
||||||
|
tracer_(std::move(other.tracer_))
|
||||||
|
|
||||||
|
{}
|
||||||
|
|
||||||
|
SPDLOG_INLINE logger &logger::operator=(logger other) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
this->swap(other);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void logger::swap(spdlog::logger &other) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
name_.swap(other.name_);
|
||||||
|
sinks_.swap(other.sinks_);
|
||||||
|
|
||||||
|
// swap level_
|
||||||
|
auto other_level = other.level_.load();
|
||||||
|
auto my_level = level_.exchange(other_level);
|
||||||
|
other.level_.store(my_level);
|
||||||
|
|
||||||
|
// swap flush level_
|
||||||
|
other_level = other.flush_level_.load();
|
||||||
|
my_level = flush_level_.exchange(other_level);
|
||||||
|
other.flush_level_.store(my_level);
|
||||||
|
|
||||||
|
custom_err_handler_.swap(other.custom_err_handler_);
|
||||||
|
std::swap(tracer_, other.tracer_);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void swap(logger &a, logger &b)
|
||||||
|
{
|
||||||
|
a.swap(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE bool logger::should_log(level::level_enum msg_level) const
|
||||||
|
{
|
||||||
|
return msg_level >= level_.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void logger::set_level(level::level_enum log_level)
|
||||||
|
{
|
||||||
|
level_.store(log_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE level::level_enum logger::level() const
|
||||||
|
{
|
||||||
|
return static_cast<level::level_enum>(level_.load(std::memory_order_relaxed));
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE const std::string &logger::name() const
|
||||||
|
{
|
||||||
|
return name_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set formatting for the sinks in this logger.
|
||||||
|
// each sink will get a seperate instance of the formatter object.
|
||||||
|
SPDLOG_INLINE void logger::set_formatter(std::unique_ptr<formatter> f)
|
||||||
|
{
|
||||||
|
for (auto it = sinks_.begin(); it != sinks_.end(); ++it)
|
||||||
|
{
|
||||||
|
if (std::next(it) == sinks_.end())
|
||||||
|
{
|
||||||
|
// last element - we can be move it.
|
||||||
|
(*it)->set_formatter(std::move(f));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
(*it)->set_formatter(f->clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void logger::set_pattern(std::string pattern, pattern_time_type time_type)
|
||||||
|
{
|
||||||
|
auto new_formatter = details::make_unique<pattern_formatter>(std::move(pattern), time_type);
|
||||||
|
set_formatter(std::move(new_formatter));
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new backtrace sink and move to it all our child sinks
|
||||||
|
SPDLOG_INLINE void logger::enable_backtrace(size_t n_messages)
|
||||||
|
{
|
||||||
|
tracer_.enable(n_messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
// restore orig sinks and level and delete the backtrace sink
|
||||||
|
SPDLOG_INLINE void logger::disable_backtrace()
|
||||||
|
{
|
||||||
|
tracer_.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void logger::dump_backtrace()
|
||||||
|
{
|
||||||
|
dump_backtrace_();
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush functions
|
||||||
|
SPDLOG_INLINE void logger::flush()
|
||||||
|
{
|
||||||
|
flush_();
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void logger::flush_on(level::level_enum log_level)
|
||||||
|
{
|
||||||
|
flush_level_.store(log_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE level::level_enum logger::flush_level() const
|
||||||
|
{
|
||||||
|
return static_cast<level::level_enum>(flush_level_.load(std::memory_order_relaxed));
|
||||||
|
}
|
||||||
|
|
||||||
|
// sinks
|
||||||
|
SPDLOG_INLINE const std::vector<sink_ptr> &logger::sinks() const
|
||||||
|
{
|
||||||
|
return sinks_;
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE std::vector<sink_ptr> &logger::sinks()
|
||||||
|
{
|
||||||
|
return sinks_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// error handler
|
||||||
|
SPDLOG_INLINE void logger::set_error_handler(err_handler handler)
|
||||||
|
{
|
||||||
|
custom_err_handler_ = handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create new logger with same sinks and configuration.
|
||||||
|
SPDLOG_INLINE std::shared_ptr<logger> logger::clone(std::string logger_name)
|
||||||
|
{
|
||||||
|
auto cloned = std::make_shared<logger>(*this);
|
||||||
|
cloned->name_ = std::move(logger_name);
|
||||||
|
return cloned;
|
||||||
|
}
|
||||||
|
|
||||||
|
// protected methods
|
||||||
|
SPDLOG_INLINE void logger::sink_it_(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
for (auto &sink : sinks_)
|
||||||
|
{
|
||||||
|
if (sink->should_log(msg.level))
|
||||||
|
{
|
||||||
|
SPDLOG_TRY
|
||||||
|
{
|
||||||
|
sink->log(msg);
|
||||||
|
}
|
||||||
|
SPDLOG_LOGGER_CATCH()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (should_flush_(msg))
|
||||||
|
{
|
||||||
|
flush_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void logger::flush_()
|
||||||
|
{
|
||||||
|
for (auto &sink : sinks_)
|
||||||
|
{
|
||||||
|
SPDLOG_TRY
|
||||||
|
{
|
||||||
|
sink->flush();
|
||||||
|
}
|
||||||
|
SPDLOG_LOGGER_CATCH()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void logger::dump_backtrace_()
|
||||||
|
{
|
||||||
|
using details::log_msg;
|
||||||
|
if (tracer_)
|
||||||
|
{
|
||||||
|
sink_it_(log_msg{name(), level::info, "****************** Backtrace Start ******************"});
|
||||||
|
tracer_.foreach_pop([this](const details::log_msg &msg) { this->sink_it_(msg); });
|
||||||
|
sink_it_(log_msg{name(), level::info, "****************** Backtrace End ********************"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE bool logger::should_flush_(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
auto flush_level = flush_level_.load(std::memory_order_relaxed);
|
||||||
|
return (msg.level >= flush_level) && (msg.level != level::off);
|
||||||
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void logger::err_handler_(const std::string &msg)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (custom_err_handler_)
|
||||||
|
{
|
||||||
|
custom_err_handler_(msg);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
using std::chrono::system_clock;
|
||||||
|
static std::mutex mutex;
|
||||||
|
static std::chrono::system_clock::time_point last_report_time;
|
||||||
|
static size_t err_counter = 0;
|
||||||
|
std::lock_guard<std::mutex> lk{mutex};
|
||||||
|
auto now = system_clock::now();
|
||||||
|
err_counter++;
|
||||||
|
if (now - last_report_time < std::chrono::seconds(1))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
last_report_time = now;
|
||||||
|
auto tm_time = details::os::localtime(system_clock::to_time_t(now));
|
||||||
|
char date_buf[64];
|
||||||
|
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
|
||||||
|
fprintf(stderr, "[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace spdlog
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
//
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
// Copyright(c) 2015-2108 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@@ -15,148 +13,371 @@
|
|||||||
// and send to its destination.
|
// and send to its destination.
|
||||||
//
|
//
|
||||||
// The use of private formatter per sink provides the opportunity to cache some
|
// The use of private formatter per sink provides the opportunity to cache some
|
||||||
// formatted data,
|
// formatted data, and support for different format per sink.
|
||||||
// and support customize format per each sink.
|
|
||||||
|
|
||||||
#include "spdlog/common.h"
|
#include "spdlog/common.h"
|
||||||
#include "spdlog/formatter.h"
|
#include "spdlog/details/log_msg.h"
|
||||||
#include "spdlog/sinks/sink.h"
|
#include "spdlog/details/backtracer.h"
|
||||||
|
|
||||||
|
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
|
#include "spdlog/details/os.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#ifndef SPDLOG_NO_EXCEPTIONS
|
||||||
|
#define SPDLOG_LOGGER_CATCH() \
|
||||||
|
catch (const std::exception &ex) \
|
||||||
|
{ \
|
||||||
|
err_handler_(ex.what()); \
|
||||||
|
} \
|
||||||
|
catch (...) \
|
||||||
|
{ \
|
||||||
|
err_handler_("Unknown exception in logger"); \
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define SPDLOG_LOGGER_CATCH()
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
|
|
||||||
class logger
|
class logger
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
logger(std::string name, sink_ptr single_sink);
|
// Empty logger
|
||||||
logger(std::string name, sinks_init_list sinks);
|
explicit logger(std::string name)
|
||||||
|
: name_(std::move(name))
|
||||||
|
, sinks_()
|
||||||
|
{}
|
||||||
|
|
||||||
|
// Logger with range on sinks
|
||||||
template<typename It>
|
template<typename It>
|
||||||
logger(std::string name, const It &begin, const It &end);
|
logger(std::string name, It begin, It end)
|
||||||
|
: name_(std::move(name))
|
||||||
|
, sinks_(begin, end)
|
||||||
|
{}
|
||||||
|
|
||||||
virtual ~logger();
|
// Logger with single sink
|
||||||
|
logger(std::string name, sink_ptr single_sink)
|
||||||
|
: logger(std::move(name), {std::move(single_sink)})
|
||||||
|
{}
|
||||||
|
|
||||||
logger(const logger &) = delete;
|
// Logger with sinks init list
|
||||||
logger &operator=(const logger &) = delete;
|
logger(std::string name, sinks_init_list sinks)
|
||||||
|
: logger(std::move(name), sinks.begin(), sinks.end())
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual ~logger() = default;
|
||||||
|
|
||||||
|
logger(const logger &other);
|
||||||
|
logger(logger &&other) SPDLOG_NOEXCEPT;
|
||||||
|
logger &operator=(logger other) SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
|
void swap(spdlog::logger &other) SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void log(level::level_enum lvl, const char *fmt, const Args &... args);
|
void log(source_loc loc, level::level_enum lvl, string_view_t fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
auto level_enabled = should_log(lvl);
|
||||||
|
if (!level_enabled && !tracer_)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SPDLOG_TRY
|
||||||
|
{
|
||||||
|
memory_buf_t buf;
|
||||||
|
fmt::format_to(buf, fmt, args...);
|
||||||
|
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
||||||
|
if (level_enabled)
|
||||||
|
{
|
||||||
|
sink_it_(log_msg);
|
||||||
|
}
|
||||||
|
if (tracer_)
|
||||||
|
{
|
||||||
|
tracer_.push_back(log_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SPDLOG_LOGGER_CATCH()
|
||||||
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void log(level::level_enum lvl, const char *msg);
|
void log(level::level_enum lvl, string_view_t fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(source_loc{}, lvl, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void trace(const char *fmt, const Args &... args);
|
void trace(string_view_t fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::trace, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void debug(const char *fmt, const Args &... args);
|
void debug(string_view_t fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::debug, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void info(const char *fmt, const Args &... args);
|
void info(string_view_t fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::info, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void warn(const char *fmt, const Args &... args);
|
void warn(string_view_t fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::warn, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void error(const char *fmt, const Args &... args);
|
void error(string_view_t fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::err, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void critical(const char *fmt, const Args &... args);
|
void critical(string_view_t fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::critical, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void log(level::level_enum lvl, const T &msg)
|
||||||
|
{
|
||||||
|
log(source_loc{}, lvl, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// T can be statically converted to string_view
|
||||||
|
template<class T, typename std::enable_if<std::is_convertible<const T &, spdlog::string_view_t>::value, T>::type * = nullptr>
|
||||||
|
void log(source_loc loc, level::level_enum lvl, const T &msg)
|
||||||
|
{
|
||||||
|
auto level_enabled = should_log(lvl);
|
||||||
|
if (!level_enabled && !tracer_)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SPDLOG_TRY
|
||||||
|
{
|
||||||
|
details::log_msg log_msg(loc, name_, lvl, msg);
|
||||||
|
if (level_enabled)
|
||||||
|
{
|
||||||
|
sink_it_(log_msg);
|
||||||
|
}
|
||||||
|
if (tracer_)
|
||||||
|
{
|
||||||
|
tracer_.push_back(log_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SPDLOG_LOGGER_CATCH()
|
||||||
|
}
|
||||||
|
|
||||||
|
void log(level::level_enum lvl, string_view_t msg)
|
||||||
|
{
|
||||||
|
log(source_loc{}, lvl, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// T cannot be statically converted to string_view or wstring_view
|
||||||
|
template<class T, typename std::enable_if<!std::is_convertible<const T &, spdlog::string_view_t>::value &&
|
||||||
|
!is_convertible_to_wstring_view<const T &>::value,
|
||||||
|
T>::type * = nullptr>
|
||||||
|
void log(source_loc loc, level::level_enum lvl, const T &msg)
|
||||||
|
{
|
||||||
|
log(loc, lvl, "{}", msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void trace(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::trace, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void debug(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::debug, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void info(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::info, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void warn(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::warn, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void error(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::err, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void critical(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::critical, msg);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
template<typename... Args>
|
#ifndef _WIN32
|
||||||
void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args);
|
#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
|
||||||
|
#else
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void trace(const wchar_t *fmt, const Args &... args);
|
void log(source_loc loc, level::level_enum lvl, wstring_view_t fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
auto level_enabled = should_log(lvl);
|
||||||
|
if (!level_enabled && !tracer_)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SPDLOG_TRY
|
||||||
|
{
|
||||||
|
// format to wmemory_buffer and convert to utf8
|
||||||
|
fmt::wmemory_buffer wbuf;
|
||||||
|
fmt::format_to(wbuf, fmt, args...);
|
||||||
|
|
||||||
|
memory_buf_t buf;
|
||||||
|
details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf);
|
||||||
|
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
||||||
|
|
||||||
|
if (level_enabled)
|
||||||
|
{
|
||||||
|
sink_it_(log_msg);
|
||||||
|
}
|
||||||
|
if (tracer_)
|
||||||
|
{
|
||||||
|
tracer_.push_back(log_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SPDLOG_LOGGER_CATCH()
|
||||||
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void debug(const wchar_t *fmt, const Args &... args);
|
void log(level::level_enum lvl, wstring_view_t fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(source_loc{}, lvl, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void info(const wchar_t *fmt, const Args &... args);
|
void trace(wstring_view_t fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::trace, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void warn(const wchar_t *fmt, const Args &... args);
|
void debug(wstring_view_t fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::debug, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void error(const wchar_t *fmt, const Args &... args);
|
void info(wstring_view_t fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::info, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void critical(const wchar_t *fmt, const Args &... args);
|
void warn(wstring_view_t fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::warn, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
void error(wstring_view_t fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::err, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
void critical(wstring_view_t fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::critical, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// T can be statically converted to wstring_view
|
||||||
|
template<class T, typename std::enable_if<is_convertible_to_wstring_view<const T &>::value, T>::type * = nullptr>
|
||||||
|
void log(source_loc loc, level::level_enum lvl, const T &msg)
|
||||||
|
{
|
||||||
|
if (!should_log(lvl))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
memory_buf_t buf;
|
||||||
|
details::os::wstr_to_utf8buf(msg, buf);
|
||||||
|
|
||||||
|
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
||||||
|
sink_it_(log_msg);
|
||||||
|
}
|
||||||
|
SPDLOG_LOGGER_CATCH()
|
||||||
|
}
|
||||||
|
#endif // _WIN32
|
||||||
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void log(level::level_enum lvl, const T &);
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void trace(const T &msg);
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void debug(const T &msg);
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void info(const T &msg);
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void warn(const T &msg);
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void error(const T &msg);
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
void critical(const T &msg);
|
|
||||||
|
|
||||||
bool should_log(level::level_enum msg_level) const;
|
bool should_log(level::level_enum msg_level) const;
|
||||||
|
|
||||||
void set_level(level::level_enum log_level);
|
void set_level(level::level_enum log_level);
|
||||||
|
|
||||||
level::level_enum level() const;
|
level::level_enum level() const;
|
||||||
|
|
||||||
const std::string &name() const;
|
const std::string &name() const;
|
||||||
|
|
||||||
// set formatting for the sinks in this logger.
|
// set formatting for the sinks in this logger.
|
||||||
// each sink will get a seperate instance of the formatter object.
|
// each sink will get a seperate instance of the formatter object.
|
||||||
void set_formatter(std::unique_ptr<formatter> formatter);
|
void set_formatter(std::unique_ptr<formatter> f);
|
||||||
|
|
||||||
void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);
|
void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);
|
||||||
|
|
||||||
|
// backtrace support.
|
||||||
|
// efficiently store all debug/trace messages in a circular buffer until needed for debugging.
|
||||||
|
void enable_backtrace(size_t n_messages);
|
||||||
|
void disable_backtrace();
|
||||||
|
void dump_backtrace();
|
||||||
|
|
||||||
|
// flush functions
|
||||||
void flush();
|
void flush();
|
||||||
void flush_on(level::level_enum log_level);
|
void flush_on(level::level_enum log_level);
|
||||||
|
level::level_enum flush_level() const;
|
||||||
|
|
||||||
|
// sinks
|
||||||
const std::vector<sink_ptr> &sinks() const;
|
const std::vector<sink_ptr> &sinks() const;
|
||||||
|
|
||||||
std::vector<sink_ptr> &sinks();
|
std::vector<sink_ptr> &sinks();
|
||||||
|
|
||||||
void set_error_handler(log_err_handler err_handler);
|
// error handler
|
||||||
log_err_handler error_handler();
|
void set_error_handler(err_handler);
|
||||||
|
|
||||||
|
// create new logger with same sinks and configuration.
|
||||||
|
virtual std::shared_ptr<logger> clone(std::string logger_name);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void sink_it_(details::log_msg &msg);
|
std::string name_;
|
||||||
virtual void flush_();
|
std::vector<sink_ptr> sinks_;
|
||||||
|
spdlog::level_t level_{level::info};
|
||||||
|
spdlog::level_t flush_level_{level::off};
|
||||||
|
err_handler custom_err_handler_{nullptr};
|
||||||
|
details::backtracer tracer_;
|
||||||
|
|
||||||
|
virtual void sink_it_(const details::log_msg &msg);
|
||||||
|
virtual void flush_();
|
||||||
|
void dump_backtrace_();
|
||||||
bool should_flush_(const details::log_msg &msg);
|
bool should_flush_(const details::log_msg &msg);
|
||||||
|
|
||||||
// default error handler: print the error to stderr with the max rate of 1
|
// handle errors during logging.
|
||||||
// message/minute
|
// default handler prints the error to stderr at max rate of 1 message/sec.
|
||||||
void default_err_handler_(const std::string &msg);
|
void err_handler_(const std::string &msg);
|
||||||
|
|
||||||
// increment the message count (only if
|
|
||||||
// defined(SPDLOG_ENABLE_MESSAGE_COUNTER))
|
|
||||||
void incr_msg_counter_(details::log_msg &msg);
|
|
||||||
|
|
||||||
const std::string name_;
|
|
||||||
std::vector<sink_ptr> sinks_;
|
|
||||||
spdlog::level_t level_;
|
|
||||||
spdlog::level_t flush_level_;
|
|
||||||
log_err_handler err_handler_;
|
|
||||||
std::atomic<time_t> last_err_time_;
|
|
||||||
std::atomic<size_t> msg_counter_;
|
|
||||||
|
|
||||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
|
||||||
std::wstring_convert<std::codecvt_utf8<wchar_t>> wstring_converter_;
|
|
||||||
std::mutex wstring_converter_mutex_;
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void swap(logger &a, logger &b);
|
||||||
|
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
#include "details/logger_impl.h"
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "logger-inl.h"
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
//
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __ANDROID__
|
||||||
|
|
||||||
#include "spdlog/details/fmt_helper.h"
|
#include "spdlog/details/fmt_helper.h"
|
||||||
#include "spdlog/details/null_mutex.h"
|
#include "spdlog/details/null_mutex.h"
|
||||||
#include "spdlog/details/os.h"
|
#include "spdlog/details/os.h"
|
||||||
#include "spdlog/sinks/base_sink.h"
|
#include "spdlog/sinks/base_sink.h"
|
||||||
|
#include "spdlog/details/synchronous_factory.h"
|
||||||
|
|
||||||
#include <android/log.h>
|
#include <android/log.h>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@@ -27,27 +28,26 @@ namespace sinks {
|
|||||||
* Android sink (logging using __android_log_write)
|
* Android sink (logging using __android_log_write)
|
||||||
*/
|
*/
|
||||||
template<typename Mutex>
|
template<typename Mutex>
|
||||||
class android_sink SPDLOG_FINAL : public base_sink<Mutex>
|
class android_sink final : public base_sink<Mutex>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit android_sink(const std::string &tag = "spdlog", bool use_raw_msg = false)
|
explicit android_sink(std::string tag = "spdlog", bool use_raw_msg = false)
|
||||||
: tag_(tag)
|
: tag_(std::move(tag))
|
||||||
, use_raw_msg_(use_raw_msg)
|
, use_raw_msg_(use_raw_msg)
|
||||||
{
|
{}
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void sink_it_(const details::log_msg &msg) override
|
void sink_it_(const details::log_msg &msg) override
|
||||||
{
|
{
|
||||||
const android_LogPriority priority = convert_to_android_(msg.level);
|
const android_LogPriority priority = convert_to_android_(msg.level);
|
||||||
fmt::memory_buffer formatted;
|
memory_buf_t formatted;
|
||||||
if (use_raw_msg_)
|
if (use_raw_msg_)
|
||||||
{
|
{
|
||||||
fmt_helper::append_buf(msg.raw, formatted);
|
details::fmt_helper::append_string_view(msg.payload, formatted);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
formatter_->format(msg, formatted);
|
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||||
}
|
}
|
||||||
formatted.push_back('\0');
|
formatted.push_back('\0');
|
||||||
const char *msg_output = formatted.data();
|
const char *msg_output = formatted.data();
|
||||||
@@ -64,7 +64,7 @@ protected:
|
|||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
{
|
{
|
||||||
throw spdlog_ex("__android_log_write() failed", ret);
|
SPDLOG_THROW(spdlog_ex("__android_log_write() failed", ret));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,16 +102,18 @@ using android_sink_st = android_sink<details::null_mutex>;
|
|||||||
|
|
||||||
// Create and register android syslog logger
|
// Create and register android syslog logger
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
inline std::shared_ptr<logger> android_logger_mt(const std::string &logger_name, const std::string &tag = "spdlog")
|
inline std::shared_ptr<logger> android_logger_mt(const std::string &logger_name, const std::string &tag = "spdlog")
|
||||||
{
|
{
|
||||||
return Factory::template create<sinks::android_sink_mt>(logger_name, tag);
|
return Factory::template create<sinks::android_sink_mt>(logger_name, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
inline std::shared_ptr<logger> android_logger_st(const std::string &logger_name, const std::string &tag = "spdlog")
|
inline std::shared_ptr<logger> android_logger_st(const std::string &logger_name, const std::string &tag = "spdlog")
|
||||||
{
|
{
|
||||||
return Factory::template create<sinks::android_sink_st>(logger_name, tag);
|
return Factory::template create<sinks::android_sink_st>(logger_name, tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#endif // __ANDROID__
|
||||||
136
include/spdlog/sinks/ansicolor_sink-inl.h
Normal file
136
include/spdlog/sinks/ansicolor_sink-inl.h
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include "spdlog/sinks/ansicolor_sink.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "spdlog/details/pattern_formatter.h"
|
||||||
|
#include "spdlog/details/os.h"
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE ansicolor_sink<ConsoleMutex>::ansicolor_sink(FILE *target_file, color_mode mode)
|
||||||
|
: target_file_(target_file)
|
||||||
|
, mutex_(ConsoleMutex::mutex())
|
||||||
|
, formatter_(details::make_unique<spdlog::pattern_formatter>())
|
||||||
|
|
||||||
|
{
|
||||||
|
set_color_mode(mode);
|
||||||
|
colors_[level::trace] = white;
|
||||||
|
colors_[level::debug] = cyan;
|
||||||
|
colors_[level::info] = green;
|
||||||
|
colors_[level::warn] = yellow_bold;
|
||||||
|
colors_[level::err] = red_bold;
|
||||||
|
colors_[level::critical] = bold_on_red;
|
||||||
|
colors_[level::off] = reset;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color(level::level_enum color_level, string_view_t color)
|
||||||
|
{
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
colors_[color_level] = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
// Wrap the originally formatted message in color codes.
|
||||||
|
// If color is not supported in the terminal, log as is instead.
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
|
||||||
|
memory_buf_t formatted;
|
||||||
|
formatter_->format(msg, formatted);
|
||||||
|
if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
|
||||||
|
{
|
||||||
|
// before color range
|
||||||
|
print_range_(formatted, 0, msg.color_range_start);
|
||||||
|
// in color range
|
||||||
|
print_ccode_(colors_[msg.level]);
|
||||||
|
print_range_(formatted, msg.color_range_start, msg.color_range_end);
|
||||||
|
print_ccode_(reset);
|
||||||
|
// after color range
|
||||||
|
print_range_(formatted, msg.color_range_end, formatted.size());
|
||||||
|
}
|
||||||
|
else // no color
|
||||||
|
{
|
||||||
|
print_range_(formatted, 0, formatted.size());
|
||||||
|
}
|
||||||
|
fflush(target_file_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::flush()
|
||||||
|
{
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
fflush(target_file_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_pattern(const std::string &pattern)
|
||||||
|
{
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
|
||||||
|
{
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
formatter_ = std::move(sink_formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE bool ansicolor_sink<ConsoleMutex>::should_color()
|
||||||
|
{
|
||||||
|
return should_do_colors_;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case color_mode::always:
|
||||||
|
should_do_colors_ = true;
|
||||||
|
return;
|
||||||
|
case color_mode::automatic:
|
||||||
|
should_do_colors_ = details::os::in_terminal(target_file_) && details::os::is_color_terminal();
|
||||||
|
return;
|
||||||
|
case color_mode::never:
|
||||||
|
should_do_colors_ = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::print_ccode_(const string_view_t &color_code)
|
||||||
|
{
|
||||||
|
fwrite(color_code.data(), sizeof(string_view_t::char_type), color_code.size(), target_file_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::print_range_(const memory_buf_t &formatted, size_t start, size_t end)
|
||||||
|
{
|
||||||
|
fwrite(formatted.data() + start, sizeof(char), end - start, target_file_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ansicolor_stdout_sink
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE ansicolor_stdout_sink<ConsoleMutex>::ansicolor_stdout_sink(color_mode mode)
|
||||||
|
: ansicolor_sink<ConsoleMutex>(stdout, mode)
|
||||||
|
{}
|
||||||
|
|
||||||
|
// ansicolor_stderr_sink
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
SPDLOG_INLINE ansicolor_stderr_sink<ConsoleMutex>::ansicolor_stderr_sink(color_mode mode)
|
||||||
|
: ansicolor_sink<ConsoleMutex>(stderr, mode)
|
||||||
|
{}
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
||||||
@@ -1,14 +1,11 @@
|
|||||||
//
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
// Copyright(c) 2017 spdlog authors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "spdlog/details/console_globals.h"
|
#include "spdlog/details/console_globals.h"
|
||||||
#include "spdlog/details/null_mutex.h"
|
#include "spdlog/details/null_mutex.h"
|
||||||
#include "spdlog/details/os.h"
|
#include "spdlog/sinks/sink.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -23,134 +20,94 @@ namespace sinks {
|
|||||||
* of the message.
|
* of the message.
|
||||||
* If no color terminal detected, omit the escape codes.
|
* If no color terminal detected, omit the escape codes.
|
||||||
*/
|
*/
|
||||||
template<typename TargetStream, class ConsoleMutex>
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
class ansicolor_sink : public sink
|
class ansicolor_sink : public sink
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using mutex_t = typename ConsoleMutex::mutex_t;
|
using mutex_t = typename ConsoleMutex::mutex_t;
|
||||||
ansicolor_sink()
|
ansicolor_sink(FILE *target_file, color_mode mode);
|
||||||
: target_file_(TargetStream::stream())
|
|
||||||
, mutex_(ConsoleMutex::mutex())
|
|
||||||
|
|
||||||
{
|
|
||||||
should_do_colors_ = details::os::in_terminal(target_file_) && details::os::is_color_terminal();
|
|
||||||
colors_[level::trace] = white;
|
|
||||||
colors_[level::debug] = cyan;
|
|
||||||
colors_[level::info] = green;
|
|
||||||
colors_[level::warn] = yellow + bold;
|
|
||||||
colors_[level::err] = red + bold;
|
|
||||||
colors_[level::critical] = bold + on_red;
|
|
||||||
colors_[level::off] = reset;
|
|
||||||
}
|
|
||||||
|
|
||||||
~ansicolor_sink() override = default;
|
~ansicolor_sink() override = default;
|
||||||
|
|
||||||
ansicolor_sink(const ansicolor_sink &other) = delete;
|
ansicolor_sink(const ansicolor_sink &other) = delete;
|
||||||
ansicolor_sink &operator=(const ansicolor_sink &other) = delete;
|
ansicolor_sink &operator=(const ansicolor_sink &other) = delete;
|
||||||
|
void set_color(level::level_enum color_level, string_view_t color);
|
||||||
|
void set_color_mode(color_mode mode);
|
||||||
|
bool should_color();
|
||||||
|
|
||||||
void set_color(level::level_enum color_level, const std::string &color)
|
void log(const details::log_msg &msg) override;
|
||||||
{
|
void flush() override;
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
void set_pattern(const std::string &pattern) final;
|
||||||
colors_[color_level] = color;
|
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override;
|
||||||
}
|
|
||||||
|
|
||||||
/// Formatting codes
|
// Formatting codes
|
||||||
const std::string reset = "\033[m";
|
const string_view_t reset = "\033[m";
|
||||||
const std::string bold = "\033[1m";
|
const string_view_t bold = "\033[1m";
|
||||||
const std::string dark = "\033[2m";
|
const string_view_t dark = "\033[2m";
|
||||||
const std::string underline = "\033[4m";
|
const string_view_t underline = "\033[4m";
|
||||||
const std::string blink = "\033[5m";
|
const string_view_t blink = "\033[5m";
|
||||||
const std::string reverse = "\033[7m";
|
const string_view_t reverse = "\033[7m";
|
||||||
const std::string concealed = "\033[8m";
|
const string_view_t concealed = "\033[8m";
|
||||||
const std::string clear_line = "\033[K";
|
const string_view_t clear_line = "\033[K";
|
||||||
|
|
||||||
// Foreground colors
|
// Foreground colors
|
||||||
const std::string black = "\033[30m";
|
const string_view_t black = "\033[30m";
|
||||||
const std::string red = "\033[31m";
|
const string_view_t red = "\033[31m";
|
||||||
const std::string green = "\033[32m";
|
const string_view_t green = "\033[32m";
|
||||||
const std::string yellow = "\033[33m";
|
const string_view_t yellow = "\033[33m";
|
||||||
const std::string blue = "\033[34m";
|
const string_view_t blue = "\033[34m";
|
||||||
const std::string magenta = "\033[35m";
|
const string_view_t magenta = "\033[35m";
|
||||||
const std::string cyan = "\033[36m";
|
const string_view_t cyan = "\033[36m";
|
||||||
const std::string white = "\033[37m";
|
const string_view_t white = "\033[37m";
|
||||||
|
|
||||||
/// Background colors
|
/// Background colors
|
||||||
const std::string on_black = "\033[40m";
|
const string_view_t on_black = "\033[40m";
|
||||||
const std::string on_red = "\033[41m";
|
const string_view_t on_red = "\033[41m";
|
||||||
const std::string on_green = "\033[42m";
|
const string_view_t on_green = "\033[42m";
|
||||||
const std::string on_yellow = "\033[43m";
|
const string_view_t on_yellow = "\033[43m";
|
||||||
const std::string on_blue = "\033[44m";
|
const string_view_t on_blue = "\033[44m";
|
||||||
const std::string on_magenta = "\033[45m";
|
const string_view_t on_magenta = "\033[45m";
|
||||||
const std::string on_cyan = "\033[46m";
|
const string_view_t on_cyan = "\033[46m";
|
||||||
const std::string on_white = "\033[47m";
|
const string_view_t on_white = "\033[47m";
|
||||||
|
|
||||||
void log(const details::log_msg &msg) SPDLOG_FINAL override
|
/// Bold colors
|
||||||
{
|
const string_view_t yellow_bold = "\033[33m\033[1m";
|
||||||
// Wrap the originally formatted message in color codes.
|
const string_view_t red_bold = "\033[31m\033[1m";
|
||||||
// If color is not supported in the terminal, log as is instead.
|
const string_view_t bold_on_red = "\033[1m\033[41m";
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
|
|
||||||
fmt::memory_buffer formatted;
|
|
||||||
formatter_->format(msg, formatted);
|
|
||||||
if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
|
|
||||||
{
|
|
||||||
// before color range
|
|
||||||
print_range_(formatted, 0, msg.color_range_start);
|
|
||||||
// in color range
|
|
||||||
print_ccode_(colors_[msg.level]);
|
|
||||||
print_range_(formatted, msg.color_range_start, msg.color_range_end);
|
|
||||||
print_ccode_(reset);
|
|
||||||
// after color range
|
|
||||||
print_range_(formatted, msg.color_range_end, formatted.size());
|
|
||||||
}
|
|
||||||
else // no color
|
|
||||||
{
|
|
||||||
print_range_(formatted, 0, formatted.size());
|
|
||||||
}
|
|
||||||
fflush(target_file_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush() SPDLOG_FINAL override
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
fflush(target_file_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_pattern(const std::string &pattern) override SPDLOG_FINAL
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override SPDLOG_FINAL
|
|
||||||
{
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
|
||||||
formatter_ = std::move(sink_formatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void print_ccode_(const std::string &color_code)
|
|
||||||
{
|
|
||||||
fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_);
|
|
||||||
}
|
|
||||||
void print_range_(const fmt::memory_buffer &formatted, size_t start, size_t end)
|
|
||||||
{
|
|
||||||
fwrite(formatted.data() + start, sizeof(char), end - start, target_file_);
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *target_file_;
|
FILE *target_file_;
|
||||||
mutex_t &mutex_;
|
mutex_t &mutex_;
|
||||||
|
|
||||||
bool should_do_colors_;
|
bool should_do_colors_;
|
||||||
std::unordered_map<level::level_enum, std::string, level::level_hasher> colors_;
|
std::unique_ptr<spdlog::formatter> formatter_;
|
||||||
|
std::unordered_map<level::level_enum, string_view_t, level::level_hasher> colors_;
|
||||||
|
void print_ccode_(const string_view_t &color_code);
|
||||||
|
void print_range_(const memory_buf_t &formatted, size_t start, size_t end);
|
||||||
};
|
};
|
||||||
|
|
||||||
using ansicolor_stdout_sink_mt = ansicolor_sink<details::console_stdout, details::console_mutex>;
|
template<typename ConsoleMutex>
|
||||||
using ansicolor_stdout_sink_st = ansicolor_sink<details::console_stdout, details::console_nullmutex>;
|
class ansicolor_stdout_sink : public ansicolor_sink<ConsoleMutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ansicolor_stdout_sink(color_mode mode = color_mode::automatic);
|
||||||
|
};
|
||||||
|
|
||||||
using ansicolor_stderr_sink_mt = ansicolor_sink<details::console_stderr, details::console_mutex>;
|
template<typename ConsoleMutex>
|
||||||
using ansicolor_stderr_sink_st = ansicolor_sink<details::console_stderr, details::console_nullmutex>;
|
class ansicolor_stderr_sink : public ansicolor_sink<ConsoleMutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit ansicolor_stderr_sink(color_mode mode = color_mode::automatic);
|
||||||
|
};
|
||||||
|
|
||||||
|
using ansicolor_stdout_sink_mt = ansicolor_stdout_sink<details::console_mutex>;
|
||||||
|
using ansicolor_stdout_sink_st = ansicolor_stdout_sink<details::console_nullmutex>;
|
||||||
|
|
||||||
|
using ansicolor_stderr_sink_mt = ansicolor_stderr_sink<details::console_mutex>;
|
||||||
|
using ansicolor_stderr_sink_st = ansicolor_stderr_sink<details::console_nullmutex>;
|
||||||
|
|
||||||
} // namespace sinks
|
} // namespace sinks
|
||||||
|
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "ansicolor_sink-inl.h"
|
||||||
|
#endif
|
||||||
|
|||||||
63
include/spdlog/sinks/base_sink-inl.h
Normal file
63
include/spdlog/sinks/base_sink-inl.h
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include "spdlog/sinks/base_sink.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "spdlog/common.h"
|
||||||
|
#include "spdlog/details/pattern_formatter.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::base_sink()
|
||||||
|
: formatter_{details::make_unique<spdlog::pattern_formatter>()}
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::base_sink(std::unique_ptr<spdlog::formatter> formatter)
|
||||||
|
: formatter_{std::move(formatter)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::log(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
std::lock_guard<Mutex> lock(mutex_);
|
||||||
|
sink_it_(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::flush()
|
||||||
|
{
|
||||||
|
std::lock_guard<Mutex> lock(mutex_);
|
||||||
|
flush_();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_pattern(const std::string &pattern)
|
||||||
|
{
|
||||||
|
std::lock_guard<Mutex> lock(mutex_);
|
||||||
|
set_pattern_(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter)
|
||||||
|
{
|
||||||
|
std::lock_guard<Mutex> lock(mutex_);
|
||||||
|
set_formatter_(std::move(sink_formatter));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_pattern_(const std::string &pattern)
|
||||||
|
{
|
||||||
|
set_formatter_(details::make_unique<spdlog::pattern_formatter>(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
void SPDLOG_INLINE spdlog::sinks::base_sink<Mutex>::set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter)
|
||||||
|
{
|
||||||
|
formatter_ = std::move(sink_formatter);
|
||||||
|
}
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
//
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
//
|
//
|
||||||
@@ -13,51 +11,37 @@
|
|||||||
|
|
||||||
#include "spdlog/common.h"
|
#include "spdlog/common.h"
|
||||||
#include "spdlog/details/log_msg.h"
|
#include "spdlog/details/log_msg.h"
|
||||||
#include "spdlog/formatter.h"
|
|
||||||
#include "spdlog/sinks/sink.h"
|
#include "spdlog/sinks/sink.h"
|
||||||
|
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace sinks {
|
namespace sinks {
|
||||||
template<typename Mutex>
|
template<typename Mutex>
|
||||||
class base_sink : public sink
|
class base_sink : public sink
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
base_sink()
|
base_sink();
|
||||||
: sink()
|
explicit base_sink(std::unique_ptr<spdlog::formatter> formatter);
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
base_sink(const base_sink &) = delete;
|
base_sink(const base_sink &) = delete;
|
||||||
base_sink &operator=(const base_sink &) = delete;
|
base_sink &operator=(const base_sink &) = delete;
|
||||||
|
void log(const details::log_msg &msg) final;
|
||||||
void log(const details::log_msg &msg) SPDLOG_FINAL override
|
void flush() final;
|
||||||
{
|
void set_pattern(const std::string &pattern) final;
|
||||||
std::lock_guard<Mutex> lock(mutex_);
|
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) final;
|
||||||
sink_it_(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush() SPDLOG_FINAL override
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(mutex_);
|
|
||||||
flush_();
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_pattern(const std::string &pattern) SPDLOG_FINAL override
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(mutex_);
|
|
||||||
formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) SPDLOG_FINAL override
|
|
||||||
{
|
|
||||||
std::lock_guard<Mutex> lock(mutex_);
|
|
||||||
formatter_ = std::move(sink_formatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
// sink formatter
|
||||||
|
std::unique_ptr<spdlog::formatter> formatter_;
|
||||||
|
Mutex mutex_;
|
||||||
|
|
||||||
virtual void sink_it_(const details::log_msg &msg) = 0;
|
virtual void sink_it_(const details::log_msg &msg) = 0;
|
||||||
virtual void flush_() = 0;
|
virtual void flush_() = 0;
|
||||||
Mutex mutex_;
|
virtual void set_pattern_(const std::string &pattern);
|
||||||
|
virtual void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter);
|
||||||
};
|
};
|
||||||
} // namespace sinks
|
} // namespace sinks
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "base_sink-inl.h"
|
||||||
|
#endif
|
||||||
|
|||||||
43
include/spdlog/sinks/basic_file_sink-inl.h
Normal file
43
include/spdlog/sinks/basic_file_sink-inl.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_HEADER_ONLY
|
||||||
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "spdlog/common.h"
|
||||||
|
#include "spdlog/details/os.h"
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE basic_file_sink<Mutex>::basic_file_sink(const filename_t &filename, bool truncate)
|
||||||
|
{
|
||||||
|
file_helper_.open(filename, truncate);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE const filename_t &basic_file_sink<Mutex>::filename() const
|
||||||
|
{
|
||||||
|
return file_helper_.filename();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE void basic_file_sink<Mutex>::sink_it_(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
memory_buf_t formatted;
|
||||||
|
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||||
|
file_helper_.write(formatted);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Mutex>
|
||||||
|
SPDLOG_INLINE void basic_file_sink<Mutex>::flush_()
|
||||||
|
{
|
||||||
|
file_helper_.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
//
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
// Copyright(c) 2015-2018 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "spdlog/details/file_helper.h"
|
#include "spdlog/details/file_helper.h"
|
||||||
#include "spdlog/details/null_mutex.h"
|
#include "spdlog/details/null_mutex.h"
|
||||||
#include "spdlog/sinks/base_sink.h"
|
#include "spdlog/sinks/base_sink.h"
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/details/synchronous_factory.h"
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -18,26 +17,15 @@ namespace sinks {
|
|||||||
* Trivial file sink with single file as target
|
* Trivial file sink with single file as target
|
||||||
*/
|
*/
|
||||||
template<typename Mutex>
|
template<typename Mutex>
|
||||||
class basic_file_sink SPDLOG_FINAL : public base_sink<Mutex>
|
class basic_file_sink final : public base_sink<Mutex>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit basic_file_sink(const filename_t &filename, bool truncate = false)
|
explicit basic_file_sink(const filename_t &filename, bool truncate = false);
|
||||||
{
|
const filename_t &filename() const;
|
||||||
file_helper_.open(filename, truncate);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void sink_it_(const details::log_msg &msg) override
|
void sink_it_(const details::log_msg &msg) override;
|
||||||
{
|
void flush_() override;
|
||||||
fmt::memory_buffer formatted;
|
|
||||||
sink::formatter_->format(msg, formatted);
|
|
||||||
file_helper_.write(formatted);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flush_() override
|
|
||||||
{
|
|
||||||
file_helper_.flush();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
details::file_helper file_helper_;
|
details::file_helper file_helper_;
|
||||||
@@ -51,16 +39,20 @@ using basic_file_sink_st = basic_file_sink<details::null_mutex>;
|
|||||||
//
|
//
|
||||||
// factory functions
|
// factory functions
|
||||||
//
|
//
|
||||||
template<typename Factory = default_factory>
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
inline std::shared_ptr<logger> basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false)
|
inline std::shared_ptr<logger> basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false)
|
||||||
{
|
{
|
||||||
return Factory::template create<sinks::basic_file_sink_mt>(logger_name, filename, truncate);
|
return Factory::template create<sinks::basic_file_sink_mt>(logger_name, filename, truncate);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
inline std::shared_ptr<logger> basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false)
|
inline std::shared_ptr<logger> basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false)
|
||||||
{
|
{
|
||||||
return Factory::template create<sinks::basic_file_sink_st>(logger_name, filename, truncate);
|
return Factory::template create<sinks::basic_file_sink_st>(logger_name, filename, truncate);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
|
#ifdef SPDLOG_HEADER_ONLY
|
||||||
|
#include "basic_file_sink-inl.h"
|
||||||
|
#endif
|
||||||
@@ -1,14 +1,15 @@
|
|||||||
//
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "spdlog/common.h"
|
||||||
#include "spdlog/details/file_helper.h"
|
#include "spdlog/details/file_helper.h"
|
||||||
#include "spdlog/details/null_mutex.h"
|
#include "spdlog/details/null_mutex.h"
|
||||||
#include "spdlog/fmt/fmt.h"
|
#include "spdlog/fmt/fmt.h"
|
||||||
#include "spdlog/sinks/base_sink.h"
|
#include "spdlog/sinks/base_sink.h"
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/details/os.h"
|
||||||
|
#include "spdlog/details/synchronous_factory.h"
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -28,49 +29,77 @@ struct daily_filename_calculator
|
|||||||
static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
|
static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
|
||||||
{
|
{
|
||||||
filename_t basename, ext;
|
filename_t basename, ext;
|
||||||
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
|
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
|
||||||
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w;
|
return fmt::format(
|
||||||
fmt::format_to(
|
SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, ext);
|
||||||
w, SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, ext);
|
|
||||||
return fmt::to_string(w);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Rotating file sink based on date. rotates at midnight
|
* Rotating file sink based on date.
|
||||||
|
* If truncate != false , the created file will be truncated.
|
||||||
|
* If max_files > 0, retain only the last max_files and delete previous.
|
||||||
*/
|
*/
|
||||||
template<typename Mutex, typename FileNameCalc = daily_filename_calculator>
|
template<typename Mutex, typename FileNameCalc = daily_filename_calculator>
|
||||||
class daily_file_sink SPDLOG_FINAL : public base_sink<Mutex>
|
class daily_file_sink final : public base_sink<Mutex>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// create daily file sink which rotates on given time
|
// create daily file sink which rotates on given time
|
||||||
daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false)
|
daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute, bool truncate = false, uint16_t max_files = 0)
|
||||||
: base_filename_(std::move(base_filename))
|
: base_filename_(std::move(base_filename))
|
||||||
, rotation_h_(rotation_hour)
|
, rotation_h_(rotation_hour)
|
||||||
, rotation_m_(rotation_minute)
|
, rotation_m_(rotation_minute)
|
||||||
, truncate_(truncate)
|
, truncate_(truncate)
|
||||||
|
, max_files_(max_files)
|
||||||
|
, filenames_q_()
|
||||||
{
|
{
|
||||||
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
|
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
|
||||||
{
|
{
|
||||||
throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
|
SPDLOG_THROW(spdlog_ex("daily_file_sink: Invalid rotation time in ctor"));
|
||||||
}
|
}
|
||||||
|
|
||||||
auto now = log_clock::now();
|
auto now = log_clock::now();
|
||||||
file_helper_.open(FileNameCalc::calc_filename(base_filename_, now_tm(now)), truncate_);
|
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
|
||||||
|
file_helper_.open(filename, truncate_);
|
||||||
rotation_tp_ = next_rotation_tp_();
|
rotation_tp_ = next_rotation_tp_();
|
||||||
|
|
||||||
|
if (max_files_ > 0)
|
||||||
|
{
|
||||||
|
filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));
|
||||||
|
filenames_q_.push_back(std::move(filename));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filename_t &filename() const
|
||||||
|
{
|
||||||
|
return file_helper_.filename();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void sink_it_(const details::log_msg &msg) override
|
void sink_it_(const details::log_msg &msg) override
|
||||||
{
|
{
|
||||||
|
#ifdef SPDLOG_NO_DATETIME
|
||||||
|
auto time = log_clock::now();
|
||||||
|
#else
|
||||||
|
auto time = msg.time;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (msg.time >= rotation_tp_)
|
bool should_rotate = time >= rotation_tp_;
|
||||||
|
if (should_rotate)
|
||||||
{
|
{
|
||||||
file_helper_.open(FileNameCalc::calc_filename(base_filename_, now_tm(msg.time)), truncate_);
|
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));
|
||||||
|
file_helper_.open(filename, truncate_);
|
||||||
rotation_tp_ = next_rotation_tp_();
|
rotation_tp_ = next_rotation_tp_();
|
||||||
}
|
}
|
||||||
fmt::memory_buffer formatted;
|
memory_buf_t formatted;
|
||||||
sink::formatter_->format(msg, formatted);
|
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||||
file_helper_.write(formatted);
|
file_helper_.write(formatted);
|
||||||
|
|
||||||
|
// Do the cleaning ony at the end because it might throw on failure.
|
||||||
|
if (should_rotate && max_files_ > 0)
|
||||||
|
{
|
||||||
|
delete_old_();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void flush_() override
|
void flush_() override
|
||||||
@@ -100,12 +129,36 @@ private:
|
|||||||
return {rotation_time + std::chrono::hours(24)};
|
return {rotation_time + std::chrono::hours(24)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete the file N rotations ago.
|
||||||
|
// Throw spdlog_ex on failure to delete the old file.
|
||||||
|
void delete_old_()
|
||||||
|
{
|
||||||
|
using details::os::filename_to_str;
|
||||||
|
using details::os::remove_if_exists;
|
||||||
|
|
||||||
|
filename_t current_file = filename();
|
||||||
|
if (filenames_q_.full())
|
||||||
|
{
|
||||||
|
auto old_filename = std::move(filenames_q_.front());
|
||||||
|
filenames_q_.pop_front();
|
||||||
|
bool ok = remove_if_exists(old_filename) == 0;
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
filenames_q_.push_back(std::move(current_file));
|
||||||
|
SPDLOG_THROW(spdlog_ex("Failed removing daily file " + filename_to_str(old_filename), errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filenames_q_.push_back(std::move(current_file));
|
||||||
|
}
|
||||||
|
|
||||||
filename_t base_filename_;
|
filename_t base_filename_;
|
||||||
int rotation_h_;
|
int rotation_h_;
|
||||||
int rotation_m_;
|
int rotation_m_;
|
||||||
log_clock::time_point rotation_tp_;
|
log_clock::time_point rotation_tp_;
|
||||||
details::file_helper file_helper_;
|
details::file_helper file_helper_;
|
||||||
bool truncate_;
|
bool truncate_;
|
||||||
|
uint16_t max_files_;
|
||||||
|
details::circular_q<filename_t> filenames_q_;
|
||||||
};
|
};
|
||||||
|
|
||||||
using daily_file_sink_mt = daily_file_sink<std::mutex>;
|
using daily_file_sink_mt = daily_file_sink<std::mutex>;
|
||||||
@@ -116,14 +169,14 @@ using daily_file_sink_st = daily_file_sink<details::null_mutex>;
|
|||||||
//
|
//
|
||||||
// factory functions
|
// factory functions
|
||||||
//
|
//
|
||||||
template<typename Factory = default_factory>
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
inline std::shared_ptr<logger> daily_logger_mt(
|
inline std::shared_ptr<logger> daily_logger_mt(
|
||||||
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false)
|
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false)
|
||||||
{
|
{
|
||||||
return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate);
|
return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Factory = default_factory>
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
inline std::shared_ptr<logger> daily_logger_st(
|
inline std::shared_ptr<logger> daily_logger_st(
|
||||||
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false)
|
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
//
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
// Copyright (c) 2015 David Schury, Gabi Melman
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "base_sink.h"
|
#include "base_sink.h"
|
||||||
#include "spdlog/details/log_msg.h"
|
#include "spdlog/details/log_msg.h"
|
||||||
#include "spdlog/details/null_mutex.h"
|
#include "spdlog/details/null_mutex.h"
|
||||||
|
#include "spdlog/details/pattern_formatter.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@@ -40,10 +39,20 @@ public:
|
|||||||
sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
|
sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_sinks(std::vector<std::shared_ptr<sink>> sinks)
|
||||||
|
{
|
||||||
|
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||||
|
sinks_ = std::move(sinks);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<sink>> &sinks()
|
||||||
|
{
|
||||||
|
return sinks_;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void sink_it_(const details::log_msg &msg) override
|
void sink_it_(const details::log_msg &msg) override
|
||||||
{
|
{
|
||||||
|
|
||||||
for (auto &sink : sinks_)
|
for (auto &sink : sinks_)
|
||||||
{
|
{
|
||||||
if (sink->should_log(msg.level))
|
if (sink->should_log(msg.level))
|
||||||
@@ -56,7 +65,23 @@ protected:
|
|||||||
void flush_() override
|
void flush_() override
|
||||||
{
|
{
|
||||||
for (auto &sink : sinks_)
|
for (auto &sink : sinks_)
|
||||||
|
{
|
||||||
sink->flush();
|
sink->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_pattern_(const std::string &pattern) override
|
||||||
|
{
|
||||||
|
set_formatter_(details::make_unique<spdlog::pattern_formatter>(pattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter) override
|
||||||
|
{
|
||||||
|
base_sink<Mutex>::formatter_ = std::move(sink_formatter);
|
||||||
|
for (auto &sink : sinks_)
|
||||||
|
{
|
||||||
|
sink->set_formatter(base_sink<Mutex>::formatter_->clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
std::vector<std::shared_ptr<sink>> sinks_;
|
std::vector<std::shared_ptr<sink>> sinks_;
|
||||||
};
|
};
|
||||||
|
|||||||
94
include/spdlog/sinks/dup_filter_sink.h
Normal file
94
include/spdlog/sinks/dup_filter_sink.h
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "dist_sink.h"
|
||||||
|
#include "spdlog/details/null_mutex.h"
|
||||||
|
#include "spdlog/details/log_msg.h"
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
// Duplicate message removal sink.
|
||||||
|
// Skip the message if previous one is identical and less than "max_skip_duration" have passed
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// #include "spdlog/sinks/dup_filter_sink.h"
|
||||||
|
//
|
||||||
|
// int main() {
|
||||||
|
// auto dup_filter = std::make_shared<dup_filter_sink_st>(std::chrono::seconds(5));
|
||||||
|
// dup_filter->add_sink(std::make_shared<stdout_color_sink_mt>());
|
||||||
|
// spdlog::logger l("logger", dup_filter);
|
||||||
|
// l.info("Hello");
|
||||||
|
// l.info("Hello");
|
||||||
|
// l.info("Hello");
|
||||||
|
// l.info("Different Hello");
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Will produce:
|
||||||
|
// [2019-06-25 17:50:56.511] [logger] [info] Hello
|
||||||
|
// [2019-06-25 17:50:56.512] [logger] [info] Skipped 3 duplicate messages..
|
||||||
|
// [2019-06-25 17:50:56.512] [logger] [info] Different Hello
|
||||||
|
|
||||||
|
#ifdef SPDLOG_NO_DATETIME
|
||||||
|
#error "spdlog::sinks::dup_filter_sink: cannot work when SPDLOG_NO_DATETIME is defined"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
template<typename Mutex>
|
||||||
|
class dup_filter_sink : public dist_sink<Mutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template<class Rep, class Period>
|
||||||
|
explicit dup_filter_sink(std::chrono::duration<Rep, Period> max_skip_duration)
|
||||||
|
: max_skip_duration_{max_skip_duration}
|
||||||
|
{}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::chrono::microseconds max_skip_duration_;
|
||||||
|
log_clock::time_point last_msg_time_;
|
||||||
|
std::string last_msg_payload_;
|
||||||
|
size_t skip_counter_ = 0;
|
||||||
|
|
||||||
|
void sink_it_(const details::log_msg &msg) override
|
||||||
|
{
|
||||||
|
bool filtered = filter_(msg);
|
||||||
|
if (!filtered)
|
||||||
|
{
|
||||||
|
skip_counter_ += 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// log the "skipped.." message
|
||||||
|
if (skip_counter_ > 0)
|
||||||
|
{
|
||||||
|
memory_buf_t buf;
|
||||||
|
fmt::format_to(buf, "Skipped {} duplicate messages..", skip_counter_);
|
||||||
|
details::log_msg skipped_msg{msg.logger_name, msg.level, string_view_t{buf.data(), buf.size()}};
|
||||||
|
dist_sink<Mutex>::sink_it_(skipped_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// log current message
|
||||||
|
dist_sink<Mutex>::sink_it_(msg);
|
||||||
|
last_msg_time_ = msg.time;
|
||||||
|
skip_counter_ = 0;
|
||||||
|
last_msg_payload_.assign(msg.payload.data(), msg.payload.data() + msg.payload.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// return whether the log msg should be displayed (true) or skipped (false)
|
||||||
|
bool filter_(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
auto filter_duration = msg.time - last_msg_time_;
|
||||||
|
return (filter_duration > max_skip_duration_) || (msg.payload != last_msg_payload_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using dup_filter_sink_mt = dup_filter_sink<std::mutex>;
|
||||||
|
using dup_filter_sink_st = dup_filter_sink<details::null_mutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2016 Alexander Dalshov.
|
// Copyright(c) 2016 Alexander Dalshov.
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
//
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
@@ -30,8 +28,8 @@ protected:
|
|||||||
void sink_it_(const details::log_msg &msg) override
|
void sink_it_(const details::log_msg &msg) override
|
||||||
{
|
{
|
||||||
|
|
||||||
fmt::memory_buffer formatted;
|
memory_buf_t formatted;
|
||||||
sink::formatter_->format(msg, formatted);
|
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||||
OutputDebugStringA(fmt::to_string(formatted).c_str());
|
OutputDebugStringA(fmt::to_string(formatted).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user