Compare commits

..

699 Commits

Author SHA1 Message Date
Gabi Melman
10578ff08c Update test_mpmc_q.cpp 2019-09-21 15:49:54 +03:00
Gabi Melman
1f0513cf4e Update .travis.yml 2019-09-21 15:35:22 +03:00
Gabi Melman
647470f3ae Update .travis.yml 2019-09-21 15:33:03 +03:00
Gabi Melman
efd0dbe5c2 Update .travis.yml 2019-09-21 15:30:29 +03:00
Gabi Melman
bd2fe64bf1 Update .travis.yml 2019-09-21 15:24:37 +03:00
gabime
7153db954f Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2019-09-21 15:14:18 +03:00
gabime
3b425affd3 Fixed missing braces around initializer warnings about std::array initializations 2019-09-21 15:13:50 +03:00
Gabi Melman
d5a79ad5d7 Update .travis.yml 2019-09-21 14:46:09 +03:00
Gabi Melman
7951338d27 Update .travis.yml 2019-09-21 14:38:54 +03:00
Gabi Melman
90801267ee Merge pull request #1234 from jbeich/dragonfly
Unbreak on more BSDs
2019-09-20 20:47:41 +03:00
Jan Beich
8d57823e51 fstat64 is missing on other DragonFly, NetBSD, OpenBSD
Modern operating systems don't need to implement transitional
extensions for large file support.
2019-09-20 13:27:58 +00:00
Gabi Melman
277ccc5e18 Update log_msg_buffer-inl.h 2019-09-20 16:17:48 +03:00
Gabi Melman
cff9db5044 Update log_msg_buffer-inl.h 2019-09-20 16:11:40 +03:00
Gabi Melman
216f905670 Fix log msg buffer operator= 2019-09-20 16:11:22 +03:00
Jan Beich
53b2308011 Implement _thread_id() on more Unices 2019-09-20 13:10:27 +00:00
gabime
c368500efd Reverted some external template declarations 2019-09-20 02:05:24 +03:00
gabime
2fed68a73b Reverted some external template declarations 2019-09-20 01:52:05 +03:00
gabime
e7ab49c973 Added extern tempalate declarations 2019-09-20 01:28:02 +03:00
gabime
5496491aa4 Added extern template declarations 2019-09-20 00:18:36 +03:00
Gabi Melman
53ca5b2870 Update spdlog.cpp 2019-09-19 12:06:53 +03:00
Gabi Melman
6aced26c35 Update spdlog.cpp 2019-09-19 12:06:22 +03:00
Gabi Melman
2331750b58 Update log_msg_buffer.h 2019-09-19 12:02:59 +03:00
Gabi Melman
b3fb4c1265 Update log_msg_buffer-inl.h 2019-09-19 12:01:11 +03:00
Gabi Melman
3ad7b9b117 Update log_msg_buffer-inl.h 2019-09-19 12:00:59 +03:00
gabime
5721debdf1 split to log_msg_buffer.h ito header and mpl 2019-09-19 11:52:03 +03:00
gabime
c1c23d1e7b Revert pull #1228 2019-09-18 16:16:44 +03:00
gabime
9605641982 Updatede meson 2019-09-18 15:31:09 +03:00
gabime
e52672c263 Fixed CMakeLists.txt 2019-09-18 15:08:15 +03:00
gabime
50f070980e Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2019-09-18 15:01:28 +03:00
gabime
7733849478 Updated meson build 2019-09-18 15:01:23 +03:00
Gabi Melman
4bbc8a89a0 Merge pull request #1228 from scatter-dev/arch_independent
Added ARCH_INDEPENDENT option
2019-09-18 14:55:19 +03:00
Christian
c87882e82f added version check 2019-09-18 09:20:01 -02:30
Christian
bd4301b2c1 added arch_independent option 2019-09-17 14:45:59 -02:30
gabime
e771f4e75e Fix mingw32 compilation 2019-09-17 13:27:42 +03:00
Gabi Melman
35835469d7 Update common.h 2019-09-17 12:52:59 +03:00
Gabi Melman
0d6992fcdd Update common.h 2019-09-17 12:48:54 +03:00
gabime
29b3f471cf Added const vecsion to circula_q::front() 2019-09-17 12:28:02 +03:00
gabime
4985875a15 Renamed item_type => value_type in circular_q 2019-09-17 12:07:54 +03:00
gabime
4fffd3a111 Avoid un necessary move when popping circular_q 2019-09-17 12:05:23 +03:00
gabime
590749e8be Avoid un necessary move when popping circular_q 2019-09-17 12:03:54 +03:00
gabime
27cc76766c Avoid un necessary move when popping circular_q 2019-09-17 12:01:15 +03:00
gabime
d52cf87d71 Avoid un necessary move when popping circular_q 2019-09-17 11:56:17 +03:00
gabime
2ddd6895e1 Backported clang warning fix from fmt 2019-09-17 00:15:35 +03:00
gabime
545e7d2de8 Fixed clang warning 2019-09-16 23:50:56 +03:00
gabime
a9ed6b352b Update utils.cpp 2019-09-16 11:58:51 +03:00
gabime
523eebe47d Update utils.cpp 2019-09-16 11:58:26 +03:00
gabime
b303d8bc40 Update tests utils 2019-09-16 11:56:56 +03:00
Gabi Melman
68118f4233 Update daily_file_sink.h 2019-09-15 21:50:29 +03:00
Gabi Melman
fcc6b97f88 Update utils.cpp 2019-09-15 21:01:15 +03:00
gabime
adc4398cc5 Fixed widows tests 2019-09-15 20:56:20 +03:00
Gabi Melman
c53d26cfca Update utils.cpp 2019-09-15 19:15:35 +03:00
Gabi Melman
c188bee229 Update utils.cpp 2019-09-15 19:09:57 +03:00
Gabi Melman
7f1a89e3f6 Update utils.cpp 2019-09-15 18:49:19 +03:00
gabime
5d46f3fcab Fix windows build 2019-09-15 18:44:45 +03:00
gabime
b55d95d365 Update test utils for windows 2019-09-15 18:38:31 +03:00
gabime
494cc8bace Implemented daily sink rotation #661 2019-09-15 18:34:29 +03:00
gabime
03e8c0f45c Fix issue #1219 2019-09-13 14:01:53 +03:00
gabime
b6388a15ff Fix issue #1223 2019-09-13 11:38:24 +03:00
gabime
45a18a61c6 CmakeLists.txt: Added version-number and debug suffixes + SPDLOG_BUILD_SHARED property 2019-09-08 00:42:23 +03:00
gabime
1857a44c7c Fix #1215 2019-09-07 20:11:58 +03:00
gabime
bd9e1475e2 clang-format 2019-09-07 20:11:35 +03:00
gabime
6883267996 Added const to circular_q empty() and full() 2019-09-06 18:55:45 +03:00
gabime
b88c784634 Improved circular_q move 2019-09-06 18:53:10 +03:00
gabime
31020f9eea Fixed vs 2015 warning abour fmt unused variable 2019-09-06 16:41:36 +03:00
gabime
e89d59995a Merge branch 'tgpfeiffer-bump-fmtlib-6.0.0' into v1.x 2019-09-06 15:18:52 +03:00
Tobias Pfeiffer
bf324a11cd made the declarations in fmt.c match the ones in fmt-6.0.0 2019-09-06 10:06:00 +09:00
Tobias Pfeiffer
e149433a80 Bumped fmt to version 6.0.0 2019-09-06 09:51:42 +09:00
gabime
65d02e495e Reverted the version() function 2019-09-05 19:50:00 +03:00
gabime
f196a9fd27 Fix dupllicate 2019-09-05 19:42:08 +03:00
gabime
7f0398ca25 Added version() function 2019-09-05 18:51:28 +03:00
gabime
d7f05722d4 Fixed example 2019-09-05 18:32:54 +03:00
gabime
26377a2195 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2019-09-05 18:31:16 +03:00
gabime
aa4eaa16bf Fixed missing includes 2019-09-05 18:31:08 +03:00
Gabi Melman
abc7bfe5c9 Update README.md 2019-09-05 14:46:07 +03:00
Gabi Melman
f0f4499540 Update README.md 2019-09-05 14:42:34 +03:00
gabime
dae4f9fef6 split spdlog.cpp to multiple .cpp files 2019-09-05 14:37:25 +03:00
gabime
4c45c6fbd8 Removed redundant func 2019-09-05 02:31:23 +03:00
gabime
172cf26d77 Update comment 2019-09-05 02:31:03 +03:00
gabime
feefb7e7e2 format 2019-09-05 01:58:34 +03:00
gabime
ced44a15ea format 2019-09-05 01:54:29 +03:00
gabime
5c2855e1c1 wip backtracer 2019-09-05 01:25:00 +03:00
gabime
433785dc64 fix warnings and build 2019-09-05 01:12:27 +03:00
gabime
28845b96bd Update circular_q.h 2019-09-05 00:46:08 +03:00
gabime
98ec35cee1 Update backtracer.h 2019-09-05 00:39:50 +03:00
gabime
f795297e15 try different apprach to backtracer object 2019-09-05 00:39:11 +03:00
gabime
3fd3c47e6d Update logger-inl.h 2019-09-03 18:20:27 +03:00
gabime
153c25dbb3 Update logger-inl.h 2019-09-03 17:55:34 +03:00
gabime
a1a6b7e64f Update backtracer.h 2019-09-03 14:56:06 +03:00
gabime
3ea7fb18d6 Fix potential race condidion when in logger copy ctor
Don't copy other.trace - just create a new one with same size instead
2019-09-03 14:54:17 +03:00
gabime
6ff52332a8 Remove unused member from backtracer 2019-09-03 14:33:30 +03:00
gabime
5e75b104d6 Fix backtracer 2019-09-03 14:04:31 +03:00
gabime
dc893701f9 Update circular_q.h 2019-09-03 13:47:55 +03:00
gabime
e6b0aaf94a typo 2019-09-03 11:53:28 +03:00
gabime
e754cbf763 Update daily_file_sink.h 2019-09-02 12:55:05 +03:00
gabime
5988895d69 Simplify daily sink filename creation 2019-09-02 12:40:47 +03:00
gabime
2af5eea2c6 SImplify filename creation in rotating sink 2019-09-02 12:35:33 +03:00
Gabi Melman
554acb7429 Merge pull request #1203 from matt77hias/v1.x
Fixed {fmt} include if using the non-bundled version
2019-08-31 22:46:06 +03:00
Matthias Moulin
9c5869ce5a Fixed {fmt} include if using the non-bundled version 2019-08-31 21:30:36 +02:00
Gabi Melman
e641ff64fd Merge pull request #1202 from Jerry-Ma/patch-2
Remove extra semicolon
2019-08-31 18:52:03 +03:00
Zhiyuan "Jerry" Ma
aa731e3297 Remove extra semicolon
... in macro SPDLOG_LOGGER_CALL
2019-08-31 11:21:36 -04:00
Gabi Melman
ac6407bb8e Update README.md 2019-08-29 01:13:43 +03:00
Gabi Melman
baf08eee09 Update README.md 2019-08-29 01:12:07 +03:00
Gabi Melman
04a43cd6a1 Update README.md 2019-08-29 01:10:50 +03:00
gabime
ed8d099607 Fixed #1197 2019-08-29 01:05:23 +03:00
gabime
b693d0cd91 Fix #1195 2019-08-29 00:28:00 +03:00
gabime
fafedd2d59 Minor cleanup 2019-08-29 00:07:21 +03:00
gabime
f3a7ef1199 Fix compile bug 2019-08-28 23:53:00 +03:00
Gabi Melman
cb890c96b9 Update logger.h 2019-08-28 22:59:16 +03:00
Gabi Melman
37bfa092a5 Update README.md 2019-08-28 20:13:35 +03:00
Gabi Melman
c517cb64ae Update README.md 2019-08-28 20:08:51 +03:00
Gabi Melman
51e09fa504 Update README.md 2019-08-28 20:07:31 +03:00
Gabi Melman
691172e28b Update README.md 2019-08-28 19:56:06 +03:00
Gabi Melman
6cf6d2159b Update README.md 2019-08-28 19:55:10 +03:00
Gabi Melman
17f0b417d5 Update README.md 2019-08-28 19:54:27 +03:00
gabime
d89baf4c5b Fixed tidy warning 2019-08-28 19:00:35 +03:00
gabime
2eb52cd047 Unified usage of fmt::memory_buffer across the codebase 2019-08-28 18:50:11 +03:00
gabime
f5492aed12 Unified usage of fmt::memory_buffer across the codebase 2019-08-28 18:46:09 +03:00
gabime
c2efd6ee58 Increased msg_buffer size to 250 2019-08-28 17:47:22 +03:00
gabime
be507bf1cc Updated bench to include backtrace testing 2019-08-28 17:37:36 +03:00
gabime
f11f3ce8b7 Updated bench 2019-08-28 16:56:24 +03:00
Gabi Melman
b2a3e930c1 Update README.md 2019-08-28 16:32:16 +03:00
Gabi Melman
147bf04d08 Merge pull request #1196 from gabime/backtrace_support
Backtrace support !
2019-08-28 16:28:32 +03:00
gabime
f4d3616c4b Update test_backtrace.cpp 2019-08-28 15:59:58 +03:00
gabime
c97c025adb clang-format 2019-08-27 20:22:07 +03:00
gabime
c55336e78d Better support for logger copy with backtrace 2019-08-27 20:20:15 +03:00
gabime
13e9135935 Update test_backtrace.cpp 2019-08-27 19:45:21 +03:00
gabime
5c1e44a93d Added bactrace tests 2019-08-27 18:06:27 +03:00
gabime
75adf9e75e Improved test_sink in unit tests 2019-08-27 17:18:09 +03:00
gabime
0fa09f6af4 Removed dead clode 2019-08-27 16:39:18 +03:00
gabime
011ed270e8 backtrace support for wchar api under windows 2019-08-27 15:06:53 +03:00
gabime
d7e58ce10e Fixed example 2019-08-27 13:08:45 +03:00
gabime
813536d4c6 Update latency bench 2019-08-27 02:35:00 +03:00
gabime
b89023efa1 Merge branch 'backtrace_support' of https://github.com/gabime/spdlog into backtrace_support 2019-08-27 02:29:38 +03:00
gabime
b155347560 Added forgotten lock in backtracer 2019-08-27 02:28:49 +03:00
Gabi Melman
15faf742f1 Update README.md 2019-08-27 02:27:21 +03:00
Gabi Melman
2ba7d1639e Update README.md 2019-08-27 02:26:28 +03:00
Gabi Melman
a2de7cf070 Update README.md 2019-08-27 02:23:22 +03:00
Gabi Melman
c6d558b6f2 Update README.md 2019-08-27 02:20:17 +03:00
gabime
d1b97c0ba9 Updated async_logger include 2019-08-27 02:16:29 +03:00
gabime
755ce0a016 Updated async_logger flush 2019-08-27 02:15:08 +03:00
gabime
79334ca5ab Fixed bench 2019-08-27 02:02:35 +03:00
gabime
11e9752536 Fixed logger's copy ctor 2019-08-27 01:56:48 +03:00
gabime
72b0f9e8f7 Updated example 2019-08-27 01:51:59 +03:00
gabime
408a162044 Removed logger::clone() from API 2019-08-27 01:16:57 +03:00
gabime
7d6444491c Fixed registy::disable_backtrace() 2019-08-27 01:08:48 +03:00
gabime
7bfb6d6b76 Fixed example 2019-08-27 00:58:01 +03:00
gabime
e1be7f3d6f Refactoring to better support backtrace 2019-08-27 00:56:49 +03:00
gabime
04a8485b17 Replaced backtace implementation 2019-08-26 19:59:16 +03:00
gabime
f330dd210e Renamed file 2019-08-26 13:43:42 +03:00
gabime
97dc27b5fa minor code formatting 2019-08-26 01:16:53 +03:00
gabime
1fd43fe673 Fixed async_logger::clone 2019-08-26 01:11:22 +03:00
gabime
29e21cc7f3 Comment 2019-08-26 00:56:05 +03:00
gabime
292fc153ef clang-format 2019-08-26 00:55:14 +03:00
gabime
25d3c83d3b Update async bench 2019-08-26 00:53:52 +03:00
gabime
6b7f3db28e Fixed async sink_it 2019-08-26 00:23:09 +03:00
gabime
eec6e28b19 Async logger cleanup 2019-08-26 00:13:43 +03:00
gabime
f3e379cf78 Fixed async dump_backtrace 2019-08-25 23:55:35 +03:00
gabime
0258c47774 some cleanup in logger 2019-08-25 19:35:57 +03:00
gabime
f63df65245 some cleanup in logger 2019-08-25 19:33:12 +03:00
gabime
099137fe9a backtrace_sink code cleanup 2019-08-25 18:05:47 +03:00
gabime
36f253893e Pass logger name to tracer 2019-08-25 17:44:08 +03:00
gabime
8280c0d64c Merge branch 'backtrace_support' of https://github.com/gabime/spdlog into backtrace_support 2019-08-25 17:28:49 +03:00
gabime
4f98b000eb Fixed example 2019-08-25 17:27:21 +03:00
gabime
b5d61b963a Fixed example 2019-08-25 17:25:06 +03:00
gabime
a7f7984c4a backtrace support api 2019-08-25 17:24:17 +03:00
gabime
dd33c16aae Replace backtrace trigger with dump_backtrace() 2019-08-23 14:47:40 +03:00
Gabi Melman
e0bf0c0301 Update thread_pool.h 2019-08-23 03:30:56 +03:00
gabime
8d8aacf5e9 Protect from double call to enable_backtrace() 2019-08-23 03:17:45 +03:00
gabime
e085ba7fcc Fix example 2019-08-23 02:48:40 +03:00
gabime
33f881ac8b clang-format 2019-08-23 02:45:41 +03:00
gabime
b24ef39b9d Update example 2019-08-23 02:45:35 +03:00
gabime
a6d8b52686 Added enable_backtrace to spdlog_api 2019-08-23 02:28:52 +03:00
gabime
65407539bb Fixed log_msg_buffer 2019-08-23 01:10:48 +03:00
gabime
543060683b Clean log_msg_buffer 2019-08-23 00:16:44 +03:00
gabime
2848e51755 Clean log_msg_buffer 2019-08-23 00:16:13 +03:00
gabime
0db4978899 Fixed example 2019-08-22 19:58:49 +03:00
gabime
0284a23d0a Changed sink_it_ to accept const log_message& 2019-08-22 19:57:59 +03:00
gabime
7e728869cc comment 2019-08-22 19:48:11 +03:00
gabime
a19d93e1a2 brace initialize some ctors 2019-08-22 19:45:32 +03:00
gabime
5aefa1af3d Fixed clang-tidy warnings 2019-08-22 19:40:45 +03:00
gabime
f1718fb5b3 clang-format 2019-08-22 19:38:00 +03:00
gabime
6b527a50dd backtrace sink and refactoring 2019-08-22 19:36:47 +03:00
gabime
74df115fc1 Removed uneeded include 2019-08-22 00:59:16 +03:00
Gabi Melman
3adfeeec3e Merge pull request #1189 from AMS21/patch-1
Fix Wundef in os-inl.h
2019-08-20 00:21:04 +03:00
AMS21
c4df94a1d9 Fix Wundef in os-inl.h 2019-08-19 20:50:46 +00:00
gabime
da1d98d603 Fix example 2019-08-19 13:12:22 +03:00
gabime
6683418983 Fix example 2019-08-19 13:06:30 +03:00
gabime
2c1d97f1ad Removed junk 2019-08-19 13:03:25 +03:00
Gabi Melman
2f854428bc Merge pull request #1188 from gabime/no-exceptions
Support for -fno exceptions
2019-08-19 12:58:11 +03:00
gabime
c1a524a969 Fixed clang-tidy warnings 2019-08-19 12:20:37 +03:00
gabime
23807e12e8 Fixed throw macros and includes 2019-08-19 12:13:37 +03:00
gabime
87ec1ab97b Fixed clang warnings 2019-08-19 12:06:29 +03:00
gabime
b057b979fa Added missing include 2019-08-19 12:03:58 +03:00
gabime
7dc378e296 Update CMakeLists.txt 2019-08-19 11:38:19 +03:00
gabime
6d8cc30f12 fix conflict 2019-08-19 11:33:39 +03:00
gabime
0335e3fcc0 Print error and abort instead of throw if SPDLOG_NO_EXEPTIONS is defined 2019-08-19 11:31:33 +03:00
Gabi Melman
76aa1059cd Update async.h 2019-08-18 20:16:52 +03:00
gabime
b0a25f0183 wip - no-exceptions support 2019-08-18 19:46:28 +03:00
gabime
db1babab5e Fixed some msvc code analysis warnings 2019-08-18 18:28:13 +03:00
gabime
7ea951613d Update logger-inl.h 2019-08-18 17:45:09 +03:00
gabime
6506b73523 Add noexcept to move ctor and operator= 2019-08-18 17:33:35 +03:00
gabime
639029007d Update .gitignore 2019-08-18 17:32:54 +03:00
gabime
01eb59ca9b Removed spdlog::default_level and its usage in registry 2019-08-16 19:04:49 +03:00
gabime
a8b5e3da29 Removed unneeded test targets from CMakeLists 2019-08-16 18:48:18 +03:00
gabime
8cc0997f79 Added SPDLOG_WCHAR_SUPPORT options to CMake under windows 2019-08-09 10:31:53 +03:00
gabime
ffb7c317b5 Fix #1176 again 2019-08-09 10:16:18 +03:00
Gabi Melman
bb7420fc22 Update logger.h 2019-08-07 06:15:46 +03:00
gabime
0df9164e7c Fix #1176 2019-08-07 01:29:59 +03:00
gabime
dcd590b9de fstat64(..) -> ::fstat64(..) 2019-08-07 00:59:29 +03:00
gabime
8dc3a66688 Fix #1180 2019-08-06 19:12:35 +03:00
Gabi Melman
88b4adebdc Update README.md 2019-07-26 22:41:24 -04:00
Gabi Melman
01f2438c1f Merge pull request #1169 from TranslucentTB/v1.x
Add more overloads to spdlog::log and spdlog::logger::log
2019-07-24 22:10:23 -04:00
Charles Milette
eb51f37c67 Fix ambiguous overload errors 2019-07-24 13:52:03 -04:00
Charles Milette
4ef4d0659d Improve correctness of convertion checks 2019-07-24 13:30:43 -04:00
Charles Milette
2ce9a3f70f Add overload to logger when T can be statically converted to wstring_view_t 2019-07-24 13:27:54 -04:00
Charles Milette
59cbdaaf49 Add more source_loc overloads in spdlog namespace 2019-07-24 13:26:42 -04:00
Gabi Melman
e0cf16b7e9 Fix #1168 2019-07-24 08:49:59 -04:00
Gabi Melman
1cdf09e9dd Merge pull request #1164 from AMS21/patch-1
Fix use of old style cast in os-inl.h
2019-07-22 17:11:14 -04:00
AMS21
9966a6a4b7 Fix use old style cast 2019-07-22 20:35:30 +00:00
Gabi Melman
20a1d1c519 Merge pull request #1161 from mensinda/mesonSubInstall
meson: Do not install when used as a subproject
2019-07-20 08:30:22 -04:00
Daniel Mensinger
313ec87dc1 meson: Do not install when used as a subproject 2019-07-20 14:26:17 +02:00
gabime
a7ba6e447d clang-format 2019-07-18 14:26:36 +03:00
gabime
baa978ab0b Added stdout_sinks.h to the library build 2019-07-18 14:00:16 +03:00
Gabi Melman
9f1b4fc9e7 Update wincolor_sink-inl.h 2019-07-18 01:37:55 +03:00
gabime
38e5dbd866 Fix win-color sink redirection to file 2019-07-18 00:55:24 +03:00
Gabi Melman
50ed27946d Update README.md 2019-07-17 21:34:26 +03:00
Gabi Melman
856ac7d773 Update README.md 2019-07-17 21:31:58 +03:00
Gabi Melman
81f12df8b5 Update README.md 2019-07-17 21:30:06 +03:00
Gabi Melman
9eca3234e8 Update README.md 2019-07-17 21:28:56 +03:00
Gabi Melman
5a540bdd42 Update README.md 2019-07-17 21:21:56 +03:00
Gabi Melman
6d394b132d Update README.md 2019-07-17 21:20:38 +03:00
Gabi Melman
96a317ce68 Update README.md 2019-07-17 21:19:16 +03:00
Gabi Melman
bca8945c26 Merge pull request #1155 from raphaelts3/how-to-compile-from-source
Update README.md
2019-07-17 21:16:55 +03:00
raphaelts3
a4b108334f Update README.md
Moving the instructions to Install section
2019-07-17 15:12:52 -03:00
raphaelts3
4f72cf9744 Update README.md
Add steps to compile and install from source
2019-07-17 13:05:29 -03:00
gabime
3c30f77d31 limit default error handler to 1 message/second to avoid flood 2019-07-17 18:05:01 +03:00
gabime
bcb6484062 Removed redundant include statements 2019-07-17 16:09:39 +03:00
gabime
11472eddbc Catch sink exceptions without affecting other sinks 2019-07-17 16:01:30 +03:00
gabime
12470f6221 Revert null_sink changes 2019-07-17 15:33:29 +03:00
gabime
a82d0e2f57 try to prevent optimizer to remove null sink code altogether 2019-07-17 14:48:44 +03:00
gabime
dca1d1e0d1 try to prevent optimizer to remove null sink code altogether 2019-07-17 14:45:52 +03:00
gabime
0cef8f3d26 try to prevent optimizer to remove null sink code altogether 2019-07-17 14:42:26 +03:00
gabime
fbde18fc02 try to prevent optimizer to remove null sink code altogether 2019-07-17 14:41:18 +03:00
gabime
b640c59087 try to prevent optimizer to remove null sink code altogether 2019-07-17 14:41:07 +03:00
gabime
1f3dea60d3 try to prevent optimizer to remove null sink code altogether 2019-07-17 14:38:01 +03:00
gabime
a7c06eadd0 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2019-07-17 14:02:27 +03:00
gabime
39910f5137 Fixed execption include 2019-07-16 15:50:42 +03:00
Gabi Melman
6fc4eb92db Merge pull request #1150 from ainola/v1.x
meson: fix typo (extrenal -> external)
2019-07-15 18:12:21 +03:00
gabime
81e82fb2d3 Rename func name wincolor sink 2019-07-15 12:22:34 +03:00
gabime
c817254495 Update wincolor_sink-inl.h
minor formatting
2019-07-15 12:14:46 +03:00
gabime
4578b0ad11 Fixed gcc9 conversion warning 2019-07-15 01:36:20 +03:00
gabime
9fbf82b603 Fixed gcc9 conversion warning 2019-07-15 01:31:31 +03:00
gabime
4b0267910c Refactored padding handling in formatter using templates 2019-07-15 01:17:22 +03:00
gabime
54456aee9e Refactored padding handling in formatter using templates 2019-07-15 01:16:57 +03:00
gabime
2a31cdcded Fix clang tidy script 2019-07-15 01:16:29 +03:00
gabime
d3f31c6038 Fixed build 2019-07-14 18:42:51 +03:00
gabime
59dd9f6203 comment 2019-07-14 18:39:43 +03:00
gabime
05cac05c06 clang_format 2019-07-14 18:35:59 +03:00
gabime
724713ac80 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2019-07-14 18:33:56 +03:00
gabime
72f3d5291c Fix issue #1147 2019-07-14 18:32:24 +03:00
gabime
c138685364 Fix issue #1147 2019-07-14 18:31:58 +03:00
Brett Cornwall
4180d00a6c meson: fix typo (extrenal -> external) 2019-07-14 07:29:23 -06:00
gabime
2512ac1e3c Added const qualifier to padinfo 2019-07-14 14:31:55 +03:00
gabime
121fc0a273 Minor update to CMakelists 2019-07-14 13:27:09 +03:00
gabime
4d9281018f Minor update to CMakelists 2019-07-14 13:26:01 +03:00
Gabi Melman
3a94a60537 Update stdout_sinks-inl.h 2019-07-14 01:41:19 +03:00
Gabi Melman
161e6fb8fb Merge pull request #1145 from k0zmo/v1.x
Fix compiler and linker errors
2019-07-14 01:38:39 +03:00
k0zmo
29fa474e4a Use SPDLOG_HEADER_ONLY uniformly 2019-07-13 10:56:22 +02:00
k0zmo
ddb19f4a4f Add missing include directive 2019-07-13 10:55:17 +02:00
k0zmo
789fb1e7c9 Move templated constructor definition to header file 2019-07-13 10:54:22 +02:00
gabime
521c5317a2 Fix #1143 2019-07-11 19:23:06 +03:00
Gabi Melman
e0d85e60a3 Merge pull request #1142 from PeterTh/v1.x
Prevent NEAR and FAR macro definitions from leaking on Windows platforms
2019-07-10 20:47:59 +03:00
PeterTh
ac7821f9bf Prevent NEAR and FAR macro definitions from leaking on Windows platforms 2019-07-10 16:54:40 +02:00
Gabi Melman
84809db955 Update example.cpp 2019-07-10 03:07:17 +03:00
Gabi Melman
e6cecd97ac Update README.md 2019-07-10 02:51:32 +03:00
Gabi Melman
60e7deaaf5 Update README.md 2019-07-10 02:51:09 +03:00
Gabi Melman
23b07d8cb6 Update README.md 2019-07-10 02:49:52 +03:00
gabime
5f27697198 clang-format 2019-07-10 02:42:47 +03:00
gabime
261d2c5ae4 Protected from size_t to int overflow in systemd sink 2019-07-10 02:25:11 +03:00
gabime
847f7de003 Simplify syslog sinks overflow check 2019-07-10 02:19:16 +03:00
gabime
dffc8df3e0 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2019-07-10 02:03:48 +03:00
gabime
330d491eba Enable again test building by default in cmake 2019-07-10 01:45:05 +03:00
gabime
db103ff340 Replaced passing time by val istead of ref in helper 2019-07-10 01:42:43 +03:00
gabime
1ac46bacfe Updated formatter bench 2019-07-10 01:41:55 +03:00
gabime
c27a4ee61f Added elapsed flag to formatter 2019-07-10 01:35:22 +03:00
Gabi Melman
2d8c4b1c88 Merge pull request #1140 from mattiasj-axis/fix-syslog-termination
Fix syslog output
2019-07-08 14:25:54 +03:00
Mattias Jernberg
d969f8621d Fix syslog output
payload does not appear to be reliably null terminated and leaks
data. Use size to the formatter to reliably terminate messages.
2019-07-08 13:14:15 +02:00
gabime
a4ec91fd06 Cleaned bench folder 2019-07-07 17:58:36 +03:00
gabime
fd53472238 Cleaned common.h 2019-07-07 17:22:58 +03:00
gabime
b3ddef2fc2 Minor cmake fix in for tests 2019-07-07 14:09:23 +03:00
gabime
07d753176f Update CMakeLists.txt to not build tests by default 2019-07-07 13:36:46 +03:00
gabime
8d758add63 Relace remaining const char* with string_view_t in the API 2019-07-07 12:55:56 +03:00
gabime
506ab1c735 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2019-07-07 12:35:54 +03:00
Gabi Melman
945020e505 Merge pull request #1139 from matt77hias/v1.x
Added fmt::(w)string_view support (v2).
2019-07-06 20:17:16 +03:00
Matthias Moulin
5a7bcd0a4f Update logger.h 2019-07-06 19:00:49 +02:00
Matthias Moulin
ae92279f5c Update spdlog.h 2019-07-06 19:00:17 +02:00
Matthias Moulin
be33f5eb89 Added wstring_view_t 2019-07-06 18:59:45 +02:00
gabime
717a582085 Dont allow including of dup_filter_sink if SPDLOG_NO_DATETIME is defined 2019-07-05 14:28:03 +03:00
gabime
ee87aee4dd Updated bench 2019-07-05 13:39:19 +03:00
Gabi Melman
2d6afeebe1 Update README.md 2019-07-05 12:30:36 +03:00
gabime
49bc58da04 Moved .clang-tidy and .clang-format to scripts dir 2019-07-05 10:49:46 +03:00
gabime
f5831d5132 clang-format 2019-07-05 10:45:37 +03:00
gabime
517ccc4088 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2019-07-05 10:45:24 +03:00
gabime
90dd56b839 Fix #1130 2019-07-05 10:44:54 +03:00
Gabi Melman
d1794f4c1b Merge pull request #1133 from Jerry-Ma/patch-1
Add missing include
2019-07-04 01:19:41 +03:00
Zhiyuan "Jerry" Ma
75bb4346b2 Add missing include 2019-07-03 17:01:17 -04:00
Gabi Melman
13477e5478 Update .travis.yml 2019-07-02 09:58:23 +03:00
Gabi Melman
1093897838 Update CMakeLists.txt 2019-07-02 09:57:42 +03:00
Gabi Melman
4d27419d7c Update CMakeLists.txt 2019-07-02 09:50:13 +03:00
Gabi Melman
89e6d66872 Update .travis.yml 2019-07-02 08:41:15 +03:00
Gabi Melman
b97c16a636 Update appveyor.yml 2019-07-01 23:55:09 +03:00
Gabi Melman
751ff59e2a Update .travis.yml 2019-07-01 23:51:05 +03:00
Gabi Melman
64a549d051 Update meson.build 2019-07-01 23:19:57 +03:00
Gabi Melman
7a686d4d21 Update CMakeLists.txt 2019-07-01 15:03:53 +03:00
Gabi Melman
7b218737cc Merge pull request #1126 from mensinda/meson
Add meson support
2019-07-01 14:53:46 +03:00
Gabi Melman
b1520a87c3 Merge pull request #1129 from s-shin/hotfix/daily_file_sink_when_no_datetime_2
Fix one more `msg.time` in daily_file_sink.
2019-07-01 13:41:49 +03:00
Daniel Mensinger
2a2a34601c moved scripts into subdirectory 2019-07-01 12:37:18 +02:00
Daniel Mensinger
3c64b3da97 Added example meson.build 2019-07-01 12:32:12 +02:00
Gabi Melman
e7889e9ce2 Delete spdlogConfigVersion.cmake 2019-07-01 13:19:25 +03:00
Gabi Melman
7c8f45747c Delete spdlogConfig.cmake 2019-07-01 13:19:18 +03:00
Gabi Melman
d37def7a72 Delete CPackSourceConfig.cmake 2019-07-01 13:19:10 +03:00
Gabi Melman
452770e374 Delete CPackConfig.cmake 2019-07-01 13:19:03 +03:00
Shintaro Seki
54e44ab477 Fix one more msg.time. 2019-07-01 19:04:37 +09:00
Gabi Melman
6012d52fdb Merge pull request #1128 from s-shin/hotfix/daily_file_sink_when_no_datetime
Fix rotation condition when SPDLOG_NO_DATETIME is defined.
2019-07-01 12:21:33 +03:00
Shintaro Seki
7ffa0766b4 Fix rotation condition when SPDLOG_NO_DATETIME is defined.
When SPDLOG_NO_DATETIME is defined, the rotation was never worked
because `msg.time` is always zero.
2019-07-01 16:22:56 +09:00
Gabi Melman
d8e17111b9 Merge pull request #1127 from TranslucentTB/v1.x
Fix deprecation warnings in filename_to_str and improve performance of wbuf_to_utf8buf
2019-07-01 05:34:16 +03:00
Charles Milette
9e602a491b Silence narrowing warning 2019-06-30 21:43:28 -04:00
Charles Milette
f529afa625 Use stack allocated space when possible 2019-06-30 21:34:19 -04:00
Gabi Melman
5a4deb6e88 Update README.md 2019-07-01 03:14:08 +03:00
Charles Milette
3bcd3cef2f Fix deprecation warnings in filename_to_str 2019-06-30 19:12:16 -04:00
gabime
fbe6f945f3 Updated travis and appveyor to new cmake options 2019-07-01 02:08:48 +03:00
gabime
bb0f3839c1 Updated CMake options and defaults 2019-07-01 02:06:09 +03:00
Daniel Mensinger
af4026104c Extract version from header file 2019-06-30 16:48:20 +02:00
Daniel Mensinger
822aee2b4f Added meson build support 2019-06-30 16:27:28 +02:00
gabime
f09334dc6f Updated pattern_foramtter tests 2019-06-28 17:31:55 +03:00
gabime
d1d2609f49 Updated syslog_sink 2019-06-28 17:31:28 +03:00
gabime
9aa6cdc494 Updated systemd sink and tests 2019-06-28 17:29:52 +03:00
Gabi Melman
8b403081c1 Merge pull request #1122 from jbelloncastro/v1.x
Provide source location support for systemd_sink.h
2019-06-28 08:07:38 +03:00
Gabi Melman
dc054c3f8a Merge branch 'v1.x' into v1.x 2019-06-28 08:07:15 +03:00
gabime
94c2810b0a Reduced max padding 128=>64 2019-06-28 01:41:11 +03:00
gabime
6e83abdbf2 Removed stdout_sinks from spdlog.cpp for faster library compile time 2019-06-28 01:01:22 +03:00
gabime
f03eaaaf33 Fixed dup_filter test 2019-06-28 00:48:30 +03:00
gabime
71162ebdbb Fixed dup_filter test 2019-06-28 00:47:10 +03:00
gabime
c75549f6db Removed un needed #include 2019-06-28 00:14:16 +03:00
gabime
2ebc96d8eb Fixed windows build 2019-06-28 00:05:06 +03:00
gabime
29f2eeea31 Removed formatter_ member from the sink interface 2019-06-27 23:56:37 +03:00
Jorge Bellon-Castro
a13981ffe4 Provide source location support for systemd_sink.h 2019-06-27 18:18:48 +01:00
gabime
cf152e6030 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2019-06-27 17:41:17 +03:00
gabime
b279196af2 Added tests for dup_filter sink 2019-06-27 17:41:04 +03:00
gabime
98e151fda7 Added tests for dup_filter sink 2019-06-27 17:40:40 +03:00
gabime
7f3b5fb84d Fix dup filter 2019-06-27 17:17:25 +03:00
Gabi Melman
3d069f7b46 Update dist_sink.h 2019-06-26 01:25:06 +03:00
Gabi Melman
65c4f955a6 Update systemd_sink.h 2019-06-26 01:22:24 +03:00
Gabi Melman
246b4b01c5 Update syslog_sink.h 2019-06-26 01:21:40 +03:00
Gabi Melman
a680b71dc7 Update null_sink.h 2019-06-26 01:18:06 +03:00
Gabi Melman
d0b5b09318 Merge pull request #1120 from casperevan/patch-1
Fix missing include for null_mutex in rotating_file_sink.h
2019-06-26 00:10:47 +03:00
casperevan
67f3a83c31 Fix missing include for null_mutex in rotating_file_sink.h 2019-06-25 13:09:16 -07:00
gabime
5dd260c336 Added missing include to dist_sink 2019-06-25 17:56:25 +03:00
gabime
ee6f165a1f New duplicate filter sink 2019-06-25 17:55:20 +03:00
Gabi Melman
0cc2ff83ed Merge pull request #1119 from MarkKoester/patch-1
Fix warning: redundant move in return statement
2019-06-21 22:33:05 +03:00
Mark Koester
a9e92d6c5c Fix warning: redundant move in return statement 2019-06-21 15:24:52 -04:00
Gabi Melman
ea5f07110b Merge pull request #1118 from DanielChabrowski/stdout-incomplete
Fix use of incomplete type in stdout sinks
2019-06-21 07:54:02 +03:00
DanielChabrowski
59746c2e36 Fix use of incomplete type in stdout sinks 2019-06-21 01:21:30 +02:00
gabime
6399e05209 clang-format 2019-06-20 01:05:57 +03:00
gabime
08de642536 Added stdout tests 2019-06-20 01:02:54 +03:00
gabime
ab9e1b3aa7 Removed unneeded includes 2019-06-20 01:02:25 +03:00
gabime
af6744b112 Fixed color 2019-06-20 01:00:50 +03:00
gabime
1d86803e38 Fix #1116 2019-06-20 00:29:23 +03:00
gabime
b12c19162b Added thread callback option to thread_pool 2019-06-19 18:38:35 +03:00
gabime
220608e52a clang-format 2019-06-19 18:31:21 +03:00
gabime
06fb5c7c69 Close issue #1113 2019-06-19 18:30:50 +03:00
gabime
8970fd5d2f CMake cosmetic 2019-06-19 18:01:34 +03:00
gabime
67d5f65507 CMake support for building shared lib (not for win32) 2019-06-19 17:52:50 +03:00
gabime
7d678be07a CMake - turn off examples by default 2019-06-19 17:31:25 +03:00
gabime
74e2aa9c66 Micoro optimize macros 2019-06-19 17:13:48 +03:00
Gabi Melman
b9cc158e52 Fix #1115 2019-06-19 01:24:06 +03:00
gabime
e68cf1c9ed Add formatting option to syslog in ctor. Fix issue #729 #1107 2019-06-19 00:52:38 +03:00
gabime
f0fcc73f92 Minor improvments to ansicolor sink 2019-06-18 23:45:18 +03:00
gabime
a340b3812c Add missing template instantiation 2019-06-18 23:42:36 +03:00
gabime
78c833a09f clang-format 2019-06-18 17:05:27 +03:00
gabime
38888ba5b3 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2019-06-18 16:56:17 +03:00
gabime
99e519cf0f Simplify color sinks 2019-06-18 12:32:51 +03:00
gabime
09cb45001b Added copy ctor and assignment to logger 2019-06-15 19:46:41 +03:00
gabime
9d3aa5a253 Revert last commit 2019-06-14 20:44:46 +03:00
gabime
314308f975 Removed unused template from spdlog.cpp 2019-06-14 19:01:28 +03:00
gabime
b658ff2124 Fix unittests 2019-06-14 18:45:16 +03:00
Gabi Melman
c844ea4423 Update test_pattern_formatter.cpp 2019-06-14 01:30:43 +03:00
gabime
db5af8ead1 clang-format 2019-06-14 00:23:51 +03:00
gabime
c09dee7717 Moved source filename shortening to pattern formatter instead of macro 2019-06-14 00:23:25 +03:00
Gabi Melman
352281313f Update README.md 2019-06-11 23:17:08 +03:00
gabime
8afe18f148 Minor improvement CMakeLists to use generators 2019-06-10 20:10:28 +03:00
gabime
90f348d26a Version 2019-06-10 19:55:26 +03:00
gabime
8d3d06b7a0 Fixed version extraction in CMake 2019-06-10 19:53:39 +03:00
Gabi Melman
c56ee8ec03 Update version.h 2019-06-10 19:19:29 +03:00
Gabi Melman
76f6c10434 Update README.md 2019-06-10 19:18:47 +03:00
gabime
cf64f2baca Fixed CMake address sanitizer 2019-06-10 18:32:10 +03:00
gabime
68a0193d95 CMake refactoring to functions 2019-06-10 18:09:36 +03:00
Gabi Melman
80740f0e46 Merge pull request #1105 from jktjkt/do-not-impose-extra-warnings
Do not impose -Wextra -Wconversion etc on library users
2019-06-10 12:35:42 +03:00
Jan Kundrát
c60f790793 Do not impose -Wextra -Wconversion etc on library users
I had to nuke the flags targetting spdlog_header_only because in CMake,
an interface target apparently really only supports the INTERFACE
option.

