Compare commits

...

494 Commits

Author SHA1 Message Date
gabime
caff7296b1 small fix to example.sln 2018-08-05 01:33:52 +03:00
gabime
779328a940 Fixed async_bench 2018-08-05 01:25:40 +03:00
gabime
01e05a4495 clang format 2018-08-05 00:35:32 +03:00
gabime
af6b3fe599 delay the release mutex in the async queue only for mingw 2018-08-05 00:34:59 +03:00
gabime
74e8bebb75 Added async_bench 2018-08-05 00:14:01 +03:00
gabime
8bfec30d48 Fix deadlock issue with mingw in async logger 2018-08-03 13:38:41 +03:00
Gabi Melman
2880eceeae Merge pull request #773 from slapenko/v1.x
We can control should daily_file_sink truncate an underlying file or not
2018-08-02 17:13:40 +03:00
slapenko
34ada56f5d Refactoring. Rid of open_file for clarity 2018-08-02 07:27:49 -05:00
slapenko
64521005ab We can control should daily_file_sink truncate an underlying file or not 2018-08-01 22:58:15 -05:00
Gabi Melman
16b18f7962 Update README.md 2018-08-01 22:01:35 +03:00
Gabi Melman
986af6ea9e Update README.md 2018-07-26 21:53:40 +03:00
Gabi Melman
691011c473 Update README.md 2018-07-26 21:52:52 +03:00
gabime
fd54719351 clang-format 2018-07-26 21:13:19 +03:00
gabime
420b17ae65 Fix issue #769 2018-07-26 21:09:40 +03:00
gabime
ef5e7af68a Fixed bench Makefile 2018-07-26 00:29:13 +03:00
gabime
05b68b8581 Fixed example and inline 2018-07-26 00:23:44 +03:00
gabime
de6ddf4e2a Some code refactoring in formatter 2018-07-26 00:20:31 +03:00
gabime
a12a21a18e Improved millis formatting 2018-07-25 23:33:03 +03:00
gabime
d8053dd6a6 -Ofast in bench Makefile 2018-07-25 20:15:27 +03:00
gabime
7d38e2b01e Removed junk folder 2018-07-25 18:48:45 +03:00
gabime
53cd47e19f Remove un needed declaration from thread_pool 2018-07-25 16:50:14 +03:00
gabime
ce5c1c24cf added explicit kw to ctor 2018-07-25 00:33:11 +03:00
gabime
2894e8de5e clang format 2018-07-25 00:06:10 +03:00
gabime
74c10df169 Changed SPDLOG_VERSION to be numeric 2018-07-25 00:03:27 +03:00
gabime
0da977f9c7 Fixed bench Makefile 2018-07-24 23:32:11 +03:00
gabime
0b7c505b50 Fixed forgotten inline keyword 2018-07-24 23:07:32 +03:00
gabime
cb9c984aa7 registery and periodic flusher fixes. 2018-07-24 22:59:34 +03:00
Gabi Melman
516a8e4212 Update periodic_worker.h 2018-07-24 03:08:49 +03:00
Gabi Melman
40aeaaee54 Update periodic_worker.h 2018-07-24 03:01:27 +03:00
Gabi Melman
5fd56ec463 Update README.md 2018-07-23 22:53:23 +03:00
gabime
33329c80a9 code formatting 2018-07-23 00:13:52 +03:00
gabime
fe73255452 Fixed issue #759 2018-07-23 00:13:24 +03:00
gabime
9d497d5afd Fix issue #765 2018-07-22 21:55:47 +03:00
gabime
693103af9c support set_formatter in spdlog.h 2018-07-22 21:52:46 +03:00
gabime
7184c42376 Changed some static string array to char* arrays 2018-07-22 13:06:16 +03:00
Gabi Melman
349829fa96 Update periodic_worker.h 2018-07-22 02:02:36 +03:00
Gabi Melman
3684228cd5 Update periodic_worker.h 2018-07-22 02:02:01 +03:00
gabime
9e4925eff0 clang format 2018-07-21 23:48:07 +03:00
gabime
a96b4d7529 Added periodic flusher support, and fixed some registry issues 2018-07-21 23:30:26 +03:00
gabime
d5af87a8e1 Added periodic flusher support, and fixed some registry issues 2018-07-21 22:43:36 +03:00
gabime
683dc0b216 Added async test to tests 2018-07-21 00:35:22 +03:00
gabime
989a10e48b Added async test to tests 2018-07-21 00:33:27 +03:00
gabime
41d248f442 Fixed Makefile.mingw example 2018-07-21 00:24:30 +03:00
gabime
2dfea6bee3 Fixed tests under windows 2018-07-21 00:13:37 +03:00
gabime
0c07df7005 Fixed async factory 2018-07-20 23:58:21 +03:00
gabime
599981e2e6 Fixed mingw build 2018-07-20 23:34:02 +03:00
gabime
b0059b290f Fix issue #761 2018-07-20 23:26:52 +03:00
gabime
9cbdd5ffd4 Added async_nonblocking factory 2018-07-20 23:20:48 +03:00
gabime
ddb3002bc1 Added non const sinks() function to support addition/removal of sinks from existing logger 2018-07-20 12:49:30 +01:00
Gabi Melman
63db70aacc Update README.md 2018-07-20 14:35:57 +03:00
gabime
6225a9fa4f Minor cleaning of spdlog.h 2018-07-19 15:09:10 +03:00
gabime
3aaefc48ec Minor cleaning of common.h 2018-07-19 15:07:54 +03:00
gabime
c832a39d44 fixed example msvc solution 2018-07-19 15:03:37 +03:00
gabime
0a3c81826f Changed some functions to accept strings instead of ref to strings for better semantics 2018-07-19 15:03:08 +03:00
gabime
b710e0fe86 Changed some functions to accept strings instead of ref to strings for better semantics 2018-07-19 15:00:05 +03:00
gabime
9df2bd256e Changed some functions to accept strings instead of ref to strings for better semantics 2018-07-19 14:54:26 +03:00
Gabi Melman
b238bf9512 fix issue #755 2018-07-17 20:09:05 +03:00
Gabi Melman
07928109af Update README.md 2018-07-17 15:28:53 +03:00
Gabi Melman
2e4e80f2a9 Update README.md 2018-07-17 15:27:21 +03:00
Gabi Melman
4b62819da0 Update README.md 2018-07-17 15:26:12 +03:00
Gabi Melman
e3af4124de Update README.md 2018-07-17 15:20:02 +03:00
Gabi Melman
653c4d6472 Update README.md 2018-07-17 15:19:38 +03:00
Gabi Melman
b96a244984 Update README.md 2018-07-17 15:17:31 +03:00
Gabi Melman
2656a031e1 Update README.md 2018-07-17 15:16:14 +03:00
Gabi Melman
532671662d Update README.md 2018-07-17 15:15:18 +03:00
Gabi Melman
58f244a003 Update README.md 2018-07-17 15:13:31 +03:00
Gabi Melman
51c851ce3d Update README.md 2018-07-17 15:09:36 +03:00
gabime
c2a49080aa Refactored sink interface and base_sink 2018-07-14 16:21:53 +03:00
Gabi Melman
2bc05b6b17 Merge pull request #752 from baishuai/v1.x
fix bug in android_sink.h
2018-07-13 20:18:14 +03:00
baishuai
41f708e438 fix bug in android_sink.h 2018-07-13 23:41:26 +08:00
gabime
7d40244a89 Fixed issue #726 and changed default filename calculator to dateonly 2018-07-13 01:44:29 +03:00
gabime
08ef5a2a66 Updated null_mt bench report 2018-07-12 23:48:24 +03:00
gabime
16ee72da7c clang format 2018-07-10 23:53:00 +03:00
gabime
d409e5367b Revert d5468e50f6 2018-07-10 23:51:20 +03:00
gabime
d5468e50f6 Small optimization in thread pool 2018-07-10 20:42:27 +03:00
gabime
ef111ddba2 Fix issue #736 2018-07-10 20:20:55 +03:00
gabime
81f29a9a7a Fixed wchar support in messages 2018-07-10 17:01:49 +03:00
gabime
898e1f2641 Fixed bench 2018-07-10 16:11:01 +03:00
gabime
23ad1aa243 Fixed bench 2018-07-10 16:08:39 +03:00
gabime
4e9fafac75 comment 2018-07-10 14:35:54 +03:00
gabime
b9180f8f5a Fix to a windows issue, where very high rotation rates sometimes fail if antivirus is running. 2018-07-10 14:33:38 +03:00
gabime
cf6e9461af Fixed comversion warning in bench under msvc 2018-07-10 10:48:29 +03:00
gabime
97c201297f Udpated example 2018-07-10 10:40:25 +03:00
gabime
4a59ea3b1e Fixedconversion warning in 32 bits 2018-07-10 10:40:09 +03:00
gabime
f9019870da Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2018-07-10 10:34:08 +03:00
gabime
60ca07c2d5 fixed latency test 2018-07-09 23:56:07 +03:00
gabime
8baa8cf8ea fixed latency test 2018-07-09 23:55:17 +03:00
gabime
a776a774e1 cache millis in full_formatter 2018-07-09 21:07:44 +03:00
gabime
374a22b4b9 bench fix 2018-07-09 21:06:59 +03:00
gabime
bc495bbc63 Added latency bench 2018-07-09 21:06:15 +03:00
gabime
c887907d4a faster reset of cached buffer 2018-07-09 15:10:43 +03:00
gabime
b4dcd592d8 faster reset of cached buffer 2018-07-09 15:10:24 +03:00
gabime
21524c16fa faster reset of cached buffer 2018-07-09 15:05:48 +03:00
Gabi Melman
bbff8abf58 Update CMakeLists.txt 2018-07-08 19:25:15 +03:00
Gabi Melman
99cc35384b Update common.h 2018-07-08 19:24:26 +03:00
Gabi Melman
8266dde556 Update README.md 2018-07-08 17:03:13 +03:00
Gabi Melman
2277fdc80d Update README.md 2018-07-08 17:02:26 +03:00
Gabi Melman
9cc731001b Update README.md 2018-07-08 17:00:57 +03:00
gabime
80855329e7 Updated readme.md 2018-07-08 16:50:56 +03:00
gabime
5851fb9de9 Updated readme.md 2018-07-08 16:49:57 +03:00
gabime
866e593138 Updated readme.md 2018-07-08 16:48:58 +03:00
gabime
7f26ad29a0 Updated bench.cpp 2018-07-08 16:46:52 +03:00
gabime
f766b6d882 Updated bench.cpp 2018-07-08 16:46:12 +03:00
gabime
ca2724d82e Updated readme 2018-07-08 16:45:35 +03:00
gabime
a8b026bd70 Updated readme 2018-07-08 16:43:55 +03:00
gabime
2292f7a27a Updated readme 2018-07-08 16:43:04 +03:00
gabime
3ea1d27aac Updated readme 2018-07-08 16:42:21 +03:00
gabime
df779f66a2 Updated readme 2018-07-08 16:39:25 +03:00
gabime
81f3cc5575 clang format 2018-07-08 16:26:25 +03:00
gabime
1fd166d417 Updated example 2018-07-08 16:26:14 +03:00
gabime
5bfeb672f7 updated slot size in async queue 2018-07-08 16:22:04 +03:00
gabime
3c40c5ca5d Added mulitsink example 2018-07-08 16:20:28 +03:00
gabime
f4771be70e Upgraded to fmt 5.1.0 2018-07-08 11:03:43 +03:00
gabime
887326e715 minor code cleanup 2018-07-08 01:41:32 +03:00
gabime
45da6c9c33 spelling 2018-07-08 00:56:05 +03:00
gabime
7ed8e1b59d renamed async block policy 2018-07-08 00:53:50 +03:00
gabime
4f1ce9189f Fixed issue #747 2018-07-07 16:40:29 +03:00
gabime
775e410b00 Fixed issue #747 2018-07-07 16:29:05 +03:00
gabime
c9331594bb Renamed file to pattern_forammter.h and fixed utc support 2018-07-07 16:15:17 +03:00
gabime
cbc4db8649 Renamed filename and other small formatter changes 2018-07-07 15:34:25 +03:00
gabime
b07069fb4d Fixed example 2018-07-07 15:27:17 +03:00
Gabi Melman
4fc59e3e7b Update example.cpp 2018-07-07 15:05:01 +03:00
Gabi Melman
5fddfca7c6 Update example.cpp 2018-07-07 15:03:57 +03:00
Gabi Melman
745b9e32ed Update example.cpp 2018-07-07 15:03:34 +03:00
gabime
9dc44c39e7 Fixed bugs in stdout_sinks and in msvc 2018-07-07 14:04:31 +03:00
gabime
bde4c7149f Renamed traits to console globals 2018-07-07 13:38:15 +03:00
gabime
39c6eb752a Code formatting 2018-07-07 13:22:43 +03:00
gabime
a9a7309001 fixed dist_sink.h 2018-07-07 13:22:09 +03:00
gabime
72f4fae207 updated example 2018-07-07 12:53:56 +03:00
gabime
b0bf457538 updated example 2018-07-07 12:37:17 +03:00
gabime
98ab8e0d73 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2018-07-07 12:12:54 +03:00
gabime
cd4dcbab36 Renamed simple_file_sink -> basic_file_sink 2018-07-07 12:12:45 +03:00
Gabi Melman
3ebdb2fd8b Update logger.h 2018-07-06 08:40:55 +03:00
gabime
69c11ea7d2 Updated bench 2018-07-05 16:35:07 +03:00
gabime
bd759bfca7 removed force_flush from simple logger 2018-07-05 16:34:20 +03:00
gabime
4a90950fe5 updated bench 2018-07-05 16:19:23 +03:00
gabime
005468248b Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2018-07-05 14:56:19 +03:00
gabime
ecd7669e42 Fixed gcc warning 2018-07-05 12:57:14 +03:00
gabime
36a823df70 Fixed msvc example solution 2018-07-05 10:55:19 +03:00
gabime
3643db6821 Fixed msvc conversion warning 2018-07-05 10:54:02 +03:00
gabime
59f54cda10 Fixed msvc conversion warnings 2018-07-04 10:44:05 +03:00
gabime
95de24e4f6 fixed tests and renamed to_short_str 2018-07-04 09:59:26 +03:00
gabime
1d9e2304be renamed level::to_str() to level::to_c_str() 2018-07-04 02:08:28 +03:00
gabime
1f801828a5 pad6 thread id and micros in formatter 2018-07-04 01:41:05 +03:00
gabime
94a7152afc async queue - overrun oldsest policy option 2018-07-04 01:24:52 +03:00
gabime
0358d115e0 removed std::string from async messages 2018-07-04 00:46:50 +03:00
gabime
b4349e4226 pre allocate async q memory 2018-07-04 00:38:23 +03:00
gabime
92e2cef67f remove unneeded includes from file 2018-06-26 02:37:35 +03:00
gabime
91264ea2f0 small improvment to formatter 2018-06-26 02:32:19 +03:00
gabime
e66ee8b710 fix gcc and clang warnings 2018-06-26 02:00:33 +03:00
gabime
a6e2f23780 Pattern formatter optimizations 2018-06-26 01:13:02 +03:00
gabime
87e013534c wincolor sink fix 2018-06-25 16:32:22 +03:00
gabime
1d9ec2373a Better support for 32bits 2018-06-25 16:31:56 +03:00
gabime
46cf0f86b8 Added set_pattern() to sink class 2018-06-24 01:55:30 +03:00
gabime
e574f57511 Fixed stdout_color_sinks.h namespace 2018-06-24 01:55:09 +03:00
gabime
8fdd26da82 Fixed example.cpp 2018-06-24 01:37:34 +03:00
gabime
0c19bdd772 pattern per sink and pattern caching 2018-06-24 01:32:39 +03:00
gabime
9d7a5c253a Moved fmt helpers to new file 2018-06-15 14:15:35 +03:00
gabime
ac6a2a4c0f -mpattern_formatter small change in pad3 2018-06-15 13:56:12 +03:00
Gabi Melman
58e09dbd33 Update common.h 2018-06-13 21:14:18 +03:00
Gabi Melman
e770673f11 Update common.h 2018-06-13 21:13:22 +03:00
gabime
6d5670fde7 format 2018-06-13 20:19:13 +03:00
gabime
da0d6d0478 Removed printf support 2018-06-13 20:16:45 +03:00
gabime
5683c06d9a solve issue #724 2018-06-13 20:16:31 +03:00
gabime
65506136e2 solve issue #724 2018-06-13 19:10:22 +03:00
gabime
2989e998ee Speed up pattern_formatter 2018-06-13 18:47:02 +03:00
gabime
3f438a8084 added bundled fmt 5.x 2018-06-13 10:55:14 +03:00
gabime
f2a8847902 Speed up pattern_formatter 2018-06-13 01:47:03 +03:00
gabime
94ac1261e4 Speed up default pattern 2018-06-13 01:06:37 +03:00
gabime
8b7e19e92f Bug fixes in pattern_formatter and async_logger 2018-06-13 00:20:54 +03:00
gabime
81fa788bca Updated tests 2018-06-12 22:43:49 +03:00
gabime
cdbf2e361b Upgrade to fmt 5.x 2018-06-12 18:48:22 +03:00
gabime
378c7789ba Fixed issue #720 2018-06-10 23:16:00 +03:00
gabime
15f3b0fea5 format 2018-06-10 23:03:55 +03:00
gabime
28ef15d669 updated example 2018-06-10 23:02:53 +03:00
gabime
145fc367f9 updated example 2018-06-10 23:01:46 +03:00
gabime
a21594bec7 move underscores to the end of private members 2018-06-10 22:59:17 +03:00
gabime
7d975de193 removed message_counter from tp 2018-06-01 18:56:51 +03:00
gabime
5833c1dec8 cleaned bench source folder 2018-06-01 17:58:57 +03:00
gabime
4b9949de7b format 2018-06-01 17:52:05 +03:00
gabime
5997e5aae7 cleaned bench folder to contain only spdlog tests 2018-06-01 17:50:59 +03:00
gabime
52d1c08896 cleaned tests 2018-06-01 17:30:38 +03:00
gabime
7815d39807 removed wait_empty() from tp and q 2018-06-01 17:25:23 +03:00
gabime
7f6220d960 increase errno string buffer size 2018-06-01 17:21:00 +03:00
gabime
8a3c858d36 bench 2018-06-01 17:09:22 +03:00
gabime
38b3ecb02e use fmt::safe_strerror 2018-06-01 17:07:39 +03:00
gabime
f2bc1571b4 update example 2018-05-27 03:21:59 +03:00
gabime
4d7245bb67 update example 2018-05-27 03:18:12 +03:00
gabime
ef5c4f027c added tp getter to async api 2018-05-27 03:14:55 +03:00
gabime
c568640595 update bench 2018-05-27 03:02:51 +03:00
gabime
8338b45b2b added tp->wait_empty() 2018-05-27 02:53:16 +03:00
gabime
0d0a841e8d format 2018-05-27 02:21:49 +03:00
gabime
31ff43ef81 more async tests 2018-05-27 02:21:31 +03:00
gabime
a43a44bb88 update example 2018-05-27 01:29:13 +03:00
gabime
cfbc8e52ba use underscore at end of private members names 2018-05-26 19:21:20 +03:00
gabime
72506b3bab use underscore at end of private members names 2018-05-26 19:02:58 +03:00
gabime
b002a21c36 use underscore at end of private members names 2018-05-26 19:02:20 +03:00
gabime
7a339e2b5e Updated usage message in async null bench 2018-05-26 18:57:29 +03:00
gabime
e033a0da9a Fixed usage message in async null bench 2018-05-26 18:54:52 +03:00
gabime
0d7a1d1ef9 format 2018-05-26 18:48:39 +03:00
gabime
80f19d7136 updated null-async bench command line flags 2018-05-26 18:48:33 +03:00
gabime
15d6432302 Fixed bench 2018-05-26 17:42:49 +03:00
gabime
59a15c02d3 use std::string in async_msg instead of fmt::MemoryWriter 2018-05-26 14:56:20 +03:00
gabime
e4ed7528e3 fixed class name 2018-05-26 14:50:42 +03:00
gabime
8932b77d63 fixed example 2018-05-26 14:50:27 +03:00
Gabi Melman
b3543452c2 Update simple_file_sink.h 2018-05-25 19:01:24 +03:00
Gabi Melman
16376c18aa Update thread_pool.h 2018-05-25 18:44:43 +03:00
Gabi Melman
a23eb3769a Update async.h 2018-05-25 18:28:29 +03:00
Gabi Melman
001e22ebee Update registry.h 2018-05-25 18:20:08 +03:00
Gabi Melman
e8a726df03 Update stdout_color_sinks.h 2018-05-25 18:09:30 +03:00
gabime
8901cbffe0 added catch macro and fixed some tests 2018-05-24 00:18:55 +03:00
gabime
b9d7c45e40 Use blocking queue 2018-05-22 21:59:27 +03:00
gabime
cf63bcb808 catch exceptions during flush and cal handler 2018-05-22 21:59:14 +03:00
Gabi Melman
d0b8ecb6dd Update README.md 2018-04-29 01:56:18 +03:00
Gabi Melman
13774e62bd Update README.md 2018-04-29 01:55:09 +03:00
gabime
4ec17f1a1a Normalized spdlog include paths 2018-04-29 01:43:42 +03:00
gabime
f886ae0005 Removed unneeded include 2018-04-29 01:39:36 +03:00
gabime
7db5b56dd7 Removed unneeded include 2018-04-29 01:38:21 +03:00
gabime
3c7103d90d Format 2018-04-29 01:36:45 +03:00
gabime
5ec37c05f7 Normalized spdlog include paths 2018-04-29 01:35:49 +03:00
gabime
780dab6977 Fixed tests 2018-04-29 01:34:36 +03:00
gabime
caa26a2a47 Normalized spdlog include paths 2018-04-29 01:31:09 +03:00
gabime
d389bda2cc moved file sinks sinks folder 2018-04-29 01:26:10 +03:00
gabime
60f8a68ae0 Splitted file sinks to seperate headers 2018-04-29 01:23:18 +03:00
gabime
99ca7f1cbe Comments 2018-04-29 00:52:56 +03:00
gabime
3e28d9ab5a Renamed default factory 2018-04-29 00:44:35 +03:00
gabime
4bee0ec294 Fixed stderr_logger typedef 2018-04-29 00:43:24 +03:00
gabime
b3a23039b1 Renamed default factory 2018-04-29 00:42:24 +03:00
gabime
e500fa013f example 2018-04-29 00:35:32 +03:00
gabime
b393c9d6e6 Fixed console traits 2018-04-29 00:23:59 +03:00
gabime
e4f0d6446b Small template rename 2018-04-29 00:21:15 +03:00
gabime
9b5c5b4f3d Small template rename 2018-04-29 00:20:04 +03:00
gabime
0d61ab82e4 small makefile fix for example 2018-04-29 00:09:56 +03:00
gabime
67ddd59701 Move msg and remove spinning 2018-04-29 00:08:46 +03:00
gabime
78ea362e27 Don't rethrow after catch(...) 2018-04-28 23:30:35 +03:00
gabime
d51102a168 example.cpp fix 2018-04-28 19:29:10 +03:00
gabime
f2023e80a8 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2018-04-22 00:26:02 +03:00
gabime
384ae1dc1b Removed unneeded intializers from async_msg 2018-04-22 00:25:20 +03:00
Gabi Melman
c63f8a6ea0 Delete spdlog_impl.h 2018-04-20 15:55:00 +03:00
gabime
64c725cee2 Fixed multisink example 2018-04-20 14:03:36 +03:00
gabime
110bdd93c8 Fixed stdout_sinks 2018-04-20 14:03:15 +03:00
gabime
c962c88342 Fixed linux port of v1.x 2018-04-20 13:20:19 +03:00
gabime
c80cc3306f Fixed tests 2018-04-20 03:04:53 +03:00
gabime
e4d3eb64e6 Udpated example and spdlog.h 2018-04-20 02:57:05 +03:00
gabime
0969118ce7 added default_factory alias 2018-04-20 01:55:31 +03:00
gabime
ba7c4c0530 stdout and color logger factories 2018-04-20 01:50:09 +03:00
gabime
d6cb447667 fixed compile error 2018-04-19 18:41:00 +03:00
gabime
b9fac2b179 added missing header 2018-04-19 18:40:58 +03:00
gabime
11e068d7a3 Added missing #include 2018-04-19 17:50:18 +03:00
gabime
924ef84241 Refactred spdlog.h and console sinks. Added global lock for all console sinks (traits) 2018-04-18 02:04:10 +03:00
gabime
9bffa921ae global mutex stdout stderr sinks 2018-04-16 01:07:22 +03:00
gabime
c50ba69689 removed un needed includes 2018-04-14 04:16:05 +03:00
gabime
b4cde3fc21 Added missing files 2018-04-14 04:11:03 +03:00
gabime
6f1dc624e6 fix example 2018-04-14 03:47:26 +03:00
gabime
7378cc297c fix example 2018-04-14 03:43:00 +03:00
gabime
6f4cd8d397 thread_pool and refactoring async 2018-04-14 03:34:57 +03:00
Gabi Melman
5e08950ed2 Created contrib/sinks directory 2018-04-13 12:45:33 +03:00
Gabi Melman
bce3b75c53 Created contrib directory 2018-04-13 12:44:43 +03:00
gabime
3fdc7996db code formatting 2018-04-09 15:14:13 +03:00
gabime
e9bb008f15 Fixed example 2018-04-09 15:12:51 +03:00
gabime
d352aa0990 Fixed example 2018-04-09 15:11:42 +03:00
gabime
cfb450c059 Fixed eol write in pattern_formatter_impl 2018-04-09 14:14:52 +03:00
Gabi Melman
b416685d6f Fix gcc warning on stat (32 bits) 2018-04-09 02:06:33 +03:00
gabime
64c2fe180b Fixed bug in wrapping colors around level name in default pattern 2018-04-08 18:27:18 +03:00
gabime
309327187a Fixed example makefile for clang 2018-04-06 04:36:22 +03:00
gabime
1dea46e1ab code formatting 2018-04-06 04:06:02 +03:00
Gabi Melman
4abe403544 Update README.md 2018-04-06 04:05:23 +03:00
Gabi Melman
21d437fbf5 Update README.md 2018-04-06 04:04:50 +03:00
Gabi Melman
84c7f927c2 Merge pull request #679 from gabime/color-formatting
Color formatting
2018-04-06 04:00:17 +03:00
gabime
644c81b9fb Added color ranges to formatter tests 2018-04-06 03:42:24 +03:00
gabime
3452892f76 minor renaming 2018-04-06 03:22:27 +03:00
gabime
2f7fdf2663 wincolor color formatting support 2018-04-06 03:06:41 +03:00
gabime
d040ab93ea wincolor color formatting support 2018-04-06 03:04:18 +03:00
gabime
c8610d9a86 support for color formatting 2018-04-06 02:24:07 +03:00
gabime
93d41b2c0e fixed gcc warning about struct stat 2018-03-22 20:35:49 +02:00
gabime
c03ae5fafd Merge branch 'master' of https://github.com/gabime/spdlog 2018-03-22 20:08:33 +02:00
Gabi Melman
b3988d6e1f Merge pull request #665 from kasru/more_bench
More benchmarks
2018-03-21 20:58:11 +02:00
Alexander Kiselev
ad2c7b3959 Add README 2018-03-21 17:15:33 +03:00
Alexander Kiselev
5e1e897d10 Remove p7-bench binary 2018-03-21 16:51:43 +03:00
Alexander Kiselev
37f209079e Update spdlog-bench 2018-03-21 16:50:45 +03:00
Alexander Kiselev
0f66c63f72 Update easyloggingpp, Add plog 2018-03-21 16:46:52 +03:00
Gabi Melman
abde900cd7 Merge pull request #663 from kasru/more_bench
More bench
2018-03-19 21:27:03 +02:00
Alexander Kiselev
fd1c4d7877 Fix: unknown conversion specifier 'Y' 2018-03-19 20:03:47 +03:00
Alexander Kiselev
70ad1aa409 Changes: boost, easylogging, g2log, glog, spdlog. 2018-03-19 19:22:02 +03:00
Alexander Kiselev
c83dd5d60e Added: g3log, log4cplus, log4cpp, p7. Changes: boost, easylogging, g2log, glog, spdlog. 2018-03-19 19:17:26 +03:00
gabime
18c99682a8 fixed clang warning about uninitialized values 2018-03-17 14:08:10 +02:00
gabime
200815892f Fix clang-tidy warnings about missing braces around if and for statements 2018-03-17 13:42:09 +02:00
gabime
7eb6ca6337 formatting 2018-03-17 12:49:45 +02:00
gabime
5a0f90bc82 clang format namespace fix 2018-03-17 12:49:31 +02:00
gabime
2a86cdb203 Merge branch 'master' of https://github.com/gabime/spdlog 2018-03-17 12:47:56 +02:00
gabime
56e4a201ec formatting 2018-03-17 12:47:46 +02:00
gabime
c739e68021 clang format namespace fix 2018-03-17 12:47:04 +02:00
Gabi Melman
fe8a519434 Update logger.h 2018-03-16 22:03:54 +02:00
gabime
4445f6f869 formatting 2018-03-16 17:35:56 +02:00
gabime
4ee89877d5 Changed clang formatting for templates 2018-03-16 17:35:50 +02:00
gabime
ea95ea8295 Fix potential issue #660 2018-03-16 17:20:56 +02:00
gabime
fe5aaf4932 Fixed example 2018-03-16 17:17:33 +02:00
gabime
5afb5dc782 Changed clang formatting for templates 2018-03-16 17:13:50 +02:00
gabime
f4ce52d206 Changed clang formatting for templates 2018-03-16 17:13:22 +02:00
gabime
1108515738 format.sh small fix 2018-03-16 17:04:35 +02:00
gabime
8ee7b772a9 Added -O3 flag to CMakeLists.txt 2018-03-16 14:15:35 +02:00
Gabi Melman
650daf7542 Update common.h
Updated spdlog version macro to 0.16.4-rc
2018-03-09 23:26:28 +02:00
Gabi Melman
1946818292 Update .clang-format 2018-03-09 23:11:48 +02:00
gabime
cbe98c0fd2 clang format 2018-03-09 15:30:48 +02:00
gabime
ad221b0990 Changed function name to level::from_str 2018-03-09 15:27:53 +02:00
gabime
a2653d409f clang-format 2018-03-09 15:26:33 +02:00
gabime
461b5ef28a Fixed missing ; 2018-03-09 15:26:00 +02:00
gabime
7f1f7b6232 Changed function name to level::from_str 2018-03-09 15:24:37 +02:00
gabime
d741f1b654 some clang format fixes 2018-03-09 15:02:15 +02:00
gabime
f0606bcdb7 Merge branch 'master' of https://github.com/gabime/spdlog 2018-03-09 14:29:23 +02:00
gabime
03bc9ebb1f .clang-format 2018-03-09 14:29:20 +02:00
gabime
1f79c01dc4 Use clang-format-5.0 instead of astyle 2018-03-09 14:27:15 +02:00
Gabi Melman
dcf803de73 Merge pull request #622 from fegomes/to_level
New function to convert level_enum from string
2018-03-09 14:27:01 +02:00
fegomes
46f9768599 change of scope the name_to_level variable 2018-03-09 09:04:44 -03:00
gabime
2e098421f1 added .log extension to bench test 2018-03-09 12:08:56 +02:00
fegomes
c21dd874d1 removed class to return size of array. 2018-03-08 19:09:46 -03:00
fegomes
48c8755d06 include test to convert functions and change suggested by @gabime 2018-03-08 19:08:24 -03:00
fegomes
f9750dddee Merge branch 'master' into to_level 2018-03-08 18:16:10 -03:00
Gabi Melman
e8a39c894f Update test_pattern_formatter.cpp 2018-03-06 10:31:36 +02:00
Gabi Melman
97cdbc45e8 Merge pull request #653 from tbastos/master
Fix implicit conversion warnings (-Wsign-conversion)
2018-03-05 22:29:51 +02:00
Thiago Bastos
d044369e3b Fix implicit conversion warnings (-Wsign-conversion) 2018-03-05 20:00:48 +01:00
gabime
84d3c90b93 Fixed g++ 4.9 warnings after the clang-tidy fixes 2018-02-28 00:11:50 +02:00
Gabi Melman
2e973c8b3d Merge pull request #647 from DanielChabrowski/clang-tidy
Applied some of clang-tidy fixes
2018-02-27 23:41:24 +02:00
Daniel Chabrowski
de642b6263 astyle applied 2018-02-25 12:41:18 +01:00
Daniel Chabrowski
5355bd3a8f readability-named-parameter 2018-02-25 12:39:37 +01:00
Daniel Chabrowski
35a843f8b6 modernize-return-braced-init-list 2018-02-25 12:24:21 +01:00
Daniel Chabrowski
17caf77784 google-build-namespaces 2018-02-25 12:12:34 +01:00
Daniel Chabrowski
0c94ce0039 deleted copy op and a little format 2018-02-25 03:35:20 +01:00
Daniel Chabrowski
af50d5ef1f readability-inconsistent-declaration-parameter-name 2018-02-25 02:19:26 +01:00
Daniel Chabrowski
9ce66f2c9a modernize-pass-by-value 2018-02-25 01:58:09 +01:00
Daniel Chabrowski
ad624432d8 google-explicit-constructor 2018-02-25 01:40:46 +01:00
Daniel Chabrowski
1e1ca23101 modernize-use-equals-default 2018-02-25 01:25:15 +01:00
Daniel Chabrowski
e5bbe57f01 cppcoreguidelines-pro-type-member 2018-02-25 01:15:35 +01:00
Daniel Chabrowski
68f91822ed performance-unnecessary-value-param 2018-02-25 00:54:14 +01:00
Daniel Chabrowski
7aed498540 modernize-use-default-member-init 2018-02-25 00:38:54 +01:00
Daniel Chabrowski
d5a3bb5234 readability-else-after-return 2018-02-25 00:24:47 +01:00
Daniel Chabrowski
9ebb9ff318 readability-implicit-bool-cast 2018-02-25 00:16:18 +01:00
Daniel Chabrowski
fb6df0512f modernize-use-override 2018-02-24 23:56:56 +01:00
Daniel Chabrowski
7f4c1bb77c modernize-use-using 2018-02-24 22:35:09 +01:00
gabime
abc0d43995 astyle 2018-02-23 18:33:03 +02:00
gabime
3826ac1433 bumped bundled fmt version to 4.1.0 2018-02-23 18:32:06 +02:00
gabime
d650fa2456 Fixed tests for older gcc compilers 2018-02-23 18:25:39 +02:00
gabime
80163dc6c1 Better support for WinRT 2018-02-23 18:16:43 +02:00
gabime
a8b5bb894e fixed cmake for tests 2018-02-23 16:49:26 +02:00
gabime
3620b31ef2 added pattern formatter tests 2018-02-23 16:40:45 +02:00
gabime
7709fc70eb small fix to pattern_formatter ctor 2018-02-23 15:10:21 +02:00
Gabi Melman
bce33698be Merge pull request #628 from emadwill/eol
Support for custom EOL per formatter -Thanks @emadwill
2018-02-23 15:11:10 +02:00
gabime
04d0dd5987 moved SPDLOG_VERSION macro to common.h 2018-02-23 14:39:41 +02:00
gabime
051048ebd7 Fixed issue #645 (include tweakme.h from common.h) 2018-02-23 14:34:25 +02:00
gabime
7fe3912f12 astyle 2018-02-23 14:29:31 +02:00
gabime
7eb41ccf0f Use -O3 flag in tests 2018-02-23 14:29:14 +02:00
gabime
79d55fd802 Merge branch 'master' of https://github.com/gabime/spdlog 2018-02-23 14:07:45 +02:00
gabime
6df52df5b4 Use emplace_back in pattern_formatter 2018-02-23 14:05:48 +02:00
gabime
bdca50e6a7 Fixed issue #637 2018-02-23 13:26:53 +02:00
Gabi Melman
851b49e147 Update CMakeLists.txt 2018-02-22 13:37:50 +02:00
Gabi Melman
e64847bd09 Merge pull request #644 from grzadr/patch-1
Fix indentation in tests/catch.hpp:2916
2018-02-22 11:44:55 +02:00
grzadr
8e861728a0 Fix indentation in line 2916
Compiling with -Wmisleading-indentation triggers warning:
/data/Git/spdlog/tests/catch.hpp: In member function ‘bool Catch::TestSpec::Filter::matches(const Catch::TestCaseInfo&) const’:
/data/Git/spdlog/tests/catch.hpp:2913:17: warning: this ‘for’ clause does not guard... [-Wmisleading-indentation]
                 for( std::vector<Ptr<Pattern> >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it )
                 ^~~
/data/Git/spdlog/tests/catch.hpp:2916:21: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘for’
                     return true;
                     ^~~~~~
2018-02-22 09:46:54 +01:00
Gabi Melman
8cfd71a9be Merge pull request #635 from DanielChabrowski/cmake_explicit_lang
CMake explicit language
2018-02-12 01:16:49 +02:00
Daniel Chabrowski
5d08bd1709 Bump example's cmake version to 3.1
CMake 3.1 is used in main CMakeLists file, no reason not to use it in example.
Threads::Threads is introduced in cmake 3.1.
2018-02-11 21:16:15 +01:00
Daniel Chabrowski
646a140ed4 Specify CXX language explicitly in CMake
Marking project as CXX will disable detecting C compiler and other checks.
Removed 'INCLUDES DESTINATION' as it made the include path appear doubled.
2018-02-11 21:13:33 +01:00
Gabi Melman
5eef243ab6 Merge pull request #634 from ColinDuquesnoy/patch-1
Fix compilation error with GCC 8
2018-02-10 19:05:33 +02:00
Colin Duquesnoy
11ee6834f6 Fix compilation error with GCC 8
error: need 'typename' before 'std::conditional<std::is_same<char, char>::value, fmt::BasicMemoryWriter<char>, fmt::BasicMemoryWriter<wchar_t> >::type' because 'std::conditional<std::is_same<char, char>::value, fmt::BasicMemoryWriter<char>, fmt::BasicMemoryWriter<wchar_t> >' is a dependent scope
         std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
2018-02-10 15:15:46 +01:00
Gabi Melman
c07e81a049 Merge pull request #632 from bschindler/wincolor_set_color_public
Make set_color public in wincolor_sink to retain configurability
2018-02-08 11:04:29 +02:00
Benjamin Schindler
05b2aabe0e Make set_color public in wincolor_sink to retain configurability 2018-02-08 09:14:45 +01:00
Emad William Farag
55680db160 Support for custom EOL per formatter 2018-02-05 21:51:01 -05:00
Fernando Gomes
f4ffddc942 Merge branch 'master' into to_level 2018-02-05 09:52:30 -02:00
gabime
5ab033fba5 Fix issue #629 2018-02-05 12:20:57 +02:00
Gabi Melman
1dfc8282df Merge pull request #624 from yisonPylkita/modern_cmake_tests
Modern CMake in /tests
2018-02-03 19:10:44 +02:00
gabime
f8aec1bdf1 Merge branch 'master' of https://github.com/gabime/spdlog.git 2018-02-03 18:58:10 +02:00
gabime
51a83da578 Repplaced map with unordered_map for level->colos mapping 2018-02-03 18:57:57 +02:00
yisonPylkita
38ccd51399 Modern CMake in /tests 2018-01-28 00:49:03 +01:00
fegomes
8696ad8739 new function to convert level_enum from string 2018-01-24 23:08:46 -02:00
Gabi Melman
c336470320 Merge pull request #621 from rcarmich/fix_example_spelling_errors
Fixed spelling mistake in example.cpp
2018-01-24 22:42:31 +02:00
Ryan Carmichael
da51d8dfd3 Fixed spelling mistake in example.cpp 2018-01-24 14:18:05 -06:00
Gabi Melman
22fdd3bf0f Merge pull request #615 from Qix-/master
A few small ANSI color improvements
2018-01-18 22:01:46 +02:00
Josh Junon
cae749fc9b clear line after writing log message contents 2018-01-18 15:06:47 +01:00
Josh Junon
58e68725ae rename grey to black 2018-01-18 15:03:26 +01:00
Josh Junon
a59f74e8a2 remove needless zeros in reset code 2018-01-18 15:03:03 +01:00
Gabi Melman
f258af4364 Update CMakeLists.txt 2018-01-12 16:06:30 +02:00
gabime
ccd675a286 version 0.16.3 2018-01-12 14:09:34 +02:00
gabime
5372d58adc comment 2018-01-12 14:09:07 +02:00
Gabi Melman
751520f0cf Merge pull request #610 from joaomoreno/fix609
Use Sleep in Windows instead of sleep_for
2018-01-11 23:09:38 +02:00
Joao Moreno
357a63d914 fix spdlog namespace 2018-01-11 21:58:02 +01:00
Joao Moreno
a938045135 use Sleep in Windows instead of sleep_for
fixes #609
2018-01-11 14:50:47 +01:00
Gabi Melman
32177aa77a Merge pull request #604 from sam-lunt/improve-macros
Ensure marcos always expand to expressions
2018-01-03 18:05:25 +02:00
Gabi Melman
ce1df17262 Merge pull request #605 from sam-lunt/add-flush-on
Add global flush_on function
2018-01-03 18:00:08 +02:00
Sam Lunt
9f8413308a add global flush_on function 2018-01-03 09:36:09 -06:00
Sam Lunt
f25f0e0e40 add (void)0 when logging is disabled 2018-01-03 09:07:58 -06:00
Gabi Melman
a2890f2778 Merge pull request #596 from Broekman/master
Issue fix for spdlog #595. Conversion warning.
2017-12-26 21:22:08 +02:00
Stefan Broekman
de4644b44a Issue fix for spdlog #595. Conversion warning.
See: https://github.com/gabime/spdlog/issues/595

On line 85 in file sinks/wincolor_sink.h:
back_color &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
FOREGROUND_INTENSITY);

'back_color' is of type 'WORD' (unsigned short) whereas a bitwise
complement/NOT returns an int. This results in a conversion warning with
-Wconversion enabled.

85:20: warning: conversion to 'WORD {aka short unsigned int}' from 'int'
may alter its value [-Wconversion] back_color &= ~(FOREGROUND_RED |
FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);

Possible solution:
We know that the result of ~(FOREGROUND_RED | FOREGROUND_GREEN |
FOREGROUND_BLUE | FOREGROUND_INTENSITY) is always within the limits of
an unsigned short so a simple cast should suffice (correct me if I'm
wrong):

back_color &= static_cast<unsigned short>(~(FOREGROUND_RED |
FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY));
2017-12-26 19:23:29 +01:00
Gabi Melman
03db102375 Update README.md 2017-12-26 13:26:04 +02:00
gabime
3d967dd716 added catch license in tests folder 2017-12-24 19:37:54 +02:00
Gabi Melman
b53d207f44 Update file_helper.h 2017-12-23 11:43:41 +02:00
Gabi Melman
fde12195ee Update file_helper.h 2017-12-22 19:06:01 +02:00
gabime
4ca6991828 astyle 2017-12-22 18:55:19 +02:00
gabime
813dcbcf63 version 0.16.2 2017-12-22 18:50:08 +02:00
gabime
7663f58379 Fixed file_helper::split_by_extenstion tests for gcc 2017-12-22 18:48:02 +02:00
gabime
8e3f968ba4 Fixed file_helper::split_by_extenstion and added more tests for it 2017-12-22 18:41:03 +02:00
gabime
f695e536dd Fixed file_helper::split_by_extenstion and added more tests for it 2017-12-22 18:37:51 +02:00
gabime
f257e4ea8c gitignore tests/logs 2017-12-22 18:36:54 +02:00
gabime
0ed3e4cf76 Merge branch 'master' of https://github.com/gabime/spdlog 2017-12-22 14:38:54 +02:00
gabime
6d355fd602 report about unknown excepptions before rethrow 2017-12-22 14:38:44 +02:00
Gabi Melman
dd0b7b2d13 Merge pull request #590 from fcharlie/master
fix split_by_extenstion parse error extenstion
2017-12-22 12:50:52 +02:00
Force Charlie
42e5d98a48 fix split_by_extenstion parse error extenstion 2017-12-22 17:52:50 +08:00
Gabi Melman
c060a10c10 update to version 0.16.1 2017-12-20 10:08:49 +02:00
Gabi Melman
79a3a633c7 uupdate version to 0.16.1 2017-12-20 10:07:50 +02:00
Gabi Melman
52dfd478d6 Merge pull request #584 from horar/master
Update version strings to 0.16.0
2017-12-20 10:02:17 +02:00
Gabi Melman
48eca46680 Merge pull request #586 from horar/fix/warnings
Solve compiler warning in tests
2017-12-20 09:50:34 +02:00
Ľubomír Carik
f93277d271 Solve compiler warning in tests
Signed-off-by: Ľubomír Carik <Lubomir.Carik@anritsu.com>
2017-12-20 04:34:08 +01:00
Ľubomír Carik
dda8af0092 Update version strings to 0.16.0
Signed-off-by: Ľubomír Carik <Lubomir.Carik@anritsu.com>
2017-12-20 03:43:47 +01:00
gabime
ed5498a2e9 report unexected exception types 2017-12-20 00:29:15 +02:00
gabime
6dd928dc3c Undo fix #529 (causes a warning under gcc and clang when no args passed) 2017-12-20 00:04:16 +02:00
gabime
de595fe2b7 Fix #529 (SPDLOG_TRACE macro problem) 2017-12-19 23:09:27 +02:00
Gabi Melman
d460c3026a Merge pull request #568 from adubovikov/master
added facilty for syslog
2017-12-19 11:49:51 +02:00
Gabi Melman
88fe218741 Update logger_impl.h 2017-12-06 15:59:27 +02:00
Gabi Melman
b1be7b9fea async log: increased sleep to to 500ms the worker loop 2017-12-05 14:07:13 +02:00
Alexandr Dubovikov
bec6919587 added facilty for syslog 2017-12-04 13:03:40 +01:00
Gabi Melman
2f81ff7f17 Update README.md 2017-12-03 21:01:07 +02:00
Gabi Melman
4db70c2078 Update README.md 2017-12-03 20:58:19 +02:00
gabime
26b390bb19 removed lock from dist_sink::_flush() (moved to base_sink::flush()) 2017-12-02 17:24:02 +02:00
gabime
a9149c6d46 added lock on flush in base_sink 2017-12-02 17:06:59 +02:00
gabime
859b7f1d58 fixed test for visual c++ 2017-12-01 04:42:23 +02:00
gabime
49989e0678 fixed test for gcc 4.8 (no regex) 2017-12-01 04:35:23 +02:00
gabime
70274924b7 fixed test comment 2017-12-01 03:59:35 +02:00
gabime
f5939f9e56 astyle 2017-12-01 03:46:19 +02:00
gabime
84e307521d Fixed test 2017-12-01 03:45:29 +02:00
gabime
3c4a2bf531 Handle file extensions in rotating and daily loggers 2017-12-01 03:40:49 +02:00
gabime
60ce47a814 Removed include_if.cpp from the tests .sln 2017-12-01 01:28:12 +02:00
Gabi Melman
613f024d42 Removed forgotten *_if declarations 2017-11-30 23:42:57 +02:00
gabime
799ba2a57b added SPDLOG_DISABLE_TID_CACHING macro to prevent invalid thread ids after fork 2017-11-26 00:40:47 +02:00
gabime
adbc22096a enable final keyword by default. Can be disabled in tweakme.h for older compilers 2017-11-25 15:53:35 +02:00
gabime
e7cf25a1c0 fixed issue #562 2017-11-25 15:41:55 +02:00
gabime
dcc7b347ca Removed all *_if functions (trace_if, debug_if, info_if,..) because they are redundant and confusing way to preform if 2017-11-25 15:19:41 +02:00
gabime
9a8f5c59e2 Added .idea/ to .gitignore 2017-11-25 15:11:18 +02:00
gabime
c41b6d28b5 astyle 2017-11-24 20:59:58 +02:00
gabime
fd170b0fe1 catch(...) exceptions, report it, and rethrow 2017-11-24 20:58:43 +02:00
Gabi Melman
587b528292 Merge pull request #556 from jpcima/syslog-build
correct include path for sink/syslog_sink.h
2017-11-14 16:29:20 +02:00
Gabi Melman
36c8e79a4a Merge pull request #555 from jpcima/windebug-sink
accept msvc_sink on all compilers, add name windebug_sink (fixes #554)
2017-11-14 16:28:39 +02:00
JP Cimalando
ecec210d0e accept msvc_sink on all compilers, add name windebug_sink (fixes #554) 2017-11-14 14:41:31 +01:00
JP Cimalando
76d2620dad correct include path for sink/syslog_sink.h 2017-11-14 14:25:43 +01:00
gabime
8ca1d84a32 Removed catch(..) from the codebase. Catch only std::exception 2017-11-12 19:46:15 +02:00
Gabi Melman
63d7c64618 Merge pull request #551 from daylanKifky/modified_includes
modified path on quoted #includes
2017-11-11 16:50:06 +02:00
daylanKifky
10772eadae fix wincolor_sink's common.h include 2017-11-11 15:38:08 +01:00
daylanKifky
b220bbb349 fix printf include 2017-11-11 15:21:34 +01:00
daylanKifky
5153b44507 minor fixes 2017-11-11 14:06:01 +01:00
daylanKifky
27e7412640 modified path on quoted #includes
Paths pointing to the root of the library where replaced for ones relatives to each file.

For example, inside /include/spdlog/details/file_helper.h:

This will look for os.h in /include/spdlog/details/spdlog/details/ which doesn't exists.

replaced with:
2017-11-11 13:44:27 +01:00
gabime
93be7713e0 astyle 2017-11-06 12:39:04 +02:00
Gabi Melman
6ab2f0e099 Merge pull request #548 from Subenle/master
Declare variables as size_t rather than unsigned.
2017-11-06 12:37:04 +02:00
Subenlele
34a9f24dba Declare variables as size_t rather than unsigned.
Modify `unsigned front, front1, back;` to `size_t front, front1, back;`
2017-11-05 20:59:37 -06:00
gabime
f70b2ef3b8 Fixed cygwin support 2017-11-05 01:17:21 +02:00
gabime
79e97fa1ec Added the license file of the fmtlib in the bundled folder 2017-11-05 00:34:16 +02:00
gabime
a66e9bbaf1 Minor fix in comment 2017-11-05 00:29:19 +02:00
gabime
f5fe681a41 Fixed issue #546 by adding an "is_empty" method to the queue instead of the buggy approx_size 2017-11-05 00:21:00 +02:00
Gabi Melman
6fd5f3c866 Merge pull request #545 from costinm/patch-1
Allow compilation on platforms with unwind (android)
2017-11-04 10:24:43 +02:00
Costin Manolache
f4f3e3fb66 Use __ANDROID__
Based on review feedback.
2017-11-03 19:37:38 -07:00
Costin Manolache
23dd8d3559 Allow compilation on platforms with unwind (android) 2017-11-02 17:12:08 -07:00
Gabi Melman
47c17be9a1 Merge pull request #457 from rkollataj/appveyor
Adding additional build environments for AppVeyor
2017-10-25 13:09:09 +03:00
Gabi Melman
1f3d939009 Merge pull request #538 from berkus/patch-1
Fix typos. Thanks @berkus
2017-10-25 12:59:38 +03:00
Gabi Melman
fbb8244f7d Merge pull request #533 from manuel-schiller/patch-1
rethrow unwind exception
2017-10-25 12:57:40 +03:00
manuel-schiller
039b34e83a rethrow unwind exception
On Linux with pthread library spdlog causes an SIGABORT and crashes
the application in case it catches a thread specific cancellation
exception in a critical execution phase while in a try/catch block
in spdlog/detail/logger_impl.h

The exception is caught by some general catch(...) clause where
it is NOT rethrown.

However rethrowing these kind of exception is mandatory, otherwise
an abort will be caused by the glibc.
2017-10-25 10:15:27 +02:00
Gabi Melman
69e6af0bf9 Merge pull request #539 from knowledge4igor/fix_cast_warnings
Fix warnings which are caused by C style cast
2017-10-25 04:36:18 +03:00
knowledge4igor
147bdbf591 Fix warnings which are caused by C style cast 2017-10-25 00:40:42 +03:00
Berkus Decker
4974743ee8 Make short month names match in length 2017-10-24 14:10:58 +03:00
Berkus Decker
1c8cc65f6c Fix typos 2017-10-24 14:10:29 +03:00
Berkus Decker
a6b5ef55a4 Fix typo 2017-10-24 13:34:58 +03:00
Berkus Decker
cefea324cb Fix typos 2017-10-24 12:03:59 +03:00
Gabi Melman
7f953c115d Merge pull request #535 from vitor-alves/master
Update README.md
2017-10-17 10:04:18 +03:00
Vitor Alves
20a4143537 Update README.md
Updated install section for Arch Linux. Added yaourt instead of pacman.
2017-10-16 22:23:19 -02:00
gabime
87a44555b6 fixed macro tests 2017-10-13 02:15:49 +03:00
gabime
21ed31844c fixed bug in SPDLOG_TRACE_IF macro and added some related tests 2017-10-13 02:04:31 +03:00
gabime
4a159ad66d Fixed unused variable warning 2017-10-12 19:59:14 +03:00
gabime
19264b8399 Merge branch 'master' of https://github.com/gabime/spdlog 2017-10-12 19:53:07 +03:00
gabime
709948ff4a Fixed issue #527 2017-10-12 19:48:04 +03:00
Gabi Melman
30d5fd16d7 Update README.md 2017-10-12 19:06:01 +03:00
Gabi Melman
9688689938 Merge pull request #531 from fogo/printf
Support for printf-style logging
2017-10-10 13:56:37 +03:00
fogo
e8b7f4194a Moved printf include to fmt.h 2017-10-09 20:05:20 -03:00
fogo
ee525f9bef allow printf style logging
* tests updated for printf style
* tests makefile support printf style
2017-10-05 10:54:06 -03:00
fogo
552d6214e0 updated bundled fmt to contain printf 2017-10-05 08:19:53 -03:00
Gabi Melman
dc8ac4c671 Update tweakme.h 2017-09-29 22:43:48 +03:00
Gabi Melman
cd6a8c9a4c Merge pull request #524 from 2Park/master
Fix SPDLOG_WCHAR_TO_UTF8_SUPPORT wchar_t logging
2017-09-28 21:44:55 +03:00
John Andre Hestad
375b7fdda5 Fix SPDLOG_WCHAR_TO_UTF8_SUPPORT wchar_t logging 2017-09-28 14:19:04 +02:00
Gabi Melman
3a21b765cb Merge pull request #522 from jasonbeach/feat_add_epoch_formatter
add formatter for unix epoch time in seconds.
2017-09-18 09:46:16 +03:00
Jason Beach
f7fabfb2c4 add formatter for unix epoch time in seconds. 2017-09-17 22:11:23 -04:00
Gabi Melman
a4e6d8877c Update README.md 2017-08-29 12:00:10 +03:00
Gabi Melman
e303f02cce Merge pull request #515 from elelel/trace
Compiler-dependent line numbering
2017-08-25 21:34:23 +03:00
El El
b242fb087d Compiler-dependent line numbering 2017-08-25 15:19:29 +00:00
Gabi Melman
02b9f970d3 Merge pull request #514 from stackforce/feature/improve-cmake-lists
Feature/improve cmake lists
2017-08-25 17:11:33 +03:00
Adrian Antonana
e6b9fa577d cmake: set project version to 0.14.0 2017-08-25 09:12:15 +02:00
Adrian Antonana
fe2fa4087d cmake: add some small comments to point out whats being done 2017-08-24 16:57:07 +02:00
Adrian Antonana
ab25004242 cmake: add some commend blocks to clearly differentiate CMakeLists file sections 2017-08-24 16:55:55 +02:00
Adrian Antonana
5504630e46 cmake: improve CMakeLists.txt
* support CMake user registry package registration
* use GNUInstallDirs to set installation firectories
* use spdlog namespace in both build and install interfaces
2017-08-24 16:55:06 +02:00
Gabi Melman
c4d93ae0b5 Merge pull request #513 from Lectem/patch-1
SPDLOG_BUILD_TESTING now depends on BUILD_TESTING
2017-08-23 17:35:04 +03:00
Lectem
47cf62f878 SPDLOG_BUILD_TESTING now depends on BUILD_TESTING
I encountered an issue when using spdlog through add_subdirectory.
Since SPDLOG_BUILD_TESTING is ON by default, it now adds tests to my project, even if BUILD_TESTING (the official CTest variable) is set to OFF.
cmake_dependent_option makes it so that if someone enables BUILD_TESTING then SPDLOG_BUILD_TESTING will be set to ON by default.
This way one can disable all external tests by setting BUILD_TESTING before using add_subdirectory and then setting it back to its original value.
The only change for those using the library directly is that they now use BUILD_TESTING instead of SPDLOG_BUILD_TESTING when configuring.
2017-08-23 15:12:40 +02:00
Remigiusz Kołłątaj
2e84ca4fa4 Adding additional build environments for AppVeyor
Signed-off-by: Remigiusz Kołłątaj <remigiusz.kollataj@gmail.com>
2017-06-09 19:11:29 +02:00
123 changed files with 14332 additions and 10243 deletions

108
.clang-format Normal file
View File

@@ -0,0 +1,108 @@
---
Language: Cpp
# BasedOnStyle: LLVM
AccessModifierOffset: -4
AlignAfterOpenBracket: DontAlign
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: true
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterEnum: true
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: true
AfterStruct: true
AfterUnion: true
BeforeCatch: true
BeforeElse: true
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BreakBeforeInheritanceComma: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: true
BreakConstructorInitializers: BeforeColon
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 140
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
- Regex: '.*'
Priority: 1
IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false
IndentWidth: 4
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 8
UseTab: Never
...

6
.gitignore vendored
View File

@@ -1,4 +1,5 @@
# Auto generated files # Auto generated files
build/*
*.slo *.slo
*.lo *.lo
*.o *.o
@@ -61,4 +62,7 @@ install_manifest.txt
/tests/tests.VC.VC.opendb /tests/tests.VC.VC.opendb
/tests/tests.VC.db /tests/tests.VC.db
/tests/tests /tests/tests
/tests/logs/file_helper_test.txt /tests/logs/*
# idea
.idea/

View File

@@ -85,6 +85,7 @@ script:
- ./"${BIN}" - ./"${BIN}"
- valgrind --trace-children=yes --leak-check=full ./"${BIN}" - valgrind --trace-children=yes --leak-check=full ./"${BIN}"
- cd $CHECKOUT_PATH/tests; make rebuild; ./tests - cd $CHECKOUT_PATH/tests; make rebuild; ./tests
- cd $CHECKOUT_PATH/tests; STYLE=printf make rebuild; ./tests
notifications: notifications:
email: false email: false

View File

@@ -4,26 +4,37 @@
# #
cmake_minimum_required(VERSION 3.1) cmake_minimum_required(VERSION 3.1)
project(spdlog VERSION 1.0.0) project(spdlog VERSION 1.0.0 LANGUAGES CXX)
include(CTest) include(CTest)
include(CMakeDependentOption)
include(GNUInstallDirs)
#---------------------------------------------------------------------------------------
# compiler config
#---------------------------------------------------------------------------------------
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
set(CMAKE_CXX_FLAGS "-Wall ${CMAKE_CXX_FLAGS}") set(CMAKE_CXX_FLAGS "-Wall -O3 ${CMAKE_CXX_FLAGS}")
endif() endif()
#---------------------------------------------------------------------------------------
# spdlog target
#---------------------------------------------------------------------------------------
add_library(spdlog INTERFACE) add_library(spdlog INTERFACE)
option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF) option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF)
option(SPDLOG_BUILD_TESTING "Build spdlog tests" ON) cmake_dependent_option(SPDLOG_BUILD_TESTING
"Build spdlog tests" ON
"BUILD_TESTING" OFF
)
target_include_directories( target_include_directories(
spdlog spdlog
INTERFACE INTERFACE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>" "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
"$<INSTALL_INTERFACE:include>" "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
) )
set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include") set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include")
@@ -36,52 +47,69 @@ if(SPDLOG_BUILD_TESTING)
add_subdirectory(tests) add_subdirectory(tests)
endif() endif()
### Install ### #---------------------------------------------------------------------------------------
# * https://github.com/forexample/package-example # Install/export targets and files
set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated") #---------------------------------------------------------------------------------------
# set files and directories
set(config_install_dir "lib/cmake/${PROJECT_NAME}") set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
set(include_install_dir "include") set(include_install_dir "${CMAKE_INSTALL_INCLUDEDIR}")
set(pkgconfig_install_dir "lib/pkgconfig") set(pkgconfig_install_dir "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
set(version_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") set(project_config "${PROJECT_NAME}Config.cmake")
set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake") set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc")
set(pkg_config "${generated_dir}/${PROJECT_NAME}.pc")
set(targets_export_name "${PROJECT_NAME}Targets") set(targets_export_name "${PROJECT_NAME}Targets")
set(namespace "${PROJECT_NAME}::") set(namespace "${PROJECT_NAME}::")
# generate package version file
include(CMakePackageConfigHelpers) include(CMakePackageConfigHelpers)
write_basic_package_version_file( write_basic_package_version_file(
"${version_config}" COMPATIBILITY SameMajorVersion "${version_config}" COMPATIBILITY SameMajorVersion
) )
# Note: use 'targets_export_name' # configure pkg config file
configure_file("cmake/Config.cmake.in" "${project_config}" @ONLY)
configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY) configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY)
# install targets
install( install(
TARGETS spdlog TARGETS spdlog
EXPORT "${targets_export_name}" EXPORT "${targets_export_name}"
INCLUDES DESTINATION "${include_install_dir}"
) )
install(DIRECTORY "include/spdlog" DESTINATION "${include_install_dir}") # install headers
install( install(
FILES "${project_config}" "${version_config}" DIRECTORY "${HEADER_BASE}/${PROJECT_NAME}"
DESTINATION "${include_install_dir}"
)
# install project version file
install(
FILES "${version_config}"
DESTINATION "${config_install_dir}" DESTINATION "${config_install_dir}"
) )
# install pkg config file
install( install(
FILES "${pkg_config}" FILES "${pkg_config}"
DESTINATION "${pkgconfig_install_dir}" DESTINATION "${pkgconfig_install_dir}"
) )
# install project config file
install( install(
EXPORT "${targets_export_name}" EXPORT "${targets_export_name}"
NAMESPACE "${namespace}" NAMESPACE "${namespace}"
DESTINATION "${config_install_dir}" DESTINATION "${config_install_dir}"
FILE ${project_config}
) )
# export build directory config file
export(
EXPORT ${targets_export_name}
NAMESPACE "${namespace}"
FILE ${project_config}
)
# register project in CMake user registry
export(PACKAGE ${PROJECT_NAME})
file(GLOB_RECURSE spdlog_include_SRCS "${HEADER_BASE}/*.h") file(GLOB_RECURSE spdlog_include_SRCS "${HEADER_BASE}/*.h")
add_custom_target(spdlog_headers_for_ide SOURCES ${spdlog_include_SRCS}) add_custom_target(spdlog_headers_for_ide SOURCES ${spdlog_include_SRCS})

276
README.md
View File

@@ -6,22 +6,22 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
## Install ## Install
#### Just copy the headers: #### Just copy the headers:
* Copy the source [folder](https://github.com/gabime/spdlog/tree/master/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.
#### Or use your favourite package manager: #### Or use your favorite package manager:
* Ubuntu: `apt-get install libspdlog-dev` * Ubuntu: `apt-get install libspdlog-dev`
* Homebrew: `brew install spdlog` * Homebrew: `brew install spdlog`
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean` * FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
* Fedora: `yum install spdlog` * Fedora: `yum install spdlog`
* Gentoo: `emerge dev-libs/spdlog` * Gentoo: `emerge dev-libs/spdlog`
* Arch Linux: `pacman -S spdlog-git` * Arch Linux: `yaourt -S spdlog-git`
* vcpkg: `vcpkg install spdlog` * vcpkg: `vcpkg install spdlog`
## Platforms ## Platforms
* Linux, FreeBSD, Solaris * Linux, FreeBSD, Solaris, AIX
* Windows (vc 2013+, cygwin/mingw) * Windows (vc 2013+, cygwin)
* Mac OSX (clang 3.5+) * Mac OSX (clang 3.5+)
* Android * Android
@@ -29,7 +29,7 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below). * Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
* Headers only, just copy and use. * Headers only, just copy and use.
* Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library. * Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library.
* Extremely fast asynchronous mode (optional) - using lockfree queues and other tricks to reach millions of calls/sec. * Fast asynchronous mode (optional)
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting. * [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
* Conditional Logging * Conditional Logging
* Multi/Single threaded loggers. * Multi/Single threaded loggers.
@@ -46,56 +46,50 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
## Benchmarks ## Benchmarks
Below are some [benchmarks](bench) comparing popular log libraries under Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz 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 #### Synchronous mode
Time needed to log 1,000,000 lines in synchronous mode (in seconds, the best of 3 runs): ```
*******************************************************************************
|threads|boost log 1.54|glog |easylogging |spdlog| Single thread, 1,000,000 iterations
|-------|:-------:|:-----:|----------:|------:| *******************************************************************************
|1| 4.169s |1.066s |0.975s |0.302s| basic_st... Elapsed: 0.231041 4,328,228/sec
|10| 6.180s |3.032s |2.857s |0.968s| rotating... Elapsed: 0.233466 4,283,282/sec
|100| 5.981s |1.139s |4.512s |0.497s| daily_st... Elapsed: 0.244491 4,090,136/sec
null_st... Elapsed: 0.162708 6,145,995/sec
*******************************************************************************
10 threads sharing same logger, 1,000,000 iterations
*******************************************************************************
basic_mt... Elapsed: 0.854029 1,170,920/sec
rotating_mt Elapsed: 0.867038 1,153,351/sec
daily_mt... Elapsed: 0.869593 1,149,963/sec
null_mt... Elapsed: 0.171215 2,033,537/sec
```
#### Asynchronous mode #### Asynchronous mode
Time needed to log 1,000,000 lines in asynchronous mode, i.e. the time it takes to put them in the async queue (in seconds, the best of 3 runs): ```
*******************************************************************************
10 threads sharing same logger, 1,000,000 iterations
*******************************************************************************
async... Elapsed: 0.442731 2,258,706/sec
async... Elapsed: 0.427072 2,341,527/sec
async... Elapsed: 0.449768 2,223,369/sec
```
|threads|g2log <sup>async logger</sup> |spdlog <sup>async mode</sup>| ## Usage samples
|:-------|:-----:|-------------------------:|
|1| 1.850s |0.216s |
|10| 0.943s |0.173s|
|100| 0.959s |0.202s|
## Usage Example
```c++ ```c++
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include <iostream> void stdout_example()
#include <memory>
void async_example();
void syslog_example();
void user_defined_example();
void err_handler_example();
namespace spd = spdlog;
int main(int, char*[])
{ {
try // create color multi threaded logger
{ auto console = spdlog::stdout_color_mt("console");
// Console logger with color
auto console = spd::stdout_color_mt("console");
console->info("Welcome to spdlog!"); console->info("Welcome to spdlog!");
console->error("Some error message with arg{}..", 1); console->error("Some error message with arg: {}", 1);
// Conditional logging example auto err_logger = spdlog::stderr_color_mt("stderr");
auto i = 2; err_logger->error("Some error message");
console->warn_if(i != 0, "an important message");
// Formatting examples // Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12); console->warn("Easy padding in numbers like {:08d}", 12);
@@ -104,93 +98,114 @@ int main(int, char*[])
console->info("Positional args are {1} {0}..", "too", "supported"); console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned"); console->info("{:<30}", "left aligned");
spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
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.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/mylogfile", 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", 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 // Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info spdlog::set_level(spdlog::level::info); // Set global log level to info
console->debug("This message shold not be displayed!"); console->debug("This message should not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level console->set_level(spdlog::level::trace); // Set specific logger's log level
console->debug("This message shold be displayed.."); console->debug("This message should be displayed..");
// Customize msg format for all loggers
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
console->info("This an info message with custom format");
// Compile time log levels // Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON // define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23); SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_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<spd::logger> l)
{
l->info("End of example.");
});
// Release and close all loggers
spd::drop_all();
} }
// Exceptions will only be thrown upon failed logger or sink construction (not during logging) ```
catch (const spd::spdlog_ex& ex) ---
#### Basic file logger
```c++
#include "spdlog/sinks/basic_file_sink.h"
void basic_logfile_example()
{
try
{
auto my_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
}
catch (const spdlog::spdlog_ex &ex)
{ {
std::cout << "Log init failed: " << ex.what() << std::endl; std::cout << "Log init failed: " << ex.what() << std::endl;
return 1; return 1;
} }
} }
```
---
#### Rotating files
```c++
#include "spdlog/sinks/rotating_file_sink.h"
void rotating_example()
{
// Create a file rotating logger with 5mb size max and 3 rotated files
auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
}
```
---
#### Daily files
```c++
#include "spdlog/sinks/daily_file_sink.h"
void daily_example()
{
// Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
}
```
---
#### Periodic flush
```c++
// periodically flush all *registered* loggers every 3 seconds:
// warning: only use if all your loggers are thread safe!
spdlog::flush_every(std::chrono::seconds(3));
```
---
#### Asynchronous logging
```c++
#include "spdlog/async.h"
void async_example() void async_example()
{ {
size_t q_size = 4096; //queue size must be power of 2 // default thread pool settings can be modified *before* creating the async logger:
spd::set_async_mode(q_size); // spdlog::init_thread_pool(8192, 1); // queue with 8k items and 1 backing thread.
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt"); auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
for (int i = 0; i < 100; ++i) // alternatively:
async_file->info("Async message #{}", i); // auto async_file = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");
} }
//syslog example ```
void syslog_example() ---
#### Logger with multi targets - each with different format and log level
```c++
// create logger with 2 targets with different log levels and formats.
// the console will show only warnings or errors, while the file will log all.
void multi_sink_example()
{ {
#ifdef SPDLOG_ENABLE_SYSLOG auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
std::string ident = "spdlog-example"; console_sink->set_level(spdlog::level::warn);
auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID); console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v");
syslog_logger->warn("This is warning that will end up in syslog..");
#endif
}
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/multisink.txt", true);
file_sink->set_level(spdlog::level::trace);
spdlog::logger logger("multi_sink", {console_sink, file_sink});
logger.set_level(spdlog::level::debug);
logger.warn("this should appear in both console and file");
logger.info("this message should not appear in the console, only in the file");
}
```
---
#### User defined types
```c++
// user defined types logging by implementing operator<< // user defined types logging by implementing operator<<
#include "spdlog/fmt/ostr.h" // must be included
struct my_type struct my_type
{ {
int i; int i;
@@ -201,23 +216,44 @@ struct my_type
} }
}; };
#include <spdlog/fmt/ostr.h> // must be included
void user_defined_example() void user_defined_example()
{ {
spd::get("console")->info("user defined type: {}", my_type { 14 }); spdlog::get("console")->info("user defined type: {}", my_type{14});
} }
// ```
//custom error handler ---
// #### Custom error handler
```c++
void err_handler_example() void err_handler_example()
{ {
spd::set_error_handler([](const std::string& msg) { // can be set globally or per logger(logger->set_error_handler(..))
std::cerr << "my err handler: " << msg << std::endl; spdlog::set_error_handler([](const std::string &msg) { spdlog::get("console")->error("*** LOGGER ERROR ***: {}", msg); });
}); spdlog::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
// (or logger->set_error_handler(..) to set for specific logger)
} }
```
---
#### syslog
```c++
#include "spdlog/sinks/syslog_sink.h"
void syslog_example()
{
std::string ident = "spdlog-example";
auto syslog_logger = spdlog::syslog_logger("syslog", ident, LOG_PID);
syslog_logger->warn("This is warning that will end up in syslog.");
}
```
---
#### Android example
```c++
#incude "spdlog/sinks/android_sink.h"
void android_example()
{
std::string tag = "spdlog-android";
auto android_logger = spdlog::android_logger("android", tag);
android_logger->critical("Use \"adb shell logcat\" to view this message.");
}
``` ```
## Documentation ## Documentation

32
appveyor.yml Normal file
View File

@@ -0,0 +1,32 @@
version: 1.0.{build}
image: Visual Studio 2015
environment:
matrix:
- GENERATOR: '"MinGW Makefiles"'
BUILD_TYPE: Debug
- GENERATOR: '"MinGW Makefiles"'
BUILD_TYPE: Release
- GENERATOR: '"Visual Studio 14 2015"'
BUILD_TYPE: Debug
- GENERATOR: '"Visual Studio 14 2015"'
BUILD_TYPE: Release
- GENERATOR: '"Visual Studio 14 2015 Win64"'
BUILD_TYPE: Debug
- GENERATOR: '"Visual Studio 14 2015 Win64"'
BUILD_TYPE: Release
build_script:
- cmd: >-
set
mkdir build
cd build
set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
set PATH=C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin;%PATH%
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE%
cmake --build . --config %BUILD_TYPE%
test: off

View File

@@ -1,5 +0,0 @@
#!/bin/bash
find . -name "*\.h" -o -name "*\.cpp"|xargs dos2unix
find . -name "*\.h" -o -name "*\.cpp"|xargs astyle -n -c -A1

View File

@@ -1,62 +1,27 @@
CXX ?= g++ CXX ?= g++
CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -I../include CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1
CXX_RELEASE_FLAGS = -O3 -flto -DNDEBUG CXX_RELEASE_FLAGS = -Ofast -flto -Wl,--no-as-needed
binaries=spdlog-bench spdlog-bench-mt spdlog-async spdlog-null-async boost-bench boost-bench-mt glog-bench glog-bench-mt g2log-async easylogging-bench easylogging-bench-mt binaries=bench latency async_bench
all: $(binaries) all: $(binaries)
spdlog-bench: spdlog-bench.cpp bench: bench.cpp
$(CXX) spdlog-bench.cpp -o spdlog-bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS) $(CXX) bench.cpp -o bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
spdlog-bench-mt: spdlog-bench-mt.cpp
$(CXX) spdlog-bench-mt.cpp -o spdlog-bench-mt $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
spdlog-async: spdlog-async.cpp
$(CXX) spdlog-async.cpp -o spdlog-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
spdlog-null-async: spdlog-null-async.cpp async_bench: async_bench.cpp
$(CXX) spdlog-null-async.cpp -o spdlog-null-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS) $(CXX) async_bench.cpp -o async_bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
latency: latency.cpp
$(CXX) latency.cpp -o latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
BOOST_FLAGS = -DBOOST_LOG_DYN_LINK -I/usr/include -lboost_log -lboost_log_setup -lboost_filesystem -lboost_system -lboost_thread -lboost_regex -lboost_date_time -lboost_chrono
boost-bench: boost-bench.cpp
$(CXX) boost-bench.cpp -o boost-bench $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
boost-bench-mt: boost-bench-mt.cpp
$(CXX) boost-bench-mt.cpp -o boost-bench-mt $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
GLOG_FLAGS = -lglog
glog-bench: glog-bench.cpp
$(CXX) glog-bench.cpp -o glog-bench $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
glog-bench-mt: glog-bench-mt.cpp
$(CXX) glog-bench-mt.cpp -o glog-bench-mt $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
G2LOG_FLAGS = -I/home/gabi/devel/g2log/g2log/src -L/home/gabi/devel/g2log/g2log -llib_g2logger
g2log-async: g2log-async.cpp
$(CXX) g2log-async.cpp -o g2log-async $(CXXFLAGS) $(G2LOG_FLAGS) $(CXX_RELEASE_FLAGS)
EASYL_FLAGS = -I../../easylogging/src/
easylogging-bench: easylogging-bench.cpp
$(CXX) easylogging-bench.cpp -o easylogging-bench $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
easylogging-bench-mt: easylogging-bench-mt.cpp
$(CXX) easylogging-bench-mt.cpp -o easylogging-bench-mt $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
.PHONY: clean .PHONY: clean
clean: clean:
rm -f *.o logs/* $(binaries) rm -f *.o logs/* $(binaries)
rebuild: clean all rebuild: clean all

View File

@@ -1,57 +0,0 @@
CXX ?= g++
CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
CXX_RELEASE_FLAGS = -O3 -flto
binaries=spdlog-bench spdlog-bench-mt spdlog-async boost-bench boost-bench-mt glog-bench glog-bench-mt g2log-async easylogging-bench easylogging-bench-mt
all: $(binaries)
spdlog-bench: spdlog-bench.cpp
$(CXX) spdlog-bench.cpp -o spdlog-bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
spdlog-bench-mt: spdlog-bench-mt.cpp
$(CXX) spdlog-bench-mt.cpp -o spdlog-bench-mt $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
spdlog-async: spdlog-async.cpp
$(CXX) spdlog-async.cpp -o spdlog-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
BOOST_FLAGS = -DBOOST_LOG_DYN_LINK -I/home/gabi/devel/boost_1_56_0/ -L/home/gabi/devel/boost_1_56_0/stage/lib -lboost_log -lboost_log_setup -lboost_filesystem -lboost_system -lboost_thread -lboost_regex -lboost_date_time -lboost_chrono
boost-bench: boost-bench.cpp
$(CXX) boost-bench.cpp -o boost-bench $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
boost-bench-mt: boost-bench-mt.cpp
$(CXX) boost-bench-mt.cpp -o boost-bench-mt $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
GLOG_FLAGS = -lglog
glog-bench: glog-bench.cpp
$(CXX) glog-bench.cpp -o glog-bench $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
glog-bench-mt: glog-bench-mt.cpp
$(CXX) glog-bench-mt.cpp -o glog-bench-mt $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
G2LOG_FLAGS = -I/home/gabi/devel/g2log/g2log/src -L/home/gabi/devel/g2log/g2log -llib_g2logger
g2log-async: g2log-async.cpp
$(CXX) g2log-async.cpp -o g2log-async $(CXXFLAGS) $(G2LOG_FLAGS) $(CXX_RELEASE_FLAGS)
EASYL_FLAGS = -I../../easylogging/src/
easylogging-bench: easylogging-bench.cpp
$(CXX) easylogging-bench.cpp -o easylogging-bench $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
easylogging-bench-mt: easylogging-bench-mt.cpp
$(CXX) easylogging-bench-mt.cpp -o easylogging-bench-mt $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
.PHONY: clean
clean:
rm -f *.o logs/* $(binaries)
rebuild: clean all

138
bench/async_bench.cpp Normal file
View File

@@ -0,0 +1,138 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
//
// bench.cpp : spdlog benchmarks
//
#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/spdlog.h"
#include "utils.h"
#include <atomic>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
using namespace std;
using namespace std::chrono;
using namespace spdlog;
using namespace spdlog::sinks;
using namespace utils;
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
int count_lines(const char *filename)
{
int counter = 0;
auto *infile = fopen(filename, "r");
int ch;
while (EOF != (ch = getc(infile)))
{
if ('\n' == ch)
counter++;
}
return counter;
}
int main(int argc, char *argv[])
{
int howmany = 1000000;
int queue_size = howmany + 2;
int threads = 10;
int iters = 3;
try
{
auto console = spdlog::stdout_color_mt("console");
if (argc == 1)
{
console->set_pattern("%v");
console->info("Usage: {} <message_count> <threads> <q_size> <iterations>", argv[0]);
return 0;
}
if (argc > 1)
howmany = atoi(argv[1]);
if (argc > 2)
threads = atoi(argv[2]);
if (argc > 3)
queue_size = atoi(argv[3]);
if (argc > 4)
iters = atoi(argv[4]);
console->info("-------------------------------------------------");
console->info("Messages: {:14n}", howmany);
console->info("Threads : {:14n}", threads);
console->info("Queue : {:14n}", queue_size);
console->info("Iters : {:>14n}", iters);
console->info("-------------------------------------------------");
const char *filename = "logs/basic_async.log";
for (int i = 0; i < iters; i++)
{
auto tp = std::make_shared<details::thread_pool>(queue_size, 1);
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
auto logger = std::make_shared<async_logger>("async_logger", std::move(file_sink), std::move(tp), async_overflow_policy::block);
bench_mt(howmany, std::move(logger), threads);
auto count = count_lines(filename);
if (count != howmany)
{
console->error("Test failed. {} has {:n} lines instead of {:n}", filename, count, howmany);
exit(1);
}
else
{
console->info("Line count OK ({:n})\n", count);
}
}
}
catch (std::exception &ex)
{
std::cerr << "Error: " << ex.what() << std::endl;
perror("Last error");
return 1;
}
return 0;
}
void thread_fun(std::shared_ptr<spdlog::logger> logger, int howmany)
{
for (int i = 0; i < howmany; i++)
{
logger->info("Hello logger: msg number {}", i);
}
}
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> logger, int thread_count)
{
using std::chrono::high_resolution_clock;
vector<thread> threads;
auto start = high_resolution_clock::now();
int msgs_per_thread = howmany / thread_count;
int msgs_per_thread_mod = howmany % thread_count;
for (int t = 0; t < thread_count; ++t)
{
if (t == 0 && msgs_per_thread_mod)
threads.push_back(std::thread(thread_fun, logger, msgs_per_thread + msgs_per_thread_mod));
else
threads.push_back(std::thread(thread_fun, logger, msgs_per_thread));
}
for (auto &t : threads)
{
t.join();
};
auto delta = high_resolution_clock::now() - start;
auto delta_d = duration_cast<duration<double>>(delta).count();
spdlog::get("console")->info("Elapsed: {} secs\t {:n}/sec", delta_d, int(howmany / delta_d));
}

View File

@@ -6,18 +6,19 @@
// //
// bench.cpp : spdlog benchmarks // bench.cpp : spdlog benchmarks
// //
#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/null_sink.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/spdlog.h"
#include "utils.h"
#include <atomic> #include <atomic>
#include <cstdlib> // EXIT_FAILURE #include <cstdlib> // EXIT_FAILURE
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <string> #include <string>
#include <thread> #include <thread>
#include "spdlog/spdlog.h"
#include "spdlog/async_logger.h"
#include "spdlog/sinks/file_sinks.h"
#include "spdlog/sinks/null_sink.h"
#include "utils.h"
using namespace std; using namespace std;
using namespace std::chrono; using namespace std::chrono;
@@ -25,15 +26,14 @@ using namespace spdlog;
using namespace spdlog::sinks; using namespace spdlog::sinks;
using namespace utils; using namespace utils;
void bench(int howmany, std::shared_ptr<spdlog::logger> log); void bench(int howmany, std::shared_ptr<spdlog::logger> log);
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count); void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
int queue_size = 1048576;
int howmany = 1000000; int howmany = 1000000;
int queue_size = howmany + 2;
int threads = 10; int threads = 10;
int file_size = 30 * 1024 * 1024; int file_size = 30 * 1024 * 1024;
int rotating_files = 5; int rotating_files = 5;
@@ -48,41 +48,51 @@ int main(int argc, char* argv[])
if (argc > 3) if (argc > 3)
queue_size = atoi(argv[3]); queue_size = atoi(argv[3]);
cout << "******************************************************************"
cout << "*******************************************************************************\n"; "*************\n";
cout << "Single thread, " << format(howmany) << " iterations" << endl; cout << "Single thread, " << format(howmany) << " iterations" << endl;
cout << "*******************************************************************************\n"; cout << "******************************************************************"
"*************\n";
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st", file_size, rotating_files); auto basic_st = spdlog::basic_logger_mt("basic_st", "logs/basic_st.log", true);
bench(howmany, basic_st);
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
bench(howmany, rotating_st); bench(howmany, rotating_st);
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st");
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
bench(howmany, daily_st); bench(howmany, daily_st);
bench(howmany, spdlog::create<null_sink_st>("null_st")); bench(howmany, spdlog::create<null_sink_st>("null_st"));
cout << "\n*******************************************************************************\n"; cout << "\n****************************************************************"
"***************\n";
cout << threads << " threads sharing same logger, " << format(howmany) << " iterations" << endl; cout << threads << " threads sharing same logger, " << format(howmany) << " iterations" << endl;
cout << "*******************************************************************************\n"; cout << "******************************************************************"
"*************\n";
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt", file_size, rotating_files); auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
bench_mt(howmany, basic_mt, threads);
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files);
bench_mt(howmany, rotating_mt, threads); bench_mt(howmany, rotating_mt, threads);
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt");
bench_mt(howmany, daily_mt, threads); bench_mt(howmany, daily_mt, threads);
bench(howmany, spdlog::create<null_sink_st>("null_mt")); bench_mt(howmany, spdlog::create<null_sink_mt>("null_mt"), threads);
cout << "\n*******************************************************************************\n"; cout << "\n****************************************************************"
"***************\n";
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations " << endl; cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations " << endl;
cout << "*******************************************************************************\n"; cout << "******************************************************************"
"*************\n";
spdlog::set_async_mode(queue_size);
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
{ {
auto as = spdlog::daily_logger_st("as", "logs/daily_async"); spdlog::init_thread_pool(queue_size, 1);
auto as = spdlog::basic_logger_mt<spdlog::async_factory>("async", "logs/basic_async.log", true);
bench_mt(howmany, as, threads); bench_mt(howmany, as, threads);
spdlog::drop("as"); spdlog::drop("async");
} }
} }
catch (std::exception &ex) catch (std::exception &ex)
@@ -94,51 +104,45 @@ int main(int argc, char* argv[])
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
void bench(int howmany, std::shared_ptr<spdlog::logger> log) void bench(int howmany, std::shared_ptr<spdlog::logger> log)
{ {
using std::chrono::high_resolution_clock;
cout << log->name() << "...\t\t" << flush; cout << log->name() << "...\t\t" << flush;
auto start = system_clock::now(); auto start = high_resolution_clock::now();
for (auto i = 0; i < howmany; ++i) for (auto i = 0; i < howmany; ++i)
{ {
log->info("Hello logger: msg number {}", i); log->info("Hello logger: msg number {}", i);
} }
auto delta = high_resolution_clock::now() - start;
auto delta = system_clock::now() - start;
auto delta_d = duration_cast<duration<double>>(delta).count(); auto delta_d = duration_cast<duration<double>>(delta).count();
cout << format(int(howmany / delta_d)) << "/sec" << endl;
}
cout << "Elapsed: " << delta_d << "\t" << format(int(howmany / delta_d)) << "/sec" << endl;
spdlog::drop(log->name());
}
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count) void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
{ {
using std::chrono::high_resolution_clock;
cout << log->name() << "...\t\t" << flush; cout << log->name() << "...\t\t" << flush;
std::atomic<int > msg_counter {0};
vector<thread> threads; vector<thread> threads;
auto start = system_clock::now(); auto start = high_resolution_clock::now();
for (int t = 0; t < thread_count; ++t) for (int t = 0; t < thread_count; ++t)
{ {
threads.push_back(std::thread([&]() threads.push_back(std::thread([&]() {
for (int j = 0; j < howmany / thread_count; j++)
{ {
for(;;) log->info("Hello logger: msg number {}", j);
{
int counter = ++msg_counter;
if (counter > howmany) break;
log->info("Hello logger: msg number {}", counter);
} }
})); }));
} }
for (auto &t : threads) for (auto &t : threads)
{ {
t.join(); t.join();
}; };
auto delta = high_resolution_clock::now() - start;
auto delta = system_clock::now() - start;
auto delta_d = duration_cast<duration<double>>(delta).count(); auto delta_d = duration_cast<duration<double>>(delta).count();
cout << format(int(howmany / delta_d)) << "/sec" << endl; cout << "Elapsed: " << delta_d << "\t" << format(int(howmany / delta_d)) << "/sec" << endl;
} }

View File

@@ -1,84 +0,0 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <thread>
#include <vector>
#include <atomic>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace sinks = boost::log::sinks;
namespace keywords = boost::log::keywords;
void init()
{
logging::add_file_log
(
keywords::file_name = "logs/boost-sample_%N.log", /*< file name pattern >*/
keywords::auto_flush = false,
keywords::format = "[%TimeStamp%]: %Message%"
);
logging::core::get()->set_filter
(
logging::trivial::severity >= logging::trivial::info
);
}
using namespace std;
int main(int argc, char* argv[])
{
int thread_count = 10;
if(argc > 1)
thread_count = atoi(argv[1]);
int howmany = 1000000;
init();
logging::add_common_attributes();
using namespace logging::trivial;
src::severity_logger_mt< severity_level > lg;
std::atomic<int > msg_counter {0};
vector<thread> threads;
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
while (true)
{
int counter = ++msg_counter;
if (counter > howmany) break;
BOOST_LOG_SEV(lg, info) << "boost message #" << counter << ": This is some text for your pleasure";
}
}));
}
for(auto &t:threads)
{
t.join();
};
return 0;
}

View File

@@ -1,47 +0,0 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
namespace logging = boost::log;
namespace src = boost::log::sources;
namespace sinks = boost::log::sinks;
namespace keywords = boost::log::keywords;
void init()
{
logging::add_file_log
(
keywords::file_name = "logs/boost-sample_%N.log", /*< file name pattern >*/
keywords::auto_flush = false,
keywords::format = "[%TimeStamp%]: %Message%"
);
logging::core::get()->set_filter
(
logging::trivial::severity >= logging::trivial::info
);
}
int main(int argc, char* [])
{
int howmany = 1000000;
init();
logging::add_common_attributes();
using namespace logging::trivial;
src::severity_logger_mt< severity_level > lg;
for(int i = 0 ; i < howmany; ++i)
BOOST_LOG_SEV(lg, info) << "boost message #" << i << ": This is some text for your pleasure";
return 0;
}

View File

@@ -1,10 +0,0 @@
* GLOBAL:
FORMAT = "[%datetime]: %msg"
FILENAME = ./logs/easylogging.log
ENABLED = true
TO_FILE = true
TO_STANDARD_OUTPUT = false
MILLISECONDS_WIDTH = 3
PERFORMANCE_TRACKING = false
MAX_LOG_FILE_SIZE = 10485760
Log_Flush_Threshold = 10485760

View File

@@ -1,52 +0,0 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <thread>
#include <vector>
#include <atomic>
#define _ELPP_THREAD_SAFE
#include "easylogging++.h"
_INITIALIZE_EASYLOGGINGPP
using namespace std;
int main(int argc, char* argv[])
{
int thread_count = 10;
if(argc > 1)
thread_count = atoi(argv[1]);
int howmany = 1000000;
// Load configuration from file
el::Configurations conf("easyl.conf");
el::Loggers::reconfigureLogger("default", conf);
std::atomic<int > msg_counter {0};
vector<thread> threads;
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
while (true)
{
int counter = ++msg_counter;
if (counter > howmany) break;
LOG(INFO) << "easylog message #" << counter << ": This is some text for your pleasure";
}
}));
}
for(auto &t:threads)
{
t.join();
};
return 0;
}

View File

@@ -1,22 +0,0 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include "easylogging++.h"
_INITIALIZE_EASYLOGGINGPP
int main(int, char* [])
{
int howmany = 1000000;
// Load configuration from file
el::Configurations conf("easyl.conf");
el::Loggers::reconfigureLogger("default", conf);
for(int i = 0 ; i < howmany; ++i)
LOG(INFO) << "easylog message #" << i << ": This is some text for your pleasure";
return 0;
}

View File

@@ -1,62 +0,0 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <thread>
#include <vector>
#include <atomic>
#include <iostream>
#include <chrono>
#include "g2logworker.h"
#include "g2log.h"
using namespace std;
template<typename T> std::string format(const T& value);
int main(int argc, char* argv[])
{
using namespace std::chrono;
using clock=steady_clock;
int thread_count = 10;
if(argc > 1)
thread_count = atoi(argv[1]);
int howmany = 1000000;
g2LogWorker g2log(argv[0], "logs");
g2::initializeLogging(&g2log);
std::atomic<int > msg_counter {0};
vector<thread> threads;
auto start = clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
while (true)
{
int counter = ++msg_counter;
if (counter > howmany) break;
LOG(INFO) << "g2log message #" << counter << ": This is some text for your pleasure";
}
}));
}
for(auto &t:threads)
{
t.join();
};
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany/deltaf;
cout << "Total: " << howmany << std::endl;
cout << "Threads: " << thread_count << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl;
}

View File

@@ -1,50 +0,0 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <thread>
#include <vector>
#include <atomic>
#include "glog/logging.h"
using namespace std;
int main(int argc, char* argv[])
{
int thread_count = 10;
if(argc > 1)
thread_count = atoi(argv[1]);
int howmany = 1000000;
FLAGS_logtostderr = 0;
FLAGS_log_dir = "logs";
google::InitGoogleLogging(argv[0]);
std::atomic<int > msg_counter {0};
vector<thread> threads;
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
while (true)
{
int counter = ++msg_counter;
if (counter > howmany) break;
LOG(INFO) << "glog message #" << counter << ": This is some text for your pleasure";
}
}));
}
for(auto &t:threads)
{
t.join();
};
return 0;
}

View File

@@ -1,21 +0,0 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include "glog/logging.h"
int main(int, char* argv[])
{
int howmany = 1000000;
FLAGS_logtostderr = 0;
FLAGS_log_dir = "logs";
google::InitGoogleLogging(argv[0]);
for(int i = 0 ; i < howmany; ++i)
LOG(INFO) << "glog message # " << i << ": This is some text for your pleasure";
return 0;
}

151
bench/latency.cpp Normal file
View File

@@ -0,0 +1,151 @@
//
// Copyright(c) 2018 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
//
// latency.cpp : spdlog latency benchmarks
//
#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/null_sink.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/spdlog.h"
#include "utils.h"
#include <atomic>
#include <cstdlib>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
using namespace std;
using namespace std::chrono;
using namespace spdlog;
using namespace spdlog::sinks;
using namespace utils;
void bench(int howmany, std::shared_ptr<spdlog::logger> log);
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
int main(int, char *[])
{
std::srand(static_cast<unsigned>(std::time(nullptr))); // use current time as seed for random generator
int howmany = 1000000;
int queue_size = howmany + 2;
int threads = 10;
int file_size = 30 * 1024 * 1024;
int rotating_files = 5;
try
{
cout << "******************************************************************"
"*************\n";
cout << "Single thread\n";
cout << "******************************************************************"
"*************\n";
auto basic_st = spdlog::basic_logger_mt("basic_st", "logs/basic_st.log", true);
bench(howmany, basic_st);
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
bench(howmany, rotating_st);
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
bench(howmany, daily_st);
bench(howmany, spdlog::create<null_sink_st>("null_st"));
cout << "\n****************************************************************"
"***************\n";
cout << threads << " threads sharing same logger\n";
cout << "******************************************************************"
"*************\n";
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
bench_mt(howmany, basic_mt, threads);
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files);
bench_mt(howmany, rotating_mt, threads);
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
bench_mt(howmany, daily_mt, threads);
bench(howmany, spdlog::create<null_sink_st>("null_mt"));
cout << "\n****************************************************************"
"***************\n";
cout << "async logging.. " << threads << " threads sharing same logger\n";
cout << "******************************************************************"
"*************\n";
for (int i = 0; i < 3; ++i)
{
spdlog::init_thread_pool(queue_size, 1);
auto as = spdlog::basic_logger_mt<spdlog::async_factory>("async", "logs/basic_async.log", true);
bench_mt(howmany, as, threads);
spdlog::drop("async");
}
}
catch (std::exception &ex)
{
std::cerr << "Error: " << ex.what() << std::endl;
perror("Last error");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
void bench(int howmany, std::shared_ptr<spdlog::logger> log)
{
using namespace std::chrono;
using chrono::high_resolution_clock;
using chrono::milliseconds;
using chrono::nanoseconds;
cout << log->name() << "...\t\t" << flush;
nanoseconds total_nanos = nanoseconds::zero();
for (auto i = 0; i < howmany; ++i)
{
auto start = high_resolution_clock::now();
log->info("Hello logger: msg number {}", i);
auto delta_nanos = chrono::duration_cast<nanoseconds>(high_resolution_clock::now() - start);
total_nanos += delta_nanos;
}
auto avg = total_nanos.count() / howmany;
cout << format(avg) << " ns/call" << endl;
}
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
{
using namespace std::chrono;
using chrono::high_resolution_clock;
using chrono::milliseconds;
using chrono::nanoseconds;
cout << log->name() << "...\t\t" << flush;
vector<thread> threads;
std::atomic<nanoseconds::rep> total_nanos{0};
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]() {
for (int j = 0; j < howmany / thread_count; j++)
{
auto start = high_resolution_clock::now();
log->info("Hello logger: msg number {}", j);
auto delta_nanos = chrono::duration_cast<nanoseconds>(high_resolution_clock::now() - start);
total_nanos += delta_nanos.count();
}
}));
}
for (auto &t : threads)
{
t.join();
};
auto avg = total_nanos / howmany;
cout << format(avg) << " ns/call" << endl;
}

View File

@@ -1,32 +0,0 @@
CXX ?= g++
CXXFLAGS = -march=native -Wall -std=c++11 -pthread
CXX_RELEASE_FLAGS = -O2 -DNDEBUG
binaries=spdlog-latency g3log-latency g3log-crush
all: $(binaries)
spdlog-latency: spdlog-latency.cpp
$(CXX) spdlog-latency.cpp -o spdlog-latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -I../../include
g3log-latency: g3log-latency.cpp
$(CXX) g3log-latency.cpp -o g3log-latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -I../../../g3log/src -L. -lg3logger
g3log-crush: g3log-crush.cpp
$(CXX) g3log-crush.cpp -o g3log-crush $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -I../../../g3log/src -L. -lg3logger
.PHONY: clean
clean:
rm -f *.o *.log $(binaries)
rebuild: clean all

View File

@@ -1,13 +0,0 @@
#!/bin/bash
echo "running spdlog and g3log tests 10 time with ${1:-10} threads each (total 1,000,000 entries).."
rm -f *.log
for i in {1..10}
do
echo
sleep 0.5
./spdlog-latency ${1:-10} 2>/dev/null || exit
sleep 0.5
./g3log-latency ${1:-10} 2>/dev/null || exit
done

View File

@@ -1,37 +0,0 @@
#include <iostream>
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
void CrusherLoop()
{
size_t counter = 0;
while (true)
{
LOGF(INFO, "Some text to crush you machine. thread:");
if(++counter % 1000000 == 0)
{
std::cout << "Wrote " << counter << " entries" << std::endl;
}
}
}
int main(int argc, char** argv)
{
std::cout << "WARNING: This test will exaust all your machine memory and will crush it!" << std::endl;
std::cout << "Are you sure you want to continue ? " << std::endl;
char c;
std::cin >> c;
if (toupper( c ) != 'Y')
return 0;
auto worker = g3::LogWorker::createLogWorker();
auto handle= worker->addDefaultLogger(argv[0], "g3log.txt");
g3::initializeLogging(worker.get());
CrusherLoop();
return 0;
}

View File

@@ -1,129 +0,0 @@
#include <thread>
#include <vector>
#include <atomic>
#include <iostream>
#include <chrono>
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <fstream>
#include <cstdio>
#include <map>
#include <numeric>
#include <functional>
#include <thread>
#include "utils.h"
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
namespace
{
const uint64_t g_iterations = 1000000;
std::atomic<size_t> g_counter = {0};
void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t>& result)
{
while (true)
{
const size_t value_now = ++g_counter;
if (value_now > g_iterations)
{
return;
}
auto start_time = std::chrono::high_resolution_clock::now();
LOGF(INFO, "Some text to log for thread: %ld", id);
auto stop_time = std::chrono::high_resolution_clock::now();
uint64_t time_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time - start_time).count();
result.push_back(time_us);
}
}
void PrintResults(const std::map<size_t, std::vector<uint64_t>>& threads_result, size_t total_us)
{
std::vector<uint64_t> all_measurements;
all_measurements.reserve(g_iterations);
for (auto& t_result : threads_result)
{
all_measurements.insert(all_measurements.end(), t_result.second.begin(), t_result.second.end());
}
// calc worst latenct
auto worst = *std::max_element(all_measurements.begin(), all_measurements.end());
// calc avg
auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus<uint64_t>());
auto avg = double(total)/all_measurements.size();
std::cout << "[g3log] worst: " << std::setw(10) << std::right << worst << "\tAvg: " << avg << "\tTotal: " << utils::format(total_us) << " us" << std::endl;
}
}// anonymous
// The purpose of this test is NOT to see how fast
// each thread can possibly write. It is to see what
// the worst latency is for writing a log entry
//
// In the test 1 million log entries will be written
// an atomic counter is used to give each thread what
// it is to write next. The overhead of atomic
// synchronization between the threads are not counted in the worst case latency
int main(int argc, char** argv)
{
size_t number_of_threads {0};
if (argc == 2)
{
number_of_threads = atoi(argv[1]);
}
if (argc != 2 || number_of_threads == 0)
{
std::cerr << "USAGE is: " << argv[0] << " number_threads" << std::endl;
return 1;
}
std::vector<std::thread> threads(number_of_threads);
std::map<size_t, std::vector<uint64_t>> threads_result;
for (size_t idx = 0; idx < number_of_threads; ++idx)
{
// reserve to 1 million for all the result
// it's a test so let's not care about the wasted space
threads_result[idx].reserve(g_iterations);
}
const std::string g_path = "./" ;
const std::string g_prefix_log_name = "g3log-performance-";
const std::string g_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt";
auto worker = g3::LogWorker::createLogWorker();
auto handle= worker->addDefaultLogger(argv[0], "g3log.txt");
g3::initializeLogging(worker.get());
auto start_time_application_total = std::chrono::high_resolution_clock::now();
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
{
threads[idx] = std::thread(MeasurePeakDuringLogWrites, idx, std::ref(threads_result[idx]));
}
for (size_t idx = 0; idx < number_of_threads; ++idx)
{
threads[idx].join();
}
auto stop_time_application_total = std::chrono::high_resolution_clock::now();
uint64_t total_time_in_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time_application_total - start_time_application_total).count();
PrintResults(threads_result, total_time_in_us);
return 0;
}

View File

@@ -1,128 +0,0 @@
#include <thread>
#include <vector>
#include <atomic>
#include <iostream>
#include <chrono>
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <map>
#include <numeric>
#include <functional>
#include "utils.h"
#include <thread>
#include "spdlog/spdlog.h"
namespace spd = spdlog;
namespace
{
const uint64_t g_iterations = 1000000;
std::atomic<size_t> g_counter = {0};
void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t>& result)
{
auto logger = spd::get("file_logger");
while (true)
{
const size_t value_now = ++g_counter;
if (value_now > g_iterations)
{
return;
}
auto start_time = std::chrono::high_resolution_clock::now();
logger->info("Some text to log for thread: [somemore text...............................] {}", id);
auto stop_time = std::chrono::high_resolution_clock::now();
uint64_t time_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time - start_time).count();
result.push_back(time_us);
}
}
void PrintResults(const std::map<size_t, std::vector<uint64_t>>& threads_result, size_t total_us)
{
std::vector<uint64_t> all_measurements;
all_measurements.reserve(g_iterations);
for (auto& t_result : threads_result)
{
all_measurements.insert(all_measurements.end(), t_result.second.begin(), t_result.second.end());
}
// calc worst latenct
auto worst = *std::max_element(all_measurements.begin(), all_measurements.end());
// calc avg
auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus<uint64_t>());
auto avg = double(total)/all_measurements.size();
std::cout << "[spdlog] worst: " << std::setw(10) << std::right << worst << "\tAvg: " << avg << "\tTotal: " << utils::format(total_us) << " us" << std::endl;
}
}// anonymous
// The purpose of this test is NOT to see how fast
// each thread can possibly write. It is to see what
// the worst latency is for writing a log entry
//
// In the test 1 million log entries will be written
// an atomic counter is used to give each thread what
// it is to write next. The overhead of atomic
// synchronization between the threads are not counted in the worst case latency
int main(int argc, char** argv)
{
size_t number_of_threads {0};
if (argc == 2)
{
number_of_threads = atoi(argv[1]);
}
if (argc != 2 || number_of_threads == 0)
{
std::cerr << "usage: " << argv[0] << " number_threads" << std::endl;
return 1;
}
std::vector<std::thread> threads(number_of_threads);
std::map<size_t, std::vector<uint64_t>> threads_result;
for (size_t idx = 0; idx < number_of_threads; ++idx)
{
// reserve to 1 million for all the result
// it's a test so let's not care about the wasted space
threads_result[idx].reserve(g_iterations);
}
int queue_size = 1048576; // 2 ^ 20
spdlog::set_async_mode(queue_size);
auto logger = spdlog::create<spd::sinks::simple_file_sink_mt>("file_logger", "spdlog.log", true);
//force flush on every call to compare with g3log
auto s = (spd::sinks::simple_file_sink_mt*)logger->sinks()[0].get();
s->set_force_flush(true);
auto start_time_application_total = std::chrono::high_resolution_clock::now();
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
{
threads[idx] = std::thread(MeasurePeakDuringLogWrites, idx, std::ref(threads_result[idx]));
}
for (size_t idx = 0; idx < number_of_threads; ++idx)
{
threads[idx].join();
}
auto stop_time_application_total = std::chrono::high_resolution_clock::now();
uint64_t total_time_in_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time_application_total - start_time_application_total).count();
PrintResults(threads_result, total_time_in_us);
return 0;
}

View File

@@ -1,35 +0,0 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <sstream>
#include <iomanip>
#include <locale>
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();
}
}

19
bench/mem Executable file
View File

@@ -0,0 +1,19 @@
#!/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

View File

@@ -3,44 +3,54 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#include <thread>
#include <vector>
#include <atomic> #include <atomic>
#include <iostream>
#include <chrono> #include <chrono>
#include <cstdlib> #include <cstdlib>
#include "spdlog/spdlog.h" #include <iostream>
#include <thread>
#include <vector>
#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"
using namespace std; using namespace std;
int main(int argc, char *argv[]) int main(int argc, char *argv[])
{ {
using namespace std::chrono; using namespace std::chrono;
using clock = steady_clock; using clock = steady_clock;
namespace spd = spdlog;
int thread_count = 10; int thread_count = 10;
if (argc > 1) if (argc > 1)
thread_count = ::atoi(argv[1]); thread_count = std::atoi(argv[1]);
int howmany = 1000000; int howmany = 1000000;
spdlog::init_thread_pool(howmany, 1);
spd::set_async_mode(1048576); auto logger = spdlog::create_async_logger<spdlog::sinks::basic_file_sink_mt>("file_logger", "logs/spdlog-bench-async.log", false);
auto logger = spdlog::create<spd::sinks::simple_file_sink_mt>("file_logger", "logs/spd-bench-async.txt", false); logger->set_pattern("[%Y-%m-%d %T.%F]: %L %t %v");
logger->set_pattern("[%Y-%b-%d %T.%e]: %v");
std::cout << "To stop, press <Enter>" << std::endl;
std::atomic<bool> run{true};
std::thread stoper(std::thread([&run]() {
std::cin.get();
run = false;
}));
while (run)
{
std::atomic<int> msg_counter{0}; std::atomic<int> msg_counter{0};
vector<thread> threads; std::vector<std::thread> threads;
auto start = clock::now(); auto start = clock::now();
for (int t = 0; t < thread_count; ++t) for (int t = 0; t < thread_count; ++t)
{ {
threads.push_back(std::thread([&]() threads.push_back(std::thread([&]() {
{
while (true) while (true)
{ {
int counter = ++msg_counter; int counter = ++msg_counter;
if (counter > howmany) break; if (counter > howmany)
break;
logger->info("spdlog message #{}: This is some text for your pleasure", counter); logger->info("spdlog message #{}: This is some text for your pleasure", counter);
} }
})); }));
@@ -49,14 +59,19 @@ int main(int argc, char* argv[])
for (auto &t : threads) for (auto &t : threads)
{ {
t.join(); t.join();
}; }
duration<float> delta = clock::now() - start; duration<float> delta = clock::now() - start;
float deltaf = delta.count(); float deltaf = delta.count();
auto rate = howmany / deltaf; auto rate = howmany / deltaf;
cout << "Total: " << howmany << std::endl; std::cout << "Total: " << howmany << std::endl;
cout << "Threads: " << thread_count << std::endl; std::cout << "Threads: " << thread_count << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl; std::cout << "Delta = " << std::fixed << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl; std::cout << "Rate = " << std::fixed << rate << "/sec" << std::endl;
} // while
stoper.join();
return 0;
} }

View File

@@ -1,55 +0,0 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <thread>
#include <vector>
#include <atomic>
#include <cstdlib>
#include "spdlog/spdlog.h"
using namespace std;
int main(int argc, char* argv[])
{
int thread_count = 10;
if(argc > 1)
thread_count = std::atoi(argv[1]);
int howmany = 1000000;
namespace spd = spdlog;
auto logger = spdlog::create<spd::sinks::simple_file_sink_mt>("file_logger", "logs/spd-bench-mt.txt", false);
logger->set_pattern("[%Y-%b-%d %T.%e]: %v");
std::atomic<int > msg_counter {0};
std::vector<thread> threads;
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
while (true)
{
int counter = ++msg_counter;
if (counter > howmany) break;
logger->info("spdlog message #{}: This is some text for your pleasure", counter);
}
}));
}
for(auto &t:threads)
{
t.join();
};
return 0;
}

View File

@@ -1,20 +0,0 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include "spdlog/spdlog.h"
int main(int, char* [])
{
int howmany = 1000000;
namespace spd = spdlog;
///Create a file rotating logger with 5mb size max and 3 rotated files
auto logger = spdlog::create<spd::sinks::simple_file_sink_st>("file_logger", "logs/spd-bench-st.txt", false);
logger->set_pattern("[%Y-%b-%d %T.%e]: %v");
for(int i = 0 ; i < howmany; ++i)
logger->info("spdlog message #{} : This is some text for your pleasure", i);
return 0;
}

View File

@@ -1,112 +0,0 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
//
// bench.cpp : spdlog benchmarks
//
#include <atomic>
#include <cstdlib> // EXIT_FAILURE
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include "spdlog/spdlog.h"
#include "spdlog/async_logger.h"
#include "spdlog/sinks/null_sink.h"
#include "utils.h"
using namespace std;
using namespace std::chrono;
using namespace spdlog;
using namespace spdlog::sinks;
using namespace utils;
size_t bench_as(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
int main(int argc, char* argv[])
{
int queue_size = 1048576;
int howmany = 1000000;
int threads = 10;
int iters = 10;
try
{
if(argc > 1)
howmany = atoi(argv[1]);
if (argc > 2)
threads = atoi(argv[2]);
if (argc > 3)
queue_size = atoi(argv[3]);
cout << "\n*******************************************************************************\n";
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " messages " << endl;
cout << "*******************************************************************************\n";
spdlog::set_async_mode(queue_size);
size_t total_rate = 0;
for(int i = 0; i < iters; ++i)
{
//auto as = spdlog::daily_logger_st("as", "logs/daily_async");
auto as = spdlog::create<null_sink_st>("async(null-sink)");
total_rate+= bench_as(howmany, as, threads);
spdlog::drop("async(null-sink)");
}
std::cout << endl;
std::cout << "Avg rate: " << format(total_rate/iters) << "/sec" <<std::endl;
}
catch (std::exception &ex)
{
std::cerr << "Error: " << ex.what() << std::endl;
perror("Last error");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
//return rate/sec
size_t bench_as(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
{
cout << log->name() << "...\t\t" << flush;
std::atomic<int > msg_counter {0};
vector<thread> threads;
auto start = system_clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
for(;;)
{
int counter = ++msg_counter;
if (counter > howmany) break;
log->info("Hello logger: msg number {}", counter);
}
}));
}
for(auto &t:threads)
{
t.join();
};
auto delta = system_clock::now() - start;
auto delta_d = duration_cast<duration<double>> (delta).count();
auto per_sec = size_t(howmany / delta_d);
cout << format(per_sec) << "/sec" << endl;
return per_sec;
}

View File

@@ -5,12 +5,11 @@
#pragma once #pragma once
#include <sstream>
#include <iomanip> #include <iomanip>
#include <locale> #include <locale>
#include <sstream>
namespace utils namespace utils {
{
template<typename T> template<typename T>
inline std::string format(const T &value) inline std::string format(const T &value)
@@ -32,4 +31,4 @@ inline std::string format(const double & value)
return ss.str(); return ss.str();
} }
} } // namespace utils

View File

@@ -21,8 +21,8 @@
# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ # * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
# *************************************************************************/ # *************************************************************************/
cmake_minimum_required(VERSION 3.0) cmake_minimum_required(VERSION 3.1)
project(SpdlogExamples) project(SpdlogExamples CXX)
if(TARGET spdlog) if(TARGET spdlog)
# Part of the main project # Part of the main project
@@ -32,16 +32,16 @@ else()
find_package(spdlog CONFIG REQUIRED) find_package(spdlog CONFIG REQUIRED)
endif() endif()
find_package(Threads) find_package(Threads REQUIRED)
add_executable(example example.cpp) add_executable(example example.cpp)
target_link_libraries(example spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(example spdlog::spdlog Threads::Threads)
add_executable(benchmark bench.cpp) add_executable(benchmark bench.cpp)
target_link_libraries(benchmark spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(benchmark spdlog::spdlog Threads::Threads)
add_executable(multisink multisink.cpp) add_executable(multisink multisink.cpp)
target_link_libraries(multisink spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(multisink spdlog::spdlog Threads::Threads)
enable_testing() enable_testing()
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")

View File

@@ -1,28 +1,20 @@
CXX ?= g++ CXX ?= g++
CXXFLAGS = CXX_FLAGS = -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1
CXX_FLAGS = -Wall -Wshadow -Wextra -pedantic -std=c++11 -pthread -I../include
CXX_RELEASE_FLAGS = -O3 -march=native CXX_RELEASE_FLAGS = -O3 -march=native
CXX_DEBUG_FLAGS= -g CXX_DEBUG_FLAGS= -g
all: example
all: example bench debug: example-debug
debug: example-debug bench-debug
example: example.cpp example: example.cpp
$(CXX) example.cpp -o example $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS) $(CXX) example.cpp -o example $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS)
bench: bench.cpp
$(CXX) bench.cpp -o bench $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS)
example-debug: example.cpp example-debug: example.cpp
$(CXX) example.cpp -o example-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS) $(CXX) example.cpp -o example-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS)
bench-debug: bench.cpp
$(CXX) bench.cpp -o bench-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS)
clean: clean:
rm -f *.o logs/*.txt example example-debug bench bench-debug rm -f *.o logs/*.txt example example-debug
rebuild: clean all rebuild: clean all

View File

@@ -1,29 +1,23 @@
CXX ?= clang++ CXX = clang++
CXXFLAGS = -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -I../include CXXFLAGS = -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -I../include
CXX_RELEASE_FLAGS = -O2 CXX_RELEASE_FLAGS = -O2
CXX_DEBUG_FLAGS= -g CXX_DEBUG_FLAGS= -g
all: example bench all: example
debug: example-debug bench-debug debug: example-debug
example: example.cpp example: example.cpp
$(CXX) example.cpp -o example-clang $(CXXFLAGS) $(CXX_RELEASE_FLAGS) $(CXX) example.cpp -o example-clang $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
bench: bench.cpp
$(CXX) bench.cpp -o bench-clang $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
example-debug: example.cpp example-debug: example.cpp
$(CXX) example.cpp -o example-clang-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS) $(CXX) example.cpp -o example-clang-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
bench-debug: bench.cpp
$(CXX) bench.cpp -o bench-clang-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
clean: clean:
rm -f *.o logs/*.txt example-clang example-clang-debug bench-clang bench-clang-debug rm -f *.o logs/*.txt example-clang example-clang-debug
rebuild: clean all rebuild: clean all

View File

@@ -1,29 +1,22 @@
CXX ?= g++ CXX ?= g++
CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include 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_RELEASE_FLAGS = -O3
CXX_DEBUG_FLAGS= -g CXX_DEBUG_FLAGS= -g
all: example bench all: example
debug: example-debug bench-debug debug: example-debug
example: example.cpp example: example.cpp
$(CXX) example.cpp -o example $(CXXFLAGS) $(CXX_RELEASE_FLAGS) $(CXX) example.cpp -o example $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
bench: bench.cpp
$(CXX) bench.cpp -o bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
example-debug: example.cpp example-debug: example.cpp
$(CXX) example.cpp -o example-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS) $(CXX) example.cpp -o example-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
bench-debug: bench.cpp
$(CXX) bench.cpp -o bench-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
clean: clean:
rm -f *.o logs/*.txt example example-debug bench bench-debug rm -f *.o logs/*.txt example example-debug
rebuild: clean all rebuild: clean all

View File

@@ -7,32 +7,74 @@
// //
// //
#define SPDLOG_TRACE_ON #include <iostream>
#define SPDLOG_DEBUG_ON
void stdout_example();
void basic_example();
void rotating_example();
void daily_example();
void async_example();
void multi_sink_example();
void user_defined_example();
void err_handler_example();
void syslog_example();
#include "spdlog/spdlog.h" #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 *[]) int main(int, char *[])
{ {
try try
{ {
// Console logger with color // console logging example
auto console = spd::stdout_color_mt("console"); stdout_example();
console->info("Welcome to spdlog!");
console->error("Some error message with arg{}..", 1);
// Conditional logging example // various file loggers
console->info_if(true, "Welcome to spdlog conditional logging!"); basic_example();
rotating_example();
daily_example();
// async logging using a backing thread pool
async_example();
// a logger can have multiple targets with different formats
multi_sink_example();
// user defined types logging by implementing operator<<
user_defined_example();
// custom error handler
err_handler_example();
// flush all *registered* loggers using a worker thread every 3 seconds.
// note: registered loggers *must* be thread safe for this to work correctly!
spdlog::flush_every(std::chrono::seconds(3));
// apply some function on all registered loggers
spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); });
// release any threads created by spdlog, and drop all loggers in the registry.
spdlog::shutdown();
}
// 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;
}
}
#include "spdlog/sinks/stdout_color_sinks.h"
// or #include "spdlog/sinks/stdout_sinks.h" if no colors needed
void stdout_example()
{
// create color multi threaded logger
auto console = spdlog::stdout_color_mt("console");
console->info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH);
console->error("Some error message with arg: {}", 1);
auto err_logger = spdlog::stderr_color_mt("stderr");
err_logger->error("Some error message");
// Formatting examples // Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12); console->warn("Easy padding in numbers like {:08d}", 12);
@@ -41,109 +83,79 @@ int main(int, char*[])
console->info("Positional args are {1} {0}..", "too", "supported"); console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned"); console->info("{:<30}", "left aligned");
SPDLOG_DEBUG_IF(console, true, "This is a debug log"); spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
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");
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/mylogfile", 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", 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 // Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info spdlog::set_level(spdlog::level::info); // Set global log level to info
console->debug("This message shold not be displayed!"); console->debug("This message should not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level console->set_level(spdlog::level::trace); // Set specific logger's log level
console->debug("This message shold be displayed.."); console->debug("This message should be displayed..");
// Customize msg format for all loggers
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
console->info("This an info message with custom format");
// Compile time log levels // Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON // define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23); SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23); SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
SPDLOG_DEBUG_IF(console, true, "This is a debug log"); }
#include "spdlog/sinks/basic_file_sink.h"
// Asynchronous logging is very fast.. void basic_example()
// 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."); // Create basic file logger (not rotated)
}); auto my_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
// 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) #include "spdlog/sinks/rotating_file_sink.h"
void rotating_example()
{ {
std::cout << "Log init failed: " << ex.what() << std::endl; // Create a file rotating logger with 5mb size max and 3 rotated files
return 1; auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
}
} }
#include "spdlog/sinks/daily_file_sink.h"
void daily_example()
{
// Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
}
#include "spdlog/async.h"
void async_example() void async_example()
{ {
size_t q_size = 4096; //queue size must be power of 2 // default thread pool settings can be modified *before* creating the async logger:
spdlog::set_async_mode(q_size); // spdlog::init_thread_pool(32768, 1); // queue with max 32k items 1 backing thread.
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log"); auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
// alternatively:
// auto async_file = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");
for (int i = 0; i < 100; ++i) for (int i = 1; i < 101; ++i)
{
async_file->info("Async message #{}", 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 // create logger with 2 targets with different log levels and formats
void android_example() // the console will show only warnings or errors, while the file will log all
{
#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
}
void multi_sink_example()
{
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
console_sink->set_level(spdlog::level::warn);
console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v");
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/multisink.txt", true);
file_sink->set_level(spdlog::level::trace);
spdlog::logger logger("multi_sink", {console_sink, file_sink});
logger.set_level(spdlog::level::debug);
logger.warn("this should appear in both console and file");
logger.info("this message should not appear in the console, only in the file");
}
// user defined types logging by implementing operator<< // user defined types logging by implementing operator<<
#include "spdlog/fmt/ostr.h" // must be included
struct my_type struct my_type
{ {
int i; int i;
@@ -154,10 +166,9 @@ struct my_type
} }
}; };
#include "spdlog/fmt/ostr.h" // must be included
void user_defined_example() void user_defined_example()
{ {
spd::get("console")->info("user defined type: {}", my_type { 14 }); spdlog::get("console")->info("user defined type: {}", my_type{14});
} }
// //
@@ -165,10 +176,30 @@ void user_defined_example()
// //
void err_handler_example() void err_handler_example()
{ {
//can be set globaly or per logger(logger->set_error_handler(..)) // can be set globally or per logger(logger->set_error_handler(..))
spdlog::set_error_handler([](const std::string& msg) spdlog::set_error_handler([](const std::string &msg) { spdlog::get("console")->error("*** ERROR HANDLER EXAMPLE ***: {}", msg); });
{ spdlog::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
std::cerr << "my err handler: " << msg << std::endl;
});
spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
} }
// syslog example (linux/osx/freebsd)
#ifndef _WIN32
#include "spdlog/sinks/syslog_sink.h"
void syslog_example()
{
std::string ident = "spdlog-example";
auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID);
syslog_logger->warn("This is warning that will end up in syslog.");
}
#endif
// Android example
#if defined(__ANDROID__)
#include "spdlog/sinks/android_sink.h"
void android_example()
{
std::string tag = "spdlog-android";
auto android_logger = spdlog::android_logger_mt("android", tag);
android_logger->critical("Use \"adb shell logcat\" to view this message.");
}
#endif

View File

@@ -1,10 +1,79 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14 # Visual Studio 15
VisualStudioVersion = 14.0.25420.1 VisualStudioVersion = 15.0.27703.2018
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example", "example.vcxproj", "{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example", "example.vcxproj", "{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}"
EndProject 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 Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32 Debug|Win32 = Debug|Win32
@@ -15,12 +84,23 @@ Global
GlobalSection(ProjectConfigurationPlatforms) = postSolution GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.ActiveCfg = Debug|Win32 {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|Win32.Build.0 = Debug|Win32
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|x64.ActiveCfg = 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.ActiveCfg = Release|Win32
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.Build.0 = Release|Win32 {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.Build.0 = Release|Win32
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|x64.ActiveCfg = Release|Win32 {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|x64.ActiveCfg = Release|x64
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|x64.Build.0 = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection 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 EndGlobal

View File

@@ -1,68 +1,56 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations"> <ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32"> <ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration> <Configuration>Debug</Configuration>
<Platform>Win32</Platform> <Platform>Win32</Platform>
</ProjectConfiguration> </ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32"> <ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration> <Configuration>Release</Configuration>
<Platform>Win32</Platform> <Platform>Win32</Platform>
</ProjectConfiguration> </ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="example.cpp" /> <ClCompile Include="example.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\spdlog\async_logger.h" />
<ClInclude Include="..\include\spdlog\common.h" />
<ClInclude Include="..\include\spdlog\details\async_logger_impl.h" />
<ClInclude Include="..\include\spdlog\details\async_log_helper.h" />
<ClInclude Include="..\include\spdlog\details\file_helper.h" />
<ClInclude Include="..\include\spdlog\details\logger_impl.h" />
<ClInclude Include="..\include\spdlog\details\log_msg.h" />
<ClInclude Include="..\include\spdlog\details\mpmc_bounded_q.h" />
<ClInclude Include="..\include\spdlog\details\null_mutex.h" />
<ClInclude Include="..\include\spdlog\details\os.h" />
<ClInclude Include="..\include\spdlog\details\pattern_formatter_impl.h" />
<ClInclude Include="..\include\spdlog\details\registry.h" />
<ClInclude Include="..\include\spdlog\details\spdlog_impl.h" />
<ClInclude Include="..\include\spdlog\fmt\fmt.h" />
<ClInclude Include="..\include\spdlog\fmt\ostr.h" />
<ClInclude Include="..\include\spdlog\formatter.h" />
<ClInclude Include="..\include\spdlog\logger.h" />
<ClInclude Include="..\include\spdlog\sinks\android_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\ansicolor_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\base_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\dist_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\file_sinks.h" />
<ClInclude Include="..\include\spdlog\sinks\msvc_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\null_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\ostream_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\sink.h" />
<ClInclude Include="..\include\spdlog\sinks\stdout_sinks.h" />
<ClInclude Include="..\include\spdlog\sinks\syslog_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\wincolor_sink.h" />
<ClInclude Include="..\include\spdlog\spdlog.h" />
<ClInclude Include="..\include\spdlog\tweakme.h" />
</ItemGroup>
<PropertyGroup Label="Globals"> <PropertyGroup Label="Globals">
<ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid> <ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid>
<Keyword>Win32Proj</Keyword> <Keyword>Win32Proj</Keyword>
<RootNamespace>.</RootNamespace> <RootNamespace>.</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries> <UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset> <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> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries> <UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset> <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> <WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
@@ -72,16 +60,28 @@
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <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" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup> </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'"> <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" /> <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup> </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 Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental> <LinkIncremental>true</LinkIncremental>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental> <LinkIncremental>false</LinkIncremental>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile> <ClCompile>
<PrecompiledHeader> <PrecompiledHeader>
@@ -98,6 +98,24 @@
<GenerateDebugInformation>true</GenerateDebugInformation> <GenerateDebugInformation>true</GenerateDebugInformation>
</Link> </Link>
</ItemDefinitionGroup> </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'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile> <ClCompile>
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
@@ -120,6 +138,30 @@
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies> <AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link> </Link>
</ItemDefinitionGroup> </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" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
</ImportGroup> </ImportGroup>

View File

@@ -1 +0,0 @@
../example.cpp

157
example/jni/example.cpp Normal file
View File

@@ -0,0 +1,157 @@
//
// 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,5 +1,6 @@
#include "spdlog/sinks/file_sinks.h"
#include "spdlog/sinks/stdout_sinks.h"
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include <iostream> #include <iostream>
#include <memory> #include <memory>
@@ -14,8 +15,8 @@ int main(int, char*[])
// Each sink can have it's own log level and a message will be logged. // Each sink can have it's own log level and a message will be logged.
std::vector<spdlog::sink_ptr> sinks; 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::stdout_sink_mt>());
sinks.push_back( std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_regular_file.txt") ); sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>("./log_regular_file.txt"));
sinks.push_back( std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_debug_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()); spdlog::logger console_multisink("multisink", sinks.begin(), sinks.end());
console_multisink.set_level(spdlog::level::warn); console_multisink.set_level(spdlog::level::warn);
@@ -44,4 +45,3 @@ int main(int, char*[])
return 1; return 1;
} }
} }

View File

@@ -5,12 +5,11 @@
#pragma once #pragma once
#include <sstream>
#include <iomanip> #include <iomanip>
#include <locale> #include <locale>
#include <sstream>
namespace utils namespace utils {
{
template<typename T> template<typename T>
inline std::string format(const T &value) inline std::string format(const T &value)
@@ -32,4 +31,4 @@ inline std::string format(const double & value)
return ss.str(); return ss.str();
} }
} } // namespace utils

9
format.sh Executable file
View File

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

87
include/spdlog/async.h Normal file
View File

@@ -0,0 +1,87 @@
//
// Copyright(c) 2018 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
//
// Async logging using global thread pool
// All loggers created here share same global thread pool.
// Each log message is pushed to a queue along withe a shared pointer to the
// logger.
// If a logger deleted while having pending messages in the queue, it's actual
// destruction will defer
// until all its messages are processed by the thread pool.
// This is because each message in the queue holds a shared_ptr to the
// originating logger.
#include "spdlog/async_logger.h"
#include "spdlog/details/registry.h"
#include "spdlog/details/thread_pool.h"
#include <memory>
#include <mutex>
namespace spdlog {
namespace details {
static const size_t default_async_q_size = 8192;
}
// async logger factory - creates async loggers backed with thread pool.
// if a global thread pool doesn't already exist, create it with default queue
// size of 8192 items and single thread.
template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
struct async_factory_impl
{
template<typename Sink, typename... SinkArgs>
static std::shared_ptr<async_logger> create(const std::string &logger_name, SinkArgs &&... args)
{
auto &registry_inst = details::registry::instance();
// create global thread pool if not already exists..
std::lock_guard<std::recursive_mutex>(registry_inst.tp_mutex());
auto tp = registry_inst.get_tp();
if (tp == nullptr)
{
tp = std::make_shared<details::thread_pool>(details::default_async_q_size, 1);
registry_inst.set_tp(tp);
}
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
auto new_logger = std::make_shared<async_logger>(logger_name, std::move(sink), std::move(tp), OverflowPolicy);
registry_inst.register_and_init(new_logger);
return new_logger;
}
};
using async_factory = async_factory_impl<async_overflow_policy::block>;
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
template<typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create_async(const std::string &logger_name, SinkArgs &&... sink_args)
{
return async_factory::create<Sink>(logger_name, std::forward<SinkArgs>(sink_args)...);
}
template<typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create_async_nb(const std::string &logger_name, SinkArgs &&... sink_args)
{
return async_factory_nonblock::create<Sink>(logger_name, std::forward<SinkArgs>(sink_args)...);
}
// set global thread pool.
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);
details::registry::instance().set_tp(std::move(tp));
}
// get the global thread pool.
inline std::shared_ptr<spdlog::details::thread_pool> thread_pool()
{
return details::registry::instance().get_tp();
}
} // namespace spdlog

View File

@@ -5,78 +5,67 @@
#pragma once #pragma once
// Very fast asynchronous logger (millions of logs per second on an average desktop) // Very fast asynchronous logger (millions of logs per second on an average
// Uses pre allocated lockfree queue for maximum throughput even under large number of threads. // desktop)
// 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 space is available in the queue) // 2. Push a new copy of the message to a queue (or block the caller until
// space is available in the queue)
// 3. will throw spdlog_ex upon log exceptions // 3. will throw spdlog_ex upon log exceptions
// Upon destruction, logs all remaining messages in the queue before destructing.. // Upon destruction, logs all remaining messages in the queue before
// destructing..
#include "spdlog/common.h" #include "spdlog/common.h"
#include "spdlog/logger.h" #include "spdlog/logger.h"
#include <chrono> #include <chrono>
#include <functional>
#include <string>
#include <memory> #include <memory>
#include <string>
namespace spdlog namespace spdlog {
{
namespace details // Async overflow policy - block by default.
enum class async_overflow_policy
{ {
class async_log_helper; block, // Block until message can be enqueued
overrun_oldest // Discard oldest message in the queue if full when trying to
// add new item.
};
namespace details {
class thread_pool;
} }
class async_logger SPDLOG_FINAL :public logger class async_logger SPDLOG_FINAL : public std::enable_shared_from_this<async_logger>, public logger
{ {
friend class details::thread_pool;
public: public:
template<class It> template<typename It>
async_logger(const std::string& name, async_logger(std::string logger_name, const It &begin, const It &end, std::weak_ptr<details::thread_pool> tp,
const It& begin, async_overflow_policy overflow_policy = async_overflow_policy::block);
const It& end,
size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()>& worker_teardown_cb = nullptr);
async_logger(const std::string& logger_name, async_logger(std::string logger_name, sinks_init_list sinks, std::weak_ptr<details::thread_pool> tp,
sinks_init_list sinks, async_overflow_policy overflow_policy = async_overflow_policy::block);
size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()>& worker_teardown_cb = nullptr);
async_logger(const std::string& logger_name, async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp,
sink_ptr single_sink, async_overflow_policy overflow_policy = async_overflow_policy::block);
size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()>& worker_teardown_cb = nullptr);
//Wait for the queue to be empty, and flush synchronously
//Warning: this can potentially last forever as we wait it to complete
void flush() override;
// Error handler
virtual void set_error_handler(log_err_handler) override;
virtual log_err_handler error_handler() override;
protected: protected:
void _sink_it(details::log_msg& msg) override; void sink_it_(details::log_msg &msg) override;
void _set_formatter(spdlog::formatter_ptr msg_formatter) override; void flush_() override;
void _set_pattern(const std::string& pattern, pattern_time_type pattern_time) override;
void backend_log_(details::log_msg &incoming_log_msg);
void backend_flush_();
private: private:
std::unique_ptr<details::async_log_helper> _async_log_helper; std::weak_ptr<details::thread_pool> thread_pool_;
async_overflow_policy overflow_policy_;
}; };
} } // namespace spdlog
#include "details/async_logger_impl.h"
#include "spdlog/details/async_logger_impl.h"

View File

@@ -5,15 +5,18 @@
#pragma once #pragma once
#include <string> #include "spdlog/tweakme.h"
#include <initializer_list>
#include <chrono>
#include <memory>
#include <atomic>
#include <exception>
#include<functional>
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) #include <atomic>
#include <chrono>
#include <functional>
#include <initializer_list>
#include <memory>
#include <stdexcept>
#include <string>
#include <unordered_map>
#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
#include <codecvt> #include <codecvt>
#include <locale> #include <locale>
#endif #endif
@@ -29,9 +32,11 @@
#define SPDLOG_CONSTEXPR constexpr #define SPDLOG_CONSTEXPR constexpr
#endif #endif
// See tweakme.h // final keyword support. On by default. See tweakme.h
#if !defined(SPDLOG_FINAL) #if defined(SPDLOG_NO_FINAL)
#define SPDLOG_FINAL #define SPDLOG_FINAL
#else
#define SPDLOG_FINAL final
#endif #endif
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
@@ -42,35 +47,30 @@
#define SPDLOG_DEPRECATED #define SPDLOG_DEPRECATED
#endif #endif
#include "spdlog/fmt/fmt.h" #include "spdlog/fmt/fmt.h"
namespace spdlog namespace spdlog {
{
class formatter; class formatter;
namespace sinks namespace sinks {
{
class sink; class sink;
} }
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 formatter_ptr = std::shared_ptr<spdlog::formatter>; using log_err_handler = std::function<void(const std::string &err_msg)>;
#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;
#else #else
using level_t = std::atomic<int>; using level_t = std::atomic<int>;
#endif #endif
using log_err_handler = std::function<void(const std::string &err_msg)>;
// Log level enum // Log level enum
namespace level namespace level {
{ enum level_enum
typedef enum
{ {
trace = 0, trace = 0,
debug = 1, debug = 1,
@@ -79,35 +79,44 @@ typedef enum
err = 4, err = 4,
critical = 5, critical = 5,
off = 6 off = 6
} level_enum; };
#if !defined(SPDLOG_LEVEL_NAMES) #if !defined(SPDLOG_LEVEL_NAMES)
#define SPDLOG_LEVEL_NAMES { "trace", "debug", "info", "warning", "error", "critical", "off" }; #define SPDLOG_LEVEL_NAMES \
{ \
"trace", "debug", "info", "warning", "error", "critical", "off" \
}
#endif #endif
static const char* level_names[] SPDLOG_LEVEL_NAMES static const char *level_names[] SPDLOG_LEVEL_NAMES;
static const char *short_level_names[]{"T", "D", "I", "W", "E", "C", "O"}; static const char *short_level_names[]{"T", "D", "I", "W", "E", "C", "O"};
inline const char* to_str(spdlog::level::level_enum l) inline const char *to_c_str(spdlog::level::level_enum l)
{ {
return level_names[l]; return level_names[l];
} }
inline const char* to_short_str(spdlog::level::level_enum l) inline const char *to_short_c_str(spdlog::level::level_enum l)
{ {
return short_level_names[l]; return short_level_names[l];
} }
} //level inline spdlog::level::level_enum from_str(const std::string &name)
//
// Async overflow policy - block by default.
//
enum class async_overflow_policy
{ {
block_retry, // Block / yield / sleep until message can be enqueued static std::unordered_map<std::string, level_enum> name_to_level = // map string->level
discard_log_msg // Discard the message it enqueue fails {{level_names[0], level::trace}, // trace
}; {level_names[1], level::debug}, // debug
{level_names[2], level::info}, // info
{level_names[3], level::warn}, // warn
{level_names[4], level::err}, // err
{level_names[5], level::critical}, // critical
{level_names[6], level::off}}; // off
auto lvl_it = name_to_level.find(name);
return lvl_it != name_to_level.end() ? lvl_it->second : level::off;
}
using level_hasher = std::hash<int>;
} // namespace level
// //
// Pattern time - specific time getting to use for pattern_formatter. // Pattern time - specific time getting to use for pattern_formatter.
@@ -122,29 +131,35 @@ enum class pattern_time_type
// //
// Log exception // Log exception
// //
namespace details class spdlog_ex : public std::runtime_error
{
namespace os
{
std::string errno_str(int err_num);
}
}
class spdlog_ex: public std::exception
{ {
public: public:
spdlog_ex(const std::string& msg):_msg(msg) explicit spdlog_ex(const std::string &msg)
{} : runtime_error(msg)
spdlog_ex(const std::string& msg, int last_errno) {
}
spdlog_ex(std::string msg, int last_errno)
: runtime_error(std::move(msg))
, last_errno_(last_errno)
{ {
_msg = msg + ": " + details::os::errno_str(last_errno);
} }
const char *what() const SPDLOG_NOEXCEPT override const char *what() const SPDLOG_NOEXCEPT override
{ {
return _msg.c_str(); if (last_errno_)
{
fmt::memory_buffer buf;
std::string msg(runtime_error::what());
fmt::format_system_error(buf, last_errno_, msg);
return fmt::to_string(buf).c_str();
}
else
{
return runtime_error::what();
}
} }
private:
std::string _msg;
private:
int last_errno_{0};
}; };
// //
@@ -156,5 +171,13 @@ using filename_t = std::wstring;
using filename_t = std::string; using filename_t = std::string;
#endif #endif
#define SPDLOG_CATCH_AND_HANDLE \
} //spdlog catch (const std::exception &ex) \
{ \
err_handler_(ex.what()); \
} \
catch (...) \
{ \
err_handler_("Unknown exeption in logger"); \
}
} // namespace spdlog

View File

@@ -1,399 +0,0 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
// async log helper :
// Process logs asynchronously using a back thread.
//
// If the internal queue of log messages reaches its max size,
// then the client call will block until there is more room.
//
#pragma once
#include "spdlog/common.h"
#include "spdlog/sinks/sink.h"
#include "spdlog/details/mpmc_bounded_q.h"
#include "spdlog/details/log_msg.h"
#include "spdlog/details/os.h"
#include "spdlog/formatter.h"
#include <chrono>
#include <exception>
#include <functional>
#include <memory>
#include <string>
#include <thread>
#include <utility>
#include <vector>
namespace spdlog
{
namespace details
{
class async_log_helper
{
// Async msg to move to/from the queue
// Movable only. should never be copied
enum class async_msg_type
{
log,
flush,
terminate
};
struct async_msg
{
std::string logger_name;
level::level_enum level;
log_clock::time_point time;
size_t thread_id;
std::string txt;
async_msg_type msg_type;
size_t msg_id;
async_msg() = default;
~async_msg() = default;
async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
logger_name(std::move(other.logger_name)),
level(std::move(other.level)),
time(std::move(other.time)),
thread_id(other.thread_id),
txt(std::move(other.txt)),
msg_type(std::move(other.msg_type)),
msg_id(other.msg_id)
{}
async_msg(async_msg_type m_type):
level(level::info),
thread_id(0),
msg_type(m_type),
msg_id(0)
{}
async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT
{
logger_name = std::move(other.logger_name);
level = other.level;
time = std::move(other.time);
thread_id = other.thread_id;
txt = std::move(other.txt);
msg_type = other.msg_type;
msg_id = other.msg_id;
return *this;
}
// never copy or assign. should only be moved..
async_msg(const async_msg&) = delete;
async_msg& operator=(const async_msg& other) = delete;
// construct from log_msg
async_msg(const details::log_msg& m):
level(m.level),
time(m.time),
thread_id(m.thread_id),
txt(m.raw.data(), m.raw.size()),
msg_type(async_msg_type::log),
msg_id(m.msg_id)
{
#ifndef SPDLOG_NO_NAME
logger_name = *m.logger_name;
#endif
}
// copy into log_msg
void fill_log_msg(log_msg &msg)
{
msg.logger_name = &logger_name;
msg.level = level;
msg.time = time;
msg.thread_id = thread_id;
msg.raw << txt;
msg.msg_id = msg_id;
}
};
public:
using item_type = async_msg;
using q_type = details::mpmc_bounded_queue<item_type>;
using clock = std::chrono::steady_clock;
async_log_helper(formatter_ptr formatter,
const std::vector<sink_ptr>& sinks,
size_t queue_size,
const log_err_handler err_handler,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()>& worker_teardown_cb = nullptr);
void log(const details::log_msg& msg);
// stop logging and join the back thread
~async_log_helper();
void set_formatter(formatter_ptr);
void flush(bool wait_for_q);
void set_error_handler(spdlog::log_err_handler err_handler);
private:
formatter_ptr _formatter;
std::vector<std::shared_ptr<sinks::sink>> _sinks;
// queue of messages to log
q_type _q;
log_err_handler _err_handler;
bool _flush_requested;
bool _terminate_requested;
// overflow policy
const async_overflow_policy _overflow_policy;
// worker thread warmup callback - one can set thread priority, affinity, etc
const std::function<void()> _worker_warmup_cb;
// auto periodic sink flush parameter
const std::chrono::milliseconds _flush_interval_ms;
// worker thread teardown callback
const std::function<void()> _worker_teardown_cb;
// worker thread
std::thread _worker_thread;
void push_msg(async_msg&& new_msg);
// worker thread main loop
void worker_loop();
// pop next message from the queue and process it. will set the last_pop to the pop time
// return false if termination of the queue is required
bool process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush);
void handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush);
// sleep,yield or return immediately using the time passed since last message as a hint
static void sleep_or_yield(const spdlog::log_clock::time_point& now, const log_clock::time_point& last_op_time);
// wait until the queue is empty
void wait_empty_q();
};
}
}
///////////////////////////////////////////////////////////////////////////////
// async_sink class implementation
///////////////////////////////////////////////////////////////////////////////
inline spdlog::details::async_log_helper::async_log_helper(
formatter_ptr formatter,
const std::vector<sink_ptr>& sinks,
size_t queue_size,
log_err_handler err_handler,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms,
const std::function<void()>& worker_teardown_cb):
_formatter(formatter),
_sinks(sinks),
_q(queue_size),
_err_handler(err_handler),
_flush_requested(false),
_terminate_requested(false),
_overflow_policy(overflow_policy),
_worker_warmup_cb(worker_warmup_cb),
_flush_interval_ms(flush_interval_ms),
_worker_teardown_cb(worker_teardown_cb),
_worker_thread(&async_log_helper::worker_loop, this)
{}
// Send to the worker thread termination message(level=off)
// and wait for it to finish gracefully
inline spdlog::details::async_log_helper::~async_log_helper()
{
try
{
push_msg(async_msg(async_msg_type::terminate));
_worker_thread.join();
}
catch (...) // don't crash in destructor
{
}
}
//Try to push and block until succeeded (if the policy is not to discard when the queue is full)
inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
{
push_msg(async_msg(msg));
}
inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg)
{
if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg)
{
auto last_op_time = details::os::now();
auto now = last_op_time;
do
{
now = details::os::now();
sleep_or_yield(now, last_op_time);
}
while (!_q.enqueue(std::move(new_msg)));
}
}
// optionally wait for the queue be empty and request flush from the sinks
inline void spdlog::details::async_log_helper::flush(bool wait_for_q)
{
push_msg(async_msg(async_msg_type::flush));
if (wait_for_q)
wait_empty_q(); //return only make after the above flush message was processed
}
inline void spdlog::details::async_log_helper::worker_loop()
{
if (_worker_warmup_cb) _worker_warmup_cb();
auto last_pop = details::os::now();
auto last_flush = last_pop;
auto active = true;
while (active)
{
try
{
active = process_next_msg(last_pop, last_flush);
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
}
if (_worker_teardown_cb) _worker_teardown_cb();
}
// process next message in the queue
// return true if this thread should still be active (while no terminate msg was received)
inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush)
{
async_msg incoming_async_msg;
if (_q.dequeue(incoming_async_msg))
{
last_pop = details::os::now();
switch (incoming_async_msg.msg_type)
{
case async_msg_type::flush:
_flush_requested = true;
break;
case async_msg_type::terminate:
_flush_requested = true;
_terminate_requested = true;
break;
default:
log_msg incoming_log_msg;
incoming_async_msg.fill_log_msg(incoming_log_msg);
_formatter->format(incoming_log_msg);
for (auto &s : _sinks)
{
if (s->should_log(incoming_log_msg.level))
{
s->log(incoming_log_msg);
}
}
}
return true;
}
// Handle empty queue..
// This is the only place where the queue can terminate or flush to avoid losing messages already in the queue
else
{
auto now = details::os::now();
handle_flush_interval(now, last_flush);
sleep_or_yield(now, last_pop);
return !_terminate_requested;
}
}
// flush all sinks if _flush_interval_ms has expired
inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush)
{
auto should_flush = _flush_requested || (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms);
if (should_flush)
{
for (auto &s : _sinks)
s->flush();
now = last_flush = details::os::now();
_flush_requested = false;
}
}
inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter)
{
_formatter = msg_formatter;
}
// spin, yield or sleep. use the time passed since last message as a hint
inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_clock::time_point& now, const spdlog::log_clock::time_point& last_op_time)
{
using namespace std::this_thread;
using std::chrono::milliseconds;
using std::chrono::microseconds;
auto time_since_op = now - last_op_time;
// spin upto 50 micros
if (time_since_op <= microseconds(50))
return;
// yield upto 150 micros
if (time_since_op <= microseconds(100))
return std::this_thread::yield();
// sleep for 20 ms upto 200 ms
if (time_since_op <= milliseconds(200))
return sleep_for(milliseconds(20));
// sleep for 200 ms
return sleep_for(milliseconds(200));
}
// wait for the queue to be empty
inline void spdlog::details::async_log_helper::wait_empty_q()
{
auto last_op = details::os::now();
while (_q.approx_size() > 0)
{
sleep_or_yield(details::os::now(), last_op);
}
}
inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler)
{
_err_handler = err_handler;
}

View File

@@ -5,101 +5,96 @@
#pragma once #pragma once
// Async Logger implementation // async logger implementation
// Use an async_sink (queue per logger) to perform the logging in a worker thread // uses a thread pool to perform the actual logging
#include "spdlog/details/async_log_helper.h" #include "spdlog/details/thread_pool.h"
#include "spdlog/async_logger.h"
#include <string>
#include <functional>
#include <chrono> #include <chrono>
#include <memory> #include <memory>
#include <string>
template<class It> template<typename It>
inline spdlog::async_logger::async_logger(const std::string& logger_name, inline spdlog::async_logger::async_logger(
const It& begin, std::string logger_name, const It &begin, const It &end, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
const It& end, : logger(std::move(logger_name), begin, end)
size_t queue_size, , thread_pool_(tp)
const async_overflow_policy overflow_policy, , overflow_policy_(overflow_policy)
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms,
const std::function<void()>& worker_teardown_cb) :
logger(logger_name, begin, end),
_async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, _err_handler, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb))
{ {
} }
inline spdlog::async_logger::async_logger(const std::string& logger_name, inline spdlog::async_logger::async_logger(
sinks_init_list sinks_list, std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
size_t queue_size, : async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), tp, overflow_policy)
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms,
const std::function<void()>& worker_teardown_cb) :
async_logger(logger_name, sinks_list.begin(), sinks_list.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
inline spdlog::async_logger::async_logger(const std::string& logger_name,
sink_ptr single_sink,
size_t queue_size,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms,
const std::function<void()>& worker_teardown_cb) :
async_logger(logger_name,
{ {
single_sink
}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
inline void spdlog::async_logger::flush()
{
_async_log_helper->flush(true);
} }
// Error handler inline spdlog::async_logger::async_logger(
inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_handler) std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
: async_logger(std::move(logger_name), {single_sink}, tp, overflow_policy)
{ {
_err_handler = err_handler;
_async_log_helper->set_error_handler(err_handler);
}
inline spdlog::log_err_handler spdlog::async_logger::error_handler()
{
return _err_handler;
} }
// send the log message to the thread pool
inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter) inline void spdlog::async_logger::sink_it_(details::log_msg &msg)
{ {
_formatter = msg_formatter; #if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
_async_log_helper->set_formatter(_formatter); incr_msg_counter_(msg);
#endif
if (auto pool_ptr = thread_pool_.lock())
{
pool_ptr->post_log(shared_from_this(), std::move(msg), overflow_policy_);
}
else
{
throw spdlog_ex("async log: thread pool doens't exist anymore");
}
} }
inline void spdlog::async_logger::_set_pattern(const std::string& pattern, pattern_time_type pattern_time) // send flush request to the thread pool
inline void spdlog::async_logger::flush_()
{ {
_formatter = std::make_shared<pattern_formatter>(pattern, pattern_time); if (auto pool_ptr = thread_pool_.lock())
_async_log_helper->set_formatter(_formatter); {
pool_ptr->post_flush(shared_from_this(), overflow_policy_);
}
else
{
throw spdlog_ex("async flush: thread pool doesn't exist anymore");
}
} }
//
inline void spdlog::async_logger::_sink_it(details::log_msg& msg) // backend functions - called from the thread pool to do the actual job
//
inline void spdlog::async_logger::backend_log_(details::log_msg &incoming_log_msg)
{ {
try try
{ {
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) for (auto &s : sinks_)
msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed);
#endif
_async_log_helper->log(msg);
if (_should_flush_on(msg))
_async_log_helper->flush(false); // do async flush
}
catch (const std::exception &ex)
{ {
_err_handler(ex.what()); if (s->should_log(incoming_log_msg.level))
}
catch (...)
{ {
_err_handler("Unknown exception"); 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
}

View File

@@ -0,0 +1,62 @@
//
// Copyright(c) 2018 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
// cirucal q view of std::vector.
#pragma once
namespace spdlog {
namespace details {
template<typename T>
class circular_q
{
public:
using item_type = T;
explicit circular_q(size_t max_items)
: max_items_(max_items + 1) // one item is reserved as marker for full q
, v_(max_items_)
{
}
// push back, overrun (oldest) item if no room left
void push_back(T &&item)
{
v_[tail_] = std::move(item);
tail_ = (tail_ + 1) % max_items_;
if (tail_ == head_) // overrun last item if full
{
head_ = (head_ + 1) % max_items_;
}
}
// Pop item from front.
// If there are no elements in the container, the behavior is undefined.
void pop_front(T &popped_item)
{
popped_item = std::move(v_[head_]);
head_ = (head_ + 1) % max_items_;
}
bool empty()
{
return tail_ == head_;
}
bool full()
{
// head is ahead of the tail by 1
return ((tail_ + 1) % max_items_) == head_;
}
private:
size_t max_items_;
typename std::vector<T>::size_type head_ = 0;
typename std::vector<T>::size_type tail_ = 0;
std::vector<T> v_;
};
} // namespace details
} // namespace spdlog

View File

@@ -0,0 +1,61 @@
#pragma once
//
// Copyright(c) 2018 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include "spdlog/details/null_mutex.h"
#include "stdio.h"
#include <mutex>
namespace spdlog {
namespace details {
struct console_stdout
{
static FILE *stream()
{
return stdout;
}
#ifdef _WIN32
static HANDLE handle()
{
return ::GetStdHandle(STD_OUTPUT_HANDLE);
}
#endif
};
struct console_stderr
{
static FILE *stream()
{
return stderr;
}
#ifdef _WIN32
static HANDLE handle()
{
return ::GetStdHandle(STD_ERROR_HANDLE);
}
#endif
};
struct console_mutex
{
using mutex_t = std::mutex;
static mutex_t &mutex()
{
static mutex_t s_mutex;
return s_mutex;
}
};
struct console_nullmutex
{
using mutex_t = null_mutex;
static mutex_t &mutex()
{
static mutex_t s_mutex;
return s_mutex;
}
};
} // namespace details
} // namespace spdlog

View File

@@ -6,22 +6,22 @@
#pragma once #pragma once
// Helper class for file sink // Helper class for file sink
// When failing to open a file, retry several times(5) with small delay between the tries(10 ms) // When failing to open a file, retry several times(5) with small delay between
// the tries(10 ms)
// Throw spdlog_ex exception on errors // Throw spdlog_ex exception on errors
#include "spdlog/details/os.h" #include "../details/log_msg.h"
#include "spdlog/details/log_msg.h" #include "../details/os.h"
#include <cerrno>
#include <chrono> #include <chrono>
#include <cstdio> #include <cstdio>
#include <string> #include <string>
#include <thread> #include <thread>
#include <cerrno> #include <tuple>
namespace spdlog namespace spdlog {
{ namespace details {
namespace details
{
class file_helper class file_helper
{ {
@@ -30,9 +30,7 @@ public:
const int open_tries = 5; const int open_tries = 5;
const int open_interval = 10; const int open_interval = 10;
explicit file_helper() : explicit file_helper() = default;
_fd(nullptr)
{}
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;
@@ -42,19 +40,19 @@ public:
close(); close();
} }
void open(const filename_t &fname, bool truncate = false) void open(const filename_t &fname, bool truncate = false)
{ {
close(); close();
auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab"); auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
_filename = fname; _filename = fname;
for (int tries = 0; tries < open_tries; ++tries) for (int tries = 0; tries < open_tries; ++tries)
{ {
if (!os::fopen_s(&_fd, fname, mode)) if (!os::fopen_s(&fd_, fname, mode))
{
return; return;
}
std::this_thread::sleep_for(std::chrono::milliseconds(open_interval)); details::os::sleep_for_millis(open_interval);
} }
throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno); throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno);
@@ -63,39 +61,43 @@ public:
void reopen(bool truncate) void reopen(bool truncate)
{ {
if (_filename.empty()) if (_filename.empty())
{
throw spdlog_ex("Failed re opening file - was not opened before"); throw spdlog_ex("Failed re opening file - was not opened before");
}
open(_filename, truncate); open(_filename, truncate);
} }
void flush() void flush()
{ {
std::fflush(_fd); std::fflush(fd_);
} }
void close() void close()
{ {
if (_fd) if (fd_ != nullptr)
{ {
std::fclose(_fd); std::fclose(fd_);
_fd = nullptr; fd_ = nullptr;
} }
} }
void write(const log_msg& msg) 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)
{ {
size_t msg_size = msg.formatted.size();
auto data = msg.formatted.data();
if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno); throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
} }
}
size_t size() size_t size() const
{
if (fd_ == nullptr)
{ {
if (!_fd)
throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename)); throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
return os::filesize(_fd); }
return os::filesize(fd_);
} }
const filename_t &filename() const const filename_t &filename() const
@@ -103,15 +105,49 @@ public:
return _filename; return _filename;
} }
static bool file_exists(const filename_t& name) static bool file_exists(const filename_t &fname)
{ {
return os::file_exists(fname);
}
return os::file_exists(name); //
// 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")
static std::tuple<filename_t, filename_t> split_by_extenstion(const spdlog::filename_t &fname)
{
auto ext_index = fname.rfind('.');
// no valid extension found - return whole path and empty string as
// extension
if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
{
return std::make_tuple(fname, spdlog::filename_t());
}
// treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
auto folder_index = fname.rfind(details::os::folder_sep);
if (folder_index != fname.npos && folder_index >= ext_index - 1)
{
return std::make_tuple(fname, spdlog::filename_t());
}
// finally - return a valid base and extension tuple
return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
} }
private: private:
FILE* _fd; FILE *fd_{nullptr};
filename_t _filename; filename_t _filename;
}; };
} } // namespace details
} } // namespace spdlog

View File

@@ -0,0 +1,130 @@
//
// Created by gabi on 6/15/18.
//
#pragma once
#include "chrono"
#include "spdlog/fmt/fmt.h"
// Some fmt helpers to efficiently format and pad ints and strings
namespace spdlog {
namespace details {
namespace fmt_helper {
template<size_t Buffer_Size>
inline void append_str(const std::string &str, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
auto *str_ptr = str.data();
dest.append(str_ptr, str_ptr + str.size());
}
template<size_t Buffer_Size>
inline void append_c_str(const char *c_str, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
char ch;
while ((ch = *c_str) != '\0')
{
dest.push_back(ch);
++c_str;
}
}
template<size_t Buffer_Size1, size_t Buffer_Size2>
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<typename T, size_t Buffer_Size>
inline void append_int(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
fmt::format_int i(n);
dest.append(i.data(), i.data() + i.size());
}
template<size_t Buffer_Size>
inline void pad2(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
if (n > 99)
{
append_int(n, dest);
return;
}
if (n > 9) // 10-99
{
dest.push_back('0' + static_cast<char>(n / 10));
dest.push_back('0' + static_cast<char>(n % 10));
return;
}
if (n >= 0) // 0-9
{
dest.push_back('0');
dest.push_back('0' + static_cast<char>(n));
return;
}
// negatives (unlikely, but just in case, let fmt deal with it)
fmt::format_to(dest, "{:02}", n);
}
template<size_t Buffer_Size>
inline void pad3(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
if (n > 999)
{
append_int(n, dest);
return;
}
if (n > 99) // 100-999
{
append_int(n / 100, dest);
pad2(n % 100, dest);
return;
}
if (n > 9) // 10-99
{
dest.push_back('0');
dest.push_back('0' + static_cast<char>(n / 10));
dest.push_back('0' + static_cast<char>(n % 10));
return;
}
if (n >= 0)
{
dest.push_back('0');
dest.push_back('0');
dest.push_back('0' + static_cast<char>(n));
return;
}
// negatives (unlikely, but just in case let fmt deal with it)
fmt::format_to(dest, "{:03}", n);
}
template<size_t Buffer_Size>
inline void pad6(size_t n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
if (n > 99999)
{
append_int(n, dest);
return;
}
pad3(static_cast<int>(n / 1000), dest);
pad3(static_cast<int>(n % 1000), dest);
}
// return fraction of a second of the given time_point.
// e.g.
// fraction<std::milliseconds>(tp) -> will return the millis part of the second
template<typename ToDuration>
inline ToDuration time_fraction(const log_clock::time_point &tp)
{
using namespace std::chrono;
auto duration = tp.time_since_epoch();
auto secs = duration_cast<seconds>(duration);
return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);
}
} // namespace fmt_helper
} // namespace details
} // namespace spdlog

View File

@@ -8,21 +8,17 @@
#include "spdlog/common.h" #include "spdlog/common.h"
#include "spdlog/details/os.h" #include "spdlog/details/os.h"
#include <string> #include <string>
#include <utility> #include <utility>
namespace spdlog namespace spdlog {
{ namespace details {
namespace details
{
struct log_msg struct log_msg
{ {
log_msg() = default; log_msg() = default;
log_msg(const std::string *loggers_name, level::level_enum lvl) : log_msg(const std::string *loggers_name, level::level_enum lvl)
logger_name(loggers_name), : logger_name(loggers_name)
level(lvl), , level(lvl)
msg_id(0)
{ {
#ifndef SPDLOG_NO_DATETIME #ifndef SPDLOG_NO_DATETIME
time = os::now(); time = os::now();
@@ -34,17 +30,18 @@ struct log_msg
} }
log_msg(const log_msg &other) = delete; log_msg(const log_msg &other) = delete;
log_msg& operator=(log_msg&& other) = delete;
log_msg(log_msg &&other) = delete; log_msg(log_msg &&other) = delete;
log_msg &operator=(log_msg &&other) = delete;
const std::string *logger_name{nullptr};
const std::string *logger_name;
level::level_enum level; level::level_enum level;
log_clock::time_point time; log_clock::time_point time;
size_t thread_id; size_t thread_id;
fmt::MemoryWriter raw; fmt::memory_buffer raw;
fmt::MemoryWriter formatted; size_t msg_id{0};
size_t msg_id; // info about wrapping the formatted text with color
mutable size_t color_range_start{0};
mutable size_t color_range_end{0};
}; };
} } // namespace details
} } // namespace spdlog

View File

@@ -5,231 +5,135 @@
#pragma once #pragma once
#include "spdlog/logger.h"
#include "spdlog/sinks/stdout_sinks.h"
#include <memory> #include <memory>
#include <string> #include <string>
// create logger with given name, sinks and the default pattern formatter // create logger with given name, sinks and the default pattern formatter
// all other ctors will call this one // all other ctors will call this one
template<class It> template<typename It>
inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end): inline spdlog::logger::logger(std::string logger_name, const It &begin, const It &end)
_name(logger_name), : name_(std::move(logger_name))
_sinks(begin, end), , sinks_(begin, end)
_formatter(std::make_shared<pattern_formatter>("%+")), , level_(level::info)
_level(level::info), , flush_level_(level::off)
_flush_level(level::off), , last_err_time_(0)
_last_err_time(0), , msg_counter_(1) // message counter will start from 1. 0-message id will be
_msg_counter(1) // message counter will start from 1. 0-message id will be reserved for controll messages // reserved for controll messages
{ {
_err_handler = [this](const std::string &msg) err_handler_ = [this](const std::string &msg) { this->default_err_handler_(msg); };
{
this->_default_err_handler(msg);
};
} }
// ctor with sinks as init list // ctor with sinks as init list
inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list): inline spdlog::logger::logger(std::string logger_name, sinks_init_list sinks_list)
logger(logger_name, sinks_list.begin(), sinks_list.end()) : logger(std::move(logger_name), sinks_list.begin(), sinks_list.end())
{} {
}
// ctor with single sink // ctor with single sink
inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink): inline spdlog::logger::logger(std::string logger_name, spdlog::sink_ptr single_sink)
logger(logger_name, : logger(std::move(logger_name), {std::move(single_sink)})
{ {
single_sink }
})
{}
inline spdlog::logger::~logger() = default; inline spdlog::logger::~logger() = default;
inline void spdlog::logger::set_formatter(std::unique_ptr<spdlog::formatter> f)
inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter)
{ {
_set_formatter(msg_formatter); for (auto &sink : sinks_)
{
sink->set_formatter(f->clone());
}
} }
inline void spdlog::logger::set_pattern(const std::string& pattern, pattern_time_type pattern_time) inline void spdlog::logger::set_pattern(std::string pattern, pattern_time_type time_type)
{ {
_set_pattern(pattern, pattern_time); set_formatter(std::unique_ptr<spdlog::formatter>(new pattern_formatter(std::move(pattern), time_type)));
} }
template<typename... Args> template<typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args) inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args)
{ {
if (!should_log(lvl)) return; if (!should_log(lvl))
{
return;
}
try try
{ {
details::log_msg log_msg(&_name, lvl); details::log_msg log_msg(&name_, lvl);
log_msg.raw.write(fmt, args...); fmt::format_to(log_msg.raw, fmt, args...);
_sink_it(log_msg); sink_it_(log_msg);
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
} }
SPDLOG_CATCH_AND_HANDLE
} }
template<typename... Args> template<typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char *msg) inline void spdlog::logger::log(level::level_enum lvl, const char *msg)
{ {
if (!should_log(lvl)) return; if (!should_log(lvl))
{
return;
}
try try
{ {
details::log_msg log_msg(&_name, lvl); details::log_msg log_msg(&name_, lvl);
log_msg.raw << msg; fmt::format_to(log_msg.raw, "{}", msg);
_sink_it(log_msg); sink_it_(log_msg);
} }
catch (const std::exception &ex) SPDLOG_CATCH_AND_HANDLE
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
} }
template<typename T> template<typename T>
inline void spdlog::logger::log(level::level_enum lvl, const T &msg) inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
{ {
if (!should_log(lvl)) return; if (!should_log(lvl))
{
return;
}
try try
{ {
details::log_msg log_msg(&_name, lvl); details::log_msg log_msg(&name_, lvl);
log_msg.raw << msg; fmt::format_to(log_msg.raw, "{}", msg);
_sink_it(log_msg); sink_it_(log_msg);
} }
catch (const std::exception &ex) SPDLOG_CATCH_AND_HANDLE
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::trace(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::trace, fmt, arg1, args...);
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::debug(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::debug, fmt, arg1, args...);
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::info(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::info, fmt, arg1, args...);
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::warn(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::warn, fmt, arg1, args...);
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::error(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::err, fmt, arg1, args...);
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::critical(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::critical, fmt, arg1, args...);
} }
template<typename... Args> template<typename... Args>
inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const char* msg) inline void spdlog::logger::trace(const char *fmt, const Args &... args)
{ {
if (flag) log(level::trace, fmt, args...);
{
log(lvl, msg);
}
} }
template<typename T> template<typename... Args>
inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const T& msg) inline void spdlog::logger::debug(const char *fmt, const Args &... args)
{ {
if (flag) log(level::debug, fmt, args...);
{
log(lvl, msg);
}
} }
template <typename Arg1, typename... Args> template<typename... Args>
inline void spdlog::logger::trace_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args) inline void spdlog::logger::info(const char *fmt, const Args &... args)
{ {
if (flag) log(level::info, fmt, args...);
{
log(level::trace, fmt, arg1, args...);
}
} }
template <typename Arg1, typename... Args> template<typename... Args>
inline void spdlog::logger::debug_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args) inline void spdlog::logger::warn(const char *fmt, const Args &... args)
{ {
if (flag) log(level::warn, fmt, args...);
{
log(level::debug, fmt, arg1, args...);
}
} }
template <typename Arg1, typename... Args> template<typename... Args>
inline void spdlog::logger::info_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args) inline void spdlog::logger::error(const char *fmt, const Args &... args)
{ {
if (flag) log(level::err, fmt, args...);
{
log(level::info, fmt, arg1, args...);
}
} }
template <typename Arg1, typename... Args> template<typename... Args>
inline void spdlog::logger::warn_if(const bool flag, const char* fmt, const Arg1& arg1, const Args&... args) inline void spdlog::logger::critical(const char *fmt, const Args &... args)
{ {
if (flag) log(level::critical, fmt, args...);
{
log(level::warn, fmt, arg1, args...);
} }
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::error_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
{
if (flag)
{
log(level::err, fmt, arg1, args...);
}
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::critical_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
{
if (flag)
{
log(level::critical, fmt, arg1, args...);
}
}
template<typename T> template<typename T>
inline void spdlog::logger::trace(const T &msg) inline void spdlog::logger::trace(const T &msg)
@@ -243,14 +147,12 @@ inline void spdlog::logger::debug(const T& msg)
log(level::debug, msg); log(level::debug, msg);
} }
template<typename T> template<typename T>
inline void spdlog::logger::info(const T &msg) inline void spdlog::logger::info(const T &msg)
{ {
log(level::info, msg); log(level::info, msg);
} }
template<typename T> template<typename T>
inline void spdlog::logger::warn(const T &msg) inline void spdlog::logger::warn(const T &msg)
{ {
@@ -269,79 +171,26 @@ inline void spdlog::logger::critical(const T& msg)
log(level::critical, msg); log(level::critical, msg);
} }
template<typename T>
inline void spdlog::logger::trace_if(const bool flag, const T& msg)
{
if (flag)
{
log(level::trace, msg);
}
}
template<typename T>
inline void spdlog::logger::debug_if(const bool flag, const T& msg)
{
if (flag)
{
log(level::debug, msg);
}
}
template<typename T>
inline void spdlog::logger::info_if(const bool flag, const T& msg)
{
if (flag)
{
log(level::info, msg);
}
}
template<typename T>
inline void spdlog::logger::warn_if(const bool flag, const T& msg)
{
if (flag)
{
log(level::warn, msg);
}
}
template<typename T>
inline void spdlog::logger::error_if(const bool flag, const T& msg)
{
if (flag)
{
log(level::err, msg);
}
}
template<typename T>
inline void spdlog::logger::critical_if(const bool flag, const T& msg)
{
if (flag)
{
log(level::critical, msg);
}
}
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
#include <codecvt>
template <typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* msg)
{
std::wstring_convert<std::codecvt_utf8<wchar_t> > conv;
log(lvl, conv.to_bytes(msg));
}
template<typename... Args> template<typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args) inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args)
{ {
fmt::WMemoryWriter wWriter; if (!should_log(lvl))
{
return;
}
wWriter.write(fmt, args...); decltype(wstring_converter_)::byte_string utf8_string;
log(lvl, wWriter.c_str());
try
{
{
std::lock_guard<std::mutex> lock(wstring_converter_mutex_);
utf8_string = wstring_converter_.to_bytes(fmt);
}
log(lvl, utf8_string.c_str(), args...);
}
SPDLOG_CATCH_AND_HANDLE
} }
template<typename... Args> template<typename... Args>
@@ -362,7 +211,6 @@ inline void spdlog::logger::info(const wchar_t* fmt, const Args&... args)
log(level::info, fmt, args...); log(level::info, fmt, args...);
} }
template<typename... Args> template<typename... Args>
inline void spdlog::logger::warn(const wchar_t *fmt, const Args &... args) inline void spdlog::logger::warn(const wchar_t *fmt, const Args &... args)
{ {
@@ -381,136 +229,71 @@ inline void spdlog::logger::critical(const wchar_t* fmt, const Args&... args)
log(level::critical, fmt, args...); log(level::critical, fmt, args...);
} }
//
// conditional logging
//
template <typename... Args>
inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const wchar_t* msg)
{
if (flag)
{
log(lvl, msg);
}
}
template <typename... Args>
inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(lvl, fmt, args);
}
}
template <typename... Args>
inline void spdlog::logger::trace_if(const bool flag, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(level::trace, fmt, args...);
}
}
template <typename... Args>
inline void spdlog::logger::debug_if(const bool flag, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(level::debug, fmt, args...);
}
}
template <typename... Args>
inline void spdlog::logger::info_if(const bool flag, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(level::info, fmt, args...);
}
}
template <typename... Args>
inline void spdlog::logger::warn_if(const bool flag, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(level::warn, fmt, args...);
}
}
template <typename... Args>
inline void spdlog::logger::error_if(const bool flag, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(level::err, fmt, args...);
}
}
template <typename... Args>
inline void spdlog::logger::critical_if(const bool flag, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(level::critical, fmt, args...);
}
}
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT #endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
// //
// name and level // name and level
// //
inline const std::string &spdlog::logger::name() const inline const std::string &spdlog::logger::name() const
{ {
return _name; return name_;
} }
inline void spdlog::logger::set_level(spdlog::level::level_enum log_level) inline void spdlog::logger::set_level(spdlog::level::level_enum log_level)
{ {
_level.store(log_level); level_.store(log_level);
} }
inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler) inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler)
{ {
_err_handler = err_handler; err_handler_ = std::move(err_handler);
} }
inline spdlog::log_err_handler spdlog::logger::error_handler() inline spdlog::log_err_handler spdlog::logger::error_handler()
{ {
return _err_handler; return err_handler_;
} }
inline void spdlog::logger::flush()
{
try
{
flush_();
}
SPDLOG_CATCH_AND_HANDLE
}
inline void spdlog::logger::flush_on(level::level_enum log_level) inline void spdlog::logger::flush_on(level::level_enum log_level)
{ {
_flush_level.store(log_level); flush_level_.store(log_level);
}
inline bool spdlog::logger::should_flush_(const details::log_msg &msg)
{
auto flush_level = flush_level_.load(std::memory_order_relaxed);
return (msg.level >= flush_level) && (msg.level != level::off);
} }
inline spdlog::level::level_enum spdlog::logger::level() const inline spdlog::level::level_enum spdlog::logger::level() const
{ {
return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed)); 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 inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const
{ {
return msg_level >= _level.load(std::memory_order_relaxed); 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 // 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) inline void spdlog::logger::sink_it_(details::log_msg &msg)
{ {
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) #if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed); incr_msg_counter_(msg);
#endif #endif
_formatter->format(msg); for (auto &sink : sinks_)
for (auto &sink : _sinks)
{ {
if (sink->should_log(msg.level)) if (sink->should_log(msg.level))
{ {
@@ -518,46 +301,45 @@ inline void spdlog::logger::_sink_it(details::log_msg& msg)
} }
} }
if(_should_flush_on(msg)) if (should_flush_(msg))
{
flush(); flush();
} }
inline void spdlog::logger::_set_pattern(const std::string& pattern, pattern_time_type pattern_time)
{
_formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
}
inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter)
{
_formatter = msg_formatter;
} }
inline void spdlog::logger::flush() inline void spdlog::logger::flush_()
{
for (auto &sink : sinks_)
{ {
for (auto& sink : _sinks)
sink->flush(); sink->flush();
} }
}
inline void spdlog::logger::_default_err_handler(const std::string &msg) inline void spdlog::logger::default_err_handler_(const std::string &msg)
{ {
auto now = time(nullptr); auto now = time(nullptr);
if (now - _last_err_time < 60) if (now - last_err_time_ < 60)
{
return; return;
}
last_err_time_ = now;
auto tm_time = details::os::localtime(now); auto tm_time = details::os::localtime(now);
char date_buf[100]; char date_buf[100];
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time); std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
details::log_msg err_msg; fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), msg);
err_msg.formatted.write("[*** LOG ERROR ***] [{}] [{}] [{}]{}", name(), msg, date_buf, details::os::eol);
sinks::stderr_sink_mt::instance()->log(err_msg);
_last_err_time = now;
} }
inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg) inline void spdlog::logger::incr_msg_counter_(details::log_msg &msg)
{ {
const auto flush_level = _flush_level.load(std::memory_order_relaxed); msg.msg_id = msg_counter_.fetch_add(1, std::memory_order_relaxed);
return (msg.level >= flush_level) && (msg.level != level::off);
} }
inline const std::vector<spdlog::sink_ptr> &spdlog::logger::sinks() const inline const std::vector<spdlog::sink_ptr> &spdlog::logger::sinks() const
{ {
return _sinks; return sinks_;
}
inline std::vector<spdlog::sink_ptr> &spdlog::logger::sinks()
{
return sinks_;
} }

View File

@@ -0,0 +1,115 @@
#pragma once
//
// Copyright(c) 2018 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
// multi producer-multi consumer blocking queue.
// enqueue(..) - will block until room found to put the new message.
// enqueue_nowait(..) - will return immediately with false if no room left in
// the queue.
// dequeue_for(..) - will block until the queue is not empty or timeout have
// passed.
#include "spdlog/details/circular_q.h"
#include <condition_variable>
#include <mutex>
namespace spdlog {
namespace details {
template<typename T>
class mpmc_blocking_queue
{
public:
using item_type = T;
explicit mpmc_blocking_queue(size_t max_items)
: q_(max_items)
{
}
#ifndef __MINGW32__
// try to enqueue and block if no room left
void enqueue(T &&item)
{
{
std::unique_lock<std::mutex> lock(queue_mutex_);
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
q_.push_back(std::move(item));
}
push_cv_.notify_one();
}
// enqueue immediately. overrun oldest message in the queue if no room left.
void enqueue_nowait(T &&item)
{
{
std::unique_lock<std::mutex> lock(queue_mutex_);
q_.push_back(std::move(item));
}
push_cv_.notify_one();
}
// try to dequeue item. if no item found. wait upto timeout and try again
// Return true, if succeeded dequeue item, false otherwise
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
{
{
std::unique_lock<std::mutex> lock(queue_mutex_);
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
{
return false;
}
q_.pop_front(popped_item);
}
pop_cv_.notify_one();
return true;
}
#else
// apparently mingw deadlocks if the mutex is released before cv.notify_one(),
// so release the mutex at the very end each function.
// try to enqueue and block if no room left
void enqueue(T &&item)
{
std::unique_lock<std::mutex> lock(queue_mutex_);
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
q_.push_back(std::move(item));
push_cv_.notify_one();
}
// enqueue immediately. overrun oldest message in the queue if no room left.
void enqueue_nowait(T &&item)
{
std::unique_lock<std::mutex> lock(queue_mutex_);
q_.push_back(std::move(item));
push_cv_.notify_one();
}
// try to dequeue item. if no item found. wait upto timeout and try again
// Return true, if succeeded dequeue item, false otherwise
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
{
std::unique_lock<std::mutex> lock(queue_mutex_);
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
{
return false;
}
q_.pop_front(popped_item);
pop_cv_.notify_one();
return true;
}
#endif
private:
std::mutex queue_mutex_;
std::condition_variable push_cv_;
std::condition_variable pop_cv_;
spdlog::details::circular_q<T> q_;
};
} // namespace details
} // namespace spdlog

View File

@@ -1,172 +0,0 @@
/*
A modified version of Bounded MPMC queue by Dmitry Vyukov.
Original code from:
http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
licensed by Dmitry Vyukov under the terms below:
Simplified BSD license
Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 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.
The views and conclusions contained in the software and documentation are those of the authors and
should not be interpreted as representing official policies, either expressed or implied, of Dmitry Vyukov.
*/
/*
The code in its current form adds the license below:
Copyright(c) 2015 Gabi Melman.
Distributed under the MIT License (http://opensource.org/licenses/MIT)
*/
#pragma once
#include "spdlog/common.h"
#include <atomic>
#include <utility>
namespace spdlog
{
namespace details
{
template<typename T>
class mpmc_bounded_queue
{
public:
using item_type = T;
mpmc_bounded_queue(size_t buffer_size)
:max_size_(buffer_size),
buffer_(new cell_t [buffer_size]),
buffer_mask_(buffer_size - 1)
{
//queue size must be power of two
if(!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0)))
throw spdlog_ex("async logger queue size must be power of two");
for (size_t i = 0; i != buffer_size; i += 1)
buffer_[i].sequence_.store(i, std::memory_order_relaxed);
enqueue_pos_.store(0, std::memory_order_relaxed);
dequeue_pos_.store(0, std::memory_order_relaxed);
}
~mpmc_bounded_queue()
{
delete [] buffer_;
}
bool enqueue(T&& data)
{
cell_t* cell;
size_t pos = enqueue_pos_.load(std::memory_order_relaxed);
for (;;)
{
cell = &buffer_[pos & buffer_mask_];
size_t seq = cell->sequence_.load(std::memory_order_acquire);
intptr_t dif = (intptr_t)seq - (intptr_t)pos;
if (dif == 0)
{
if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
break;
}
else if (dif < 0)
{
return false;
}
else
{
pos = enqueue_pos_.load(std::memory_order_relaxed);
}
}
cell->data_ = std::move(data);
cell->sequence_.store(pos + 1, std::memory_order_release);
return true;
}
bool dequeue(T& data)
{
cell_t* cell;
size_t pos = dequeue_pos_.load(std::memory_order_relaxed);
for (;;)
{
cell = &buffer_[pos & buffer_mask_];
size_t seq =
cell->sequence_.load(std::memory_order_acquire);
intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1);
if (dif == 0)
{
if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
break;
}
else if (dif < 0)
return false;
else
pos = dequeue_pos_.load(std::memory_order_relaxed);
}
data = std::move(cell->data_);
cell->sequence_.store(pos + buffer_mask_ + 1, std::memory_order_release);
return true;
}
size_t approx_size()
{
size_t first_pos = dequeue_pos_.load(std::memory_order_relaxed);
size_t last_pos = enqueue_pos_.load(std::memory_order_relaxed);
if (last_pos <= first_pos)
return 0;
auto size = last_pos - first_pos;
return size < max_size_ ? size : max_size_;
}
private:
struct cell_t
{
std::atomic<size_t> sequence_;
T data_;
};
size_t const max_size_;
static size_t const cacheline_size = 64;
typedef char cacheline_pad_t [cacheline_size];
cacheline_pad_t pad0_;
cell_t* const buffer_;
size_t const buffer_mask_;
cacheline_pad_t pad1_;
std::atomic<size_t> enqueue_pos_;
cacheline_pad_t pad2_;
std::atomic<size_t> dequeue_pos_;
cacheline_pad_t pad3_;
mpmc_bounded_queue(mpmc_bounded_queue const&) = delete;
void operator= (mpmc_bounded_queue const&) = delete;
};
} // ns details
} // ns spdlog

View File

@@ -8,10 +8,8 @@
#include <atomic> #include <atomic>
// 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() {}
@@ -27,8 +25,10 @@ struct null_atomic_int
int value; int value;
null_atomic_int() = default; null_atomic_int() = default;
null_atomic_int(int val):value(val) explicit null_atomic_int(int val)
{} : value(val)
{
}
int load(std::memory_order) const int load(std::memory_order) const
{ {
@@ -41,5 +41,5 @@ struct null_atomic_int
} }
}; };
} } // namespace details
} } // namespace spdlog

View File

@@ -4,19 +4,19 @@
// //
#pragma once #pragma once
#include "spdlog/common.h" #include "../common.h"
#include <algorithm>
#include <chrono>
#include <cstdio> #include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime> #include <ctime>
#include <functional> #include <functional>
#include <string> #include <string>
#include <chrono>
#include <thread>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <thread>
#ifdef _WIN32 #ifdef _WIN32
@@ -27,9 +27,9 @@
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif #endif
#include <windows.h>
#include <process.h> // _get_pid support
#include <io.h> // _get_osfhandle and _isatty support #include <io.h> // _get_osfhandle and _isatty support
#include <process.h> // _get_pid support
#include <windows.h>
#ifdef __MINGW32__ #ifdef __MINGW32__
#include <share.h> #include <share.h>
@@ -37,8 +37,8 @@
#else // unix #else // unix
#include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h>
#ifdef __linux__ #ifdef __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id #include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
@@ -53,13 +53,9 @@
#define __has_feature(x) 0 // Compatibility with non-clang compilers. #define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif #endif
namespace spdlog {
namespace spdlog namespace details {
{ namespace os {
namespace details
{
namespace os
{
inline spdlog::log_clock::time_point now() inline spdlog::log_clock::time_point now()
{ {
@@ -68,14 +64,11 @@ inline spdlog::log_clock::time_point now()
timespec ts; timespec ts;
::clock_gettime(CLOCK_REALTIME_COARSE, &ts); ::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
return std::chrono::time_point<log_clock, typename log_clock::duration>( return std::chrono::time_point<log_clock, typename log_clock::duration>(
std::chrono::duration_cast<typename log_clock::duration>( std::chrono::duration_cast<typename log_clock::duration>(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
#else #else
return log_clock::now(); return log_clock::now();
#endif #endif
} }
inline std::tm localtime(const std::time_t &time_tt) inline std::tm localtime(const std::time_t &time_tt)
{ {
@@ -96,7 +89,6 @@ inline std::tm localtime()
return localtime(now_t); return localtime(now_t);
} }
inline std::tm gmtime(const std::time_t &time_tt) inline std::tm gmtime(const std::time_t &time_tt)
{ {
@@ -117,13 +109,8 @@ inline std::tm gmtime()
} }
inline bool operator==(const std::tm &tm1, const std::tm &tm2) inline bool operator==(const std::tm &tm1, const std::tm &tm2)
{ {
return (tm1.tm_sec == tm2.tm_sec && return (tm1.tm_sec == tm2.tm_sec && tm1.tm_min == tm2.tm_min && tm1.tm_hour == tm2.tm_hour && tm1.tm_mday == tm2.tm_mday &&
tm1.tm_min == tm2.tm_min && tm1.tm_mon == tm2.tm_mon && tm1.tm_year == tm2.tm_year && tm1.tm_isdst == tm2.tm_isdst);
tm1.tm_hour == tm2.tm_hour &&
tm1.tm_mday == tm2.tm_mday &&
tm1.tm_mon == tm2.tm_mon &&
tm1.tm_year == tm2.tm_year &&
tm1.tm_isdst == tm2.tm_isdst);
} }
inline bool operator!=(const std::tm &tm1, const std::tm &tm2) inline bool operator!=(const std::tm &tm1, const std::tm &tm2)
@@ -140,25 +127,35 @@ inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
#endif #endif
#endif #endif
SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL; SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1;
// folder separator
#ifdef _WIN32
SPDLOG_CONSTEXPR static const char folder_sep = '\\';
#else
SPDLOG_CONSTEXPR static const char folder_sep = '/';
#endif
inline void prevent_child_fd(FILE *f) inline void prevent_child_fd(FILE *f)
{ {
#ifdef _WIN32 #ifdef _WIN32
#if !defined(__cplusplus_winrt)
auto file_handle = (HANDLE)_get_osfhandle(_fileno(f)); auto file_handle = (HANDLE)_get_osfhandle(_fileno(f));
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
throw spdlog_ex("SetHandleInformation failed", errno); throw spdlog_ex("SetHandleInformation failed", errno);
#endif
#else #else
auto fd = fileno(f); auto fd = fileno(f);
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
{
throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno); throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno);
}
#endif #endif
} }
// fopen_s on non windows for writing // fopen_s on non windows for writing
inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode) inline bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
{ {
#ifdef _WIN32 #ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES #ifdef SPDLOG_WCHAR_FILENAMES
@@ -172,12 +169,13 @@ inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode
#ifdef SPDLOG_PREVENT_CHILD_FD #ifdef SPDLOG_PREVENT_CHILD_FD
if (*fp != nullptr) if (*fp != nullptr)
{
prevent_child_fd(*fp); prevent_child_fd(*fp);
}
#endif #endif
return *fp == nullptr; return *fp == nullptr;
} }
inline int remove(const filename_t &filename) inline int remove(const filename_t &filename)
{ {
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
@@ -196,7 +194,6 @@ inline int rename(const filename_t& filename1, const filename_t& filename2)
#endif #endif
} }
// Return if file exists // Return if file exists
inline bool file_exists(const filename_t &filename) inline bool file_exists(const filename_t &filename)
{ {
@@ -213,46 +210,51 @@ inline bool file_exists(const filename_t& filename)
#endif #endif
} }
// Return file size according to open FILE* object // Return file size according to open FILE* object
inline size_t filesize(FILE *f) inline size_t filesize(FILE *f)
{ {
if (f == nullptr) if (f == nullptr)
{
throw spdlog_ex("Failed getting file size. fd is null"); throw spdlog_ex("Failed getting file size. fd is null");
#ifdef _WIN32 }
#if defined(_WIN32) && !defined(__CYGWIN__)
int fd = _fileno(f); int fd = _fileno(f);
#if _WIN64 // 64 bits #if _WIN64 // 64 bits
struct _stat64 st; struct _stat64 st;
if (_fstat64(fd, &st) == 0) if (_fstat64(fd, &st) == 0)
{
return st.st_size; return st.st_size;
}
#else // windows 32 bits #else // windows 32 bits
long ret = _filelength(fd); long ret = _filelength(fd);
if (ret >= 0) if (ret >= 0)
{
return static_cast<size_t>(ret); return static_cast<size_t>(ret);
}
#endif #endif
#else // unix #else // unix
int fd = fileno(f); int fd = fileno(f);
//64 bits(but not in osx, where fstat64 is deprecated) // 64 bits(but not in osx or cygwin, where fstat64 is deprecated)
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) #if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__)
struct stat64 st; struct stat64 st;
if (fstat64(fd, &st) == 0) if (fstat64(fd, &st) == 0)
{
return static_cast<size_t>(st.st_size); return static_cast<size_t>(st.st_size);
#else // unix 32 bits or osx }
#else // unix 32 bits or cygwin
struct stat st; struct stat st;
if (fstat(fd, &st) == 0) if (fstat(fd, &st) == 0)
{
return static_cast<size_t>(st.st_size); return static_cast<size_t>(st.st_size);
}
#endif #endif
#endif #endif
throw spdlog_ex("Failed getting file size from fd", errno); 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()) inline int utc_minutes_offset(const std::tm &tm = details::os::localtime())
{ {
@@ -270,13 +272,17 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
int offset = -tzinfo.Bias; int offset = -tzinfo.Bias;
if (tm.tm_isdst) if (tm.tm_isdst)
{
offset -= tzinfo.DaylightBias; offset -= tzinfo.DaylightBias;
}
else else
{
offset -= tzinfo.StandardBias; offset -= tzinfo.StandardBias;
}
return offset; return offset;
#else #else
#if defined(sun) || defined(__sun) #if defined(sun) || defined(__sun) || defined(_AIX)
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
struct helper struct helper
{ {
@@ -287,16 +293,15 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
long int days = ( long int days = (
// difference in day of year // difference in day of year
localtm.tm_yday - gmtm.tm_yday localtm.tm_yday -
gmtm.tm_yday
// + intervening leap days // + intervening leap days
+ ((local_year >> 2) - (gmt_year >> 2)) + ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
- (local_year / 100 - gmt_year / 100) ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
+ ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
// + difference in years * 365 */ // + difference in years * 365 */
+ (long int)(local_year - gmt_year) * 365 + (long int)(local_year - gmt_year) * 365);
);
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour); long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min); long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
@@ -306,9 +311,9 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
} }
}; };
long int offset_seconds = helper::calculate_gmt_offset(tm); auto offset_seconds = helper::calculate_gmt_offset(tm);
#else #else
long int offset_seconds = tm.tm_gmtoff; auto offset_seconds = tm.tm_gmtoff;
#endif #endif
return static_cast<int>(offset_seconds / 60); return static_cast<int>(offset_seconds / 60);
@@ -316,7 +321,8 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
} }
// 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(espcially under VS 2013) // It exists because the std::this_thread::get_id() is much slower(especially
// under VS 2013)
inline size_t _thread_id() inline size_t _thread_id()
{ {
#ifdef _WIN32 #ifdef _WIN32
@@ -342,16 +348,25 @@ inline size_t _thread_id()
// 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() inline size_t thread_id()
{ {
#if defined(_MSC_VER) && (_MSC_VER < 1900) || defined(__clang__) && !__has_feature(cxx_thread_local) #if defined(SPDLOG_DISABLE_TID_CACHING) || (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt) || \
(defined(__clang__) && !__has_feature(cxx_thread_local))
return _thread_id(); return _thread_id();
#else #else // cache thread id in tls
static thread_local const size_t tid = _thread_id(); static thread_local const size_t tid = _thread_id();
return tid; return tid;
#endif #endif
} }
// This is avoid msvc issue in sleep_for that happens if the clock changes.
// See https://github.com/gabime/spdlog/issues/609
inline void sleep_for_millis(int milliseconds)
{
#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) // wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
@@ -369,73 +384,25 @@ inline std::string filename_to_str(const filename_t& filename)
} }
#endif #endif
inline std::string errno_to_string(char[256], char* res)
{
return std::string(res);
}
inline std::string errno_to_string(char buf[256], int res)
{
if (res == 0)
{
return std::string(buf);
}
else
{
return "Unknown error";
}
}
// Return errno string (thread safe)
inline std::string errno_str(int err_num)
{
char buf[256];
SPDLOG_CONSTEXPR auto buf_size = sizeof(buf);
#ifdef _WIN32
if (strerror_s(buf, buf_size, err_num) == 0)
return std::string(buf);
else
return "Unknown error";
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \
((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version
if (strerror_r(err_num, buf, buf_size) == 0)
return std::string(buf);
else
return "Unknown error";
#else // gnu version (might not use the given buf, so its retval pointer must be used)
auto err = strerror_r(err_num, buf, buf_size); // let compiler choose type
return errno_to_string(buf, err); // use overloading to select correct stringify function
#endif
}
inline int pid() inline int pid()
{ {
#ifdef _WIN32 #ifdef _WIN32
return ::_getpid(); return static_cast<int>(::GetCurrentProcessId());
#else #else
return static_cast<int>(::getpid()); return static_cast<int>(::getpid());
#endif #endif
} }
// Determine if the terminal supports colors
// Detrmine if the terminal supports colors
// Source: https://github.com/agauniyal/rang/ // Source: https://github.com/agauniyal/rang/
inline bool is_color_terminal() inline bool is_color_terminal()
{ {
#ifdef _WIN32 #ifdef _WIN32
return true; return true;
#else #else
static constexpr const char* Terms[] = static constexpr const char *Terms[] = {
{ "ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"};
"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm",
"linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"
};
const char *env_p = std::getenv("TERM"); const char *env_p = std::getenv("TERM");
if (env_p == nullptr) if (env_p == nullptr)
@@ -443,27 +410,23 @@ inline bool is_color_terminal()
return false; return false;
} }
static const bool result = std::any_of( static const bool result =
std::begin(Terms), std::end(Terms), [&](const char* term) std::any_of(std::begin(Terms), std::end(Terms), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
{
return std::strstr(env_p, term) != nullptr;
});
return result; return result;
#endif #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) inline bool in_terminal(FILE *file)
{ {
#ifdef _WIN32 #ifdef _WIN32
return _isatty(_fileno(file)) ? true : false; return _isatty(_fileno(file)) != 0;
#else #else
return isatty(fileno(file)) ? true : false; return isatty(fileno(file)) != 0;
#endif #endif
} }
} //os } // namespace os
} //details } // namespace details
} //spdlog } // namespace spdlog

View File

@@ -0,0 +1,772 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include "spdlog/details/fmt_helper.h"
#include "spdlog/details/log_msg.h"
#include "spdlog/details/os.h"
#include "spdlog/fmt/fmt.h"
#include "spdlog/formatter.h"
#include <array>
#include <chrono>
#include <ctime>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <utility>
#include <vector>
namespace spdlog {
namespace details {
class flag_formatter
{
public:
virtual ~flag_formatter() = default;
virtual void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) = 0;
};
///////////////////////////////////////////////////////////////////////
// name & level pattern appenders
///////////////////////////////////////////////////////////////////////
class name_formatter : public flag_formatter
{
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{
fmt_helper::append_str(*msg.logger_name, dest);
}
};
// log level appender
class level_formatter : public flag_formatter
{
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{
fmt_helper::append_c_str(level::to_c_str(msg.level), dest);
}
};
// short log level appender
class short_level_formatter : public flag_formatter
{
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{
fmt_helper::append_c_str(level::to_short_c_str(msg.level), dest);
}
};
///////////////////////////////////////////////////////////////////////
// Date time pattern appenders
///////////////////////////////////////////////////////////////////////
static const char *ampm(const tm &t)
{
return t.tm_hour >= 12 ? "PM" : "AM";
}
static unsigned int to12h(const tm &t)
{
return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour;
}
// Abbreviated weekday name
static const char *days[]{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
class a_formatter : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
fmt_helper::append_c_str(days[tm_time.tm_wday], dest);
}
};
// Full weekday name
static const char *full_days[]{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
class A_formatter : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
fmt_helper::append_c_str(full_days[tm_time.tm_wday], dest);
}
};
// Abbreviated month
static const char *months[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"};
class b_formatter : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
fmt_helper::append_c_str(months[tm_time.tm_mon], dest);
}
};
// Full month name
static const char *full_months[]{
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
class B_formatter : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
fmt_helper::append_c_str(full_months[tm_time.tm_mon], dest);
}
};
// Date and time representation (Thu Aug 23 15:35:46 2014)
class c_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
// fmt::format_to(dest, "{} {} {} ", days[tm_time.tm_wday],
// months[tm_time.tm_mon], tm_time.tm_mday);
// date
fmt_helper::append_str(days[tm_time.tm_wday], dest);
dest.push_back(' ');
fmt_helper::append_str(months[tm_time.tm_mon], dest);
dest.push_back(' ');
fmt_helper::append_int(tm_time.tm_mday, dest);
dest.push_back(' ');
// time
fmt_helper::pad2(tm_time.tm_hour, dest);
dest.push_back(':');
fmt_helper::pad2(tm_time.tm_min, dest);
dest.push_back(':');
fmt_helper::pad2(tm_time.tm_sec, dest);
dest.push_back(' ');
fmt_helper::append_int(tm_time.tm_year + 1900, dest);
}
};
// year - 2 digit
class C_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
fmt_helper::pad2(tm_time.tm_year % 100, dest);
}
};
// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
class D_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
fmt_helper::pad2(tm_time.tm_mon + 1, dest);
dest.push_back('/');
fmt_helper::pad2(tm_time.tm_mday, dest);
dest.push_back('/');
fmt_helper::pad2(tm_time.tm_year % 100, dest);
}
};
// year - 4 digit
class Y_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
fmt_helper::append_int(tm_time.tm_year + 1900, dest);
}
};
// month 1-12
class m_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
fmt_helper::pad2(tm_time.tm_mon + 1, dest);
}
};
// day of month 1-31
class d_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
fmt_helper::pad2(tm_time.tm_mday, dest);
}
};
// hours in 24 format 0-23
class H_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
fmt_helper::pad2(tm_time.tm_hour, dest);
}
};
// hours in 12 format 1-12
class I_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
fmt_helper::pad2(to12h(tm_time), dest);
}
};
// minutes 0-59
class M_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
fmt_helper::pad2(tm_time.tm_min, dest);
}
};
// seconds 0-59
class S_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
fmt_helper::pad2(tm_time.tm_sec, dest);
}
};
// milliseconds
class e_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{
auto millis = fmt_helper::time_fraction<std::chrono::milliseconds>(msg.time);
fmt_helper::pad3(static_cast<int>(millis.count()), dest);
}
};
// microseconds
class f_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{
auto micros = fmt_helper::time_fraction<std::chrono::microseconds>(msg.time);
fmt_helper::pad6(static_cast<int>(micros.count()), dest);
}
};
// nanoseconds
class F_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{
auto ns = fmt_helper::time_fraction<std::chrono::nanoseconds>(msg.time);
fmt::format_to(dest, "{:09}", ns.count());
}
};
// seconds since epoch
class E_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{
auto duration = msg.time.time_since_epoch();
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
fmt_helper::append_int(seconds, dest);
}
};
// AM/PM
class p_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
fmt_helper::append_c_str(ampm(tm_time), dest);
}
};
// 12 hour clock 02:55:02 pm
class r_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
fmt_helper::pad2(to12h(tm_time), dest);
dest.push_back(':');
fmt_helper::pad2(tm_time.tm_min, dest);
dest.push_back(':');
fmt_helper::pad2(tm_time.tm_sec, dest);
dest.push_back(' ');
fmt_helper::append_c_str(ampm(tm_time), dest);
}
};
// 24-hour HH:MM time, equivalent to %H:%M
class R_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
fmt_helper::pad2(tm_time.tm_hour, dest);
dest.push_back(':');
fmt_helper::pad2(tm_time.tm_min, dest);
}
};
// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
class T_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
// fmt::format_to(dest, "{:02}:{:02}:{:02}", tm_time.tm_hour,
// tm_time.tm_min, tm_time.tm_sec);
fmt_helper::pad2(tm_time.tm_hour, dest);
dest.push_back(':');
fmt_helper::pad2(tm_time.tm_min, dest);
dest.push_back(':');
fmt_helper::pad2(tm_time.tm_sec, dest);
}
};
// ISO 8601 offset from UTC in timezone (+-HH:MM)
class z_formatter SPDLOG_FINAL : public flag_formatter
{
public:
const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
z_formatter() = default;
z_formatter(const z_formatter &) = delete;
z_formatter &operator=(const z_formatter &) = delete;
void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
#ifdef _WIN32
int total_minutes = get_cached_offset(msg, tm_time);
#else
// No need to chache under gcc,
// it is very fast (already stored in tm.tm_gmtoff)
(void)(msg);
int total_minutes = os::utc_minutes_offset(tm_time);
#endif
bool is_negative = total_minutes < 0;
if (is_negative)
{
total_minutes = -total_minutes;
dest.push_back('-');
}
else
{
dest.push_back('+');
}
fmt_helper::pad2(total_minutes / 60, dest); // hours
dest.push_back(':');
fmt_helper::pad2(total_minutes % 60, dest); // minutes
}
private:
log_clock::time_point last_update_{std::chrono::seconds(0)};
#ifdef _WIN32
int offset_minutes_{0};
int get_cached_offset(const log_msg &msg, const std::tm &tm_time)
{
if (msg.time - last_update_ >= cache_refresh)
{
offset_minutes_ = os::utc_minutes_offset(tm_time);
last_update_ = msg.time;
}
return offset_minutes_;
}
#endif
};
// Thread id
class t_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{
fmt_helper::pad6(msg.thread_id, dest);
}
};
// Current pid
class pid_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override
{
fmt_helper::append_int(details::os::pid(), dest);
}
};
// message counter formatter
class i_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{
fmt_helper::pad6(msg.msg_id, dest);
}
};
class v_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{
fmt_helper::append_buf(msg.raw, dest);
}
};
class ch_formatter SPDLOG_FINAL : public flag_formatter
{
public:
explicit ch_formatter(char ch)
: ch_(ch)
{
}
void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override
{
dest.push_back(ch_);
}
private:
char ch_;
};
// aggregate user chars to display as is
class aggregate_formatter SPDLOG_FINAL : public flag_formatter
{
public:
aggregate_formatter() = default;
void add_ch(char ch)
{
str_ += ch;
}
void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override
{
fmt_helper::append_str(str_, dest);
}
private:
std::string str_;
};
// mark the color range. expect it to be in the form of "%^colored text%$"
class color_start_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{
msg.color_range_start = dest.size();
}
};
class color_stop_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{
msg.color_range_end = dest.size();
}
};
// Full info formatter
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
class full_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
using namespace std::chrono;
#ifndef SPDLOG_NO_DATETIME
// cache the date/time part for the next second.
auto duration = msg.time.time_since_epoch();
auto secs = duration_cast<seconds>(duration);
if (cache_timestamp_ != secs || cached_datetime_.size() == 0)
{
cached_datetime_.resize(0);
cached_datetime_.push_back('[');
fmt_helper::append_int(tm_time.tm_year + 1900, cached_datetime_);
cached_datetime_.push_back('-');
fmt_helper::pad2(tm_time.tm_mon + 1, cached_datetime_);
cached_datetime_.push_back('-');
fmt_helper::pad2(tm_time.tm_mday, cached_datetime_);
cached_datetime_.push_back(' ');
fmt_helper::pad2(tm_time.tm_hour, cached_datetime_);
cached_datetime_.push_back(':');
fmt_helper::pad2(tm_time.tm_min, cached_datetime_);
cached_datetime_.push_back(':');
fmt_helper::pad2(tm_time.tm_sec, cached_datetime_);
cached_datetime_.push_back('.');
cache_timestamp_ = secs;
}
fmt_helper::append_buf(cached_datetime_, dest);
auto millis = fmt_helper::time_fraction<milliseconds>(msg.time);
fmt_helper::pad3(static_cast<int>(millis.count()), dest);
dest.push_back(']');
dest.push_back(' ');
#else // no datetime needed
(void)tm_time;
#endif
#ifndef SPDLOG_NO_NAME
dest.push_back('[');
fmt_helper::append_str(*msg.logger_name, dest);
dest.push_back(']');
dest.push_back(' ');
#endif
dest.push_back('[');
// wrap the level name with color
msg.color_range_start = dest.size();
fmt_helper::append_c_str(level::to_c_str(msg.level), dest);
msg.color_range_end = dest.size();
dest.push_back(']');
dest.push_back(' ');
fmt_helper::append_buf(msg.raw, dest);
}
private:
std::chrono::seconds cache_timestamp_{0};
fmt::basic_memory_buffer<char, 128> cached_datetime_;
};
} // namespace details
class pattern_formatter SPDLOG_FINAL : public formatter
{
public:
explicit pattern_formatter(
std::string pattern, pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol)
: pattern_(std::move(pattern))
, eol_(std::move(eol))
, pattern_time_type_(time_type)
, last_log_secs_(0)
{
std::memset(&cached_tm_, 0, sizeof(cached_tm_));
compile_pattern_(pattern_);
}
pattern_formatter(const pattern_formatter &other) = delete;
pattern_formatter &operator=(const pattern_formatter &other) = delete;
virtual std::unique_ptr<formatter> clone() const override
{
return std::unique_ptr<formatter>(new pattern_formatter(pattern_, pattern_time_type_, eol_));
}
void format(const details::log_msg &msg, fmt::memory_buffer &dest) override
{
#ifndef SPDLOG_NO_DATETIME
auto secs = std::chrono::duration_cast<std::chrono::seconds>(msg.time.time_since_epoch());
if (secs != last_log_secs_)
{
cached_tm_ = get_time_(msg);
last_log_secs_ = secs;
}
#endif
for (auto &f : formatters_)
{
f->format(msg, cached_tm_, dest);
}
// write eol
details::fmt_helper::append_str(eol_, dest);
}
private:
std::string pattern_;
std::string eol_;
pattern_time_type pattern_time_type_;
std::tm cached_tm_;
std::chrono::seconds last_log_secs_;
std::vector<std::unique_ptr<details::flag_formatter>> formatters_;
std::tm get_time_(const details::log_msg &msg)
{
if (pattern_time_type_ == pattern_time_type::local)
{
return details::os::localtime(log_clock::to_time_t(msg.time));
}
return details::os::gmtime(log_clock::to_time_t(msg.time));
}
void handle_flag_(char flag)
{
switch (flag)
{
// logger name
case 'n':
formatters_.emplace_back(new details::name_formatter());
break;
case 'l':
formatters_.emplace_back(new details::level_formatter());
break;
case 'L':
formatters_.emplace_back(new details::short_level_formatter());
break;
case ('t'):
formatters_.emplace_back(new details::t_formatter());
break;
case ('v'):
formatters_.emplace_back(new details::v_formatter());
break;
case ('a'):
formatters_.emplace_back(new details::a_formatter());
break;
case ('A'):
formatters_.emplace_back(new details::A_formatter());
break;
case ('b'):
case ('h'):
formatters_.emplace_back(new details::b_formatter());
break;
case ('B'):
formatters_.emplace_back(new details::B_formatter());
break;
case ('c'):
formatters_.emplace_back(new details::c_formatter());
break;
case ('C'):
formatters_.emplace_back(new details::C_formatter());
break;
case ('Y'):
formatters_.emplace_back(new details::Y_formatter());
break;
case ('D'):
case ('x'):
formatters_.emplace_back(new details::D_formatter());
break;
case ('m'):
formatters_.emplace_back(new details::m_formatter());
break;
case ('d'):
formatters_.emplace_back(new details::d_formatter());
break;
case ('H'):
formatters_.emplace_back(new details::H_formatter());
break;
case ('I'):
formatters_.emplace_back(new details::I_formatter());
break;
case ('M'):
formatters_.emplace_back(new details::M_formatter());
break;
case ('S'):
formatters_.emplace_back(new details::S_formatter());
break;
case ('e'):
formatters_.emplace_back(new details::e_formatter());
break;
case ('f'):
formatters_.emplace_back(new details::f_formatter());
break;
case ('F'):
formatters_.emplace_back(new details::F_formatter());
break;
case ('E'):
formatters_.emplace_back(new details::E_formatter());
break;
case ('p'):
formatters_.emplace_back(new details::p_formatter());
break;
case ('r'):
formatters_.emplace_back(new details::r_formatter());
break;
case ('R'):
formatters_.emplace_back(new details::R_formatter());
break;
case ('T'):
case ('X'):
formatters_.emplace_back(new details::T_formatter());
break;
case ('z'):
formatters_.emplace_back(new details::z_formatter());
break;
case ('+'):
formatters_.emplace_back(new details::full_formatter());
break;
case ('P'):
formatters_.emplace_back(new details::pid_formatter());
break;
case ('i'):
formatters_.emplace_back(new details::i_formatter());
break;
case ('^'):
formatters_.emplace_back(new details::color_start_formatter());
break;
case ('$'):
formatters_.emplace_back(new details::color_stop_formatter());
break;
default: // Unknown flag appears as is
formatters_.emplace_back(new details::ch_formatter('%'));
formatters_.emplace_back(new details::ch_formatter(flag));
break;
}
}
void compile_pattern_(const std::string &pattern)
{
auto end = pattern.end();
std::unique_ptr<details::aggregate_formatter> user_chars;
formatters_.clear();
for (auto it = pattern.begin(); it != end; ++it)
{
if (*it == '%')
{
if (user_chars) // append user chars found so far
{
formatters_.push_back(std::move(user_chars));
}
// if(
if (++it != end)
{
handle_flag_(*it);
}
else
{
break;
}
}
else // chars not following the % sign should be displayed as is
{
if (!user_chars)
{
user_chars = std::unique_ptr<details::aggregate_formatter>(new details::aggregate_formatter());
}
user_chars->add_ch(*it);
}
}
if (user_chars) // append raw chars found so far
{
formatters_.push_back(std::move(user_chars));
}
}
};
} // namespace spdlog

View File

@@ -1,665 +0,0 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include "spdlog/formatter.h"
#include "spdlog/details/log_msg.h"
#include "spdlog/details/os.h"
#include "spdlog/fmt/fmt.h"
#include <chrono>
#include <ctime>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include <utility>
#include <vector>
#include <array>
namespace spdlog
{
namespace details
{
class flag_formatter
{
public:
virtual ~flag_formatter()
{}
virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0;
};
///////////////////////////////////////////////////////////////////////
// name & level pattern appenders
///////////////////////////////////////////////////////////////////////
namespace
{
class name_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << *msg.logger_name;
}
};
}
// log level appender
class level_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << level::to_str(msg.level);
}
};
// short log level appender
class short_level_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << level::to_short_str(msg.level);
}
};
///////////////////////////////////////////////////////////////////////
// Date time pattern appenders
///////////////////////////////////////////////////////////////////////
static const char* ampm(const tm& t)
{
return t.tm_hour >= 12 ? "PM" : "AM";
}
static int to12h(const tm& t)
{
return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour;
}
//Abbreviated weekday name
static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
class a_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << days[tm_time.tm_wday];
}
};
//Full weekday name
static const std::string full_days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
class A_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << full_days[tm_time.tm_wday];
}
};
//Abbreviated month
static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" };
class b_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << months[tm_time.tm_mon];
}
};
//Full month name
static const std::string full_months[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
class B_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << full_months[tm_time.tm_mon];
}
};
//write 2 ints seperated by sep with padding of 2
static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, char sep)
{
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0');
return w;
}
//write 3 ints seperated by sep with padding of 2
static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v3, char sep)
{
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0');
return w;
}
//Date and time representation (Thu Aug 23 15:35:46 2014)
class c_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << days[tm_time.tm_wday] << ' ' << months[tm_time.tm_mon] << ' ' << tm_time.tm_mday << ' ';
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << tm_time.tm_year + 1900;
}
};
// year - 2 digit
class C_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_year % 100, 2, '0');
}
};
// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
class D_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
pad_n_join(msg.formatted, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_year % 100, '/');
}
};
// year - 4 digit
class Y_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << tm_time.tm_year + 1900;
}
};
// month 1-12
class m_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_mon + 1, 2, '0');
}
};
// day of month 1-31
class d_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_mday, 2, '0');
}
};
// hours in 24 format 0-23
class H_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_hour, 2, '0');
}
};
// hours in 12 format 1-12
class I_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(to12h(tm_time), 2, '0');
}
};
// minutes 0-59
class M_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_min, 2, '0');
}
};
// seconds 0-59
class S_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_sec, 2, '0');
}
};
// milliseconds
class e_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
auto duration = msg.time.time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
msg.formatted << fmt::pad(static_cast<int>(millis), 3, '0');
}
};
// microseconds
class f_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
auto duration = msg.time.time_since_epoch();
auto micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count() % 1000000;
msg.formatted << fmt::pad(static_cast<int>(micros), 6, '0');
}
};
// nanoseconds
class F_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
auto duration = msg.time.time_since_epoch();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % 1000000000;
msg.formatted << fmt::pad(static_cast<int>(ns), 9, '0');
}
};
// AM/PM
class p_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
msg.formatted << ampm(tm_time);
}
};
// 12 hour clock 02:55:02 pm
class r_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
pad_n_join(msg.formatted, to12h(tm_time), tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << ampm(tm_time);
}
};
// 24-hour HH:MM time, equivalent to %H:%M
class R_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, ':');
}
};
// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
class T_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':');
}
};
// ISO 8601 offset from UTC in timezone (+-HH:MM)
class z_formatter SPDLOG_FINAL:public flag_formatter
{
public:
const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
z_formatter():_last_update(std::chrono::seconds(0)), _offset_minutes(0)
{}
z_formatter(const z_formatter&) = delete;
z_formatter& operator=(const z_formatter&) = delete;
void format(details::log_msg& msg, const std::tm& tm_time) override
{
#ifdef _WIN32
int total_minutes = get_cached_offset(msg, tm_time);
#else
// No need to chache under gcc,
// it is very fast (already stored in tm.tm_gmtoff)
int total_minutes = os::utc_minutes_offset(tm_time);
#endif
bool is_negative = total_minutes < 0;
char sign;
if (is_negative)
{
total_minutes = -total_minutes;
sign = '-';
}
else
{
sign = '+';
}
int h = total_minutes / 60;
int m = total_minutes % 60;
msg.formatted << sign;
pad_n_join(msg.formatted, h, m, ':');
}
private:
log_clock::time_point _last_update;
int _offset_minutes;
std::mutex _mutex;
int get_cached_offset(const log_msg& msg, const std::tm& tm_time)
{
using namespace std::chrono;
std::lock_guard<std::mutex> l(_mutex);
if (msg.time - _last_update >= cache_refresh)
{
_offset_minutes = os::utc_minutes_offset(tm_time);
_last_update = msg.time;
}
return _offset_minutes;
}
};
// Thread id
class t_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << msg.thread_id;
}
};
// Current pid
class pid_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << details::os::pid();
}
};
class v_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
}
};
class ch_formatter SPDLOG_FINAL:public flag_formatter
{
public:
explicit ch_formatter(char ch): _ch(ch)
{}
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << _ch;
}
private:
char _ch;
};
//aggregate user chars to display as is
class aggregate_formatter SPDLOG_FINAL:public flag_formatter
{
public:
aggregate_formatter()
{}
void add_ch(char ch)
{
_str += ch;
}
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << _str;
}
private:
std::string _str;
};
// Full info formatter
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
class full_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
#ifndef SPDLOG_NO_DATETIME
auto duration = msg.time.time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
/* Slower version(while still very fast - about 3.2 million lines/sec under 10 threads),
msg.formatted.write("[{:d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}.{:03d}] [{}] [{}] {} ",
tm_time.tm_year + 1900,
tm_time.tm_mon + 1,
tm_time.tm_mday,
tm_time.tm_hour,
tm_time.tm_min,
tm_time.tm_sec,
static_cast<int>(millis),
msg.logger_name,
level::to_str(msg.level),
msg.raw.str());*/
// Faster (albeit uglier) way to format the line (5.6 million lines/sec under 10 threads)
msg.formatted << '[' << static_cast<unsigned int>(tm_time.tm_year + 1900) << '-'
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_mon + 1), 2, '0') << '-'
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_mday), 2, '0') << ' '
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_hour), 2, '0') << ':'
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_min), 2, '0') << ':'
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_sec), 2, '0') << '.'
<< fmt::pad(static_cast<unsigned int>(millis), 3, '0') << "] ";
//no datetime needed
#else
(void)tm_time;
#endif
#ifndef SPDLOG_NO_NAME
msg.formatted << '[' << *msg.logger_name << "] ";
#endif
msg.formatted << '[' << level::to_str(msg.level) << "] ";
msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
}
};
}
}
///////////////////////////////////////////////////////////////////////////////
// pattern_formatter inline impl
///////////////////////////////////////////////////////////////////////////////
inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern, pattern_time_type pattern_time)
: _pattern_time(pattern_time)
{
compile_pattern(pattern);
}
inline void spdlog::pattern_formatter::compile_pattern(const std::string& pattern)
{
auto end = pattern.end();
std::unique_ptr<details::aggregate_formatter> user_chars;
for (auto it = pattern.begin(); it != end; ++it)
{
if (*it == '%')
{
if (user_chars) //append user chars found so far
_formatters.push_back(std::move(user_chars));
if (++it != end)
handle_flag(*it);
else
break;
}
else // chars not following the % sign should be displayed as is
{
if (!user_chars)
user_chars = std::unique_ptr<details::aggregate_formatter>(new details::aggregate_formatter());
user_chars->add_ch(*it);
}
}
if (user_chars) //append raw chars found so far
{
_formatters.push_back(std::move(user_chars));
}
}
inline void spdlog::pattern_formatter::handle_flag(char flag)
{
switch (flag)
{
// logger name
case 'n':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::name_formatter()));
break;
case 'l':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::level_formatter()));
break;
case 'L':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::short_level_formatter()));
break;
case('t'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::t_formatter()));
break;
case('v'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::v_formatter()));
break;
case('a'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::a_formatter()));
break;
case('A'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::A_formatter()));
break;
case('b'):
case('h'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::b_formatter()));
break;
case('B'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::B_formatter()));
break;
case('c'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::c_formatter()));
break;
case('C'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::C_formatter()));
break;
case('Y'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::Y_formatter()));
break;
case('D'):
case('x'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::D_formatter()));
break;
case('m'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::m_formatter()));
break;
case('d'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::d_formatter()));
break;
case('H'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::H_formatter()));
break;
case('I'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::I_formatter()));
break;
case('M'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::M_formatter()));
break;
case('S'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::S_formatter()));
break;
case('e'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::e_formatter()));
break;
case('f'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::f_formatter()));
break;
case('F'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::F_formatter()));
break;
case('p'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::p_formatter()));
break;
case('r'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::r_formatter()));
break;
case('R'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::R_formatter()));
break;
case('T'):
case('X'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::T_formatter()));
break;
case('z'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::z_formatter()));
break;
case ('+'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::full_formatter()));
break;
case ('P'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::pid_formatter()));
break;
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
case ('i'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::i_formatter()));
break;
#endif
default: //Unknown flag appears as is
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter('%')));
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter(flag)));
break;
}
}
inline std::tm spdlog::pattern_formatter::get_time(details::log_msg& msg)
{
if (_pattern_time == pattern_time_type::local)
return details::os::localtime(log_clock::to_time_t(msg.time));
else
return details::os::gmtime(log_clock::to_time_t(msg.time));
}
inline void spdlog::pattern_formatter::format(details::log_msg& msg)
{
#ifndef SPDLOG_NO_DATETIME
auto tm_time = get_time(msg);
#else
std::tm tm_time;
#endif
for (auto &f : _formatters)
{
f->format(msg, tm_time);
}
//write eol
msg.formatted.write(details::os::eol, details::os::eol_size);
}

View File

@@ -0,0 +1,71 @@
//
// Copyright(c) 2018 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
// periodic worker thread - periodically executes the given callback function.
//
// RAII over the owned thread:
// creates the thread on construction.
// stops and joins the thread on destruction.
#include <chrono>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <thread>
namespace spdlog {
namespace details {
class periodic_worker
{
public:
periodic_worker(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 &operator=(const periodic_worker &) = delete;
// stop the worker thread and join it
~periodic_worker()
{
if (worker_thread_.joinable())
{
{
std::lock_guard<std::mutex> lock(mutex_);
active_ = false;
}
cv_.notify_one();
worker_thread_.join();
}
}
private:
bool active_;
std::thread worker_thread_;
std::mutex mutex_;
std::condition_variable cv_;
};
} // namespace details
} // namespace spdlog

View File

@@ -10,205 +10,209 @@
// 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/details/null_mutex.h"
#include "spdlog/logger.h"
#include "spdlog/async_logger.h"
#include "spdlog/common.h" #include "spdlog/common.h"
#include "spdlog/details/periodic_worker.h"
#include "spdlog/logger.h"
#include <chrono> #include <chrono>
#include <functional> #include <functional>
#include <memory> #include <memory>
#include <mutex>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
namespace spdlog namespace spdlog {
{ namespace details {
namespace details class thread_pool;
{
template <class Mutex> class registry_t class registry
{ {
public: public:
registry(const registry &) = delete;
registry &operator=(const registry &) = delete;
void register_logger(std::shared_ptr<logger> logger) void register_logger(std::shared_ptr<logger> new_logger)
{ {
std::lock_guard<Mutex> lock(_mutex); std::lock_guard<std::mutex> lock(logger_map_mutex_);
auto logger_name = logger->name(); auto logger_name = new_logger->name();
throw_if_exists(logger_name); throw_if_exists_(logger_name);
_loggers[logger_name] = logger; loggers_[logger_name] = new_logger;
} }
void register_and_init(std::shared_ptr<logger> new_logger)
{
std::lock_guard<std::mutex> lock(logger_map_mutex_);
auto logger_name = new_logger->name();
throw_if_exists_(logger_name);
// set the global formatter pattern
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_);
// add to registry
loggers_[logger_name] = new_logger;
}
std::shared_ptr<logger> get(const std::string &logger_name) std::shared_ptr<logger> get(const std::string &logger_name)
{ {
std::lock_guard<Mutex> lock(_mutex); std::lock_guard<std::mutex> lock(logger_map_mutex_);
auto found = _loggers.find(logger_name); auto found = loggers_.find(logger_name);
return found == _loggers.end() ? nullptr : found->second; return found == loggers_.end() ? nullptr : found->second;
} }
template<class It> void set_tp(std::shared_ptr<thread_pool> tp)
std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
{ {
std::lock_guard<Mutex> lock(_mutex); std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
throw_if_exists(logger_name); tp_ = std::move(tp);
std::shared_ptr<logger> new_logger;
if (_async_mode)
new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb);
else
new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end);
if (_formatter)
new_logger->set_formatter(_formatter);
if (_err_handler)
new_logger->set_error_handler(_err_handler);
new_logger->set_level(_level);
//Add to registry
_loggers[logger_name] = new_logger;
return new_logger;
} }
template<class It> std::shared_ptr<thread_pool> get_tp()
std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, const It& sinks_begin, const It& sinks_end)
{ {
std::lock_guard<Mutex> lock(_mutex); std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
throw_if_exists(logger_name); return tp_;
auto new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb);
if (_formatter)
new_logger->set_formatter(_formatter);
if (_err_handler)
new_logger->set_error_handler(_err_handler);
new_logger->set_level(_level);
//Add to registry
_loggers[logger_name] = new_logger;
return new_logger;
} }
void apply_all(std::function<void(std::shared_ptr<logger>)> fun) // Set global formatter. Each sink in each logger will get a clone of this object
void set_formatter(std::unique_ptr<formatter> formatter)
{ {
std::lock_guard<Mutex> lock(_mutex); std::lock_guard<std::mutex> lock(logger_map_mutex_);
for (auto &l : _loggers) formatter_ = std::move(formatter);
fun(l.second); for (auto &l : loggers_)
{
l.second->set_formatter(formatter_->clone());
} }
void drop(const std::string& logger_name)
{
std::lock_guard<Mutex> lock(_mutex);
_loggers.erase(logger_name);
}
void drop_all()
{
std::lock_guard<Mutex> lock(_mutex);
_loggers.clear();
}
std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks)
{
return create(logger_name, sinks.begin(), sinks.end());
}
std::shared_ptr<logger> create(const std::string& logger_name, sink_ptr sink)
{
return create(logger_name, { sink });
}
std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, sinks_init_list sinks)
{
return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks.begin(), sinks.end());
}
std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, sink_ptr sink)
{
return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, { sink });
}
void formatter(formatter_ptr f)
{
std::lock_guard<Mutex> lock(_mutex);
_formatter = f;
for (auto& l : _loggers)
l.second->set_formatter(_formatter);
}
void set_pattern(const std::string& pattern)
{
std::lock_guard<Mutex> lock(_mutex);
_formatter = std::make_shared<pattern_formatter>(pattern);
for (auto& l : _loggers)
l.second->set_formatter(_formatter);
} }
void set_level(level::level_enum log_level) void set_level(level::level_enum log_level)
{ {
std::lock_guard<Mutex> lock(_mutex); std::lock_guard<std::mutex> lock(logger_map_mutex_);
for (auto& l : _loggers) for (auto &l : loggers_)
{
l.second->set_level(log_level); l.second->set_level(log_level);
_level = log_level; }
level_ = log_level;
}
void 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;
}
void 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_.reset(new periodic_worker(clbk, interval));
} }
void set_error_handler(log_err_handler handler) void set_error_handler(log_err_handler handler)
{ {
for (auto& l : _loggers) std::lock_guard<std::mutex> lock(logger_map_mutex_);
for (auto &l : loggers_)
{
l.second->set_error_handler(handler); l.second->set_error_handler(handler);
_err_handler = handler; }
err_handler_ = handler;
} }
void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb) void apply_all(std::function<void(std::shared_ptr<logger>)> fun)
{ {
std::lock_guard<Mutex> lock(_mutex); std::lock_guard<std::mutex> lock(logger_map_mutex_);
_async_mode = true; for (auto &l : loggers_)
_async_q_size = q_size; {
_overflow_policy = overflow_policy; fun(l.second);
_worker_warmup_cb = worker_warmup_cb; }
_flush_interval_ms = flush_interval_ms;
_worker_teardown_cb = worker_teardown_cb;
} }
void set_sync_mode() void flush_all()
{ {
std::lock_guard<Mutex> lock(_mutex); std::lock_guard<std::mutex> lock(logger_map_mutex_);
_async_mode = false; for (auto &l : loggers_)
{
l.second->flush();
}
} }
static registry_t<Mutex>& instance() void drop(const std::string &logger_name)
{ {
static registry_t<Mutex> s_instance; std::lock_guard<std::mutex> lock(logger_map_mutex_);
loggers_.erase(logger_name);
}
void drop_all()
{
std::lock_guard<std::mutex> lock(logger_map_mutex_);
loggers_.clear();
}
// clean all reasources and threads started by the registry
void 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();
}
}
std::recursive_mutex &tp_mutex()
{
return tp_mutex_;
}
static registry &instance()
{
static registry s_instance;
return s_instance; return s_instance;
} }
private: private:
registry_t<Mutex>() {} registry()
registry_t<Mutex>(const registry_t<Mutex>&) = delete; : formatter_(new pattern_formatter("%+"))
registry_t<Mutex>& operator=(const registry_t<Mutex>&) = delete; {
}
void throw_if_exists(const std::string &logger_name)
~registry()
{
/*std::lock_guard<std::mutex> lock(flusher_mutex_);
periodic_flusher_.reset();*/
}
void throw_if_exists_(const std::string &logger_name)
{
if (loggers_.find(logger_name) != loggers_.end())
{ {
if (_loggers.find(logger_name) != _loggers.end())
throw spdlog_ex("logger with name '" + logger_name + "' already exists"); throw spdlog_ex("logger with name '" + logger_name + "' already exists");
} }
Mutex _mutex; }
std::unordered_map <std::string, std::shared_ptr<logger>> _loggers;
formatter_ptr _formatter; std::mutex logger_map_mutex_, flusher_mutex_;
level::level_enum _level = level::info; std::recursive_mutex tp_mutex_;
log_err_handler _err_handler; std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
bool _async_mode = false; std::unique_ptr<formatter> formatter_;
size_t _async_q_size = 0; level::level_enum level_ = level::info;
async_overflow_policy _overflow_policy = async_overflow_policy::block_retry; level::level_enum flush_level_ = level::off;
std::function<void()> _worker_warmup_cb = nullptr; log_err_handler err_handler_;
std::chrono::milliseconds _flush_interval_ms; std::shared_ptr<thread_pool> tp_;
std::function<void()> _worker_teardown_cb = nullptr; std::unique_ptr<periodic_worker> periodic_flusher_;
}; };
#ifdef SPDLOG_NO_REGISTRY_MUTEX
typedef registry_t<spdlog::details::null_mutex> registry; } // namespace details
#else } // namespace spdlog
typedef registry_t<std::mutex> registry;
#endif
}
}

View File

@@ -1,263 +0,0 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
//
// Global registry functions
//
#include "spdlog/spdlog.h"
#include "spdlog/details/registry.h"
#include "spdlog/sinks/file_sinks.h"
#include "spdlog/sinks/stdout_sinks.h"
#ifdef SPDLOG_ENABLE_SYSLOG
#include "spdlog/sinks/syslog_sink.h"
#endif
#ifdef _WIN32
#include "spdlog/sinks/wincolor_sink.h"
#else
#include "spdlog/sinks/ansicolor_sink.h"
#endif
#ifdef __ANDROID__
#include "spdlog/sinks/android_sink.h"
#endif
#include <chrono>
#include <functional>
#include <memory>
#include <string>
inline void spdlog::register_logger(std::shared_ptr<logger> logger)
{
return details::registry::instance().register_logger(logger);
}
inline std::shared_ptr<spdlog::logger> spdlog::get(const std::string& name)
{
return details::registry::instance().get(name);
}
inline void spdlog::drop(const std::string &name)
{
details::registry::instance().drop(name);
}
// Create multi/single threaded simple file logger
inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate)
{
return create<spdlog::sinks::simple_file_sink_mt>(logger_name, filename, truncate);
}
inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate)
{
return create<spdlog::sinks::simple_file_sink_st>(logger_name, filename, truncate);
}
// Create multi/single threaded rotating file logger
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
{
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
}
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
{
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
}
// Create file logger which creates new file at midnight):
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute)
{
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, hour, minute);
}
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour, int minute)
{
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, hour, minute);
}
//
// stdout/stderr loggers
//
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name)
{
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name)
{
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name)
{
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name)
{
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance());
}
//
// stdout/stderr color loggers
//
#ifdef _WIN32
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
#else //ansi terminal colors
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
#endif
#ifdef SPDLOG_ENABLE_SYSLOG
// Create syslog logger
inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option)
{
return create<spdlog::sinks::syslog_sink>(logger_name, syslog_ident, syslog_option);
}
#endif
#ifdef __ANDROID__
inline std::shared_ptr<spdlog::logger> spdlog::android_logger(const std::string& logger_name, const std::string& tag)
{
return create<spdlog::sinks::android_sink>(logger_name, tag);
}
#endif
// Create and register a logger a single sink
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const spdlog::sink_ptr& sink)
{
return details::registry::instance().create(logger_name, sink);
}
//Create logger with multiple sinks
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, spdlog::sinks_init_list sinks)
{
return details::registry::instance().create(logger_name, sinks);
}
template <typename Sink, typename... Args>
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, Args... args)
{
sink_ptr sink = std::make_shared<Sink>(args...);
return details::registry::instance().create(logger_name, { sink });
}
template<class It>
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
{
return details::registry::instance().create(logger_name, sinks_begin, sinks_end);
}
// Create and register an async logger with a single sink
inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, const sink_ptr& sink, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
{
return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sink);
}
// Create and register an async logger with multiple sinks
inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb )
{
return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks);
}
template<class It>
inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, const It& sinks_begin, const It& sinks_end, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
{
return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks_begin, sinks_end);
}
inline void spdlog::set_formatter(spdlog::formatter_ptr f)
{
details::registry::instance().formatter(f);
}
inline void spdlog::set_pattern(const std::string& format_string)
{
return details::registry::instance().set_pattern(format_string);
}
inline void spdlog::set_level(level::level_enum log_level)
{
return details::registry::instance().set_level(log_level);
}
inline void spdlog::set_error_handler(log_err_handler handler)
{
return details::registry::instance().set_error_handler(handler);
}
inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
{
details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb);
}
inline void spdlog::set_sync_mode()
{
details::registry::instance().set_sync_mode();
}
inline void spdlog::apply_all(std::function<void(std::shared_ptr<logger>)> fun)
{
details::registry::instance().apply_all(fun);
}
inline void spdlog::drop_all()
{
details::registry::instance().drop_all();
}

View File

@@ -0,0 +1,220 @@
#pragma once
#include "spdlog/details/log_msg.h"
#include "spdlog/details/mpmc_blocking_q.h"
#include "spdlog/details/os.h"
#include <chrono>
#include <memory>
#include <thread>
#include <vector>
namespace spdlog {
namespace details {
using async_logger_ptr = std::shared_ptr<spdlog::async_logger>;
enum class async_msg_type
{
log,
flush,
terminate
};
// Async msg to move to/from the queue
// Movable only. should never be copied
struct async_msg
{
async_msg_type msg_type;
level::level_enum level;
log_clock::time_point time;
size_t thread_id;
fmt::basic_memory_buffer<char, 176> raw;
size_t msg_id;
async_logger_ptr worker_ptr;
async_msg() = default;
~async_msg() = default;
// should only be moved in or out of the queue..
async_msg(const async_msg &) = delete;
// support for vs2013 move
#if defined(_MSC_VER) && _MSC_VER <= 1800
async_msg(async_msg &&other) SPDLOG_NOEXCEPT : msg_type(other.msg_type),
level(other.level),
time(other.time),
thread_id(other.thread_id),
raw(move(other.raw)),
msg_id(other.msg_id),
worker_ptr(std::move(other.worker_ptr))
{
}
async_msg &operator=(async_msg &&other) SPDLOG_NOEXCEPT
{
msg_type = other.msg_type;
level = other.level;
time = other.time;
thread_id = other.thread_id;
raw = std::move(other.raw);
msg_id = other.msg_id;
worker_ptr = std::move(other.worker_ptr);
return *this;
}
#else // (_MSC_VER) && _MSC_VER <= 1800
async_msg(async_msg &&other) = default;
async_msg &operator=(async_msg &&other) = default;
#endif
// construct from log_msg with given type
async_msg(async_logger_ptr &&worker, async_msg_type the_type, details::log_msg &&m)
: msg_type(the_type)
, level(m.level)
, time(m.time)
, thread_id(m.thread_id)
, msg_id(m.msg_id)
, worker_ptr(std::forward<async_logger_ptr>(worker))
{
fmt_helper::append_buf(m.raw, raw);
}
async_msg(async_logger_ptr &&worker, async_msg_type the_type)
: async_msg(std::forward<async_logger_ptr>(worker), the_type, details::log_msg())
{
}
explicit async_msg(async_msg_type the_type)
: async_msg(nullptr, the_type, details::log_msg())
{
}
// copy into log_msg
void to_log_msg(log_msg &msg)
{
msg.logger_name = &worker_ptr->name();
msg.level = level;
msg.time = time;
msg.thread_id = thread_id;
fmt_helper::append_buf(raw, msg.raw);
msg.msg_id = msg_id;
msg.color_range_start = 0;
msg.color_range_end = 0;
}
};
class thread_pool
{
public:
using item_type = async_msg;
using q_type = details::mpmc_blocking_queue<item_type>;
thread_pool(size_t q_max_items, size_t threads_n)
: q_(q_max_items)
{
// std::cout << "thread_pool() q_size_bytes: " << q_size_bytes <<
// "\tthreads_n: " << threads_n << std::endl;
if (threads_n == 0 || threads_n > 1000)
{
throw spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid "
"range is 1-1000)");
}
for (size_t i = 0; i < threads_n; i++)
{
threads_.emplace_back(std::bind(&thread_pool::worker_loop_, this));
}
}
// message all threads to terminate gracefully join them
~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 (...)
{
}
}
void post_log(async_logger_ptr &&worker_ptr, details::log_msg &&msg, async_overflow_policy overflow_policy)
{
async_msg async_m(std::forward<async_logger_ptr>(worker_ptr), async_msg_type::log, std::forward<log_msg>(msg));
post_async_msg_(std::move(async_m), overflow_policy);
}
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy)
{
post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy);
}
private:
q_type q_;
std::vector<std::thread> threads_;
void 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 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 process_next_msg_()
{
async_msg incoming_async_msg;
bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10));
if (!dequeued)
{
return true;
}
switch (incoming_async_msg.msg_type)
{
case async_msg_type::flush:
{
incoming_async_msg.worker_ptr->backend_flush_();
return true;
}
case async_msg_type::terminate:
{
return false;
}
default:
{
log_msg msg;
incoming_async_msg.to_log_msg(msg);
incoming_async_msg.worker_ptr->backend_log_(msg);
return true;
}
}
return true; // should not be reached
}
};
} // namespace details
} // namespace spdlog

View File

@@ -0,0 +1,23 @@
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
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

@@ -0,0 +1,257 @@
// Formatting library for C++ - the core API
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
//
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
// All Rights Reserved
// {fmt} support for rgb color output.
#ifndef FMT_COLORS_H_
#define FMT_COLORS_H_
#include "format.h"
FMT_BEGIN_NAMESPACE
// rgb is a struct for red, green and blue colors.
// We use rgb as name because some editors will show it as color direct in the
// editor.
struct rgb
{
FMT_CONSTEXPR_DECL rgb()
: r(0)
, g(0)
, b(0)
{
}
FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_)
: r(r_)
, g(g_)
, b(b_)
{
}
FMT_CONSTEXPR_DECL rgb(uint32_t hex)
: r((hex >> 16) & 0xFF)
, g((hex >> 8) & 0xFF)
, b((hex)&0xFF)
{
}
uint8_t r;
uint8_t g;
uint8_t b;
};
namespace internal {
FMT_CONSTEXPR inline void to_esc(uint8_t c, char out[], int offset)
{
out[offset + 0] = static_cast<char>('0' + c / 100);
out[offset + 1] = static_cast<char>('0' + c / 10 % 10);
out[offset + 2] = static_cast<char>('0' + c % 10);
}
} // namespace internal
FMT_FUNC void vprint_rgb(rgb fd, string_view format, format_args args)
{
char escape_fd[] = "\x1b[38;2;000;000;000m";
static FMT_CONSTEXPR_DECL const char RESET_COLOR[] = "\x1b[0m";
internal::to_esc(fd.r, escape_fd, 7);
internal::to_esc(fd.g, escape_fd, 11);
internal::to_esc(fd.b, escape_fd, 15);
std::fputs(escape_fd, stdout);
vprint(format, args);
std::fputs(RESET_COLOR, stdout);
}
FMT_FUNC void vprint_rgb(rgb fd, rgb bg, string_view format, format_args args)
{
char escape_fd[] = "\x1b[38;2;000;000;000m"; // foreground color
char escape_bg[] = "\x1b[48;2;000;000;000m"; // background color
static FMT_CONSTEXPR_DECL const char RESET_COLOR[] = "\x1b[0m";
internal::to_esc(fd.r, escape_fd, 7);
internal::to_esc(fd.g, escape_fd, 11);
internal::to_esc(fd.b, escape_fd, 15);
internal::to_esc(bg.r, escape_bg, 7);
internal::to_esc(bg.g, escape_bg, 11);
internal::to_esc(bg.b, escape_bg, 15);
std::fputs(escape_fd, stdout);
std::fputs(escape_bg, stdout);
vprint(format, args);
std::fputs(RESET_COLOR, stdout);
}
template<typename... Args>
inline void print_rgb(rgb fd, string_view format_str, const Args &... args)
{
vprint_rgb(fd, format_str, make_format_args(args...));
}
// rgb foreground color
template<typename... Args>
inline void print(rgb fd, string_view format_str, const Args &... args)
{
vprint_rgb(fd, format_str, make_format_args(args...));
}
// rgb foreground color and background color
template<typename... Args>
inline void print(rgb fd, rgb bg, string_view format_str, const Args &... args)
{
vprint_rgb(fd, bg, format_str, make_format_args(args...));
}
enum class color : uint32_t
{
alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215)
aqua = 0x00FFFF, // rgb(0,255,255)
aquamarine = 0x7FFFD4, // rgb(127,255,212)
azure = 0xF0FFFF, // rgb(240,255,255)
beige = 0xF5F5DC, // rgb(245,245,220)
bisque = 0xFFE4C4, // rgb(255,228,196)
black = 0x000000, // rgb(0,0,0)
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
blue = 0x0000FF, // rgb(0,0,255)
blue_violet = 0x8A2BE2, // rgb(138,43,226)
brown = 0xA52A2A, // rgb(165,42,42)
burly_wood = 0xDEB887, // rgb(222,184,135)
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
chartreuse = 0x7FFF00, // rgb(127,255,0)
chocolate = 0xD2691E, // rgb(210,105,30)
coral = 0xFF7F50, // rgb(255,127,80)
cornflower_blue = 0x6495ED, // rgb(100,149,237)
cornsilk = 0xFFF8DC, // rgb(255,248,220)
crimson = 0xDC143C, // rgb(220,20,60)
cyan = 0x00FFFF, // rgb(0,255,255)
dark_blue = 0x00008B, // rgb(0,0,139)
dark_cyan = 0x008B8B, // rgb(0,139,139)
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
dark_gray = 0xA9A9A9, // rgb(169,169,169)
dark_green = 0x006400, // rgb(0,100,0)
dark_khaki = 0xBDB76B, // rgb(189,183,107)
dark_magenta = 0x8B008B, // rgb(139,0,139)
dark_olive_green = 0x556B2F, // rgb(85,107,47)
dark_orange = 0xFF8C00, // rgb(255,140,0)
dark_orchid = 0x9932CC, // rgb(153,50,204)
dark_red = 0x8B0000, // rgb(139,0,0)
dark_salmon = 0xE9967A, // rgb(233,150,122)
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
dark_turquoise = 0x00CED1, // rgb(0,206,209)
dark_violet = 0x9400D3, // rgb(148,0,211)
deep_pink = 0xFF1493, // rgb(255,20,147)
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
dim_gray = 0x696969, // rgb(105,105,105)
dodger_blue = 0x1E90FF, // rgb(30,144,255)
fire_brick = 0xB22222, // rgb(178,34,34)
floral_white = 0xFFFAF0, // rgb(255,250,240)
forest_green = 0x228B22, // rgb(34,139,34)
fuchsia = 0xFF00FF, // rgb(255,0,255)
gainsboro = 0xDCDCDC, // rgb(220,220,220)
ghost_white = 0xF8F8FF, // rgb(248,248,255)
gold = 0xFFD700, // rgb(255,215,0)
golden_rod = 0xDAA520, // rgb(218,165,32)
gray = 0x808080, // rgb(128,128,128)
green = 0x008000, // rgb(0,128,0)
green_yellow = 0xADFF2F, // rgb(173,255,47)
honey_dew = 0xF0FFF0, // rgb(240,255,240)
hot_pink = 0xFF69B4, // rgb(255,105,180)
indian_red = 0xCD5C5C, // rgb(205,92,92)
indigo = 0x4B0082, // rgb(75,0,130)
ivory = 0xFFFFF0, // rgb(255,255,240)
khaki = 0xF0E68C, // rgb(240,230,140)
lavender = 0xE6E6FA, // rgb(230,230,250)
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
lawn_green = 0x7CFC00, // rgb(124,252,0)
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
light_blue = 0xADD8E6, // rgb(173,216,230)
light_coral = 0xF08080, // rgb(240,128,128)
light_cyan = 0xE0FFFF, // rgb(224,255,255)
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
light_gray = 0xD3D3D3, // rgb(211,211,211)
light_green = 0x90EE90, // rgb(144,238,144)
light_pink = 0xFFB6C1, // rgb(255,182,193)
light_salmon = 0xFFA07A, // rgb(255,160,122)
light_sea_green = 0x20B2AA, // rgb(32,178,170)
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
light_slate_gray = 0x778899, // rgb(119,136,153)
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
light_yellow = 0xFFFFE0, // rgb(255,255,224)
lime = 0x00FF00, // rgb(0,255,0)
lime_green = 0x32CD32, // rgb(50,205,50)
linen = 0xFAF0E6, // rgb(250,240,230)
magenta = 0xFF00FF, // rgb(255,0,255)
maroon = 0x800000, // rgb(128,0,0)
medium_aqua_marine = 0x66CDAA, // rgb(102,205,170)
medium_blue = 0x0000CD, // rgb(0,0,205)
medium_orchid = 0xBA55D3, // rgb(186,85,211)
medium_purple = 0x9370DB, // rgb(147,112,219)
medium_sea_green = 0x3CB371, // rgb(60,179,113)
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
medium_violet_red = 0xC71585, // rgb(199,21,133)
midnight_blue = 0x191970, // rgb(25,25,112)
mint_cream = 0xF5FFFA, // rgb(245,255,250)
misty_rose = 0xFFE4E1, // rgb(255,228,225)
moccasin = 0xFFE4B5, // rgb(255,228,181)
navajo_white = 0xFFDEAD, // rgb(255,222,173)
navy = 0x000080, // rgb(0,0,128)
old_lace = 0xFDF5E6, // rgb(253,245,230)
olive = 0x808000, // rgb(128,128,0)
olive_drab = 0x6B8E23, // rgb(107,142,35)
orange = 0xFFA500, // rgb(255,165,0)
orange_red = 0xFF4500, // rgb(255,69,0)
orchid = 0xDA70D6, // rgb(218,112,214)
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
pale_green = 0x98FB98, // rgb(152,251,152)
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
pale_violet_red = 0xDB7093, // rgb(219,112,147)
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
peach_puff = 0xFFDAB9, // rgb(255,218,185)
peru = 0xCD853F, // rgb(205,133,63)
pink = 0xFFC0CB, // rgb(255,192,203)
plum = 0xDDA0DD, // rgb(221,160,221)
powder_blue = 0xB0E0E6, // rgb(176,224,230)
purple = 0x800080, // rgb(128,0,128)
rebecca_purple = 0x663399, // rgb(102,51,153)
red = 0xFF0000, // rgb(255,0,0)
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
royal_blue = 0x4169E1, // rgb(65,105,225)
saddle_brown = 0x8B4513, // rgb(139,69,19)
salmon = 0xFA8072, // rgb(250,128,114)
sandy_brown = 0xF4A460, // rgb(244,164,96)
sea_green = 0x2E8B57, // rgb(46,139,87)
sea_shell = 0xFFF5EE, // rgb(255,245,238)
sienna = 0xA0522D, // rgb(160,82,45)
silver = 0xC0C0C0, // rgb(192,192,192)
sky_blue = 0x87CEEB, // rgb(135,206,235)
slate_blue = 0x6A5ACD, // rgb(106,90,205)
slate_gray = 0x708090, // rgb(112,128,144)
snow = 0xFFFAFA, // rgb(255,250,250)
spring_green = 0x00FF7F, // rgb(0,255,127)
steel_blue = 0x4682B4, // rgb(70,130,180)
tan = 0xD2B48C, // rgb(210,180,140)
teal = 0x008080, // rgb(0,128,128)
thistle = 0xD8BFD8, // rgb(216,191,216)
tomato = 0xFF6347, // rgb(255,99,71)
turquoise = 0x40E0D0, // rgb(64,224,208)
violet = 0xEE82EE, // rgb(238,130,238)
wheat = 0xF5DEB3, // rgb(245,222,179)
white = 0xFFFFFF, // rgb(255,255,255)
white_smoke = 0xF5F5F5, // rgb(245,245,245)
yellow = 0xFFFF00, // rgb(255,255,0)
yellow_green = 0x9ACD32, // rgb(154,205,50)
}; // enum class colors
FMT_END_NAMESPACE
#endif // FMT_COLORS_H_

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,578 @@
// Formatting library for C++
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_FORMAT_INL_H_
#define FMT_FORMAT_INL_H_
#include "format.h"
#include <string.h>
#include <cctype>
#include <cerrno>
#include <climits>
#include <cmath>
#include <cstdarg>
#include <cstddef> // for std::ptrdiff_t
#include <locale>
#if defined(_WIN32) && defined(__MINGW32__)
#include <cstring>
#endif
#if FMT_USE_WINDOWS_H
#if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
#define WIN32_LEAN_AND_MEAN
#endif
#if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
#include <windows.h>
#else
#define NOMINMAX
#include <windows.h>
#undef NOMINMAX
#endif
#endif
#if FMT_EXCEPTIONS
#define FMT_TRY try
#define FMT_CATCH(x) catch (x)
#else
#define FMT_TRY if (true)
#define FMT_CATCH(x) if (false)
#endif
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4127) // conditional expression is constant
#pragma warning(disable : 4702) // unreachable code
// Disable deprecation warning for strerror. The latter is not called but
// MSVC fails to detect it.
#pragma warning(disable : 4996)
#endif
// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
inline fmt::internal::null<> strerror_r(int, char *, ...)
{
return fmt::internal::null<>();
}
inline fmt::internal::null<> strerror_s(char *, std::size_t, ...)
{
return fmt::internal::null<>();
}
FMT_BEGIN_NAMESPACE
namespace {
#ifndef _MSC_VER
#define FMT_SNPRINTF snprintf
#else // _MSC_VER
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...)
{
va_list args;
va_start(args, format);
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
va_end(args);
return result;
}
#define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
#define FMT_SWPRINTF snwprintf
#else
#define FMT_SWPRINTF swprintf
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
typedef void (*FormatFunc)(internal::buffer &, int, string_view);
// Portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
// This can be either a pointer to a string stored in buffer,
// or a pointer to some static immutable string.
// Returns one of the following values:
// 0 - success
// ERANGE - buffer is not large enough to store the error message
// other - failure
// Buffer should be at least of size 1.
int safe_strerror(int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT
{
FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer");
class dispatcher
{
private:
int error_code_;
char *&buffer_;
std::size_t buffer_size_;
// A noop assignment operator to avoid bogus warnings.
void operator=(const dispatcher &) {}
// Handle the result of XSI-compliant version of strerror_r.
int handle(int result)
{
// glibc versions before 2.13 return result in errno.
return result == -1 ? errno : result;
}
// Handle the result of GNU-specific version of strerror_r.
int handle(char *message)
{
// If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
return ERANGE;
buffer_ = message;
return 0;
}
// Handle the case when strerror_r is not available.
int handle(internal::null<>)
{
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
// Fallback to strerror_s when strerror_r is not available.
int fallback(int result)
{
// If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE : result;
}
// Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(internal::null<>)
{
errno = 0;
buffer_ = strerror(error_code_);
return errno;
}
public:
dispatcher(int err_code, char *&buf, std::size_t buf_size)
: error_code_(err_code)
, buffer_(buf)
, buffer_size_(buf_size)
{
}
int run()
{
return handle(strerror_r(error_code_, buffer_, buffer_size_));
}
};
return dispatcher(error_code, buffer, buffer_size).run();
}
void format_error_code(internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT
{
// Report error code making sure that the output fits into
// inline_buffer_size to avoid dynamic memory allocation and potential
// bad_alloc.
out.resize(0);
static const char SEP[] = ": ";
static const char ERROR_STR[] = "error ";
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
typedef internal::int_traits<int>::main_type main_type;
main_type abs_value = static_cast<main_type>(error_code);
if (internal::is_negative(error_code))
{
abs_value = 0 - abs_value;
++error_code_size;
}
error_code_size += internal::count_digits(abs_value);
writer w(out);
if (message.size() <= inline_buffer_size - error_code_size)
{
w.write(message);
w.write(SEP);
}
w.write(ERROR_STR);
w.write(error_code);
assert(out.size() <= inline_buffer_size);
}
void report_error(FormatFunc func, int error_code, string_view message) FMT_NOEXCEPT
{
memory_buffer full_message;
func(full_message, error_code, message);
// Use Writer::data instead of Writer::c_str to avoid potential memory
// allocation.
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
std::fputc('\n', stderr);
}
} // namespace
class locale
{
private:
std::locale locale_;
public:
explicit locale(std::locale loc = std::locale())
: locale_(loc)
{
}
std::locale get()
{
return locale_;
}
};
template<typename Char>
FMT_FUNC Char internal::thousands_sep(locale_provider *lp)
{
std::locale loc = lp ? lp->locale().get() : std::locale();
return std::use_facet<std::numpunct<Char>>(loc).thousands_sep();
}
FMT_FUNC void system_error::init(int err_code, string_view format_str, format_args args)
{
error_code_ = err_code;
memory_buffer buffer;
format_system_error(buffer, err_code, vformat(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(to_string(buffer));
}
namespace internal {
template<typename T>
int char_traits<char>::format_float(char *buffer, std::size_t size, const char *format, int precision, T value)
{
return precision < 0 ? FMT_SNPRINTF(buffer, size, format, value) : FMT_SNPRINTF(buffer, size, format, precision, value);
}
template<typename T>
int char_traits<wchar_t>::format_float(wchar_t *buffer, std::size_t size, const wchar_t *format, int precision, T value)
{
return precision < 0 ? FMT_SWPRINTF(buffer, size, format, value) : FMT_SWPRINTF(buffer, size, format, precision, value);
}
template<typename T>
const char basic_data<T>::DIGITS[] = "0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
#define FMT_POWERS_OF_10(factor) \
factor * 10, factor * 100, factor * 1000, factor * 10000, factor * 100000, factor * 1000000, factor * 10000000, factor * 100000000, \
factor * 1000000000
template<typename T>
const uint32_t basic_data<T>::POWERS_OF_10_32[] = {0, FMT_POWERS_OF_10(1)};
template<typename T>
const uint64_t basic_data<T>::POWERS_OF_10_64[] = {0, FMT_POWERS_OF_10(1), FMT_POWERS_OF_10(1000000000ull), 10000000000000000000ull};
// Normalized 64-bit significands of pow(10, k), for k = -348, -340, ..., 340.
// These are generated by support/compute-powers.py.
template<typename T>
const uint64_t basic_data<T>::POW10_SIGNIFICANDS[] = {0xfa8fd5a0081c0288, 0xbaaee17fa23ebf76, 0x8b16fb203055ac76, 0xcf42894a5dce35ea,
0x9a6bb0aa55653b2d, 0xe61acf033d1a45df, 0xab70fe17c79ac6ca, 0xff77b1fcbebcdc4f, 0xbe5691ef416bd60c, 0x8dd01fad907ffc3c,
0xd3515c2831559a83, 0x9d71ac8fada6c9b5, 0xea9c227723ee8bcb, 0xaecc49914078536d, 0x823c12795db6ce57, 0xc21094364dfb5637,
0x9096ea6f3848984f, 0xd77485cb25823ac7, 0xa086cfcd97bf97f4, 0xef340a98172aace5, 0xb23867fb2a35b28e, 0x84c8d4dfd2c63f3b,
0xc5dd44271ad3cdba, 0x936b9fcebb25c996, 0xdbac6c247d62a584, 0xa3ab66580d5fdaf6, 0xf3e2f893dec3f126, 0xb5b5ada8aaff80b8,
0x87625f056c7c4a8b, 0xc9bcff6034c13053, 0x964e858c91ba2655, 0xdff9772470297ebd, 0xa6dfbd9fb8e5b88f, 0xf8a95fcf88747d94,
0xb94470938fa89bcf, 0x8a08f0f8bf0f156b, 0xcdb02555653131b6, 0x993fe2c6d07b7fac, 0xe45c10c42a2b3b06, 0xaa242499697392d3,
0xfd87b5f28300ca0e, 0xbce5086492111aeb, 0x8cbccc096f5088cc, 0xd1b71758e219652c, 0x9c40000000000000, 0xe8d4a51000000000,
0xad78ebc5ac620000, 0x813f3978f8940984, 0xc097ce7bc90715b3, 0x8f7e32ce7bea5c70, 0xd5d238a4abe98068, 0x9f4f2726179a2245,
0xed63a231d4c4fb27, 0xb0de65388cc8ada8, 0x83c7088e1aab65db, 0xc45d1df942711d9a, 0x924d692ca61be758, 0xda01ee641a708dea,
0xa26da3999aef774a, 0xf209787bb47d6b85, 0xb454e4a179dd1877, 0x865b86925b9bc5c2, 0xc83553c5c8965d3d, 0x952ab45cfa97a0b3,
0xde469fbd99a05fe3, 0xa59bc234db398c25, 0xf6c69a72a3989f5c, 0xb7dcbf5354e9bece, 0x88fcf317f22241e2, 0xcc20ce9bd35c78a5,
0x98165af37b2153df, 0xe2a0b5dc971f303a, 0xa8d9d1535ce3b396, 0xfb9b7cd9a4a7443c, 0xbb764c4ca7a44410, 0x8bab8eefb6409c1a,
0xd01fef10a657842c, 0x9b10a4e5e9913129, 0xe7109bfba19c0c9d, 0xac2820d9623bf429, 0x80444b5e7aa7cf85, 0xbf21e44003acdd2d,
0x8e679c2f5e44ff8f, 0xd433179d9c8cb841, 0x9e19db92b4e31ba9, 0xeb96bf6ebadf77d9, 0xaf87023b9bf0ee6b};
// Binary exponents of pow(10, k), for k = -348, -340, ..., 340, corresponding
// to significands above.
template<typename T>
const int16_t basic_data<T>::POW10_EXPONENTS[] = {-1220, -1193, -1166, -1140, -1113, -1087, -1060, -1034, -1007, -980, -954, -927, -901,
-874, -847, -821, -794, -768, -741, -715, -688, -661, -635, -608, -582, -555, -529, -502, -475, -449, -422, -396, -369, -343, -316,
-289, -263, -236, -210, -183, -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, 375,
402, 428, 455, 481, 508, 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066};
template<typename T>
const char basic_data<T>::RESET_COLOR[] = "\x1b[0m";
template<typename T>
const wchar_t basic_data<T>::WRESET_COLOR[] = L"\x1b[0m";
FMT_FUNC fp operator*(fp x, fp y)
{
// Multiply 32-bit parts of significands.
uint64_t mask = (1ULL << 32) - 1;
uint64_t a = x.f >> 32, b = x.f & mask;
uint64_t c = y.f >> 32, d = y.f & mask;
uint64_t ac = a * c, bc = b * c, ad = a * d, bd = b * d;
// Compute mid 64-bit of result and round.
uint64_t mid = (bd >> 32) + (ad & mask) + (bc & mask) + (1U << 31);
return fp(ac + (ad >> 32) + (bc >> 32) + (mid >> 32), x.e + y.e + 64);
}
FMT_FUNC fp get_cached_power(int min_exponent, int &pow10_exponent)
{
const double one_over_log2_10 = 0.30102999566398114; // 1 / log2(10)
int index = static_cast<int>(std::ceil((min_exponent + fp::significand_size - 1) * one_over_log2_10));
// Decimal exponent of the first (smallest) cached power of 10.
const int first_dec_exp = -348;
// Difference between two consecutive decimal exponents in cached powers of
// 10.
const int dec_exp_step = 8;
index = (index - first_dec_exp - 1) / dec_exp_step + 1;
pow10_exponent = first_dec_exp + index * dec_exp_step;
return fp(data::POW10_SIGNIFICANDS[index], data::POW10_EXPONENTS[index]);
}
} // namespace internal
#if FMT_USE_WINDOWS_H
FMT_FUNC internal::utf8_to_utf16::utf8_to_utf16(string_view s)
{
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
if (s.size() > INT_MAX)
FMT_THROW(windows_error(ERROR_INVALID_PARAMETER, ERROR_MSG));
int s_size = static_cast<int>(s.size());
if (s_size == 0)
{
// MultiByteToWideChar does not support zero length, handle separately.
buffer_.resize(1);
buffer_[0] = 0;
return;
}
int length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
if (length == 0)
FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
buffer_.resize(length + 1);
length = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
if (length == 0)
FMT_THROW(windows_error(GetLastError(), ERROR_MSG));
buffer_[length] = 0;
}
FMT_FUNC internal::utf16_to_utf8::utf16_to_utf8(wstring_view s)
{
if (int error_code = convert(s))
{
FMT_THROW(windows_error(error_code, "cannot convert string from UTF-16 to UTF-8"));
}
}
FMT_FUNC int internal::utf16_to_utf8::convert(wstring_view s)
{
if (s.size() > INT_MAX)
return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
if (s_size == 0)
{
// WideCharToMultiByte does not support zero length, handle separately.
buffer_.resize(1);
buffer_[0] = 0;
return 0;
}
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
if (length == 0)
return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL);
if (length == 0)
return GetLastError();
buffer_[length] = 0;
return 0;
}
FMT_FUNC void windows_error::init(int err_code, string_view format_str, format_args args)
{
error_code_ = err_code;
memory_buffer buffer;
internal::format_windows_error(buffer, err_code, vformat(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(to_string(buffer));
}
FMT_FUNC void internal::format_windows_error(internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT
{
FMT_TRY
{
wmemory_buffer buf;
buf.resize(inline_buffer_size);
for (;;)
{
wchar_t *system_message = &buf[0];
int result = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, FMT_NULL, error_code,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message, static_cast<uint32_t>(buf.size()), FMT_NULL);
if (result != 0)
{
utf16_to_utf8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS)
{
writer w(out);
w.write(message);
w.write(": ");
w.write(utf8_message);
return;
}
break;
}
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
break; // Can't get error message, report error code instead.
buf.resize(buf.size() * 2);
}
}
FMT_CATCH(...) {}
format_error_code(out, error_code, message);
}
#endif // FMT_USE_WINDOWS_H
FMT_FUNC void format_system_error(internal::buffer &out, int error_code, string_view message) FMT_NOEXCEPT
{
FMT_TRY
{
memory_buffer buf;
buf.resize(inline_buffer_size);
for (;;)
{
char *system_message = &buf[0];
int result = safe_strerror(error_code, system_message, buf.size());
if (result == 0)
{
writer w(out);
w.write(message);
w.write(": ");
w.write(system_message);
return;
}
if (result != ERANGE)
break; // Can't get error message, report error code instead.
buf.resize(buf.size() * 2);
}
}
FMT_CATCH(...) {}
format_error_code(out, error_code, message);
}
template<typename Char>
void basic_fixed_buffer<Char>::grow(std::size_t)
{
FMT_THROW(std::runtime_error("buffer overflow"));
}
FMT_FUNC void internal::error_handler::on_error(const char *message)
{
FMT_THROW(format_error(message));
}
FMT_FUNC void report_system_error(int error_code, fmt::string_view message) FMT_NOEXCEPT
{
report_error(format_system_error, error_code, message);
}
#if FMT_USE_WINDOWS_H
FMT_FUNC void report_windows_error(int error_code, fmt::string_view message) FMT_NOEXCEPT
{
report_error(internal::format_windows_error, error_code, message);
}
#endif
FMT_FUNC void vprint(std::FILE *f, string_view format_str, format_args args)
{
memory_buffer buffer;
vformat_to(buffer, format_str, args);
std::fwrite(buffer.data(), 1, buffer.size(), f);
}
FMT_FUNC void vprint(std::FILE *f, wstring_view format_str, wformat_args args)
{
wmemory_buffer buffer;
vformat_to(buffer, format_str, args);
std::fwrite(buffer.data(), sizeof(wchar_t), buffer.size(), f);
}
FMT_FUNC void vprint(string_view format_str, format_args args)
{
vprint(stdout, format_str, args);
}
FMT_FUNC void vprint(wstring_view format_str, wformat_args args)
{
vprint(stdout, format_str, args);
}
#ifndef FMT_EXTENDED_COLORS
FMT_FUNC 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);
}
FMT_FUNC 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
namespace internal {
FMT_CONSTEXPR void to_esc(uint8_t c, char out[], int offset)
{
out[offset + 0] = static_cast<char>('0' + c / 100);
out[offset + 1] = static_cast<char>('0' + c / 10 % 10);
out[offset + 2] = static_cast<char>('0' + c % 10);
}
} // namespace internal
FMT_FUNC void vprint_rgb(rgb fd, string_view format, format_args args)
{
char escape_fd[] = "\x1b[38;2;000;000;000m";
internal::to_esc(fd.r, escape_fd, 7);
internal::to_esc(fd.g, escape_fd, 11);
internal::to_esc(fd.b, escape_fd, 15);
std::fputs(escape_fd, stdout);
vprint(format, args);
std::fputs(internal::data::RESET_COLOR, stdout);
}
FMT_FUNC void vprint_rgb(rgb fd, rgb bg, string_view format, format_args args)
{
char escape_fd[] = "\x1b[38;2;000;000;000m"; // foreground color
char escape_bg[] = "\x1b[48;2;000;000;000m"; // background color
internal::to_esc(fd.r, escape_fd, 7);
internal::to_esc(fd.g, escape_fd, 11);
internal::to_esc(fd.b, escape_fd, 15);
internal::to_esc(bg.r, escape_bg, 7);
internal::to_esc(bg.g, escape_bg, 11);
internal::to_esc(bg.b, escape_bg, 15);
std::fputs(escape_fd, stdout);
std::fputs(escape_bg, stdout);
vprint(format, args);
std::fputs(internal::data::RESET_COLOR, stdout);
}
#endif
FMT_FUNC locale locale_provider::locale()
{
return fmt::locale();
}
FMT_END_NAMESPACE
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif // FMT_FORMAT_INL_H_

View File

@@ -1,535 +0,0 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
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.
*/
#include "format.h"
#include <string.h>
#include <cctype>
#include <cerrno>
#include <climits>
#include <cmath>
#include <cstdarg>
#include <cstddef> // for std::ptrdiff_t
#if defined(_WIN32) && defined(__MINGW32__)
# include <cstring>
#endif
#if FMT_USE_WINDOWS_H
# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
# define WIN32_LEAN_AND_MEAN
# endif
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
# include <windows.h>
# else
# define NOMINMAX
# include <windows.h>
# undef NOMINMAX
# endif
#endif
#if FMT_EXCEPTIONS
# define FMT_TRY try
# define FMT_CATCH(x) catch (x)
#else
# define FMT_TRY if (true)
# define FMT_CATCH(x) if (false)
#endif
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4127) // conditional expression is constant
# pragma warning(disable: 4702) // unreachable code
// Disable deprecation warning for strerror. The latter is not called but
// MSVC fails to detect it.
# pragma warning(disable: 4996)
#endif
// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
return fmt::internal::Null<>();
}
static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
return fmt::internal::Null<>();
}
namespace fmt {
FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {}
FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {}
FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {}
namespace {
#ifndef _MSC_VER
# define FMT_SNPRINTF snprintf
#else // _MSC_VER
inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
va_list args;
va_start(args, format);
int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
va_end(args);
return result;
}
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
#if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
# define FMT_SWPRINTF snwprintf
#else
# define FMT_SWPRINTF swprintf
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
const char RESET_COLOR[] = "\x1b[0m";
typedef void (*FormatFunc)(Writer &, int, StringRef);
// Portable thread-safe version of strerror.
// Sets buffer to point to a string describing the error code.
// This can be either a pointer to a string stored in buffer,
// or a pointer to some static immutable string.
// Returns one of the following values:
// 0 - success
// ERANGE - buffer is not large enough to store the error message
// other - failure
// Buffer should be at least of size 1.
int safe_strerror(
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer");
class StrError {
private:
int error_code_;
char *&buffer_;
std::size_t buffer_size_;
// A noop assignment operator to avoid bogus warnings.
void operator=(const StrError &) {}
// Handle the result of XSI-compliant version of strerror_r.
int handle(int result) {
// glibc versions before 2.13 return result in errno.
return result == -1 ? errno : result;
}
// Handle the result of GNU-specific version of strerror_r.
int handle(char *message) {
// If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
return ERANGE;
buffer_ = message;
return 0;
}
// Handle the case when strerror_r is not available.
int handle(internal::Null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
// Fallback to strerror_s when strerror_r is not available.
int fallback(int result) {
// If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
ERANGE : result;
}
// Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(internal::Null<>) {
errno = 0;
buffer_ = strerror(error_code_);
return errno;
}
public:
StrError(int err_code, char *&buf, std::size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
int run() {
// Suppress a warning about unused strerror_r.
strerror_r(0, FMT_NULL, "");
return handle(strerror_r(error_code_, buffer_, buffer_size_));
}
};
return StrError(error_code, buffer, buffer_size).run();
}
void format_error_code(Writer &out, int error_code,
StringRef message) FMT_NOEXCEPT {
// Report error code making sure that the output fits into
// INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
// bad_alloc.
out.clear();
static const char SEP[] = ": ";
static const char ERROR_STR[] = "error ";
// Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
typedef internal::IntTraits<int>::MainType MainType;
MainType abs_value = static_cast<MainType>(error_code);
if (internal::is_negative(error_code)) {
abs_value = 0 - abs_value;
++error_code_size;
}
error_code_size += internal::count_digits(abs_value);
if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size)
out << message << SEP;
out << ERROR_STR << error_code;
assert(out.size() <= internal::INLINE_BUFFER_SIZE);
}
void report_error(FormatFunc func, int error_code,
StringRef message) FMT_NOEXCEPT {
MemoryWriter full_message;
func(full_message, error_code, message);
// Use Writer::data instead of Writer::c_str to avoid potential memory
// allocation.
std::fwrite(full_message.data(), full_message.size(), 1, stderr);
std::fputc('\n', stderr);
}
} // namespace
FMT_FUNC void SystemError::init(
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
format_system_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
template <typename T>
int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, T value) {
if (width == 0) {
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, value) :
FMT_SNPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ?
FMT_SNPRINTF(buffer, size, format, width, value) :
FMT_SNPRINTF(buffer, size, format, width, precision, value);
}
template <typename T>
int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, T value) {
if (width == 0) {
return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, value) :
FMT_SWPRINTF(buffer, size, format, precision, value);
}
return precision < 0 ?
FMT_SWPRINTF(buffer, size, format, width, value) :
FMT_SWPRINTF(buffer, size, format, width, precision, value);
}
template <typename T>
const char internal::BasicData<T>::DIGITS[] =
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"4041424344454647484950515253545556575859"
"6061626364656667686970717273747576777879"
"8081828384858687888990919293949596979899";
#define FMT_POWERS_OF_10(factor) \
factor * 10, \
factor * 100, \
factor * 1000, \
factor * 10000, \
factor * 100000, \
factor * 1000000, \
factor * 10000000, \
factor * 100000000, \
factor * 1000000000
template <typename T>
const uint32_t internal::BasicData<T>::POWERS_OF_10_32[] = {
0, FMT_POWERS_OF_10(1)
};
template <typename T>
const uint64_t internal::BasicData<T>::POWERS_OF_10_64[] = {
0,
FMT_POWERS_OF_10(1),
FMT_POWERS_OF_10(ULongLong(1000000000)),
// Multiply several constants instead of using a single long long constant
// to avoid warnings about C++98 not supporting long long.
ULongLong(1000000000) * ULongLong(1000000000) * 10
};
FMT_FUNC void internal::report_unknown_type(char code, const char *type) {
(void)type;
if (std::isprint(static_cast<unsigned char>(code))) {
FMT_THROW(FormatError(
format("unknown format code '{}' for {}", code, type)));
}
FMT_THROW(FormatError(
format("unknown format code '\\x{:02x}' for {}",
static_cast<unsigned>(code), type)));
}
#if FMT_USE_WINDOWS_H
FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) {
static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
if (s.size() > INT_MAX)
FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
int s_size = static_cast<int>(s.size());
int length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_.resize(length + 1);
length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_[length] = 0;
}
FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s) {
if (int error_code = convert(s)) {
FMT_THROW(WindowsError(error_code,
"cannot convert string from UTF-16 to UTF-8"));
}
}
FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) {
if (s.size() > INT_MAX)
return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
int length = WideCharToMultiByte(
CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
if (length == 0)
return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL);
if (length == 0)
return GetLastError();
buffer_[length] = 0;
return 0;
}
FMT_FUNC void WindowsError::init(
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
MemoryWriter w;
internal::format_windows_error(w, err_code, format(format_str, args));
std::runtime_error &base = *this;
base = std::runtime_error(w.str());
}
FMT_FUNC void internal::format_windows_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
FMT_TRY {
MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer;
buffer.resize(INLINE_BUFFER_SIZE);
for (;;) {
wchar_t *system_message = &buffer[0];
int result = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
system_message, static_cast<uint32_t>(buffer.size()), FMT_NULL);
if (result != 0) {
UTF16ToUTF8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
out << message << ": " << utf8_message;
return;
}
break;
}
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2);
}
} FMT_CATCH(...) {}
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
#endif // FMT_USE_WINDOWS_H
FMT_FUNC void format_system_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
FMT_TRY {
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> buffer;
buffer.resize(internal::INLINE_BUFFER_SIZE);
for (;;) {
char *system_message = &buffer[0];
int result = safe_strerror(error_code, system_message, buffer.size());
if (result == 0) {
out << message << ": " << system_message;
return;
}
if (result != ERANGE)
break; // Can't get error message, report error code instead.
buffer.resize(buffer.size() * 2);
}
} FMT_CATCH(...) {}
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
template <typename Char>
void internal::ArgMap<Char>::init(const ArgList &args) {
if (!map_.empty())
return;
typedef internal::NamedArg<Char> NamedArg;
const NamedArg *named_arg = FMT_NULL;
bool use_values =
args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE;
if (use_values) {
for (unsigned i = 0;/*nothing*/; ++i) {
internal::Arg::Type arg_type = args.type(i);
switch (arg_type) {
case internal::Arg::NONE:
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.values_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/;
}
}
return;
}
for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) {
internal::Arg::Type arg_type = args.type(i);
if (arg_type == internal::Arg::NAMED_ARG) {
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
}
}
for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) {
switch (args.args_[i].type) {
case internal::Arg::NONE:
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/;
}
}
}
template <typename Char>
void internal::FixedBuffer<Char>::grow(std::size_t) {
FMT_THROW(std::runtime_error("buffer overflow"));
}
FMT_FUNC internal::Arg internal::FormatterBase::do_get_arg(
unsigned arg_index, const char *&error) {
internal::Arg arg = args_[arg_index];
switch (arg.type) {
case internal::Arg::NONE:
error = "argument index out of range";
break;
case internal::Arg::NAMED_ARG:
arg = *static_cast<const internal::Arg*>(arg.pointer);
break;
default:
/*nothing*/;
}
return arg;
}
FMT_FUNC void report_system_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
// 'fmt::' is for bcc32.
report_error(format_system_error, error_code, message);
}
#if FMT_USE_WINDOWS_H
FMT_FUNC void report_windows_error(
int error_code, fmt::StringRef message) FMT_NOEXCEPT {
// 'fmt::' is for bcc32.
report_error(internal::format_windows_error, error_code, message);
}
#endif
FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
std::fwrite(w.data(), 1, w.size(), f);
}
FMT_FUNC void print(CStringRef format_str, ArgList args) {
print(stdout, format_str, args);
}
FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) {
char escape[] = "\x1b[30m";
escape[3] = static_cast<char>('0' + c);
std::fputs(escape, stdout);
print(format, args);
std::fputs(RESET_COLOR, stdout);
}
#ifndef FMT_HEADER_ONLY
template struct internal::BasicData<void>;
// Explicit instantiations for char.
template void internal::FixedBuffer<char>::grow(std::size_t);
template void internal::ArgMap<char>::init(const ArgList &args);
template FMT_API int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, double value);
template FMT_API int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, long double value);
// Explicit instantiations for wchar_t.
template void internal::FixedBuffer<wchar_t>::grow(std::size_t);
template void internal::ArgMap<wchar_t>::init(const ArgList &args);
template FMT_API int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, double value);
template FMT_API int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, long double value);
#endif // FMT_HEADER_ONLY
} // namespace fmt
#ifdef _MSC_VER
# pragma warning(pop)
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,27 @@
// Formatting library for C++ - locale support
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "format.h"
#include <locale>
namespace fmt {
class locale
{
private:
std::locale locale_;
public:
explicit locale(std::locale loc = std::locale())
: locale_(loc)
{
}
std::locale get()
{
return locale_;
}
};
} // namespace fmt

View File

@@ -1,35 +0,0 @@
/*
Formatting library for C++ - std::ostream support
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#include "ostream.h"
namespace fmt {
namespace internal {
FMT_FUNC void write(std::ostream &os, Writer &w) {
const char *data = w.data();
typedef internal::MakeUnsigned<std::streamsize>::Type UnsignedStreamSize;
UnsignedStreamSize size = w.size();
UnsignedStreamSize max_size =
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
do {
UnsignedStreamSize n = size <= max_size ? size : max_size;
os.write(data, static_cast<std::streamsize>(n));
data += n;
size -= n;
} while (size != 0);
}
}
FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) {
MemoryWriter w;
w.write(format_str, args);
internal::write(os, w);
}
} // namespace fmt

View File

@@ -1,11 +1,9 @@
/* // Formatting library for C++ - std::ostream support
Formatting library for C++ - std::ostream support //
// Copyright (c) 2012 - 2016, Victor Zverovich
Copyright (c) 2012 - 2016, Victor Zverovich // All rights reserved.
All rights reserved. //
// For the license information refer to format.h.
For the license information refer to format.h.
*/
#ifndef FMT_OSTREAM_H_ #ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_ #define FMT_OSTREAM_H_
@@ -13,20 +11,23 @@
#include "format.h" #include "format.h"
#include <ostream> #include <ostream>
namespace fmt { 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; typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type; typedef typename std::basic_streambuf<Char>::traits_type traits_type;
Buffer<Char> &buffer_; basic_buffer<Char> &buffer_;
public: public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer) {} formatbuf(basic_buffer<Char> &buffer)
: buffer_(buffer)
{
}
protected: protected:
// The put-area is actually always empty. This makes the implementation // The put-area is actually always empty. This makes the implementation
@@ -36,70 +37,137 @@ class FormatBuf : public std::basic_streambuf<Char> {
// to overflow. There is no disadvantage here for sputn since this always // to overflow. There is no disadvantage here for sputn since this always
// results in a call to xsputn. // results in a call to xsputn.
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE { int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE
{
if (!traits_type::eq_int_type(ch, traits_type::eof())) if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<Char>(ch)); buffer_.push_back(static_cast<Char>(ch));
return ch; return ch;
} }
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE { std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE
{
buffer_.append(s, s + count); buffer_.append(s, s + count);
return count; return count;
} }
}; };
Yes &convert(std::ostream &); template<typename Char>
struct test_stream : std::basic_ostream<Char>
struct DummyStream : std::ostream { {
DummyStream(); // Suppress a bogus warning in MSVC. private:
// Hide all operator<< overloads from std::ostream. struct null;
void operator<<(Null<>); // Hide all operator<< from std::basic_ostream<Char>.
void operator<<(null);
}; };
No &operator<<(std::ostream &, int); // Checks if T has a user-defined operator<< (e.g. not a member of
// std::ostream).
template<typename T, typename Char>
class is_streamable
{
private:
template<typename U>
static decltype(internal::declval<test_stream<Char> &>() << internal::declval<U>(), std::true_type()) test(int);
template<typename T> template<typename>
struct ConvertToIntImpl<T, true> { static std::false_type test(...);
// Convert to int only if T doesn't have an overloaded operator<<.
enum { typedef decltype(test<T>(0)) result;
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
}; public:
// std::string operator<< is not considered user-defined because we handle
// strings
// specially.
static const bool value = result::value && !std::is_same<T, std::string>::value;
}; };
// Write the content of w to os. // Disable conversion to int if T has an overloaded operator<< which is a free
FMT_API void write(std::ostream &os, Writer &w); // function (not a member of std::ostream).
} // namespace internal template<typename T, typename Char>
class convert_to_int<T, Char, true>
{
public:
static const bool value = convert_to_int<T, Char, false>::value && !is_streamable<T, Char>::value;
};
// Formats a value. // Write the content of buf to os.
template <typename Char, typename ArgFormatter_, typename T> template<typename Char>
void format_arg(BasicFormatter<Char, ArgFormatter_> &f, void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf)
const Char *&format_str, const T &value) { {
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer; const Char *data = buf.data();
typedef std::make_unsigned<std::streamsize>::type UnsignedStreamSize;
internal::FormatBuf<Char> format_buf(buffer); UnsignedStreamSize size = buf.size();
std::basic_ostream<Char> output(&format_buf); UnsignedStreamSize max_size = internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
output << value; do
{
BasicStringRef<Char> str(&buffer[0], buffer.size()); UnsignedStreamSize n = size <= max_size ? size : max_size;
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg; os.write(data, static_cast<std::streamsize>(n));
format_str = f.format(format_str, MakeArg(str)); data += n;
size -= n;
} while (size != 0);
} }
template<typename Char, typename T>
void format_value(basic_buffer<Char> &buffer, const T &value)
{
internal::formatbuf<Char> format_buf(buffer);
std::basic_ostream<Char> output(&format_buf);
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value;
buffer.resize(buffer.size());
}
// Disable builtin formatting of enums and use operator<< instead.
template<typename T>
struct format_enum<T, typename std::enable_if<std::is_enum<T>::value>::type> : std::false_type
{
};
} // namespace internal
// Formats an object of type T that has an overloaded ostream operator<<.
template<typename T, typename Char>
struct formatter<T, Char, typename std::enable_if<internal::is_streamable<T, Char>::value>::type> : formatter<basic_string_view<Char>, Char>
{
template<typename Context>
auto format(const T &value, Context &ctx) -> decltype(ctx.out())
{
basic_memory_buffer<Char> buffer;
internal::format_value(buffer, value);
basic_string_view<Char> str(buffer.data(), buffer.size());
formatter<basic_string_view<Char>, Char>::format(str, ctx);
return ctx.out();
}
};
template<typename Char>
inline void vprint(
std::basic_ostream<Char> &os, basic_string_view<Char> format_str, basic_format_args<typename buffer_context<Char>::type> args)
{
basic_memory_buffer<Char> buffer;
vformat_to(buffer, format_str, args);
internal::write(os, buffer);
}
/** /**
\rst \rst
Prints formatted data to the stream *os*. Prints formatted data to the stream *os*.
**Example**:: **Example**::
print(cerr, "Don't {}!", "panic"); fmt::print(cerr, "Don't {}!", "panic");
\endrst \endrst
*/ */
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args); template<typename... Args>
FMT_VARIADIC(void, print, std::ostream &, CStringRef) inline void print(std::ostream &os, string_view format_str, const Args &... args)
} // namespace fmt {
vprint<char>(os, format_str, make_format_args<format_context>(args...));
}
#ifdef FMT_HEADER_ONLY template<typename... Args>
# include "ostream.cc" inline void print(std::wostream &os, wstring_view format_str, const Args &... args)
#endif {
vprint<wchar_t>(os, format_str, make_format_args<wformat_context>(args...));
}
FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_ #endif // FMT_OSTREAM_H_

View File

@@ -1,241 +0,0 @@
/*
A C++ interface to POSIX functions.
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
// Disable bogus MSVC warnings.
#ifndef _CRT_SECURE_NO_WARNINGS
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "posix.h"
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef _WIN32
# include <unistd.h>
#else
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h>
# include <io.h>
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# ifndef S_IRUSR
# define S_IRUSR _S_IREAD
# endif
# ifndef S_IWUSR
# define S_IWUSR _S_IWRITE
# endif
# ifdef __MINGW32__
# define _SH_DENYNO 0x40
# endif
#endif // _WIN32
#ifdef fileno
# undef fileno
#endif
namespace {
#ifdef _WIN32
// Return type of read and write functions.
typedef int RWResult;
// On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow.
inline unsigned convert_rwcount(std::size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#else
// Return type of read and write functions.
typedef ssize_t RWResult;
inline std::size_t convert_rwcount(std::size_t count) { return count; }
#endif
}
fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
fmt::report_system_error(errno, "cannot close file");
}
fmt::BufferedFile::BufferedFile(
fmt::CStringRef filename, fmt::CStringRef mode) {
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0);
if (!file_)
FMT_THROW(SystemError(errno, "cannot open file {}", filename));
}
void fmt::BufferedFile::close() {
if (!file_)
return;
int result = FMT_SYSTEM(fclose(file_));
file_ = FMT_NULL;
if (result != 0)
FMT_THROW(SystemError(errno, "cannot close file"));
}
// A macro used to prevent expansion of fileno on broken versions of MinGW.
#define FMT_ARGS
int fmt::BufferedFile::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1)
FMT_THROW(SystemError(errno, "cannot get file descriptor"));
return fd;
}
fmt::File::File(fmt::CStringRef path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
#if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
#else
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
#endif
if (fd_ == -1)
FMT_THROW(SystemError(errno, "cannot open file {}", path));
}
fmt::File::~File() FMT_NOEXCEPT {
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
fmt::report_system_error(errno, "cannot close file");
}
void fmt::File::close() {
if (fd_ == -1)
return;
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
int result = FMT_POSIX_CALL(close(fd_));
fd_ = -1;
if (result != 0)
FMT_THROW(SystemError(errno, "cannot close file"));
}
fmt::LongLong fmt::File::size() const {
#ifdef _WIN32
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
// is less than 0x0500 as is the case with some default MinGW builds.
// Both functions support large file sizes.
DWORD size_upper = 0;
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
if (size_lower == INVALID_FILE_SIZE) {
DWORD error = GetLastError();
if (error != NO_ERROR)
FMT_THROW(WindowsError(GetLastError(), "cannot get file size"));
}
fmt::ULongLong long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
#else
typedef struct stat Stat;
Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(SystemError(errno, "cannot get file attributes"));
FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(file_stat.st_size),
"return type of File::size is not large enough");
return file_stat.st_size;
#endif
}
std::size_t fmt::File::read(void *buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0)
FMT_THROW(SystemError(errno, "cannot read from file"));
return internal::to_unsigned(result);
}
std::size_t fmt::File::write(const void *buffer, std::size_t count) {
RWResult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0)
FMT_THROW(SystemError(errno, "cannot write to file"));
return internal::to_unsigned(result);
}
fmt::File fmt::File::dup(int fd) {
// Don't retry as dup doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd));
if (new_fd == -1)
FMT_THROW(SystemError(errno, "cannot duplicate file descriptor {}", fd));
return File(new_fd);
}
void fmt::File::dup2(int fd) {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) {
FMT_THROW(SystemError(errno,
"cannot duplicate file descriptor {} to {}", fd_, fd));
}
}
void fmt::File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1)
ec = ErrorCode(errno);
}
void fmt::File::pipe(File &read_end, File &write_end) {
// Close the descriptors first to make sure that assignments don't throw
// and there are no leaks.
read_end.close();
write_end.close();
int fds[2] = {};
#ifdef _WIN32
// Make the default pipe capacity same as on Linux 2.6.11+.
enum { DEFAULT_CAPACITY = 65536 };
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
#else
// Don't retry as the pipe function doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds));
#endif
if (result != 0)
FMT_THROW(SystemError(errno, "cannot create pipe"));
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_end = File(fds[0]);
write_end = File(fds[1]);
}
fmt::BufferedFile fmt::File::fdopen(const char *mode) {
// Don't retry as fdopen doesn't return EINTR.
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
if (!f)
FMT_THROW(SystemError(errno, "cannot associate stream with file descriptor"));
BufferedFile file(f);
fd_ = -1;
return file;
}
long fmt::getpagesize() {
#ifdef _WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwPageSize;
#else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
if (size < 0)
FMT_THROW(SystemError(errno, "cannot get memory page size"));
return size;
#endif
}

View File

@@ -1,11 +1,9 @@
/* // A C++ interface to POSIX functions.
A C++ interface to POSIX functions. //
// Copyright (c) 2012 - 2016, Victor Zverovich
Copyright (c) 2012 - 2016, Victor Zverovich // All rights reserved.
All rights reserved. //
// For the license information refer to format.h.
For the license information refer to format.h.
*/
#ifndef FMT_POSIX_H_ #ifndef FMT_POSIX_H_
#define FMT_POSIX_H_ #define FMT_POSIX_H_
@@ -55,7 +53,8 @@
// 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
@@ -64,34 +63,100 @@
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
namespace fmt { FMT_BEGIN_NAMESPACE
/**
\rst
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following typedefs for common character types:
+---------------+-----------------------------+
| Type | Definition |
+===============+=============================+
| cstring_view | basic_cstring_view<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/
template<typename Char>
class basic_cstring_view
{
private:
const Char *data_;
public:
/** Constructs a string reference object from a C string. */
basic_cstring_view(const Char *s)
: data_(s)
{
}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char> &s)
: data_(s.c_str())
{
}
/** Returns the pointer to a C string. */
const Char *c_str() const
{
return data_;
}
};
typedef basic_cstring_view<char> cstring_view;
typedef basic_cstring_view<wchar_t> wcstring_view;
// An error code. // An error code.
class ErrorCode { class error_code
{
private: private:
int value_; int value_;
public: public:
explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {} explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; } int get() const FMT_NOEXCEPT
{
return value_;
}
}; };
// A buffered file. // A buffered file.
class BufferedFile { class buffered_file
{
private: private:
FILE *file_; FILE *file_;
friend class File; friend class file;
explicit BufferedFile(FILE *f) : file_(f) {} explicit buffered_file(FILE *f)
: file_(f)
{
}
public: public:
// Constructs a BufferedFile object which doesn't represent any file. // Constructs a buffered_file object which doesn't represent any file.
BufferedFile() FMT_NOEXCEPT : file_(FMT_NULL) {} buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {}
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~BufferedFile() FMT_NOEXCEPT; FMT_API ~buffered_file() FMT_DTOR_NOEXCEPT;
#if !FMT_USE_RVALUE_REFERENCES #if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue // Emulate a move constructor and a move assignment operator if rvalue
@@ -100,28 +165,32 @@ class BufferedFile {
private: private:
// A proxy object to emulate a move constructor. // A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly. // It is private to make it impossible call operator Proxy directly.
struct Proxy { struct Proxy
{
FILE *file; FILE *file;
}; };
public: public:
// A "move constructor" for moving from a temporary. // A "move constructor" for moving from a temporary.
BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {} buffered_file(Proxy p) FMT_NOEXCEPT : file_(p.file) {}
// A "move constructor" for moving from an lvalue. // A "move constructor" for moving from an lvalue.
BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) { buffered_file(buffered_file &f) FMT_NOEXCEPT : file_(f.file_)
{
f.file_ = FMT_NULL; f.file_ = FMT_NULL;
} }
// A "move assignment operator" for moving from a temporary. // A "move assignment operator" for moving from a temporary.
BufferedFile &operator=(Proxy p) { buffered_file &operator=(Proxy p)
{
close(); close();
file_ = p.file; file_ = p.file;
return *this; return *this;
} }
// A "move assignment operator" for moving from an lvalue. // A "move assignment operator" for moving from an lvalue.
BufferedFile &operator=(BufferedFile &other) { buffered_file &operator=(buffered_file &other)
{
close(); close();
file_ = other.file_; file_ = other.file_;
other.file_ = FMT_NULL; other.file_ = FMT_NULL;
@@ -129,8 +198,9 @@ public:
} }
// Returns a proxy object for moving from a temporary: // Returns a proxy object for moving from a temporary:
// BufferedFile file = BufferedFile(...); // buffered_file file = buffered_file(...);
operator Proxy() FMT_NOEXCEPT { operator Proxy() FMT_NOEXCEPT
{
Proxy p = {file_}; Proxy p = {file_};
file_ = FMT_NULL; file_ = FMT_NULL;
return p; return p;
@@ -138,14 +208,16 @@ public:
#else #else
private: private:
FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile); FMT_DISALLOW_COPY_AND_ASSIGN(buffered_file);
public: public:
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) { buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_)
{
other.file_ = FMT_NULL; other.file_ = FMT_NULL;
} }
BufferedFile& operator=(BufferedFile &&other) { buffered_file &operator=(buffered_file &&other)
{
close(); close();
file_ = other.file_; file_ = other.file_;
other.file_ = FMT_NULL; other.file_ = FMT_NULL;
@@ -154,50 +226,64 @@ public:
#endif #endif
// Opens a file. // Opens a file.
FMT_API BufferedFile(CStringRef filename, CStringRef mode); FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file. // Closes the 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 print(CStringRef format_str, const ArgList &args) { void vprint(string_view format_str, format_args args)
fmt::print(file_, format_str, args); {
fmt::vprint(file_, format_str, args);
}
template<typename... Args>
inline void print(string_view format_str, const Args &... args)
{
vprint(format_str, make_format_args(args...));
} }
FMT_VARIADIC(void, print, CStringRef)
}; };
// A file. Closed file is represented by a File object with descriptor -1. // A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw // Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::SystemError in case of failure. Note that some errors such as // fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather // closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the // than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler. // invalid parameter handler with _set_invalid_parameter_handler.
class File { class file
{
private: private:
int fd_; // File descriptor. int fd_; // File descriptor.
// Constructs a File object with a given descriptor. // Constructs a file object with a given descriptor.
explicit File(int fd) : fd_(fd) {} explicit file(int fd)
: fd_(fd)
{
}
public: public:
// Possible values for the oflag argument to the constructor. // Possible values for the oflag argument to the constructor.
enum { enum
{
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing. RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
}; };
// Constructs a File object which doesn't represent any file. // Constructs a file object which doesn't represent any file.
File() FMT_NOEXCEPT : fd_(-1) {} file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a File object representing this file. // Opens a file and constructs a file object representing this file.
FMT_API File(CStringRef path, int oflag); FMT_API file(cstring_view path, int oflag);
#if !FMT_USE_RVALUE_REFERENCES #if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue // Emulate a move constructor and a move assignment operator if rvalue
@@ -206,28 +292,32 @@ class File {
private: private:
// A proxy object to emulate a move constructor. // A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly. // It is private to make it impossible call operator Proxy directly.
struct Proxy { struct Proxy
{
int fd; int fd;
}; };
public: public:
// A "move constructor" for moving from a temporary. // A "move constructor" for moving from a temporary.
File(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {} file(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {}
// A "move constructor" for moving from an lvalue. // A "move constructor" for moving from an lvalue.
File(File &other) FMT_NOEXCEPT : fd_(other.fd_) { file(file &other) FMT_NOEXCEPT : fd_(other.fd_)
{
other.fd_ = -1; other.fd_ = -1;
} }
// A "move assignment operator" for moving from a temporary. // A "move assignment operator" for moving from a temporary.
File &operator=(Proxy p) { file &operator=(Proxy p)
{
close(); close();
fd_ = p.fd; fd_ = p.fd;
return *this; return *this;
} }
// A "move assignment operator" for moving from an lvalue. // A "move assignment operator" for moving from an lvalue.
File &operator=(File &other) { file &operator=(file &other)
{
close(); close();
fd_ = other.fd_; fd_ = other.fd_;
other.fd_ = -1; other.fd_ = -1;
@@ -235,8 +325,9 @@ class File {
} }
// Returns a proxy object for moving from a temporary: // Returns a proxy object for moving from a temporary:
// File file = File(...); // file f = file(...);
operator Proxy() FMT_NOEXCEPT { operator Proxy() FMT_NOEXCEPT
{
Proxy p = {fd_}; Proxy p = {fd_};
fd_ = -1; fd_ = -1;
return p; return p;
@@ -244,14 +335,16 @@ class File {
#else #else
private: private:
FMT_DISALLOW_COPY_AND_ASSIGN(File); FMT_DISALLOW_COPY_AND_ASSIGN(file);
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;
@@ -260,17 +353,20 @@ class File {
#endif #endif
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~File() FMT_NOEXCEPT; FMT_API ~file() FMT_DTOR_NOEXCEPT;
// Returns the file descriptor. // Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; } int descriptor() const FMT_NOEXCEPT
{
return fd_;
}
// Closes the file. // Closes the file.
FMT_API void close(); FMT_API void close();
// Returns the file size. The size has signed type for consistency with // Returns the file size. The size has signed type for consistency with
// stat::st_size. // stat::st_size.
FMT_API LongLong 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);
@@ -280,7 +376,7 @@ class File {
// 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.
FMT_API static File dup(int fd); FMT_API static file dup(int fd);
// 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.
@@ -288,43 +384,49 @@ 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, ErrorCode &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 BufferedFile 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 BufferedFile 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)) && \ #if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && !defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__)
!defined(__ANDROID__) && !defined(__CYGWIN__)
#define FMT_LOCALE #define FMT_LOCALE
#endif #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 _MSC_VER
typedef _locale_t locale_t; typedef _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
@@ -336,17 +438,26 @@ class Locale {
public: public:
typedef locale_t Type; typedef locale_t Type;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) { Locale()
: locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL))
{
if (!locale_) if (!locale_)
FMT_THROW(fmt::SystemError(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 = FMT_NULL;
double result = strtod_l(str, &end, locale_); double result = strtod_l(str, &end, locale_);
str = end; str = end;
@@ -354,14 +465,20 @@ class Locale {
} }
}; };
#endif // FMT_LOCALE #endif // FMT_LOCALE
} // namespace fmt FMT_END_NAMESPACE
#if !FMT_USE_RVALUE_REFERENCES #if !FMT_USE_RVALUE_REFERENCES
namespace std { namespace std {
// For compatibility with C++98. // For compatibility with C++98.
inline fmt::BufferedFile &move(fmt::BufferedFile &f) { return f; } inline fmt::buffered_file &move(fmt::buffered_file &f)
inline fmt::File &move(fmt::File &f) { return f; } {
return f;
} }
inline fmt::file &move(fmt::file &f)
{
return f;
}
} // namespace std
#endif #endif
#endif // FMT_POSIX_H_ #endif // FMT_POSIX_H_

View File

@@ -0,0 +1,807 @@
// Formatting library for C++
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::fill_n
#include <limits> // std::numeric_limits
#include "ostream.h"
FMT_BEGIN_NAMESPACE
namespace internal {
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template<bool IsSigned>
struct int_checker
{
template<typename T>
static bool fits_in_int(T value)
{
unsigned max = std::numeric_limits<int>::max();
return value <= max;
}
static bool fits_in_int(bool)
{
return true;
}
};
template<>
struct int_checker<true>
{
template<typename T>
static bool fits_in_int(T value)
{
return value >= std::numeric_limits<int>::min() && value <= std::numeric_limits<int>::max();
}
static bool fits_in_int(int)
{
return true;
}
};
class printf_precision_handler : public function<int>
{
public:
template<typename T>
typename std::enable_if<std::is_integral<T>::value, int>::type operator()(T value)
{
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(format_error("number is too big"));
return static_cast<int>(value);
}
template<typename T>
typename std::enable_if<!std::is_integral<T>::value, int>::type operator()(T)
{
FMT_THROW(format_error("precision is not integer"));
return 0;
}
};
// An argument visitor that returns true iff arg is a zero integer.
class is_zero_int : public function<bool>
{
public:
template<typename T>
typename std::enable_if<std::is_integral<T>::value, bool>::type operator()(T value)
{
return value == 0;
}
template<typename T>
typename std::enable_if<!std::is_integral<T>::value, bool>::type operator()(T)
{
return false;
}
};
template<typename T>
struct make_unsigned_or_bool : std::make_unsigned<T>
{
};
template<>
struct make_unsigned_or_bool<bool>
{
typedef bool type;
};
template<typename T, typename Context>
class arg_converter : public function<void>
{
private:
typedef typename Context::char_type Char;
basic_format_arg<Context> &arg_;
typename Context::char_type type_;
public:
arg_converter(basic_format_arg<Context> &arg, Char type)
: arg_(arg)
, type_(type)
{
}
void operator()(bool value)
{
if (type_ != 's')
operator()<bool>(value);
}
template<typename U>
typename std::enable_if<std::is_integral<U>::value>::type operator()(U value)
{
bool is_signed = type_ == 'd' || type_ == 'i';
typedef typename std::conditional<std::is_same<T, void>::value, U, T>::type TargetType;
if (const_check(sizeof(TargetType) <= sizeof(int)))
{
// Extra casts are used to silence warnings.
if (is_signed)
{
arg_ = internal::make_arg<Context>(static_cast<int>(static_cast<TargetType>(value)));
}
else
{
typedef typename make_unsigned_or_bool<TargetType>::type Unsigned;
arg_ = internal::make_arg<Context>(static_cast<unsigned>(static_cast<Unsigned>(value)));
}
}
else
{
if (is_signed)
{
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_ = internal::make_arg<Context>(static_cast<long long>(value));
}
else
{
arg_ = internal::make_arg<Context>(static_cast<typename make_unsigned_or_bool<U>::type>(value));
}
}
}
template<typename U>
typename std::enable_if<!std::is_integral<U>::value>::type operator()(U)
{
// No coversion needed for non-integral types.
}
};
// Converts an integer argument to T for printf, if T is an integral type.
// If T is void, the argument is converted to corresponding signed or unsigned
// type depending on the type specifier: 'd' and 'i' - signed, other -
// unsigned).
template<typename T, typename Context, typename Char>
void convert_arg(basic_format_arg<Context> &arg, Char type)
{
visit(arg_converter<T, Context>(arg, type), arg);
}
// Converts an integer argument to char for printf.
template<typename Context>
class char_converter : public function<void>
{
private:
basic_format_arg<Context> &arg_;
FMT_DISALLOW_COPY_AND_ASSIGN(char_converter);
public:
explicit char_converter(basic_format_arg<Context> &arg)
: arg_(arg)
{
}
template<typename T>
typename std::enable_if<std::is_integral<T>::value>::type operator()(T value)
{
typedef typename Context::char_type Char;
arg_ = internal::make_arg<Context>(static_cast<Char>(value));
}
template<typename T>
typename std::enable_if<!std::is_integral<T>::value>::type operator()(T)
{
// No coversion needed for non-integral types.
}
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
template<typename Char>
class printf_width_handler : public function<unsigned>
{
private:
typedef basic_format_specs<Char> format_specs;
format_specs &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(printf_width_handler);
public:
explicit printf_width_handler(format_specs &spec)
: spec_(spec)
{
}
template<typename T>
typename std::enable_if<std::is_integral<T>::value, unsigned>::type operator()(T value)
{
typedef typename internal::int_traits<T>::main_type UnsignedType;
UnsignedType width = static_cast<UnsignedType>(value);
if (internal::is_negative(value))
{
spec_.align_ = ALIGN_LEFT;
width = 0 - width;
}
unsigned int_max = std::numeric_limits<int>::max();
if (width > int_max)
FMT_THROW(format_error("number is too big"));
return static_cast<unsigned>(width);
}
template<typename T>
typename std::enable_if<!std::is_integral<T>::value, unsigned>::type operator()(T)
{
FMT_THROW(format_error("width is not integer"));
return 0;
}
};
} // namespace internal
template<typename Range>
class printf_arg_formatter;
template<typename OutputIt, typename Char, typename ArgFormatter = printf_arg_formatter<back_insert_range<internal::basic_buffer<Char>>>>
class basic_printf_context;
/**
\rst
The ``printf`` argument formatter.
\endrst
*/
template<typename Range>
class printf_arg_formatter : public internal::function<typename internal::arg_formatter_base<Range>::iterator>,
public internal::arg_formatter_base<Range>
{
private:
typedef typename Range::value_type char_type;
typedef decltype(internal::declval<Range>().begin()) iterator;
typedef internal::arg_formatter_base<Range> base;
typedef basic_printf_context<iterator, char_type> context_type;
context_type &context_;
void write_null_pointer(char)
{
this->spec().type_ = 0;
this->write("(nil)");
}
void write_null_pointer(wchar_t)
{
this->spec().type_ = 0;
this->write(L"(nil)");
}
public:
typedef typename base::format_specs format_specs;
/**
\rst
Constructs an argument formatter object.
*buffer* is a reference to the output buffer and *spec* contains format
specifier information for standard argument types.
\endrst
*/
printf_arg_formatter(internal::basic_buffer<char_type> &buffer, format_specs &spec, context_type &ctx)
: base(back_insert_range<internal::basic_buffer<char_type>>(buffer), spec)
, context_(ctx)
{
}
template<typename T>
typename std::enable_if<std::is_integral<T>::value, iterator>::type operator()(T value)
{
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
if (std::is_same<T, bool>::value)
{
format_specs &fmt_spec = this->spec();
if (fmt_spec.type_ != 's')
return base::operator()(value ? 1 : 0);
fmt_spec.type_ = 0;
this->write(value != 0);
}
else if (std::is_same<T, char_type>::value)
{
format_specs &fmt_spec = this->spec();
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
return (*this)(static_cast<int>(value));
fmt_spec.flags_ = 0;
fmt_spec.align_ = ALIGN_RIGHT;
return base::operator()(value);
}
else
{
return base::operator()(value);
}
return this->out();
}
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, iterator>::type operator()(T value)
{
return base::operator()(value);
}
/** Formats a null-terminated C string. */
iterator operator()(const char *value)
{
if (value)
base::operator()(value);
else if (this->spec().type_ == 'p')
write_null_pointer(char_type());
else
this->write("(null)");
return this->out();
}
/** Formats a null-terminated wide C string. */
iterator operator()(const wchar_t *value)
{
if (value)
base::operator()(value);
else if (this->spec().type_ == 'p')
write_null_pointer(char_type());
else
this->write(L"(null)");
return this->out();
}
iterator operator()(basic_string_view<char_type> value)
{
return base::operator()(value);
}
iterator operator()(monostate value)
{
return base::operator()(value);
}
/** Formats a pointer. */
iterator operator()(const void *value)
{
if (value)
return base::operator()(value);
this->spec().type_ = 0;
write_null_pointer(char_type());
return this->out();
}
/** Formats an argument of a custom (user-defined) type. */
iterator operator()(typename basic_format_arg<context_type>::handle handle)
{
handle.format(context_);
return this->out();
}
};
template<typename T>
struct printf_formatter
{
template<typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin())
{
return ctx.begin();
}
template<typename FormatContext>
auto format(const T &value, FormatContext &ctx) -> decltype(ctx.out())
{
internal::format_value(internal::get_container(ctx.out()), value);
return ctx.out();
}
};
/** This template formats data and writes the output to a writer. */
template<typename OutputIt, typename Char, typename ArgFormatter>
class basic_printf_context : private internal::context_base<OutputIt, basic_printf_context<OutputIt, Char, ArgFormatter>, Char>
{
public:
/** The character type for the output. */
typedef Char char_type;
template<typename T>
struct formatter_type
{
typedef printf_formatter<T> type;
};
private:
typedef internal::context_base<OutputIt, basic_printf_context, Char> base;
typedef typename base::format_arg format_arg;
typedef basic_format_specs<char_type> format_specs;
typedef internal::null_terminating_iterator<char_type> iterator;
void parse_flags(format_specs &spec, iterator &it);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
format_arg get_arg(iterator it, unsigned arg_index = (std::numeric_limits<unsigned>::max)());
// Parses argument index, flags and width and returns the argument index.
unsigned parse_header(iterator &it, format_specs &spec);
public:
/**
\rst
Constructs a ``printf_context`` object. References to the arguments and
the writer are stored in the context object so make sure they have
appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str, basic_format_args<basic_printf_context> args)
: base(out, format_str, args)
{
}
using base::advance_to;
using base::out;
using base::parse_context;
/** Formats stored arguments and writes the output to the range. */
void format();
};
template<typename OutputIt, typename Char, typename AF>
void basic_printf_context<OutputIt, Char, AF>::parse_flags(format_specs &spec, iterator &it)
{
for (;;)
{
switch (*it++)
{
case '-':
spec.align_ = ALIGN_LEFT;
break;
case '+':
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
break;
case '0':
spec.fill_ = '0';
break;
case ' ':
spec.flags_ |= SIGN_FLAG;
break;
case '#':
spec.flags_ |= HASH_FLAG;
break;
default:
--it;
return;
}
}
}
template<typename OutputIt, typename Char, typename AF>
typename basic_printf_context<OutputIt, Char, AF>::format_arg basic_printf_context<OutputIt, Char, AF>::get_arg(
iterator it, unsigned arg_index)
{
(void)it;
if (arg_index == std::numeric_limits<unsigned>::max())
return this->do_get_arg(this->parse_context().next_arg_id());
return base::get_arg(arg_index - 1);
}
template<typename OutputIt, typename Char, typename AF>
unsigned basic_printf_context<OutputIt, Char, AF>::parse_header(iterator &it, format_specs &spec)
{
unsigned arg_index = std::numeric_limits<unsigned>::max();
char_type c = *it;
if (c >= '0' && c <= '9')
{
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
internal::error_handler eh;
unsigned value = parse_nonnegative_int(it, eh);
if (*it == '$')
{ // value is an argument index
++it;
arg_index = value;
}
else
{
if (c == '0')
spec.fill_ = '0';
if (value != 0)
{
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
spec.width_ = value;
return arg_index;
}
}
}
parse_flags(spec, it);
// Parse width.
if (*it >= '0' && *it <= '9')
{
internal::error_handler eh;
spec.width_ = parse_nonnegative_int(it, eh);
}
else if (*it == '*')
{
++it;
spec.width_ = visit(internal::printf_width_handler<char_type>(spec), get_arg(it));
}
return arg_index;
}
template<typename OutputIt, typename Char, typename AF>
void basic_printf_context<OutputIt, Char, AF>::format()
{
auto &buffer = internal::get_container(this->out());
auto start = iterator(this->parse_context());
auto it = start;
using internal::pointer_from;
while (*it)
{
char_type c = *it++;
if (c != '%')
continue;
if (*it == c)
{
buffer.append(pointer_from(start), pointer_from(it));
start = ++it;
continue;
}
buffer.append(pointer_from(start), pointer_from(it) - 1);
format_specs spec;
spec.align_ = ALIGN_RIGHT;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(it, spec);
// Parse precision.
if (*it == '.')
{
++it;
if ('0' <= *it && *it <= '9')
{
internal::error_handler eh;
spec.precision_ = static_cast<int>(parse_nonnegative_int(it, eh));
}
else if (*it == '*')
{
++it;
spec.precision_ = visit(internal::printf_precision_handler(), get_arg(it));
}
else
{
spec.precision_ = 0;
}
}
format_arg arg = get_arg(it, arg_index);
if (spec.flag(HASH_FLAG) && visit(internal::is_zero_int(), arg))
spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG);
if (spec.fill_ == '0')
{
if (arg.is_arithmetic())
spec.align_ = ALIGN_NUMERIC;
else
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
}
// Parse length and convert the argument to the required type.
using internal::convert_arg;
switch (*it++)
{
case 'h':
if (*it == 'h')
convert_arg<signed char>(arg, *++it);
else
convert_arg<short>(arg, *it);
break;
case 'l':
if (*it == 'l')
convert_arg<long long>(arg, *++it);
else
convert_arg<long>(arg, *it);
break;
case 'j':
convert_arg<intmax_t>(arg, *it);
break;
case 'z':
convert_arg<std::size_t>(arg, *it);
break;
case 't':
convert_arg<std::ptrdiff_t>(arg, *it);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--it;
convert_arg<void>(arg, *it);
}
// Parse type.
if (!*it)
FMT_THROW(format_error("invalid format string"));
spec.type_ = static_cast<char>(*it++);
if (arg.is_integral())
{
// Normalize type.
switch (spec.type_)
{
case 'i':
case 'u':
spec.type_ = 'd';
break;
case 'c':
// TODO: handle wchar_t better?
visit(internal::char_converter<basic_printf_context>(arg), arg);
break;
}
}
start = it;
// Format argument.
visit(AF(buffer, spec, *this), arg);
}
buffer.append(pointer_from(start), pointer_from(it));
}
template<typename Char, typename Context>
void printf(internal::basic_buffer<Char> &buf, basic_string_view<Char> format, basic_format_args<Context> args)
{
Context(std::back_inserter(buf), format, args).format();
}
template<typename Buffer>
struct printf_context
{
typedef basic_printf_context<std::back_insert_iterator<Buffer>, typename Buffer::value_type> type;
};
template<typename... Args>
inline format_arg_store<printf_context<internal::buffer>::type, Args...> make_printf_args(const Args &... args)
{
return format_arg_store<printf_context<internal::buffer>::type, Args...>(args...);
}
typedef basic_format_args<printf_context<internal::buffer>::type> printf_args;
typedef basic_format_args<printf_context<internal::wbuffer>::type> wprintf_args;
inline std::string vsprintf(string_view format, printf_args args)
{
memory_buffer buffer;
printf(buffer, format, args);
return to_string(buffer);
}
/**
\rst
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
template<typename... Args>
inline std::string sprintf(string_view format_str, const Args &... args)
{
return vsprintf(format_str, make_format_args<typename printf_context<internal::buffer>::type>(args...));
}
inline std::wstring vsprintf(wstring_view format, wprintf_args args)
{
wmemory_buffer buffer;
printf(buffer, format, args);
return to_string(buffer);
}
template<typename... Args>
inline std::wstring sprintf(wstring_view format_str, const Args &... args)
{
return vsprintf(format_str, make_format_args<typename printf_context<internal::wbuffer>::type>(args...));
}
template<typename Char>
inline int vfprintf(
std::FILE *f, basic_string_view<Char> format, basic_format_args<typename printf_context<internal::basic_buffer<Char>>::type> args)
{
basic_memory_buffer<Char> buffer;
printf(buffer, format, args);
std::size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size ? -1 : static_cast<int>(size);
}
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
template<typename... Args>
inline int fprintf(std::FILE *f, string_view format_str, const Args &... args)
{
auto vargs = make_format_args<typename printf_context<internal::buffer>::type>(args...);
return vfprintf<char>(f, format_str, vargs);
}
template<typename... Args>
inline int fprintf(std::FILE *f, wstring_view format_str, const Args &... args)
{
return vfprintf(f, format_str, make_format_args<typename printf_context<internal::wbuffer>::type>(args...));
}
inline int vprintf(string_view format, printf_args args)
{
return vfprintf(stdout, format, args);
}
inline int vprintf(wstring_view format, wprintf_args args)
{
return vfprintf(stdout, format, args);
}
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
template<typename... Args>
inline int printf(string_view format_str, const Args &... args)
{
return vprintf(format_str, make_format_args<typename printf_context<internal::buffer>::type>(args...));
}
template<typename... Args>
inline int printf(wstring_view format_str, const Args &... args)
{
return vprintf(format_str, make_format_args<typename printf_context<internal::wbuffer>::type>(args...));
}
inline int vfprintf(std::ostream &os, string_view format_str, printf_args args)
{
memory_buffer buffer;
printf(buffer, format_str, args);
internal::write(os, buffer);
return static_cast<int>(buffer.size());
}
inline int vfprintf(std::wostream &os, wstring_view format_str, wprintf_args args)
{
wmemory_buffer buffer;
printf(buffer, format_str, args);
internal::write(os, buffer);
return static_cast<int>(buffer.size());
}
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fmt::fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
template<typename... Args>
inline int fprintf(std::ostream &os, string_view format_str, const Args &... args)
{
auto vargs = make_format_args<typename printf_context<internal::buffer>::type>(args...);
return vfprintf(os, format_str, vargs);
}
template<typename... Args>
inline int fprintf(std::wostream &os, wstring_view format_str, const Args &... args)
{
auto vargs = make_format_args<typename printf_context<internal::buffer>::type>(args...);
return vfprintf(os, format_str, vargs);
}
FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_

View File

@@ -0,0 +1,344 @@
// Formatting library for C++ - the core API
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
//
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
// All Rights Reserved
// {fmt} support for ranges, containers and types tuple interface.
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
#include "format.h"
#include <type_traits>
// output only up to N items from the range.
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
#define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
#endif
FMT_BEGIN_NAMESPACE
template<typename Char>
struct formatting_base
{
template<typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
{
return ctx.begin();
}
};
template<typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char>
{
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.
Char prefix;
Char delimiter;
Char postfix;
formatting_range()
: prefix('{')
, delimiter(',')
, postfix('}')
{
}
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};
template<typename Char, typename Enable = void>
struct formatting_tuple : formatting_base<Char>
{
Char prefix;
Char delimiter;
Char postfix;
formatting_tuple()
: prefix('(')
, delimiter(',')
, postfix(')')
{
}
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};
namespace internal {
template<typename RangeT, typename OutputIterator>
void copy(const RangeT &range, OutputIterator out)
{
for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it;
}
template<typename OutputIterator>
void copy(const char *str, OutputIterator out)
{
const char *p_curr = str;
while (*p_curr)
{
*out++ = *p_curr++;
}
}
template<typename OutputIterator>
void copy(char ch, OutputIterator out)
{
*out++ = ch;
}
/// Return true value if T has std::string interface, like std::string_view.
template<typename T>
class is_like_std_string
{
template<typename U>
static auto check(U *p) -> decltype(p->find('a'), p->length(), p->data(), int());
template<typename>
static void check(...);
public:
static FMT_CONSTEXPR_DECL const bool value = !std::is_void<decltype(check<T>(FMT_NULL))>::value;
};
template<typename... Ts>
struct conditional_helper
{
};
template<typename T, typename _ = void>
struct is_range_ : std::false_type
{
};
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
template<typename T>
struct is_range_<T, typename std::conditional<false,
conditional_helper<decltype(internal::declval<T>().begin()), decltype(internal::declval<T>().end())>, void>::type>
: std::true_type
{
};
#endif
/// tuple_size and tuple_element check.
template<typename T>
class is_tuple_like_
{
template<typename U>
static auto check(U *p) -> decltype(std::tuple_size<U>::value, internal::declval<typename std::tuple_element<0, U>::type>(), int());
template<typename>
static void check(...);
public:
static FMT_CONSTEXPR_DECL const bool value = !std::is_void<decltype(check<T>(FMT_NULL))>::value;
};
// Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
template<typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>;
template<std::size_t... N>
using index_sequence = std::index_sequence<N...>;
template<std::size_t N>
using make_index_sequence = std::make_index_sequence<N>;
#else
template<typename T, T... N>
struct integer_sequence
{
typedef T value_type;
static FMT_CONSTEXPR std::size_t size()
{
return sizeof...(N);
}
};
template<std::size_t... N>
using index_sequence = integer_sequence<std::size_t, N...>;
template<typename T, std::size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...>
{
};
template<typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...>
{
};
template<std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;
#endif
template<class Tuple, class F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT
{
using std::get;
// using free function get<I>(T) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
(void)_; // blocks warnings
}
template<class T>
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(T const &)
{
return {};
}
template<class Tuple, class F>
void for_each(Tuple &&tup, F &&f)
{
const auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
}
template<typename Arg>
FMT_CONSTEXPR const char *format_str_quoted(
bool add_space, const Arg &, typename std::enable_if<!is_like_std_string<typename std::decay<Arg>::type>::value>::type * = nullptr)
{
return add_space ? " {}" : "{}";
}
template<typename Arg>
FMT_CONSTEXPR const char *format_str_quoted(
bool add_space, const Arg &, typename std::enable_if<is_like_std_string<typename std::decay<Arg>::type>::value>::type * = nullptr)
{
return add_space ? " \"{}\"" : "\"{}\"";
}
FMT_CONSTEXPR const char *format_str_quoted(bool add_space, const char *)
{
return add_space ? " \"{}\"" : "\"{}\"";
}
FMT_CONSTEXPR const wchar_t *format_str_quoted(bool add_space, const wchar_t *)
{
return add_space ? L" \"{}\"" : L"\"{}\"";
}
FMT_CONSTEXPR const char *format_str_quoted(bool add_space, const char)
{
return add_space ? " '{}'" : "'{}'";
}
FMT_CONSTEXPR const wchar_t *format_str_quoted(bool add_space, const wchar_t)
{
return add_space ? L" '{}'" : L"'{}'";
}
} // namespace internal
template<typename T>
struct is_tuple_like
{
static FMT_CONSTEXPR_DECL const bool value = internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
};
template<typename TupleT, typename Char>
struct formatter<TupleT, Char, typename std::enable_if<fmt::is_tuple_like<TupleT>::value>::type>
{
private:
// C++11 generic lambda for format()
template<typename FormatContext>
struct format_each
{
template<typename T>
void operator()(const T &v)
{
if (i > 0)
{
if (formatting.add_prepostfix_space)
{
*out++ = ' ';
}
internal::copy(formatting.delimiter, out);
}
format_to(out, internal::format_str_quoted((formatting.add_delimiter_spaces && i > 0), v), v);
++i;
}
formatting_tuple<Char> &formatting;
std::size_t &i;
typename std::add_lvalue_reference<decltype(std::declval<FormatContext>().out())>::type out;
};
public:
formatting_tuple<Char> formatting;
template<typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
{
return formatting.parse(ctx);
}
template<typename FormatContext = format_context>
auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out())
{
auto out = ctx.out();
std::size_t i = 0;
internal::copy(formatting.prefix, out);
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
if (formatting.add_prepostfix_space)
{
*out++ = ' ';
}
internal::copy(formatting.postfix, out);
return ctx.out();
}
};
template<typename T>
struct is_range
{
static FMT_CONSTEXPR_DECL const bool value = internal::is_range_<T>::value && !internal::is_like_std_string<T>::value;
};
template<typename RangeT, typename Char>
struct formatter<RangeT, Char, typename std::enable_if<fmt::is_range<RangeT>::value>::type>
{
formatting_range<Char> formatting;
template<typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin())
{
return formatting.parse(ctx);
}
template<typename FormatContext>
typename FormatContext::iterator format(const RangeT &values, FormatContext &ctx)
{
auto out = ctx.out();
internal::copy(formatting.prefix, out);
std::size_t i = 0;
for (auto it = values.begin(), end = values.end(); it != end; ++it)
{
if (i > 0)
{
if (formatting.add_prepostfix_space)
{
*out++ = ' ';
}
internal::copy(formatting.delimiter, out);
}
format_to(out, internal::format_str_quoted((formatting.add_delimiter_spaces && i > 0), *it), *it);
if (++i > formatting.range_length_limit)
{
format_to(out, " ... <other elements>");
break;
}
}
if (formatting.add_prepostfix_space)
{
*out++ = ' ';
}
internal::copy(formatting.postfix, out);
return ctx.out();
}
};
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_

View File

@@ -1,11 +1,9 @@
/* // Formatting library for C++ - time formatting
Formatting library for C++ - time formatting //
// Copyright (c) 2012 - 2016, Victor Zverovich
Copyright (c) 2012 - 2016, Victor Zverovich // All rights reserved.
All rights reserved. //
// For the license information refer to format.h.
For the license information refer to format.h.
*/
#ifndef FMT_TIME_H_ #ifndef FMT_TIME_H_
#define FMT_TIME_H_ #define FMT_TIME_H_
@@ -13,36 +11,175 @@
#include "format.h" #include "format.h"
#include <ctime> #include <ctime>
#ifdef _MSC_VER FMT_BEGIN_NAMESPACE
# pragma warning(push)
# pragma warning(disable: 4702) // unreachable code
# pragma warning(disable: 4996) // "deprecated" functions
#endif
namespace fmt { namespace internal {
template <typename ArgFormatter> inline null<> localtime_r(...)
void format_arg(BasicFormatter<char, ArgFormatter> &f, {
const char *&format_str, const std::tm &tm) { return null<>();
if (*format_str == ':') }
++format_str; inline null<> localtime_s(...)
const char *end = format_str; {
return null<>();
}
inline null<> gmtime_r(...)
{
return null<>();
}
inline null<> gmtime_s(...)
{
return null<>();
}
} // namespace internal
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time)
{
struct dispatcher
{
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t)
: time_(t)
{
}
bool run()
{
using namespace fmt::internal;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm *tm)
{
return tm != FMT_NULL;
}
bool handle(internal::null<>)
{
using namespace fmt::internal;
return fallback(localtime_s(&tm_, &time_));
}
bool fallback(int res)
{
return res == 0;
}
bool fallback(internal::null<>)
{
using namespace fmt::internal;
std::tm *tm = std::localtime(&time_);
if (tm)
tm_ = *tm;
return tm != FMT_NULL;
}
};
dispatcher lt(time);
if (lt.run())
return lt.tm_;
// Too big time values may be unsupported.
FMT_THROW(format_error("time_t value out of range"));
}
// Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time)
{
struct dispatcher
{
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t)
: time_(t)
{
}
bool run()
{
using namespace fmt::internal;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm *tm)
{
return tm != FMT_NULL;
}
bool handle(internal::null<>)
{
using namespace fmt::internal;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res)
{
return res == 0;
}
bool fallback(internal::null<>)
{
std::tm *tm = std::gmtime(&time_);
if (tm)
tm_ = *tm;
return tm != FMT_NULL;
}
};
dispatcher gt(time);
if (gt.run())
return gt.tm_;
// Too big time values may be unsupported.
FMT_THROW(format_error("time_t value out of range"));
}
namespace internal {
inline std::size_t strftime(char *str, std::size_t count, const char *format, const std::tm *time)
{
return std::strftime(str, count, format, time);
}
inline std::size_t strftime(wchar_t *str, std::size_t count, const wchar_t *format, const std::tm *time)
{
return std::wcsftime(str, count, format, time);
}
} // namespace internal
template<typename Char>
struct formatter<std::tm, Char>
{
template<typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin())
{
auto it = internal::null_terminating_iterator<Char>(ctx);
if (*it == ':')
++it;
auto end = it;
while (*end && *end != '}') while (*end && *end != '}')
++end; ++end;
if (*end != '}') tm_format.reserve(end - it + 1);
FMT_THROW(FormatError("missing '}' in format string")); using internal::pointer_from;
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format; tm_format.append(pointer_from(it), pointer_from(end));
format.append(format_str, end + 1); tm_format.push_back('\0');
format[format.size() - 1] = '\0'; return pointer_from(end);
Buffer<char> &buffer = f.writer().buffer(); }
std::size_t start = buffer.size();
for (;;) { template<typename FormatContext>
std::size_t size = buffer.capacity() - start; auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out())
std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm); {
if (count != 0) { internal::basic_buffer<Char> &buf = internal::get_container(ctx.out());
buffer.resize(start + count); 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; break;
} }
if (size >= format.size() * 256) { if (size >= tm_format.size() * 256)
{
// If the buffer is 256 times larger than the format string, assume // 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 // that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases: // better way to distinguish the two cases:
@@ -50,94 +187,13 @@ void format_arg(BasicFormatter<char, ArgFormatter> &f,
break; break;
} }
const std::size_t MIN_GROWTH = 10; const std::size_t MIN_GROWTH = 10;
buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH)); buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
} }
format_str = end + 1; return ctx.out();
} }
namespace internal{ basic_memory_buffer<Char> tm_format;
inline Null<> localtime_r(...) { return Null<>(); }
inline Null<> localtime_s(...) { return Null<>(); }
inline Null<> gmtime_r(...) { return Null<>(); }
inline Null<> gmtime_s(...) { return Null<>(); }
}
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) {
struct LocalTime {
std::time_t time_;
std::tm tm_;
LocalTime(std::time_t t): time_(t) {}
bool run() {
using namespace fmt::internal;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm *tm) { return tm != FMT_NULL; }
bool handle(internal::Null<>) {
using namespace fmt::internal;
return fallback(localtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
bool fallback(internal::Null<>) {
using namespace fmt::internal;
std::tm *tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != FMT_NULL;
}
}; };
LocalTime lt(time); FMT_END_NAMESPACE
if (lt.run())
return lt.tm_;
// Too big time values may be unsupported.
FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm();
}
// Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time) {
struct GMTime {
std::time_t time_;
std::tm tm_;
GMTime(std::time_t t): time_(t) {}
bool run() {
using namespace fmt::internal;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm *tm) { return tm != FMT_NULL; }
bool handle(internal::Null<>) {
using namespace fmt::internal;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
bool fallback(internal::Null<>) {
std::tm *tm = std::gmtime(&time_);
if (tm != FMT_NULL) tm_ = *tm;
return tm != FMT_NULL;
}
};
GMTime gt(time);
if (gt.run())
return gt.tm_;
// Too big time values may be unsupported.
FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm();
}
} //namespace fmt
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#endif // FMT_TIME_H_ #endif // FMT_TIME_H_

View File

@@ -1,5 +1,5 @@
// //
// Copyright(c) 2016 Gabi Melman. // Copyright(c) 2016-2018 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
@@ -11,18 +11,15 @@
// //
#if !defined(SPDLOG_FMT_EXTERNAL) #if !defined(SPDLOG_FMT_EXTERNAL)
#ifndef FMT_HEADER_ONLY #ifndef FMT_HEADER_ONLY
#define FMT_HEADER_ONLY #define FMT_HEADER_ONLY
#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 "spdlog/fmt/bundled/format.h" #include "bundled/core.h"
#include "bundled/format.h"
#else // external fmtlib #else // external fmtlib
#include <fmt/core.h>
#include <fmt/format.h> #include <fmt/format.h>
#endif #endif

View File

@@ -4,14 +4,15 @@
// //
#pragma once #pragma once
//
// include external or bundled copy of fmtlib's ostream support // include bundled or external copy of fmtlib's ostream support
// //
#if !defined(SPDLOG_FMT_EXTERNAL) #if !defined(SPDLOG_FMT_EXTERNAL)
#include "spdlog/fmt/fmt.h" #ifndef FMT_HEADER_ONLY
#include "spdlog/fmt/bundled/ostream.h" #define FMT_HEADER_ONLY
#endif
#include "bundled/ostream.h"
#include "fmt.h"
#else #else
#include <fmt/ostream.h> #include <fmt/ostream.h>
#endif #endif

View File

@@ -5,43 +5,16 @@
#pragma once #pragma once
#include "fmt/fmt.h"
#include "spdlog/details/log_msg.h" #include "spdlog/details/log_msg.h"
#include <vector> namespace spdlog {
#include <string>
#include <memory>
namespace spdlog
{
namespace details
{
class flag_formatter;
}
class formatter class formatter
{ {
public: public:
virtual ~formatter() {} virtual ~formatter() = default;
virtual void format(details::log_msg& msg) = 0; virtual void format(const details::log_msg &msg, fmt::memory_buffer &dest) = 0;
virtual std::unique_ptr<formatter> clone() const = 0;
}; };
} // namespace spdlog
class pattern_formatter SPDLOG_FINAL : public formatter
{
public:
explicit pattern_formatter(const std::string& pattern, pattern_time_type pattern_time = pattern_time_type::local);
pattern_formatter(const pattern_formatter&) = delete;
pattern_formatter& operator=(const pattern_formatter&) = delete;
void format(details::log_msg& msg) override;
private:
const std::string _pattern;
const pattern_time_type _pattern_time;
std::vector<std::unique_ptr<details::flag_formatter>> _formatters;
std::tm get_time(details::log_msg& msg);
void handle_flag(char flag);
void compile_pattern(const std::string& pattern);
};
}
#include "spdlog/details/pattern_formatter_impl.h"

View File

@@ -1,132 +1,162 @@
// //
// Copyright(c) 2015 Gabi Melman. // 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
// Thread safe logger (except for set_pattern(..), set_formatter(..) and set_error_handler()) // Thread safe logger (except for set_pattern(..), set_formatter(..) and
// set_error_handler())
// Has name, log level, vector of std::shared sink pointers and formatter // Has name, log level, vector of std::shared sink pointers and formatter
// 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 and if yes:
// 2. Format the message using the formatter function // 2. Call the underlying sinks to do the job.
// 3. Pass the formatted message to its sinks to performa the actual logging // 3. Each sink use its own private copy of a formatter to format the message
// and send to its destination.
//
// The use of private formatter per sink provides the opportunity to cache some
// formatted data,
// and support customize format per each sink.
#include "spdlog/sinks/base_sink.h"
#include "spdlog/common.h" #include "spdlog/common.h"
#include "spdlog/formatter.h"
#include "spdlog/sinks/sink.h"
#include <vector>
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector>
namespace spdlog namespace spdlog {
{
class logger class logger
{ {
public: public:
logger(const std::string& logger_name, sink_ptr single_sink); logger(std::string name, sink_ptr single_sink);
logger(const std::string& name, sinks_init_list); logger(std::string name, sinks_init_list sinks);
template<class It>
logger(const std::string& name, const It& begin, const It& end); template<typename It>
logger(std::string name, const It &begin, const It &end);
virtual ~logger(); virtual ~logger();
logger(const logger &) = delete; logger(const logger &) = delete;
logger &operator=(const logger &) = delete; logger &operator=(const logger &) = delete;
template<typename... Args>
void log(level::level_enum lvl, const char *fmt, const Args &... args);
template <typename... Args> void log(level::level_enum lvl, const char* fmt, const Args&... args); template<typename... Args>
template <typename... Args> void log(level::level_enum lvl, const char* msg); void log(level::level_enum lvl, const char *msg);
template <typename Arg1, typename... Args> void trace(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void debug(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void info(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void warn(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void error(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void critical(const char* fmt, const Arg1&, const Args&... args);
template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const char* fmt, const Args&... args); template<typename... Args>
template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const char* msg); void trace(const char *fmt, const Args &... args);
template <typename Arg1, typename... Args> void trace_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void debug_if(const bool flag, const char* fmt, const Arg1&, const Args&... args); template<typename... Args>
template <typename Arg1, typename... Args> void info_if(const bool flag, const char* fmt, const Arg1&, const Args&... args); void debug(const char *fmt, const Args &... args);
template <typename Arg1, typename... Args> void warn_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void error_if(const bool flag, const char* fmt, const Arg1&, const Args&... args); template<typename... Args>
template <typename Arg1, typename... Args> void critical_if(const bool flag, const char* fmt, const Arg1&, const Args&... args); void info(const char *fmt, const Args &... args);
template<typename... Args>
void warn(const char *fmt, const Args &... args);
template<typename... Args>
void error(const char *fmt, const Args &... args);
template<typename... Args>
void critical(const char *fmt, const Args &... args);
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
template <typename... Args> void log(level::level_enum lvl, const wchar_t* msg); template<typename... Args>
template <typename... Args> void log(level::level_enum lvl, const wchar_t* fmt, const Args&... args); void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args);
template <typename... Args> void trace(const wchar_t* fmt, const Args&... args);
template <typename... Args> void debug(const wchar_t* fmt, const Args&... args);
template <typename... Args> void info(const wchar_t* fmt, const Args&... args);
template <typename... Args> void warn(const wchar_t* fmt, const Args&... args);
template <typename... Args> void error(const wchar_t* fmt, const Args&... args);
template <typename... Args> void critical(const wchar_t* fmt, const Args&... args);
template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const wchar_t* msg); template<typename... Args>
template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const wchar_t* fmt, const Args&... args); void trace(const wchar_t *fmt, const Args &... args);
template <typename... Args> void trace_if(const bool flag, const wchar_t* fmt, const Args&... args);
template <typename... Args> void debug_if(const bool flag, const wchar_t* fmt, const Args&... args); template<typename... Args>
template <typename... Args> void info_if(const bool flag, const wchar_t* fmt, const Args&... args); void debug(const wchar_t *fmt, const Args &... args);
template <typename... Args> void warn_if(const bool flag, const wchar_t* fmt, const Args&... args);
template <typename... Args> void error_if(const bool flag, const wchar_t* fmt, const Args&... args); template<typename... Args>
template <typename... Args> void critical_if(const bool flag, const wchar_t* fmt, const Args&... args); void info(const wchar_t *fmt, const Args &... args);
template<typename... Args>
void warn(const wchar_t *fmt, const Args &... args);
template<typename... Args>
void error(const wchar_t *fmt, const Args &... args);
template<typename... Args>
void critical(const wchar_t *fmt, const Args &... args);
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT #endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
template <typename T> void log(level::level_enum lvl, const T&); template<typename T>
template <typename T> void trace(const T&); void log(level::level_enum lvl, const T &);
template <typename T> void debug(const T&);
template <typename T> void info(const T&);
template <typename T> void warn(const T&);
template <typename T> void error(const T&);
template <typename T> void critical(const T&);
template <typename T> void log_if(const bool flag, level::level_enum lvl, const T&); template<typename T>
template <typename T> void trace_if(const bool flag, const T&); void trace(const T &msg);
template <typename T> void debug_if(const bool flag, const T&);
template <typename T> void info_if(const bool flag, const T&);
template <typename T> void warn_if(const bool flag, const T&);
template <typename T> void error_if(const bool flag, const T&);
template <typename T> void critical_if(const bool flag, const T&);
bool should_log(level::level_enum) const; template<typename T>
void set_level(level::level_enum); 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;
void set_level(level::level_enum log_level);
level::level_enum level() const; level::level_enum level() const;
const std::string &name() const; const std::string &name() const;
void set_pattern(const std::string&, pattern_time_type = pattern_time_type::local);
void set_formatter(formatter_ptr);
// automatically call flush() if message level >= log_level // set formatting for the sinks in this logger.
// each sink will get a seperate instance of the formatter object.
void set_formatter(std::unique_ptr<formatter> formatter);
void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);
void flush();
void flush_on(level::level_enum log_level); void flush_on(level::level_enum log_level);
virtual void flush();
const std::vector<sink_ptr> &sinks() const; const std::vector<sink_ptr> &sinks() const;
// error handler std::vector<sink_ptr> &sinks();
virtual void set_error_handler(log_err_handler);
virtual log_err_handler error_handler(); void set_error_handler(log_err_handler err_handler);
log_err_handler error_handler();
protected: protected:
virtual void _sink_it(details::log_msg&); virtual void sink_it_(details::log_msg &msg);
virtual void _set_pattern(const std::string&, pattern_time_type); virtual void flush_();
virtual void _set_formatter(formatter_ptr);
// default error handler: print the error to stderr with the max rate of 1 message/minute bool should_flush_(const details::log_msg &msg);
virtual void _default_err_handler(const std::string &msg);
// return true if the given message level should trigger a flush // default error handler: print the error to stderr with the max rate of 1
bool _should_flush_on(const details::log_msg&); // message/minute
void default_err_handler_(const std::string &msg);
const std::string _name; // increment the message count (only if
std::vector<sink_ptr> _sinks; // defined(SPDLOG_ENABLE_MESSAGE_COUNTER))
formatter_ptr _formatter; void incr_msg_counter_(details::log_msg &msg);
spdlog::level_t _level;
spdlog::level_t _flush_level; const std::string name_;
log_err_handler _err_handler; std::vector<sink_ptr> sinks_;
std::atomic<time_t> _last_err_time; spdlog::level_t level_;
std::atomic<size_t> _msg_counter; spdlog::level_t flush_level_;
log_err_handler err_handler_;
std::atomic<time_t> last_err_time_;
std::atomic<size_t> msg_counter_;
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
std::wstring_convert<std::codecvt_utf8<wchar_t>> wstring_converter_;
std::mutex wstring_converter_mutex_;
#endif
}; };
} } // namespace spdlog
#include "spdlog/details/logger_impl.h" #include "details/logger_impl.h"

View File

@@ -5,46 +5,60 @@
#pragma once #pragma once
#if defined(__ANDROID__) #include "spdlog/details/fmt_helper.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/sinks/sink.h" #include "spdlog/details/os.h"
#include "spdlog/sinks/base_sink.h"
#include <android/log.h>
#include <chrono>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <android/log.h>
#include <thread> #include <thread>
#include <chrono>
#if !defined(SPDLOG_ANDROID_RETRIES) #if !defined(SPDLOG_ANDROID_RETRIES)
#define SPDLOG_ANDROID_RETRIES 2 #define SPDLOG_ANDROID_RETRIES 2
#endif #endif
namespace spdlog namespace spdlog {
{ namespace sinks {
namespace sinks
{
/* /*
* Android sink (logging using __android_log_write) * Android sink (logging using __android_log_write)
* __android_log_write is thread-safe. No lock is needed.
*/ */
class android_sink : public sink template<typename Mutex>
class android_sink SPDLOG_FINAL : public base_sink<Mutex>
{ {
public: public:
explicit android_sink(const std::string& tag = "spdlog", bool use_raw_msg = false): _tag(tag), _use_raw_msg(use_raw_msg) {} explicit android_sink(const std::string &tag = "spdlog", bool use_raw_msg = false)
: tag_(tag)
void log(const details::log_msg& msg) override , use_raw_msg_(use_raw_msg)
{ {
const android_LogPriority priority = convert_to_android(msg.level); }
const char *msg_output = (_use_raw_msg ? msg.raw.c_str() : msg.formatted.c_str());
protected:
void sink_it_(const details::log_msg &msg) override
{
const android_LogPriority priority = convert_to_android_(msg.level);
fmt::memory_buffer formatted;
if (use_raw_msg_)
{
fmt_helper::append_buf(msg.raw, formatted);
}
else
{
formatter_->format(msg, formatted);
}
formatted.push_back('\0');
const char *msg_output = formatted.data();
// See system/core/liblog/logger_write.c for explanation of return value // See system/core/liblog/logger_write.c for explanation of return value
int ret = __android_log_write(priority, _tag.c_str(), msg_output); int ret = __android_log_write(priority, tag_.c_str(), msg_output);
int retry_count = 0; int retry_count = 0;
while ((ret == -11 /*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES)) while ((ret == -11 /*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES))
{ {
std::this_thread::sleep_for(std::chrono::milliseconds(5)); details::os::sleep_for_millis(5);
ret = __android_log_write(priority, _tag.c_str(), msg_output); ret = __android_log_write(priority, tag_.c_str(), msg_output);
retry_count++; retry_count++;
} }
@@ -54,12 +68,10 @@ public:
} }
} }
void flush() override void flush_() override {}
{
}
private: private:
static android_LogPriority convert_to_android(spdlog::level::level_enum level) static android_LogPriority convert_to_android_(spdlog::level::level_enum level)
{ {
switch (level) switch (level)
{ {
@@ -80,11 +92,26 @@ private:
} }
} }
std::string _tag; std::string tag_;
bool _use_raw_msg; bool use_raw_msg_;
}; };
} using android_sink_mt = android_sink<std::mutex>;
using android_sink_st = android_sink<details::null_mutex>;
} // namespace sinks
// Create and register android syslog logger
template<typename Factory = default_factory>
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);
} }
#endif template<typename Factory = default_factory>
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);
}
} // namespace spdlog

View File

@@ -5,60 +5,67 @@
#pragma once #pragma once
#include "spdlog/sinks/base_sink.h" #include "spdlog/details/console_globals.h"
#include "spdlog/common.h" #include "spdlog/details/null_mutex.h"
#include "spdlog/details/os.h" #include "spdlog/details/os.h"
#include <memory>
#include <mutex>
#include <string> #include <string>
#include <map> #include <unordered_map>
namespace spdlog namespace spdlog {
{ namespace sinks {
namespace sinks
{
/** /**
* This sink prefixes the output with an ANSI escape sequence color code depending on the severity * This sink prefixes the output with an ANSI escape sequence color code
* depending on the severity
* 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 <class Mutex> template<typename TargetStream, class ConsoleMutex>
class ansicolor_sink: public base_sink<Mutex> class ansicolor_sink : public sink
{ {
public: public:
ansicolor_sink(FILE* file): target_file_(file) using mutex_t = typename ConsoleMutex::mutex_t;
ansicolor_sink()
: target_file_(TargetStream::stream())
, mutex_(ConsoleMutex::mutex())
{ {
should_do_colors_ = details::os::in_terminal(file) && details::os::is_color_terminal(); should_do_colors_ = details::os::in_terminal(target_file_) && details::os::is_color_terminal();
colors_[level::trace] = cyan; colors_[level::trace] = white;
colors_[level::debug] = cyan; colors_[level::debug] = cyan;
colors_[level::info] = reset; colors_[level::info] = green;
colors_[level::warn] = yellow + bold; colors_[level::warn] = yellow + bold;
colors_[level::err] = red + bold; colors_[level::err] = red + bold;
colors_[level::critical] = bold + on_red; colors_[level::critical] = bold + on_red;
colors_[level::off] = reset; colors_[level::off] = reset;
} }
virtual ~ansicolor_sink()
{ ~ansicolor_sink() override = default;
_flush();
} ansicolor_sink(const ansicolor_sink &other) = delete;
ansicolor_sink &operator=(const ansicolor_sink &other) = delete;
void set_color(level::level_enum color_level, const std::string &color) void set_color(level::level_enum color_level, const std::string &color)
{ {
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex); std::lock_guard<mutex_t> lock(mutex_);
colors_[color_level] = color; colors_[color_level] = color;
} }
/// Formatting codes /// Formatting codes
const std::string reset = "\033[00m"; const std::string reset = "\033[m";
const std::string bold = "\033[1m"; const std::string bold = "\033[1m";
const std::string dark = "\033[2m"; const std::string dark = "\033[2m";
const std::string underline = "\033[4m"; const std::string underline = "\033[4m";
const std::string blink = "\033[5m"; const std::string blink = "\033[5m";
const std::string reverse = "\033[7m"; const std::string reverse = "\033[7m";
const std::string concealed = "\033[8m"; const std::string concealed = "\033[8m";
const std::string clear_line = "\033[K";
// Foreground colors // Foreground colors
const std::string grey = "\033[30m"; const std::string black = "\033[30m";
const std::string red = "\033[31m"; const std::string red = "\033[31m";
const std::string green = "\033[32m"; const std::string green = "\033[32m";
const std::string yellow = "\033[33m"; const std::string yellow = "\033[33m";
@@ -68,7 +75,7 @@ public:
const std::string white = "\033[37m"; const std::string white = "\033[37m";
/// Background colors /// Background colors
const std::string on_grey = "\033[40m"; const std::string on_black = "\033[40m";
const std::string on_red = "\033[41m"; const std::string on_red = "\033[41m";
const std::string on_green = "\033[42m"; const std::string on_green = "\033[42m";
const std::string on_yellow = "\033[43m"; const std::string on_yellow = "\033[43m";
@@ -77,57 +84,73 @@ public:
const std::string on_cyan = "\033[46m"; const std::string on_cyan = "\033[46m";
const std::string on_white = "\033[47m"; const std::string on_white = "\033[47m";
protected: void log(const details::log_msg &msg) SPDLOG_FINAL override
virtual void _sink_it(const details::log_msg& msg) override
{ {
// Wrap the originally formatted message in color codes. // Wrap the originally formatted message in color codes.
// If color is not supported in the terminal, log as is instead. // If color is not supported in the terminal, log as is instead.
if (should_do_colors_) std::lock_guard<mutex_t> lock(mutex_);
{
const std::string& prefix = colors_[msg.level];
fwrite(prefix.data(), sizeof(char), prefix.size(), target_file_);
fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), target_file_);
fwrite(reset.data(), sizeof(char), reset.size(), target_file_);
}
else
{
fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), target_file_);
}
_flush();
}
void _flush() override 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_); fflush(target_file_);
} }
void flush() SPDLOG_FINAL override
{
std::lock_guard<mutex_t> lock(mutex_);
fflush(target_file_);
}
void set_pattern(const std::string &pattern) override SPDLOG_FINAL
{
std::lock_guard<mutex_t> lock(mutex_);
formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
}
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override SPDLOG_FINAL
{
std::lock_guard<mutex_t> lock(mutex_);
formatter_ = std::move(sink_formatter);
}
private:
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_;
bool should_do_colors_; bool should_do_colors_;
std::map<level::level_enum, std::string> colors_; std::unordered_map<level::level_enum, std::string, level::level_hasher> colors_;
}; };
using ansicolor_stdout_sink_mt = ansicolor_sink<details::console_stdout, details::console_mutex>;
using ansicolor_stdout_sink_st = ansicolor_sink<details::console_stdout, details::console_nullmutex>;
template<class Mutex> using ansicolor_stderr_sink_mt = ansicolor_sink<details::console_stderr, details::console_mutex>;
class ansicolor_stdout_sink: public ansicolor_sink<Mutex> using ansicolor_stderr_sink_st = ansicolor_sink<details::console_stderr, details::console_nullmutex>;
{
public:
ansicolor_stdout_sink(): ansicolor_sink<Mutex>(stdout)
{}
};
template<class Mutex>
class ansicolor_stderr_sink: public ansicolor_sink<Mutex>
{
public:
ansicolor_stderr_sink(): ansicolor_sink<Mutex>(stderr)
{}
};
typedef ansicolor_stdout_sink<std::mutex> ansicolor_stdout_sink_mt;
typedef ansicolor_stdout_sink<details::null_mutex> ansicolor_stdout_sink_st;
typedef ansicolor_stderr_sink<std::mutex> ansicolor_stderr_sink_mt;
typedef ansicolor_stderr_sink<details::null_mutex> ansicolor_stderr_sink_st;
} // namespace sinks } // namespace sinks
} // namespace spdlog
} // namespace spdlog

View File

@@ -6,45 +6,58 @@
#pragma once #pragma once
// //
// base sink templated over a mutex (either dummy or real) // base sink templated over a mutex (either dummy or real)
// concrete implementation should only override the _sink_it method. // concrete implementation should override the sink_it_() and flush_() methods.
// all locking is taken care of here so no locking needed by the implementers.. // locking is taken care of in this class - no locking needed by the
// implementers..
// //
#include "spdlog/sinks/sink.h"
#include "spdlog/formatter.h"
#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 <mutex> namespace spdlog {
namespace sinks {
namespace spdlog template<typename Mutex>
{
namespace sinks
{
template<class Mutex>
class base_sink : public sink class base_sink : public sink
{ {
public: public:
base_sink():_mutex() {} base_sink()
virtual ~base_sink() = default; : sink()
{
}
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) SPDLOG_FINAL override void log(const details::log_msg &msg) SPDLOG_FINAL override
{ {
std::lock_guard<Mutex> lock(_mutex); std::lock_guard<Mutex> lock(mutex_);
_sink_it(msg); sink_it_(msg);
} }
void flush() SPDLOG_FINAL override void flush() SPDLOG_FINAL override
{ {
_flush(); std::lock_guard<Mutex> lock(mutex_);
flush_();
}
void set_pattern(const std::string &pattern) SPDLOG_FINAL override
{
std::lock_guard<Mutex> lock(mutex_);
formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
}
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) SPDLOG_FINAL override
{
std::lock_guard<Mutex> lock(mutex_);
formatter_ = std::move(sink_formatter);
} }
protected: protected:
virtual void _sink_it(const details::log_msg& msg) = 0; virtual void sink_it_(const details::log_msg &msg) = 0;
virtual void _flush() = 0; virtual void flush_() = 0;
Mutex _mutex; Mutex mutex_;
}; };
} } // namespace sinks
} } // namespace spdlog

View File

@@ -0,0 +1,66 @@
//
// Copyright(c) 2015-2018 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include "spdlog/details/file_helper.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/sinks/base_sink.h"
#include "spdlog/spdlog.h"
#include <mutex>
#include <string>
namespace spdlog {
namespace sinks {
/*
* Trivial file sink with single file as target
*/
template<typename Mutex>
class basic_file_sink SPDLOG_FINAL : public base_sink<Mutex>
{
public:
explicit basic_file_sink(const filename_t &filename, bool truncate = false)
{
file_helper_.open(filename, truncate);
}
protected:
void sink_it_(const details::log_msg &msg) override
{
fmt::memory_buffer formatted;
sink::formatter_->format(msg, formatted);
file_helper_.write(formatted);
}
void flush_() override
{
file_helper_.flush();
}
private:
details::file_helper file_helper_;
};
using basic_file_sink_mt = basic_file_sink<std::mutex>;
using basic_file_sink_st = basic_file_sink<details::null_mutex>;
} // namespace sinks
//
// factory functions
//
template<typename Factory = default_factory>
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);
}
template<typename Factory = default_factory>
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);
}
} // namespace spdlog

View File

@@ -0,0 +1,132 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include "spdlog/details/file_helper.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/fmt/fmt.h"
#include "spdlog/sinks/base_sink.h"
#include "spdlog/spdlog.h"
#include <chrono>
#include <cstdio>
#include <ctime>
#include <mutex>
#include <string>
namespace spdlog {
namespace sinks {
/*
* Generator of daily log file names in format basename.YYYY-MM-DD.ext
*/
struct daily_filename_calculator
{
// Create filename for the form basename.YYYY-MM-DD
static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
{
filename_t basename, ext;
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w;
fmt::format_to(
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
*/
template<typename Mutex, typename FileNameCalc = daily_filename_calculator>
class daily_file_sink SPDLOG_FINAL : public base_sink<Mutex>
{
public:
// 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)
: base_filename_(std::move(base_filename))
, rotation_h_(rotation_hour)
, rotation_m_(rotation_minute)
, truncate_(truncate)
{
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
{
throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
}
auto now = log_clock::now();
file_helper_.open(FileNameCalc::calc_filename(base_filename_, now_tm(now)), truncate_);
rotation_tp_ = next_rotation_tp_();
}
protected:
void sink_it_(const details::log_msg &msg) override
{
if (msg.time >= rotation_tp_)
{
file_helper_.open(FileNameCalc::calc_filename(base_filename_, now_tm(msg.time)), truncate_);
rotation_tp_ = next_rotation_tp_();
}
fmt::memory_buffer formatted;
sink::formatter_->format(msg, formatted);
file_helper_.write(formatted);
}
void flush_() override
{
file_helper_.flush();
}
private:
tm now_tm(log_clock::time_point tp)
{
time_t tnow = log_clock::to_time_t(tp);
return spdlog::details::os::localtime(tnow);
}
log_clock::time_point next_rotation_tp_()
{
auto now = log_clock::now();
tm date = now_tm(now);
date.tm_hour = rotation_h_;
date.tm_min = rotation_m_;
date.tm_sec = 0;
auto rotation_time = log_clock::from_time_t(std::mktime(&date));
if (rotation_time > now)
{
return rotation_time;
}
return {rotation_time + std::chrono::hours(24)};
}
filename_t base_filename_;
int rotation_h_;
int rotation_m_;
log_clock::time_point rotation_tp_;
details::file_helper file_helper_;
bool truncate_;
};
using daily_file_sink_mt = daily_file_sink<std::mutex>;
using daily_file_sink_st = daily_file_sink<details::null_mutex>;
} // namespace sinks
//
// factory functions
//
template<typename Factory = default_factory>
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)
{
return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate);
}
template<typename Factory = default_factory>
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)
{
return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate);
}
} // namespace spdlog

View File

@@ -5,37 +5,46 @@
#pragma once #pragma once
#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/sinks/base_sink.h"
#include "spdlog/sinks/sink.h"
#include <algorithm> #include <algorithm>
#include <mutex>
#include <memory> #include <memory>
#include <mutex>
#include <vector> #include <vector>
// Distribution sink (mux). Stores a vector of sinks which get called when log is called // Distribution sink (mux). Stores a vector of sinks which get called when log
// is called
namespace spdlog namespace spdlog {
{ namespace sinks {
namespace sinks
{ template<typename Mutex>
template<class Mutex>
class dist_sink : public base_sink<Mutex> class dist_sink : public base_sink<Mutex>
{ {
public: public:
explicit dist_sink() :_sinks() {} dist_sink() = default;
dist_sink(const dist_sink &) = delete; dist_sink(const dist_sink &) = delete;
dist_sink &operator=(const dist_sink &) = delete; dist_sink &operator=(const dist_sink &) = delete;
virtual ~dist_sink() = default;
void add_sink(std::shared_ptr<sink> sink)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
sinks_.push_back(sink);
}
void remove_sink(std::shared_ptr<sink> sink)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
}
protected: protected:
std::vector<std::shared_ptr<sink>> _sinks; 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))
{ {
@@ -44,30 +53,16 @@ protected:
} }
} }
void _flush() override void flush_() override
{ {
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex); for (auto &sink : sinks_)
for (auto &sink : _sinks)
sink->flush(); sink->flush();
} }
std::vector<std::shared_ptr<sink>> sinks_;
public:
void add_sink(std::shared_ptr<sink> sink)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
_sinks.push_back(sink);
}
void remove_sink(std::shared_ptr<sink> sink)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
_sinks.erase(std::remove(_sinks.begin(), _sinks.end(), sink), _sinks.end());
}
}; };
typedef dist_sink<std::mutex> dist_sink_mt; using dist_sink_mt = dist_sink<std::mutex>;
typedef dist_sink<details::null_mutex> dist_sink_st; using dist_sink_st = dist_sink<details::null_mutex>;
}
} } // namespace sinks
} // namespace spdlog

View File

@@ -1,242 +0,0 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include "spdlog/sinks/base_sink.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/details/file_helper.h"
#include "spdlog/fmt/fmt.h"
#include <algorithm>
#include <chrono>
#include <cstdio>
#include <ctime>
#include <mutex>
#include <string>
#include <cerrno>
namespace spdlog
{
namespace sinks
{
/*
* Trivial file sink with single file as target
*/
template<class Mutex>
class simple_file_sink SPDLOG_FINAL : public base_sink < Mutex >
{
public:
explicit simple_file_sink(const filename_t &filename, bool truncate = false):_force_flush(false)
{
_file_helper.open(filename, truncate);
}
void set_force_flush(bool force_flush)
{
_force_flush = force_flush;
}
protected:
void _sink_it(const details::log_msg& msg) override
{
_file_helper.write(msg);
if(_force_flush)
_file_helper.flush();
}
void _flush() override
{
_file_helper.flush();
}
private:
details::file_helper _file_helper;
bool _force_flush;
};
typedef simple_file_sink<std::mutex> simple_file_sink_mt;
typedef simple_file_sink<details::null_mutex> simple_file_sink_st;
/*
* Rotating file sink based on size
*/
template<class Mutex>
class rotating_file_sink SPDLOG_FINAL : public base_sink < Mutex >
{
public:
rotating_file_sink(const filename_t &base_filename,
std::size_t max_size, std::size_t max_files) :
_base_filename(base_filename),
_max_size(max_size),
_max_files(max_files),
_current_size(0),
_file_helper()
{
_file_helper.open(calc_filename(_base_filename, 0));
_current_size = _file_helper.size(); //expensive. called only once
}
protected:
void _sink_it(const details::log_msg& msg) override
{
_current_size += msg.formatted.size();
if (_current_size > _max_size)
{
_rotate();
_current_size = msg.formatted.size();
}
_file_helper.write(msg);
}
void _flush() override
{
_file_helper.flush();
}
private:
static filename_t calc_filename(const filename_t& filename, std::size_t index)
{
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
if (index)
w.write(SPDLOG_FILENAME_T("{}.{}"), filename, index);
else
w.write(SPDLOG_FILENAME_T("{}"), filename);
return w.str();
}
// Rotate files:
// log.txt -> log.txt.1
// log.txt.1 -> log.txt.2
// log.txt.2 -> log.txt.3
// lo3.txt.3 -> delete
void _rotate()
{
using details::os::filename_to_str;
_file_helper.close();
for (auto i = _max_files; i > 0; --i)
{
filename_t src = calc_filename(_base_filename, i - 1);
filename_t target = calc_filename(_base_filename, i);
if (details::file_helper::file_exists(target))
{
if (details::os::remove(target) != 0)
{
throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target), errno);
}
}
if (details::file_helper::file_exists(src) && details::os::rename(src, target))
{
throw spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
}
}
_file_helper.reopen(true);
}
filename_t _base_filename;
std::size_t _max_size;
std::size_t _max_files;
std::size_t _current_size;
details::file_helper _file_helper;
};
typedef rotating_file_sink<std::mutex> rotating_file_sink_mt;
typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
/*
* Default generator of daily log file names.
*/
struct default_daily_file_name_calculator
{
// Create filename for the form basename.YYYY-MM-DD_hh-mm
static filename_t calc_filename(const filename_t& basename)
{
std::tm tm = spdlog::details::os::localtime();
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
return w.str();
}
};
/*
* Generator of daily log file names in format basename.YYYY-MM-DD
*/
struct dateonly_daily_file_name_calculator
{
// Create filename for the form basename.YYYY-MM-DD
static filename_t calc_filename(const filename_t& basename)
{
std::tm tm = spdlog::details::os::localtime();
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
return w.str();
}
};
/*
* Rotating file sink based on date. rotates at midnight
*/
template<class Mutex, class FileNameCalc = default_daily_file_name_calculator>
class daily_file_sink SPDLOG_FINAL :public base_sink < Mutex >
{
public:
//create daily file sink which rotates on given time
daily_file_sink(
const filename_t& base_filename,
int rotation_hour,
int rotation_minute) : _base_filename(base_filename),
_rotation_h(rotation_hour),
_rotation_m(rotation_minute)
{
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
_rotation_tp = _next_rotation_tp();
_file_helper.open(FileNameCalc::calc_filename(_base_filename));
}
protected:
void _sink_it(const details::log_msg& msg) override
{
if (std::chrono::system_clock::now() >= _rotation_tp)
{
_file_helper.open(FileNameCalc::calc_filename(_base_filename));
_rotation_tp = _next_rotation_tp();
}
_file_helper.write(msg);
}
void _flush() override
{
_file_helper.flush();
}
private:
std::chrono::system_clock::time_point _next_rotation_tp()
{
auto now = std::chrono::system_clock::now();
time_t tnow = std::chrono::system_clock::to_time_t(now);
tm date = spdlog::details::os::localtime(tnow);
date.tm_hour = _rotation_h;
date.tm_min = _rotation_m;
date.tm_sec = 0;
auto rotation_time = std::chrono::system_clock::from_time_t(std::mktime(&date));
if (rotation_time > now)
return rotation_time;
else
return std::chrono::system_clock::time_point(rotation_time + std::chrono::hours(24));
}
filename_t _base_filename;
int _rotation_h;
int _rotation_m;
std::chrono::system_clock::time_point _rotation_tp;
details::file_helper _file_helper;
};
typedef daily_file_sink<std::mutex> daily_file_sink_mt;
typedef daily_file_sink<details::null_mutex> daily_file_sink_st;
}
}

View File

@@ -5,47 +5,46 @@
#pragma once #pragma once
#if defined(_MSC_VER) #if defined(_WIN32)
#include "spdlog/sinks/base_sink.h"
#include "spdlog/details/null_mutex.h" #include "spdlog/details/null_mutex.h"
#include "spdlog/sinks/base_sink.h"
#include <WinBase.h> #include <winbase.h>
#include <mutex> #include <mutex>
#include <string> #include <string>
namespace spdlog namespace spdlog {
{ namespace sinks {
namespace sinks
{
/* /*
* MSVC sink (logging using OutputDebugStringA) * MSVC sink (logging using OutputDebugStringA)
*/ */
template<class Mutex> template<typename Mutex>
class msvc_sink : public base_sink<Mutex> class msvc_sink : public base_sink<Mutex>
{ {
public: public:
explicit msvc_sink() explicit msvc_sink() {}
{
}
protected: protected:
void _sink_it(const details::log_msg& msg) override void sink_it_(const details::log_msg &msg) override
{ {
OutputDebugStringA(msg.formatted.c_str());
fmt::memory_buffer formatted;
sink::formatter_->format(msg, formatted);
OutputDebugStringA(fmt::to_string(formatted).c_str());
} }
void _flush() override void flush_() override {}
{}
}; };
typedef msvc_sink<std::mutex> msvc_sink_mt; using msvc_sink_mt = msvc_sink<std::mutex>;
typedef msvc_sink<details::null_mutex> msvc_sink_st; using msvc_sink_st = msvc_sink<details::null_mutex>;
} using windebug_sink_mt = msvc_sink_mt;
} using windebug_sink_st = msvc_sink_st;
} // namespace sinks
} // namespace spdlog
#endif #endif

View File

@@ -5,30 +5,24 @@
#pragma once #pragma once
#include "spdlog/sinks/base_sink.h"
#include "spdlog/details/null_mutex.h" #include "spdlog/details/null_mutex.h"
#include "spdlog/sinks/base_sink.h"
#include <mutex> #include <mutex>
namespace spdlog namespace spdlog {
{ namespace sinks {
namespace sinks
{
template <class Mutex> template<typename Mutex>
class null_sink : public base_sink<Mutex> class null_sink : public base_sink<Mutex>
{ {
protected: protected:
void _sink_it(const details::log_msg&) override void sink_it_(const details::log_msg &) override {}
{} void flush_() override {}
void _flush() override
{}
}; };
typedef null_sink<details::null_mutex> null_sink_st;
typedef null_sink<details::null_mutex> null_sink_mt;
} using null_sink_mt = null_sink<std::mutex>;
} using null_sink_st = null_sink<details::null_mutex>;
} // namespace sinks
} // namespace spdlog

View File

@@ -8,40 +8,44 @@
#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 <ostream>
#include <mutex> #include <mutex>
#include <ostream>
namespace spdlog namespace spdlog {
{ namespace sinks {
namespace sinks template<typename Mutex>
{ class ostream_sink SPDLOG_FINAL : public base_sink<Mutex>
template<class Mutex>
class ostream_sink: public base_sink<Mutex>
{ {
public: public:
explicit ostream_sink(std::ostream& os, bool force_flush=false) :_ostream(os), _force_flush(force_flush) {} explicit ostream_sink(std::ostream &os, bool force_flush = false)
: ostream_(os)
, force_flush_(force_flush)
{
}
ostream_sink(const ostream_sink &) = delete; ostream_sink(const ostream_sink &) = delete;
ostream_sink &operator=(const ostream_sink &) = delete; ostream_sink &operator=(const ostream_sink &) = delete;
virtual ~ostream_sink() = default;
protected: protected:
void _sink_it(const details::log_msg& msg) override void sink_it_(const details::log_msg &msg) override
{ {
_ostream.write(msg.formatted.data(), msg.formatted.size()); fmt::memory_buffer formatted;
if (_force_flush) sink::formatter_->format(msg, formatted);
_ostream.flush(); ostream_.write(formatted.data(), formatted.size());
if (force_flush_)
ostream_.flush();
} }
void _flush() override void flush_() override
{ {
_ostream.flush(); ostream_.flush();
} }
std::ostream& _ostream; std::ostream &ostream_;
bool _force_flush; bool force_flush_;
}; };
typedef ostream_sink<std::mutex> ostream_sink_mt; using ostream_sink_mt = ostream_sink<std::mutex>;
typedef ostream_sink<details::null_mutex> ostream_sink_st; using ostream_sink_st = ostream_sink<details::null_mutex>;
}
} } // namespace sinks
} // namespace spdlog

View File

@@ -0,0 +1,144 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include "spdlog/details/file_helper.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/fmt/fmt.h"
#include "spdlog/sinks/base_sink.h"
#include "spdlog/spdlog.h"
#include <cerrno>
#include <chrono>
#include <ctime>
#include <mutex>
#include <string>
#include <tuple>
namespace spdlog {
namespace sinks {
//
// Rotating file sink based on size
//
template<typename Mutex>
class rotating_file_sink SPDLOG_FINAL : public base_sink<Mutex>
{
public:
rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files)
: base_filename_(std::move(base_filename))
, max_size_(max_size)
, max_files_(max_files)
{
file_helper_.open(calc_filename(base_filename_, 0));
current_size_ = file_helper_.size(); // expensive. called only once
}
// calc filename according to index and file extension if exists.
// e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
static filename_t calc_filename(const filename_t &filename, std::size_t index)
{
typename std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w;
if (index != 0u)
{
filename_t basename, ext;
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
fmt::format_to(w, SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
}
else
{
fmt::format_to(w, SPDLOG_FILENAME_T("{}"), filename);
}
return fmt::to_string(w);
}
protected:
void sink_it_(const details::log_msg &msg) override
{
fmt::memory_buffer formatted;
sink::formatter_->format(msg, formatted);
current_size_ += formatted.size();
if (current_size_ > max_size_)
{
rotate_();
current_size_ = formatted.size();
}
file_helper_.write(formatted);
}
void flush_() override
{
file_helper_.flush();
}
private:
// Rotate files:
// log.txt -> log.1.txt
// log.1.txt -> log.2.txt
// log.2.txt -> log.3.txt
// log.3.txt -> delete
void rotate_()
{
using details::os::filename_to_str;
file_helper_.close();
for (auto i = max_files_; i > 0; --i)
{
filename_t src = calc_filename(base_filename_, i - 1);
filename_t target = calc_filename(base_filename_, i);
if (details::file_helper::file_exists(target))
{
if (details::os::remove(target) != 0)
{
throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target), errno);
}
}
if (details::file_helper::file_exists(src) && details::os::rename(src, target) != 0)
{
// if failed try again after small delay.
// this is a workaround to a windows issue, where very high rotation
// rates sometimes fail (because of antivirus?).
details::os::sleep_for_millis(20);
details::os::remove(target);
if (details::os::rename(src, target) != 0)
{
throw spdlog_ex(
"rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
}
}
}
file_helper_.reopen(true);
}
filename_t base_filename_;
std::size_t max_size_;
std::size_t max_files_;
std::size_t current_size_;
details::file_helper file_helper_;
};
using rotating_file_sink_mt = rotating_file_sink<std::mutex>;
using rotating_file_sink_st = rotating_file_sink<details::null_mutex>;
} // namespace sinks
//
// factory functions
//
template<typename Factory = default_factory>
inline std::shared_ptr<logger> rotating_logger_mt(
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
{
return Factory::template create<sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
}
template<typename Factory = default_factory>
inline std::shared_ptr<logger> rotating_logger_st(
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
{
return Factory::template create<sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
}
} // namespace spdlog

View File

@@ -3,51 +3,55 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#pragma once #pragma once
#include "spdlog/details/log_msg.h" #include "spdlog/details/log_msg.h"
#include "spdlog/details/pattern_formatter.h"
#include "spdlog/formatter.h"
namespace spdlog namespace spdlog {
{ namespace sinks {
namespace sinks
{
class sink class sink
{ {
public: public:
sink() sink()
: level_(level::trace)
, formatter_(new pattern_formatter("%+"))
{ {
_level = level::trace;
} }
virtual ~sink() {} sink(std::unique_ptr<spdlog::pattern_formatter> formatter)
: level_(level::trace)
, formatter_(std::move(formatter)){};
virtual ~sink() = default;
virtual void log(const details::log_msg &msg) = 0; virtual void log(const details::log_msg &msg) = 0;
virtual void flush() = 0; virtual void flush() = 0;
virtual void set_pattern(const std::string &pattern) = 0;
virtual void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) = 0;
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); {
level::level_enum level() const; return msg_level >= level_.load(std::memory_order_relaxed);
}
private: void set_level(level::level_enum log_level)
level_t _level; {
level_.store(log_level);
}
level::level_enum level() const
{
return static_cast<spdlog::level::level_enum>(level_.load(std::memory_order_relaxed));
}
protected:
// sink log level - default is all
level_t level_;
// sink formatter - default is full format
std::unique_ptr<spdlog::formatter> formatter_;
}; };
inline bool sink::should_log(level::level_enum msg_level) const } // namespace sinks
{ } // namespace spdlog
return msg_level >= _level.load(std::memory_order_relaxed);
}
inline void sink::set_level(level::level_enum log_level)
{
_level.store(log_level);
}
inline level::level_enum sink::level() const
{
return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed));
}
}
}

View File

@@ -0,0 +1,53 @@
//
// Copyright(c) 2018 spdlog
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include "spdlog/spdlog.h"
#ifdef _WIN32
#include "spdlog/sinks/wincolor_sink.h"
#else
#include "spdlog/sinks/ansicolor_sink.h"
#endif
namespace spdlog {
namespace sinks {
#ifdef _WIN32
using stdout_color_sink_mt = wincolor_stdout_sink_mt;
using stdout_color_sink_st = wincolor_stdout_sink_st;
using stderr_color_sink_mt = wincolor_stderr_sink_mt;
using stderr_color_sink_st = wincolor_stderr_sink_st;
#else
using stdout_color_sink_mt = ansicolor_stdout_sink_mt;
using stdout_color_sink_st = ansicolor_stdout_sink_st;
using stderr_color_sink_mt = ansicolor_stderr_sink_mt;
using stderr_color_sink_st = ansicolor_stderr_sink_st;
#endif
} // namespace sinks
template<typename Factory = default_factory>
inline std::shared_ptr<logger> stdout_color_mt(const std::string &logger_name)
{
return Factory::template create<sinks::stdout_color_sink_mt>(logger_name);
}
template<typename Factory = default_factory>
inline std::shared_ptr<logger> stdout_color_st(const std::string &logger_name)
{
return Factory::template create<sinks::stdout_color_sink_st>(logger_name);
}
template<typename Factory = default_factory>
inline std::shared_ptr<logger> stderr_color_mt(const std::string &logger_name)
{
return Factory::template create<sinks::stderr_color_sink_mt>(logger_name);
}
template<typename Factory = default_factory>
inline std::shared_ptr<logger> stderr_color_st(const std::string &logger_name)
{
return Factory::template create<sinks::stderr_color_sink_mt>(logger_name);
}
} // namespace spdlog

View File

@@ -5,73 +5,96 @@
#pragma once #pragma once
#include "spdlog/details/console_globals.h"
#include "spdlog/details/null_mutex.h" #include "spdlog/details/null_mutex.h"
#include "spdlog/sinks/base_sink.h" #include "spdlog/spdlog.h"
#include <cstdio> #include <cstdio>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <spdlog/details/console_globals.h>
namespace spdlog namespace spdlog {
{
namespace sinks
{
template <class Mutex> namespace sinks {
class stdout_sink SPDLOG_FINAL : public base_sink<Mutex>
template<typename TargetStream, typename ConsoleMutex>
class stdout_sink : public sink
{ {
using MyType = stdout_sink<Mutex>;
public: public:
using mutex_t = typename ConsoleMutex::mutex_t;
stdout_sink() stdout_sink()
{} : mutex_(ConsoleMutex::mutex())
static std::shared_ptr<MyType> instance() , file_(TargetStream::stream())
{ {
static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
return instance;
} }
protected: ~stdout_sink() = default;
void _sink_it(const details::log_msg& msg) override
stdout_sink(const stdout_sink &other) = delete;
stdout_sink &operator=(const stdout_sink &other) = delete;
void log(const details::log_msg &msg) override
{ {
fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stdout); std::lock_guard<mutex_t> lock(mutex_);
_flush(); fmt::memory_buffer formatted;
formatter_->format(msg, formatted);
fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
fflush(TargetStream::stream());
} }
void _flush() override void flush() override
{ {
fflush(stdout); std::lock_guard<mutex_t> lock(mutex_);
fflush(file_);
} }
void set_pattern(const std::string &pattern) override SPDLOG_FINAL
{
std::lock_guard<mutex_t> lock(mutex_);
formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
}
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override SPDLOG_FINAL
{
std::lock_guard<mutex_t> lock(mutex_);
formatter_ = std::move(sink_formatter);
}
private:
mutex_t &mutex_;
FILE *file_;
}; };
typedef stdout_sink<details::null_mutex> stdout_sink_st; using stdout_sink_mt = stdout_sink<details::console_stdout, details::console_mutex>;
typedef stdout_sink<std::mutex> stdout_sink_mt; using stdout_sink_st = stdout_sink<details::console_stdout, details::console_nullmutex>;
using stderr_sink_mt = stdout_sink<details::console_stderr, details::console_mutex>;
using stderr_sink_st = stdout_sink<details::console_stderr, details::console_nullmutex>;
template <class Mutex> } // namespace sinks
class stderr_sink SPDLOG_FINAL : public base_sink<Mutex>
// factory methods
template<typename Factory = default_factory>
inline std::shared_ptr<logger> stdout_logger_mt(const std::string &logger_name)
{ {
using MyType = stderr_sink<Mutex>; return Factory::template create<sinks::stdout_sink_mt>(logger_name);
public:
stderr_sink()
{}
static std::shared_ptr<MyType> instance()
{
static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
return instance;
}
protected:
void _sink_it(const details::log_msg& msg) override
{
fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stderr);
_flush();
} }
void _flush() override template<typename Factory = default_factory>
inline std::shared_ptr<logger> stdout_logger_st(const std::string &logger_name)
{ {
fflush(stderr); return Factory::template create<sinks::stdout_sink_st>(logger_name);
} }
};
typedef stderr_sink<std::mutex> stderr_sink_mt; template<typename Factory = default_factory>
typedef stderr_sink<details::null_mutex> stderr_sink_st; inline std::shared_ptr<logger> stderr_logger_mt(const std::string &logger_name)
{
return Factory::template create<sinks::stderr_sink_mt>(logger_name);
} }
template<typename Factory = default_factory>
inline std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name)
{
return Factory::template create<sinks::stderr_sink_st>(logger_name);
} }
} // namespace spdlog

View File

@@ -5,46 +5,41 @@
#pragma once #pragma once
#include "spdlog/common.h" #include "spdlog/sinks/base_sink.h"
#include "spdlog/spdlog.h"
#ifdef SPDLOG_ENABLE_SYSLOG
#include "spdlog/sinks/sink.h"
#include "spdlog/details/log_msg.h"
#include <array> #include <array>
#include <string> #include <string>
#include <syslog.h> #include <syslog.h>
namespace spdlog {
namespace spdlog namespace sinks {
{
namespace sinks
{
/** /**
* Sink that write to syslog using the `syscall()` library call. * Sink that write to syslog using the `syscall()` library call.
* *
* Locking is not needed, as `syslog()` itself is thread-safe. * Locking is not needed, as `syslog()` itself is thread-safe.
*/ */
class syslog_sink : public sink template<typename Mutex>
class syslog_sink : public base_sink<Mutex>
{ {
public: public:
// //
syslog_sink(const std::string& ident = "", int syslog_option=0, int syslog_facility=LOG_USER): syslog_sink(const std::string &ident = "", int syslog_option = 0, int syslog_facility = LOG_USER)
_ident(ident) : ident_(ident)
{ {
_priorities[static_cast<int>(level::trace)] = LOG_DEBUG; priorities_[static_cast<size_t>(level::trace)] = LOG_DEBUG;
_priorities[static_cast<int>(level::debug)] = LOG_DEBUG; priorities_[static_cast<size_t>(level::debug)] = LOG_DEBUG;
_priorities[static_cast<int>(level::info)] = LOG_INFO; priorities_[static_cast<size_t>(level::info)] = LOG_INFO;
_priorities[static_cast<int>(level::warn)] = LOG_WARNING; priorities_[static_cast<size_t>(level::warn)] = LOG_WARNING;
_priorities[static_cast<int>(level::err)] = LOG_ERR; priorities_[static_cast<size_t>(level::err)] = LOG_ERR;
_priorities[static_cast<int>(level::critical)] = LOG_CRIT; priorities_[static_cast<size_t>(level::critical)] = LOG_CRIT;
_priorities[static_cast<int>(level::off)] = LOG_INFO; priorities_[static_cast<size_t>(level::off)] = LOG_INFO;
// set ident to be program name if empty // set ident to be program name if empty
::openlog(_ident.empty()? nullptr:_ident.c_str(), syslog_option, syslog_facility); ::openlog(ident_.empty() ? nullptr : ident_.c_str(), syslog_option, syslog_facility);
} }
~syslog_sink()
~syslog_sink() override
{ {
::closelog(); ::closelog();
} }
@@ -52,30 +47,45 @@ public:
syslog_sink(const syslog_sink &) = delete; syslog_sink(const syslog_sink &) = delete;
syslog_sink &operator=(const syslog_sink &) = delete; syslog_sink &operator=(const syslog_sink &) = delete;
void log(const details::log_msg &msg) override protected:
{ void sink_it_(const details::log_msg &msg) override
::syslog(syslog_prio_from_level(msg), "%s", msg.raw.str().c_str());
}
void flush() override
{ {
::syslog(syslog_prio_from_level(msg), "%s", fmt::to_string(msg.raw).c_str());
} }
void flush_() override {}
private: private:
std::array<int, 7> _priorities; std::array<int, 7> priorities_;
//must store the ident because the man says openlog might use the pointer as is and not a string copy // must store the ident because the man says openlog might use the pointer as
const std::string _ident; // is and not a string copy
const std::string ident_;
// //
// Simply maps spdlog's log level to syslog priority level. // Simply maps spdlog's log level to syslog priority level.
// //
int syslog_prio_from_level(const details::log_msg &msg) const int syslog_prio_from_level(const details::log_msg &msg) const
{ {
return _priorities[static_cast<int>(msg.level)]; return priorities_[static_cast<size_t>(msg.level)];
} }
}; };
}
using syslog_sink_mt = syslog_sink<std::mutex>;
using syslog_sink_st = syslog_sink<details::null_mutex>;
} // namespace sinks
// Create and register a syslog logger
template<typename Factory = default_factory>
inline std::shared_ptr<logger> syslog_logger_mt(
const std::string &logger_name, const std::string &syslog_ident = "", int syslog_option = 0, int syslog_facility = (1 << 3))
{
return Factory::template create<sinks::syslog_sink_mt>(logger_name, syslog_ident, syslog_option, syslog_facility);
} }
#endif template<typename Factory = default_factory>
inline std::shared_ptr<logger> syslog_logger_st(
const std::string &logger_name, const std::string &syslog_ident = "", int syslog_option = 0, int syslog_facility = (1 << 3))
{
return Factory::template create<sinks::syslog_sink_st>(logger_name, syslog_ident, syslog_option, syslog_facility);
}
} // namespace spdlog

View File

@@ -5,44 +5,48 @@
#pragma once #pragma once
#include "spdlog/sinks/base_sink.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/common.h" #include "spdlog/common.h"
#include "spdlog/details/console_globals.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/sinks/sink.h"
#include <memory>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <map> #include <unordered_map>
#include <wincon.h> #include <wincon.h>
namespace spdlog namespace spdlog {
{ namespace sinks {
namespace sinks
{
/* /*
* Windows color console sink. Uses WriteConsoleA to write to the console with colors * Windows color console sink. Uses WriteConsoleA to write to the console with
* colors
*/ */
template<class Mutex> template<typename OutHandle, typename ConsoleMutex>
class wincolor_sink: public base_sink<Mutex> class wincolor_sink : public sink
{ {
public: public:
const WORD BOLD = FOREGROUND_INTENSITY; const WORD BOLD = FOREGROUND_INTENSITY;
const WORD RED = FOREGROUND_RED; const WORD RED = FOREGROUND_RED;
const WORD GREEN = FOREGROUND_GREEN;
const WORD CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE; const WORD CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE;
const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN; const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN;
wincolor_sink(HANDLE std_handle): out_handle_(std_handle) wincolor_sink()
: out_handle_(OutHandle::handle())
, mutex_(ConsoleMutex::mutex())
{ {
colors_[level::trace] = CYAN; colors_[level::trace] = WHITE;
colors_[level::debug] = CYAN; colors_[level::debug] = CYAN;
colors_[level::info] = WHITE | BOLD; colors_[level::info] = GREEN;
colors_[level::warn] = YELLOW | BOLD; colors_[level::warn] = YELLOW | BOLD;
colors_[level::err] = RED | BOLD; // red bold colors_[level::err] = RED | BOLD; // red bold
colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background
colors_[level::off] = 0; colors_[level::off] = 0;
} }
virtual ~wincolor_sink() ~wincolor_sink() override
{ {
this->flush(); this->flush();
} }
@@ -50,72 +54,86 @@ public:
wincolor_sink(const wincolor_sink &other) = delete; wincolor_sink(const wincolor_sink &other) = delete;
wincolor_sink &operator=(const wincolor_sink &other) = delete; wincolor_sink &operator=(const wincolor_sink &other) = delete;
protected: // change the color for the given level
virtual void _sink_it(const details::log_msg& msg) override void set_color(level::level_enum level, WORD color)
{ {
auto color = colors_[msg.level]; std::lock_guard<mutex_t> lock(mutex_);
auto orig_attribs = set_console_attribs(color); colors_[level] = color;
WriteConsoleA(out_handle_, msg.formatted.data(), static_cast<DWORD>(msg.formatted.size()), nullptr, nullptr);
SetConsoleTextAttribute(out_handle_, orig_attribs); //reset to orig colors
} }
virtual void _flush() override void log(const details::log_msg &msg) SPDLOG_FINAL override
{
std::lock_guard<mutex_t> lock(mutex_);
fmt::memory_buffer formatted;
formatter_->format(msg, formatted);
if (msg.color_range_end > msg.color_range_start)
{
// before color range
print_range_(formatted, 0, msg.color_range_start);
// in color range
auto orig_attribs = set_console_attribs(colors_[msg.level]);
print_range_(formatted, msg.color_range_start, msg.color_range_end);
::SetConsoleTextAttribute(out_handle_,
orig_attribs); // reset to orig colors
// after color range
print_range_(formatted, msg.color_range_end, formatted.size());
}
else // print without colors if color range is invalid
{
print_range_(formatted, 0, formatted.size());
}
}
void flush() SPDLOG_FINAL override
{ {
// windows console always flushed? // windows console always flushed?
} }
// change the color for the given level void set_pattern(const std::string &pattern) override SPDLOG_FINAL
void set_color(level::level_enum level, WORD color)
{ {
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex); std::lock_guard<mutex_t> lock(mutex_);
colors_[level] = color; formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
}
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override SPDLOG_FINAL
{
std::lock_guard<mutex_t> lock(mutex_);
formatter_ = std::move(sink_formatter);
} }
private: private:
HANDLE out_handle_; using mutex_t = typename ConsoleMutex::mutex_t;
std::map<level::level_enum, WORD> colors_;
// set color and return the orig console attributes (for resetting later) // set color and return the orig console attributes (for resetting later)
WORD set_console_attribs(WORD attribs) WORD set_console_attribs(WORD attribs)
{ {
CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info; CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info); ::GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info);
WORD back_color = orig_buffer_info.wAttributes; WORD back_color = orig_buffer_info.wAttributes;
// retrieve the current background color // retrieve the current background color
back_color &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY); back_color &= static_cast<WORD>(~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY));
// keep the background color unchanged // keep the background color unchanged
SetConsoleTextAttribute(out_handle_, attribs | back_color); ::SetConsoleTextAttribute(out_handle_, attribs | back_color);
return orig_buffer_info.wAttributes; // return orig attribs return orig_buffer_info.wAttributes; // return orig attribs
} }
};
// // print a range of formatted message to console
// windows color console to stdout void print_range_(const fmt::memory_buffer &formatted, size_t start, size_t end)
//
template<class Mutex>
class wincolor_stdout_sink: public wincolor_sink<Mutex>
{ {
public: auto size = static_cast<DWORD>(end - start);
wincolor_stdout_sink() : wincolor_sink<Mutex>(GetStdHandle(STD_OUTPUT_HANDLE)) ::WriteConsoleA(out_handle_, formatted.data() + start, size, nullptr, nullptr);
{} }
HANDLE out_handle_;
mutex_t &mutex_;
std::unordered_map<level::level_enum, WORD, level::level_hasher> colors_;
}; };
typedef wincolor_stdout_sink<std::mutex> wincolor_stdout_sink_mt; using wincolor_stdout_sink_mt = wincolor_sink<details::console_stdout, details::console_mutex>;
typedef wincolor_stdout_sink<details::null_mutex> wincolor_stdout_sink_st; using wincolor_stdout_sink_st = wincolor_sink<details::console_stdout, details::console_nullmutex>;
// using wincolor_stderr_sink_mt = wincolor_sink<details::console_stderr, details::console_mutex>;
// windows color console to stderr using wincolor_stderr_sink_st = wincolor_sink<details::console_stderr, details::console_nullmutex>;
//
template<class Mutex>
class wincolor_stderr_sink: public wincolor_sink<Mutex>
{
public:
wincolor_stderr_sink() : wincolor_sink<Mutex>(GetStdHandle(STD_ERROR_HANDLE))
{}
};
typedef wincolor_stderr_sink<std::mutex> wincolor_stderr_sink_mt; } // namespace sinks
typedef wincolor_stderr_sink<details::null_mutex> wincolor_stderr_sink_st; } // namespace spdlog
}
}

View File

@@ -1,5 +1,5 @@
// //
// Copyright(c) 2015 Gabi Melman. // 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)
// //
// spdlog main header file. // spdlog main header file.
@@ -7,154 +7,128 @@
#pragma once #pragma once
#define SPDLOG_VERSION "0.14.0"
#include "spdlog/tweakme.h"
#include "spdlog/common.h" #include "spdlog/common.h"
#include "spdlog/details/registry.h"
#include "spdlog/logger.h" #include "spdlog/logger.h"
#include "spdlog/version.h"
#include <memory>
#include <functional>
#include <chrono> #include <chrono>
#include <functional>
#include <memory>
#include <string> #include <string>
namespace spdlog namespace spdlog {
// Default logger factory- creates synchronous loggers
struct synchronous_factory
{ {
template<typename Sink, typename... SinkArgs>
// static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... args)
// Return an existing logger or nullptr if a logger with such name doesn't exist. {
// example: spdlog::get("my_logger")->info("hello {}", "world"); auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
// auto new_logger = std::make_shared<logger>(std::move(logger_name), std::move(sink));
std::shared_ptr<logger> get(const std::string& name); details::registry::instance().register_and_init(new_logger);
return new_logger;
}
};
using default_factory = synchronous_factory;
// // Create and register a logger with a templated sink type
// Set global formatting // The logger's level, formatter and flush level will be set according the
// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v"); // global settings.
//
void set_pattern(const std::string& format_string);
void set_formatter(formatter_ptr f);
//
// Set global logging level for
//
void set_level(level::level_enum log_level);
//
// Set global error handler
//
void set_error_handler(log_err_handler);
//
// Turn on async mode (off by default) and set the queue size for each async_logger.
// effective only for loggers created after this call.
// queue_size: size of queue (must be power of 2):
// Each logger will pre-allocate a dedicated queue with queue_size entries upon construction.
//
// async_overflow_policy (optional, block_retry by default):
// async_overflow_policy::block_retry - if queue is full, block until queue has room for the new log entry.
// async_overflow_policy::discard_log_msg - never block and discard any new messages when queue overflows.
//
// worker_warmup_cb (optional):
// callback function that will be called in worker thread upon start (can be used to init stuff like thread affinity)
//
// worker_teardown_cb (optional):
// callback function that will be called in worker thread upon exit
//
void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr);
// Turn off async mode
void set_sync_mode();
//
// Create and register multi/single threaded basic file logger.
// Basic logger simply writes to given file without any limitations or rotations.
//
std::shared_ptr<logger> basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate = false);
std::shared_ptr<logger> basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate = false);
//
// Create and register multi/single threaded rotating file logger
//
std::shared_ptr<logger> rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files);
std::shared_ptr<logger> rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files);
//
// Create file logger which creates new file on the given time (default in midnight):
//
std::shared_ptr<logger> daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0);
std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0);
//
// Create and register stdout/stderr loggers
//
std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name);
std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name);
std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name);
std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name);
//
// Create and register colored stdout/stderr loggers
//
std::shared_ptr<logger> stdout_color_mt(const std::string& logger_name);
std::shared_ptr<logger> stdout_color_st(const std::string& logger_name);
std::shared_ptr<logger> stderr_color_mt(const std::string& logger_name);
std::shared_ptr<logger> stderr_color_st(const std::string& logger_name);
//
// Create and register a syslog logger
//
#ifdef SPDLOG_ENABLE_SYSLOG
std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0);
#endif
#if defined(__ANDROID__)
std::shared_ptr<logger> android_logger(const std::string& logger_name, const std::string& tag = "spdlog");
#endif
// Create and register a logger with a single sink
std::shared_ptr<logger> create(const std::string& logger_name, const sink_ptr& sink);
// Create and register a logger with multiple sinks
std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks);
template<class It>
std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end);
// Create and register a logger with templated sink type
// Example: // Example:
// spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename"); // spdlog::create<daily_file_sink_st>("logger_name", "dailylog_filename", 11, 59);
template <typename Sink, typename... Args> template<typename Sink, typename... SinkArgs>
std::shared_ptr<spdlog::logger> create(const std::string& logger_name, Args...); inline std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... sink_args)
{
return default_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
}
// Create and register an async logger with a single sink // Return an existing logger or nullptr if a logger with such name doesn't
std::shared_ptr<logger> create_async(const std::string& logger_name, const sink_ptr& sink, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr); // exist.
// example: spdlog::get("my_logger")->info("hello {}", "world");
inline std::shared_ptr<logger> get(const std::string &name)
{
return details::registry::instance().get(name);
}
// Create and register an async logger with multiple sinks // Set global formatter. Each sink in each logger will get a clone of this object
std::shared_ptr<logger> create_async(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr); inline void set_formatter(std::unique_ptr<spdlog::formatter> formatter)
template<class It> {
std::shared_ptr<logger> create_async(const std::string& logger_name, const It& sinks_begin, const It& sinks_end, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr); details::registry::instance().set_formatter(std::move(formatter));
}
// Set global format string.
// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v");
inline void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local)
{
set_formatter(std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern, time_type)));
}
// Set global logging level
inline void set_level(level::level_enum log_level)
{
details::registry::instance().set_level(log_level);
}
// Set global flush level
inline void flush_on(level::level_enum log_level)
{
details::registry::instance().flush_on(log_level);
}
// Start/Restart a periodic flusher thread
// Warning: Use only if all your loggers are thread safe!
inline void flush_every(std::chrono::seconds interval)
{
details::registry::instance().flush_every(interval);
}
// Set global error handler
inline void set_error_handler(log_err_handler handler)
{
details::registry::instance().set_error_handler(std::move(handler));
}
// Register the given logger with the given name // Register the given logger with the given name
void register_logger(std::shared_ptr<logger> logger); inline void register_logger(std::shared_ptr<logger> logger)
{
details::registry::instance().register_logger(std::move(logger));
}
// Apply a user defined function on all registered loggers // Apply a user defined function on all registered loggers
// Example: // Example:
// spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->flush();}); // spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->flush();});
void apply_all(std::function<void(std::shared_ptr<logger>)> fun); inline void apply_all(std::function<void(std::shared_ptr<logger>)> fun)
{
details::registry::instance().apply_all(std::move(fun));
}
// Drop the reference to the given logger // Drop the reference to the given logger
void drop(const std::string &name); inline void drop(const std::string &name)
{
details::registry::instance().drop(name);
}
// Drop all references from the registry // Drop all references from the registry
void drop_all(); inline void drop_all()
{
details::registry::instance().drop_all();
}
// stop any running threads started by spdlog and clean registry loggers
inline void shutdown()
{
details::registry::instance().shutdown();
}
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// //
// Trace & Debug can be switched on/off at compile time for zero cost debug statements. // Trace & Debug can be switched on/off at compile time for zero cost debug
// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in teakme.h to enable. // statements.
// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in tweakme.h to enable.
// SPDLOG_TRACE(..) will also print current file and line. // SPDLOG_TRACE(..) will also print current file and line.
// //
// Example: // Example:
@@ -162,28 +136,26 @@ void drop_all();
// SPDLOG_TRACE(my_logger, "some trace message"); // SPDLOG_TRACE(my_logger, "some trace message");
// SPDLOG_TRACE(my_logger, "another trace message {} {}", 1, 2); // SPDLOG_TRACE(my_logger, "another trace message {} {}", 1, 2);
// SPDLOG_DEBUG(my_logger, "some debug message {} {}", 3, 4); // SPDLOG_DEBUG(my_logger, "some debug message {} {}", 3, 4);
// SPDLOG_DEBUG_IF(my_logger, true, "some debug message {} {}", 3, 4);
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#ifdef SPDLOG_TRACE_ON #ifdef SPDLOG_TRACE_ON
#define SPDLOG_STR_H(x) #x #define SPDLOG_STR_H(x) #x
#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x) #define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x)
#define SPDLOG_TRACE(logger, ...) logger->trace("[" __FILE__ " line #" SPDLOG_STR_HELPER(__LINE__) "] " __VA_ARGS__) #ifdef _MSC_VER
#define SPDLOG_TRACE_IF(logger, flag, ...) logger->trace_if(flag, "[" __FILE__ " line #" SPDLOG_STR_HELPER(__LINE__) "] " __VA_ARGS__) #define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__)
#else #else
#define SPDLOG_TRACE(logger, ...) #define SPDLOG_TRACE(logger, ...) \
#define SPDLOG_TRACE_IF(logger, flag, ...) logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ]" \
" " __VA_ARGS__)
#endif
#else
#define SPDLOG_TRACE(logger, ...) (void)0
#endif #endif
#ifdef SPDLOG_DEBUG_ON #ifdef SPDLOG_DEBUG_ON
#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__) #define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__)
#define SPDLOG_DEBUG_IF(logger, flag, ...) logger->debug_if(flag, __VA_ARGS__)
#else #else
#define SPDLOG_DEBUG(logger, ...) #define SPDLOG_DEBUG(logger, ...) (void)0
#define SPDLOG_DEBUG_IF(logger, flag, ...)
#endif #endif
} } // namespace spdlog
#include "spdlog/details/spdlog_impl.h"

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