As I am only targeting Linux, the MSVC changes rely on spdlog's CI and
manual code review. That said, it makes sense to also stop forcing these
options on third party code, assuming that the CMake installations work
the same on Windows.

Fixes #1104
2019-06-10 10:54:49 +02:00
gabime
e0b4ec54bd Fix warning in bench 2019-06-07 16:39:32 +03:00
gabime
d1bed6bf45 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2019-06-06 19:26:17 +03:00
Gabi Melman
b82966e775 Update README.md 2019-06-06 19:25:07 +03:00
Gabi Melman
19a9d87486 Update README.md 2019-06-06 19:23:55 +03:00
gabime
3448e5867e Added oberrun policy bench to async-bench and removed async from bench 2019-06-06 19:23:15 +03:00
Gabi Melman
e013d6b98c Update README.md 2019-06-06 18:50:03 +03:00
gabime
d392739049 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2019-06-06 18:31:21 +03:00
gabime
12266ad004 Fix async bench under msvc 2019-06-06 18:31:12 +03:00
gabime
e03c160e27 Optmize set_formatter to avoid redundant clone 2019-06-06 18:19:36 +03:00
gabime
03f0e2196e Fix async bench under msvc 2019-06-06 18:13:30 +03:00
Gabi Melman
34ea38c12e Update README.md 2019-06-04 23:42:13 +03:00
gabime
a33de607df Fix issue #1099 2019-06-04 16:41:24 +03:00
gabime
e39959a132 Updated tests to catch v2.8.0 2019-06-04 16:35:34 +03:00
gabime
33a42202c7 Replace string pointer with string_view in log_msg 2019-06-04 16:10:49 +03:00
gabime
efc358da9f Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2019-06-04 15:39:02 +03:00
gabime
c1b39eb2ce Fix circular include 2019-06-04 15:38:26 +03:00
Gabi Melman
12e30f0eb4 Update format.sh 2019-06-04 12:26:49 +03:00
Gabi Melman
94bf971f72 Update clang_tidy.sh 2019-06-04 12:26:00 +03:00
gabime
bcfa9241b8 Updated copyright headers 2019-06-04 00:09:16 +03:00
gabime
eea9d6136f Moved default sync factory to seperate file to avoid cyclic includes 2019-06-03 23:56:18 +03:00
gabime
c35f33e61a Fixed clang warning about size_t indexing 2019-06-03 23:24:57 +03:00
gabime
78eeba940a Fix issue #114 2019-06-03 23:20:53 +03:00
gabime
b3ed5f77f2 Updated tweakme 2019-06-03 23:10:01 +03:00
gabime
38f6b5ea71 Keep clang-tidy happy 2019-06-03 22:49:21 +03:00
gabime
c000a6164c Removed unused member from log_msg 2019-06-03 22:20:46 +03:00
Gabi Melman
cb6208a6fa Update .clang-tidy 2019-06-03 12:53:36 +03:00
Gabi Melman
b540558db6 Update INSTALL 2019-06-03 12:51:42 +03:00
Gabi Melman
e70b2dfbb2 Update INSTALL 2019-06-03 12:51:20 +03:00
Gabi Melman
f64cad89c4 Update INSTALL 2019-06-03 12:50:52 +03:00
Gabi Melman
935b3de2ad Update INSTALL 2019-06-03 12:50:27 +03:00
Gabi Melman
abc0359522 Update INSTALL 2019-06-03 12:49:35 +03:00
Gabi Melman
fcd48d9b2d Update INSTALL 2019-06-03 12:48:43 +03:00
Gabi Melman
97a79a4511 Update README.md 2019-06-03 12:23:33 +03:00
Gabi Melman
31172b0ecc Update README.md 2019-06-03 12:21:17 +03:00
Gabi Melman
74bd1613bd Update README.md 2019-06-03 12:19:57 +03:00
Gabi Melman
1fb3f95fdb Update README.md 2019-06-03 12:19:34 +03:00
gabime
ca571e7a7a Updated bench of empty logger 2019-06-01 15:00:14 +03:00
gabime
52b6be0dfe Added logger ctor that accepts only name(empty logger) 2019-06-01 14:57:57 +03:00
gabime
abd6a6784e Set default queue size in async bench to 8192 2019-06-01 13:44:21 +03:00
gabime
978df46611 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2019-06-01 13:41:57 +03:00
gabime
31c428cece Report memory consumption in async bench 2019-06-01 13:41:39 +03:00
Gabi Melman
67c892991a Update README.md 2019-05-30 10:20:29 +03:00
gabime
5743adc467 CMake use extract version from version.h 2019-05-29 15:15:35 +03:00
gabime
76fc166e11 CMakeLists.txt minor improvments 2019-05-29 01:41:48 +03:00
gabime
459cd21070 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2019-05-29 01:33:44 +03:00
gabime
fc53e3339f CMakeLists.txt minor improvments 2019-05-29 01:33:20 +03:00
Gabi Melman
9b788a882d Update README.md 2019-05-29 01:07:42 +03:00
Gabi Melman
5a3e0d5e85 Update README.md 2019-05-29 01:06:31 +03:00
Gabi Melman
ce4da69cc0 Update README.md 2019-05-29 01:04:54 +03:00
Gabi Melman
1a779077db Update README.md 2019-05-29 01:04:33 +03:00
Gabi Melman
e79531b292 Update README.md 2019-05-29 01:03:34 +03:00
Gabi Melman
7fea60183f Update README.md 2019-05-29 01:02:19 +03:00
Gabi Melman
41df6c4df2 Update README.md 2019-05-29 01:01:44 +03:00
Gabi Melman
b6a28b497b Update README.md 2019-05-29 01:00:29 +03:00
Gabi Melman
c75fbabc0f Update README.md 2019-05-29 00:59:36 +03:00
Gabi Melman
4cc0876efa Update README.md 2019-05-29 00:57:56 +03:00
Gabi Melman
abe1e37253 Update README.md 2019-05-29 00:57:29 +03:00
Gabi Melman
4dec965569 Update README.md 2019-05-29 00:57:00 +03:00
Gabi Melman
f478eaa98e Update README.md 2019-05-29 00:56:22 +03:00
Gabi Melman
1e105f88ca Update README.md 2019-05-29 00:53:50 +03:00
Gabi Melman
6586f3ed29 Update README.md 2019-05-29 00:52:59 +03:00
Gabi Melman
ba7019de8a Update README.md 2019-05-29 00:52:18 +03:00
Gabi Melman
9562ce3b87 Update README.md 2019-05-29 00:51:30 +03:00
Gabi Melman
a48fe674ee Update README.md 2019-05-29 00:51:04 +03:00
gabime
d439f75491 Minor CMake cosmetic fix 2019-05-29 00:17:38 +03:00
gabime
c559067f77 CMake refactoring 2019-05-29 00:13:02 +03:00
gabime
30bd80bd85 CMake improvements 2019-05-29 00:05:24 +03:00
Gabi Melman
5709cb10d1 Merge pull request #1089 from gabime/static-lib
Static lib support using -DSPDLOG_COMPILED_LIB
2019-05-25 23:14:32 +03:00
gabime
9329f8d3cd Merge v1.x 2019-05-24 12:46:20 +03:00
gabime
884c23a9c9 Merge v1.x 2019-05-24 12:44:51 +03:00
gabime
7a3a560c44 Merge v1.x 2019-05-24 12:33:14 +03:00
gabime
2963da1392 Updated travis 2019-05-24 08:47:29 +03:00
gabime
54f1941691 Fixed target_compile_options in cmake 2019-05-24 01:39:02 +03:00
gabime
ca14ae19db Turn off tsan in travis - gives false positives 2019-05-24 01:38:26 +03:00
gabime
6636ae6e63 CmakeLists.txt compiler warnings 2019-05-23 23:35:30 +03:00
gabime
2e75f42c69 Install using GNUInstallDirs in cmake 2019-05-23 19:17:52 +03:00
gabime
c9547f383a Fixed appveyor cmake 2019-05-23 18:33:02 +03:00
gabime
65576707bf Removed shared_lib option (not supported by windwos) and remove example and test from default build 2019-05-23 18:26:47 +03:00
gabime
6ec8a06a09 Fixed Cmake under windows and disable bench build from default config 2019-05-22 12:32:59 +03:00
gabime
9205c9d031 Merge branch 'static-lib' of https://github.com/gabime/spdlog into static-lib 2019-05-22 12:19:55 +03:00
gabime
1ef80d6330 Updated CMakeLists.txt 2019-05-19 19:39:38 +03:00
Gabi Melman
322665a22f Delete example.cpp 2019-05-19 17:59:00 +03:00
Gabi Melman
cfa6d12691 Delete Application.mk 2019-05-19 17:58:52 +03:00
Gabi Melman
c264c3e2dd Delete Android.mk 2019-05-19 17:58:44 +03:00
Gabi Melman
d21bcd2f87 Delete utils.h 2019-05-19 17:57:18 +03:00
Gabi Melman
84f25b9f18 Update CMakeLists.txt 2019-05-19 17:19:35 +03:00
Gabi Melman
576fec4c36 Update CMakeLists.txt 2019-05-19 17:17:58 +03:00
Gabi Melman
48acafd10d Update CMakeLists.txt 2019-05-19 17:16:22 +03:00
Gabi Melman
a532a072ce Update CMakeLists.txt 2019-05-19 17:06:22 +03:00
gabime
2cd53c6ff1 Updated cmake example 2019-05-19 16:34:38 +03:00
gabime
18ccd55725 Removed multisink and test in example 2019-05-19 15:57:22 +03:00
gabime
87eb569929 More updates to CMake (version support , cmake.in) 2019-05-19 15:47:49 +03:00
gabime
92387b1527 Merge branch 'static-lib' of https://github.com/gabime/spdlog into static-lib 2019-05-19 12:27:11 +03:00
David Zemon
dd2f293f33 Clean up CMake a bit more 2019-05-17 23:33:44 -05:00
David Zemon
24e4f0aa87 Allowed overriding of SPDLOG_MASTER_PROJECT to better support Conan 2019-05-17 23:31:30 -05:00
David Zemon
6fe899af10 Set Threads::Threads dependency on spdlog libs - don't make user do it 2019-05-17 23:28:53 -05:00
David Zemon
107fe0a142 Ensure header_only library works by adding another example exe 2019-05-17 23:27:44 -05:00
David Zemon
b021be29e5 Add support for .tar.gz and .zip packages via CPack 2019-05-17 23:27:44 -05:00
David Zemon
55e7844ca0 Remove the namespaced
Namespaces are good for avoiding collisions, but since the
non-namespaced targets still exist, it does no good to add the
namespaced targets on top.
2019-05-17 23:27:44 -05:00
David Zemon
8dd85285e7 Allow user to choose between static or shared library 2019-05-17 23:26:06 -05:00
David Zemon
dbcbeb7a57 Ignore CLion's default build directories 2019-05-17 23:05:48 -05:00
David Zemon
a9aee1c5b3 Disable automatic handling of line endings 2019-05-17 23:02:39 -05:00
gabime
b3fe4b54c8 Fixed CMakeLists.txt 2019-05-13 01:28:06 +03:00
gabime
872ea6bf09 Deleted redundant Make files and solutions 2019-05-13 01:13:58 +03:00
gabime
32fb9d51b9 Cmake update to support both header-only and static 2019-05-13 01:06:25 +03:00
gabime
ce637440bb wip static-lib 2019-05-13 00:09:00 +03:00
gabime
61e4597488 Removed include of fmt_helper from thrad_pool.h 2019-05-13 00:02:55 +03:00
gabime
26a064ed2d wip static-lib 2019-05-12 23:36:48 +03:00
gabime
d5c9bac3c7 wip static-lib 2019-05-12 23:08:14 +03:00
gabime
1665006401 Minor fix in example 2019-05-12 17:12:08 +03:00
gabime
5220ac4a9e Added missing header 2019-05-12 17:05:14 +03:00
gabime
ee0fdf016a Minore cmake fix 2019-05-12 16:13:13 +03:00
gabime
8b4eedb594 More template instantiations for static lib 2019-05-12 16:11:35 +03:00
gabime
01f5efa1d9 Added wincolor sink to static build 2019-05-12 15:32:54 +03:00
gabime
130bc26b9a wip static-lib 2019-05-12 02:16:31 +03:00
gabime
09e83937de wip static-lib 2019-05-12 02:10:39 +03:00
gabime
42e30468a9 added rotating_file_sink to static build 2019-05-12 01:43:30 +03:00
gabime
3834acad5b #error if compiling spdlog.cpp under header only configuration 2019-05-12 01:12:46 +03:00
gabime
654f7eceee #error if compiling spdlog.cpp under header only configuration 2019-05-12 01:04:48 +03:00
gabime
ca9c8ae5fb Warn if compiling spdlog.cpp under header only configuration 2019-05-12 01:01:37 +03:00
gabime
1752086cfd Don't include fmt format.cc source if using external fmt 2019-05-12 00:50:43 +03:00
gabime
369b2f7cd2 Moved format.cpp into spdlog.cpp - this way only one src file is needed 2019-05-12 00:44:26 +03:00
gabime
8b244ca988 Moved format.cpp into spdlog.cpp - this way only one src file is needed 2019-05-12 00:43:58 +03:00
gabime
fb9e51d943 Fixed include order and example 2019-05-12 00:34:55 +03:00
gabime
bb3dc87953 Fixed include order and example 2019-05-12 00:32:57 +03:00
gabime
6bcb422c80 clang format and SPLDOG_HEADER_ONLY macro 2019-05-12 00:22:39 +03:00
gabime
540f865355 Normalized copyright message 2019-05-11 20:15:03 +03:00
gabime
46ef71e3ec Normalized copyright message 2019-05-11 20:06:17 +03:00
gabime
005450ff13 wip 2019-05-11 13:52:46 +03:00
gabime
f809427575 Moved cpp files to inl.h 2019-05-11 13:20:11 +03:00
gabime
9564eb2edb Moved cpp files to inl.h 2019-05-11 13:19:53 +03:00
gabime
49708f209b fix inclusion of fmt 2019-05-11 12:28:36 +03:00
Gabi Melman
14381fe8d0 Update os.h 2019-05-11 03:05:22 +03:00
gabime
17bec5c3ce Use fmt src file instead of inline in static lib build 2019-05-11 01:33:48 +03:00
gabime
2b90ab496a ansicolor_sink.cpp 2019-05-10 18:48:03 +03:00
gabime
74dbf4cf70 mutex protect should_color() 2019-05-10 18:46:13 +03:00
Gabi Melman
e504aceeb5 Update wincolor_sink.h 2019-05-10 17:59:48 +03:00
gabime
3ce9ac74a6 Mutex protect set_color_mode() 2019-05-10 17:41:21 +03:00
Gabi Melman
3b0e7b4d0d Merge pull request #1075 from psalz/add-force-color-output-option
Add option to force color output without TTY
2019-05-10 17:20:12 +03:00
Philip Salzmann
5e856c6b4d Add mode enum to control output of color sinks
This adds a new "color_mode" enum that can be used to control the color
code output behavior of sinks with color support. It can be one of three
values: always, automatic and never.
2019-05-10 09:47:16 +02:00
gabime
6651a48c4d wip 2019-05-08 17:50:23 +03:00
gabime
c031ae2aab Cmake message 2019-05-08 17:37:25 +03:00
gabime
1ac6c9f9c2 clang-format static-lib 2019-05-08 17:17:11 +03:00
gabime
5d0eb6dda5 wip static-lib 2019-05-08 17:16:56 +03:00
gabime
29c949ab03 static lib by default 2019-05-08 16:36:14 +03:00
gabime
576e389788 static lib by default and tests 2019-05-08 16:34:20 +03:00
gabime
7b15a3d345 wip cmake 2019-04-27 19:42:19 +03:00
gabime
eedb43d756 wip cmake 2019-04-27 19:41:25 +03:00
gabime
338125b93a wip cmake 2019-04-27 19:40:24 +03:00
gabime
3ecc3ab798 wip cmake 2019-04-27 19:38:39 +03:00
gabime
de1cdb2dbe wip cmake 2019-04-27 19:38:26 +03:00
gabime
c9887874bc wip cmake 2019-04-27 19:37:59 +03:00
gabime
69fcaf14e5 wip static-lib 2019-04-27 18:44:48 +03:00
gabime
f414198fee Cmake fix 2019-04-27 02:42:33 +03:00
gabime
2de924a187 clang-format 2019-04-27 02:34:50 +03:00
gabime
c1c2ff2d07 wip 2019-04-27 02:33:33 +03:00
gabime
ff89f1476d Restored error_handler as std::function 2019-04-26 18:14:59 +03:00
gabime
e8d99cee70 fix formatter bench 2019-04-26 17:52:47 +03:00
gabime
ccfa3f03b0 Fixed clang tidy 2019-04-26 17:39:58 +03:00
gabime
baefe0b3f6 wip 2019-04-26 15:11:01 +03:00
Gabi Melman
3669351427 Merge pull request #1064 from jhasse/extra-semi
Remove unnecessary semicolons (-Wextra-semi)
2019-04-17 20:57:59 +02:00
Jan Niklas Hasse
79938b98da Remove unnecessary semicolons (-Wextra-semi) 2019-04-17 15:24:25 +02:00
Gabi Melman
411d588fea Merge pull request #1058 from mnemotic/cmake-fmt-header-only
Add a CMake option to use a header-only external fmt
2019-04-10 10:24:53 +03:00
Martin Green
7e63d773ef Make header-only external fmt the default 2019-04-10 06:49:57 +03:00
gabime
3e378f009d Fixed build 2019-04-09 16:49:19 +03:00
gabime
13db9d9452 Fix build 2019-04-09 16:46:17 +03:00
gabime
c1c6e6265c Fixed header only build 2019-04-09 16:42:44 +03:00
Martin Green
a984b1b073 Add a CMake option to use a header-only external fmt 2019-04-07 17:29:27 +03:00
gabime
215b6aea95 file_helper.cpp and filename_t fix 2019-04-06 13:45:33 +03:00
gabime
96b7214ae2 fix common.h 2019-04-06 13:13:47 +03:00
gabime
1b0752b0a9 Merge branch 'static-lib' of https://github.com/gabime/spdlog into static-lib 2019-04-06 13:08:30 +03:00
Gabi Melman
02329f61e3 Merge pull request #1057 from martinkrammer/patch-1
Update README.md
2019-04-06 12:58:05 +03:00
gabime
7e29c48379 removed spdlite 2019-04-06 12:57:09 +03:00
Martin Krammer
ad63efdaf7 Update README.md
In order to use basic_logger_mt an additional include is required.
2019-04-06 11:17:11 +02:00
Gabi Melman
6bec53dcd2 Update logger.h 2019-04-06 02:14:08 +03:00
gabime
fef405ac98 Merge branch 'static-lib' of https://github.com/gabime/spdlog into static-lib 2019-04-06 01:44:18 +03:00
gabime
97f9cc4bc0 some cleanup 2019-04-06 01:44:03 +03:00
gabime
7ab6fd9db6 source_loc change line to int 2019-04-06 01:40:53 +03:00
gabime
b7ecec0c23 some cleanup 2019-04-06 01:37:51 +03:00
gabime
d12a858897 some cleanup 2019-04-06 01:37:27 +03:00
gabime
9716ff69c4 added inline statemetns 2019-04-06 01:32:15 +03:00
gabime
4dd1a24d0b wip static lib 2019-04-06 01:25:33 +03:00
gabime
c69c49047b wip static lib 2019-04-06 00:15:14 +03:00
gabime
bfbb4e4050 some err handler cleanup 2019-04-05 23:40:27 +03:00
gabime
2aceb13f3e fix support for utf8 logging under win32 2019-04-05 23:34:55 +03:00
gabime
e9f34fbd26 wip 2019-04-05 23:10:12 +03:00
gabime
17f9cdd401 static lib wip 2019-04-05 23:05:46 +03:00
gabime
156b856a80 static lib wip 2019-04-05 16:57:49 +03:00
gabime
e2e3df9013 static lib wip 2019-04-05 16:44:17 +03:00
gabime
ef8773a89b Moved logger_impl back into logger.h 2019-04-05 13:26:33 +03:00
Gabi Melman
536f5d8203 Merge pull request #1055 from db-panda/v1.x
Fix unexpected log macro expansion.
2019-04-04 17:44:43 +03:00
Daniel Binsmaier
631416d54a Fix missing ';' in log macros 2019-04-04 12:04:53 +02:00
Daniel Binsmaier
d366a06461 Fix unexpected log macro expansion 2019-04-04 09:25:21 +02:00
Gabi Melman
7bf8f14879 Merge pull request #1045 from p-po/v1.x
Namespace qualify stat functions
2019-04-01 02:00:48 +03:00
Ponnuvel Palaniyappan
cd65d6de69 namespace qualify stat functions 2019-03-31 22:05:37 +01:00
gabime
b57d514b1e added missing files 2019-03-30 16:56:37 +03:00
gabime
f36be4d5e4 Moved lite source to folders| 2019-03-30 16:49:02 +03:00
gabime
c2b0e223fa wip lite 2019-03-30 14:53:02 +03:00
gabime
e32c856a04 wip lite 2019-03-30 13:49:54 +03:00
gabime
bc7cd2ccc2 move lite namespace to topelevel spdlite 2019-03-30 10:58:56 +03:00
gabime
1842669104 Merge branch 'lite' of https://github.com/gabime/spdlog into lite 2019-03-29 14:46:54 +03:00
gabime
c7535a91a6 wip lite 2019-03-29 14:46:32 +03:00
gabime
99a5484dfb added header to cmake 2019-03-29 14:46:32 +03:00
gabime
b78ae5ab10 comment 2019-03-29 14:46:32 +03:00
gabime
f74d3e7e94 comment 2019-03-29 14:46:32 +03:00
gabime
eba37e8fbe clone support in lite logger 2019-03-29 14:46:32 +03:00
gabime
84fb11599e Removed default ctor from lite logger 2019-03-29 14:46:32 +03:00
gabime
099812d219 Renamed source file 2019-03-29 14:46:32 +03:00
gabime
c4291510e8 renamed printf 2019-03-29 14:46:32 +03:00
gabime
775a411215 renamed printf 2019-03-29 14:46:32 +03:00
gabime
59b4dd4c46 update example 2019-03-29 14:46:32 +03:00
gabime
2f907e3a92 clang-format 2019-03-29 14:46:32 +03:00
gabime
9971fd2864 Fixed examples 2019-03-29 14:46:32 +03:00
gabime
23a394d1fc moved macros to seprate headers 2019-03-29 14:46:32 +03:00
gabime
91d450df4e Removed source logging 2019-03-29 14:46:32 +03:00
gabime
ea3943a87a added printf syntax support for even faster compilation! 2019-03-29 14:46:32 +03:00
gabime
019eda5b0c wip lite 2019-03-29 14:46:32 +03:00
gabime
5056437ca1 support for const char* messages 2019-03-29 14:46:32 +03:00
gabime
90c912a5e2 wip lite 2019-03-29 14:46:32 +03:00
gabime
9219613957 Dont check level in macros (redundant) 2019-03-29 14:46:32 +03:00
gabime
9858f2e499 renamed 2019-03-29 14:46:31 +03:00
gabime
6b0bf33f8e clang-format 2019-03-29 14:46:31 +03:00
gabime
9ea6079072 wip lite 2019-03-29 14:46:31 +03:00
gabime
478f16234d wip lite 2019-03-29 14:46:31 +03:00
gabime
57a312cb1a lite wip 2019-03-29 14:46:31 +03:00
gabime
bb88a74f92 wip lite 2019-03-29 14:38:40 +03:00
gabime
4a07ce5fae added header to cmake 2019-03-29 13:56:44 +03:00
Gabi Melman
043c4acc7e Merge pull request #1035 from tt4g/add-public-api-initialize-logger
Add spdlog::initialize_logger
2019-03-25 12:02:31 +02:00
tt4g
eb478e38b2 Add spdlog::initialize_logger 2019-03-25 12:58:55 +09:00
gabime
bf307e24c5 comment 2019-03-24 01:50:56 +02:00
gabime
4a00590a1b comment 2019-03-24 01:44:40 +02:00
gabime
4ccca079a5 clone support in lite logger 2019-03-24 01:40:20 +02:00
gabime
9e5d1b3ba5 Removed default ctor from lite logger 2019-03-24 01:34:21 +02:00
gabime
240f13a0a6 Renamed source file 2019-03-24 01:27:05 +02:00
gabime
221ce33eb5 renamed printf 2019-03-24 01:23:14 +02:00
gabime
722943000e renamed printf 2019-03-24 01:21:07 +02:00
gabime
509a503761 update example 2019-03-24 01:18:05 +02:00
gabime
bac1e4a850 clang-format 2019-03-24 00:41:22 +02:00
gabime
934cc892eb Fixed examples 2019-03-24 00:40:27 +02:00
gabime
b1c90f08cc moved macros to seprate headers 2019-03-24 00:38:00 +02:00
gabime
680f19a424 Removed source logging 2019-03-24 00:31:55 +02:00
gabime
621e0a8330 added printf syntax support for even faster compilation! 2019-03-24 00:30:08 +02:00
gabime
4449bf6f83 wip lite 2019-03-23 23:13:38 +02:00
gabime
569c62e528 support for const char* messages 2019-03-23 20:06:49 +02:00
gabime
4c240edf94 wip lite 2019-03-23 19:34:50 +02:00
gabime
0e977d66c1 Dont check level in macros (redundant) 2019-03-23 19:23:37 +02:00
gabime
3dee10772d renamed 2019-03-23 17:25:50 +02:00
gabime
1d72edcc4f clang-format 2019-03-23 16:39:32 +02:00
gabime
c29b7d22d9 wip lite 2019-03-23 16:39:05 +02:00
gabime
ee502aed49 wip lite 2019-03-23 16:15:58 +02:00
gabime
414ff25564 lite wip 2019-03-23 13:31:57 +02:00
gabime
b084b8b1d8 Cleaned systemd test 2019-03-22 14:16:16 +02:00
gabime
3ca19a8580 fix link to systemd in tests 2019-03-22 14:01:34 +02:00
Gabi Melman
9c12a44d6e Merge pull request #1027 from WarShoe/v1.x
Add systemd sink.
2019-03-20 17:22:52 +02:00
Alexander Zvyagin
4706b0ada4 Add signature. 2019-03-20 15:32:47 +01:00
Alexander Zvyagin
cc98e9850d Avoid unnecessary conversion of log_msg::payload to std::string. 2019-03-20 15:29:38 +01:00
Alexander Zvyagin
af80db8c22 Add systemd sink. 2019-03-14 16:20:58 +01:00
Gabi Melman
053d5ad24d Update README.md 2019-03-11 19:19:36 +02:00
Gabi Melman
e2805ac68a Merge pull request #1022 from floouuu/v1.x
Expose should_do_colors_ in ansicolor_sink.h
2019-03-11 19:12:03 +02:00
Florian Wörter
1caf05cc52 Renamed exposed method. [#1004] 2019-03-11 14:15:58 +01:00
Florian Wörter
63cfb7db25 Exposed should_do_colors_ in ansicolor_sink.h. [#1004] 2019-03-11 14:12:26 +01:00
Gabi Melman
bdfc7d2a5a Merge pull request #996 from MFornander/v1.x
Add tweak support for user short level names
2019-02-18 23:59:15 +02:00
Mattias Fornander
0ccbdcdd1f Add tweak support for user short level names 2019-02-18 14:33:41 -07:00
Gabi Melman
c598b2fa2d Merge pull request #994 from romainthomas/fix/README
Fix typo in Android logger
2019-02-13 11:51:28 +02:00
Romain Thomas
4f86448bd4 Fix typo in Android logger 2019-02-13 09:19:29 +01:00
Gabi Melman
d235e7d46f Fix #992 2019-02-08 12:14:17 +02:00
Gabi Melman
e25b323d1b Merge pull request #990 from mnemotic/cmake-conditional-install
Add CMake option for conditional installation
2019-02-07 21:20:44 +02:00
Gabi Melman
42093c48b2 Merge pull request #988 from DanielChabrowski/remove-log-func
Remove unnecessary log function
2019-02-07 21:15:02 +02:00
Martin Green
9fca0b20f0 Add CMake option for conditional installation 2019-02-06 07:38:26 +02:00
DanielChabrowski
a6229d9e87 Remove pointless log with enable_if 2019-02-06 05:24:25 +01:00
Gabi Melman
c443896644 Merge pull request #986 from DanielChabrowski/sink-allow-custom-formatter
Allow custom formatter in sink's ctor
2019-02-03 19:50:22 +02:00
DanielChabrowski
79f11bd655 Allow custom formatter in sink ctor 2019-02-03 16:49:29 +01:00
Gabi Melman
c5552dac1f Merge pull request #985 from FrancoisChabot/v1.x
Allow the inclusion of an external fmtlib as as sub_directory
2019-02-01 22:33:05 +02:00
Francois Chabot
734af31c13 Allow the inclusion of an external fmtlib as as sub_directory 2019-02-01 12:12:58 -05:00
Gabi Melman
2d96896fae Merge pull request #984 from ZGCDDoo/shadowed_parameter
Shadowed parameter
2019-02-01 17:34:08 +02:00
charles-david
20a0f82701 Renamed input parameters to "params_in". 2019-02-01 07:59:50 -05:00
charles-david
e4b7dbce7f CMakeLists.txt same as v1.x branch. 2019-01-31 23:05:38 -05:00
charles-david
0f128fd561 Warning for shadowed parameter corrected in common.h
However, not in bundled fmt. Corrects compiling with basic logger with header only and -Werror.
2019-01-31 22:47:02 -05:00
Gabi Melman
ac8a7bc12d Merge pull request #981 from ksergey/cmake-fix
CMAKE_BINARY_DIR -> CMAKE_CURRENT_BINARY_DIR
2019-01-28 12:10:49 +02:00
Sergey Kovalevich
90a299f424 CMAKE_BINARY_DIR -> CMAKE_CURRENT_BINARY_DIR 2019-01-28 11:48:45 +03:00
Gabi Melman
00a7f5d75d Update README.md 2019-01-27 19:10:34 +02:00
Gabi Melman
2463fe92bd Merge pull request #958 from pwm1234/pwm1234/rotate_on_open
Pwm1234/rotate on open
2019-01-24 18:23:48 +02:00
Philip Miller
4f65fcd7b1 remove minor optimization for the sake of simplicity 2019-01-24 09:19:52 -05:00
Philip Miller
e41b92c55a fix inadvertent rename to original test function 2019-01-24 00:13:14 -05:00
Philip Miller
3925f8fa16 streamline constructor logic
and improve test for rotate_on_open=true
2019-01-24 00:06:15 -05:00
Philip Miller
cce1e36e26 Merge remote-tracking branch 'origin/v1.x' into pwm1234/rotate_on_open 2019-01-23 21:44:27 -05:00
Gabi Melman
3466c9c8cb Merge pull request #978 from markniebur/file-sink-filename-method
Add method to filesinks to return filename
2019-01-23 23:07:57 +02:00
Mark Niebur
6e2dadc63a Add method to filesinks to return filename 2019-01-23 11:35:55 -07:00
Gabi Melman
00e89a23f6 Merge pull request #977 from Myz/cmake-baseline-configuration-without-google-benchmark
Disable building benchmarks by default
2019-01-23 12:00:36 +02:00
Pius Raeder
a29e518cfe Disable building benchmarks by default
Building the benchmarks requires google-benchmark to be installed which is
not the case for most of the build systems. The option now has a tiny
hint what the requirement is and where to get it.

closes #975
2019-01-23 10:03:03 +01:00
gabime
a7148b718e Version 1.3.1 2019-01-18 12:13:07 +02:00
gabime
23fdc0eae4 Fixed issue #959 2019-01-17 17:09:35 +02:00
Gabi Melman
8cb1bc89f1 Merge pull request #969 from scribam/patch-1
Update log_msg.h
2019-01-17 03:18:36 +02:00
scribam
1798a1fa12 Update log_msg.h
`SPDLOG_NO_THREAD_ID` should only affect thread retrieval.
2019-01-17 00:23:06 +01:00
Gabi Melman
f4c737ef42 Merge pull request #962 from myd7349/bench-patch
Fix #961 benchmark link error
2019-01-14 15:53:28 +02:00
myd7349
611df4964d Fix #961 benchmark link error 2019-01-14 20:18:35 +08:00
Gabi Melman
0a4ccf22da Update README.md 2019-01-14 11:39:35 +02:00
Philip Miller
4fe98bf6e6 Merge remote-tracking branch 'origin/v1.x' into pwm1234/rotate_on_open 2019-01-11 10:04:42 -05:00
Philip Miller
22f85deb2c Merge remote-tracking branch 'origin/v1.x' into pwm1234/rotate_on_open 2018-09-06 11:40:46 -04:00
Philip Miller
52a01b2cf2 add argument to rotating file sink for rotate_on_open
when true, the log file will be rotated when it is opened so the newly constructed file will start off being empty
2018-09-06 02:47:48 -04:00
158 changed files with 27866 additions and 19640 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
* text=false

19
.gitignore vendored
View File

@@ -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

View File

@@ -5,6 +5,7 @@
sudo: required sudo: required
language: cpp language: cpp
# gcc 4.8
addons: &gcc48 addons: &gcc48
apt: apt:
packages: packages:
@@ -12,6 +13,7 @@ addons: &gcc48
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
# gcc 7.0
addons: &gcc7 addons: &gcc7
apt: apt:
packages: packages:
@@ -19,6 +21,7 @@ addons: &gcc7
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
# Clang 3.5
addons: &clang35 addons: &clang35
apt: apt:
packages: packages:
@@ -26,14 +29,16 @@ addons: &clang35
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.5 - llvm-toolchain-precise-3.5
addons: &clang6 # Clang 7.0
addons: &clang70
apt: apt:
packages: packages:
- clang-6.0 - clang-7
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
- llvm-toolchain-trusty-6.0 - llvm-toolchain-trusty-7
matrix: matrix:
@@ -46,7 +51,7 @@ matrix:
- env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11 - env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11
os: linux os: linux
addons: *gcc48 addons: *gcc48
- env: GCC_VERSION=7 BUILD_TYPE=Release CPP=11 - env: GCC_VERSION=7 BUILD_TYPE=Release CPP=11
os: linux os: linux
addons: *gcc7 addons: *gcc7
@@ -60,28 +65,16 @@ matrix:
os: linux os: linux
addons: *clang35 addons: *clang35
# Test clang-6.0: C++11, Build=Debug, ASAN=On # Test clang-7.0: C++11, Build=Debug, ASAN=On
- env: CLANG_VERSION=6.0 BUILD_TYPE=Debug CPP=11 ASAN=On TSAN=Off - env: CLANG_VERSION=7 BUILD_TYPE=Debug CPP=11 ASAN=On TSAN=Off
os: linux dist: bionic
addons: *clang6
- env: CLANG_VERSION=6.0 BUILD_TYPE=Release CPP=11 ASAN=On TSAN=Off
os: linux
addons: *clang6
# Test clang-6.0: C++11, Build=Debug, TSAN=On
- env: CLANG_VERSION=6.0 BUILD_TYPE=Debug CPP=11 ASAN=Off TSAN=On
os: linux
addons: *clang6
- env: CLANG_VERSION=6.0 BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=On
os: linux
addons: *clang6
- env: CLANG_VERSION=7 BUILD_TYPE=Release CPP=11 ASAN=On TSAN=Off
dist: bionic
# osx # osx
- env: BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=Off - env: BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=Off
os: osx os: osx
@@ -102,11 +95,13 @@ script:
--warn-uninitialized \ --warn-uninitialized \
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \ -DCMAKE_BUILD_TYPE=$BUILD_TYPE \
-DCMAKE_CXX_STANDARD=$CPP \ -DCMAKE_CXX_STANDARD=$CPP \
-DSPDLOG_BUILD_EXAMPLES=ON \ -DSPDLOG_BUILD_EXAMPLE=ON \
-DSPDLOG_BUILD_EXAMPLE_HO=ON \
-DSPDLOG_BUILD_BENCH=OFF \ -DSPDLOG_BUILD_BENCH=OFF \
-DSPDLOG_BUILD_TESTS=ON \ -DSPDLOG_BUILD_TESTS=ON \
-DSPDLOG_SANITIZE_ADDRESS=$ASAN \ -DSPDLOG_BUILD_TESTS_HO=OFf \
-DSPDLOG_SANITIZE_THREAD=$TSAN -DSPDLOG_SANITIZE_ADDRESS=$ASAN
- make VERBOSE=1 -j2 - make VERBOSE=1 -j2
- ctest -j2 --output-on-failure - ctest -j2 --output-on-failure

View File

@@ -1,157 +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.3.0 LANGUAGES CXX)
include(CMakeDependentOption)
include(GNUInstallDirs)
#--------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------
# set default build to release # 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) if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE)
endif() endif()
message(STATUS "Build type: " ${CMAKE_BUILD_TYPE}) project(spdlog VERSION ${SPDLOG_VERSION} LANGUAGES CXX)
message(STATUS "Build spdlog: ${SPDLOG_VERSION}")
#--------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------
# compiler config # 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) set(CMAKE_CXX_EXTENSIONS OFF)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") #---------------------------------------------------------------------------------------
add_compile_options("-Wall") # Set SPDLOG_MASTER_PROJECT to ON if we are building spdlog
add_compile_options("-Wextra") #---------------------------------------------------------------------------------------
add_compile_options("-Wconversion") # Check if spdlog is being used directly or via add_subdirectory, but allow overriding
add_compile_options("-pedantic") if (NOT DEFINED SPDLOG_MASTER_PROJECT)
add_compile_options("-Wfatal-errors") 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
# address sanitizers check option(SPDLOG_BUILD_EXAMPLE "Build example" ${SPDLOG_MASTER_PROJECT})
#--------------------------------------------------------------------------------------- option(SPDLOG_BUILD_EXAMPLE_HO "Build header only example" OFF)
include(cmake/sanitizers.cmake)
#--------------------------------------------------------------------------------------- # testing options
# spdlog target
#---------------------------------------------------------------------------------------
add_library(spdlog INTERFACE)
add_library(spdlog::spdlog ALIAS spdlog)
# Check if spdlog is being used directly or via add_subdirectory
set(SPDLOG_MASTER_PROJECT OFF)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(SPDLOG_MASTER_PROJECT ON)
endif()
option(SPDLOG_BUILD_EXAMPLES "Build examples" ${SPDLOG_MASTER_PROJECT})
option(SPDLOG_BUILD_BENCH "Build benchmarks" ${SPDLOG_MASTER_PROJECT})
option(SPDLOG_BUILD_TESTS "Build tests" ${SPDLOG_MASTER_PROJECT}) 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) option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF)
if(SPDLOG_FMT_EXTERNAL) if(WIN32)
find_package(fmt REQUIRED CONFIG) option(SPDLOG_WCHAR_SUPPORT "Support wchar api" OFF)
endif() endif()
target_include_directories( option(SPDLOG_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF)
spdlog
INTERFACE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
if(SPDLOG_FMT_EXTERNAL)
target_compile_definitions(spdlog INTERFACE SPDLOG_FMT_EXTERNAL) find_package(Threads REQUIRED)
target_link_libraries(spdlog INTERFACE fmt::fmt)
message(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
#---------------------------------------------------------------------------------------
# Static/Shared library (shared not supported in windows yet)
#---------------------------------------------------------------------------------------
set(SPDLOG_SRCS
src/spdlog.cpp
src/stdout_sinks.cpp
src/fmt.cpp
src/color_sinks.cpp
src/file_sinks.cpp
src/async.cpp)
if (SPDLOG_BUILD_SHARED)
if(WIN32)
message(FATAL_ERROR "spdlog shared lib is not yet supported under windows")
endif()
add_library(spdlog SHARED ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
else()
add_library(spdlog STATIC ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
endif() 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_TESTS) if(SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_TESTS_HO)
include(CTest) message(STATUS "Generating tests")
enable_testing()
add_subdirectory(tests) add_subdirectory(tests)
endif() endif()
if(SPDLOG_BUILD_BENCH) if(SPDLOG_BUILD_BENCH)
message(STATUS "Generating benchmarks")
add_subdirectory(bench) add_subdirectory(bench)
endif() 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 "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake") set(version_config_file "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfigVersion.cmake")
set(targets_config "${PROJECT_NAME}Targets.cmake") set(export_dest_dir "${CMAKE_INSTALL_LIBDIR}/spdlog/cmake")
set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc")
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
# configure spdlogConfig.cmake file #---------------------------------------------------------------------------------------
configure_file("cmake/Config.cmake.in" "${project_config}" @ONLY) 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 config and version file endif ()
install(
FILES "${project_config}" "${version_config}"
DESTINATION "${config_install_dir}"
)
# install pkg config file
install(
FILES "${pkg_config}"
DESTINATION "${pkgconfig_install_dir}"
)
# install targets config file
install(
EXPORT "${targets_export_name}"
NAMESPACE "${namespace}"
DESTINATION "${config_install_dir}"
FILE ${targets_config}
)
# export build directory targets file
export(
EXPORT ${targets_export_name}
NAMESPACE "${namespace}"
FILE ${targets_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
View File

@@ -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

157
README.md
View File

@@ -1,24 +1,21 @@
# spdlog # spdlog
Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=master)](https://travis-ci.org/gabime/spdlog)&nbsp; [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true)](https://ci.appveyor.com/project/gabime/spdlog) Very fast, header-only/compiled, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=master)](https://travis-ci.org/gabime/spdlog)&nbsp; [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true)](https://ci.appveyor.com/project/gabime/spdlog)
## Install ## Install
#### Just copy the headers: #### 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.
* Copy the source [folder](https://github.com/gabime/spdlog/tree/v1.x/include/spdlog) to your build tree and use a C++11 compiler. #### Static lib version (recommended - much faster compile times, v1.4.0)
```console
#### Or use your favorite package manager: $ git clone https://github.com/gabime/spdlog.git
$ cd spdlog && mkdir build && cd build
* Ubuntu: `apt-get install libspdlog-dev` $ cmake .. && make -j
* Homebrew: `brew install spdlog` ```
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
* Fedora: `yum install spdlog` see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v1.x/example/CMakeLists.txt) on how to use.
* Gentoo: `emerge dev-libs/spdlog`
* Arch Linux: `yaourt -S spdlog-git`
* vcpkg: `vcpkg install spdlog`
## Platforms ## Platforms
* Linux, FreeBSD, OpenBSD, Solaris, AIX * Linux, FreeBSD, OpenBSD, Solaris, AIX
@@ -26,10 +23,19 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
* macOS (clang 3.5+) * macOS (clang 3.5+)
* Android * Android
## Package managers:
* Homebrew: `brew install spdlog`
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
* Fedora: `yum install spdlog`
* Gentoo: `emerge dev-libs/spdlog`
* Arch Linux: `yaourt -S spdlog-git`
* vcpkg: `vcpkg install spdlog`
## Features ## Features
* Very fast (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 formatting, 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.
* Multi/Single threaded loggers. * Multi/Single threaded loggers.
@@ -41,45 +47,15 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
* Windows debugger (```OutputDebugString(..)```) * Windows debugger (```OutputDebugString(..)```)
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) 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.
* Binary data logging.
## 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.181652 5,505,042/sec
rotating_st... Elapsed: 0.181781 5,501,117/sec
daily_st... Elapsed: 0.187595 5,330,630/sec
null_st... Elapsed: 0.0504704 19,813,602/sec
*******************************************************************************
10 threads sharing same logger, 1,000,000 iterations
*******************************************************************************
basic_mt... Elapsed: 0.616035 1,623,284/sec
rotating_mt... Elapsed: 0.620344 1,612,008/sec
daily_mt... Elapsed: 0.648353 1,542,369/sec
null_mt... Elapsed: 0.151972 6,580,166/sec
```
#### Asynchronous mode
```
*******************************************************************************
10 threads sharing same logger, 1,000,000 iterations
*******************************************************************************
async... Elapsed: 0.350066 2,856,606/sec
async... Elapsed: 0.314865 3,175,960/sec
async... Elapsed: 0.349851 2,858,358/sec
```
## Usage samples ## Usage samples
#### Basic usage #### Basic usage
```c++ ```c++
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"
int main() int main()
{ {
spdlog::info("Welcome to spdlog!"); spdlog::info("Welcome to spdlog!");
@@ -91,7 +67,7 @@ int main()
spdlog::info("Positional args are {1} {0}..", "too", "supported"); spdlog::info("Positional args are {1} {0}..", "too", "supported");
spdlog::info("{:<30}", "left aligned"); spdlog::info("{:<30}", "left aligned");
spdlog::set_level(spdlog::level::debug/ Set global log level to debug spdlog::set_level(spdlog::level::debug); // Set global log level to debug
spdlog::debug("This message should be displayed.."); spdlog::debug("This message should be displayed..");
// change log pattern // change log pattern
@@ -101,7 +77,10 @@ int main()
// define SPDLOG_ACTIVE_LEVEL to desired level // define SPDLOG_ACTIVE_LEVEL to desired level
SPDLOG_TRACE("Some trace message with param {}", {}); SPDLOG_TRACE("Some trace message with param {}", {});
SPDLOG_DEBUG("Some debug message"); 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 #### create stdout/stderr logger object
@@ -157,15 +136,20 @@ void daily_example()
``` ```
--- ---
#### Cloning loggers #### Backtrace support
```c++ ```c++
// clone a logger and give it new name. // Loggers can store in a ring buffer all messages (including debug/trace) and display later on demand.
// Useful for creating subsystem loggers from some "root" logger // When needed, call dump_backtrace() to see them
void clone_example() 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++)
{ {
auto network_logger = spdlog::get("root")->clone("network"); spdlog::debug("Backtrace message {}", i); // not logged yet..
network_logger->info("Logging network stuff..");
} }
// e.g. if some error happened:
spdlog::dump_backtrace(); // log them now! show the last 32 messages
// or my_logger->dump_backtrace(32)..
``` ```
--- ---
@@ -178,9 +162,8 @@ spdlog::flush_every(std::chrono::seconds(3));
``` ```
--- ---
#### Binary logging #### Log binary data in hex
```c++ ```c++
// log binary data as hex.
// many types of std::container<char> types can be used. // many types of std::container<char> types can be used.
// ranges are supported too. // ranges are supported too.
// format flags: // format flags:
@@ -310,10 +293,64 @@ void syslog_example()
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.

View File

@@ -26,7 +26,7 @@ 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% -DSPDLOG_BUILD_BENCH=OFF 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%

View File

@@ -1,28 +1,8 @@
# *************************************************************************/ # 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(SpdlogBench CXX) project(spdlog_bench CXX)
if(NOT TARGET spdlog) if(NOT TARGET spdlog)
# Stand-alone build # Stand-alone build
@@ -30,20 +10,19 @@ if(NOT TARGET spdlog)
endif() endif()
find_package(Threads REQUIRED) find_package(Threads REQUIRED)
find_package(benchmark CONFIG REQUIRED)
add_executable(bench bench.cpp) add_executable(bench bench.cpp)
target_link_libraries(bench spdlog::spdlog Threads::Threads) spdlog_enable_warnings(bench)
target_link_libraries(bench PRIVATE spdlog::spdlog)
add_executable(async_bench async_bench.cpp) add_executable(async_bench async_bench.cpp)
target_link_libraries(async_bench spdlog::spdlog Threads::Threads) target_link_libraries(async_bench PRIVATE spdlog::spdlog)
add_executable(latency latency.cpp) add_executable(latency latency.cpp)
set(CMAKE_CXX_STANDARD_LIBRARIES -lbenchmark) target_link_libraries(latency PRIVATE benchmark::benchmark spdlog::spdlog)
target_link_libraries(latency spdlog::spdlog Threads::Threads)
add_executable(formatter-bench formatter-bench.cpp) add_executable(formatter-bench formatter-bench.cpp)
set(CMAKE_CXX_STANDARD_LIBRARIES -lbenchmark) target_link_libraries(formatter-bench PRIVATE benchmark::benchmark spdlog::spdlog)
target_link_libraries(formatter-bench spdlog::spdlog Threads::Threads)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")

View File

@@ -1,31 +0,0 @@
CXX ?= g++
CXXFLAGS = -march=native -Wall -Wextra -pedantic -Wconversion -std=c++11 -pthread -I../include -fmax-errors=1
CXX_RELEASE_FLAGS = -O3 -flto -Wl,--no-as-needed
binaries=bench async_bench latency formatter-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) -lbenchmark
formatter-bench: formatter-bench.cpp
$(CXX) formatter-bench.cpp -o formatter-bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -lbenchmark
.PHONY: clean
clean:
rm -f *.o logs/* latecy_logs $(binaries)
rebuild: clean all

View File

@@ -26,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;
@@ -40,19 +45,36 @@ int count_lines(const char *filename)
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
{ {
spdlog::set_pattern("[%^%l%$] %v");
if (argc == 1) if (argc == 1)
{ {
spdlog::set_pattern("%v");
spdlog::info("Usage: {} <message_count> <threads> <q_size> <iterations>", argv[0]); spdlog::info("Usage: {} <message_count> <threads> <q_size> <iterations>", argv[0]);
return 0; return 0;
} }
@@ -62,37 +84,54 @@ 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]);
auto slot_size = sizeof(spdlog::details::async_msg);
spdlog::info("-------------------------------------------------"); spdlog::info("-------------------------------------------------");
spdlog::info("Messages: {:14n}", howmany); spdlog::info("Messages : {:n}", howmany);
spdlog::info("Threads : {:14n}", threads); spdlog::info("Threads : {:n}", threads);
spdlog::info("Queue : {:14n}", queue_size); spdlog::info("Queue : {:n} slots", queue_size);
spdlog::info("Iters : {:>14n}", iters); 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("-------------------------------------------------"); 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) spdlog::info("");
{ spdlog::info("*********************************");
spdlog::error("Test failed. {} has {:n} lines instead of {:n}", filename, count, howmany); spdlog::info("Queue Overflow Policy: overrun");
exit(1); spdlog::info("*********************************");
} // do same test but discard oldest if queue is full instead of blocking
else filename = "logs/basic_async-overrun.log";
{ for (int i = 0; i < iters; i++)
spdlog::info("Line count OK ({:n})\n", count); {
} 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(); spdlog::shutdown();
} }
@@ -102,7 +141,6 @@ int main(int argc, char *argv[])
perror("Last error"); perror("Last error");
return 1; return 1;
} }
return 0; return 0;
} }

View File

@@ -7,7 +7,6 @@
// bench.cpp : spdlog benchmarks // bench.cpp : spdlog benchmarks
// //
#include "spdlog/spdlog.h" #include "spdlog/spdlog.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"
@@ -31,81 +30,99 @@ 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_default_api(int howmany, std::shared_ptr<spdlog::logger> log);
void bench_c_string(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);
spdlog::default_logger()->set_pattern("[%^%l%$] %v"); spdlog::default_logger()->set_pattern("[%^%l%$] %v");
int howmany = 1000000; int iters = 250000;
int queue_size = howmany + 2; int threads = 4;
int threads = 10;
size_t file_size = 30 * 1024 * 1024;
size_t 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]);
spdlog::info("**************************************************************"); bench_single_threaded(iters);
spdlog::info("Single thread, {:n} iterations", howmany); bench_threaded_logging(1, iters);
spdlog::info("**************************************************************"); bench_threaded_logging(threads, iters);
auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true);
bench(howmany, std::move(basic_st));
basic_st.reset();
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
bench(howmany, std::move(rotating_st));
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
bench(howmany, std::move(daily_st));
bench(howmany, spdlog::create<null_sink_st>("null_st"));
spdlog::info("**************************************************************");
spdlog::info("C-string (400 bytes). Single thread, {:n} iterations", howmany);
spdlog::info("**************************************************************");
basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_cs.log", true);
bench_c_string(howmany, std::move(basic_st));
rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_cs.log", file_size, rotating_files);
bench_c_string(howmany, std::move(rotating_st));
daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_cs.log");
bench_c_string(howmany, std::move(daily_st));
bench_c_string(howmany, spdlog::create<null_sink_st>("null_st"));
spdlog::info("**************************************************************");
spdlog::info("{:n} threads sharing same logger, {:n} iterations", threads, howmany);
spdlog::info("**************************************************************");
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
bench_mt(howmany, std::move(basic_mt), threads);
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files);
bench_mt(howmany, std::move(rotating_mt), threads);
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
bench_mt(howmany, std::move(daily_mt), threads);
bench_mt(howmany, spdlog::create<null_sink_mt>("null_mt"), threads);
spdlog::info("**************************************************************");
spdlog::info("Asyncronous.. {:n} threads sharing same logger, {:n} iterations", threads, howmany);
spdlog::info("**************************************************************");
for (int i = 0; i < 3; ++i)
{
spdlog::init_thread_pool(static_cast<size_t>(queue_size), 1);
auto as = spdlog::basic_logger_mt<spdlog::async_factory>("async", "logs/basic_async.log", true);
bench_mt(howmany, std::move(as), threads);
}
} }
catch (std::exception &ex) catch (std::exception &ex)
{ {
@@ -127,7 +144,7 @@ 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();
spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d)); 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());
} }
@@ -153,7 +170,7 @@ 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();
spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d)); 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());
} }
@@ -172,7 +189,7 @@ void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log)
auto delta_d = duration_cast<duration<double>>(delta).count(); auto delta_d = duration_cast<duration<double>>(delta).count();
spdlog::drop(log->name()); spdlog::drop(log->name());
spdlog::set_default_logger(std::move(orig_default)); spdlog::set_default_logger(std::move(orig_default));
spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d)); 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) void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
@@ -195,5 +212,5 @@ void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
auto delta_d = duration_cast<duration<double>>(delta).count(); auto delta_d = duration_cast<duration<double>>(delta).count();
spdlog::drop(log->name()); spdlog::drop(log->name());
spdlog::set_default_logger(std::move(orig_default)); spdlog::set_default_logger(std::move(orig_default));
spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d)); spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
} }

View File

@@ -8,27 +8,15 @@
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include "spdlog/details/pattern_formatter.h" #include "spdlog/details/pattern_formatter.h"
void bench_scoped_pad(benchmark::State &state, size_t wrapped_size, spdlog::details::padding_info padinfo)
{
fmt::memory_buffer dest;
for (auto _ : state)
{
{
spdlog::details::scoped_pad p(wrapped_size, padinfo, dest);
benchmark::DoNotOptimize(p);
dest.clear();
}
}
}
void bench_formatter(benchmark::State &state, std::string pattern) void bench_formatter(benchmark::State &state, std::string pattern)
{ {
auto formatter = spdlog::details::make_unique<spdlog::pattern_formatter>(pattern); auto formatter = spdlog::details::make_unique<spdlog::pattern_formatter>(pattern);
fmt::memory_buffer dest; spdlog::memory_buf_t dest;
std::string logger_name = "logger-name"; std::string logger_name = "logger-name";
const char *text = "Hello. This is some message with length of 80 "; const char *text = "Hello. This is some message with length of 80 ";
spdlog::details::log_msg msg(&logger_name, spdlog::level::info, text); 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) for (auto _ : state)
{ {
@@ -41,7 +29,7 @@ void bench_formatter(benchmark::State &state, std::string pattern)
void bench_formatters() void bench_formatters()
{ {
// basic patterns(single flag) // basic patterns(single flag)
std::string all_flags = "+vtPnlLaAbBcCYDmdHIMSefFprRTXzEi%"; std::string all_flags = "+vtPnlLaAbBcCYDmdHIMSefFprRTXzEisg@luioO%";
std::vector<std::string> basic_patterns; std::vector<std::string> basic_patterns;
for (auto &flag : all_flags) for (auto &flag : all_flags)
{ {

View File

@@ -90,29 +90,50 @@ int main(int argc, char *argv[])
disabled_logger->set_level(spdlog::level::off); disabled_logger->set_level(spdlog::level::off);
benchmark::RegisterBenchmark("disabled-at-compile-time", bench_disabled_macro, disabled_logger); benchmark::RegisterBenchmark("disabled-at-compile-time", bench_disabled_macro, disabled_logger);
benchmark::RegisterBenchmark("disabled-at-runtime", bench_logger, 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>()); 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 (500_bytes c_str)", bench_c_string, std::move(null_logger_st));
benchmark::RegisterBenchmark("null_sink_st", bench_logger, 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 // basic_st
auto basic_st = spdlog::basic_logger_st("basic_st", "latency_logs/basic_st.log", true); 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(); benchmark::RegisterBenchmark("basic_st", bench_logger, std::move(basic_st))->UseRealTime();
spdlog::drop("basic_st"); 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 // rotating st
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "latency_logs/rotating_st.log", file_size, rotating_files); 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(); benchmark::RegisterBenchmark("rotating_st", bench_logger, std::move(rotating_st))->UseRealTime();
spdlog::drop("rotating_st"); 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 // daily st
auto daily_st = spdlog::daily_logger_mt("daily_st", "latency_logs/daily_st.log"); 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(); benchmark::RegisterBenchmark("daily_st", bench_logger, std::move(daily_st))->UseRealTime();
spdlog::drop("daily_st"); 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 // 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>()); 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(); benchmark::RegisterBenchmark("null_sink_mt", bench_logger, null_logger_mt)->Threads(n_threads)->UseRealTime();
@@ -138,6 +159,11 @@ int main(int argc, char *argv[])
"async_logger", std::make_shared<null_sink_mt>(), std::move(tp), spdlog::async_overflow_policy::overrun_oldest); "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(); 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::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks(); benchmark::RunSpecifiedBenchmarks();
} }

View File

@@ -1,4 +0,0 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

View File

@@ -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
View 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')

View File

@@ -1,2 +0,0 @@
#!/bin/bash
clang-tidy example/example.cpp -- -I ./include

View File

@@ -1,31 +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. */
# *************************************************************************/
set(SPDLOG_FMT_EXTERNAL @SPDLOG_FMT_EXTERNAL@)
include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
if(SPDLOG_FMT_EXTERNAL)
include(CMakeFindDependencyMacro)
find_dependency(fmt CONFIG)
endif()

18
cmake/ide.cmake Normal file
View 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})

View File

@@ -1,21 +0,0 @@
if(SPDLOG_SANITIZE_THREAD AND SPDLOG_SANITIZE_ADDRESS)
message(FATAL_ERROR "AddressSanitizer is not compatible with ThreadSanitizer.")
endif()
if(SPDLOG_SANITIZE_ADDRESS)
message(STATUS "AddressSanitizer enabled")
set(SANITIZER_FLAGS "-fsanitize=address,undefined")
add_compile_options("-fno-sanitize=signed-integer-overflow")
endif()
if(SPDLOG_SANITIZE_THREAD)
message(STATUS "ThreadSanitizer enabled")
set(SANITIZER_FLAGS "-fsanitize=thread")
endif()
if(SPDLOG_SANITIZE_THREAD OR SPDLOG_SANITIZE_ADDRESS)
add_compile_options(${SANITIZER_FLAGS})
add_compile_options("-fno-sanitize-recover=all")
add_compile_options("-fno-omit-frame-pointer")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_FLAGS} -fuse-ld=gold")
endif()

View File

@@ -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
View 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)

View 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
View 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()

View File

@@ -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(NOT TARGET spdlog) if(NOT TARGET spdlog)
# Stand-alone build # Stand-alone build
find_package(spdlog CONFIG REQUIRED) find_package(spdlog REQUIRED)
endif() endif()
find_package(Threads REQUIRED) #---------------------------------------------------------------------------------------
# Example of using pre-compiled library
#---------------------------------------------------------------------------------------
add_executable(example example.cpp) add_executable(example example.cpp)
if(CMAKE_SYSTEM_NAME STREQUAL "Android") spdlog_enable_warnings(example)
find_library(log-lib log) target_link_libraries(example PRIVATE spdlog::spdlog)
target_link_libraries(example spdlog::spdlog Threads::Threads log)
else() #---------------------------------------------------------------------------------------
target_link_libraries(example 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() endif()
# Create logs directory
add_executable(multisink multisink.cpp)
target_link_libraries(multisink spdlog::spdlog Threads::Threads)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
enable_testing()
add_test(NAME example COMMAND example)

View File

@@ -1,21 +0,0 @@
CXX ?= g++
CXX_FLAGS = -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1 -Wconversion
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

View File

@@ -1,22 +0,0 @@
#-Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded
CXX ?= g++
CXX_FLAGS = -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1 -Wconversion -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-weak-vtables -Wno-global-constructors
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

View File

@@ -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

View File

@@ -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

View File

@@ -1,11 +1,8 @@
// //
// 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 <cstdio> #include <cstdio>
@@ -20,13 +17,12 @@ void multi_sink_example();
void user_defined_example(); void user_defined_example();
void err_handler_example(); void err_handler_example();
void syslog_example(); void syslog_example();
void clone_example();
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
int main(int, char *[]) int main(int, char *[])
{ {
spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH); 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::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::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("Support for floats {:03.2f}", 1.23456);
@@ -43,6 +39,18 @@ int main(int, char *[])
spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [thread %t] %v"); spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [thread %t] %v");
spdlog::info("This an info message with custom format"); spdlog::info("This an info message with custom format");
spdlog::set_pattern("%+"); // back to default 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
{ {
@@ -50,7 +58,6 @@ int main(int, char *[])
basic_example(); basic_example();
rotating_example(); rotating_example();
daily_example(); daily_example();
clone_example();
async_example(); async_example();
binary_example(); binary_example();
multi_sink_example(); multi_sink_example();
@@ -109,14 +116,6 @@ void daily_example()
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);
} }
// Clone a logger and give it new name.
// Useful for creating component/subsystem loggers from some "root" logger.
void clone_example()
{
auto network_logger = spdlog::default_logger()->clone("network");
network_logger->info("Logging network stuff..");
}
#include "spdlog/async.h" #include "spdlog/async.h"
void async_example() void async_example()
{ {

View File

@@ -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

View File

@@ -1,167 +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>
</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>

View File

@@ -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)

View File

@@ -1,2 +0,0 @@
# Exceptions are used in spdlog. Link to an exception-ready C++ runtime.
APP_STL = gnustl_static

View File

@@ -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);
}

View File

@@ -1 +0,0 @@
*.txt

5
example/meson.build Normal file
View 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')

View File

@@ -1,47 +0,0 @@
#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/stdout_sinks.h"
#include <iostream>
#include <memory>
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 spdlog::spdlog_ex &ex)
{
std::cout << "Log init failed: " << ex.what() << std::endl;
return 1;
}
}

View File

@@ -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

View File

@@ -1,9 +0,0 @@
#!/bin/bash
echo -n "Running dos2unix "
find . -name "*\.h" -o -name "*\.cpp"|grep -v bundled|xargs -I {} sh -c "dos2unix '{}' 2>/dev/null; echo -n '.'"
echo
echo -n "Running clang-format "
find . -name "*\.h" -o -name "*\.cpp"|grep -v bundled|xargs -I {} sh -c "clang-format -i {}; echo -n '.'"
echo

View File

@@ -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 {
@@ -42,7 +40,9 @@ struct async_factory_impl
auto &registry_inst = details::registry::instance(); auto &registry_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> tp_lock(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)
{ {
@@ -72,11 +72,17 @@ inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name,
return async_factory_nonblock::create<Sink>(std::move(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.

View 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;
}

View File

@@ -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.
@@ -47,7 +37,11 @@ class async_logger final : public std::enable_shared_from_this<async_logger>, pu
public: public:
template<typename It> template<typename It>
async_logger(std::string logger_name, It begin, 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_list, 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);
@@ -58,10 +52,9 @@ public:
std::shared_ptr<logger> clone(std::string new_name) override; std::shared_ptr<logger> clone(std::string new_name) override;
protected: protected:
void sink_it_(details::log_msg &msg) override; void sink_it_(const details::log_msg &msg) override;
void flush_() override; void flush_() override;
void backend_sink_it_(const details::log_msg &incoming_log_msg);
void backend_log_(const details::log_msg &incoming_log_msg);
void backend_flush_(); void backend_flush_();
private: private:
@@ -70,4 +63,6 @@ private:
}; };
} // namespace spdlog } // namespace spdlog
#include "details/async_logger_impl.h" #ifdef SPDLOG_HEADER_ONLY
#include "async_logger-inl.h"
#endif

View 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

View File

@@ -1,35 +1,45 @@
// // 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 <cstring>
#include <type_traits> #include <type_traits>
#include <unordered_map> #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" #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
@@ -51,23 +61,25 @@
#endif #endif
#endif #endif
// Get the basename of __FILE__ (at compile time if possible)
#if FMT_HAS_FEATURE(__builtin_strrchr)
#define SPDLOG_STRRCHR(str, sep) __builtin_strrchr(str, sep)
#else
#define SPDLOG_STRRCHR(str, sep) strrchr(str, sep)
#endif //__builtin_strrchr not defined
#ifdef _WIN32
#define SPDLOG_FILE_BASENAME(file) SPDLOG_STRRCHR("\\" file, '\\') + 1
#else
#define SPDLOG_FILE_BASENAME(file) SPDLOG_STRRCHR("/" file, '/') + 1
#endif
#ifndef SPDLOG_FUNCTION #ifndef SPDLOG_FUNCTION
#define SPDLOG_FUNCTION __FUNCTION__ #define SPDLOG_FUNCTION __FUNCTION__
#endif #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 {
class formatter; class formatter;
@@ -76,17 +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>;
// string_view type - either std::string_view or fmt::string_view (pre c++17) #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
#if defined(FMT_USE_STD_STRING_VIEW) #ifndef _WIN32
using string_view_t = std::string_view; #error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
#else #else
using string_view_t = fmt::string_view; using wstring_view_t = basic_string_view_t<wchar_t>;
#endif
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;
@@ -126,36 +157,31 @@ enum level_enum
} }
#endif #endif
static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES; #if !defined(SPDLOG_SHORT_LEVEL_NAMES)
static const char *short_level_names[]{"T", "D", "I", "W", "E", "C", "O"};
inline string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT #define SPDLOG_SHORT_LEVEL_NAMES \
{ { \
return level_string_views[l]; "T", "D", "I", "W", "E", "C", "O" \
}
inline const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
{
return short_level_names[l];
}
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; #endif
}
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;
spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
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
@@ -172,58 +198,30 @@ enum class pattern_time_type
class spdlog_ex : public std::exception class spdlog_ex : public std::exception
{ {
public: public:
explicit spdlog_ex(std::string msg) explicit spdlog_ex(std::string msg);
: msg_(std::move(msg)) spdlog_ex(const std::string &msg, int last_errno);
{ const char *what() const SPDLOG_NOEXCEPT override;
}
spdlog_ex(const std::string &msg, int last_errno)
{
fmt::memory_buffer outbuf;
fmt::format_system_error(outbuf, last_errno, msg);
msg_ = fmt::to_string(outbuf);
}
const char *what() const SPDLOG_NOEXCEPT override
{
return msg_.c_str();
}
private: private:
std::string msg_; std::string msg_;
}; };
//
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
//
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
using filename_t = std::wstring;
#else
using filename_t = std::string;
#endif
struct source_loc struct source_loc
{ {
SPDLOG_CONSTEXPR source_loc() SPDLOG_CONSTEXPR source_loc() = default;
: filename{""} SPDLOG_CONSTEXPR source_loc(const char *filename_in, int line_in, const char *funcname_in)
, line{0} : filename{filename_in}
, funcname{""} , line{line_in}
{ , funcname{funcname_in}
} {}
SPDLOG_CONSTEXPR source_loc(const char *filename, int line, const char *funcname)
: filename{filename}
, line{static_cast<uint32_t>(line)}
, funcname{funcname}
{
}
SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT
{ {
return line == 0; return line == 0;
} }
const char *filename; const char *filename{nullptr};
uint32_t line; int line{0};
const char *funcname; const char *funcname{nullptr};
}; };
namespace details { namespace details {
@@ -240,4 +238,9 @@ std::unique_ptr<T> make_unique(Args &&... args)
} }
#endif #endif
} // namespace details } // namespace details
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY
#include "common-inl.h"
#endif

View File

@@ -1,110 +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, It begin, It end, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
: logger(std::move(logger_name), begin, end)
, thread_pool_(std::move(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(), std::move(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), {std::move(single_sink)}, std::move(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(), msg, overflow_policy_);
}
else
{
throw spdlog_ex("async log: thread pool doesn'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_(const 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
}
inline std::shared_ptr<spdlog::logger> spdlog::async_logger::clone(std::string new_name)
{
auto cloned = std::make_shared<spdlog::async_logger>(std::move(new_name), sinks_.begin(), sinks_.end(), thread_pool_, overflow_policy_);
cloned->set_level(this->level());
cloned->flush_on(this->flush_level());
cloned->set_error_handler(this->error_handler());
return std::move(cloned);
}

View 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

View 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

View File

@@ -1,7 +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)
//
// cirucal q view of std::vector. // cirucal q view of std::vector.
#pragma once #pragma once
@@ -13,45 +11,87 @@ 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);
++overrun_counter_; 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 size_t overrun_counter() const
@@ -60,13 +100,20 @@ public:
} }
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;
size_t overrun_counter_ = 0; other.head_ = other.tail_ = 0;
other.overrun_counter_ = 0;
}
}; };
} // namespace details } // namespace details
} // namespace spdlog } // namespace spdlog

View File

@@ -1,55 +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 <cstdio>
#include <mutex> #include <mutex>
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX // prevent windows redefining min/max
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#endif
namespace spdlog { namespace spdlog {
namespace details { namespace details {
struct console_stdout
{
static std::FILE *stream()
{
return stdout;
}
#ifdef _WIN32
static HANDLE handle()
{
return ::GetStdHandle(STD_OUTPUT_HANDLE);
}
#endif
};
struct console_stderr
{
static std::FILE *stream()
{
return stderr;
}
#ifdef _WIN32
static HANDLE handle()
{
return ::GetStdHandle(STD_ERROR_HANDLE);
}
#endif
};
struct console_mutex struct console_mutex
{ {

View 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

View File

@@ -1,113 +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 sinks. #include "spdlog/common.h"
// When failing to open a file, retry several times(5) with a delay interval(10 ms).
// Throw spdlog_ex exception on errors.
#include "spdlog/details/log_msg.h"
#include "spdlog/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:
@@ -122,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_extension(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 != filename_t::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:
const int open_tries = 5;
const int open_interval = 10;
std::FILE *fd_{nullptr}; 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

View File

@@ -1,33 +1,23 @@
// // 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 <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 spdlog::string_view_t to_string_view(const fmt::basic_memory_buffer<char, Buffer_Size> &buf) SPDLOG_NOEXCEPT
{ {
return spdlog::string_view_t(buf.data(), buf.size()); return spdlog::string_view_t{buf.data(), buf.size()};
} }
template<size_t Buffer_Size1, size_t Buffer_Size2> inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest)
inline void append_buf(const fmt::basic_memory_buffer<char, Buffer_Size1> &buf, fmt::basic_memory_buffer<char, Buffer_Size2> &dest)
{
auto *buf_ptr = buf.data();
dest.append(buf_ptr, buf_ptr + buf.size());
}
template<size_t Buffer_Size>
inline void append_string_view(spdlog::string_view_t view, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{ {
auto *buf_ptr = view.data(); auto *buf_ptr = view.data();
if (buf_ptr != nullptr) if (buf_ptr != nullptr)
@@ -36,8 +26,8 @@ inline void append_string_view(spdlog::string_view_t view, fmt::basic_memory_buf
} }
} }
template<typename T, size_t Buffer_Size> template<typename T>
inline void append_int(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest) inline void append_int(T n, memory_buf_t &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());
@@ -50,8 +40,7 @@ inline unsigned count_digits(T n)
return static_cast<unsigned>(fmt::internal::count_digits(static_cast<count_type>(n))); return static_cast<unsigned>(fmt::internal::count_digits(static_cast<count_type>(n)));
} }
template<size_t Buffer_Size> inline void pad2(int n, memory_buf_t &dest)
inline void pad2(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{ {
if (n > 99) if (n > 99)
{ {
@@ -73,8 +62,8 @@ inline void pad2(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
} }
} }
template<typename T, size_t Buffer_Size> template<typename T>
inline void pad_uint(T n, unsigned int width, fmt::basic_memory_buffer<char, Buffer_Size> &dest) inline void pad_uint(T n, unsigned int width, memory_buf_t &dest)
{ {
static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T"); static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
auto digits = count_digits(n); auto digits = count_digits(n);
@@ -86,20 +75,20 @@ inline void pad_uint(T n, unsigned int width, fmt::basic_memory_buffer<char, Buf
append_int(n, dest); append_int(n, dest);
} }
template<typename T, size_t Buffer_Size> template<typename T>
inline void pad3(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest) inline void pad3(T n, memory_buf_t &dest)
{ {
pad_uint(n, 3, dest); pad_uint(n, 3, dest);
} }
template<typename T, size_t Buffer_Size> template<typename T>
inline void pad6(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest) inline void pad6(T n, memory_buf_t &dest)
{ {
pad_uint(n, 6, dest); pad_uint(n, 6, dest);
} }
template<typename T, size_t Buffer_Size> template<typename T>
inline void pad9(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest) inline void pad9(T n, memory_buf_t &dest)
{ {
pad_uint(n, 9, dest); pad_uint(n, 9, dest);
} }
@@ -108,7 +97,7 @@ inline void pad9(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
// 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 std::chrono::duration_cast; using std::chrono::duration_cast;
using std::chrono::seconds; using std::chrono::seconds;

View 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

View File

@@ -1,55 +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(source_loc loc, const std::string *loggers_name, level::level_enum lvl, string_view_t view) 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)
#ifndef SPDLOG_NO_DATETIME
, time(os::now())
#endif
#ifndef SPDLOG_NO_THREAD_ID
, thread_id(os::thread_id())
, source(loc)
, payload(view)
#endif
{
}
log_msg(const std::string *loggers_name, level::level_enum lvl, string_view_t view)
: log_msg(source_loc{}, loggers_name, lvl, view)
{
}
log_msg(const log_msg &other) = default; log_msg(const log_msg &other) = default;
const std::string *logger_name{nullptr}; string_view_t logger_name;
level::level_enum level{level::off}; level::level_enum level{level::off};
log_clock::time_point time; log_clock::time_point time;
size_t thread_id{0}; size_t thread_id{0};
size_t msg_id{0};
// wrapping the formatted text with color (updated by pattern_formatter). // wrapping the formatted text with color (updated by pattern_formatter).
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; source_loc source;
const string_view_t payload; string_view_t payload;
}; };
} // namespace details } // namespace details
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY
#include "log_msg-inl.h"
#endif

View 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

View 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

View File

@@ -1,441 +0,0 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include "spdlog/details/fmt_helper.h"
#include <memory>
#include <string>
#define SPDLOG_CATCH_AND_HANDLE \
catch (const std::exception &ex) \
{ \
err_handler_(ex.what()); \
} \
catch (...) \
{ \
err_handler_("Unknown exception in logger"); \
}
// 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, It begin, It end)
: name_(std::move(logger_name))
, sinks_(begin, end)
{
}
// 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)
{
auto new_formatter = details::make_unique<spdlog::pattern_formatter>(std::move(pattern), time_type);
set_formatter(std::move(new_formatter));
}
template<typename... Args>
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const char *fmt, const Args &... args)
{
if (!should_log(lvl))
{
return;
}
try
{
using details::fmt_helper::to_string_view;
fmt::memory_buffer buf;
fmt::format_to(buf, fmt, args...);
details::log_msg log_msg(source, &name_, lvl, to_string_view(buf));
sink_it_(log_msg);
}
SPDLOG_CATCH_AND_HANDLE
}
template<typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args)
{
log(source_loc{}, lvl, fmt, args...);
}
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const char *msg)
{
if (!should_log(lvl))
{
return;
}
try
{
details::log_msg log_msg(source, &name_, lvl, spdlog::string_view_t(msg));
sink_it_(log_msg);
}
SPDLOG_CATCH_AND_HANDLE
}
inline void spdlog::logger::log(level::level_enum lvl, const char *msg)
{
log(source_loc{}, lvl, msg);
}
template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const T &msg)
{
if (!should_log(lvl))
{
return;
}
try
{
details::log_msg log_msg(source, &name_, lvl, msg);
sink_it_(log_msg);
}
SPDLOG_CATCH_AND_HANDLE
}
template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
{
log(source_loc{}, lvl, msg);
}
template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const T &msg)
{
if (!should_log(lvl))
{
return;
}
try
{
using details::fmt_helper::to_string_view;
fmt::memory_buffer buf;
fmt::format_to(buf, "{}", msg);
details::log_msg log_msg(source, &name_, lvl, to_string_view(buf));
sink_it_(log_msg);
}
SPDLOG_CATCH_AND_HANDLE
}
template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
{
log(source_loc{}, lvl, msg);
}
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
inline void wbuf_to_utf8buf(const fmt::wmemory_buffer &wbuf, fmt::memory_buffer &target)
{
int wbuf_size = static_cast<int>(wbuf.size());
if (wbuf_size == 0)
{
return;
}
auto result_size = ::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, NULL, 0, NULL, NULL);
if (result_size > 0)
{
target.resize(result_size);
::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, &target.data()[0], result_size, NULL, NULL);
}
else
{
throw spdlog::spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
}
}
template<typename... Args>
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const wchar_t *fmt, const Args &... args)
{
if (!should_log(lvl))
{
return;
}
try
{
// format to wmemory_buffer and convert to utf8
using details::fmt_helper::to_string_view;
fmt::wmemory_buffer wbuf;
fmt::format_to(wbuf, fmt, args...);
fmt::memory_buffer buf;
wbuf_to_utf8buf(wbuf, buf);
details::log_msg log_msg(source, &name_, lvl, to_string_view(buf));
sink_it_(log_msg);
}
SPDLOG_CATCH_AND_HANDLE
}
template<typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args)
{
log(source_loc{}, lvl, fmt, args...);
}
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() const
{
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 spdlog::level::level_enum spdlog::logger::flush_level() const
{
return static_cast<spdlog::level::level_enum>(flush_level_.load(std::memory_order_relaxed));
}
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::default_level()
{
return static_cast<spdlog::level::level_enum>(SPDLOG_ACTIVE_LEVEL);
}
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_;
}
inline std::shared_ptr<spdlog::logger> spdlog::logger::clone(std::string logger_name)
{
auto cloned = std::make_shared<spdlog::logger>(std::move(logger_name), sinks_.begin(), sinks_.end());
cloned->set_level(this->level());
cloned->flush_on(this->flush_level());
cloned->set_error_handler(this->error_handler());
return cloned;
}

View File

@@ -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,7 +97,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;
} }

View File

@@ -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
} }
}; };

View 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

View File

@@ -1,112 +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_NOEXCEPT 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) SPDLOG_NOEXCEPT
{
#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() SPDLOG_NOEXCEPT 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) SPDLOG_NOEXCEPT
{
#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() SPDLOG_NOEXCEPT
{
std::time_t now_t = time(nullptr);
return gmtime(now_t);
}
// eol definition // eol definition
#if !defined(SPDLOG_EOL) #if !defined(SPDLOG_EOL)
@@ -121,301 +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_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 // 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) SPDLOG_NOEXCEPT // 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) SPDLOG_NOEXCEPT 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) SPDLOG_NOEXCEPT 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
__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(__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() SPDLOG_NOEXCEPT 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() SPDLOG_NOEXCEPT 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. // 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) SPDLOG_NOEXCEPT 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() SPDLOG_NOEXCEPT 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) SPDLOG_NOEXCEPT 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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View 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

View File

@@ -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,43 +20,11 @@ namespace details {
class periodic_worker class periodic_worker
{ {
public: public:
periodic_worker(const 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

View 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(&registry::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 &registry::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 &registry::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

View File

@@ -1,37 +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"
#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 <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
{ {
@@ -39,247 +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);
register_logger_(std::move(new_logger)); std::shared_ptr<logger> default_logger();
}
void 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 (automatic_registration_)
{
register_logger_(std::move(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;
}
std::shared_ptr<logger> default_logger()
{
std::lock_guard<std::mutex> lock(logger_map_mutex_);
return default_logger_;
}
// Return raw ptr to the default logger. // Return raw ptr to the default logger.
// To be used directly by the spdlog default api (e.g. spdlog::info) // 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(). // 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. // e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
logger *get_default_raw() logger *get_default_raw();
{
return default_logger_.get();
}
// set default logger. // set default logger.
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map. // 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) void 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);
}
void set_tp(std::shared_ptr<thread_pool> tp) 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::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(&registry::flush_all, this);
periodic_flusher_ = details::make_unique<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(const std::function<void(const 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);
if (default_logger_ && default_logger_->name() == logger_name)
{
default_logger_.reset();
}
}
void drop_all() void flush_all();
{
std::lock_guard<std::mutex> lock(logger_map_mutex_); void drop(const std::string &logger_name);
loggers_.clear();
default_logger_.reset(); void drop_all();
}
// clean all resources and threads started by the registry // clean all resources and threads started by the registry
void shutdown() void shutdown();
{
{
std::lock_guard<std::mutex> lock(flusher_mutex_);
periodic_flusher_.reset();
}
drop_all(); std::recursive_mutex &tp_mutex();
{ void set_automatic_registration(bool automatic_regsistration);
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
tp_.reset();
}
}
std::recursive_mutex &tp_mutex() static registry &instance();
{
return tp_mutex_;
}
void set_automatic_registration(bool automatic_regsistration)
{
std::lock_guard<std::mutex> lock(logger_map_mutex_);
automatic_registration_ = automatic_regsistration;
}
static registry &instance()
{
static registry s_instance;
return s_instance;
}
private: private:
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
}
~registry() = default; ~registry() = default;
void throw_if_exists_(const std::string &logger_name) void throw_if_exists_(const std::string &logger_name);
{ void register_logger_(std::shared_ptr<logger> new_logger);
if (loggers_.find(logger_name) != loggers_.end())
{
throw spdlog_ex("logger with name '" + logger_name + "' already exists");
}
}
void 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);
}
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_ = spdlog::logger::default_level(); 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_; std::shared_ptr<logger> default_logger_;
bool automatic_registration_ = true; 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

View 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

View 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

View File

@@ -1,7 +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/fmt_helper.h" #include "spdlog/details/log_msg_buffer.h"
#include "spdlog/details/log_msg.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"
@@ -9,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>;
@@ -22,18 +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;
source_loc source;
async_logger_ptr worker_ptr; async_logger_ptr worker_ptr;
async_msg() = default; async_msg() = default;
@@ -44,26 +43,16 @@ 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),
source(other.source),
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;
source = other.source;
worker_ptr = std::move(other.worker_ptr); worker_ptr = std::move(other.worker_ptr);
return *this; return *this;
} }
@@ -73,46 +62,21 @@ struct async_msg
#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)
, source(m.source)
, worker_ptr(std::move(worker))
{
fmt_helper::append_string_view(m.payload, raw);
}
async_msg(async_logger_ptr &&worker, async_msg_type the_type) async_msg(async_logger_ptr &&worker, async_msg_type the_type)
: msg_type(the_type) : log_msg_buffer{}
, level(level::off) , msg_type{the_type}
, time() , worker_ptr{std::move(worker)}
, thread_id(0) {}
, msg_id(0)
, source()
, 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) : async_msg{nullptr, the_type}
{ {}
}
// copy into log_msg
log_msg to_log_msg()
{
log_msg msg(&worker_ptr->name(), level, string_view_t(raw.data(), raw.size()));
msg.time = time;
msg.thread_id = thread_id;
msg.msg_id = msg_id;
msg.source = source;
msg.color_range_start = 0;
msg.color_range_end = 0;
return msg;
}
}; };
class thread_pool class thread_pool
@@ -121,118 +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(&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_)
{
t.join();
}
}
catch (...)
{
}
}
thread_pool(const thread_pool &) = delete; thread_pool(const thread_pool &) = delete;
thread_pool &operator=(thread_pool &&) = delete; thread_pool &operator=(thread_pool &&) = delete;
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::move(worker_ptr), async_msg_type::log, 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);
}
size_t overrun_counter()
{
return q_.overrun_counter();
}
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::log:
{
auto msg = incoming_async_msg.to_log_msg();
incoming_async_msg.worker_ptr->backend_log_(msg);
return true;
}
case async_msg_type::flush:
{
incoming_async_msg.worker_ptr->backend_flush_();
return true;
}
case async_msg_type::terminate:
{
return false;
}
}
assert(false && "Unexpected async_msg_type");
return true;
}
}; };
} // namespace details } // namespace details
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY
#include "thread_pool-inl.h"
#endif

View File

@@ -31,8 +31,7 @@ public:
bytes_range(It range_begin, It range_end) bytes_range(It range_begin, It range_end)
: begin_(range_begin) : begin_(range_begin)
, end_(range_end) , end_(range_end)
{ {}
}
It begin() const It begin() const
{ {
@@ -117,7 +116,11 @@ struct formatter<spdlog::details::bytes_range<T>>
std::size_t pos = 0; std::size_t pos = 0;
std::size_t column = line_size; std::size_t column = line_size;
#if FMT_VERSION < 60000
auto inserter = ctx.begin(); auto inserter = ctx.begin();
#else
auto inserter = ctx.out();
#endif
for (auto &item : the_range) for (auto &item : the_range)
{ {

View File

@@ -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.

View File

@@ -16,9 +16,181 @@
#include <locale> #include <locale>
#include <sstream> #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 FMT_BEGIN_NAMESPACE
namespace internal{ // 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 { enum class numeric_system {
standard, standard,
@@ -28,8 +200,9 @@ enum class numeric_system {
// Parses a put_time-like format string and invokes handler actions. // Parses a put_time-like format string and invokes handler actions.
template <typename Char, typename Handler> template <typename Char, typename Handler>
FMT_CONSTEXPR const Char *parse_chrono_format( FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
const Char *begin, const Char *end, Handler &&handler) { const Char* end,
Handler&& handler) {
auto ptr = begin; auto ptr = begin;
while (ptr != end) { while (ptr != end) {
auto c = *ptr; auto c = *ptr;
@@ -38,11 +211,9 @@ FMT_CONSTEXPR const Char *parse_chrono_format(
++ptr; ++ptr;
continue; continue;
} }
if (begin != ptr) if (begin != ptr) handler.on_text(begin, ptr);
handler.on_text(begin, ptr); ++ptr; // consume '%'
++ptr; // consume '%' if (ptr == end) FMT_THROW(format_error("invalid format"));
if (ptr == end)
throw format_error("invalid format");
c = *ptr++; c = *ptr++;
switch (c) { switch (c) {
case '%': case '%':
@@ -119,6 +290,12 @@ FMT_CONSTEXPR const Char *parse_chrono_format(
case 'p': case 'p':
handler.on_am_pm(); handler.on_am_pm();
break; break;
case 'Q':
handler.on_duration_value();
break;
case 'q':
handler.on_duration_unit();
break;
case 'z': case 'z':
handler.on_utc_offset(); handler.on_utc_offset();
break; break;
@@ -127,8 +304,7 @@ FMT_CONSTEXPR const Char *parse_chrono_format(
break; break;
// Alternative representation: // Alternative representation:
case 'E': { case 'E': {
if (ptr == end) if (ptr == end) FMT_THROW(format_error("invalid format"));
throw format_error("invalid format");
c = *ptr++; c = *ptr++;
switch (c) { switch (c) {
case 'c': case 'c':
@@ -141,13 +317,12 @@ FMT_CONSTEXPR const Char *parse_chrono_format(
handler.on_loc_time(numeric_system::alternative); handler.on_loc_time(numeric_system::alternative);
break; break;
default: default:
throw format_error("invalid format"); FMT_THROW(format_error("invalid format"));
} }
break; break;
} }
case 'O': case 'O':
if (ptr == end) if (ptr == end) FMT_THROW(format_error("invalid format"));
throw format_error("invalid format");
c = *ptr++; c = *ptr++;
switch (c) { switch (c) {
case 'w': case 'w':
@@ -169,96 +344,259 @@ FMT_CONSTEXPR const Char *parse_chrono_format(
handler.on_second(numeric_system::alternative); handler.on_second(numeric_system::alternative);
break; break;
default: default:
throw format_error("invalid format"); FMT_THROW(format_error("invalid format"));
} }
break; break;
default: default:
throw format_error("invalid format"); FMT_THROW(format_error("invalid format"));
} }
begin = ptr; begin = ptr;
} }
if (begin != ptr) if (begin != ptr) handler.on_text(begin, ptr);
handler.on_text(begin, ptr);
return ptr; return ptr;
} }
struct chrono_format_checker { struct chrono_format_checker {
void report_no_date() { throw format_error("no date"); } FMT_NORETURN void report_no_date() { FMT_THROW(format_error("no date")); }
template <typename Char> template <typename Char> void on_text(const Char*, const Char*) {}
void on_text(const Char *, const Char *) {} FMT_NORETURN void on_abbr_weekday() { report_no_date(); }
void on_abbr_weekday() { report_no_date(); } FMT_NORETURN void on_full_weekday() { report_no_date(); }
void on_full_weekday() { report_no_date(); } FMT_NORETURN void on_dec0_weekday(numeric_system) { report_no_date(); }
void on_dec0_weekday(numeric_system) { report_no_date(); } FMT_NORETURN void on_dec1_weekday(numeric_system) { report_no_date(); }
void on_dec1_weekday(numeric_system) { report_no_date(); } FMT_NORETURN void on_abbr_month() { report_no_date(); }
void on_abbr_month() { report_no_date(); } FMT_NORETURN void on_full_month() { report_no_date(); }
void on_full_month() { report_no_date(); }
void on_24_hour(numeric_system) {} void on_24_hour(numeric_system) {}
void on_12_hour(numeric_system) {} void on_12_hour(numeric_system) {}
void on_minute(numeric_system) {} void on_minute(numeric_system) {}
void on_second(numeric_system) {} void on_second(numeric_system) {}
void on_datetime(numeric_system) { report_no_date(); } FMT_NORETURN void on_datetime(numeric_system) { report_no_date(); }
void on_loc_date(numeric_system) { report_no_date(); } FMT_NORETURN void on_loc_date(numeric_system) { report_no_date(); }
void on_loc_time(numeric_system) { report_no_date(); } FMT_NORETURN void on_loc_time(numeric_system) { report_no_date(); }
void on_us_date() { report_no_date(); } FMT_NORETURN void on_us_date() { report_no_date(); }
void on_iso_date() { report_no_date(); } FMT_NORETURN void on_iso_date() { report_no_date(); }
void on_12_hour_time() {} void on_12_hour_time() {}
void on_24_hour_time() {} void on_24_hour_time() {}
void on_iso_time() {} void on_iso_time() {}
void on_am_pm() {} void on_am_pm() {}
void on_utc_offset() { report_no_date(); } void on_duration_value() {}
void on_tz_name() { report_no_date(); } void on_duration_unit() {}
FMT_NORETURN void on_utc_offset() { report_no_date(); }
FMT_NORETURN void on_tz_name() { report_no_date(); }
}; };
template <typename Int> template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
inline int to_int(Int value) { inline bool isnan(T) {
FMT_ASSERT(value >= (std::numeric_limits<int>::min)() && return false;
value <= (std::numeric_limits<int>::max)(), "invalid value"); }
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); return static_cast<int>(value);
} }
template <typename FormatContext, typename OutputIt> 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 { struct chrono_formatter {
FormatContext &context; FormatContext& context;
OutputIt out; OutputIt out;
std::chrono::seconds s; int precision;
std::chrono::milliseconds ms; // 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;
typedef typename FormatContext::char_type char_type; using char_type = typename FormatContext::char_type;
explicit chrono_formatter(FormatContext &ctx, OutputIt o) explicit chrono_formatter(FormatContext& ctx, OutputIt o,
: context(ctx), out(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;
}
int hour() const { return to_int((s.count() / 3600) % 24); } // this may overflow and/or the result may not fit in the
// target type.
int hour12() const { #if FMT_SAFE_DURATION_CAST
auto hour = to_int((s.count() / 3600) % 12); // might need checked conversion (rep!=Rep)
return hour > 0 ? hour : 12; 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
} }
int minute() const { return to_int((s.count() / 60) % 60); } // returns true if nan or inf, writes to out.
int second() const { return to_int(s.count() % 60); } 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 { std::tm time() const {
auto time = std::tm(); auto time = std::tm();
time.tm_hour = hour(); time.tm_hour = to_nonnegative_int(hour(), 24);
time.tm_min = minute(); time.tm_min = to_nonnegative_int(minute(), 60);
time.tm_sec = second(); time.tm_sec = to_nonnegative_int(second(), 60);
return time; return time;
} }
void write(int value, int width) { void write_sign() {
typedef typename int_traits<int>::main_type main_type; if (negative) {
main_type n = to_unsigned(value); *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); int num_digits = internal::count_digits(n);
if (width > num_digits) if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
out = std::fill_n(out, width - num_digits, '0');
out = format_decimal<char_type>(out, n, num_digits); out = format_decimal<char_type>(out, n, num_digits);
} }
void format_localized(const tm &time, const char *format) { 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 locale = context.locale().template get<std::locale>();
auto &facet = std::use_facet<std::time_put<char_type>>(locale); auto& facet = std::use_facet<std::time_put<char_type>>(locale);
std::basic_ostringstream<char_type> os; std::basic_ostringstream<char_type> os;
os.imbue(locale); os.imbue(locale);
facet.put(os, os, ' ', &time, format, format + std::strlen(format)); facet.put(os, os, ' ', &time, format, format + std::strlen(format));
@@ -266,7 +604,7 @@ struct chrono_formatter {
std::copy(str.begin(), str.end(), out); std::copy(str.begin(), str.end(), out);
} }
void on_text(const char_type *begin, const char_type *end) { void on_text(const char_type* begin, const char_type* end) {
std::copy(begin, end, out); std::copy(begin, end, out);
} }
@@ -286,46 +624,70 @@ struct chrono_formatter {
void on_tz_name() {} void on_tz_name() {}
void on_24_hour(numeric_system ns) { void on_24_hour(numeric_system ns) {
if (ns == numeric_system::standard) if (handle_nan_inf()) return;
return write(hour(), 2);
if (ns == numeric_system::standard) return write(hour(), 2);
auto time = tm(); auto time = tm();
time.tm_hour = hour(); time.tm_hour = to_nonnegative_int(hour(), 24);
format_localized(time, "%OH"); format_localized(time, "%OH");
} }
void on_12_hour(numeric_system ns) { void on_12_hour(numeric_system ns) {
if (ns == numeric_system::standard) if (handle_nan_inf()) return;
return write(hour12(), 2);
if (ns == numeric_system::standard) return write(hour12(), 2);
auto time = tm(); auto time = tm();
time.tm_hour = hour(); time.tm_hour = to_nonnegative_int(hour12(), 12);
format_localized(time, "%OI"); format_localized(time, "%OI");
} }
void on_minute(numeric_system ns) { void on_minute(numeric_system ns) {
if (ns == numeric_system::standard) if (handle_nan_inf()) return;
return write(minute(), 2);
if (ns == numeric_system::standard) return write(minute(), 2);
auto time = tm(); auto time = tm();
time.tm_min = minute(); time.tm_min = to_nonnegative_int(minute(), 60);
format_localized(time, "%OM"); format_localized(time, "%OM");
} }
void on_second(numeric_system ns) { void on_second(numeric_system ns) {
if (handle_nan_inf()) return;
if (ns == numeric_system::standard) { if (ns == numeric_system::standard) {
write(second(), 2); 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)) { if (ms != std::chrono::milliseconds(0)) {
*out++ = '.'; *out++ = '.';
write(to_int(ms.count()), 3); write(ms.count(), 3);
} }
return; return;
} }
auto time = tm(); auto time = tm();
time.tm_sec = second(); time.tm_sec = to_nonnegative_int(second(), 60);
format_localized(time, "%OS"); format_localized(time, "%OS");
} }
void on_12_hour_time() { format_localized(time(), "%r"); } void on_12_hour_time() {
if (handle_nan_inf()) return;
format_localized(time(), "%r");
}
void on_24_hour_time() { void on_24_hour_time() {
if (handle_nan_inf()) {
*out++ = ':';
handle_nan_inf();
return;
}
write(hour(), 2); write(hour(), 2);
*out++ = ':'; *out++ = ':';
write(minute(), 2); write(minute(), 2);
@@ -334,115 +696,130 @@ struct chrono_formatter {
void on_iso_time() { void on_iso_time() {
on_24_hour_time(); on_24_hour_time();
*out++ = ':'; *out++ = ':';
if (handle_nan_inf()) return;
write(second(), 2); write(second(), 2);
} }
void on_am_pm() { format_localized(time(), "%p"); } 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 } // namespace internal
template <typename Period> FMT_CONSTEXPR const char *get_units() {
return FMT_NULL;
}
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";
}
template <typename Rep, typename Period, typename Char> template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> { struct formatter<std::chrono::duration<Rep, Period>, Char> {
private: private:
align_spec spec; basic_format_specs<Char> specs;
internal::arg_ref<Char> width_ref; 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; mutable basic_string_view<Char> format_str;
typedef std::chrono::duration<Rep, Period> duration; using duration = std::chrono::duration<Rep, Period>;
struct spec_handler { struct spec_handler {
formatter &f; formatter& f;
basic_parse_context<Char> &context; basic_parse_context<Char>& context;
basic_string_view<Char> format_str;
typedef internal::arg_ref<Char> arg_ref_type; template <typename Id> FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
template <typename Id>
FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
context.check_arg_id(arg_id); context.check_arg_id(arg_id);
return arg_ref_type(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) { FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
return arg_ref_type(context.next_arg_id()); return arg_ref_type(context.next_arg_id());
} }
void on_error(const char *msg) { throw format_error(msg); } void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
void on_fill(Char fill) { f.spec.fill_ = fill; } void on_fill(Char fill) { f.specs.fill[0] = fill; }
void on_align(alignment align) { f.spec.align_ = align; } void on_align(align_t align) { f.specs.align = align; }
void on_width(unsigned width) { f.spec.width_ = width; } void on_width(unsigned width) { f.specs.width = width; }
void on_precision(unsigned precision) { f.precision = precision; }
void end_precision() {}
template <typename Id> template <typename Id> void on_dynamic_width(Id arg_id) {
void on_dynamic_width(Id arg_id) {
f.width_ref = make_arg_ref(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);
}
}; };
public: using iterator = typename basic_parse_context<Char>::iterator;
formatter() : spec() {} struct parse_range {
iterator begin;
iterator end;
};
FMT_CONSTEXPR auto parse(basic_parse_context<Char> &ctx) FMT_CONSTEXPR parse_range do_parse(basic_parse_context<Char>& ctx) {
-> decltype(ctx.begin()) {
auto begin = ctx.begin(), end = ctx.end(); auto begin = ctx.begin(), end = ctx.end();
if (begin == end) return begin; if (begin == end || *begin == '}') return {begin, begin};
spec_handler handler{*this, ctx}; spec_handler handler{*this, ctx, format_str};
begin = internal::parse_align(begin, end, handler); begin = internal::parse_align(begin, end, handler);
if (begin == end) return begin; if (begin == end) return {begin, begin};
begin = internal::parse_width(begin, end, handler); 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()); end = parse_chrono_format(begin, end, internal::chrono_format_checker());
format_str = basic_string_view<Char>(&*begin, internal::to_unsigned(end - begin)); return {begin, end};
return 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> template <typename FormatContext>
auto format(const duration &d, FormatContext &ctx) auto format(const duration& d, FormatContext& ctx) -> decltype(ctx.out()) {
-> decltype(ctx.out()) {
auto begin = format_str.begin(), end = format_str.end(); auto begin = format_str.begin(), end = format_str.end();
memory_buffer buf; // As a possible future optimization, we could avoid extra copying if width
typedef output_range<decltype(ctx.out()), Char> range; // is not specified.
basic_writer<range> w(range(ctx.out())); 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 == '}') { if (begin == end || *begin == '}') {
if (const char *unit = get_units<Period>()) out = internal::format_chrono_duration_value(out, d.count(), precision);
format_to(buf, "{}{}", d.count(), unit); internal::format_chrono_duration_unit<Period>(out);
else if (Period::den == 1)
format_to(buf, "{}[{}]s", d.count(), Period::num);
else
format_to(buf, "{}[{}/{}]s", d.count(), Period::num, Period::den);
internal::handle_dynamic_spec<internal::width_checker>(
spec.width_, width_ref, ctx);
} else { } else {
auto out = std::back_inserter(buf); internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
internal::chrono_formatter<FormatContext, decltype(out)> f(ctx, out); ctx, out, d);
f.s = std::chrono::duration_cast<std::chrono::seconds>(d); f.precision = precision;
f.ms = std::chrono::duration_cast<std::chrono::milliseconds>(d - f.s);
parse_chrono_format(begin, end, f); parse_chrono_format(begin, end, f);
} }
w.write(buf.data(), buf.size(), spec); w.write(buf.data(), buf.size(), specs);
return w.out(); return w.out();
} }
}; };

View File

@@ -12,184 +12,149 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
#ifdef FMT_DEPRECATED_COLORS
// color and (v)print_colored are deprecated.
enum color { black, red, green, yellow, blue, magenta, cyan, white };
FMT_API void vprint_colored(color c, string_view format, format_args args);
FMT_API void vprint_colored(color c, wstring_view format, wformat_args args);
template <typename... Args>
inline void print_colored(color c, string_view format_str,
const Args & ... args) {
vprint_colored(c, format_str, make_format_args(args...));
}
template <typename... Args>
inline void print_colored(color c, wstring_view format_str,
const Args & ... args) {
vprint_colored(c, format_str, make_format_args<wformat_context>(args...));
}
inline void vprint_colored(color c, string_view format, format_args args) {
char escape[] = "\x1b[30m";
escape[3] = static_cast<char>('0' + c);
std::fputs(escape, stdout);
vprint(format, args);
std::fputs(internal::data::RESET_COLOR, stdout);
}
inline void vprint_colored(color c, wstring_view format, wformat_args args) {
wchar_t escape[] = L"\x1b[30m";
escape[3] = static_cast<wchar_t>('0' + c);
std::fputws(escape, stdout);
vprint(format, args);
std::fputws(internal::data::WRESET_COLOR, stdout);
}
#else
enum class color : uint32_t { enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255) alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215) antique_white = 0xFAEBD7, // rgb(250,235,215)
aqua = 0x00FFFF, // rgb(0,255,255) aqua = 0x00FFFF, // rgb(0,255,255)
aquamarine = 0x7FFFD4, // rgb(127,255,212) aquamarine = 0x7FFFD4, // rgb(127,255,212)
azure = 0xF0FFFF, // rgb(240,255,255) azure = 0xF0FFFF, // rgb(240,255,255)
beige = 0xF5F5DC, // rgb(245,245,220) beige = 0xF5F5DC, // rgb(245,245,220)
bisque = 0xFFE4C4, // rgb(255,228,196) bisque = 0xFFE4C4, // rgb(255,228,196)
black = 0x000000, // rgb(0,0,0) black = 0x000000, // rgb(0,0,0)
blanched_almond = 0xFFEBCD, // rgb(255,235,205) blanched_almond = 0xFFEBCD, // rgb(255,235,205)
blue = 0x0000FF, // rgb(0,0,255) blue = 0x0000FF, // rgb(0,0,255)
blue_violet = 0x8A2BE2, // rgb(138,43,226) blue_violet = 0x8A2BE2, // rgb(138,43,226)
brown = 0xA52A2A, // rgb(165,42,42) brown = 0xA52A2A, // rgb(165,42,42)
burly_wood = 0xDEB887, // rgb(222,184,135) burly_wood = 0xDEB887, // rgb(222,184,135)
cadet_blue = 0x5F9EA0, // rgb(95,158,160) cadet_blue = 0x5F9EA0, // rgb(95,158,160)
chartreuse = 0x7FFF00, // rgb(127,255,0) chartreuse = 0x7FFF00, // rgb(127,255,0)
chocolate = 0xD2691E, // rgb(210,105,30) chocolate = 0xD2691E, // rgb(210,105,30)
coral = 0xFF7F50, // rgb(255,127,80) coral = 0xFF7F50, // rgb(255,127,80)
cornflower_blue = 0x6495ED, // rgb(100,149,237) cornflower_blue = 0x6495ED, // rgb(100,149,237)
cornsilk = 0xFFF8DC, // rgb(255,248,220) cornsilk = 0xFFF8DC, // rgb(255,248,220)
crimson = 0xDC143C, // rgb(220,20,60) crimson = 0xDC143C, // rgb(220,20,60)
cyan = 0x00FFFF, // rgb(0,255,255) cyan = 0x00FFFF, // rgb(0,255,255)
dark_blue = 0x00008B, // rgb(0,0,139) dark_blue = 0x00008B, // rgb(0,0,139)
dark_cyan = 0x008B8B, // rgb(0,139,139) dark_cyan = 0x008B8B, // rgb(0,139,139)
dark_golden_rod = 0xB8860B, // rgb(184,134,11) dark_golden_rod = 0xB8860B, // rgb(184,134,11)
dark_gray = 0xA9A9A9, // rgb(169,169,169) dark_gray = 0xA9A9A9, // rgb(169,169,169)
dark_green = 0x006400, // rgb(0,100,0) dark_green = 0x006400, // rgb(0,100,0)
dark_khaki = 0xBDB76B, // rgb(189,183,107) dark_khaki = 0xBDB76B, // rgb(189,183,107)
dark_magenta = 0x8B008B, // rgb(139,0,139) dark_magenta = 0x8B008B, // rgb(139,0,139)
dark_olive_green = 0x556B2F, // rgb(85,107,47) dark_olive_green = 0x556B2F, // rgb(85,107,47)
dark_orange = 0xFF8C00, // rgb(255,140,0) dark_orange = 0xFF8C00, // rgb(255,140,0)
dark_orchid = 0x9932CC, // rgb(153,50,204) dark_orchid = 0x9932CC, // rgb(153,50,204)
dark_red = 0x8B0000, // rgb(139,0,0) dark_red = 0x8B0000, // rgb(139,0,0)
dark_salmon = 0xE9967A, // rgb(233,150,122) dark_salmon = 0xE9967A, // rgb(233,150,122)
dark_sea_green = 0x8FBC8F, // rgb(143,188,143) dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
dark_slate_blue = 0x483D8B, // rgb(72,61,139) dark_slate_blue = 0x483D8B, // rgb(72,61,139)
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79) dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
dark_turquoise = 0x00CED1, // rgb(0,206,209) dark_turquoise = 0x00CED1, // rgb(0,206,209)
dark_violet = 0x9400D3, // rgb(148,0,211) dark_violet = 0x9400D3, // rgb(148,0,211)
deep_pink = 0xFF1493, // rgb(255,20,147) deep_pink = 0xFF1493, // rgb(255,20,147)
deep_sky_blue = 0x00BFFF, // rgb(0,191,255) deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
dim_gray = 0x696969, // rgb(105,105,105) dim_gray = 0x696969, // rgb(105,105,105)
dodger_blue = 0x1E90FF, // rgb(30,144,255) dodger_blue = 0x1E90FF, // rgb(30,144,255)
fire_brick = 0xB22222, // rgb(178,34,34) fire_brick = 0xB22222, // rgb(178,34,34)
floral_white = 0xFFFAF0, // rgb(255,250,240) floral_white = 0xFFFAF0, // rgb(255,250,240)
forest_green = 0x228B22, // rgb(34,139,34) forest_green = 0x228B22, // rgb(34,139,34)
fuchsia = 0xFF00FF, // rgb(255,0,255) fuchsia = 0xFF00FF, // rgb(255,0,255)
gainsboro = 0xDCDCDC, // rgb(220,220,220) gainsboro = 0xDCDCDC, // rgb(220,220,220)
ghost_white = 0xF8F8FF, // rgb(248,248,255) ghost_white = 0xF8F8FF, // rgb(248,248,255)
gold = 0xFFD700, // rgb(255,215,0) gold = 0xFFD700, // rgb(255,215,0)
golden_rod = 0xDAA520, // rgb(218,165,32) golden_rod = 0xDAA520, // rgb(218,165,32)
gray = 0x808080, // rgb(128,128,128) gray = 0x808080, // rgb(128,128,128)
green = 0x008000, // rgb(0,128,0) green = 0x008000, // rgb(0,128,0)
green_yellow = 0xADFF2F, // rgb(173,255,47) green_yellow = 0xADFF2F, // rgb(173,255,47)
honey_dew = 0xF0FFF0, // rgb(240,255,240) honey_dew = 0xF0FFF0, // rgb(240,255,240)
hot_pink = 0xFF69B4, // rgb(255,105,180) hot_pink = 0xFF69B4, // rgb(255,105,180)
indian_red = 0xCD5C5C, // rgb(205,92,92) indian_red = 0xCD5C5C, // rgb(205,92,92)
indigo = 0x4B0082, // rgb(75,0,130) indigo = 0x4B0082, // rgb(75,0,130)
ivory = 0xFFFFF0, // rgb(255,255,240) ivory = 0xFFFFF0, // rgb(255,255,240)
khaki = 0xF0E68C, // rgb(240,230,140) khaki = 0xF0E68C, // rgb(240,230,140)
lavender = 0xE6E6FA, // rgb(230,230,250) lavender = 0xE6E6FA, // rgb(230,230,250)
lavender_blush = 0xFFF0F5, // rgb(255,240,245) lavender_blush = 0xFFF0F5, // rgb(255,240,245)
lawn_green = 0x7CFC00, // rgb(124,252,0) lawn_green = 0x7CFC00, // rgb(124,252,0)
lemon_chiffon = 0xFFFACD, // rgb(255,250,205) lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
light_blue = 0xADD8E6, // rgb(173,216,230) light_blue = 0xADD8E6, // rgb(173,216,230)
light_coral = 0xF08080, // rgb(240,128,128) light_coral = 0xF08080, // rgb(240,128,128)
light_cyan = 0xE0FFFF, // rgb(224,255,255) light_cyan = 0xE0FFFF, // rgb(224,255,255)
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210) light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
light_gray = 0xD3D3D3, // rgb(211,211,211) light_gray = 0xD3D3D3, // rgb(211,211,211)
light_green = 0x90EE90, // rgb(144,238,144) light_green = 0x90EE90, // rgb(144,238,144)
light_pink = 0xFFB6C1, // rgb(255,182,193) light_pink = 0xFFB6C1, // rgb(255,182,193)
light_salmon = 0xFFA07A, // rgb(255,160,122) light_salmon = 0xFFA07A, // rgb(255,160,122)
light_sea_green = 0x20B2AA, // rgb(32,178,170) light_sea_green = 0x20B2AA, // rgb(32,178,170)
light_sky_blue = 0x87CEFA, // rgb(135,206,250) light_sky_blue = 0x87CEFA, // rgb(135,206,250)
light_slate_gray = 0x778899, // rgb(119,136,153) light_slate_gray = 0x778899, // rgb(119,136,153)
light_steel_blue = 0xB0C4DE, // rgb(176,196,222) light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
light_yellow = 0xFFFFE0, // rgb(255,255,224) light_yellow = 0xFFFFE0, // rgb(255,255,224)
lime = 0x00FF00, // rgb(0,255,0) lime = 0x00FF00, // rgb(0,255,0)
lime_green = 0x32CD32, // rgb(50,205,50) lime_green = 0x32CD32, // rgb(50,205,50)
linen = 0xFAF0E6, // rgb(250,240,230) linen = 0xFAF0E6, // rgb(250,240,230)
magenta = 0xFF00FF, // rgb(255,0,255) magenta = 0xFF00FF, // rgb(255,0,255)
maroon = 0x800000, // rgb(128,0,0) maroon = 0x800000, // rgb(128,0,0)
medium_aquamarine = 0x66CDAA, // rgb(102,205,170) medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
medium_blue = 0x0000CD, // rgb(0,0,205) medium_blue = 0x0000CD, // rgb(0,0,205)
medium_orchid = 0xBA55D3, // rgb(186,85,211) medium_orchid = 0xBA55D3, // rgb(186,85,211)
medium_purple = 0x9370DB, // rgb(147,112,219) medium_purple = 0x9370DB, // rgb(147,112,219)
medium_sea_green = 0x3CB371, // rgb(60,179,113) medium_sea_green = 0x3CB371, // rgb(60,179,113)
medium_slate_blue = 0x7B68EE, // rgb(123,104,238) medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
medium_spring_green = 0x00FA9A, // rgb(0,250,154) medium_spring_green = 0x00FA9A, // rgb(0,250,154)
medium_turquoise = 0x48D1CC, // rgb(72,209,204) medium_turquoise = 0x48D1CC, // rgb(72,209,204)
medium_violet_red = 0xC71585, // rgb(199,21,133) medium_violet_red = 0xC71585, // rgb(199,21,133)
midnight_blue = 0x191970, // rgb(25,25,112) midnight_blue = 0x191970, // rgb(25,25,112)
mint_cream = 0xF5FFFA, // rgb(245,255,250) mint_cream = 0xF5FFFA, // rgb(245,255,250)
misty_rose = 0xFFE4E1, // rgb(255,228,225) misty_rose = 0xFFE4E1, // rgb(255,228,225)
moccasin = 0xFFE4B5, // rgb(255,228,181) moccasin = 0xFFE4B5, // rgb(255,228,181)
navajo_white = 0xFFDEAD, // rgb(255,222,173) navajo_white = 0xFFDEAD, // rgb(255,222,173)
navy = 0x000080, // rgb(0,0,128) navy = 0x000080, // rgb(0,0,128)
old_lace = 0xFDF5E6, // rgb(253,245,230) old_lace = 0xFDF5E6, // rgb(253,245,230)
olive = 0x808000, // rgb(128,128,0) olive = 0x808000, // rgb(128,128,0)
olive_drab = 0x6B8E23, // rgb(107,142,35) olive_drab = 0x6B8E23, // rgb(107,142,35)
orange = 0xFFA500, // rgb(255,165,0) orange = 0xFFA500, // rgb(255,165,0)
orange_red = 0xFF4500, // rgb(255,69,0) orange_red = 0xFF4500, // rgb(255,69,0)
orchid = 0xDA70D6, // rgb(218,112,214) orchid = 0xDA70D6, // rgb(218,112,214)
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170) pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
pale_green = 0x98FB98, // rgb(152,251,152) pale_green = 0x98FB98, // rgb(152,251,152)
pale_turquoise = 0xAFEEEE, // rgb(175,238,238) pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
pale_violet_red = 0xDB7093, // rgb(219,112,147) pale_violet_red = 0xDB7093, // rgb(219,112,147)
papaya_whip = 0xFFEFD5, // rgb(255,239,213) papaya_whip = 0xFFEFD5, // rgb(255,239,213)
peach_puff = 0xFFDAB9, // rgb(255,218,185) peach_puff = 0xFFDAB9, // rgb(255,218,185)
peru = 0xCD853F, // rgb(205,133,63) peru = 0xCD853F, // rgb(205,133,63)
pink = 0xFFC0CB, // rgb(255,192,203) pink = 0xFFC0CB, // rgb(255,192,203)
plum = 0xDDA0DD, // rgb(221,160,221) plum = 0xDDA0DD, // rgb(221,160,221)
powder_blue = 0xB0E0E6, // rgb(176,224,230) powder_blue = 0xB0E0E6, // rgb(176,224,230)
purple = 0x800080, // rgb(128,0,128) purple = 0x800080, // rgb(128,0,128)
rebecca_purple = 0x663399, // rgb(102,51,153) rebecca_purple = 0x663399, // rgb(102,51,153)
red = 0xFF0000, // rgb(255,0,0) red = 0xFF0000, // rgb(255,0,0)
rosy_brown = 0xBC8F8F, // rgb(188,143,143) rosy_brown = 0xBC8F8F, // rgb(188,143,143)
royal_blue = 0x4169E1, // rgb(65,105,225) royal_blue = 0x4169E1, // rgb(65,105,225)
saddle_brown = 0x8B4513, // rgb(139,69,19) saddle_brown = 0x8B4513, // rgb(139,69,19)
salmon = 0xFA8072, // rgb(250,128,114) salmon = 0xFA8072, // rgb(250,128,114)
sandy_brown = 0xF4A460, // rgb(244,164,96) sandy_brown = 0xF4A460, // rgb(244,164,96)
sea_green = 0x2E8B57, // rgb(46,139,87) sea_green = 0x2E8B57, // rgb(46,139,87)
sea_shell = 0xFFF5EE, // rgb(255,245,238) sea_shell = 0xFFF5EE, // rgb(255,245,238)
sienna = 0xA0522D, // rgb(160,82,45) sienna = 0xA0522D, // rgb(160,82,45)
silver = 0xC0C0C0, // rgb(192,192,192) silver = 0xC0C0C0, // rgb(192,192,192)
sky_blue = 0x87CEEB, // rgb(135,206,235) sky_blue = 0x87CEEB, // rgb(135,206,235)
slate_blue = 0x6A5ACD, // rgb(106,90,205) slate_blue = 0x6A5ACD, // rgb(106,90,205)
slate_gray = 0x708090, // rgb(112,128,144) slate_gray = 0x708090, // rgb(112,128,144)
snow = 0xFFFAFA, // rgb(255,250,250) snow = 0xFFFAFA, // rgb(255,250,250)
spring_green = 0x00FF7F, // rgb(0,255,127) spring_green = 0x00FF7F, // rgb(0,255,127)
steel_blue = 0x4682B4, // rgb(70,130,180) steel_blue = 0x4682B4, // rgb(70,130,180)
tan = 0xD2B48C, // rgb(210,180,140) tan = 0xD2B48C, // rgb(210,180,140)
teal = 0x008080, // rgb(0,128,128) teal = 0x008080, // rgb(0,128,128)
thistle = 0xD8BFD8, // rgb(216,191,216) thistle = 0xD8BFD8, // rgb(216,191,216)
tomato = 0xFF6347, // rgb(255,99,71) tomato = 0xFF6347, // rgb(255,99,71)
turquoise = 0x40E0D0, // rgb(64,224,208) turquoise = 0x40E0D0, // rgb(64,224,208)
violet = 0xEE82EE, // rgb(238,130,238) violet = 0xEE82EE, // rgb(238,130,238)
wheat = 0xF5DEB3, // rgb(245,222,179) wheat = 0xF5DEB3, // rgb(245,222,179)
white = 0xFFFFFF, // rgb(255,255,255) white = 0xFFFFFF, // rgb(255,255,255)
white_smoke = 0xF5F5F5, // rgb(245,245,245) white_smoke = 0xF5F5F5, // rgb(245,245,245)
yellow = 0xFFFF00, // rgb(255,255,0) yellow = 0xFFFF00, // rgb(255,255,0)
yellow_green = 0x9ACD32 // rgb(154,205,50) yellow_green = 0x9ACD32 // rgb(154,205,50)
}; // enum class color }; // enum class color
enum class terminal_color : uint8_t { enum class terminal_color : uint8_t {
black = 30, black = 30,
@@ -208,27 +173,26 @@ enum class terminal_color : uint8_t {
bright_magenta, bright_magenta,
bright_cyan, bright_cyan,
bright_white bright_white
}; // enum class terminal_color };
enum class emphasis : uint8_t { enum class emphasis : uint8_t {
bold = 1, bold = 1,
italic = 1 << 1, italic = 1 << 1,
underline = 1 << 2, underline = 1 << 2,
strikethrough = 1 << 3 strikethrough = 1 << 3
}; // enum class emphasis };
// rgb is a struct for red, green and blue colors. // 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 // Using the name "rgb" makes some editors show the color in a tooltip.
// editor.
struct rgb { struct rgb {
FMT_CONSTEXPR_DECL rgb() : r(0), g(0), b(0) {} FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_) FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
: r(r_), g(g_), b(b_) {} FMT_CONSTEXPR rgb(uint32_t hex)
FMT_CONSTEXPR_DECL rgb(uint32_t hex) : r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b((hex) & 0xFF) {} FMT_CONSTEXPR rgb(color hex)
FMT_CONSTEXPR_DECL rgb(color hex) : r((uint32_t(hex) >> 16) & 0xFF),
: r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF),
b(uint32_t(hex) & 0xFF) {} b(uint32_t(hex) & 0xFF) {}
uint8_t r; uint8_t r;
uint8_t g; uint8_t g;
uint8_t b; uint8_t b;
@@ -238,19 +202,17 @@ namespace internal {
// color is a struct of either a rgb color or a terminal color. // color is a struct of either a rgb color or a terminal color.
struct color_type { struct color_type {
FMT_CONSTEXPR color_type() FMT_NOEXCEPT FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
: is_rgb(), value{} {} FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT value{} {
: is_rgb(true), value{} {
value.rgb_color = static_cast<uint32_t>(rgb_color); value.rgb_color = static_cast<uint32_t>(rgb_color);
} }
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} {
: is_rgb(true), value{} { value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
| (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
} }
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
: is_rgb(), value{} { value{} {
value.term_color = static_cast<uint8_t>(term_color); value.term_color = static_cast<uint8_t>(term_color);
} }
bool is_rgb; bool is_rgb;
@@ -259,21 +221,23 @@ struct color_type {
uint32_t rgb_color; uint32_t rgb_color;
} value; } value;
}; };
} // namespace internal } // namespace internal
// Experimental text formatting support. // Experimental text formatting support.
class text_style { class text_style {
public: public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
: set_foreground_color(), set_background_color(), ems(em) {} : set_foreground_color(),
set_background_color(),
ems(em) {}
FMT_CONSTEXPR text_style &operator|=(const text_style &rhs) { FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
if (!set_foreground_color) { if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color; set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color; foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) { } else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
throw format_error("can't OR a terminal color"); FMT_THROW(format_error("can't OR a terminal color"));
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color; foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
} }
@@ -282,7 +246,7 @@ class text_style {
background_color = rhs.background_color; background_color = rhs.background_color;
} else if (rhs.set_background_color) { } else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb) if (!background_color.is_rgb || !rhs.background_color.is_rgb)
throw format_error("can't OR a terminal color"); FMT_THROW(format_error("can't OR a terminal color"));
background_color.value.rgb_color |= rhs.background_color.value.rgb_color; background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
} }
@@ -291,18 +255,18 @@ class text_style {
return *this; return *this;
} }
friend FMT_CONSTEXPR friend FMT_CONSTEXPR text_style operator|(text_style lhs,
text_style operator|(text_style lhs, const text_style &rhs) { const text_style& rhs) {
return lhs |= rhs; return lhs |= rhs;
} }
FMT_CONSTEXPR text_style &operator&=(const text_style &rhs) { FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) {
if (!set_foreground_color) { if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color; set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color; foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) { } else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb) if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
throw format_error("can't AND a terminal color"); FMT_THROW(format_error("can't AND a terminal color"));
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color; foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
} }
@@ -311,7 +275,7 @@ class text_style {
background_color = rhs.background_color; background_color = rhs.background_color;
} else if (rhs.set_background_color) { } else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb) if (!background_color.is_rgb || !rhs.background_color.is_rgb)
throw format_error("can't AND a terminal color"); FMT_THROW(format_error("can't AND a terminal color"));
background_color.value.rgb_color &= rhs.background_color.value.rgb_color; background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
} }
@@ -320,8 +284,8 @@ class text_style {
return *this; return *this;
} }
friend FMT_CONSTEXPR friend FMT_CONSTEXPR text_style operator&(text_style lhs,
text_style operator&(text_style lhs, const text_style &rhs) { const text_style& rhs) {
return lhs &= rhs; return lhs &= rhs;
} }
@@ -347,20 +311,20 @@ class text_style {
return ems; return ems;
} }
private: private:
FMT_CONSTEXPR text_style(bool is_foreground, FMT_CONSTEXPR text_style(bool is_foreground,
internal::color_type text_color) FMT_NOEXCEPT internal::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(), : set_foreground_color(),
set_background_color(), set_background_color(),
ems() { ems() {
if (is_foreground) { if (is_foreground) {
foreground_color = text_color; foreground_color = text_color;
set_foreground_color = true; set_foreground_color = true;
} else { } else {
background_color = text_color; background_color = text_color;
set_background_color = true; set_background_color = true;
} }
} }
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground) friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
FMT_NOEXCEPT; FMT_NOEXCEPT;
@@ -388,19 +352,17 @@ FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
namespace internal { namespace internal {
template <typename Char> template <typename Char> struct ansi_color_escape {
struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color, FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
const char * esc) FMT_NOEXCEPT { const char* esc) FMT_NOEXCEPT {
// If we have a terminal color, we need to output another escape code // If we have a terminal color, we need to output another escape code
// sequence. // sequence.
if (!text_color.is_rgb) { if (!text_color.is_rgb) {
bool is_background = esc == internal::data::BACKGROUND_COLOR; bool is_background = esc == internal::data::background_color;
uint32_t value = text_color.value.term_color; uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with // Background ASCII codes are the same as the foreground ones but with
// 10 more. // 10 more.
if (is_background) if (is_background) value += 10u;
value += 10u;
std::size_t index = 0; std::size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b'); buffer[index++] = static_cast<Char>('\x1b');
@@ -422,7 +384,7 @@ struct ansi_color_escape {
buffer[i] = static_cast<Char>(esc[i]); buffer[i] = static_cast<Char>(esc[i]);
} }
rgb color(text_color.value.rgb_color); rgb color(text_color.value.rgb_color);
to_esc(color.r, buffer + 7, ';'); to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';'); to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm'); to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0); buffer[19] = static_cast<Char>(0);
@@ -430,19 +392,15 @@ struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
uint8_t em_codes[4] = {}; uint8_t em_codes[4] = {};
uint8_t em_bits = static_cast<uint8_t>(em); uint8_t em_bits = static_cast<uint8_t>(em);
if (em_bits & static_cast<uint8_t>(emphasis::bold)) if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
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::italic)) if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
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)) if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9; em_codes[3] = 9;
std::size_t index = 0; std::size_t index = 0;
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
if (!em_codes[i]) if (!em_codes[i]) continue;
continue;
buffer[index++] = static_cast<Char>('\x1b'); buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('['); buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]); buffer[index++] = static_cast<Char>('0' + em_codes[i]);
@@ -450,12 +408,17 @@ struct ansi_color_escape {
} }
buffer[index++] = static_cast<Char>(0); buffer[index++] = static_cast<Char>(0);
} }
FMT_CONSTEXPR operator const Char *() const FMT_NOEXCEPT { return buffer; } FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
private: 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]; Char buffer[7u + 3u * 4u + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char *out, static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) FMT_NOEXCEPT { char delimiter) FMT_NOEXCEPT {
out[0] = static_cast<Char>('0' + c / 100); out[0] = static_cast<Char>('0' + c / 100);
out[1] = static_cast<Char>('0' + c / 10 % 10); out[1] = static_cast<Char>('0' + c / 10 % 10);
@@ -465,67 +428,90 @@ private:
}; };
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
make_foreground_color(internal::color_type foreground) FMT_NOEXCEPT { internal::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, internal::data::FOREGROUND_COLOR); return ansi_color_escape<Char>(foreground, internal::data::foreground_color);
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
make_background_color(internal::color_type background) FMT_NOEXCEPT { internal::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, internal::data::BACKGROUND_COLOR); return ansi_color_escape<Char>(background, internal::data::background_color);
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
make_emphasis(emphasis em) FMT_NOEXCEPT {
return ansi_color_escape<Char>(em); return ansi_color_escape<Char>(em);
} }
template <typename Char> template <typename Char>
inline void fputs(const Char *chars, FILE *stream) FMT_NOEXCEPT { inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
std::fputs(chars, stream); std::fputs(chars, stream);
} }
template <> template <>
inline void fputs<wchar_t>(const wchar_t *chars, FILE *stream) FMT_NOEXCEPT { inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
std::fputws(chars, stream); 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> template <typename Char>
inline void reset_color(FILE *stream) FMT_NOEXCEPT { inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
fputs(internal::data::RESET_COLOR, stream); const char* begin = data::reset_color;
const char* end = begin + sizeof(data::reset_color) - 1;
buffer.append(begin, end);
} }
template <> template <typename Char>
inline void reset_color<wchar_t>(FILE *stream) FMT_NOEXCEPT { std::basic_string<Char> vformat(const text_style& ts,
fputs(internal::data::WRESET_COLOR, stream); basic_string_view<Char> format_str,
} basic_format_args<buffer_context<Char> > args) {
basic_memory_buffer<Char> buffer;
// The following specialiazation disables using std::FILE as a character type,
// which is needed because or else
// fmt::print(stderr, fmt::emphasis::bold, "");
// would take stderr (a std::FILE *) as the format string.
template <>
struct is_string<std::FILE *> : std::false_type {};
template <>
struct is_string<const std::FILE *> : std::false_type {};
} // namespace internal
template <
typename S, typename Char = typename internal::char_t<S>::type>
void vprint(std::FILE *f, const text_style &ts, const S &format,
basic_format_args<typename buffer_context<Char>::type> args) {
bool has_style = false; bool has_style = false;
if (ts.has_emphasis()) { if (ts.has_emphasis()) {
has_style = true; has_style = true;
internal::fputs<Char>( ansi_color_escape<Char> escape = make_emphasis<Char>(ts.get_emphasis());
internal::make_emphasis<Char>(ts.get_emphasis()), f); 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()) { if (ts.has_foreground()) {
has_style = true; has_style = true;
internal::fputs<Char>( internal::fputs<Char>(
internal::make_foreground_color<Char>(ts.get_foreground()), f); internal::make_foreground_color<Char>(ts.get_foreground()), f);
} }
if (ts.has_background()) { if (ts.has_background()) {
has_style = true; has_style = true;
@@ -545,15 +531,14 @@ void vprint(std::FILE *f, const text_style &ts, const S &format,
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23); "Elapsed time: {0:.2f} seconds", 1.23);
*/ */
template <typename String, typename... Args> template <typename S, typename... Args,
typename std::enable_if<internal::is_string<String>::value>::type print( FMT_ENABLE_IF(internal::is_string<S>::value)>
std::FILE *f, const text_style &ts, const String &format_str, void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args &... args) { const Args&... args) {
internal::check_format_string<Args...>(format_str); internal::check_format_string<Args...>(format_str);
typedef typename internal::char_t<String>::type char_t; using context = buffer_context<char_t<S> >;
typedef typename buffer_context<char_t>::type context_t; format_arg_store<context, Args...> as{args...};
format_arg_store<context_t, Args...> as{args...}; vprint(f, ts, format_str, basic_format_args<context>(as));
vprint(f, ts, format_str, basic_format_args<context_t>(as));
} }
/** /**
@@ -563,14 +548,37 @@ typename std::enable_if<internal::is_string<String>::value>::type print(
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23); "Elapsed time: {0:.2f} seconds", 1.23);
*/ */
template <typename String, typename... Args> template <typename S, typename... Args,
typename std::enable_if<internal::is_string<String>::value>::type print( FMT_ENABLE_IF(internal::is_string<S>::value)>
const text_style &ts, const String &format_str, void print(const text_style& ts, const S& format_str, const Args&... args) {
const Args &... args) {
return print(stdout, ts, format_str, args...); return print(stdout, ts, format_str, args...);
} }
#endif 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 FMT_END_NAMESPACE

View 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

View File

@@ -8,65 +8,65 @@
#ifndef FMT_LOCALE_H_ #ifndef FMT_LOCALE_H_
#define FMT_LOCALE_H_ #define FMT_LOCALE_H_
#include "format.h"
#include <locale> #include <locale>
#include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace internal {
template <typename Char> template <typename Char>
typename buffer_context<Char>::type::iterator vformat_to( typename buffer_context<Char>::iterator vformat_to(
const std::locale &loc, basic_buffer<Char> &buf, const std::locale& loc, buffer<Char>& buf,
basic_string_view<Char> format_str, basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) { basic_format_args<buffer_context<Char>> args) {
typedef back_insert_range<basic_buffer<Char> > range; using range = buffer_range<Char>;
return vformat_to<arg_formatter<range>>( return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
buf, to_string_view(format_str), args, internal::locale_ref(loc)); internal::locale_ref(loc));
} }
template <typename Char> template <typename Char>
std::basic_string<Char> vformat( std::basic_string<Char> vformat(const std::locale& loc,
const std::locale &loc, basic_string_view<Char> format_str, basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) { basic_format_args<buffer_context<Char>> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
internal::vformat_to(loc, buffer, format_str, args); internal::vformat_to(loc, buffer, format_str, args);
return fmt::to_string(buffer); return fmt::to_string(buffer);
} }
} } // namespace internal
template <typename S, typename Char = FMT_CHAR(S)> template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat( inline std::basic_string<Char> vformat(
const std::locale &loc, const S &format_str, const std::locale& loc, const S& format_str,
basic_format_args<typename buffer_context<Char>::type> args) { basic_format_args<buffer_context<Char>> args) {
return internal::vformat(loc, to_string_view(format_str), args); return internal::vformat(loc, to_string_view(format_str), args);
} }
template <typename S, typename... Args> template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<FMT_CHAR(S)> format( inline std::basic_string<Char> format(const std::locale& loc,
const std::locale &loc, const S &format_str, const Args &... args) { const S& format_str, Args&&... args) {
return internal::vformat( return internal::vformat(
loc, to_string_view(format_str), loc, to_string_view(format_str),
*internal::checked_args<S, Args...>(format_str, args...)); {internal::make_args_checked<Args...>(format_str, args...)});
} }
template <typename String, typename OutputIt, typename... Args> template <typename S, typename OutputIt, typename... Args,
inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value, typename Char = enable_if_t<
OutputIt>::type internal::is_output_iterator<OutputIt>::value, char_t<S>>>
vformat_to(OutputIt out, const std::locale &loc, const String &format_str, inline OutputIt vformat_to(OutputIt out, const std::locale& loc,
typename format_args_t<OutputIt, FMT_CHAR(String)>::type args) { const S& format_str,
typedef output_range<OutputIt, FMT_CHAR(String)> range; format_args_t<OutputIt, Char> args) {
using range = internal::output_range<OutputIt, Char>;
return vformat_to<arg_formatter<range>>( return vformat_to<arg_formatter<range>>(
range(out), to_string_view(format_str), args, internal::locale_ref(loc)); range(out), to_string_view(format_str), args, internal::locale_ref(loc));
} }
template <typename OutputIt, typename S, typename... Args> template <typename OutputIt, typename S, typename... Args,
inline typename std::enable_if< FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
internal::is_string<S>::value && internal::is_string<S>::value)>
internal::is_output_iterator<OutputIt>::value, OutputIt>::type inline OutputIt format_to(OutputIt out, const std::locale& loc,
format_to(OutputIt out, const std::locale &loc, const S &format_str, const S& format_str, Args&&... args) {
const Args &... args) {
internal::check_format_string<Args...>(format_str); internal::check_format_string<Args...>(format_str);
typedef typename format_context_t<OutputIt, FMT_CHAR(S)>::type context; using context = format_context_t<OutputIt, char_t<S>>;
format_arg_store<context, Args...> as{args...}; format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str), return vformat_to(out, loc, to_string_view(format_str),
basic_format_args<context>(as)); basic_format_args<context>(as));

View File

@@ -8,22 +8,21 @@
#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: private:
typedef typename std::basic_streambuf<Char>::int_type int_type; using int_type = typename std::basic_streambuf<Char>::int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type; using traits_type = typename std::basic_streambuf<Char>::traits_type;
basic_buffer<Char> &buffer_; buffer<Char>& buffer_;
public: public:
formatbuf(basic_buffer<Char> &buffer) : buffer_(buffer) {} formatbuf(buffer<Char>& buf) : buffer_(buf) {}
protected: protected:
// The put-area is actually always empty. This makes the implementation // The put-area is actually always empty. This makes the implementation
@@ -39,33 +38,32 @@ class formatbuf : public std::basic_streambuf<Char> {
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: private:
struct null; struct null;
// Hide all operator<< from std::basic_ostream<Char>. // Hide all operator<< from std::basic_ostream<Char>.
void operator<<(null); void operator<<(null);
}; };
// Checks if T has a user-defined operator<< (e.g. not a member of std::ostream). // Checks if T has a user-defined operator<< (e.g. not a member of
template <typename T, typename Char> // std::ostream).
class is_streamable { template <typename T, typename Char> class is_streamable {
private: private:
template <typename U> template <typename U>
static decltype( static decltype((void)(std::declval<test_stream<Char>&>()
internal::declval<test_stream<Char>&>() << std::declval<U>()),
<< 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:
static const bool value = result::value; static const bool value = result::value;
@@ -73,65 +71,51 @@ class is_streamable {
// 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 *data = buf.data(); const Char* buf_data = buf.data();
typedef std::make_unsigned<std::streamsize>::type UnsignedStreamSize; using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
UnsignedStreamSize size = buf.size(); unsigned_streamsize size = buf.size();
UnsignedStreamSize max_size = unsigned_streamsize max_size =
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)()); to_unsigned((std::numeric_limits<std::streamsize>::max)());
do { do {
UnsignedStreamSize n = size <= max_size ? size : max_size; unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(data, static_cast<std::streamsize>(n)); os.write(buf_data, static_cast<std::streamsize>(n));
data += n; buf_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) {
internal::formatbuf<Char> format_buf(buffer); formatbuf<Char> format_buf(buf);
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;
buffer.resize(buffer.size()); buf.resize(buf.size());
} }
} // namespace internal
// 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>
struct convert_to_int<T, Char, void> {
static const bool value =
convert_to_int<T, Char, int>::value &&
!internal::is_streamable<T, Char>::value;
};
// 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, struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
typename std::enable_if<
internal::is_streamable<T, Char>::value &&
!internal::format_type<
typename buffer_context<Char>::type, T>::value>::type>
: formatter<basic_string_view<Char>, Char> { : 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; basic_memory_buffer<Char> buffer;
internal::format_value(buffer, value); format_value(buffer, value);
basic_string_view<Char> str(buffer.data(), buffer.size()); basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx); return formatter<basic_string_view<Char>, Char>::format(str, ctx);
} }
}; };
} // namespace internal
template <typename Char> template <typename Char>
inline void vprint(std::basic_ostream<Char> &os, void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_string_view<Char> format_str, basic_format_args<buffer_context<Char>> args) {
basic_format_args<typename buffer_context<Char>::type> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args); internal::vformat_to(buffer, format_str, args);
internal::write(os, buffer); internal::write(os, buffer);
} }
/** /**
\rst \rst
Prints formatted data to the stream *os*. Prints formatted data to the stream *os*.
@@ -141,12 +125,11 @@ inline void vprint(std::basic_ostream<Char> &os,
fmt::print(cerr, "Don't {}!", "panic"); fmt::print(cerr, "Don't {}!", "panic");
\endrst \endrst
*/ */
template <typename S, typename... Args> template <typename S, typename... Args,
inline typename std::enable_if<internal::is_string<S>::value>::type typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
print(std::basic_ostream<FMT_CHAR(S)> &os, const S &format_str, void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
const Args & ... args) { vprint(os, to_string_view(format_str),
internal::checked_args<S, Args...> ca(format_str, args...); {internal::make_args_checked<Args...>(format_str, args...)});
vprint(os, to_string_view(format_str), *ca);
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE

View File

@@ -10,7 +10,7 @@
#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>
@@ -22,42 +22,42 @@
#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)
@@ -69,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 |
@@ -89,28 +89,27 @@ 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: private:
const Char *data_; 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) : data_(s) {} basic_cstring_view(const Char* 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) : data_(s.c_str()) {} basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */ /** Returns the pointer to a C string. */
const Char *c_str() const { return data_; } const Char* c_str() const { 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 {
@@ -126,33 +125,32 @@ class error_code {
// 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) : file_(f) {} explicit buffered_file(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_NOEXCEPT; FMT_API ~buffered_file() FMT_NOEXCEPT;
private: private:
buffered_file(const buffered_file &) = delete; buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file &) = delete; void operator=(const buffered_file&) = delete;
public: public:
buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_) { buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = FMT_NULL; other.file_ = nullptr;
} }
buffered_file& operator=(buffered_file &&other) { buffered_file& operator=(buffered_file&& other) {
close(); close();
file_ = other.file_; file_ = other.file_;
other.file_ = FMT_NULL; other.file_ = nullptr;
return *this; return *this;
} }
@@ -163,18 +161,18 @@ class buffered_file {
FMT_API void close(); FMT_API void close();
// Returns the pointer to a FILE object representing this file. // Returns the pointer to a FILE object representing this file.
FILE *get() const FMT_NOEXCEPT { return file_; } FILE* get() const FMT_NOEXCEPT { return file_; }
// We place parentheses around fileno to workaround a bug in some versions // We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro. // of MinGW that define fileno as a macro.
FMT_API int (fileno)() const; FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) { void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args); fmt::vprint(file_, format_str, args);
} }
template <typename... Args> template <typename... Args>
inline void print(string_view format_str, const Args & ... args) { inline void print(string_view format_str, const Args&... args) {
vprint(format_str, make_format_args(args...)); vprint(format_str, make_format_args(args...));
} }
}; };
@@ -195,9 +193,9 @@ class file {
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.
@@ -207,15 +205,13 @@ class file {
FMT_API file(cstring_view path, int oflag); FMT_API file(cstring_view path, int oflag);
private: private:
file(const file &) = delete; file(const file&) = delete;
void operator=(const file &) = delete; void operator=(const file&) = delete;
public: public:
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) { file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
other.fd_ = -1;
}
file& operator=(file &&other) { file& operator=(file&& other) {
close(); close();
fd_ = other.fd_; fd_ = other.fd_;
other.fd_ = -1; other.fd_ = -1;
@@ -236,10 +232,10 @@ class file {
FMT_API long long size() const; FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer. // Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void *buffer, std::size_t count); FMT_API std::size_t read(void* buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file. // 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); FMT_API std::size_t write(const void* buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns // Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object. // the duplicate as a file object.
@@ -251,68 +247,59 @@ class file {
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
FMT_API void dup2(int fd, error_code &ec) FMT_NOEXCEPT; 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 // Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively. // and writing respectively.
FMT_API static void pipe(file &read_end, file &write_end); FMT_API static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches // Creates a buffered_file object associated with this file and detaches
// this file object from the file. // this file object from the file.
FMT_API buffered_file fdopen(const char *mode); 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__) && \
!defined(__NEWLIB_H__)
# 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 _MSC_VER # ifdef _WIN32
typedef _locale_t locale_t; using locale_t = _locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC }; enum { 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_;
Locale(const Locale &) = delete; Locale(const Locale&) = delete;
void operator=(const Locale &) = delete; void operator=(const Locale&) = delete;
public: public:
typedef locale_t Type; using type = locale_t;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) { Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
if (!locale_) if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
FMT_THROW(system_error(errno, "cannot create locale"));
} }
~Locale() { freelocale(locale_); } ~Locale() { freelocale(locale_); }
Type get() const { return locale_; } type get() const { 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 = FMT_NULL; char* end = nullptr;
double result = strtod_l(str, &end, locale_); double result = strtod_l(str, &end, locale_);
str = end; str = end;
return result; return result;

File diff suppressed because it is too large Load Diff

View File

@@ -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,20 +12,19 @@
#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> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); return ctx.begin();
} }
}; };
@@ -33,7 +32,8 @@ struct formatting_base {
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 range. FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
// range.
Char prefix; Char prefix;
Char delimiter; Char delimiter;
Char postfix; Char postfix;
@@ -55,87 +55,78 @@ struct formatting_tuple : formatting_base<Char> {
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) {
const char *p_curr = str; while (*str) *out++ = *str++;
while (*p_curr) { return out;
*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> template <typename U>
static auto check(U *p) -> static auto check(U* p)
decltype(p->find('a'), p->length(), p->data(), int()); -> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
template <typename> template <typename> static void check(...);
static void check(...);
public: public:
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(FMT_NULL))>::value; is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value;
}; };
template <typename Char> template <typename Char>
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {}; struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
template <typename... Ts> template <typename... Ts> struct conditional_helper {};
struct conditional_helper {};
template <typename T, typename _ = void> template <typename T, typename _ = void> struct is_range_ : std::false_type {};
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< struct is_range_<
false, T, conditional_t<false,
conditional_helper<decltype(internal::declval<T>().begin()), conditional_helper<decltype(std::declval<T>().begin()),
decltype(internal::declval<T>().end())>, decltype(std::declval<T>().end())>,
void>::type> : std::true_type {}; 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> template <typename U>
static auto check(U *p) -> static auto check(U* p)
decltype(std::tuple_size<U>::value, -> 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 = static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(FMT_NULL))>::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>
@@ -151,7 +142,7 @@ 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)...};
@@ -159,26 +150,25 @@ void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT {
} }
template <class T> template <class T>
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
get_indexes(T const &) { return {}; } T const&) {
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); const auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f)); 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(bool add_space, const Arg&, typename std::decay<Arg>::type>::value)>
typename std::enable_if< FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
!is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
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(bool add_space, const Arg&, typename std::decay<Arg>::type>::value)>
typename std::enable_if< FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
return add_space ? " \"{}\"" : "\"{}\""; return add_space ? " \"{}\"" : "\"{}\"";
} }
@@ -186,61 +176,58 @@ 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 = static FMT_CONSTEXPR_DECL const bool value =
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::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, struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
typename std::enable_if<fmt::is_tuple_like<TupleT>::value>::type> { private:
private:
// C++11 generic lambda for format() // C++11 generic lambda for format()
template <typename FormatContext> template <typename FormatContext> struct format_each {
struct format_each { template <typename T> void operator()(const T& v) {
template <typename T>
void operator()(const T& v) {
if (i > 0) { if (i > 0) {
if (formatting.add_prepostfix_space) { if (formatting.add_prepostfix_space) {
*out++ = ' '; *out++ = ' ';
} }
internal::copy(formatting.delimiter, out); out = internal::copy(formatting.delimiter, out);
} }
format_to(out, out = format_to(out,
internal::format_str_quoted( internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v), (formatting.add_delimiter_spaces && i > 0), v),
v); v);
++i; ++i;
} }
formatting_tuple<Char>& formatting; formatting_tuple<Char>& formatting;
std::size_t& i; std::size_t& i;
typename std::add_lvalue_reference<decltype(std::declval<FormatContext>().out())>::type out; typename std::add_lvalue_reference<decltype(
std::declval<FormatContext>().out())>::type out;
}; };
public: public:
formatting_tuple<Char> formatting; formatting_tuple<Char> formatting;
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx); return formatting.parse(ctx);
} }
template <typename FormatContext = format_context> template <typename FormatContext = format_context>
auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out()) { auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out(); auto out = ctx.out();
std::size_t i = 0; std::size_t i = 0;
internal::copy(formatting.prefix, out); internal::copy(formatting.prefix, out);
@@ -255,54 +242,47 @@ public:
} }
}; };
template <typename T> template <typename T, typename Char> struct is_range {
struct is_range {
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
internal::is_range_<T>::value && !internal::is_like_std_string<T>::value; internal::is_range_<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, struct formatter<RangeT, Char,
typename std::enable_if<fmt::is_range<RangeT>::value>::type> { enable_if_t<fmt::is_range<RangeT, Char>::value>> {
formatting_range<Char> formatting; formatting_range<Char> formatting;
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx); return formatting.parse(ctx);
} }
template <typename FormatContext> template <typename FormatContext>
typename FormatContext::iterator format( typename FormatContext::iterator format(const RangeT& values,
const RangeT &values, FormatContext &ctx) { FormatContext& ctx) {
auto out = ctx.out(); auto out = internal::copy(formatting.prefix, ctx.out());
internal::copy(formatting.prefix, out);
std::size_t i = 0; std::size_t i = 0;
for (auto it = values.begin(), end = values.end(); it != end; ++it) { for (auto it = values.begin(), end = values.end(); it != end; ++it) {
if (i > 0) { if (i > 0) {
if (formatting.add_prepostfix_space) { if (formatting.add_prepostfix_space) *out++ = ' ';
*out++ = ' '; out = internal::copy(formatting.delimiter, out);
}
internal::copy(formatting.delimiter, out);
} }
format_to(out, out = format_to(out,
internal::format_str_quoted( internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it), (formatting.add_delimiter_spaces && i > 0), *it),
*it); *it);
if (++i > formatting.range_length_limit) { if (++i > formatting.range_length_limit) {
format_to(out, " ... <other elements>"); out = format_to(out, " ... <other elements>");
break; break;
} }
} }
if (formatting.add_prepostfix_space) { if (formatting.add_prepostfix_space) *out++ = ' ';
*out++ = ' '; return internal::copy(formatting.postfix, out);
}
internal::copy(formatting.postfix, out);
return ctx.out();
} }
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_RANGES_H_ #endif // FMT_RANGES_H_

View 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

View File

@@ -1,160 +0,0 @@
// Formatting library for C++ - time formatting
//
// Copyright (c) 2012 - present, 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>
#include <locale>
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 != FMT_NULL; }
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 != FMT_NULL;
}
#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 != FMT_NULL; }
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 != FMT_NULL;
}
#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);
}
}
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;
};
FMT_END_NAMESPACE
#endif // FMT_TIME_H_

View File

@@ -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

View File

@@ -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
View 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

View File

@@ -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,137 +13,333 @@
// 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, It begin, 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(source_loc loc, level::level_enum lvl, const char *fmt, const Args &... args); void log(level::level_enum lvl, string_view_t fmt, const Args &... args)
{
void log(level::level_enum lvl, const char *msg); log(source_loc{}, lvl, fmt, args...);
}
void log(source_loc loc, level::level_enum lvl, const char *msg);
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
#ifndef _WIN32 #ifndef _WIN32
#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows #error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
#else #else
template<typename... Args>
void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args);
template<typename... Args> template<typename... Args>
void log(source_loc source, level::level_enum lvl, 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 trace(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 debug(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 info(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 warn(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 error(const wchar_t *fmt, const Args &... args); void warn(wstring_view_t fmt, const Args &... args)
{
log(level::warn, fmt, args...);
}
template<typename... Args> template<typename... Args>
void critical(const wchar_t *fmt, const Args &... 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 // _WIN32
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT #endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
// T can be statically converted to string_view
template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
void log(level::level_enum lvl, const T &);
// T can be statically converted to string_view
template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
void log(source_loc loc, level::level_enum lvl, const T &);
// T cannot be statically converted to string_view
template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
void log(level::level_enum lvl, const T &);
// T cannot be statically converted to string_view
template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
void log(source_loc loc, 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);
static level::level_enum default_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 // flush functions
void flush(); void flush();
void flush_on(level::level_enum log_level); void flush_on(level::level_enum log_level);
@@ -153,36 +347,37 @@ public:
// sinks // 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();
// error handler // error handler
void set_error_handler(log_err_handler err_handler); void set_error_handler(err_handler);
log_err_handler error_handler() const;
// create new logger with same sinks and configuration. // create new logger with same sinks and configuration.
virtual std::shared_ptr<logger> clone(std::string logger_name); 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. // handle errors during logging.
// print the error to stderr with the max rate of 1 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::logger::default_level()};
spdlog::level_t flush_level_{level::off};
log_err_handler err_handler_{[this](const std::string &msg) { this->default_err_handler_(msg); }};
std::atomic<time_t> last_err_time_{0};
std::atomic<size_t> msg_counter_{1};
}; };
void swap(logger &a, logger &b);
} // namespace spdlog } // namespace spdlog
#include "details/logger_impl.h" #ifdef SPDLOG_HEADER_ONLY
#include "logger-inl.h"
#endif

View File

@@ -1,18 +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
#ifndef SPDLOG_H #ifdef __ANDROID__
#error "spdlog.h must be included before this file."
#endif
#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>
@@ -37,21 +34,20 @@ public:
explicit android_sink(std::string tag = "spdlog", bool use_raw_msg = false) explicit android_sink(std::string tag = "spdlog", bool use_raw_msg = false)
: tag_(std::move(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_)
{ {
details::fmt_helper::append_string_view(msg.payload, formatted); details::fmt_helper::append_string_view(msg.payload, formatted);
} }
else else
{ {
sink::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();
@@ -68,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));
} }
} }
@@ -106,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__

View 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

View File

@@ -1,19 +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
#ifndef SPDLOG_H
#error "spdlog.h must be included before this file."
#endif
#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 "spdlog/sinks/sink.h"
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <string> #include <string>
@@ -28,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>
class ansicolor_sink final : public sink template<typename ConsoleMutex>
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) 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() override
{
std::lock_guard<mutex_t> lock(mutex_);
fflush(target_file_);
}
void set_pattern(const std::string &pattern) 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
{
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

View 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);
}

View File

@@ -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,57 +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() = default; base_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) final 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() final
{
std::lock_guard<Mutex> lock(mutex_);
flush_();
}
void set_pattern(const std::string &pattern) final
{
std::lock_guard<Mutex> lock(mutex_);
set_pattern_(pattern);
}
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) final
{
std::lock_guard<Mutex> lock(mutex_);
set_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;
virtual void set_pattern_(const std::string &pattern);
virtual void set_pattern_(const std::string &pattern) virtual void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter);
{
set_formatter_(details::make_unique<spdlog::pattern_formatter>(pattern));
}
virtual void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter)
{
formatter_ = std::move(sink_formatter);
}
Mutex mutex_;
}; };
} // namespace sinks } // namespace sinks
} // namespace spdlog } // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY
#include "base_sink-inl.h"
#endif

View 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

View File

@@ -1,17 +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
#ifndef SPDLOG_H
#error "spdlog.h must be included before this file."
#endif
#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/details/synchronous_factory.h"
#include <mutex> #include <mutex>
#include <string> #include <string>
@@ -25,23 +20,12 @@ template<typename Mutex>
class basic_file_sink 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_;
@@ -55,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

View File

@@ -1,18 +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
#ifndef SPDLOG_H #include "spdlog/common.h"
#error "spdlog.h must be included before this file."
#endif
#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/details/os.h"
#include "spdlog/details/synchronous_factory.h"
#include <chrono> #include <chrono>
#include <cstdio> #include <cstdio>
@@ -33,48 +30,76 @@ struct daily_filename_calculator
{ {
filename_t basename, ext; filename_t basename, ext;
std::tie(basename, ext) = details::file_helper::split_by_extension(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 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
@@ -104,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>;
@@ -120,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)
{ {

View File

@@ -1,17 +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
#ifndef SPDLOG_H
#error "spdlog.h must be included before this file."
#endif
#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>
@@ -50,10 +45,14 @@ public:
sinks_ = std::move(sinks); 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))

View 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

Some files were not shown because too many files have changed in this diff Show More