Compare commits

...

362 Commits

Author SHA1 Message Date
Gabi Melman
83b9149930 Update README.md 2020-05-14 00:13:56 +03:00
gabime
5ca5cbd447 Added missing SPDLOG_API to color_sinks.cpp 2020-05-05 02:12:39 +03:00
gabime
597e89efe3 clang-format 2020-05-05 01:42:39 +03:00
gabime
683e44f5f8 Use static fun instead of member 2020-05-05 01:33:01 +03:00
gabime
0b36d4e360 Fix #1540 2020-05-05 01:03:33 +03:00
Gabi Melman
67606e2460 Delete meson.build 2020-05-02 18:54:59 +03:00
Gabi Melman
b6c6b30c0d Delete meson.build 2020-05-02 18:53:40 +03:00
Gabi Melman
e5935f0ced Delete meson.build 2020-05-02 17:39:15 +03:00
gabime
75c15e8028 Formatted cmake 2020-05-02 11:42:08 +03:00
gabime
4831ae17d9 Set tabsize to 4 in when formatting cmake 2020-05-02 11:41:32 +03:00
gabime
22655d7554 Formatted cmake using cmake-format 2020-05-02 11:39:59 +03:00
gabime
ff0e430e46 Formatted cmake using cmake-format 2020-05-02 11:38:14 +03:00
gabime
e86dc8c338 Added call to cmake-format 2020-05-02 11:35:13 +03:00
gabime
0814de6371 Added call to cmake-format 2020-05-02 11:34:04 +03:00
Gabi Melman
34bc6907d0 Update .travis.yml 2020-05-02 09:16:54 +03:00
Gabi Melman
a6dd1a2b4b Update README.md 2020-05-02 09:15:08 +03:00
Gabi Melman
4fe5d3d5e3 Merge pull request #1537 from waywardmonkeys/cmake-whitespace
cmake: Fix tabs, whitespace at EOL.
2020-05-02 08:47:19 +03:00
Bruce Mitchener
937fe7e909 cmake: Fix tabs, whitespace at EOL. 2020-05-02 07:17:15 +07:00
Gabi Melman
89ab1e679d Merge pull request #1536 from waywardmonkeys/fix-typos
Fix typos.
2020-05-01 12:12:48 +03:00
Bruce Mitchener
559984b2fe Fix typos. 2020-05-01 08:54:16 +07:00
Gabi Melman
3ac9540351 Update test_file_helper.cpp 2020-04-29 14:50:25 +03:00
Gabi Melman
d5709c9d70 temp test appveoyr 2020-04-29 14:15:52 +03:00
Gabi Melman
891cc95add Update appveyor.yml 2020-04-29 04:18:57 +03:00
Gabi Melman
0246b5657a Update appveyor.yml 2020-04-29 03:54:59 +03:00
Gabi Melman
26ca1fb9f3 Update appveyor.yml 2020-04-29 03:32:52 +03:00
Gabi Melman
6b4355b76f Delete meson_options.txt 2020-04-29 01:57:00 +03:00
Gabi Melman
90bd9692f5 Delete meson.build 2020-04-29 01:56:45 +03:00
gabime
8878185628 Add dll configuration to appveyor 2020-04-29 01:32:10 +03:00
gabime
15066d1d37 clang-format 2020-04-29 01:19:23 +03:00
gabime
abaae6e28b Add dll configuration to appveyor 2020-04-29 01:06:17 +03:00
Gabi Melman
2170de8819 Update common.h 2020-04-28 18:42:59 +03:00
Gabi Melman
300ec667f6 Update common.h 2020-04-28 18:37:57 +03:00
Gabi Melman
348c4380d6 Fix #1535 2020-04-28 18:01:48 +03:00
Gabi Melman
98388d18de Update README.md 2020-04-26 19:55:58 +03:00
Gabi Melman
bbc5753b96 Update README.md 2020-04-26 19:53:58 +03:00
Gabi Melman
564eecaa3b Update README.md 2020-04-26 19:50:33 +03:00
Gabi Melman
0480920058 Update README.md 2020-04-26 19:49:12 +03:00
Gabi Melman
d977dd4395 Update README.md 2020-04-26 09:40:56 +03:00
Gabi Melman
ca402379a9 Update README.md 2020-04-26 09:35:52 +03:00
Gabi Melman
903bf2135d Update README.md 2020-04-26 09:34:03 +03:00
Gabi Melman
1f732585b2 Update README.md 2020-04-26 09:26:52 +03:00
Gabi Melman
224de0601e Update README.md 2020-04-26 09:24:33 +03:00
Gabi Melman
9b84337830 Update README.md 2020-04-26 09:23:31 +03:00
Gabi Melman
a4665c27df Update README.md 2020-04-26 09:20:14 +03:00
Gabi Melman
3337015346 Update README.md 2020-04-26 09:19:05 +03:00
gabime
42c466296a Fix #1527 again 2020-04-26 01:58:55 +03:00
Gabi Melman
a9fcf9db47 Revert last commit 2020-04-26 00:47:32 +03:00
Gabi Melman
f3b55fcab0 Fix #1527 2020-04-26 00:43:36 +03:00
Gabi Melman
b56b6509b1 Update README.md 2020-04-23 02:49:37 +03:00
Gabi Melman
ac85e383a9 Update README.md 2020-04-23 02:48:50 +03:00
Gabi Melman
2b326e90b8 Merge pull request #1525 from Montellese/improvement/fmt_min_version
Set minimum version of fmt to 5.0.0
2020-04-20 21:10:05 +03:00
Montellese
3e8be645d2 Set minimum version of fmt to 5.3.0
The used fmt::fmt target has been introduced with fmt 5.0.0.
2020-04-20 19:59:50 +02:00
gabime
81444265f4 clang-format 2020-04-19 11:33:36 +03:00
Gabi Melman
5716ab70ec Merge pull request #1521 from ron003/single_log_method_with_time_point
add single logger method and log_msg constructor and tests/test_time_…
2020-04-19 09:55:07 +03:00
Ron Rechenmacher
faaef7686d pass log_time param by value 2020-04-18 22:44:13 -05:00
Ron Rechenmacher
9e6f5b6b2d add single logger method and log_msg constructor and tests/test_time_point.cpp 2020-04-18 08:58:11 -05:00
Gabi Melman
d28465bf60 Update .travis.yml 2020-04-18 14:04:46 +03:00
gabime
867fec260b Another shot at travis clang-10 2020-04-18 13:22:07 +03:00
Gabi Melman
f5309d902a Update .travis.yml 2020-04-18 04:48:26 +03:00
gabime
82823e50dd Another shot at travis clang-10 2020-04-17 17:23:39 +03:00
gabime
394f79e9d3 small refactoring 2020-04-15 19:44:54 +03:00
gabime
595bbbd3e4 travis fix 2020-04-15 02:29:38 +03:00
gabime
2127572c33 travis fix? 2020-04-15 02:25:30 +03:00
gabime
7d6c927684 Updated travis to clang-9 2020-04-15 01:13:33 +03:00
gabime
9d2d4c82df Updated travis to clang-9 2020-04-15 01:06:28 +03:00
gabime
515ce9bebb Updated travis to clang-9 2020-04-15 01:05:09 +03:00
gabime
7698bb0ae1 Bump fmt version to 6.2.0 2020-04-15 00:51:03 +03:00
Gabi Melman
c89a5148b2 Update README.md 2020-04-14 12:48:11 +03:00
Gabi Melman
c37adba77b Update README.md 2020-04-14 12:47:18 +03:00
Gabi Melman
95cc3dec3f Update README.md 2020-04-14 00:42:01 +03:00
Gabi Melman
42c4a91041 Update README.md 2020-04-13 22:07:51 +03:00
Gabi Melman
d253dad2ee Update README.md 2020-04-13 21:46:29 +03:00
Gabi Melman
7f0265e674 Update README.md 2020-04-13 21:35:39 +03:00
Gabi Melman
b9726ba66d Update syslog_sink.h 2020-04-13 21:12:42 +03:00
Gabi Melman
b20ffa7369 Fix #1523 2020-04-13 21:09:27 +03:00
gabime
854abdf5e6 Added cmake option SPDLOG_BUILD_ALL 2020-04-12 02:43:42 +03:00
gabime
d0fc8a572c Fixed clang-tidy warnings 2020-04-12 02:30:47 +03:00
gabime
8bc1ca0e44 clang-format 2020-04-12 02:21:48 +03:00
gabime
d38bd138cd Micro optimze pattern-formatter when padding not needed 2020-04-12 02:21:14 +03:00
gabime
7766bc25d1 Updated fmt_helper::pad2() 2020-04-12 01:40:22 +03:00
Gabi Melman
34244656a6 Update test_fmt_helper.cpp 2020-04-11 21:09:48 +03:00
gabime
619849c793 fixed comment 2020-04-11 20:15:04 +03:00
gabime
927b2b3942 Fixed conversion warnings 2020-04-11 20:07:40 +03:00
gabime
76389e057f Optimize fmt_helper::pad3() 2020-04-11 19:50:19 +03:00
Gabi Melman
0a14e491ab Update README.md 2020-04-11 11:19:17 +03:00
Gabi Melman
1f7f1c1ffb Update CMakeLists.txt 2020-04-10 16:39:59 +03:00
gabime
6440733002 Fixed cmake 2020-04-10 15:51:34 +03:00
gabime
02802af97f Renamed SPDLOG_ENABLE_WARNINGS to SPDLOG_BUILD_WARNINGS 2020-04-10 15:38:33 +03:00
gabime
a8169a3d6b clang-format 2020-04-10 15:35:58 +03:00
gabime
9ba7fc94a5 Disable building tests by default in cmake 2020-04-10 15:35:41 +03:00
gabime
48b71a02d7 Updated latency test to run null logging be default 2020-04-10 15:29:43 +03:00
gabime
18ed04b990 Fixed clang-tidy warnings in bench.cpp 2020-04-09 16:12:52 +03:00
gabime
d09e03606c Fixed multiple clang-tidy warnings 2020-04-08 18:18:27 +03:00
gabime
b2017f5653 Fixed multiple clang-tidy warnings 2020-04-08 18:17:21 +03:00
gabime
c16eb80d7f Fixed multiple clang-tidy warnings 2020-04-08 17:04:10 +03:00
gabime
0c56f98a92 Fix .clang-tidy 2020-04-08 16:27:12 +03:00
gabime
490940cd53 Minor update to fmt_helper 2020-04-04 20:11:59 +03:00
gabime
92d27b0aa3 Minor update to fmt_helper 2020-04-04 20:11:17 +03:00
gabime
ca9c83f824 Minor improvment to number padding 2020-04-04 20:06:04 +03:00
gabime
fc900e2432 Enabled warnings in ci 2020-04-04 17:20:48 +03:00
gabime
e3257e56ab Fix #1480 2020-04-04 17:13:03 +03:00
Gabi Melman
7d2337c6eb Fix #1504 - Extra warnings OFF by default 2020-04-03 20:37:05 +03:00
Gabi Melman
58629f1fea Merge pull request #1503 from Glamhoth/warnings_as_option
Added an option to disable compiler warnings
2020-04-03 20:32:38 +03:00
FIlip Demski
132ec0a5fc Added an option to disable compiler warnings 2020-04-03 10:09:29 +02:00
gabime
c2b47430fb Enabled warning on examples only if built as part of spdlog 2020-04-01 00:23:04 +03:00
Gabi Melman
7906592230 Fix #1499 2020-04-01 00:10:57 +03:00
Gabi Melman
f57378d8ba Update test_eventlog.cpp 2020-03-28 13:59:59 +03:00
Gabi Melman
1ccdc225af Merge pull request #1495 from trondhe/v1.x
add /WX mscv compiler option for only mscv compiler
2020-03-28 13:30:43 +03:00
Trond H Emaus
3e4df86ac0 create MSVC_OPTIONS list only if compiler is msvc 2020-03-28 10:26:32 +01:00
Trond H Emaus
7054cf7a35 replace MSVC_OPTIONS variable as list 2020-03-28 00:03:59 +01:00
Trond H Emaus
2a7fc9e30e add /WX mscv compiler option for only mscv compiler
clang on windows support both gcc and mscv style options. Clang.exe on windows
defaults to gcc style, which will result in /WX unknown compiler command.
This will set /WX if and only if the compiler is MSVC and greater than version 1900
2020-03-27 19:59:22 +01:00
Gabi Melman
f1b4f15dbb Merge pull request #1494 from zyansheep/zyansheep-patch-1
yaourt is outdated and insecure, use yay in README.md
2020-03-26 22:50:08 +02:00
Zyansheep
c98152e9d0 Use community database instead of AUR 2020-03-26 16:25:38 -04:00
Zyansheep
7c34859e0c yaourt is outdated and insecure, use yay
Not maintained anymore: https://archlinux.fr/yaourt-en
Alternatively: spdlog is now apart of the community repo (https://www.archlinux.org/packages/community/x86_64/spdlog/)
2020-03-26 11:45:22 -04:00
Gabi Melman
dd38e096b2 Fix issue #1486 2020-03-24 02:15:14 +02:00
Gabi Melman
ea89efbed7 Merge pull request #1487 from Naios/v1.x
Fix a build issue when SPDLOG_PREVENT_CHILD_FD is defined
2020-03-23 23:18:37 +02:00
Denis Blank
61408a0f29 Fix a build issue when SPDLOG_PREVENT_CHILD_FD is defined
* Ref 033fe9f133
2020-03-23 22:16:25 +01:00
Gabi Melman
cca004efe4 Update README.md 2020-03-22 17:19:11 +02:00
Gabi Melman
da3f3da92c Update win_eventlog_sink.h 2020-03-22 02:30:15 +02:00
gabime
f0c35819bd Removed unneeded nullptr check 2020-03-22 02:17:48 +02:00
gabime
ff616002cf Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2020-03-22 01:09:16 +02:00
gabime
e1c79869b6 Replaced more SPDLOG_THROW with throw_spdlog_ex 2020-03-22 01:09:08 +02:00
Gabi Melman
bd43403f5a Update fwd.h 2020-03-22 00:54:23 +02:00
gabime
d3997cc4d1 Fix #1481 2020-03-22 00:50:22 +02:00
gabime
5b0b8579b2 Fix #1485 by removing SPDLOG_NO_NAME option 2020-03-22 00:30:01 +02:00
gabime
c927de137c clang-format 2020-03-22 00:16:27 +02:00
gabime
eb23d505f8 resolve issue #1483 2020-03-22 00:09:56 +02:00
gabime
2400cf16a4 Merge branch 'issue-#1483' into v1.x 2020-03-22 00:05:02 +02:00
gabime
bbe3ace533 Fixed bench 2020-03-21 23:42:22 +02:00
Gabi Melman
3b87eb3d08 Moved throw to dedicated function throw_spdlog_ex 2020-03-21 23:25:12 +02:00
Gabi Melman
d43a17304e Merge pull request #1484 from OlivierLDff/pch-support
Support for precompiled headers with SPDLOG_ENABLE_PCH
2020-03-21 17:51:50 +02:00
Olivier LDff
21d41b8e81 Support for precompiled header for static & dynamic spdlog compilation.
Compilation is shorten by 2 on windows with msvc. Some improvement for gcc and appleclang. Add SPDLOG_ENABLE_PCH option to CMake that is defaulted to ON. This will enable precompiled header by default if supported. It can be disable if user have a conflict of any kind. Or to check that project is still compiling with CMake version that do not support precompiled header.
2020-03-21 15:36:14 +01:00
gabime
332eaaf916 Update readme with custom flags example 2020-03-21 15:54:01 +02:00
gabime
0a5ada6411 clang-format 2020-03-21 15:25:26 +02:00
gabime
963f8d3485 Fixed tests 2020-03-21 15:25:05 +02:00
Gabi Melman
60a8c5f1c9 Added padding support and set_pattern to custom_flags 2020-03-21 15:03:41 +02:00
Gabi Melman
d1a1024465 Moved pattern formatter from spdlog/details to spdlog/ 2020-03-21 13:35:16 +02:00
Gabi Melman
0d7ff9ac47 Moved pattern formatter from spdlog/details to spdlog/ 2020-03-21 13:35:04 +02:00
Gabi Melman
752d5685dc Moved pattern formatter from spdlog/details to spdlog/ 2020-03-21 13:33:04 +02:00
Gabi Melman
c6c517431f wip custom formatter flags 2020-03-20 16:09:30 +02:00
Gabi Melman
ee54f54ced Use /W3 for mvsc 2020-03-20 13:56:54 +02:00
Gabi Melman
2c5c96e159 Merge pull request #1475 from Crunkle/v1.x
Fix Windows setenv check
2020-03-18 16:17:04 +02:00
Crunkle
a10763138e Fix Windows setenv check 2020-03-18 09:46:32 +00:00
Gabi Melman
208eb0ca07 Merge pull request #1473 from avrahamshukron/patch-1
README: add conda as a supported package manager
2020-03-15 22:07:39 +02:00
Avraham Shukron
f0403fa9e4 README: add conda as a supported package manager
spdlog is available on conda-forge for a long time.
https://anaconda.org/conda-forge/spdlog
2020-03-15 21:59:34 +02:00
gabime
3f86b250e6 Updated comment 2020-03-15 18:56:38 +02:00
gabime
d1819f5f76 clang-format 2020-03-15 18:51:52 +02:00
gabime
19c7e63858 Added final keywords to pattern_formatter classes 2020-03-15 18:46:10 +02:00
gabime
7efdcc26fe CMakeLists.txt: use same policy as fmt 2020-03-15 18:41:34 +02:00
gabime
3ab3970dd2 CMakeLists.txt: use same policy as fmt 2020-03-15 18:40:42 +02:00
gabime
5ab487dbae Updated example 2020-03-15 17:38:15 +02:00
gabime
55fbc2c78e Merge branch 'ngugcx-hexdump' into v1.x 2020-03-15 17:32:11 +02:00
gabime
643426e2b2 Small refactor 2020-03-15 17:20:02 +02:00
gabime
f31a834613 clang-format 2020-03-15 17:16:28 +02:00
gabime
683080be53 Fix clang-tidy warning 2020-03-15 17:14:52 +02:00
gabime
d14b8a9ad6 Merge branch 'hexdump' of https://github.com/ngugcx/spdlog into ngugcx-hexdump 2020-03-15 16:27:58 +02:00
ngugcx
0f87ba6c93 fix a test case error. 2020-03-15 20:21:58 +08:00
ngugcx
02bfa0898c Do not fill blanks to align if there is only one line. 2020-03-15 20:09:28 +08:00
ngugcx
f5313f92f1 Enhance to_hex like the unix command hexdump. 2020-03-15 12:01:31 +08:00
Gabi Melman
e41a258b93 Update tcp_sink.h 2020-03-13 01:38:10 +02:00
Gabi Melman
ffa85cda1a Update tcp_sink.h 2020-03-13 01:36:33 +02:00
Gabi Melman
0123d41647 Update tcp_sink.h 2020-03-13 01:36:03 +02:00
Gabi Melman
2b0481deed Always use SOCK_CLOEXEC in tcp client 2020-03-13 01:18:27 +02:00
Gabi Melman
1389f86675 Update tcp_client.h 2020-03-13 01:17:12 +02:00
Gabi Melman
cf6bb88af2 Update tcp_client-windows.h 2020-03-13 01:12:05 +02:00
Gabi Melman
8e19a267bd Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2020-03-13 01:01:28 +02:00
Gabi Melman
3b55709e7f tcp_sink windows impl 2020-03-13 01:01:19 +02:00
gabime
f78bca4ad8 Removed junk files 2020-03-12 16:09:34 +02:00
Gabi Melman
713feca582 Update CMakeLists.txt 2020-03-12 14:46:49 +02:00
Gabi Melman
26c20ed91d Update CMakeLists.txt 2020-03-12 14:46:01 +02:00
Gabi Melman
e399249f31 Update CMakeLists.txt 2020-03-12 14:45:01 +02:00
Gabi Melman
b4a1b4b59a Updated verion.rc and CMakeLists.txt for dll build 2020-03-12 14:35:30 +02:00
Gabi Melman
b309a88bea Update utils.cmake 2020-03-12 14:29:39 +02:00
Gabi Melman
e88bee49a6 Added version info to dll build 2020-03-12 14:29:23 +02:00
Gabi Melman
ec12770693 Merge pull request #1467 from vejmartin/v1.x
Add shared library support for Windows
2020-03-11 13:31:39 +02:00
Martin Vejdarski
5b3a18319e fix SPDLOG_BUILD_SHARED cmake option 2020-03-11 03:05:44 +07:00
Martin Vejdarski
3b73278348 fix shared lib issues with bundled fmt 2020-03-11 00:39:42 +07:00
Martin Vejdarski
0ca2cb625e Add shared library support for Windows 2020-03-10 03:14:36 +07:00
Gabi Melman
67561f97ec Update argv.h 2020-03-06 16:27:08 +02:00
Gabi Melman
b667bae65d Update argv.h 2020-03-06 16:26:11 +02:00
gabime
54be9bd8b9 Use brackets to include spdlog in srcs 2020-03-06 15:51:07 +02:00
Gabi Melman
06d0299639 version 1.6.0 rc 2020-03-06 15:37:50 +02:00
Gabi Melman
84851e230f Update README.md 2020-03-06 15:33:24 +02:00
Gabi Melman
52aed9e0de Update README.md 2020-03-06 15:32:51 +02:00
Gabi Melman
ead9a550fd Update README.md 2020-03-06 15:32:23 +02:00
Gabi Melman
cf80b492a3 Update README.md 2020-03-06 15:31:38 +02:00
gabime
69f3d2678e Updated readme. 2020-03-06 15:30:14 +02:00
gabime
8038bc2fc8 Update example 2020-03-06 15:21:21 +02:00
gabime
f20b12cf3f Update example 2020-03-06 15:16:48 +02:00
gabime
c8bd53509c Update example 2020-03-06 15:15:59 +02:00
gabime
006124d816 Update example 2020-03-06 15:15:10 +02:00
gabime
efd73ac956 Merge remote-tracking branch 'origin/v1.x' into conf-env3 2020-03-06 15:10:24 +02:00
gabime
b7d7334451 Renamed level loaders 2020-03-06 15:09:46 +02:00
gabime
8284865f9a Fix tidy warning 2020-03-04 16:21:07 +02:00
gabime
1f8b04cbd1 Fix tidy warning 2020-03-04 16:09:04 +02:00
gabime
b3402a0b9f Fix tidy warning 2020-03-04 16:08:35 +02:00
gabime
4037959945 Fix tidy warning 2020-03-04 15:59:45 +02:00
gabime
d7313a3274 Fix tidy warning 2020-03-04 15:52:42 +02:00
gabime
8302086942 Fixed tcp_client 2020-03-04 15:40:04 +02:00
Gabi Melman
6095db951b Update README.md 2020-03-04 14:12:56 +02:00
gabime
817d2764b6 Fix bench tidy warning 2020-03-03 23:53:28 +02:00
Gabi Melman
62189602cb Update README.md 2020-03-03 09:51:56 +02:00
Gabi Melman
0120dcc787 Update logger-inl.h 2020-02-29 13:20:26 +02:00
Gabi Melman
6bfc0ec3a7 Merge pull request #1457 from Ruffel/fix-clang-tidy-warning
Resolve erroneous clang-tidy warning about using a moved from pointer
2020-02-29 13:16:49 +02:00
Steven Cartmell
f999d879d5 fix: Break from loop on last iteration to resolve clang-tidy warning
The clang-tidy warning `clang-analyzer-cplusplus.Move` warns when a
moved from object is deferenced. This is triggered in spdlog because
clang-tidy fails to detect that the `logger:set_formatter` will only
move the unique_ptr on the last iteration of the loop, assuming that
`f->clone` may be called on it afterwards.

To fix, add a break statement after moving the pointer (on the last
iteration) to let clang-tidy know the logger pointer is not used after
this point.
2020-02-28 21:09:31 +00:00
gabime
c861e2d9cf Updated example 2020-02-26 19:21:36 +02:00
gabime
e696978d11 Renamed function name 2020-02-26 18:47:51 +02:00
gabime
fbf2e942a9 Allow "err" when converting string to level 2020-02-26 18:33:49 +02:00
gabime
d18f282938 Fixed missing cfg.cpp 2020-02-26 18:21:24 +02:00
gabime
c10be7eaec merge with v1.x 2020-02-26 18:13:13 +02:00
gabime
05ecad4263 Use windows_include in os-inl.h 2020-02-26 18:10:47 +02:00
Gabi Melman
4cdb159ccb Update spdlog.h 2020-02-26 12:54:31 +02:00
Gabi Melman
fccb25586f Update spdlog.h 2020-02-26 12:52:46 +02:00
Gabi Melman
ab2f3307eb Update spdlog.h 2020-02-26 12:51:16 +02:00
Gabi Melman
db26a103d6 Update spdlog.h 2020-02-26 12:50:51 +02:00
Gabi Melman
32902f79ad Merge pull request #1453 from david-bodor-at-craftunique/v1.x
only include windows.h when it's unavoidable
2020-02-26 12:41:51 +02:00
Dávid Bodor
fab33dd230 refactor: extract windows.h include to details/windows_include.h 2020-02-26 08:54:56 +01:00
gabime
daaa025356 moved .clang-format to top level 2020-02-25 18:52:35 +02:00
gabime
ffe272c165 Added SDPLOG_TIDY option to CMakeLists.txt instead of clang_tidy.sh 2020-02-25 18:49:28 +02:00
Dávid Bodor
6e763d2776 only include windows.h when it's unavoidable 2020-02-25 15:36:39 +01:00
gabime
c71b433a35 clang-format 2020-02-25 15:10:44 +02:00
gabime
0b91d55269 Refactored tcp_client_sink 2020-02-25 15:09:03 +02:00
gabime
9f41903067 Refactored tcp_client_sink 2020-02-25 15:00:42 +02:00
gabime
64de8807e2 Fix #1452 2020-02-24 17:01:09 +02:00
gabime
3848cbe24a Fix #1452 2020-02-24 17:00:35 +02:00
Gabi Melman
15ac7b08f7 Merge pull request #1451 from tcraigtyler/more-rpm-cpack
Updates supporting CPack RPM generation
2020-02-21 02:53:07 +02:00
Craig Tyler
e47ecc1828 Updates supporting CPack RPM generation
Enable command line overrides of default settings
* CPACK_GENERATOR
* CPACK_PACKAGE_RELOCATABLE

Enables command line setting of generated RPM version attributes
* CPACK_RPM_PACKAGE_RELEASE
* CPACK_RPM_PACKAGE_ARCHITECTURE

Adds dependency on pkgconfig RPM when not generating relocatable RPM
2020-02-20 14:52:56 -08:00
Gabi Melman
c09641cf47 Merge pull request #1450 from rabbull/fmt_string
Add example to FMT_STRING in README
2020-02-20 20:27:03 +02:00
Karl Liu
92467db591 add example to FMT_STRING 2020-02-21 02:06:06 +08:00
Gabi Melman
ea5e7182ab Merge pull request #1444 from Crunkle/v1.x
Fix Windows event log sink
2020-02-17 21:37:27 +02:00
Crunkle
d38d53d9dd Fix Win32 event log sink 2020-02-17 17:15:43 +00:00
Gabi Melman
c9e094d9fc Merge pull request #1442 from niamster/v1.x
workaround for `Unknown extension ".c" for file` issue
2020-02-16 22:41:52 +02:00
Dmytro Milinevskyi
af75985ec6 workaround for Unknown extension ".c" for file issue
http://www.grokit.ca/cnt/CMakeProblemsSolutions/
2020-02-16 19:57:49 +01:00
gabime
4b7c05903b optimize wincolor_sink to use array instead of map to find color codes 2020-02-15 12:11:01 +02:00
gabime
695912c7cf Optimize ansicolor_sink to use array instead of map to find color codes 2020-02-15 12:06:01 +02:00
gabime
5c06306ccc Updated clang-tidy 2020-02-15 12:05:07 +02:00
gabime
d4fd17f64f Fixed #1439 2020-02-15 11:29:04 +02:00
Gabi Melman
76d94e69ae Fix #1439 2020-02-15 10:57:12 +02:00
Gabi Melman
0f42744f5c Update rotating_file_sink-inl.h 2020-02-12 12:11:03 +02:00
Gabi Melman
e8daf7c73b Merge pull request #1431 from tt4g/avoid-references-to-race-data-filename
Avoid references to race data filename
2020-02-12 12:09:25 +02:00
tt4g
0cf1af5bbf Avoid references to race data filename 2020-02-12 16:30:44 +09:00
Gabi Melman
a343328a21 Update rotating_file_sink-inl.h 2020-02-12 08:56:24 +02:00
Gabi Melman
53a56b82af Update rotating_file_sink-inl.h 2020-02-12 07:51:42 +02:00
Gabi Melman
64dd4dc219 Update rotating_file_sink-inl.h 2020-02-12 07:49:29 +02:00
Gabi Melman
9e9da42c64 Update rotating_file_sink.h 2020-02-12 07:48:41 +02:00
Gabi Melman
5c410f4ca2 Merge pull request #1430 from tt4g/add-critical-section-to-filename-api
Add critical section to filename function of  each file sink
2020-02-12 07:35:52 +02:00
tt4g
0778211116 Add critical section to filename function of each file sink 2020-02-12 11:01:35 +09:00
Gabi Melman
574563d711 Update tcp_sink.h 2020-02-10 23:00:08 +02:00
Gabi Melman
e9d0b424d5 Merge pull request #1427 from dominicpoeschko/patch-1
Handling SPDLOG_PREVENT_CHILD_FD in tcp_sink
2020-02-10 19:04:32 +02:00
dominicpoeschko
eef981e05f Handling SPDLOG_PREVENT_CHILD_FD in tcp_sink
Adding SOCK_CLOEXEC to socket

Fixing bug in sink_it_ (bytes_sent not added to buffer)
2020-02-10 17:52:54 +01:00
gabime
9f24f4bc69 revert last commit 2020-02-10 17:38:31 +02:00
gabime
5da9818676 updated member name in win eventlog sink 2020-02-10 17:25:39 +02:00
gabime
ff59b07986 Update tcp_sink.h 2020-02-10 17:21:32 +02:00
gabime
1b6d4fd277 Update tcp_sink.h 2020-02-10 17:19:22 +02:00
gabime
7b19890deb Update tcp_sink.h 2020-02-10 17:18:18 +02:00
gabime
5370443ece clang-format 2020-02-10 17:13:41 +02:00
gabime
ad4fb1cf84 Fixed tcp sink 2020-02-10 17:13:04 +02:00
gabime
7f8169f0da Fixed tcp_sink to accept hostnames 2020-02-10 15:02:48 +02:00
gabime
66e8652862 Fix tcp_sink 2020-02-10 14:34:00 +02:00
gabime
05cbdbc1ef moved tcp sink to sinks 2020-02-10 14:21:26 +02:00
Gabi Melman
38584a1fca Merge pull request #1426 from Proheeler/v1.x
tcp_sink implementation for fluentbit
2020-02-10 14:19:21 +02:00
Vyacheslav
d96d8c49ac Code Style naming 2020-02-10 15:13:39 +03:00
Vyacheslav
4bb623a0a3 removed unneccessary namespace std 2020-02-10 15:08:51 +03:00
Vyacheslav
3aa94a0997 Added send function verification + licence 2020-02-10 14:58:54 +03:00
Vyacheslav
ccad4ae04f Resolve modification requests 2020-02-10 14:46:39 +03:00
v.reshetnikov
346b9ae5a1 tcp_sink implementation for fluentbit 2020-02-10 14:24:17 +03:00
Gabi Melman
12f36debae Merge pull request #1414 from BioDataAnalysis/bda_tests_support_empty_eol
Make tests support empty SPDLOG_EOL
2020-02-10 13:12:07 +02:00
Mario Emmenlauer
87acec6a91 Make tests support empty SPDLOG_EOL with new helper method require_message_count() 2020-02-10 11:26:48 +01:00
Mario Emmenlauer
58a5e654f9 tests/utils.cpp: Use binary mode for reading test results, to preserve EOL 2020-02-10 11:26:48 +01:00
Gabi Melman
e278953191 Update win_eventlog_sink.h 2020-02-10 02:23:25 +02:00
Gabi Melman
573ddf8aec Merge pull request #1424 from dominicpoeschko/patch-2
adding additional log overload to prevent unnecessary fmt::format
2020-02-10 01:31:08 +02:00
gabime
4f32243214 Update comment 2020-02-10 00:54:43 +02:00
gabime
601bdfb1b4 Minor cleanup 2020-02-10 00:53:01 +02:00
gabime
90454a93b2 Update test_eventlog.cpp 2020-02-10 00:37:48 +02:00
gabime
640921cd3f Optimize win_eventlog to avoid string allocation 2020-02-10 00:37:45 +02:00
Gabi Melman
fccee959b1 Merge pull request #1418 from ban-dana/v1.x
Add eventlog_sink for logging to Windows Event Log (local only)
2020-02-09 23:27:00 +02:00
bandana2004
67a8ecf2bf Remove registration and stderr reporting from win_eventlog_sink 2020-02-09 22:59:02 +02:00
bandana2004
d8701890b2 cleanup win_eventlog_sink 2020-02-09 18:12:54 +02:00
bandana2004
2435f46d06 fix registry key handle leak in win_eventlog_sink::add_registry_info 2020-02-09 18:04:38 +02:00
bandana2004
4bece787c8 Refactor Event Log sink 2020-02-09 15:12:30 +02:00
Gabi Melman
d4ce938679 Merge pull request #1423 from dominicpoeschko/patch-1
Properly handling SPDLOG_PREVENT_CHILD_FD
2020-02-08 14:05:38 +02:00
dominic
033fe9f133 Properly handling SPDLOG_PREVENT_CHILD_FD
Removed check for posix version so that missing O_CLOEXEC leads to
compiler error.

Removed extra function since it hat no real purpose anymore.

Error behavior between Windows and Unix now equivalent.
2020-02-08 12:15:05 +01:00
dominicpoeschko
25b10dc264 additional log overload
calling log with a string_view as msg called
```
 template<typename... Args>
    void log(source_loc loc, level::level_enum lvl, string_view_t fmt, const Args &... args)
```

instead of
```
template<class T, typename std::enable_if<std::is_convertible<const T &, spdlog::string_view_t>::value, T>::type * = nullptr>
    void log(source_loc loc, level::level_enum lvl, const T &msg)
```

which lead to an unnecessary call to fmt::format
2020-02-08 11:11:04 +01:00
dominicpoeschko
a9c3630d1b Properly handling SPDLOG_PREVENT_CHILD_FD
Using the SPDLOG_PREVENT_CHILD_FD option there where still a race when
a other thread was using fork and exec in between the call to fopen and fcntl.

Using open and O_CLOEXEC when possible prevents this race.

I have no idea if this problem  exists on Windows.
2020-02-07 17:59:11 +01:00
Gabi Melman
f3d99f41d4 Merge pull request #1422 from mr-c/post_installation_testing
Enable running the tests against an installed copy of spdlog
2020-02-07 18:23:50 +02:00
Gabi Melman
fdb46b857f Merge pull request #1407 from vitlav/fix-GNUInstallDirs-using
CMakeLists.txt: put GNUInstallDirs after set project language
2020-02-07 18:22:03 +02:00
Michael R. Crusoe
8d06df9775 Enable building & running the example against an installed copy of spdlog 2020-02-07 16:09:20 +01:00
Michael R. Crusoe
a8d6e60ec6 Enable running the tests against an installed copy of spdlog 2020-02-07 15:39:26 +01:00
Gabi Melman
4e643fa42c Merge pull request #1421 from tt4g/ctest-run-serial
Don't run spdlog-utests and spdlog-utests-ho in parallel
2020-02-07 15:49:25 +02:00
tt4g
eb234bbf91 Don't run spdlog-utests and spdlog-utests-ho in parallel
spdlog-utests and spdlog-utests-ho use the same log output directory, so some tests will randomly fail when run in parallel.
2020-02-07 22:20:54 +09:00
bandana2004
db1a221427 Add eventlog_sink for logging to Windows Event Log (local only). 2020-02-07 15:04:47 +02:00
Gabi Melman
5378f35239 Merge pull request #1417 from BioDataAnalysis/bda_fix_trivial_issue_in_test_utils
tests/utils.h: removed duplicate entry for count_lines()
2020-02-07 12:49:13 +02:00
Mario Emmenlauer
622f5eb967 tests/utils.h: removed duplicate entry for count_lines() 2020-02-07 10:22:58 +01:00
Gabi Melman
966d827d35 Update LICENSE 2020-02-03 22:32:17 +02:00
Gabi Melman
bed56d3e52 Update LICENSE 2020-02-03 21:12:12 +02:00
Gabi Melman
24173d5ebc Update LICENSE 2020-02-03 21:11:45 +02:00
Gabi Melman
60853b5e54 Update LICENSE 2020-02-02 14:20:44 +02:00
Gabi Melman
da2ff552c5 Update LICENSE 2020-02-02 14:20:24 +02:00
Gabi Melman
742df52236 Update license to add note about fmt lib dependecy 2020-02-02 14:16:57 +02:00
Vitaly Lipatov
1b4621962f CMakeLists.txt: put GNUInstallDirs after set project language 2020-01-30 14:51:42 +03:00
Gabi Melman
0a36828ff3 Merge pull request #1394 from ruoshui1314/v1.x
add max files for rotating days
2020-01-19 12:34:57 +02:00
weiy
85ea4297b9 update method name 2020-01-19 17:29:17 +08:00
weiy
34cc3419fa add max files for rotating days 2020-01-19 13:52:01 +08:00
weiy
46fcd2e844 add max files for rotating days 2020-01-19 13:46:43 +08:00
Gabi Melman
23f0cdf901 Merge pull request #1389 from XNephila/support-on-MacPorts
latest spdlog now available on MacPorts
2020-01-14 01:06:42 +02:00
X Nephila
26bdf66659 latest spdlog now available on MacPorts
I am the maintainer of spdlog on MacPorts, now the latest version(1.4.2) of spdlog is available on MacPorts.
2020-01-14 07:04:27 +08:00
gabime
5bf99dfd61 Renamed loaders.cpp to cfg.cpp 2019-12-22 22:51:52 +02:00
gabime
bc42415ceb Updated fmt.cpp to 6.1.2 2019-12-22 22:51:12 +02:00
gabime
284e6a80ac Fixed cfg tests 2019-12-22 22:33:19 +02:00
gabime
0243882238 Updated example 2019-12-22 20:58:16 +02:00
gabime
877eee408e renamed loaders with cfg 2019-12-22 20:40:19 +02:00
gabime
8dd54de326 Merge remote-tracking branch 'origin/v1.x' into conf-env3 2019-12-22 20:29:31 +02:00
Gabi Melman
2544fca519 Update test_loaders.cpp 2019-12-22 08:58:36 +02:00
Gabi Melman
0b55e2c332 Update test_loaders.cpp 2019-12-22 00:17:32 +02:00
gabime
b105046202 Fixed loaders tests 2019-12-21 20:15:18 +02:00
gabime
de20255c71 wip 2019-12-21 20:01:25 +02:00
gabime
1a1c37db7c wip 2019-12-21 19:59:23 +02:00
gabime
a87700a28c wip 2019-12-21 19:52:37 +02:00
gabime
1f8e9ad0fc renamed cfg namespace to loaders 2019-12-21 19:45:14 +02:00
gabime
e13e978af4 added loaders tests 2019-12-21 19:25:54 +02:00
gabime
28e334c728 Fixed tests 2019-12-21 18:52:35 +02:00
gabime
15a9427112 wip 2019-12-21 18:08:42 +02:00
gabime
010b0e1d75 wip 2019-12-21 17:58:31 +02:00
gabime
cd5ddca00d wip 2019-12-21 17:55:30 +02:00
gabime
f18e1fccfd cfg.cpp 2019-12-21 17:49:34 +02:00
gabime
773b8c5a54 refectored file names 2019-12-21 17:47:02 +02:00
gabime
fc3d18ed64 format 2019-12-21 15:54:42 +02:00
gabime
68ed281461 Refactored to cfg::log_levels class 2019-12-21 15:54:09 +02:00
gabime
65ada37399 refactor and and support for init from argv 2019-12-21 13:29:03 +02:00
gabime
9f539d7028 Merge branch 'v1.x' into conf-env2 2019-12-21 11:58:13 +02:00
gabime
0dfb1d264e removed include 2019-12-13 18:32:37 +02:00
gabime
a056b9115b clang-format 2019-12-13 18:26:36 +02:00
gabime
62ecc04212 Merge branch 'v1.x' into conf-env2 2019-12-13 18:25:24 +02:00
gabime
4a0f4fc186 keep clang-tidy happy 2019-12-13 17:55:39 +02:00
gabime
3a61dcd360 Aceept also 'warn' string in level::from_str() 2019-12-13 16:22:20 +02:00
gabime
04d0240f8d updated example 2019-12-13 16:17:09 +02:00
gabime
13ebfc0779 rename function 2019-12-13 16:00:55 +02:00
gabime
d70d5aa9d8 Merge branch 'conf-env2' of https://github.com/gabime/spdlog into conf-env2 2019-12-13 15:54:46 +02:00
gabime
70d3c2cd3e so::getenv - Always empty string under windows uwp since it's not supported 2019-12-13 15:50:27 +02:00
gabime
6fbe0dec2c Throw if trying to getenv under windows uwp 2019-12-13 15:35:37 +02:00
gabime
9d3591dcd5 refactor 2019-12-13 15:22:21 +02:00
gabime
8992f36fbf refactor env-inl 2019-12-13 15:14:42 +02:00
gabime
3d203aa7c4 Simplify and removed SPDLOG_PATTERN support 2019-12-13 14:49:41 +02:00
gabime
cd8d7e6de9 wip 2019-12-13 12:44:11 +02:00
gabime
5d4e6f17ee wip 2019-12-13 01:35:50 +02:00
gabime
49f707ec93 wip 2019-12-13 01:24:34 +02:00
gabime
6a305df46d wip 2019-12-13 01:17:29 +02:00
gabime
35e9482574 wip 2019-12-13 01:14:38 +02:00
gabime
dac61d4e9c merge 2019-12-13 00:14:33 +02:00
gabime
4fa463dff6 Merge branch 'v1.x' into conf-env2 2019-12-13 00:09:36 +02:00
gabime
8d9d9899b7 wip 2019-12-12 20:00:18 +02:00
gabime
cff7448fb2 Merge branch 'v1.x' into conf-env2 2019-12-11 01:28:24 +02:00
gabime
3812c22f86 Merge remote-tracking branch 'origin/v1.x' into conf-env2 2019-12-11 01:25:19 +02:00
gabime
2b3000dddc wip 2019-12-11 01:24:51 +02:00
gabime
b278baf94e wip 2019-12-11 00:42:00 +02:00
gabime
4119b72d50 clang-format 2019-12-11 00:13:53 +02:00
gabime
da2c15ecb4 wip config from env 2019-12-11 00:13:27 +02:00
gabime
d0ed873ab6 wip 2019-12-08 17:09:17 +02:00
gabime
0f24399887 wip 2019-12-08 17:08:20 +02:00
gabime
abbbda6f74 wip 2019-12-08 17:07:52 +02:00
gabime
1a5ee7ab83 Merge remote-tracking branch 'origin/v1.x' into conf-env2 2019-12-08 14:17:59 +02:00
gabime
4d41fdf0fc Wip 2019-12-08 13:35:15 +02:00
119 changed files with 4125 additions and 2129 deletions

View File

@@ -1,19 +1,32 @@
Checks: '\
cppcoreguidelines-*,\
performance-*,\
-performance-unnecessary-value-param,\
modernize-*,\
-modernize-use-trailing-return-type,\
google-*,\
-google-runtime-references,\
misc-*,\
-misc-non-private-member-variables-in-classes,\
cert-*,\
readability-*,\
clang-analyzer-*'
Checks: 'cppcoreguidelines-*,
performance-*,
modernize-*,
google-*,
misc-*,
cert-*,
readability-*,
clang-analyzer-*,
-performance-unnecessary-value-param,
-modernize-use-trailing-return-type,
-google-runtime-references,
-misc-non-private-member-variables-in-classes,
-readability-braces-around-statements,
-google-readability-braces-around-statements,
-cppcoreguidelines-avoid-magic-numbers,
-readability-magic-numbers,
-readability-magic-numbers,
-cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
-cppcoreguidelines-avoid-c-arrays,
-modernize-avoid-c-arrays,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
-readability-named-parameter,
-cert-env33-c
'
WarningsAsErrors: ''
HeaderFilterRegex: 'async.h|async_logger.h|common.h|details|formatter.h|logger.h|sinks|spdlog.h|tweakme.h|version.h'
HeaderFilterRegex: '*spdlog/[^f].*'
AnalyzeTemporaryDtors: false
FormatStyle: none

View File

@@ -30,15 +30,16 @@ addons: &clang35
- ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.5
# Clang 7.0
addons: &clang70
addons: &clang10
apt:
packages:
- clang-7
- clang-10
- lldb-10
- lld-10
sources:
- ubuntu-toolchain-r-test
- llvm-toolchain-trusty-7
- sourceline: "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main"
key_url: "https://apt.llvm.org/llvm-snapshot.gpg.key"
matrix:
@@ -65,17 +66,20 @@ matrix:
os: linux
addons: *clang35
# Test clang-7.0: C++11, Build=Debug, ASAN=On
- env: CLANG_VERSION=7 BUILD_TYPE=Debug CPP=11 ASAN=On TSAN=Off
dist: bionic
- env: CLANG_VERSION=7 BUILD_TYPE=Release CPP=11 ASAN=On TSAN=Off
dist: bionic
# osx
- env: BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=Off
os: osx
# Test clang-10.0: C++11, Build=Debug/Release
- env: CLANG_VERSION=10 BUILD_TYPE=Debug CPP=11
os: linux
dist: bionic
addons: *clang10
- env: CLANG_VERSION=10 BUILD_TYPE=Release CPP=11 ASAN=On
os: linux
dist: bionic
addons: *clang10
before_script:
@@ -97,6 +101,7 @@ script:
-DCMAKE_CXX_STANDARD=$CPP \
-DSPDLOG_BUILD_EXAMPLE=ON \
-DSPDLOG_BUILD_EXAMPLE_HO=ON \
-DSPDLOG_ENABLE_WARNINGS=ON \
-DSPDLOG_BUILD_BENCH=OFF \
-DSPDLOG_BUILD_TESTS=ON \
-DSPDLOG_BUILD_TESTS_HO=OFf \
@@ -104,8 +109,4 @@ script:
- make VERBOSE=1 -j2
- ctest -j2 --output-on-failure
notifications:
email: false

View File

@@ -1,65 +1,75 @@
# Copyright(c) 2019 spdlog authors
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
cmake_minimum_required(VERSION 3.2)
#---------------------------------------------------------------------------------------
if(${CMAKE_VERSION} VERSION_LESS 3.11)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
else()
cmake_policy(VERSION 3.11)
endif()
enable_language(C)
# ---------------------------------------------------------------------------------------
# Start spdlog project
#---------------------------------------------------------------------------------------
include(GNUInstallDirs)
# ---------------------------------------------------------------------------------------
include(cmake/utils.cmake)
include(cmake/ide.cmake)
spdlog_extract_version()
#---------------------------------------------------------------------------------------
project(spdlog VERSION ${SPDLOG_VERSION} LANGUAGES CXX)
message(STATUS "Build spdlog: ${SPDLOG_VERSION}")
include(GNUInstallDirs)
# ---------------------------------------------------------------------------------------
# Set default build to release
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
if(NOT CMAKE_BUILD_TYPE)
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE)
endif()
project(spdlog VERSION ${SPDLOG_VERSION} LANGUAGES CXX)
message(STATUS "Build spdlog: ${SPDLOG_VERSION}")
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
# Compiler config
#---------------------------------------------------------------------------------------
if (NOT CMAKE_CXX_STANDARD)
# ---------------------------------------------------------------------------------------
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_EXTENSIONS OFF)
if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN")
set(CMAKE_CXX_EXTENSIONS ON)
set(CMAKE_CXX_EXTENSIONS ON)
endif()
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
# Set SPDLOG_MASTER_PROJECT to ON if we are building spdlog
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
# Check if spdlog is being used directly or via add_subdirectory, but allow overriding
if (NOT DEFINED SPDLOG_MASTER_PROJECT)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
if(NOT DEFINED SPDLOG_MASTER_PROJECT)
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(SPDLOG_MASTER_PROJECT ON)
else()
set(SPDLOG_MASTER_PROJECT OFF)
endif()
endif ()
endif()
option(SPDLOG_BUILD_ALL "Build all artifacts" OFF)
# build shared option
if(NOT WIN32)
option(SPDLOG_BUILD_SHARED "Build shared library" OFF)
endif()
option(SPDLOG_BUILD_SHARED "Build shared library" OFF)
# precompiled headers option
option(SPDLOG_ENABLE_PCH "Build static or shared library using precompiled header to speed up compilation time" OFF)
# example options
option(SPDLOG_BUILD_EXAMPLE "Build example" ${SPDLOG_MASTER_PROJECT})
option(SPDLOG_BUILD_EXAMPLE_HO "Build header only example" OFF)
# testing options
option(SPDLOG_BUILD_TESTS "Build tests" ${SPDLOG_MASTER_PROJECT})
option(SPDLOG_BUILD_TESTS "Build tests" OFF)
option(SPDLOG_BUILD_TESTS_HO "Build tests using the header only version" OFF)
# bench options
@@ -68,13 +78,16 @@ option(SPDLOG_BUILD_BENCH "Build benchmarks (Requires https://github.com/google/
# sanitizer options
option(SPDLOG_SANITIZE_ADDRESS "Enable address sanitizer in tests" OFF)
# warning options
option(SPDLOG_BUILD_WARNINGS "Enable compiler warnings" OFF)
# install options
option(SPDLOG_INSTALL "Generate the install target" ${SPDLOG_MASTER_PROJECT})
option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF)
option(SPDLOG_FMT_EXTERNAL_HO "Use external fmt header-only library instead of bundled" OFF)
option(SPDLOG_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF)
if (SPDLOG_FMT_EXTERNAL AND SPDLOG_FMT_EXTERNAL_HO)
if(SPDLOG_FMT_EXTERNAL AND SPDLOG_FMT_EXTERNAL_HO)
message(FATAL_ERROR "SPDLOG_FMT_EXTERNAL and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive")
endif()
@@ -84,36 +97,54 @@ if(WIN32)
option(SPDLOG_WCHAR_FILENAMES "Support wchar filenames" OFF)
endif()
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
option(SPDLOG_CLOCK_COARSE "Use the much faster (but much less accurate) CLOCK_REALTIME_COARSE instead of the regular clock," OFF)
option(SPDLOG_CLOCK_COARSE
"Use the much faster (but much less accurate) CLOCK_REALTIME_COARSE instead of the regular clock," OFF)
endif()
option(SPDLOG_PREVENT_CHILD_FD "Prevent from child processes to inherit log file descriptors" OFF)
option(SPDLOG_NO_THREAD_ID "prevent spdlog from querying the thread id on each log call if thread id is not needed" OFF)
option(SPDLOG_NO_TLS "prevent spdlog from using thread local storage" OFF)
option(SPDLOG_NO_ATOMIC_LEVELS "prevent spdlog from using of std::atomic log levels (use only if your code never modifies log levels concurrently" OFF)
option(
SPDLOG_NO_ATOMIC_LEVELS
"prevent spdlog from using of std::atomic log levels (use only if your code never modifies log levels concurrently"
OFF)
# clang-tidy
if(${CMAKE_VERSION} VERSION_GREATER "3.5")
option(SPDLOG_TIDY "run clang-tidy" OFF)
endif()
if(SPDLOG_TIDY)
set(CMAKE_CXX_CLANG_TIDY "clang-tidy")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
message(STATUS "Enabled clang-tidy")
endif()
find_package(Threads REQUIRED)
message(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
# Static/Shared library (shared not supported in windows yet)
#---------------------------------------------------------------------------------------
set(SPDLOG_SRCS
src/spdlog.cpp
src/stdout_sinks.cpp
src/color_sinks.cpp
src/file_sinks.cpp
src/async.cpp)
# ---------------------------------------------------------------------------------------
set(SPDLOG_SRCS src/spdlog.cpp src/stdout_sinks.cpp src/color_sinks.cpp src/file_sinks.cpp src/async.cpp src/cfg.cpp)
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
list(APPEND SPDLOG_SRCS src/fmt.cpp)
endif()
if (SPDLOG_BUILD_SHARED)
if(WIN32)
message(FATAL_ERROR "spdlog shared lib is not yet supported under windows")
endif()
if(WIN32 AND SPDLOG_BUILD_SHARED)
list(APPEND SPDLOG_SRCS ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
endif()
if(SPDLOG_BUILD_SHARED)
add_library(spdlog SHARED ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
target_compile_definitions(spdlog PUBLIC SPDLOG_SHARED_LIB)
if(WIN32)
target_compile_options(spdlog PUBLIC /wd4251 /wd4275)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY)
endif()
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
target_compile_definitions(spdlog PRIVATE FMT_EXPORT PUBLIC FMT_SHARED)
endif()
else()
add_library(spdlog STATIC ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
endif()
@@ -121,34 +152,36 @@ endif()
add_library(spdlog::spdlog ALIAS spdlog)
target_compile_definitions(spdlog PUBLIC SPDLOG_COMPILED_LIB)
target_include_directories(spdlog PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
target_include_directories(spdlog PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
target_link_libraries(spdlog PUBLIC Threads::Threads)
spdlog_enable_warnings(spdlog)
set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION ${SPDLOG_VERSION_MAJOR})
set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX d)
#---------------------------------------------------------------------------------------
if(COMMAND target_precompile_headers AND SPDLOG_ENABLE_PCH)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/pch.h.in ${PROJECT_BINARY_DIR}/spdlog_pch.h @ONLY)
target_precompile_headers(spdlog PRIVATE ${PROJECT_BINARY_DIR}/spdlog_pch.h)
endif()
# ---------------------------------------------------------------------------------------
# Header only version
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
add_library(spdlog_header_only INTERFACE)
add_library(spdlog::spdlog_header_only ALIAS spdlog_header_only)
target_include_directories(spdlog_header_only INTERFACE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
target_include_directories(spdlog_header_only INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
target_link_libraries(spdlog_header_only INTERFACE Threads::Threads)
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
# Use fmt package if using external fmt
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO)
if (NOT TARGET fmt::fmt)
find_package(fmt REQUIRED)
endif ()
if(NOT TARGET fmt::fmt)
find_package(fmt 5.3.0 REQUIRED)
endif()
target_compile_definitions(spdlog PUBLIC SPDLOG_FMT_EXTERNAL)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FMT_EXTERNAL)
@@ -164,23 +197,23 @@ if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO)
set(PKG_CONFIG_REQUIRES fmt) # add dependency to pkg-config
endif()
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
# Misc definitions according to tweak options
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
if(SPDLOG_WCHAR_SUPPORT)
target_compile_definitions(spdlog PUBLIC SPDLOG_WCHAR_TO_UTF8_SUPPORT)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_WCHAR_TO_UTF8_SUPPORT)
endif()
target_compile_definitions(spdlog PUBLIC SPDLOG_WCHAR_TO_UTF8_SUPPORT)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_WCHAR_TO_UTF8_SUPPORT)
endif()
if(SPDLOG_WCHAR_FILENAMES)
target_compile_definitions(spdlog PUBLIC SPDLOG_WCHAR_FILENAMES)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_WCHAR_FILENAMES)
endif()
if(SPDLOG_WCHAR_FILENAMES)
target_compile_definitions(spdlog PUBLIC SPDLOG_WCHAR_FILENAMES)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_WCHAR_FILENAMES)
endif()
if(SPDLOG_NO_EXCEPTIONS)
target_compile_definitions(spdlog PUBLIC SPDLOG_NO_EXCEPTIONS)
if(SPDLOG_NO_EXCEPTIONS)
target_compile_definitions(spdlog PUBLIC SPDLOG_NO_EXCEPTIONS)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_EXCEPTIONS)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_EXCEPTIONS)
if(NOT MSVC)
target_compile_options(spdlog PRIVATE -fno-exceptions)
@@ -212,30 +245,33 @@ if(SPDLOG_NO_ATOMIC_LEVELS)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_ATOMIC_LEVELS)
endif()
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
# Build binaries
#---------------------------------------------------------------------------------------
if(SPDLOG_BUILD_EXAMPLE OR SPDLOG_BUILD_EXAMPLE_HO)
message(STATUS "Generating examples")
# ---------------------------------------------------------------------------------------
if(SPDLOG_BUILD_EXAMPLE OR SPDLOG_BUILD_EXAMPLE_HO OR SPDLOG_BUILD_ALL)
message(STATUS "Generating example(s)")
add_subdirectory(example)
spdlog_enable_warnings(example)
if(SPDLOG_BUILD_EXAMPLE_HO)
spdlog_enable_warnings(example_header_only)
endif()
endif()
if(SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_TESTS_HO)
if(SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_TESTS_HO OR SPDLOG_BUILD_ALL)
message(STATUS "Generating tests")
enable_testing()
add_subdirectory(tests)
endif()
if(SPDLOG_BUILD_BENCH)
if(SPDLOG_BUILD_BENCH OR SPDLOG_BUILD_ALL)
message(STATUS "Generating benchmarks")
add_subdirectory(bench)
endif()
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
# Install
#---------------------------------------------------------------------------------------
if (SPDLOG_INSTALL)
# ---------------------------------------------------------------------------------------
if(SPDLOG_INSTALL)
message(STATUS "Generating install")
set(project_config_in "${CMAKE_CURRENT_LIST_DIR}/cmake/spdlogConfig.cmake.in")
set(project_config_out "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfig.cmake")
@@ -245,46 +281,44 @@ if (SPDLOG_INSTALL)
set(pkgconfig_install_dir "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc")
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
# Include files
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" PATTERN "fmt/bundled" EXCLUDE)
install(TARGETS spdlog spdlog_header_only EXPORT spdlog DESTINATION "${CMAKE_INSTALL_LIBDIR}")
install(
TARGETS spdlog spdlog_header_only
EXPORT spdlog
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
install(DIRECTORY include/${PROJECT_NAME}/fmt/bundled/
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/fmt/bundled/")
endif()
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
# Install pkg-config file
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
get_target_property(PKG_CONFIG_DEFINES spdlog INTERFACE_COMPILE_DEFINITIONS)
string(REPLACE ";" " -D" PKG_CONFIG_DEFINES "${PKG_CONFIG_DEFINES}")
string(CONCAT PKG_CONFIG_DEFINES "-D" "${PKG_CONFIG_DEFINES}")
configure_file("cmake/${PROJECT_NAME}.pc.in" "${pkg_config}" @ONLY)
install(FILES "${pkg_config}" DESTINATION "${pkgconfig_install_dir}")
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
# Install CMake config files
#---------------------------------------------------------------------------------------
install(EXPORT spdlog
DESTINATION ${export_dest_dir}
NAMESPACE spdlog::
FILE ${config_targets_file})
# ---------------------------------------------------------------------------------------
install(EXPORT spdlog DESTINATION ${export_dest_dir} NAMESPACE spdlog:: FILE ${config_targets_file})
include(CMakePackageConfigHelpers)
configure_file("${project_config_in}" "${project_config_out}" @ONLY)
write_basic_package_version_file("${version_config_file}" COMPATIBILITY SameMajorVersion)
install(FILES
"${project_config_out}"
"${version_config_file}" DESTINATION "${export_dest_dir}")
install(FILES "${project_config_out}" "${version_config_file}" DESTINATION "${export_dest_dir}")
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
# Support creation of installable packages
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
include(cmake/spdlogCPack.cmake)
endif ()
endif()

View File

@@ -20,3 +20,7 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-- NOTE: Third party dependency used by this software --
This software depends on the fmt lib (MIT License),
and users must comply to its license: https://github.com/fmtlib/fmt/blob/master/LICENSE.rst

View File

@@ -1,8 +1,6 @@
# spdlog
Very fast, header-only/compiled, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=master)](https://travis-ci.org/gabime/spdlog)&nbsp; [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true)](https://ci.appveyor.com/project/gabime/spdlog)
Very fast, header-only/compiled, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=v1.x)](https://travis-ci.org/gabime/spdlog)&nbsp; [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true)](https://ci.appveyor.com/project/gabime/spdlog) [![Release](https://img.shields.io/github/release/gabime/spdlog.svg)](https://github.com/gabime/spdlog/releases/latest)
## Install
#### Header only version
@@ -25,20 +23,21 @@ $ cmake .. && make -j
## Package managers:
* Homebrew: `brew install spdlog`
* MacPorts: `sudo port install spdlog`
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
* Fedora: `yum install spdlog`
* Gentoo: `emerge dev-libs/spdlog`
* Arch Linux: `yaourt -S spdlog-git`
* Arch Linux: `pacman -S spdlog`
* vcpkg: `vcpkg install spdlog`
* conan: `spdlog/[>=1.4.1]`
* conda: `conda install -c conda-forge spdlog`
## Features
* Very fast (see [benchmarks](#benchmarks) below).
* Headers only, just copy and use. Or use as a compiled library.
* Headers only or compiled
* Feature rich formatting, using the excellent [fmt](https://github.com/fmtlib/fmt) library.
* **New!** [Backtrace](#backtrace-support) support - store debug messages in a ring buffer and display later on demand.
* Fast asynchronous mode (optional)
* Asynchronous mode (optional)
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
* Multi/Single threaded loggers.
* Various log targets:
@@ -48,8 +47,9 @@ $ cmake .. && make -j
* syslog.
* Windows debugger (```OutputDebugString(..)```)
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
* Severity based filtering - threshold levels can be modified in runtime as well as in compile time.
* Log filtering - log levels can be modified in runtime as well as in compile time.
* Support for loading log levels from argv or from environment var.
* [Backtrace](#backtrace-support) support - store debug messages in a ring buffer and display later on demand.
## Usage samples
@@ -84,8 +84,10 @@ int main()
auto file_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic.txt");
spdlog::set_default_logger(file_logger);
}
```
#### create stdout/stderr logger object
---
#### Create stdout/stderr logger object
```c++
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
@@ -97,6 +99,7 @@ void stdout_example()
spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
}
```
---
#### Basic file logger
```c++
@@ -105,7 +108,7 @@ void basic_logfile_example()
{
try
{
auto my_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
auto logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
}
catch (const spdlog::spdlog_ex &ex)
{
@@ -120,7 +123,9 @@ void basic_logfile_example()
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);
auto max_size = 1048576 * 5;
auto max_files = 3;
auto logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", max_size, max_files);
}
```
@@ -132,7 +137,7 @@ void rotating_example()
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);
auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
}
```
@@ -142,7 +147,8 @@ void daily_example()
```c++
// Loggers can store in a ring buffer all messages (including debug/trace) and display later on demand.
// When needed, call dump_backtrace() to see them
spdlog::enable_backtrace(32); // create ring buffer with capacity of 32 messages
spdlog::enable_backtrace(32); // Store the latest 32 messages in a buffer. Older messages will be dropped.
// or my_logger->enable_backtrace(32)..
for(int i = 0; i < 100; i++)
{
@@ -266,6 +272,37 @@ void user_defined_example()
}
```
---
#### User defined flags in the log pattern
```c++
// Log patterns can contain custom flags.
// the following example will add new flag '%*' - which will be bound to a <my_formatter_flag> instance.
#include "spdlog/pattern_formatter.h"
class my_formatter_flag : public spdlog::custom_flag_formatter
{
public:
void format(const spdlog::details::log_msg &, const std::tm &, spdlog::memory_buf_t &dest) override
{
std::string some_txt = "custom-flag";
dest.append(some_txt.data(), some_txt.data() + some_txt.size());
}
std::unique_ptr<custom_flag_formatter> clone() const override
{
return spdlog::details::make_unique<my_formatter_flag>();
}
};
void custom_flags_example()
{
auto formatter = std::make_unique<spdlog::pattern_formatter>();
formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v");
spdlog::set_formatter(std::move(formatter));
}
```
---
#### Custom error handler
```c++
@@ -277,6 +314,7 @@ void err_handler_example()
}
```
---
#### syslog
```c++
@@ -300,6 +338,28 @@ void android_example()
}
```
---
#### Load log levels from env variable or from argv
```c++
#include "spdlog/cfg/env.h"
int main (int argc, char *argv[])
{
spdlog::cfg::load_env_levels();
// or from command line:
// ./example SPDLOG_LEVEL=info,mylogger=trace
// #include "spdlog/cfg/argv.h" // for loading levels from argv
// spdlog::cfg::load_argv_levels(argc, argv);
}
```
So then you can:
```console
$ export SPDLOG_LEVEL=info,mylogger=trace
$ ./example
```
---
## Benchmarks
Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/bench.cpp) done in Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
@@ -328,7 +388,7 @@ Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/ben
[info] daily_mt Elapsed: 0.61 secs 1,638,305/sec
[info] null_mt Elapsed: 0.16 secs 6,272,758/sec
```
#### ASynchronous mode
#### Asynchronous mode
```
[info] -------------------------------------------------
[info] Messages : 1,000,000
@@ -355,3 +415,9 @@ Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/ben
## Documentation
Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages.
---
Thanks to [JetBrains](https://www.jetbrains.com/?from=spdlog) for donating licenses to their products to help developing **spdlog** <a href="https://www.jetbrains.com/?from=spdlog"><img src="logos/jetbrains-variant-4.svg" width="94" align="center" /></a>

View File

@@ -2,37 +2,50 @@ version: 1.0.{build}
image: Visual Studio 2017
environment:
matrix:
- GENERATOR: '"Visual Studio 14 2015"'
BUILD_TYPE: Debug
WCHAR: 'OFF'
- GENERATOR: '"Visual Studio 14 2015"'
BUILD_TYPE: Release
WCHAR: 'ON'
- GENERATOR: '"Visual Studio 14 2015 Win64"'
BUILD_TYPE: Debug
WCHAR: 'ON'
- GENERATOR: '"Visual Studio 14 2015 Win64"'
BUILD_TYPE: Release
WCHAR: 'ON'
- GENERATOR: '"Visual Studio 15 2017 Win64"'
BUILD_TYPE: Debug
WCHAR: 'ON'
- GENERATOR: '"Visual Studio 15 2017 Win64"'
BUILD_TYPE: Release
WCHAR: 'OFf'
- GENERATOR: '"Visual Studio 14 2015"'
BUILD_TYPE: Debug
WCHAR: 'OFF'
BUILD_SHARED: 'OFF'
- GENERATOR: '"Visual Studio 14 2015"'
BUILD_TYPE: Release
WCHAR: 'ON'
BUILD_SHARED: 'OFF'
- GENERATOR: '"Visual Studio 14 2015 Win64"'
BUILD_TYPE: Debug
WCHAR: 'ON'
BUILD_SHARED: 'OFF'
- GENERATOR: '"Visual Studio 14 2015 Win64"'
BUILD_TYPE: Release
WCHAR: 'ON'
BUILD_SHARED: 'OFF'
- GENERATOR: '"Visual Studio 15 2017 Win64"'
BUILD_TYPE: Debug
WCHAR: 'ON'
BUILD_SHARED: 'OFF'
- GENERATOR: '"Visual Studio 15 2017 Win64"'
BUILD_TYPE: Release
WCHAR: 'OFF'
BUILD_SHARED: 'OFF'
- GENERATOR: '"Visual Studio 15 2017 Win64"'
BUILD_TYPE: Release
WCHAR: 'OFF'
BUILD_SHARED: 'ON'
build_script:
- cmd: >-
set
mkdir build
cd build
- cmd: >-
set
set PATH=%PATH%:C:\Program Files\Git\usr\bin
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DSPDLOG_WCHAR_SUPPORT=%WCHAR% -DSPDLOG_BUILD_EXAMPLE=ON -DSPDLOG_BUILD_EXAMPLE_HO=ON -DSPDLOG_BUILD_TESTS=ON -DSPDLOG_BUILD_TESTS_HO=OFF
mkdir build
cmake --build . --config %BUILD_TYPE%
cd build
set PATH=%PATH%;C:\Program Files\Git\usr\bin
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DSPDLOG_WCHAR_SUPPORT=%WCHAR% -DSPDLOG_BUILD_SHARED=%BUILD_SHARED% -DSPDLOG_BUILD_EXAMPLE=ON -DSPDLOG_BUILD_EXAMPLE_HO=ON -DSPDLOG_BUILD_TESTS=ON -DSPDLOG_BUILD_TESTS_HO=OFF -DSPDLOG_ENABLE_WARNINGS=ON
cmake --build . --config %BUILD_TYPE%
before_test:
- set PATH=%PATH%;C:\projects\spdlog\build\%BUILD_TYPE%
test_script:
- ctest -VV -C "%BUILD_TYPE%"
- C:\projects\spdlog\build\tests\%BUILD_TYPE%\spdlog-utests.exe

View File

@@ -1,12 +1,11 @@
# Copyright(c) 2019 spdlog authors
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
cmake_minimum_required(VERSION 3.1)
project(spdlog_bench CXX)
if(NOT TARGET spdlog)
# Stand-alone build
find_package(spdlog CONFIG REQUIRED)
# Stand-alone build
find_package(spdlog CONFIG REQUIRED)
endif()
find_package(Threads REQUIRED)

View File

@@ -19,21 +19,17 @@
#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);
void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log);
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log);
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, size_t thread_count);
static size_t file_size = 30 * 1024 * 1024;
static size_t rotating_files = 5;
// void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log);
// void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log);
void bench_threaded_logging(int threads, int iters)
static const size_t file_size = 30 * 1024 * 1024;
static const size_t rotating_files = 5;
static const int max_threads = 1000;
void bench_threaded_logging(size_t threads, int iters)
{
spdlog::info("**************************************************************");
spdlog::info("Multi threaded: {:n} threads, {:n} messages", threads, iters);
@@ -111,14 +107,23 @@ int main(int argc, char *argv[])
spdlog::set_automatic_registration(false);
spdlog::default_logger()->set_pattern("[%^%l%$] %v");
int iters = 250000;
int threads = 4;
size_t threads = 4;
try
{
if (argc > 1)
iters = atoi(argv[1]);
{
iters = std::stoi(argv[1]);
}
if (argc > 2)
threads = atoi(argv[2]);
{
threads = std::stoul(argv[2]);
}
if (threads > max_threads)
{
throw std::runtime_error(fmt::format("Number of threads exceeds maximum({}})", max_threads));
}
bench_single_threaded(iters);
bench_threaded_logging(1, iters);
@@ -134,7 +139,10 @@ int main(int argc, char *argv[])
void bench(int howmany, std::shared_ptr<spdlog::logger> log)
{
using std::chrono::duration;
using std::chrono::duration_cast;
using std::chrono::high_resolution_clock;
auto start = high_resolution_clock::now();
for (auto i = 0; i < howmany; ++i)
{
@@ -148,19 +156,23 @@ void bench(int howmany, std::shared_ptr<spdlog::logger> log)
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, size_t thread_count)
{
using std::chrono::duration;
using std::chrono::duration_cast;
using std::chrono::high_resolution_clock;
vector<thread> threads;
std::vector<std::thread> threads;
threads.reserve(thread_count);
auto start = high_resolution_clock::now();
for (int t = 0; t < thread_count; ++t)
for (size_t t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]() {
for (int j = 0; j < howmany / thread_count; j++)
threads.emplace_back([&]() {
for (int j = 0; j < howmany / static_cast<int>(thread_count); j++)
{
log->info("Hello logger: msg number {}", j);
}
}));
});
}
for (auto &t : threads)
@@ -174,9 +186,13 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count
spdlog::drop(log->name());
}
/*
void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log)
{
using std::chrono::high_resolution_clock;
using std::chrono::duration;
using std::chrono::duration_cast;
auto orig_default = spdlog::default_logger();
spdlog::set_default_logger(log);
auto start = high_resolution_clock::now();
@@ -194,18 +210,22 @@ void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log)
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
{
using std::chrono::high_resolution_clock;
using std::chrono::duration;
using std::chrono::duration_cast;
const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus "
"lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus volutpat mi, eu consequat sem "
"libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam non dapibus eros. Donec fringilla dui sed "
"augue pretium, nec scelerisque est maximus. Nullam convallis, sem nec blandit maximus, nisi turpis ornare "
"nisl, sit amet volutpat neque massa eu odio. Maecenas malesuada quam ex, posuere congue nibh turpis duis.";
using std::chrono::high_resolution_clock;
auto orig_default = spdlog::default_logger();
spdlog::set_default_logger(log);
auto start = high_resolution_clock::now();
for (auto i = 0; i < howmany; ++i)
{
spdlog::log(level::info, msg);
spdlog::log(spdlog::level::info, msg);
}
auto delta = high_resolution_clock::now() - start;
@@ -214,3 +234,5 @@ void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
spdlog::set_default_logger(std::move(orig_default));
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
}
*/

View File

@@ -6,7 +6,7 @@
#include "benchmark/benchmark.h"
#include "spdlog/spdlog.h"
#include "spdlog/details/pattern_formatter.h"
#include "spdlog/pattern_formatter.h"
void bench_formatter(benchmark::State &state, std::string pattern)
{

View File

@@ -51,11 +51,21 @@ void bench_disabled_macro(benchmark::State &state, std::shared_ptr<spdlog::logge
}
}
#ifdef __linux__
void bench_dev_null()
{
auto dev_null_st = spdlog::basic_logger_st("/dev/null_st", "/dev/null");
benchmark::RegisterBenchmark("/dev/null_st", bench_logger, std::move(dev_null_st))->UseRealTime();
spdlog::drop("/dev/null_st");
auto dev_null_mt = spdlog::basic_logger_mt("/dev/null_mt", "/dev/null");
benchmark::RegisterBenchmark("/dev/null_mt", bench_logger, std::move(dev_null_mt))->UseRealTime();
spdlog::drop("/dev/null_mt");
}
#endif // __linux__
int main(int argc, char *argv[])
{
using spdlog::sinks::basic_file_sink_mt;
using spdlog::sinks::basic_file_sink_st;
using spdlog::sinks::null_sink_mt;
using spdlog::sinks::null_sink_st;
@@ -63,6 +73,8 @@ int main(int argc, char *argv[])
size_t rotating_files = 5;
int n_threads = benchmark::CPUInfo::Get().num_cpus;
auto full_bench = argc > 1 && std::string(argv[1]) == "full";
// disabled loggers
auto disabled_logger = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>());
disabled_logger->set_level(spdlog::level::off);
@@ -81,54 +93,61 @@ int main(int argc, char *argv[])
tracing_null_logger_st->enable_backtrace(64);
benchmark::RegisterBenchmark("null_sink_st/backtrace", bench_logger, tracing_null_logger_st);
// basic_st
auto basic_st = spdlog::basic_logger_st("basic_st", "latency_logs/basic_st.log", true);
benchmark::RegisterBenchmark("basic_st", bench_logger, std::move(basic_st))->UseRealTime();
spdlog::drop("basic_st");
// with backtrace of 64
auto tracing_basic_st = spdlog::basic_logger_st("tracing_basic_st", "latency_logs/tracing_basic_st.log", true);
tracing_basic_st->enable_backtrace(64);
benchmark::RegisterBenchmark("basic_st/backtrace", bench_logger, std::move(tracing_basic_st))->UseRealTime();
spdlog::drop("tracing_basic_st");
#ifdef __linux
bench_dev_null();
#endif // __linux__
// rotating st
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "latency_logs/rotating_st.log", file_size, rotating_files);
benchmark::RegisterBenchmark("rotating_st", bench_logger, std::move(rotating_st))->UseRealTime();
spdlog::drop("rotating_st");
// with backtrace of 64
auto tracing_rotating_st =
spdlog::rotating_logger_st("tracing_rotating_st", "latency_logs/tracing_rotating_st.log", file_size, rotating_files);
benchmark::RegisterBenchmark("rotating_st/backtrace", bench_logger, std::move(tracing_rotating_st))->UseRealTime();
spdlog::drop("tracing_rotating_st");
if (full_bench)
{
// basic_st
auto basic_st = spdlog::basic_logger_st("basic_st", "latency_logs/basic_st.log", true);
benchmark::RegisterBenchmark("basic_st", bench_logger, std::move(basic_st))->UseRealTime();
spdlog::drop("basic_st");
// with backtrace of 64
auto tracing_basic_st = spdlog::basic_logger_st("tracing_basic_st", "latency_logs/tracing_basic_st.log", true);
tracing_basic_st->enable_backtrace(64);
benchmark::RegisterBenchmark("basic_st/backtrace", bench_logger, std::move(tracing_basic_st))->UseRealTime();
spdlog::drop("tracing_basic_st");
// daily st
auto daily_st = spdlog::daily_logger_mt("daily_st", "latency_logs/daily_st.log");
benchmark::RegisterBenchmark("daily_st", bench_logger, std::move(daily_st))->UseRealTime();
spdlog::drop("daily_st");
auto tracing_daily_st = spdlog::daily_logger_mt("tracing_daily_st", "latency_logs/daily_st.log");
benchmark::RegisterBenchmark("daily_st/backtrace", bench_logger, std::move(tracing_daily_st))->UseRealTime();
spdlog::drop("tracing_daily_st");
// rotating st
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "latency_logs/rotating_st.log", file_size, rotating_files);
benchmark::RegisterBenchmark("rotating_st", bench_logger, std::move(rotating_st))->UseRealTime();
spdlog::drop("rotating_st");
// with backtrace of 64
auto tracing_rotating_st =
spdlog::rotating_logger_st("tracing_rotating_st", "latency_logs/tracing_rotating_st.log", file_size, rotating_files);
benchmark::RegisterBenchmark("rotating_st/backtrace", bench_logger, std::move(tracing_rotating_st))->UseRealTime();
spdlog::drop("tracing_rotating_st");
//
// Multi threaded bench, 10 loggers using same logger concurrently
//
auto null_logger_mt = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>());
benchmark::RegisterBenchmark("null_sink_mt", bench_logger, null_logger_mt)->Threads(n_threads)->UseRealTime();
// daily st
auto daily_st = spdlog::daily_logger_mt("daily_st", "latency_logs/daily_st.log");
benchmark::RegisterBenchmark("daily_st", bench_logger, std::move(daily_st))->UseRealTime();
spdlog::drop("daily_st");
auto tracing_daily_st = spdlog::daily_logger_mt("tracing_daily_st", "latency_logs/daily_st.log");
benchmark::RegisterBenchmark("daily_st/backtrace", bench_logger, std::move(tracing_daily_st))->UseRealTime();
spdlog::drop("tracing_daily_st");
// basic_mt
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "latency_logs/basic_mt.log", true);
benchmark::RegisterBenchmark("basic_mt", bench_logger, std::move(basic_mt))->Threads(n_threads)->UseRealTime();
spdlog::drop("basic_mt");
//
// Multi threaded bench, 10 loggers using same logger concurrently
//
auto null_logger_mt = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>());
benchmark::RegisterBenchmark("null_sink_mt", bench_logger, null_logger_mt)->Threads(n_threads)->UseRealTime();
// rotating mt
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "latency_logs/rotating_mt.log", file_size, rotating_files);
benchmark::RegisterBenchmark("rotating_mt", bench_logger, std::move(rotating_mt))->Threads(n_threads)->UseRealTime();
spdlog::drop("rotating_mt");
// basic_mt
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "latency_logs/basic_mt.log", true);
benchmark::RegisterBenchmark("basic_mt", bench_logger, std::move(basic_mt))->Threads(n_threads)->UseRealTime();
spdlog::drop("basic_mt");
// daily mt
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "latency_logs/daily_mt.log");
benchmark::RegisterBenchmark("daily_mt", bench_logger, std::move(daily_mt))->Threads(n_threads)->UseRealTime();
spdlog::drop("daily_mt");
// rotating mt
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "latency_logs/rotating_mt.log", file_size, rotating_files);
benchmark::RegisterBenchmark("rotating_mt", bench_logger, std::move(rotating_mt))->Threads(n_threads)->UseRealTime();
spdlog::drop("rotating_mt");
// daily mt
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "latency_logs/daily_mt.log");
benchmark::RegisterBenchmark("daily_mt", bench_logger, std::move(daily_mt))->Threads(n_threads)->UseRealTime();
spdlog::drop("daily_mt");
}
// async
auto queue_size = 1024 * 1024 * 3;

View File

@@ -1,14 +0,0 @@
benchmark = dependency('benchmark')
bench_matrix = [
['bench', [spdlog_dep], []],
['async_bench', [spdlog_dep], []],
['formatter-bench', [spdlog_dep, benchmark], ['all']],
['latency', [spdlog_dep, benchmark], []],
]
foreach i : bench_matrix
bench_exe = executable(i[0], i[0] + '.cpp', dependencies: i[1])
benchmark('bench_' + i[0], bench_exe, args: i[2])
endforeach

View File

@@ -1,6 +1,6 @@
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
# IDE support for headers
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
set(SPDLOG_HEADERS_DIR "${CMAKE_CURRENT_LIST_DIR}/../include")
file(GLOB SPDLOG_TOP_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/*.h")
@@ -8,11 +8,11 @@ file(GLOB SPDLOG_DETAILS_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/details/*.h")
file(GLOB SPDLOG_SINKS_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/sinks/*.h")
file(GLOB SPDLOG_FMT_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/fmt/*.h")
file(GLOB SPDLOG_FMT_BUNDELED_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/fmt/bundled/*.h")
set(SPDLOG_ALL_HEADERS ${SPDLOG_TOP_HEADERS} ${SPDLOG_DETAILS_HEADERS} ${SPDLOG_SINKS_HEADERS} ${SPDLOG_FMT_HEADERS} ${SPDLOG_FMT_BUNDELED_HEADERS})
set(SPDLOG_ALL_HEADERS ${SPDLOG_TOP_HEADERS} ${SPDLOG_DETAILS_HEADERS} ${SPDLOG_SINKS_HEADERS} ${SPDLOG_FMT_HEADERS}
${SPDLOG_FMT_BUNDELED_HEADERS})
source_group("Header Files\\spdlog" FILES ${SPDLOG_TOP_HEADERS})
source_group("Header Files\\spdlog\\details" FILES ${SPDLOG_DETAILS_HEADERS})
source_group("Header Files\\spdlog\\sinks" FILES ${SPDLOG_SINKS_HEADERS})
source_group("Header Files\\spdlog\\fmt" FILES ${SPDLOG_FMT_HEADERS})
source_group("Header Files\\spdlog\\fmt\\bundled\\" FILES ${SPDLOG_FMT_BUNDELED_HEADERS})

258
cmake/pch.h.in Normal file
View File

@@ -0,0 +1,258 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
// details/pattern_formatter-inl.h
// fmt/bin_to_hex.h
// fmt/bundled/format-inl.h
#include <cctype>
// details/file_helper-inl.h
// details/os-inl.h
// fmt/bundled/core.h
// fmt/bundled/posix.h
// logger-inl.h
// sinks/daily_file_sink.h
// sinks/stdout_sinks.h
#include <cstdio>
// details/os-inl.h
// fmt/bundled/posix.h
#include <cstdlib>
// details/os-inl.h
// details/pattern_formatter-inl.h
// fmt/bundled/core.h
// fmt/bundled/format-inl.h
#include <cstring>
// details/os-inl.h
// details/os.h
// details/pattern_formatter-inl.h
// details/pattern_formatter.h
// fmt/bundled/chrono.h
// sinks/daily_file_sink.h
// sinks/rotating_file_sink-inl.h
#include <ctime>
// fmt/bundled/format-inl.h
#include <climits>
// fmt/bundled/format-inl.h
#include <cwchar>
// fmt/bundled/format-inl.h
// fmt/bundled/format.h
#include <cmath>
// fmt/bundled/format-inl.h
#include <cstdarg>
// details/file_helper-inl.h
// fmt/bundled/format.h
// fmt/bundled/posix.h
// sinks/rotating_file_sink-inl.h
#include <cerrno>
// details/circular_q.h
// details/thread_pool-inl.h
// fmt/bundled/format-inl.h
#include <cassert>
// async_logger-inl.h
// cfg/helpers-inl.h
// log_levels.h
// common.h
// details/file_helper-inl.h
// details/log_msg.h
// details/os-inl.h
// details/pattern_formatter-inl.h
// details/pattern_formatter.h
// details/registry-inl.h
// details/registry.h
// details/tcp_client-windows.h
// details/tcp_client.h
// fmt/bundled/core.h
// sinks/android_sink.h
// sinks/ansicolor_sink.h
// sinks/basic_file_sink.h
// sinks/daily_file_sink.h
// sinks/dup_filter_sink.h
// sinks/msvc_sink.h
// sinks/ringbuffer_sink.h
// sinks/rotating_file_sink-inl.h
// sinks/rotating_file_sink.h
// sinks/syslog_sink.h
// sinks/tcp_sink.h
// sinks/win_eventlog_sink.h
// sinks/wincolor_sink.h
// spdlog.h:
#include <string>
// cfg/helpers-inl.h
// fmt/bundled/chrono.h
#include <sstream>
// fmt/bundled/ostream.h
// sinks/ostream_sink.h
#include <ostream>
// cfg/log_levels.h
// details/registry-inl.h
// details/registry.h
#include <unordered_map>
// details/circular_q.h
// details/pattern_formatter-inl.h
// details/pattern_formatter.h
// details/thread_pool.h
// fmt/bundled/compile.h
// logger.h
// sinks/dist_sink.h
// sinks/ringbuffer_sink.h
// sinks/win_eventlog_sink.h
#include <vector>
// details/os-inl.h
// details/pattern_formatter-inl.h
// sinks/ansicolor_sink.h
// sinks/syslog_sink.h
// sinks/systemd_sink.h
// sinks/wincolor_sink.h
#include <array>
// details/file_helper-inl.h
// details/file_helper.h
// sinks/rotating_file_sink-inl.h
#include <tuple>
// details/os-inl.h
// fmt/bundled/format.h
// fmt/bundled/printf.h
#include <limits>
// common.h
// details/backtracer.h
// details/null_mutex.h
#include <atomic>
// common.h
// details/backtracer.h
// details/null_mutex.h
#include <locale>
// common.h
#include <initializer_list>
// common.h
#include <exception>
// common.h
// details/fmt_helper.h
// fmt/bundled/core.h
// fmt/bundled/ranges.h
#include <type_traits>
// cfg/helpers-inl.h
// details/null_mutex.h
// details/pattern_formatter-inl.h
#include <utility>
// async.h
// async_logger-inl.h
// common.h
// details/pattern_formatter-inl.h
// details/pattern_formatter.h
// details/registry-inl.h
// details/registry.h
// details/thread_pool.h
// fmt/bundled/format.h
// sinks/ansicolor_sink.h
// sinks/base_sink-inl.h
// sinks/dist_sink.h
// sinks/stdout_sinks-inl.h
// sinks/wincolor_sink.h
// spdlog.h
#include <memory>
// async.h
// common.h
// details/backtracer.h
// details/periodic_worker.h
// details/registry-inl.h
// details/registry.h
// details/thread_pool.h
// sinks/tcp_sink.h
// spdlog.h
#include <functional>
// details/mpmc_blocking_q.h
// details/periodic_worker.h
#include <condition_variable>
// details/os-inl.h
// fmt/bundled/format.h
// fmt/bundled/printf.h
// sinks/dist_sink.h
#include <algorithm>
// common.h
// details/file_helper-inl.h
// details/fmt_helper.h
// details/os-inl.h
// details/pattern_formatter-inl.h
// details/pattern_formatter.h
// details/periodic_worker.h
// details/registry-inl.h
// details/registry.h
// details/thread_pool.h
// fmt/bundled/chrono.h
// sinks/android_sink.h
// sinks/daily_file_sink.h
// sinks/dup_filter_sink.h
// sinks/rotating_file_sink-inl.h
// sinks/rotating_file_sink.h
// sinks/tcp_sink.h
// spdlog.h
#include <chrono>
// details/file_helper-inl.h
// details/os-inl.h
// details/pattern_formatter-inl.h
// details/periodic_worker.h
// details/thread_pool.h
// sinks/android_sink.h
#include <thread>
// async.h
// details/backtracer.h
// details/console_globals.h
// details/mpmc_blocking_q.h
// details/pattern_formatter-inl.h
// details/periodic_worker.h
// details/registry.h
// sinks/android_sink.h
// sinks/ansicolor_sink.h
// sinks/basic_file_sink.h
// sinks/daily_file_sink.h
// sinks/dist_sink.h
// sinks/dup_filter_sink.h
// sinks/msvc_sink.h
// sinks/null_sink.h
// sinks/ostream_sink.h
// sinks/ringbuffer_sink.h
// sinks/rotating_file_sink-inl.h
// sinks/rotating_file_sink.h
// sinks/tcp_sink.h
// sinks/win_eventlog_sink.h
// sinks/wincolor_sink.h
//
// color_sinks.cpp
// file_sinks.cpp
// spdlog.cpp
// stdout_sinks.cpp
#include <mutex>
// spdlog
#include <spdlog/common.h>

View File

@@ -1,15 +1,7 @@
set(CPACK_GENERATOR
TGZ
ZIP
)
set(CPACK_GENERATOR "TGZ;ZIP" CACHE STRING "Semicolon separated list of generators")
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0)
set(CPACK_INSTALL_CMAKE_PROJECTS
"${CMAKE_BINARY_DIR}"
"${PROJECT_NAME}"
ALL
.
)
set(CPACK_INSTALL_CMAKE_PROJECTS "${CMAKE_BINARY_DIR}" "${PROJECT_NAME}" ALL .)
set(CPACK_PROJECT_URL "https://github.com/gabime/spdlog")
set(CPACK_PACKAGE_VENDOR "Gabi Melman")
@@ -19,14 +11,36 @@ set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
if (PROJECT_VERSION_TWEAK)
if(PROJECT_VERSION_TWEAK)
set(CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION}.${PROJECT_VERSION_TWEAK})
endif ()
set(CPACK_PACKAGE_RELOCATABLE ON)
endif()
set(CPACK_PACKAGE_RELOCATABLE ON CACHE BOOL "Build relocatable package")
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_PACKAGE_GROUP "System Environment/Libraries")
set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries")
set(CPACK_RPM_PACKAGE_URL ${CPACK_PROJECT_URL})
set(CPACK_RPM_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.")
if(CPACK_PACKAGE_NAME)
set(CPACK_RPM_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
else()
set(CPACK_RPM_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}")
endif()
if(CPACK_RPM_PACKAGE_RELEASE)
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}-${CPACK_RPM_PACKAGE_RELEASE}")
endif()
if(CPACK_RPM_PACKAGE_ARCHITECTURE)
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.${CPACK_RPM_PACKAGE_ARCHITECTURE}")
endif()
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.rpm")
if(NOT CPACK_PACKAGE_RELOCATABLE)
# Depend on pkgconfig rpm to create the system pkgconfig folder
set(CPACK_RPM_PACKAGE_REQUIRES pkgconfig)
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION
"${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/pkgconfig")
endif()
include(CPack)

View File

@@ -1,50 +1,61 @@
# Get spdlog version from include/spdlog/version.h and put it in SPDLOG_VERSION
function(spdlog_extract_version)
file(READ "${CMAKE_CURRENT_LIST_DIR}/include/spdlog/version.h" file_contents)
string(REGEX MATCH "SPDLOG_VER_MAJOR ([0-9]+)" _ "${file_contents}")
if(NOT CMAKE_MATCH_COUNT EQUAL 1)
message(FATAL_ERROR "Could not extract major version number from spdlog/version.h")
endif()
set(ver_major ${CMAKE_MATCH_1})
string(REGEX MATCH "SPDLOG_VER_MINOR ([0-9]+)" _ "${file_contents}")
if(NOT CMAKE_MATCH_COUNT EQUAL 1)
message(FATAL_ERROR "Could not extract minor version number from spdlog/version.h")
endif()
set(ver_minor ${CMAKE_MATCH_1})
string(REGEX MATCH "SPDLOG_VER_PATCH ([0-9]+)" _ "${file_contents}")
if(NOT CMAKE_MATCH_COUNT EQUAL 1)
message(FATAL_ERROR "Could not extract patch version number from spdlog/version.h")
endif()
set(ver_patch ${CMAKE_MATCH_1})
set(SPDLOG_VERSION_MAJOR ${ver_major} PARENT_SCOPE)
set (SPDLOG_VERSION "${ver_major}.${ver_minor}.${ver_patch}" PARENT_SCOPE)
endfunction()
# Turn on warnings on the given target
function(spdlog_enable_warnings target_name)
target_compile_options(${target_name} PRIVATE
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
-Wall -Wextra -Wconversion -pedantic -Wfatal-errors>
$<$<CXX_COMPILER_ID:MSVC>:/W4>)
if(MSVC_VERSION GREATER 1900) #Allow non fatal security wanrnings for msvc 2015
target_compile_options(${target_name} PRIVATE /WX)
endif()
endfunction()
# Enable address sanitizer (gcc/clang only)
function(spdlog_enable_sanitizer target_name)
if (NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
message(FATAL_ERROR "Sanitizer supported only for gcc/clang")
endif()
message(STATUS "Address sanitizer enabled")
target_compile_options(${target_name} PRIVATE -fsanitize=address,undefined)
target_compile_options(${target_name} PRIVATE -fno-sanitize=signed-integer-overflow)
target_compile_options(${target_name} PRIVATE -fno-sanitize-recover=all)
target_compile_options(${target_name} PRIVATE -fno-omit-frame-pointer)
target_link_libraries(${target_name} PRIVATE -fsanitize=address,undefined -fuse-ld=gold)
endfunction()
# Get spdlog version from include/spdlog/version.h and put it in SPDLOG_VERSION
function(spdlog_extract_version)
file(READ "${CMAKE_CURRENT_LIST_DIR}/include/spdlog/version.h" file_contents)
string(REGEX MATCH "SPDLOG_VER_MAJOR ([0-9]+)" _ "${file_contents}")
if(NOT CMAKE_MATCH_COUNT EQUAL 1)
message(FATAL_ERROR "Could not extract major version number from spdlog/version.h")
endif()
set(ver_major ${CMAKE_MATCH_1})
string(REGEX MATCH "SPDLOG_VER_MINOR ([0-9]+)" _ "${file_contents}")
if(NOT CMAKE_MATCH_COUNT EQUAL 1)
message(FATAL_ERROR "Could not extract minor version number from spdlog/version.h")
endif()
set(ver_minor ${CMAKE_MATCH_1})
string(REGEX MATCH "SPDLOG_VER_PATCH ([0-9]+)" _ "${file_contents}")
if(NOT CMAKE_MATCH_COUNT EQUAL 1)
message(FATAL_ERROR "Could not extract patch version number from spdlog/version.h")
endif()
set(ver_patch ${CMAKE_MATCH_1})
set(SPDLOG_VERSION_MAJOR ${ver_major} PARENT_SCOPE)
set(SPDLOG_VERSION_MINOR ${ver_minor} PARENT_SCOPE)
set(SPDLOG_VERSION_PATCH ${ver_patch} PARENT_SCOPE)
set(SPDLOG_VERSION "${ver_major}.${ver_minor}.${ver_patch}" PARENT_SCOPE)
endfunction()
# Turn on warnings on the given target
function(spdlog_enable_warnings target_name)
if(SPDLOG_BUILD_WARNINGS)
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
list(APPEND MSVC_OPTIONS "/W3")
if(MSVC_VERSION GREATER 1900) # Allow non fatal security warnings for msvc 2015
list(APPEND MSVC_OPTIONS "/WX")
endif()
endif()
target_compile_options(
${target_name}
PRIVATE $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
-Wall
-Wextra
-Wconversion
-pedantic
-Wfatal-errors>
$<$<CXX_COMPILER_ID:MSVC>:${MSVC_OPTIONS}>)
endif()
endfunction()
# Enable address sanitizer (gcc/clang only)
function(spdlog_enable_sanitizer target_name)
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
message(FATAL_ERROR "Sanitizer supported only for gcc/clang")
endif()
message(STATUS "Address sanitizer enabled")
target_compile_options(${target_name} PRIVATE -fsanitize=address,undefined)
target_compile_options(${target_name} PRIVATE -fno-sanitize=signed-integer-overflow)
target_compile_options(${target_name} PRIVATE -fno-sanitize-recover=all)
target_compile_options(${target_name} PRIVATE -fno-omit-frame-pointer)
target_link_libraries(${target_name} PRIVATE -fsanitize=address,undefined -fuse-ld=gold)
endfunction()

42
cmake/version.rc.in Normal file
View File

@@ -0,0 +1,42 @@
#define APSTUDIO_READONLY_SYMBOLS
#include "winres.h"
#undef APSTUDIO_READONLY_SYMBOLS
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
VS_VERSION_INFO VERSIONINFO
FILEVERSION @SPDLOG_VERSION_MAJOR@,@SPDLOG_VERSION_MINOR@,@SPDLOG_VERSION_PATCH@,0
PRODUCTVERSION @SPDLOG_VERSION_MAJOR@,@SPDLOG_VERSION_MINOR@,@SPDLOG_VERSION_PATCH@,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "FileDescription", "spdlog dll\0"
VALUE "FileVersion", "@SPDLOG_VERSION@.0\0"
VALUE "InternalName", "spdlog.dll\0"
VALUE "LegalCopyright", "Copyright (C) spdlog\0"
VALUE "ProductName", "spdlog\0"
VALUE "ProductVersion", "@SPDLOG_VERSION@.0\0"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View File

@@ -1,7 +1,6 @@
# Copyright(c) 2019 spdlog authors
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
cmake_minimum_required(VERSION 3.1)
cmake_minimum_required(VERSION 3.2)
project(spdlog_examples CXX)
if(NOT TARGET spdlog)
@@ -9,19 +8,16 @@ if(NOT TARGET spdlog)
find_package(spdlog REQUIRED)
endif()
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
# Example of using pre-compiled library
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
add_executable(example example.cpp)
spdlog_enable_warnings(example)
target_link_libraries(example PRIVATE spdlog::spdlog)
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
# Example of using header-only library
#---------------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------------
if(SPDLOG_BUILD_EXAMPLE_HO)
add_executable(example_header_only example.cpp)
spdlog_enable_warnings(example_header_only)
target_link_libraries(example_header_only PRIVATE spdlog::spdlog_header_only)
endif()

View File

@@ -6,6 +6,7 @@
#include <cstdio>
void load_levels_example();
void stdout_logger_example();
void basic_example();
void rotating_example();
@@ -17,12 +18,18 @@ void multi_sink_example();
void user_defined_example();
void err_handler_example();
void syslog_example();
void custom_flags_example();
#include "spdlog/spdlog.h"
#include "spdlog/cfg/env.h" // for loading levels from the environment variable
int main(int, char *[])
{
// Log levels can be loaded from argv/env using "SPDLOG_LEVEL"
load_levels_example();
spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH);
spdlog::warn("Easy padding in numbers like {:08d}", 12);
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
spdlog::info("Support for floats {:03.2f}", 1.23456);
@@ -64,6 +71,7 @@ int main(int, char *[])
user_defined_example();
err_handler_example();
trace_example();
custom_flags_example();
// Flush all *registered* loggers using a worker thread every 3 seconds.
// note: registered loggers *must* be thread safe for this to work correctly!
@@ -116,6 +124,18 @@ void daily_example()
auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
}
#include "spdlog/cfg/env.h"
void load_levels_example()
{
// Set the log level to "info" and mylogger to to "trace":
// SPDLOG_LEVEL=info,mylogger=trace && ./example
spdlog::cfg::load_env_levels();
// or from command line:
// ./example SPDLOG_LEVEL=info,mylogger=trace
// #include "spdlog/cfg/argv.h" // for loading levels from argv
// spdlog::cfg::load_argv_levels(args, argv);
}
#include "spdlog/async.h"
void async_example()
{
@@ -154,6 +174,8 @@ void binary_example()
// logger->info("uppercase: {:X}", spdlog::to_hex(buf));
// logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf));
// logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf));
// logger->info("hexdump style: {:a}", spdlog::to_hex(buf));
// logger->info("hexdump style, 20 chars per line {:a}", spdlog::to_hex(buf, 20));
}
// Compile time log levels.
@@ -230,5 +252,31 @@ void android_example()
auto android_logger = spdlog::android_logger_mt("android", tag);
android_logger->critical("Use \"adb shell logcat\" to view this message.");
}
#endif
// Log patterns can contain custom flags.
// this will add custom flag '%*' which will be bound to a <my_formatter_flag> instance
#include "spdlog/pattern_formatter.h"
class my_formatter_flag : public spdlog::custom_flag_formatter
{
public:
void format(const spdlog::details::log_msg &, const std::tm &, spdlog::memory_buf_t &dest) override
{
std::string some_txt = "custom-flag";
dest.append(some_txt.data(), some_txt.data() + some_txt.size());
}
std::unique_ptr<custom_flag_formatter> clone() const override
{
return spdlog::details::make_unique<my_formatter_flag>();
}
};
void custom_flags_example()
{
using spdlog::details::make_unique; // for pre c++14
auto formatter = make_unique<spdlog::pattern_formatter>();
formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v");
spdlog::set_formatter(std::move(formatter));
}

View File

@@ -1,4 +0,0 @@
executable('example', 'example.cpp', dependencies: spdlog_dep)
executable('example_header_only', 'example.cpp', dependencies: spdlog_headeronly_dep)

View File

@@ -32,7 +32,7 @@ SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg)
}
else
{
SPDLOG_THROW(spdlog_ex("async log: thread pool doesn't exist anymore"));
throw_spdlog_ex("async log: thread pool doesn't exist anymore");
}
}
@@ -45,7 +45,7 @@ SPDLOG_INLINE void spdlog::async_logger::flush_()
}
else
{
SPDLOG_THROW(spdlog_ex("async flush: thread pool doesn't exist anymore"));
throw_spdlog_ex("async flush: thread pool doesn't exist anymore");
}
}

View File

@@ -30,7 +30,7 @@ namespace details {
class thread_pool;
}
class async_logger final : public std::enable_shared_from_this<async_logger>, public logger
class SPDLOG_API async_logger final : public std::enable_shared_from_this<async_logger>, public logger
{
friend class details::thread_pool;

45
include/spdlog/cfg/argv.h Normal file
View File

@@ -0,0 +1,45 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/cfg/helpers.h>
#include <spdlog/details/registry.h>
//
// Init log levels using each argv entry that starts with "SPDLOG_LEVEL="
//
// set all loggers to debug level:
// example.exe "SPDLOG_LEVEL=debug"
// set logger1 to trace level
// example.exe "SPDLOG_LEVEL=logger1=trace"
// turn off all logging except for logger1 and logger2:
// example.exe "SPDLOG_LEVEL=off,logger1=debug,logger2=info"
namespace spdlog {
namespace cfg {
// search for SPDLOG_LEVEL= in the args and use it to init the levels
void load_argv_levels(int argc, const char **argv)
{
const std::string spdlog_level_prefix = "SPDLOG_LEVEL=";
for (int i = 1; i < argc; i++)
{
std::string arg = argv[i];
if (arg.find(spdlog_level_prefix) == 0)
{
auto levels_string = arg.substr(spdlog_level_prefix.size());
auto levels = helpers::extract_levels(levels_string);
details::registry::instance().update_levels(std::move(levels));
}
}
}
void load_argv_levels(int argc, char **argv)
{
load_argv_levels(argc, const_cast<const char **>(argv));
}
} // namespace cfg
} // namespace spdlog

36
include/spdlog/cfg/env.h Normal file
View File

@@ -0,0 +1,36 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/cfg/helpers.h>
#include <spdlog/details/registry.h>
#include <spdlog/details/os.h>
//
// Init levels and patterns from env variables SPDLOG_LEVEL
// Inspired from Rust's "env_logger" crate (https://crates.io/crates/env_logger).
// Note - fallback to "info" level on unrecognized levels
//
// Examples:
//
// set global level to debug:
// export SPDLOG_LEVEL=debug
//
// turn off all logging except for logger1:
// export SPDLOG_LEVEL="off,logger1=debug"
//
// turn off all logging except for logger1 and logger2:
// export SPDLOG_LEVEL="off,logger1=debug,logger2=info"
namespace spdlog {
namespace cfg {
void load_env_levels()
{
auto env_val = details::os::getenv("SPDLOG_LEVEL");
auto levels = helpers::extract_levels(env_val);
details::registry::instance().update_levels(std::move(levels));
}
} // namespace cfg
} // namespace spdlog

View File

@@ -0,0 +1,103 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#ifndef SPDLOG_HEADER_ONLY
#include <spdlog/cfg/helpers.h>
#endif
#include <spdlog/spdlog.h>
#include <spdlog/details/os.h>
#include <spdlog/details/registry.h>
#include <string>
#include <utility>
#include <sstream>
namespace spdlog {
namespace cfg {
namespace helpers {
// inplace convert to lowercase
inline std::string &to_lower_(std::string &str)
{
std::transform(
str.begin(), str.end(), str.begin(), [](char ch) { return static_cast<char>((ch >= 'A' && ch <= 'Z') ? ch + ('a' - 'A') : ch); });
return str;
}
// inplace trim spaces
inline std::string &trim_(std::string &str)
{
const char *spaces = " \n\r\t";
str.erase(str.find_last_not_of(spaces) + 1);
str.erase(0, str.find_first_not_of(spaces));
return str;
}
// return (name,value) trimmed pair from given "name=value" string.
// return empty string on missing parts
// "key=val" => ("key", "val")
// " key = val " => ("key", "val")
// "key=" => ("key", "")
// "val" => ("", "val")
inline std::pair<std::string, std::string> extract_kv_(char sep, const std::string &str)
{
auto n = str.find(sep);
std::string k, v;
if (n == std::string::npos)
{
v = str;
}
else
{
k = str.substr(0, n);
v = str.substr(n + 1);
}
return std::make_pair(trim_(k), trim_(v));
}
// return vector of key/value pairs from sequence of "K1=V1,K2=V2,.."
// "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...}
inline std::unordered_map<std::string, std::string> extract_key_vals_(const std::string &str)
{
std::string token;
std::istringstream token_stream(str);
std::unordered_map<std::string, std::string> rv{};
while (std::getline(token_stream, token, ','))
{
if (token.empty())
{
continue;
}
auto kv = extract_kv_('=', token);
rv[kv.first] = kv.second;
}
return rv;
}
SPDLOG_INLINE log_levels extract_levels(const std::string &input)
{
auto key_vals = extract_key_vals_(input);
log_levels rv;
for (auto &name_level : key_vals)
{
auto &logger_name = name_level.first;
auto level_name = to_lower_(name_level.second);
auto level = level::from_str(level_name);
// fallback to "info" if unrecognized level name
if (level == level::off && level_name != "off")
{
level = level::info;
}
rv.set(logger_name, level);
}
return rv;
}
} // namespace helpers
} // namespace cfg
} // namespace spdlog

View File

@@ -0,0 +1,28 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/cfg/log_levels.h>
namespace spdlog {
namespace cfg {
namespace helpers {
//
// Init levels from given string
//
// Examples:
//
// set global level to debug: "debug"
// turn off all logging except for logger1: "off,logger1=debug"
// turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info"
//
SPDLOG_API log_levels extract_levels(const std::string &txt);
} // namespace helpers
} // namespace cfg
} // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY
#include "helpers-inl.h"
#endif // SPDLOG_HEADER_ONLY

View File

@@ -0,0 +1,47 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/common.h>
#include <string>
#include <unordered_map>
namespace spdlog {
namespace cfg {
class log_levels
{
std::unordered_map<std::string, spdlog::level::level_enum> levels_;
spdlog::level::level_enum default_level_ = level::info;
public:
void set(const std::string &logger_name, level::level_enum lvl)
{
if (logger_name.empty())
{
default_level_ = lvl;
}
else
{
levels_[logger_name] = lvl;
}
}
void set_default(level::level_enum lvl)
{
default_level_ = lvl;
}
level::level_enum get(const std::string &logger_name)
{
auto it = levels_.find(logger_name);
return it != levels_.end() ? it->second : default_level_;
}
level::level_enum default_level()
{
return default_level_;
}
};
} // namespace cfg
} // namespace spdlog

View File

@@ -34,6 +34,15 @@ SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG
}
level++;
}
// check also for "warn" and "err" before giving up..
if (name == "warn")
{
return level::warn;
}
if (name == "err")
{
return level::err;
}
return level::off;
}
} // namespace level
@@ -54,4 +63,14 @@ SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT
return msg_.c_str();
}
SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno)
{
SPDLOG_THROW(spdlog_ex(msg, last_errno));
}
SPDLOG_INLINE void throw_spdlog_ex(std::string msg)
{
SPDLOG_THROW(spdlog_ex(std::move(msg)));
}
} // namespace spdlog

View File

@@ -15,25 +15,23 @@
#include <type_traits>
#include <functional>
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX // prevent windows redefining min/max
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#endif //_WIN32
#ifdef SPDLOG_COMPILED_LIB
#undef SPDLOG_HEADER_ONLY
#define SPDLOG_INLINE
#if defined(_WIN32) && defined(SPDLOG_SHARED_LIB)
#ifdef spdlog_EXPORTS
#define SPDLOG_API __declspec(dllexport)
#else
#define SPDLOG_API __declspec(dllimport)
#endif
#else // !defined(_WIN32) || !defined(SPDLOG_SHARED_LIB)
#define SPDLOG_API
#endif
#define SPDLOG_INLINE
#else // !defined(SPDLOG_COMPILED_LIB)
#define SPDLOG_API
#define SPDLOG_HEADER_ONLY
#define SPDLOG_INLINE inline
#endif
#endif // #ifdef SPDLOG_COMPILED_LIB
#include <spdlog/fmt/fmt.h>
@@ -147,6 +145,7 @@ enum level_enum
err = SPDLOG_LEVEL_ERROR,
critical = SPDLOG_LEVEL_CRITICAL,
off = SPDLOG_LEVEL_OFF,
n_levels
};
#if !defined(SPDLOG_LEVEL_NAMES)
@@ -164,9 +163,9 @@ enum level_enum
}
#endif
string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
SPDLOG_API string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
using level_hasher = std::hash<int>;
} // namespace level
@@ -194,7 +193,7 @@ enum class pattern_time_type
//
// Log exception
//
class spdlog_ex : public std::exception
class SPDLOG_API spdlog_ex : public std::exception
{
public:
explicit spdlog_ex(std::string msg);
@@ -205,6 +204,9 @@ private:
std::string msg_;
};
SPDLOG_API void throw_spdlog_ex(const std::string &msg, int last_errno);
SPDLOG_API void throw_spdlog_ex(std::string msg);
struct source_loc
{
SPDLOG_CONSTEXPR source_loc() = default;
@@ -237,7 +239,6 @@ std::unique_ptr<T> make_unique(Args &&... args)
}
#endif
} // namespace details
} // namespace spdlog
#ifdef SPDLOG_HEADER_ONLY

View File

@@ -15,7 +15,7 @@
namespace spdlog {
namespace details {
class backtracer
class SPDLOG_API backtracer
{
mutable std::mutex mutex_;
std::atomic<bool> enabled_{false};

View File

@@ -43,14 +43,14 @@ SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate)
details::os::sleep_for_millis(open_interval_);
}
SPDLOG_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);
}
SPDLOG_INLINE void file_helper::reopen(bool truncate)
{
if (filename_.empty())
{
SPDLOG_THROW(spdlog_ex("Failed re opening file - was not opened before"));
throw_spdlog_ex("Failed re opening file - was not opened before");
}
this->open(filename_, truncate);
}
@@ -75,7 +75,7 @@ SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf)
auto data = buf.data();
if (std::fwrite(data, 1, msg_size, fd_) != msg_size)
{
SPDLOG_THROW(spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno));
throw_spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno);
}
}
@@ -83,7 +83,7 @@ SPDLOG_INLINE size_t file_helper::size() const
{
if (fd_ == nullptr)
{
SPDLOG_THROW(spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_)));
throw_spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_));
}
return os::filesize(fd_);
}

View File

@@ -13,7 +13,7 @@ namespace details {
// When failing to open a file, retry several times(5) with a delay interval(10 ms).
// Throw spdlog_ex exception on errors.
class file_helper
class SPDLOG_API file_helper
{
public:
explicit file_helper() = default;

View File

@@ -20,10 +20,7 @@ inline spdlog::string_view_t to_string_view(const memory_buf_t &buf) SPDLOG_NOEX
inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest)
{
auto *buf_ptr = view.data();
if (buf_ptr != nullptr)
{
dest.append(buf_ptr, buf_ptr + view.size());
}
dest.append(buf_ptr, buf_ptr + view.size());
}
template<typename T>
@@ -34,29 +31,20 @@ inline void append_int(T n, memory_buf_t &dest)
}
template<typename T>
inline unsigned count_digits(T n)
inline unsigned int count_digits(T n)
{
using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
return static_cast<unsigned>(fmt::internal::count_digits(static_cast<count_type>(n)));
return static_cast<unsigned int>(fmt::internal::count_digits(static_cast<count_type>(n)));
}
inline void pad2(int n, memory_buf_t &dest)
{
if (n > 99)
{
append_int(n, dest);
}
else if (n > 9) // 10-99
if (n >= 0 && n < 100) // 0-99
{
dest.push_back(static_cast<char>('0' + n / 10));
dest.push_back(static_cast<char>('0' + n % 10));
}
else if (n >= 0) // 0-9
{
dest.push_back('0');
dest.push_back(static_cast<char>('0' + n));
}
else // negatives (unlikely, but just in case, let fmt deal with it)
else // unlikely, but just in case, let fmt deal with it
{
fmt::format_to(dest, "{:02}", n);
}
@@ -66,11 +54,9 @@ template<typename T>
inline void pad_uint(T n, unsigned int width, memory_buf_t &dest)
{
static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
auto digits = count_digits(n);
if (width > digits)
for (auto digits = count_digits(n); digits < width; digits++)
{
const char *zeroes = "0000000000000000000";
dest.append(zeroes, zeroes + width - digits);
dest.push_back('0');
}
append_int(n, dest);
}
@@ -78,7 +64,18 @@ inline void pad_uint(T n, unsigned int width, memory_buf_t &dest)
template<typename T>
inline void pad3(T n, memory_buf_t &dest)
{
pad_uint(n, 3, dest);
static_assert(std::is_unsigned<T>::value, "pad3 must get unsigned T");
if (n < 1000)
{
dest.push_back(static_cast<char>(n / 100 + '0'));
n = n % 100;
dest.push_back(static_cast<char>((n / 10) + '0'));
dest.push_back(static_cast<char>((n % 10) + '0'));
}
else
{
append_int(n, dest);
}
}
template<typename T>

View File

@@ -12,11 +12,11 @@
namespace spdlog {
namespace details {
SPDLOG_INLINE log_msg::log_msg(
spdlog::source_loc loc, string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
SPDLOG_INLINE log_msg::log_msg(spdlog::log_clock::time_point log_time, spdlog::source_loc loc, string_view_t a_logger_name,
spdlog::level::level_enum lvl, spdlog::string_view_t msg)
: logger_name(a_logger_name)
, level(lvl)
, time(os::now())
, time(log_time)
#ifndef SPDLOG_NO_THREAD_ID
, thread_id(os::thread_id())
#endif
@@ -24,8 +24,13 @@ SPDLOG_INLINE log_msg::log_msg(
, payload(msg)
{}
SPDLOG_INLINE log_msg::log_msg(
spdlog::source_loc loc, string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
: log_msg(os::now(), loc, a_logger_name, lvl, msg)
{}
SPDLOG_INLINE log_msg::log_msg(string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
: log_msg(source_loc{}, a_logger_name, lvl, msg)
: log_msg(os::now(), source_loc{}, a_logger_name, lvl, msg)
{}
} // namespace details

View File

@@ -8,9 +8,10 @@
namespace spdlog {
namespace details {
struct log_msg
struct SPDLOG_API log_msg
{
log_msg() = default;
log_msg(log_clock::time_point log_time, source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg);
log_msg(const log_msg &other) = default;

View File

@@ -9,9 +9,9 @@ namespace spdlog {
namespace details {
// Extend log_msg with internal buffer to store its payload.
// THis is needed since log_msg holds string_views that points to stack data.
// This is needed since log_msg holds string_views that points to stack data.
class log_msg_buffer : public log_msg
class SPDLOG_API log_msg_buffer : public log_msg
{
memory_buf_t buffer;
void update_string_views();

View File

@@ -23,16 +23,9 @@
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX // prevent windows redefining min/max
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <io.h> // _get_osfhandle and _isatty support
#include <process.h> // _get_pid support
#include <windows.h>
#include <spdlog/details/windows_include.h>
#ifdef __MINGW32__
#include <share.h>
@@ -126,23 +119,6 @@ SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT
return gmtime(now_t);
}
#ifdef SPDLOG_PREVENT_CHILD_FD
SPDLOG_INLINE void prevent_child_fd(FILE *f)
{
#ifdef _WIN32
auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(f)));
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
SPDLOG_THROW(spdlog_ex("SetHandleInformation failed", errno));
#else
auto fd = ::fileno(f);
if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
{
SPDLOG_THROW(spdlog_ex("fcntl with FD_CLOEXEC failed", errno));
}
#endif
}
#endif // SPDLOG_PREVENT_CHILD_FD
// fopen_s on non windows for writing
SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
{
@@ -152,17 +128,35 @@ SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename
#else
*fp = ::_fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
#endif
#else // unix
*fp = ::fopen((filename.c_str()), mode.c_str());
#endif
#ifdef SPDLOG_PREVENT_CHILD_FD
// prevent child processes from inheriting log file descriptors
#if defined(SPDLOG_PREVENT_CHILD_FD)
if (*fp != nullptr)
{
prevent_child_fd(*fp);
auto file_handle = reinterpret_cast<HANDLE>(_get_osfhandle(::_fileno(*fp)));
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
{
::fclose(*fp);
*fp = nullptr;
}
}
#endif
#else // unix
#if defined(SPDLOG_PREVENT_CHILD_FD)
const int mode_flag = mode == SPDLOG_FILENAME_T("ab") ? O_APPEND : O_TRUNC;
const int fd = ::open((filename.c_str()), O_CREAT | O_WRONLY | O_CLOEXEC | mode_flag, mode_t(0644));
if (fd == -1)
{
return false;
}
*fp = ::fdopen(fd, mode.c_str());
if (*fp == nullptr)
{
::close(fd);
}
#else
*fp = ::fopen((filename.c_str()), mode.c_str());
#endif
#endif
return *fp == nullptr;
}
@@ -210,7 +204,7 @@ SPDLOG_INLINE size_t filesize(FILE *f)
{
if (f == nullptr)
{
SPDLOG_THROW(spdlog_ex("Failed getting file size. fd is null"));
throw_spdlog_ex("Failed getting file size. fd is null");
}
#if defined(_WIN32) && !defined(__CYGWIN__)
int fd = ::_fileno(f);
@@ -251,7 +245,8 @@ SPDLOG_INLINE size_t filesize(FILE *f)
}
#endif
#endif
SPDLOG_THROW(spdlog_ex("Failed getting file size from fd", errno));
throw_spdlog_ex("Failed getting file size from fd", errno);
return 0; // will not be reached.
}
// Return utc offset in minutes or throw spdlog_ex on failure
@@ -267,7 +262,7 @@ SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm)
auto rv = ::GetDynamicTimeZoneInformation(&tzinfo);
#endif
if (rv == TIME_ZONE_ID_INVALID)
SPDLOG_THROW(spdlog::spdlog_ex("Failed getting timezone info. ", errno));
throw_spdlog_ex("Failed getting timezone info. ", errno);
int offset = -tzinfo.Bias;
if (tm.tm_isdst)
@@ -396,13 +391,13 @@ SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT
}
// Determine if the terminal supports colors
// Source: https://github.com/agauniyal/rang/
// Based on: https://github.com/agauniyal/rang/
SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
{
#ifdef _WIN32
return true;
#else
static constexpr std::array<const char *, 14> Terms = {
static constexpr std::array<const char *, 14> terms = {
{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"}};
const char *env_p = std::getenv("TERM");
@@ -412,12 +407,12 @@ SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
}
static const bool result =
std::any_of(std::begin(Terms), std::end(Terms), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
std::any_of(terms.begin(), terms.end(), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
return result;
#endif
}
// Detrmine if the terminal attached
// Determine if the terminal attached
// Source: https://github.com/agauniyal/rang/
SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
{
@@ -434,7 +429,7 @@ SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
{
if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()))
{
SPDLOG_THROW(spdlog::spdlog_ex("UTF-16 string is too big to be converted to UTF-8"));
throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
}
int wstr_size = static_cast<int>(wstr.size());
@@ -462,7 +457,7 @@ SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
}
}
SPDLOG_THROW(spdlog::spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError())));
throw_spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
}
#endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
@@ -536,6 +531,24 @@ SPDLOG_INLINE filename_t dir_name(filename_t path)
return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
}
std::string SPDLOG_INLINE getenv(const char *field)
{
#if defined(_MSC_VER)
#if defined(__cplusplus_winrt)
return std::string{}; // not supported under uwp
#else
size_t len = 0;
char buf[128];
bool ok = ::getenv_s(&len, buf, sizeof(buf), field) == 0;
return ok ? buf : std::string{};
#endif
#else // revert to getenv
char *buf = ::getenv(field);
return buf ? buf : std::string{};
#endif
}
} // namespace os
} // namespace details
} // namespace spdlog

View File

@@ -10,15 +10,15 @@ namespace spdlog {
namespace details {
namespace os {
spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT;
SPDLOG_API spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT;
std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
SPDLOG_API std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
std::tm localtime() SPDLOG_NOEXCEPT;
SPDLOG_API std::tm localtime() SPDLOG_NOEXCEPT;
std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
SPDLOG_API std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT;
std::tm gmtime() SPDLOG_NOEXCEPT;
SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT;
// eol definition
#if !defined(SPDLOG_EOL)
@@ -38,54 +38,50 @@ static const char folder_sep = '\\';
SPDLOG_CONSTEXPR static const char folder_sep = '/';
#endif
#ifdef SPDLOG_PREVENT_CHILD_FD
void prevent_child_fd(FILE *f);
#endif
// fopen_s on non windows for writing
bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
// Remove filename. return 0 on success
int remove(const filename_t &filename) SPDLOG_NOEXCEPT;
SPDLOG_API int remove(const filename_t &filename) SPDLOG_NOEXCEPT;
// Remove file if exists. return 0 on success
// Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread)
int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
SPDLOG_API int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT;
SPDLOG_API int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT;
// Return if file exists.
bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
SPDLOG_API bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT;
// Return file size according to open FILE* object
size_t filesize(FILE *f);
SPDLOG_API size_t filesize(FILE *f);
// Return utc offset in minutes or throw spdlog_ex on failure
int utc_minutes_offset(const std::tm &tm = details::os::localtime());
SPDLOG_API int utc_minutes_offset(const std::tm &tm = details::os::localtime());
// Return current thread id as size_t
// It exists because the std::this_thread::get_id() is much slower(especially
// under VS 2013)
size_t _thread_id() SPDLOG_NOEXCEPT;
SPDLOG_API size_t _thread_id() SPDLOG_NOEXCEPT;
// Return current thread id as size_t (from thread local storage)
size_t thread_id() SPDLOG_NOEXCEPT;
SPDLOG_API size_t thread_id() SPDLOG_NOEXCEPT;
// This is avoid msvc issue in sleep_for that happens if the clock changes.
// See https://github.com/gabime/spdlog/issues/609
void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT;
SPDLOG_API void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT;
std::string filename_to_str(const filename_t &filename);
SPDLOG_API std::string filename_to_str(const filename_t &filename);
int pid() SPDLOG_NOEXCEPT;
SPDLOG_API int pid() SPDLOG_NOEXCEPT;
// Determine if the terminal supports colors
// Source: https://github.com/agauniyal/rang/
bool is_color_terminal() SPDLOG_NOEXCEPT;
SPDLOG_API bool is_color_terminal() SPDLOG_NOEXCEPT;
// Determine if the terminal attached
// Source: https://github.com/agauniyal/rang/
bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
SPDLOG_API bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
@@ -96,11 +92,15 @@ void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
// "abc/" => "abc"
// "abc" => ""
// "abc///" => "abc//"
filename_t dir_name(filename_t path);
SPDLOG_API filename_t dir_name(filename_t path);
// Create a dir from the given path.
// Return true if succeeded or if this dir already exists.
bool create_dir(filename_t path);
SPDLOG_API bool create_dir(filename_t path);
// non thread safe, cross platform getenv/getenv_s
// return empty string if field not found
SPDLOG_API std::string getenv(const char *field);
} // namespace os
} // namespace details

View File

@@ -17,7 +17,7 @@
namespace spdlog {
namespace details {
class periodic_worker
class SPDLOG_API periodic_worker
{
public:
periodic_worker(const std::function<void()> &callback_fun, std::chrono::seconds interval);

View File

@@ -10,7 +10,7 @@
#include <spdlog/common.h>
#include <spdlog/details/periodic_worker.h>
#include <spdlog/logger.h>
#include <spdlog/details/pattern_formatter.h>
#include <spdlog/pattern_formatter.h>
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
// support for the default stdout color logger
@@ -48,6 +48,9 @@ SPDLOG_INLINE registry::registry()
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
}
SPDLOG_INLINE registry::~registry() = default;
SPDLOG_INLINE void registry::register_logger(std::shared_ptr<logger> new_logger)
{
std::lock_guard<std::mutex> lock(logger_map_mutex_);
@@ -64,7 +67,7 @@ SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logge
new_logger->set_error_handler(err_handler_);
}
new_logger->set_level(level_);
new_logger->set_level(levels_.get(new_logger->name()));
new_logger->flush_on(flush_level_);
if (backtrace_n_messages_ > 0)
@@ -168,7 +171,7 @@ SPDLOG_INLINE void registry::set_level(level::level_enum log_level)
{
l.second->set_level(log_level);
}
level_ = log_level;
levels_.set_default(log_level);
}
SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
@@ -184,7 +187,7 @@ SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
SPDLOG_INLINE void registry::flush_every(std::chrono::seconds interval)
{
std::lock_guard<std::mutex> lock(flusher_mutex_);
std::function<void()> clbk = std::bind(&registry::flush_all, this);
auto clbk = [this]() { this->flush_all(); };
periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval);
}
@@ -260,6 +263,17 @@ SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registrat
automatic_registration_ = automatic_registration;
}
SPDLOG_INLINE void registry::update_levels(cfg::log_levels levels)
{
std::lock_guard<std::mutex> lock(logger_map_mutex_);
levels_ = std::move(levels);
for (auto &l : loggers_)
{
auto &logger = l.second;
logger->set_level(levels_.get(logger->name()));
}
}
SPDLOG_INLINE registry &registry::instance()
{
static registry s_instance;
@@ -270,7 +284,7 @@ SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name)
{
if (loggers_.find(logger_name) != loggers_.end())
{
SPDLOG_THROW(spdlog_ex("logger with name '" + logger_name + "' already exists"));
throw_spdlog_ex("logger with name '" + logger_name + "' already exists");
}
}
@@ -280,5 +294,6 @@ SPDLOG_INLINE void registry::register_logger_(std::shared_ptr<logger> new_logger
throw_if_exists_(logger_name);
loggers_[logger_name] = std::move(new_logger);
}
} // namespace details
} // namespace spdlog

View File

@@ -9,6 +9,7 @@
// This class is thread safe
#include <spdlog/common.h>
#include <spdlog/cfg/log_levels.h>
#include <chrono>
#include <functional>
@@ -24,7 +25,7 @@ namespace details {
class thread_pool;
class periodic_worker;
class registry
class SPDLOG_API registry
{
public:
registry(const registry &) = delete;
@@ -79,19 +80,21 @@ public:
void set_automatic_registration(bool automatic_registration);
void update_levels(cfg::log_levels levels);
static registry &instance();
private:
registry();
~registry() = default;
~registry();
void throw_if_exists_(const std::string &logger_name);
void register_logger_(std::shared_ptr<logger> new_logger);
std::mutex logger_map_mutex_, flusher_mutex_;
std::recursive_mutex tp_mutex_;
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
cfg::log_levels levels_;
std::unique_ptr<formatter> formatter_;
level::level_enum level_ = level::info;
level::level_enum flush_level_ = level::off;
void (*err_handler_)(const std::string &msg);
std::shared_ptr<thread_pool> tp_;

View File

@@ -0,0 +1,175 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#define WIN32_LEAN_AND_MEAN
// tcp client helper
#include <spdlog/common.h>
#include <spdlog/details/os.h>
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <string>
#pragma comment(lib, "Ws2_32.lib")
#pragma comment(lib, "Mswsock.lib")
#pragma comment(lib, "AdvApi32.lib")
namespace spdlog {
namespace details {
class tcp_client
{
SOCKET socket_ = INVALID_SOCKET;
static bool winsock_initialized_()
{
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (s == INVALID_SOCKET)
{
return false;
}
else
{
closesocket(s);
return true;
}
}
static void init_winsock_()
{
WSADATA wsaData;
auto rv = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (rv != 0)
{
throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
}
}
static void throw_winsock_error_(const std::string &msg, int last_error)
{
char buf[512];
::FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(char)), NULL);
throw_spdlog_ex(fmt::format("tcp_sink - {}: {}", msg, buf));
}
public:
bool is_connected() const
{
return socket_ != INVALID_SOCKET;
}
void close()
{
::closesocket(socket_);
socket_ = INVALID_SOCKET;
WSACleanup();
}
SOCKET fd() const
{
return socket_;
}
~tcp_client()
{
close();
}
// try to connect or throw on failure
void connect(const std::string &host, int port)
{
// initialize winsock if needed
if (!winsock_initialized_())
{
init_winsock_();
}
if (is_connected())
{
close();
}
struct addrinfo hints
{};
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET; // IPv4
hints.ai_socktype = SOCK_STREAM; // TCP
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
hints.ai_protocol = 0;
auto port_str = std::to_string(port);
struct addrinfo *addrinfo_result;
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
int last_error = 0;
if (rv != 0)
{
last_error = ::WSAGetLastError();
WSACleanup();
throw_winsock_error_("getaddrinfo failed", last_error);
}
// Try each address until we successfully connect(2).
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
{
socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
if (socket_ == INVALID_SOCKET)
{
last_error = ::WSAGetLastError();
WSACleanup();
continue;
}
if (::connect(socket_, rp->ai_addr, (int)rp->ai_addrlen) == 0)
{
break;
}
else
{
last_error = ::WSAGetLastError();
close();
}
}
::freeaddrinfo(addrinfo_result);
if (socket_ == INVALID_SOCKET)
{
WSACleanup();
throw_winsock_error_("connect failed", last_error);
}
// set TCP_NODELAY
int enable_flag = 1;
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, (char *)&enable_flag, sizeof(enable_flag));
}
// Send exactly n_bytes of the given data.
// On error close the connection and throw.
void send(const char *data, size_t n_bytes)
{
size_t bytes_sent = 0;
while (bytes_sent < n_bytes)
{
const int send_flags = 0;
auto write_result = ::send(socket_, data + bytes_sent, (int)(n_bytes - bytes_sent), send_flags);
if (write_result == SOCKET_ERROR)
{
int last_error = ::WSAGetLastError();
close();
throw_winsock_error_("send failed", last_error);
}
if (write_result == 0) // (probably should not happen but in any case..)
{
break;
}
bytes_sent += static_cast<size_t>(write_result);
}
}
};
} // namespace details
} // namespace spdlog

View File

@@ -0,0 +1,145 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#ifdef _WIN32
#error include tcp_client-windows.h instead
#endif
// tcp client helper
#include <spdlog/common.h>
#include <spdlog/details/os.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/tcp.h>
#include <string>
namespace spdlog {
namespace details {
class tcp_client
{
int socket_ = -1;
public:
bool is_connected() const
{
return socket_ != -1;
}
void close()
{
if (is_connected())
{
::close(socket_);
socket_ = -1;
}
}
int fd() const
{
return socket_;
}
~tcp_client()
{
close();
}
// try to connect or throw on failure
void connect(const std::string &host, int port)
{
close();
struct addrinfo hints
{};
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET; // IPv4
hints.ai_socktype = SOCK_STREAM; // TCP
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
hints.ai_protocol = 0;
auto port_str = std::to_string(port);
struct addrinfo *addrinfo_result;
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
if (rv != 0)
{
auto msg = fmt::format("::getaddrinfo failed: {}", gai_strerror(rv));
throw_spdlog_ex(msg);
}
// Try each address until we successfully connect(2).
int last_errno = 0;
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
{
int const flags = SOCK_CLOEXEC;
socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
if (socket_ == -1)
{
last_errno = errno;
continue;
}
rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen);
if (rv == 0)
{
break;
}
else
{
last_errno = errno;
::close(socket_);
socket_ = -1;
}
}
::freeaddrinfo(addrinfo_result);
if (socket_ == -1)
{
throw_spdlog_ex("::connect failed", last_errno);
}
// set TCP_NODELAY
int enable_flag = 1;
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, (char *)&enable_flag, sizeof(enable_flag));
// prevent sigpipe on systems where MSG_NOSIGNAL is not available
#if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
::setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, (char *)&enable_flag, sizeof(enable_flag));
#endif
#if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
#error "tcp_sink would raise SIGPIPE since niether SO_NOSIGPIPE nor MSG_NOSIGNAL are available"
#endif
}
// Send exactly n_bytes of the given data.
// On error close the connection and throw.
void send(const char *data, size_t n_bytes)
{
size_t bytes_sent = 0;
while (bytes_sent < n_bytes)
{
#if defined(MSG_NOSIGNAL)
const int send_flags = MSG_NOSIGNAL;
#else
const int send_flags = 0;
#endif
auto write_result = ::send(socket_, data + bytes_sent, n_bytes - bytes_sent, send_flags);
if (write_result < 0)
{
close();
throw_spdlog_ex("write(2) failed", errno);
}
if (write_result == 0) // (probably should not happen but in any case..)
{
break;
}
bytes_sent += static_cast<size_t>(write_result);
}
}
};
} // namespace details
} // namespace spdlog

View File

@@ -18,8 +18,8 @@ SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std
{
if (threads_n == 0 || threads_n > 1000)
{
SPDLOG_THROW(spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid "
"range is 1-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++)
{
@@ -113,7 +113,7 @@ bool SPDLOG_INLINE thread_pool::process_next_msg_()
}
default: {
assert(false && "Unexpected async_msg_type");
assert(false);
}
}

View File

@@ -79,7 +79,7 @@ struct async_msg : log_msg_buffer
{}
};
class thread_pool
class SPDLOG_API thread_pool
{
public:
using item_type = async_msg;

View File

@@ -0,0 +1,11 @@
#pragma once
#ifndef NOMINMAX
#define NOMINMAX // prevent windows redefining min/max
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>

View File

@@ -5,6 +5,8 @@
#pragma once
#include <cctype>
//
// Support for logging binary data as hex
// format flags:
@@ -12,6 +14,7 @@
// {:s} - don't separate each byte with space.
// {:p} - don't print the position on each line start.
// {:n} - don't split the output to lines.
// {:a} - show ASCII if :n is not set
//
// Examples:
@@ -20,17 +23,19 @@
// logger->info("Some buffer {}", spdlog::to_hex(v));
// char buf[128];
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf), 16));
namespace spdlog {
namespace details {
template<typename It>
class bytes_range
class dump_info
{
public:
bytes_range(It range_begin, It range_end)
dump_info(It range_begin, It range_end, size_t size_per_line)
: begin_(range_begin)
, end_(range_end)
, size_per_line_(size_per_line)
{}
It begin() const
@@ -41,26 +46,31 @@ public:
{
return end_;
}
size_t size_per_line() const
{
return size_per_line_;
}
private:
It begin_, end_;
size_t size_per_line_;
};
} // namespace details
// create a bytes_range that wraps the given container
// create a dump_info that wraps the given container
template<typename Container>
inline details::bytes_range<typename Container::const_iterator> to_hex(const Container &container)
inline details::dump_info<typename Container::const_iterator> to_hex(const Container &container, size_t size_per_line = 32)
{
static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
using Iter = typename Container::const_iterator;
return details::bytes_range<Iter>(std::begin(container), std::end(container));
return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
}
// create bytes_range from ranges
// create dump_info from ranges
template<typename It>
inline details::bytes_range<It> to_hex(const It range_begin, const It range_end)
inline details::dump_info<It> to_hex(const It range_begin, const It range_end, size_t size_per_line = 32)
{
return details::bytes_range<It>(range_begin, range_end);
return details::dump_info<It>(range_begin, range_end, size_per_line);
}
} // namespace spdlog
@@ -68,15 +78,14 @@ inline details::bytes_range<It> to_hex(const It range_begin, const It range_end)
namespace fmt {
template<typename T>
struct formatter<spdlog::details::bytes_range<T>>
struct formatter<spdlog::details::dump_info<T>>
{
const std::size_t line_size = 100;
const char delimiter = ' ';
bool put_newlines = true;
bool put_delimiters = true;
bool use_uppercase = false;
bool put_positions = true; // position on start of each line
bool show_ascii = false;
// parse the format string flags
template<typename ParseContext>
@@ -98,6 +107,13 @@ struct formatter<spdlog::details::bytes_range<T>>
break;
case 'n':
put_newlines = false;
show_ascii = false;
break;
case 'a':
if (put_newlines)
{
show_ascii = true;
}
break;
}
@@ -108,53 +124,83 @@ struct formatter<spdlog::details::bytes_range<T>>
// format the given bytes range as hex
template<typename FormatContext, typename Container>
auto format(const spdlog::details::bytes_range<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
auto format(const spdlog::details::dump_info<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
{
SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
std::size_t pos = 0;
std::size_t column = line_size;
#if FMT_VERSION < 60000
auto inserter = ctx.begin();
#else
auto inserter = ctx.out();
#endif
for (auto &item : the_range)
int size_per_line = static_cast<int>(the_range.size_per_line());
auto start_of_line = the_range.begin();
for (auto i = the_range.begin(); i != the_range.end(); i++)
{
auto ch = static_cast<unsigned char>(item);
pos++;
auto ch = static_cast<unsigned char>(*i);
if (put_newlines && column >= line_size)
if (put_newlines && (i == the_range.begin() || i - start_of_line >= size_per_line))
{
column = put_newline(inserter, pos);
if (show_ascii && i != the_range.begin())
{
*inserter++ = delimiter;
*inserter++ = delimiter;
for (auto j = start_of_line; j < i; j++)
{
auto pc = static_cast<unsigned char>(*j);
*inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
}
}
put_newline(inserter, static_cast<size_t>(i - the_range.begin()));
// put first byte without delimiter in front of it
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
*inserter++ = hex_chars[ch & 0x0f];
column += 2;
start_of_line = i;
continue;
}
if (put_delimiters)
{
*inserter++ = delimiter;
++column;
}
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
*inserter++ = hex_chars[ch & 0x0f];
column += 2;
}
if (show_ascii) // add ascii to last line
{
if (the_range.end() - the_range.begin() > size_per_line)
{
auto blank_num = size_per_line - (the_range.end() - start_of_line);
while (blank_num-- > 0)
{
*inserter++ = delimiter;
*inserter++ = delimiter;
if (put_delimiters)
{
*inserter++ = delimiter;
}
}
}
*inserter++ = delimiter;
*inserter++ = delimiter;
for (auto j = start_of_line; j != the_range.end(); j++)
{
auto pc = static_cast<unsigned char>(*j);
*inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
}
}
return inserter;
}
// put newline(and position header)
// return the next column
template<typename It>
std::size_t put_newline(It inserter, std::size_t pos)
void put_newline(It inserter, std::size_t pos)
{
#ifdef _WIN32
*inserter++ = '\r';
@@ -163,12 +209,7 @@ struct formatter<spdlog::details::bytes_range<T>>
if (put_positions)
{
fmt::format_to(inserter, "{:<04X}: ", pos - 1);
return 7;
}
else
{
return 1;
fmt::format_to(inserter, "{:<04X}: ", pos);
}
}
};

View File

@@ -8,14 +8,14 @@
#ifndef FMT_CHRONO_H_
#define FMT_CHRONO_H_
#include "format.h"
#include "locale.h"
#include <chrono>
#include <ctime>
#include <locale>
#include <sstream>
#include "format.h"
#include "locale.h"
FMT_BEGIN_NAMESPACE
// Enable safe chrono durations, unless explicitly disabled.
@@ -495,12 +495,12 @@ FMT_CONSTEXPR const Char* parse_chrono_format(const Char* begin,
handler.on_text(ptr - 1, ptr);
break;
case 'n': {
const char newline[] = "\n";
const Char newline[] = {'\n'};
handler.on_text(newline, newline + 1);
break;
}
case 't': {
const char tab[] = "\t";
const Char tab[] = {'\t'};
handler.on_text(tab, tab + 1);
break;
}
@@ -759,18 +759,30 @@ inline std::chrono::duration<Rep, std::milli> get_milliseconds(
return std::chrono::duration<Rep, std::milli>(static_cast<Rep>(ms));
}
template <typename Rep, typename OutputIt>
OutputIt format_chrono_duration_value(OutputIt out, Rep val, int precision) {
if (precision >= 0) return format_to(out, "{:.{}f}", val, precision);
return format_to(out, std::is_floating_point<Rep>::value ? "{:g}" : "{}",
template <typename Char, typename Rep, typename OutputIt>
OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
const Char pr_f[] = {'{', ':', '.', '{', '}', 'f', '}', 0};
if (precision >= 0) return format_to(out, pr_f, val, precision);
const Char fp_f[] = {'{', ':', 'g', '}', 0};
const Char format[] = {'{', '}', 0};
return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format,
val);
}
template <typename Period, typename OutputIt>
static OutputIt format_chrono_duration_unit(OutputIt out) {
if (const char* unit = get_units<Period>()) return format_to(out, "{}", unit);
if (Period::den == 1) return format_to(out, "[{}]s", Period::num);
return format_to(out, "[{}/{}]s", Period::num, Period::den);
template <typename Char, typename Period, typename OutputIt>
OutputIt format_duration_unit(OutputIt out) {
if (const char* unit = get_units<Period>()) {
string_view s(unit);
if (const_check(std::is_same<Char, wchar_t>())) {
utf8_to_utf16 u(s);
return std::copy(u.c_str(), u.c_str() + u.size(), out);
}
return std::copy(s.begin(), s.end(), out);
}
const Char num_f[] = {'[', '{', '}', ']', 's', 0};
if (Period::den == 1) return format_to(out, num_f, Period::num);
const Char num_def_f[] = {'[', '{', '}', '/', '{', '}', ']', 's', 0};
return format_to(out, num_def_f, Period::num, Period::den);
}
template <typename FormatContext, typename OutputIt, typename Rep,
@@ -871,13 +883,13 @@ struct chrono_formatter {
void write_pinf() { std::copy_n("inf", 3, out); }
void write_ninf() { std::copy_n("-inf", 4, out); }
void format_localized(const tm& time, const char* format) {
void format_localized(const tm& time, char format, char modifier = 0) {
if (isnan(val)) return write_nan();
auto locale = context.locale().template get<std::locale>();
auto& facet = std::use_facet<std::time_put<char_type>>(locale);
std::basic_ostringstream<char_type> os;
os.imbue(locale);
facet.put(os, os, ' ', &time, format, format + std::strlen(format));
facet.put(os, os, ' ', &time, format, modifier);
auto str = os.str();
std::copy(str.begin(), str.end(), out);
}
@@ -907,7 +919,7 @@ struct chrono_formatter {
if (ns == numeric_system::standard) return write(hour(), 2);
auto time = tm();
time.tm_hour = to_nonnegative_int(hour(), 24);
format_localized(time, "%OH");
format_localized(time, 'H', 'O');
}
void on_12_hour(numeric_system ns) {
@@ -916,7 +928,7 @@ struct chrono_formatter {
if (ns == numeric_system::standard) return write(hour12(), 2);
auto time = tm();
time.tm_hour = to_nonnegative_int(hour12(), 12);
format_localized(time, "%OI");
format_localized(time, 'I', 'O');
}
void on_minute(numeric_system ns) {
@@ -925,7 +937,7 @@ struct chrono_formatter {
if (ns == numeric_system::standard) return write(minute(), 2);
auto time = tm();
time.tm_min = to_nonnegative_int(minute(), 60);
format_localized(time, "%OM");
format_localized(time, 'M', 'O');
}
void on_second(numeric_system ns) {
@@ -950,13 +962,12 @@ struct chrono_formatter {
}
auto time = tm();
time.tm_sec = to_nonnegative_int(second(), 60);
format_localized(time, "%OS");
format_localized(time, 'S', 'O');
}
void on_12_hour_time() {
if (handle_nan_inf()) return;
format_localized(time(), "%r");
format_localized(time(), 'r');
}
void on_24_hour_time() {
@@ -980,16 +991,18 @@ struct chrono_formatter {
void on_am_pm() {
if (handle_nan_inf()) return;
format_localized(time(), "%p");
format_localized(time(), 'p');
}
void on_duration_value() {
if (handle_nan_inf()) return;
write_sign();
out = format_chrono_duration_value(out, val, precision);
out = format_duration_value<char_type>(out, val, precision);
}
void on_duration_unit() { out = format_chrono_duration_unit<Period>(out); }
void on_duration_unit() {
out = format_duration_unit<char_type, Period>(out);
}
};
} // namespace internal
@@ -1024,7 +1037,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
}
void on_error(const char* msg) { FMT_THROW(format_error(msg)); }
void on_fill(Char fill) { f.specs.fill[0] = fill; }
void on_fill(basic_string_view<Char> fill) { f.specs.fill = fill; }
void on_align(align_t align) { f.specs.align = align; }
void on_width(int width) { f.specs.width = width; }
void on_precision(int _precision) { f.precision = _precision; }
@@ -1088,8 +1101,8 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
internal::handle_dynamic_spec<internal::precision_checker>(
precision, precision_ref, ctx);
if (begin == end || *begin == '}') {
out = internal::format_chrono_duration_value(out, d.count(), precision);
internal::format_chrono_duration_unit<Period>(out);
out = internal::format_duration_value<Char>(out, d.count(), precision);
internal::format_duration_unit<Char, Period>(out);
} else {
internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
ctx, out, d);

View File

@@ -412,7 +412,7 @@ template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
return buffer + std::strlen(buffer);
return buffer + std::char_traits<Char>::length(buffer);
}
private:
@@ -491,10 +491,8 @@ void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
internal::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
vformat_to(buf, format_str, args);
if (has_style) {
internal::reset_color<Char>(buf);
}
internal::vformat_to(buf, format_str, args);
if (has_style) internal::reset_color<Char>(buf);
}
} // namespace internal
@@ -540,7 +538,7 @@ void print(const text_style& ts, const S& format_str, const Args&... args) {
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<Char>> args) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf);
@@ -562,7 +560,7 @@ template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) {
return vformat(ts, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
internal::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@@ -9,6 +9,7 @@
#define FMT_COMPILE_H_
#include <vector>
#include "format.h"
FMT_BEGIN_NAMESPACE
@@ -350,6 +351,8 @@ template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
template <int N, typename T>
using get_type = typename get_type_impl<N, T>::type;
template <typename T> struct is_compiled_format : std::false_type {};
template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
@@ -361,6 +364,9 @@ template <typename Char> struct text {
}
};
template <typename Char>
struct is_compiled_format<text<Char>> : std::true_type {};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
@@ -406,6 +412,9 @@ template <typename Char, typename T, int N> struct field {
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
template <typename L, typename R> struct concat {
L lhs;
R rhs;
@@ -418,6 +427,9 @@ template <typename L, typename R> struct concat {
}
};
template <typename L, typename R>
struct is_compiled_format<concat<L, R>> : std::true_type {};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
return {lhs, rhs};
@@ -508,8 +520,7 @@ constexpr auto compile(S format_str) {
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
cf.format(std::back_inserter(buffer), args...);
@@ -517,8 +528,7 @@ std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(!std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
FMT_ENABLE_IF(internal::is_compiled_format<CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
@@ -549,7 +559,7 @@ std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
using range = buffer_range<Char>;
using context = buffer_context<Char>;
internal::cf::vformat_to<context>(range(buffer), cf,
{make_format_args<context>(args...)});
make_format_args<context>(args...));
return to_string(buffer);
}
@@ -561,8 +571,8 @@ OutputIt format_to(OutputIt out, const CompiledFormat& cf,
using char_type = typename CompiledFormat::char_type;
using range = internal::output_range<OutputIt, char_type>;
using context = format_context_t<OutputIt, char_type>;
return internal::cf::vformat_to<context>(
range(out), cf, {make_format_args<context>(args...)});
return internal::cf::vformat_to<context>(range(out), cf,
make_format_args<context>(args...));
}
template <typename OutputIt, typename CompiledFormat, typename... Args,

View File

@@ -10,12 +10,15 @@
#include <cstdio> // std::FILE
#include <cstring>
#include <functional>
#include <iterator>
#include <memory>
#include <string>
#include <type_traits>
#include <vector>
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 60102
#define FMT_VERSION 60200
#ifdef __has_feature
# define FMT_HAS_FEATURE(x) __has_feature(x)
@@ -36,6 +39,18 @@
# define FMT_HAS_CPP_ATTRIBUTE(x) 0
#endif
#define FMT_HAS_CPP14_ATTRIBUTE(attribute) \
(__cplusplus >= 201402L && FMT_HAS_CPP_ATTRIBUTE(attribute))
#define FMT_HAS_CPP17_ATTRIBUTE(attribute) \
(__cplusplus >= 201703L && FMT_HAS_CPP_ATTRIBUTE(attribute))
#ifdef __clang__
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
#else
# define FMT_CLANG_VERSION 0
#endif
#if defined(__GNUC__) && !defined(__clang__)
# define FMT_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__)
#else
@@ -117,16 +132,25 @@
# endif
#endif
// [[noreturn]] is disabled on MSVC because of bogus unreachable code warnings.
#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER
// [[noreturn]] is disabled on MSVC and NVCC because of bogus unreachable code
// warnings.
#if FMT_EXCEPTIONS && FMT_HAS_CPP_ATTRIBUTE(noreturn) && !FMT_MSC_VER && \
!FMT_NVCC
# define FMT_NORETURN [[noreturn]]
#else
# define FMT_NORETURN
#endif
#ifndef FMT_MAYBE_UNUSED
# if FMT_HAS_CPP17_ATTRIBUTE(maybe_unused)
# define FMT_MAYBE_UNUSED [[maybe_unused]]
# else
# define FMT_MAYBE_UNUSED
# endif
#endif
#ifndef FMT_DEPRECATED
# if (FMT_HAS_CPP_ATTRIBUTE(deprecated) && __cplusplus >= 201402L) || \
FMT_MSC_VER >= 1900
# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
# define FMT_DEPRECATED [[deprecated]]
# else
# if defined(__GNUC__) || defined(__clang__)
@@ -139,8 +163,8 @@
# endif
#endif
// Workaround broken [[deprecated]] in the Intel compiler and NVCC.
#if defined(__INTEL_COMPILER) || FMT_NVCC
// Workaround broken [[deprecated]] in the Intel, PGI and NVCC compilers.
#if defined(__INTEL_COMPILER) || defined(__PGI) || FMT_NVCC
# define FMT_DEPRECATED_ALIAS
#else
# define FMT_DEPRECATED_ALIAS FMT_DEPRECATED
@@ -166,6 +190,12 @@
#endif
#if !defined(FMT_HEADER_ONLY) && defined(_WIN32)
# if FMT_MSC_VER
# define FMT_NO_W4275 __pragma(warning(suppress : 4275))
# else
# define FMT_NO_W4275
# endif
# define FMT_CLASS_API FMT_NO_W4275
# ifdef FMT_EXPORT
# define FMT_API __declspec(dllexport)
# elif defined(FMT_SHARED)
@@ -173,12 +203,24 @@
# define FMT_EXTERN_TEMPLATE_API FMT_API
# endif
#endif
#ifndef FMT_CLASS_API
# define FMT_CLASS_API
#endif
#ifndef FMT_API
# define FMT_API
# if FMT_GCC_VERSION || FMT_CLANG_VERSION
# define FMT_API __attribute__((visibility("default")))
# define FMT_EXTERN_TEMPLATE_API FMT_API
# define FMT_INSTANTIATION_DEF_API
# else
# define FMT_API
# endif
#endif
#ifndef FMT_EXTERN_TEMPLATE_API
# define FMT_EXTERN_TEMPLATE_API
#endif
#ifndef FMT_INSTANTIATION_DEF_API
# define FMT_INSTANTIATION_DEF_API FMT_API
#endif
#ifndef FMT_HEADER_ONLY
# define FMT_EXTERN extern
@@ -197,9 +239,16 @@
# define FMT_USE_EXPERIMENTAL_STRING_VIEW
#endif
#ifndef FMT_UNICODE
# define FMT_UNICODE !FMT_MSC_VER
#endif
#if FMT_UNICODE && FMT_MSC_VER
# pragma execution_character_set("utf-8")
#endif
FMT_BEGIN_NAMESPACE
// Implementations of enable_if_t and other types for pre-C++14 systems.
// Implementations of enable_if_t and other metafunctions for older systems.
template <bool B, class T = void>
using enable_if_t = typename std::enable_if<B, T>::type;
template <bool B, class T, class F>
@@ -211,6 +260,8 @@ template <typename T>
using remove_const_t = typename std::remove_const<T>::type;
template <typename T>
using remove_cvref_t = typename std::remove_cv<remove_reference_t<T>>::type;
template <typename T> struct type_identity { using type = T; };
template <typename T> using type_identity_t = typename type_identity<T>::type;
struct monostate {};
@@ -221,19 +272,25 @@ struct monostate {};
namespace internal {
// A helper function to suppress bogus "conditional expression is constant"
// warnings.
template <typename T> FMT_CONSTEXPR T const_check(T value) { return value; }
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
template <typename... Ts> struct void_t_impl { using type = void; };
FMT_API void assert_fail(const char* file, int line, const char* message);
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
const char* message);
#ifndef FMT_ASSERT
# ifdef NDEBUG
# define FMT_ASSERT(condition, message)
// FMT_ASSERT is not empty to avoid -Werror=empty-body.
# define FMT_ASSERT(condition, message) ((void)0)
# else
# define FMT_ASSERT(condition, message) \
((condition) \
? void() \
: fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
# define FMT_ASSERT(condition, message) \
((condition) /* void() fails with -Winvalid-constexpr on clang 4.0.1 */ \
? (void)0 \
: ::fmt::internal::assert_fail(__FILE__, __LINE__, (message)))
# endif
#endif
@@ -248,7 +305,7 @@ template <typename T> struct std_string_view {};
#ifdef FMT_USE_INT128
// Do nothing.
#elif defined(__SIZEOF_INT128__)
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC
# define FMT_USE_INT128 1
using int128_t = __int128_t;
using uint128_t = __uint128_t;
@@ -266,6 +323,19 @@ FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value) {
FMT_ASSERT(value >= 0, "negative value");
return static_cast<typename std::make_unsigned<Int>::type>(value);
}
constexpr unsigned char micro[] = "\u00B5";
template <typename Char> constexpr bool is_unicode() {
return FMT_UNICODE || sizeof(Char) != 1 ||
(sizeof(micro) == 3 && micro[0] == 0xC2 && micro[1] == 0xB5);
}
#ifdef __cpp_char8_t
using char8_type = char8_t;
#else
enum char8_type : unsigned char {};
#endif
} // namespace internal
template <typename... Ts>
@@ -284,7 +354,8 @@ template <typename Char> class basic_string_view {
size_t size_;
public:
using char_type = Char;
using char_type FMT_DEPRECATED_ALIAS = Char;
using value_type = Char;
using iterator = const Char*;
FMT_CONSTEXPR basic_string_view() FMT_NOEXCEPT : data_(nullptr), size_(0) {}
@@ -300,6 +371,9 @@ template <typename Char> class basic_string_view {
the size with ``std::char_traits<Char>::length``.
\endrst
*/
#if __cplusplus >= 201703L // C++17's char_traits::length() is constexpr.
FMT_CONSTEXPR
#endif
basic_string_view(const Char* s)
: data_(s), size_(std::char_traits<Char>::length(s)) {}
@@ -365,15 +439,15 @@ using string_view = basic_string_view<char>;
using wstring_view = basic_string_view<wchar_t>;
#ifndef __cpp_char8_t
// A UTF-8 code unit type.
enum char8_t : unsigned char {};
// char8_t is deprecated; use char instead.
using char8_t FMT_DEPRECATED_ALIAS = internal::char8_type;
#endif
/** Specifies if ``T`` is a character type. Can be specialized by users. */
template <typename T> struct is_char : std::false_type {};
template <> struct is_char<char> : std::true_type {};
template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<char8_t> : std::true_type {};
template <> struct is_char<internal::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {};
@@ -442,7 +516,7 @@ struct is_string : std::is_class<decltype(to_string_view(std::declval<S>()))> {
template <typename S, typename = void> struct char_t_impl {};
template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
using result = decltype(to_string_view(std::declval<S>()));
using type = typename result::char_type;
using type = typename result::value_type;
};
struct error_handler {
@@ -603,6 +677,9 @@ template <typename T> class buffer {
T* begin() FMT_NOEXCEPT { return ptr_; }
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
const T* begin() const FMT_NOEXCEPT { return ptr_; }
const T* end() const FMT_NOEXCEPT { return ptr_ + size_; }
/** Returns the size of this buffer. */
std::size_t size() const FMT_NOEXCEPT { return size_; }
@@ -639,8 +716,10 @@ template <typename T> class buffer {
/** Appends data to the end of the buffer. */
template <typename U> void append(const U* begin, const U* end);
T& operator[](std::size_t index) { return ptr_[index]; }
const T& operator[](std::size_t index) const { return ptr_[index]; }
template <typename I> T& operator[](I index) { return ptr_[index]; }
template <typename I> const T& operator[](I index) const {
return ptr_[index];
}
};
// A container-backed buffer.
@@ -684,7 +763,7 @@ using has_fallback_formatter =
template <typename Char> struct named_arg_base;
template <typename T, typename Char> struct named_arg;
enum type {
enum class type {
none_type,
named_arg_type,
// Integer types should go first,
@@ -710,11 +789,12 @@ enum type {
// Maps core type T to the corresponding type enum constant.
template <typename T, typename Char>
struct type_constant : std::integral_constant<type, custom_type> {};
struct type_constant : std::integral_constant<type, type::custom_type> {};
#define FMT_TYPE_CONSTANT(Type, constant) \
template <typename Char> \
struct type_constant<Type, Char> : std::integral_constant<type, constant> {}
struct type_constant<Type, Char> \
: std::integral_constant<type, type::constant> {}
FMT_TYPE_CONSTANT(const named_arg_base<Char>&, named_arg_type);
FMT_TYPE_CONSTANT(int, int_type);
@@ -733,13 +813,13 @@ FMT_TYPE_CONSTANT(basic_string_view<Char>, string_type);
FMT_TYPE_CONSTANT(const void*, pointer_type);
FMT_CONSTEXPR bool is_integral_type(type t) {
FMT_ASSERT(t != named_arg_type, "invalid argument type");
return t > none_type && t <= last_integer_type;
FMT_ASSERT(t != type::named_arg_type, "invalid argument type");
return t > type::none_type && t <= type::last_integer_type;
}
FMT_CONSTEXPR bool is_arithmetic_type(type t) {
FMT_ASSERT(t != named_arg_type, "invalid argument type");
return t > none_type && t <= last_numeric_type;
FMT_ASSERT(t != type::named_arg_type, "invalid argument type");
return t > type::none_type && t <= type::last_numeric_type;
}
template <typename Char> struct string_value {
@@ -869,7 +949,8 @@ template <typename Context> struct arg_mapper {
template <typename T,
FMT_ENABLE_IF(
std::is_constructible<basic_string_view<char_type>, T>::value &&
!is_string<T>::value)>
!is_string<T>::value && !has_formatter<T, Context>::value &&
!has_fallback_formatter<T, Context>::value)>
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
return basic_string_view<char_type>(val);
}
@@ -878,7 +959,8 @@ template <typename Context> struct arg_mapper {
FMT_ENABLE_IF(
std::is_constructible<std_string_view<char_type>, T>::value &&
!std::is_constructible<basic_string_view<char_type>, T>::value &&
!is_string<T>::value && !has_formatter<T, Context>::value)>
!is_string<T>::value && !has_formatter<T, Context>::value &&
!has_fallback_formatter<T, Context>::value)>
FMT_CONSTEXPR basic_string_view<char_type> map(const T& val) {
return std_string_view<char_type>(val);
}
@@ -907,18 +989,15 @@ template <typename Context> struct arg_mapper {
FMT_ENABLE_IF(std::is_enum<T>::value &&
!has_formatter<T, Context>::value &&
!has_fallback_formatter<T, Context>::value)>
FMT_CONSTEXPR auto map(const T& val) -> decltype(
map(static_cast<typename std::underlying_type<T>::type>(val))) {
FMT_CONSTEXPR auto map(const T& val)
-> decltype(std::declval<arg_mapper>().map(
static_cast<typename std::underlying_type<T>::type>(val))) {
return map(static_cast<typename std::underlying_type<T>::type>(val));
}
template <
typename T,
FMT_ENABLE_IF(
!is_string<T>::value && !is_char<T>::value &&
!std::is_constructible<basic_string_view<char_type>, T>::value &&
(has_formatter<T, Context>::value ||
(has_fallback_formatter<T, Context>::value &&
!std::is_constructible<std_string_view<char_type>, T>::value)))>
template <typename T,
FMT_ENABLE_IF(!is_string<T>::value && !is_char<T>::value &&
(has_formatter<T, Context>::value ||
has_fallback_formatter<T, Context>::value))>
FMT_CONSTEXPR const T& map(const T& val) {
return val;
}
@@ -930,6 +1009,16 @@ template <typename Context> struct arg_mapper {
std::memcpy(val.data, &arg, sizeof(arg));
return val;
}
int map(...) {
constexpr bool formattable = sizeof(Context) == 0;
static_assert(
formattable,
"Cannot format argument. To make type T formattable provide a "
"formatter<T> specialization: "
"https://fmt.dev/latest/api.html#formatting-user-defined-types");
return 0;
}
};
// A type constant after applying arg_mapper<Context>.
@@ -981,10 +1070,10 @@ template <typename Context> class basic_format_arg {
internal::custom_value<Context> custom_;
};
FMT_CONSTEXPR basic_format_arg() : type_(internal::none_type) {}
FMT_CONSTEXPR basic_format_arg() : type_(internal::type::none_type) {}
FMT_CONSTEXPR explicit operator bool() const FMT_NOEXCEPT {
return type_ != internal::none_type;
return type_ != internal::type::none_type;
}
internal::type type() const { return type_; }
@@ -1006,47 +1095,47 @@ FMT_CONSTEXPR auto visit_format_arg(Visitor&& vis,
-> decltype(vis(0)) {
using char_type = typename Context::char_type;
switch (arg.type_) {
case internal::none_type:
case internal::type::none_type:
break;
case internal::named_arg_type:
case internal::type::named_arg_type:
FMT_ASSERT(false, "invalid argument type");
break;
case internal::int_type:
case internal::type::int_type:
return vis(arg.value_.int_value);
case internal::uint_type:
case internal::type::uint_type:
return vis(arg.value_.uint_value);
case internal::long_long_type:
case internal::type::long_long_type:
return vis(arg.value_.long_long_value);
case internal::ulong_long_type:
case internal::type::ulong_long_type:
return vis(arg.value_.ulong_long_value);
#if FMT_USE_INT128
case internal::int128_type:
case internal::type::int128_type:
return vis(arg.value_.int128_value);
case internal::uint128_type:
case internal::type::uint128_type:
return vis(arg.value_.uint128_value);
#else
case internal::int128_type:
case internal::uint128_type:
case internal::type::int128_type:
case internal::type::uint128_type:
break;
#endif
case internal::bool_type:
case internal::type::bool_type:
return vis(arg.value_.bool_value);
case internal::char_type:
case internal::type::char_type:
return vis(arg.value_.char_value);
case internal::float_type:
case internal::type::float_type:
return vis(arg.value_.float_value);
case internal::double_type:
case internal::type::double_type:
return vis(arg.value_.double_value);
case internal::long_double_type:
case internal::type::long_double_type:
return vis(arg.value_.long_double_value);
case internal::cstring_type:
case internal::type::cstring_type:
return vis(arg.value_.string.data);
case internal::string_type:
case internal::type::string_type:
return vis(basic_string_view<char_type>(arg.value_.string.data,
arg.value_.string.size));
case internal::pointer_type:
case internal::type::pointer_type:
return vis(arg.value_.pointer);
case internal::custom_type:
case internal::type::custom_type:
return vis(typename basic_format_arg<Context>::handle(arg.value_.custom));
}
return vis(monostate());
@@ -1106,7 +1195,7 @@ template <typename> constexpr unsigned long long encode_types() { return 0; }
template <typename Context, typename Arg, typename... Args>
constexpr unsigned long long encode_types() {
return mapped_type_constant<Arg, Context>::value |
return static_cast<unsigned>(mapped_type_constant<Arg, Context>::value) |
(encode_types<Context, Args...>() << packed_arg_bits);
}
@@ -1129,6 +1218,43 @@ template <bool IS_PACKED, typename Context, typename T,
inline basic_format_arg<Context> make_arg(const T& value) {
return make_arg<Context>(value);
}
template <typename T> struct is_reference_wrapper : std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
class dynamic_arg_list {
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So storage_node_base is made a fake template.
template <typename = void> struct node {
virtual ~node() = default;
std::unique_ptr<node<>> next;
};
template <typename T> struct typed_node : node<> {
T value;
template <typename Arg>
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
template <typename Char>
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
: value(arg.data(), arg.size()) {}
};
std::unique_ptr<node<>> head_;
public:
template <typename T, typename Arg> const T& push(const Arg& arg) {
auto node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
auto& value = node->value;
node->next = std::move(head_);
head_ = std::move(node);
return value;
}
};
} // namespace internal
// Formatting context.
@@ -1191,7 +1317,13 @@ using wformat_context = buffer_context<wchar_t>;
such as `~fmt::vformat`.
\endrst
*/
template <typename Context, typename... Args> class format_arg_store {
template <typename Context, typename... Args>
class format_arg_store
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround a GCC template argument substitution bug.
: public basic_format_args<Context>
#endif
{
private:
static const size_t num_args = sizeof...(Args);
static const bool is_packed = num_args < internal::max_packed_args;
@@ -1210,7 +1342,12 @@ template <typename Context, typename... Args> class format_arg_store {
: internal::is_unpacked_bit | num_args;
format_arg_store(const Args&... args)
: data_{internal::make_arg<is_packed, Context>(args)...} {}
:
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
basic_format_args<Context>(*this),
#endif
data_{internal::make_arg<is_packed, Context>(args)...} {
}
};
/**
@@ -1227,7 +1364,112 @@ inline format_arg_store<Context, Args...> make_format_args(
return {args...};
}
/** Formatting arguments. */
/**
\rst
A dynamic version of `fmt::format_arg_store<>`.
It's equipped with a storage to potentially temporary objects which lifetime
could be shorter than the format arguments object.
It can be implicitly converted into `~fmt::basic_format_args` for passing
into type-erased formatting functions such as `~fmt::vformat`.
\endrst
*/
template <typename Context>
class dynamic_format_arg_store
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround a GCC template argument substitution bug.
: public basic_format_args<Context>
#endif
{
private:
using char_type = typename Context::char_type;
template <typename T> struct need_copy {
static constexpr internal::type mapped_type =
internal::mapped_type_constant<T, Context>::value;
enum {
value = !(internal::is_reference_wrapper<T>::value ||
std::is_same<T, basic_string_view<char_type>>::value ||
std::is_same<T, internal::std_string_view<char_type>>::value ||
(mapped_type != internal::type::cstring_type &&
mapped_type != internal::type::string_type &&
mapped_type != internal::type::custom_type &&
mapped_type != internal::type::named_arg_type))
};
};
template <typename T>
using stored_type = conditional_t<internal::is_string<T>::value,
std::basic_string<char_type>, T>;
// Storage of basic_format_arg must be contiguous.
std::vector<basic_format_arg<Context>> data_;
// Storage of arguments not fitting into basic_format_arg must grow
// without relocation because items in data_ refer to it.
internal::dynamic_arg_list dynamic_args_;
friend class basic_format_args<Context>;
unsigned long long get_types() const {
return internal::is_unpacked_bit | data_.size();
}
template <typename T> void emplace_arg(const T& arg) {
data_.emplace_back(internal::make_arg<Context>(arg));
}
public:
/**
\rst
Adds an argument into the dynamic store for later passing to a formating
function.
Note that custom types and string types (but not string views!) are copied
into the store with dynamic memory (in addition to resizing vector).
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
store.push_back("abc");
store.push_back(1.5f);
std::string result = fmt::vformat("{} and {} and {}", store);
\endrst
*/
template <typename T> void push_back(const T& arg) {
static_assert(
!std::is_base_of<internal::named_arg_base<char_type>, T>::value,
"named arguments are not supported yet");
if (internal::const_check(need_copy<T>::value))
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
else
emplace_arg(arg);
}
/**
Adds a reference to the argument into the dynamic store for later passing to
a formating function.
*/
template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert(
need_copy<T>::value,
"objects of built-in types and string views are always copied");
emplace_arg(arg.get());
}
};
/**
\rst
A view of a collection of formatting arguments. To avoid lifetime issues it
should only be used as a parameter type in type-erased functions such as
``vformat``::
void vlog(string_view format_str, format_args args); // OK
format_args args = make_format_args(42); // Error: dangling reference
\endrst
*/
template <typename Context> class basic_format_args {
public:
using size_type = int;
@@ -1269,7 +1511,7 @@ template <typename Context> class basic_format_args {
}
if (index > internal::max_packed_args) return arg;
arg.type_ = type(index);
if (arg.type_ == internal::none_type) return arg;
if (arg.type_ == internal::type::none_type) return arg;
internal::value<Context>& val = arg.value_;
val = values_[index];
return arg;
@@ -1289,6 +1531,17 @@ template <typename Context> class basic_format_args {
set_data(store.data_);
}
/**
\rst
Constructs a `basic_format_args` object from
`~fmt::dynamic_format_arg_store`.
\endrst
*/
basic_format_args(const dynamic_format_arg_store<Context>& store)
: types_(store.get_types()) {
set_data(store.data_.data());
}
/**
\rst
Constructs a `basic_format_args` object from a dynamic set of arguments.
@@ -1302,7 +1555,7 @@ template <typename Context> class basic_format_args {
/** Returns the argument at specified index. */
format_arg get(int index) const {
format_arg arg = do_get(index);
if (arg.type_ == internal::named_arg_type)
if (arg.type_ == internal::type::named_arg_type)
arg = arg.value_.named_arg->template deserialize<Context>();
return arg;
}
@@ -1319,12 +1572,12 @@ template <typename Context> class basic_format_args {
struct format_args : basic_format_args<format_context> {
template <typename... Args>
format_args(Args&&... args)
: basic_format_args<format_context>(std::forward<Args>(args)...) {}
: basic_format_args<format_context>(static_cast<Args&&>(args)...) {}
};
struct wformat_args : basic_format_args<wformat_context> {
template <typename... Args>
wformat_args(Args&&... args)
: basic_format_args<wformat_context>(std::forward<Args>(args)...) {}
: basic_format_args<wformat_context>(static_cast<Args&&>(args)...) {}
};
template <typename Container> struct is_contiguous : std::false_type {};
@@ -1358,7 +1611,10 @@ template <typename Char> struct named_arg_base {
}
};
template <typename T, typename Char> struct named_arg : named_arg_base<Char> {
struct view {};
template <typename T, typename Char>
struct named_arg : view, named_arg_base<Char> {
const T& value;
named_arg(basic_string_view<Char> name, const T& val)
@@ -1376,7 +1632,6 @@ inline void check_format_string(const S&) {
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
void check_format_string(S);
struct view {};
template <bool...> struct bool_pack;
template <bool... Args>
using all_true =
@@ -1386,32 +1641,38 @@ template <typename... Args, typename S, typename Char = char_t<S>>
inline format_arg_store<buffer_context<Char>, remove_reference_t<Args>...>
make_args_checked(const S& format_str,
const remove_reference_t<Args>&... args) {
static_assert(all_true<(!std::is_base_of<view, remove_reference_t<Args>>() ||
!std::is_reference<Args>())...>::value,
"passing views as lvalues is disallowed");
check_format_string<remove_const_t<remove_reference_t<Args>>...>(format_str);
static_assert(
all_true<(!std::is_base_of<view, remove_reference_t<Args>>::value ||
!std::is_reference<Args>::value)...>::value,
"passing views as lvalues is disallowed");
check_format_string<Args...>(format_str);
return {args...};
}
template <typename Char>
std::basic_string<Char> vformat(basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args);
std::basic_string<Char> vformat(
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args);
template <typename Char>
typename buffer_context<Char>::iterator vformat_to(
buffer<Char>& buf, basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args);
basic_format_args<buffer_context<type_identity_t<Char>>> args);
template <typename Char, typename Args,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
inline void vprint_mojibake(std::FILE*, basic_string_view<Char>, const Args&) {}
FMT_API void vprint_mojibake(std::FILE*, string_view, format_args);
#ifndef _WIN32
inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
#endif
} // namespace internal
/**
\rst
Returns a named argument to be used in a formatting function.
The named argument holds a reference and does not extend the lifetime
of its arguments.
Consequently, a dangling reference can accidentally be created.
The user should take care to only pass this function temporaries when
the named argument is itself a temporary, as per the following example.
Returns a named argument to be used in a formatting function. It should only
be used in a call to a formatting function.
**Example**::
@@ -1434,8 +1695,9 @@ void arg(S, internal::named_arg<T, Char>) = delete;
template <typename OutputIt, typename S, typename Char = char_t<S>,
FMT_ENABLE_IF(
internal::is_contiguous_back_insert_iterator<OutputIt>::value)>
OutputIt vformat_to(OutputIt out, const S& format_str,
basic_format_args<buffer_context<Char>> args) {
OutputIt vformat_to(
OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
using container = remove_reference_t<decltype(internal::get_container(out))>;
internal::container_buffer<container> buf((internal::get_container(out)));
internal::vformat_to(buf, to_string_view(format_str), args);
@@ -1448,14 +1710,14 @@ template <typename Container, typename S, typename... Args,
inline std::back_insert_iterator<Container> format_to(
std::back_insert_iterator<Container> out, const S& format_str,
Args&&... args) {
return vformat_to(
out, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
return vformat_to(out, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
}
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const S& format_str, basic_format_args<buffer_context<Char>> args) {
const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return internal::vformat(to_string_view(format_str), args);
}
@@ -1475,44 +1737,52 @@ template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const S& format_str, Args&&... args) {
return internal::vformat(
to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
internal::make_args_checked<Args...>(format_str, args...));
}
FMT_API void vprint(std::FILE* f, string_view format_str, format_args args);
FMT_API void vprint(string_view format_str, format_args args);
FMT_API void vprint(string_view, format_args);
FMT_API void vprint(std::FILE*, string_view, format_args);
/**
\rst
Prints formatted data to the file *f*. For wide format strings,
*f* should be in wide-oriented mode set via ``fwide(f, 1)`` or
``_setmode(_fileno(f), _O_U8TEXT)`` on Windows.
Formats ``args`` according to specifications in ``format_str`` and writes the
output to the file ``f``. Strings are assumed to be Unicode-encoded unless the
``FMT_UNICODE`` macro is set to 0.
**Example**::
fmt::print(stderr, "Don't {}!", "panic");
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
template <typename S, typename... Args, typename Char = char_t<S>>
inline void print(std::FILE* f, const S& format_str, Args&&... args) {
vprint(f, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
return internal::is_unicode<Char>()
? vprint(f, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...))
: internal::vprint_mojibake(
f, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
}
/**
\rst
Prints formatted data to ``stdout``.
Formats ``args`` according to specifications in ``format_str`` and writes
the output to ``stdout``. Strings are assumed to be Unicode-encoded unless
the ``FMT_UNICODE`` macro is set to 0.
**Example**::
fmt::print("Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
template <typename S, typename... Args, typename Char = char_t<S>>
inline void print(const S& format_str, Args&&... args) {
vprint(to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
return internal::is_unicode<Char>()
? vprint(to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...))
: internal::vprint_mojibake(
stdout, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@@ -8,8 +8,6 @@
#ifndef FMT_FORMAT_INL_H_
#define FMT_FORMAT_INL_H_
#include "format.h"
#include <cassert>
#include <cctype>
#include <climits>
@@ -17,29 +15,15 @@
#include <cstdarg>
#include <cstring> // for std::memmove
#include <cwchar>
#include "format.h"
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
# include <locale>
#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)
#ifdef _WIN32
# include <io.h>
# include <windows.h>
#endif
#ifdef _MSC_VER
@@ -73,8 +57,6 @@ inline int fmt_snprintf(char* buffer, size_t size, const char* format, ...) {
# define FMT_SNPRINTF fmt_snprintf
#endif // _MSC_VER
using format_func = void (*)(internal::buffer<char>&, int, string_view);
// A 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,
@@ -104,6 +86,7 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
}
// Handle the result of GNU-specific version of strerror_r.
FMT_MAYBE_UNUSED
int handle(char* message) {
// If the buffer is full then the message is probably truncated.
if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
@@ -113,11 +96,13 @@ FMT_FUNC int safe_strerror(int error_code, char*& buffer,
}
// Handle the case when strerror_r is not available.
FMT_MAYBE_UNUSED
int handle(internal::null<>) {
return fallback(strerror_s(buffer_, buffer_size_, error_code_));
}
// Fallback to strerror_s when strerror_r is not available.
FMT_MAYBE_UNUSED
int fallback(int result) {
// If the buffer is full then the message is probably truncated.
return result == 0 && strlen(buffer_) == buffer_size_ - 1 ? ERANGE
@@ -168,15 +153,6 @@ FMT_FUNC void format_error_code(internal::buffer<char>& out, int error_code,
assert(out.size() <= inline_buffer_size);
}
// A wrapper around fwrite that throws on error.
FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count,
FILE* stream) {
size_t written = std::fwrite(ptr, size, count, stream);
if (written < count) {
FMT_THROW(system_error(errno, "cannot write to file"));
}
}
FMT_FUNC void report_error(format_func func, int error_code,
string_view message) FMT_NOEXCEPT {
memory_buffer full_message;
@@ -185,6 +161,13 @@ FMT_FUNC void report_error(format_func func, int error_code,
(void)std::fwrite(full_message.data(), full_message.size(), 1, stderr);
std::fputc('\n', stderr);
}
// A wrapper around fwrite that throws on error.
FMT_FUNC void fwrite_fully(const void* ptr, size_t size, size_t count,
FILE* stream) {
size_t written = std::fwrite(ptr, size, count, stream);
if (written < count) FMT_THROW(system_error(errno, "cannot write to file"));
}
} // namespace internal
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
@@ -356,6 +339,10 @@ class fp {
private:
using significand_type = uint64_t;
public:
significand_type f;
int e;
// All sizes are in bits.
// Subtract 1 to account for an implicit most significant bit in the
// normalized form.
@@ -363,11 +350,6 @@ class fp {
std::numeric_limits<double>::digits - 1;
static FMT_CONSTEXPR_DECL const uint64_t implicit_bit =
1ULL << double_significand_size;
public:
significand_type f;
int e;
static FMT_CONSTEXPR_DECL const int significand_size =
bits<significand_type>::value;
@@ -378,22 +360,6 @@ class fp {
// errors on platforms where double is not IEEE754.
template <typename Double> explicit fp(Double d) { assign(d); }
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
template <int SHIFT> friend fp normalize(fp value) {
// Handle subnormals.
const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
while ((value.f & shifted_implicit_bit) == 0) {
value.f <<= 1;
--value.e;
}
// Subtract 1 to account for hidden bit.
const auto offset =
fp::significand_size - fp::double_significand_size - SHIFT - 1;
value.f <<= offset;
value.e -= offset;
return value;
}
// Assigns d to this and return true iff predecessor is closer than successor.
template <typename Double, FMT_ENABLE_IF(sizeof(Double) == sizeof(uint64_t))>
bool assign(Double d) {
@@ -406,7 +372,8 @@ class fp {
const int exponent_bias = (1 << exponent_size) - limits::max_exponent - 1;
auto u = bit_cast<uint64_t>(d);
f = u & significand_mask;
auto biased_e = (u & exponent_mask) >> double_significand_size;
int biased_e =
static_cast<int>((u & exponent_mask) >> double_significand_size);
// Predecessor is closer if d is a normalized power of 2 (f == 0) other than
// the smallest normalized number (biased_e > 1).
bool is_predecessor_closer = f == 0 && biased_e > 1;
@@ -414,7 +381,7 @@ class fp {
f += implicit_bit;
else
biased_e = 1; // Subnormals use biased exponent 1 (min exponent).
e = static_cast<int>(biased_e - exponent_bias - double_significand_size);
e = biased_e - exponent_bias - double_significand_size;
return is_predecessor_closer;
}
@@ -453,6 +420,22 @@ class fp {
}
};
// Normalizes the value converted from double and multiplied by (1 << SHIFT).
template <int SHIFT> fp normalize(fp value) {
// Handle subnormals.
const auto shifted_implicit_bit = fp::implicit_bit << SHIFT;
while ((value.f & shifted_implicit_bit) == 0) {
value.f <<= 1;
--value.e;
}
// Subtract 1 to account for hidden bit.
const auto offset =
fp::significand_size - fp::double_significand_size - SHIFT - 1;
value.f <<= offset;
value.e -= offset;
return value;
}
inline bool operator==(fp x, fp y) { return x.f == y.f && x.e == y.e; }
// Computes lhs * rhs / pow(2, 64) rounded to nearest with half-up tie breaking.
@@ -477,14 +460,12 @@ inline fp operator*(fp x, fp y) { return {multiply(x.f, y.f), x.e + y.e + 64}; }
// Returns a cached power of 10 `c_k = c_k.f * pow(2, c_k.e)` such that its
// (binary) exponent satisfies `min_exponent <= c_k.e <= min_exponent + 28`.
FMT_FUNC fp get_cached_power(int min_exponent, int& pow10_exponent) {
const uint64_t one_over_log2_10 = 0x4d104d42; // round(pow(2, 32) / log2(10))
inline fp get_cached_power(int min_exponent, int& pow10_exponent) {
const int64_t one_over_log2_10 = 0x4d104d42; // round(pow(2, 32) / log2(10))
int index = static_cast<int>(
static_cast<int64_t>(
(min_exponent + fp::significand_size - 1) * one_over_log2_10 +
((uint64_t(1) << 32) - 1) // ceil
) >>
32 // arithmetic shift
((min_exponent + fp::significand_size - 1) * one_over_log2_10 +
((int64_t(1) << 32) - 1)) // ceil
>> 32 // arithmetic shift
);
// Decimal exponent of the first (smallest) cached power of 10.
const int first_dec_exp = -348;
@@ -526,20 +507,23 @@ class bigint {
basic_memory_buffer<bigit, bigits_capacity> bigits_;
int exp_;
bigit operator[](int index) const { return bigits_[to_unsigned(index)]; }
bigit& operator[](int index) { return bigits_[to_unsigned(index)]; }
static FMT_CONSTEXPR_DECL const int bigit_bits = bits<bigit>::value;
friend struct formatter<bigint>;
void subtract_bigits(int index, bigit other, bigit& borrow) {
auto result = static_cast<double_bigit>(bigits_[index]) - other - borrow;
bigits_[index] = static_cast<bigit>(result);
auto result = static_cast<double_bigit>((*this)[index]) - other - borrow;
(*this)[index] = static_cast<bigit>(result);
borrow = static_cast<bigit>(result >> (bigit_bits * 2 - 1));
}
void remove_leading_zeros() {
int num_bigits = static_cast<int>(bigits_.size()) - 1;
while (num_bigits > 0 && bigits_[num_bigits] == 0) --num_bigits;
bigits_.resize(num_bigits + 1);
while (num_bigits > 0 && (*this)[num_bigits] == 0) --num_bigits;
bigits_.resize(to_unsigned(num_bigits + 1));
}
// Computes *this -= other assuming aligned bigints and *this >= other.
@@ -548,8 +532,7 @@ class bigint {
FMT_ASSERT(compare(*this, other) >= 0, "");
bigit borrow = 0;
int i = other.exp_ - exp_;
for (int j = 0, n = static_cast<int>(other.bigits_.size()); j != n;
++i, ++j) {
for (size_t j = 0, n = other.bigits_.size(); j != n; ++i, ++j) {
subtract_bigits(i, other.bigits_[j], borrow);
}
while (borrow > 0) subtract_bigits(i, 0, borrow);
@@ -600,7 +583,7 @@ class bigint {
}
void assign(uint64_t n) {
int num_bigits = 0;
size_t num_bigits = 0;
do {
bigits_[num_bigits++] = n & ~bigit(0);
n >>= bigit_bits;
@@ -641,7 +624,7 @@ class bigint {
int end = i - j;
if (end < 0) end = 0;
for (; i >= end; --i, --j) {
bigit lhs_bigit = lhs.bigits_[i], rhs_bigit = rhs.bigits_[j];
bigit lhs_bigit = lhs[i], rhs_bigit = rhs[j];
if (lhs_bigit != rhs_bigit) return lhs_bigit > rhs_bigit ? 1 : -1;
}
if (i != j) return i > j ? 1 : -1;
@@ -656,7 +639,7 @@ class bigint {
if (max_lhs_bigits + 1 < num_rhs_bigits) return -1;
if (max_lhs_bigits > num_rhs_bigits) return 1;
auto get_bigit = [](const bigint& n, int i) -> bigit {
return i >= n.exp_ && i < n.num_bigits() ? n.bigits_[i - n.exp_] : 0;
return i >= n.exp_ && i < n.num_bigits() ? n[i - n.exp_] : 0;
};
double_bigit borrow = 0;
int min_exp = (std::min)((std::min)(lhs1.exp_, lhs2.exp_), rhs.exp_);
@@ -696,7 +679,7 @@ class bigint {
basic_memory_buffer<bigit, bigits_capacity> n(std::move(bigits_));
int num_bigits = static_cast<int>(bigits_.size());
int num_result_bigits = 2 * num_bigits;
bigits_.resize(num_result_bigits);
bigits_.resize(to_unsigned(num_result_bigits));
using accumulator_t = conditional_t<FMT_USE_INT128, uint128_t, accumulator>;
auto sum = accumulator_t();
for (int bigit_index = 0; bigit_index < num_bigits; ++bigit_index) {
@@ -706,7 +689,7 @@ class bigint {
// Most terms are multiplied twice which can be optimized in the future.
sum += static_cast<double_bigit>(n[i]) * n[j];
}
bigits_[bigit_index] = static_cast<bigit>(sum);
(*this)[bigit_index] = static_cast<bigit>(sum);
sum >>= bits<bigit>::value; // Compute the carry.
}
// Do the same for the top half.
@@ -714,7 +697,7 @@ class bigint {
++bigit_index) {
for (int j = num_bigits - 1, i = bigit_index - j; i < num_bigits;)
sum += static_cast<double_bigit>(n[i++]) * n[j--];
bigits_[bigit_index] = static_cast<bigit>(sum);
(*this)[bigit_index] = static_cast<bigit>(sum);
sum >>= bits<bigit>::value;
}
--num_result_bigits;
@@ -728,11 +711,11 @@ class bigint {
FMT_ASSERT(this != &divisor, "");
if (compare(*this, divisor) < 0) return 0;
int num_bigits = static_cast<int>(bigits_.size());
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1] != 0, "");
FMT_ASSERT(divisor.bigits_[divisor.bigits_.size() - 1u] != 0, "");
int exp_difference = exp_ - divisor.exp_;
if (exp_difference > 0) {
// Align bigints by adding trailing zeros to simplify subtraction.
bigits_.resize(num_bigits + exp_difference);
bigits_.resize(to_unsigned(num_bigits + exp_difference));
for (int i = num_bigits - 1, j = i + exp_difference; i >= 0; --i, --j)
bigits_[j] = bigits_[i];
std::uninitialized_fill_n(bigits_.data(), exp_difference, 0);
@@ -747,7 +730,7 @@ class bigint {
}
};
enum round_direction { unknown, up, down };
enum class round_direction { unknown, up, down };
// Given the divisor (normally a power of 10), the remainder = v % divisor for
// some number v and the error, returns whether v should be rounded up, down, or
@@ -760,13 +743,13 @@ inline round_direction get_round_direction(uint64_t divisor, uint64_t remainder,
FMT_ASSERT(error < divisor - error, ""); // error * 2 won't overflow.
// Round down if (remainder + error) * 2 <= divisor.
if (remainder <= divisor - remainder && error * 2 <= divisor - remainder * 2)
return down;
return round_direction::down;
// Round up if (remainder - error) * 2 >= divisor.
if (remainder >= error &&
remainder - error >= divisor - (remainder - error)) {
return up;
return round_direction::up;
}
return unknown;
return round_direction::unknown;
}
namespace digits {
@@ -777,6 +760,20 @@ enum result {
};
}
// A version of count_digits optimized for grisu_gen_digits.
inline int grisu_count_digits(uint32_t n) {
if (n < 10) return 1;
if (n < 100) return 2;
if (n < 1000) return 3;
if (n < 10000) return 4;
if (n < 100000) return 5;
if (n < 1000000) return 6;
if (n < 10000000) return 7;
if (n < 100000000) return 8;
if (n < 1000000000) return 9;
return 10;
}
// Generates output using the Grisu digit-gen algorithm.
// error: the size of the region (lower, upper) outside of which numbers
// definitely do not round to value (Delta in Grisu3).
@@ -792,7 +789,7 @@ FMT_ALWAYS_INLINE digits::result grisu_gen_digits(fp value, uint64_t error,
FMT_ASSERT(integral == value.f >> -one.e, "");
// The fractional part of scaled value (p2 in Grisu) c = value % one.
uint64_t fractional = value.f & (one.f - 1);
exp = count_digits(integral); // kappa in Grisu.
exp = grisu_count_digits(integral); // kappa in Grisu.
// Divide by 10 to prevent overflow.
auto result = handler.on_start(data::powers_of_10_64[exp - 1] << -one.e,
value.f / 10, error * 10, exp);
@@ -882,8 +879,8 @@ struct fixed_handler {
if (precision > 0) return digits::more;
if (precision < 0) return digits::done;
auto dir = get_round_direction(divisor, remainder, error);
if (dir == unknown) return digits::error;
buf[size++] = dir == up ? '1' : '0';
if (dir == round_direction::unknown) return digits::error;
buf[size++] = dir == round_direction::up ? '1' : '0';
return digits::done;
}
@@ -901,7 +898,8 @@ struct fixed_handler {
FMT_ASSERT(error == 1 && divisor > 2, "");
}
auto dir = get_round_direction(divisor, remainder, error);
if (dir != up) return dir == down ? digits::done : digits::error;
if (dir != round_direction::up)
return dir == round_direction::down ? digits::done : digits::error;
++buf[size - 1];
for (int i = size - 1; i > 0 && buf[i] > '9'; --i) {
buf[i] = '0';
@@ -1028,7 +1026,7 @@ void fallback_format(Double d, buffer<char>& buf, int& exp10) {
if (result > 0 || (result == 0 && (digit % 2) != 0))
++data[num_digits - 1];
}
buf.resize(num_digits);
buf.resize(to_unsigned(num_digits));
exp10 -= num_digits - 1;
return;
}
@@ -1043,7 +1041,7 @@ void fallback_format(Double d, buffer<char>& buf, int& exp10) {
// if T is a IEEE754 binary32 or binary64 and snprintf otherwise.
template <typename T>
int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
static_assert(!std::is_same<T, float>(), "");
static_assert(!std::is_same<T, float>::value, "");
FMT_ASSERT(value >= 0, "value is negative");
const bool fixed = specs.format == float_format::fixed;
@@ -1062,25 +1060,7 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
int exp = 0;
const int min_exp = -60; // alpha in Grisu.
int cached_exp10 = 0; // K in Grisu.
if (precision != -1) {
if (precision > 17) return snprintf_float(value, precision, specs, buf);
fp normalized = normalize(fp(value));
const auto cached_pow = get_cached_power(
min_exp - (normalized.e + fp::significand_size), cached_exp10);
normalized = normalized * cached_pow;
fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error)
return snprintf_float(value, precision, specs, buf);
int num_digits = handler.size;
if (!fixed) {
// Remove trailing zeros.
while (num_digits > 0 && buf[num_digits - 1] == '0') {
--num_digits;
++exp;
}
}
buf.resize(to_unsigned(num_digits));
} else {
if (precision < 0) {
fp fp_value;
auto boundaries = specs.binary32
? fp_value.assign_float_with_boundaries(value)
@@ -1109,6 +1089,24 @@ int format_float(T value, int precision, float_specs specs, buffer<char>& buf) {
return exp;
}
buf.resize(to_unsigned(handler.size));
} else {
if (precision > 17) return snprintf_float(value, precision, specs, buf);
fp normalized = normalize(fp(value));
const auto cached_pow = get_cached_power(
min_exp - (normalized.e + fp::significand_size), cached_exp10);
normalized = normalized * cached_pow;
fixed_handler handler{buf.data(), 0, precision, -cached_exp10, fixed};
if (grisu_gen_digits(normalized, 1, exp, handler) == digits::error)
return snprintf_float(value, precision, specs, buf);
int num_digits = handler.size;
if (!fixed) {
// Remove trailing zeros.
while (num_digits > 0 && buf[num_digits - 1] == '0') {
--num_digits;
++exp;
}
}
buf.resize(to_unsigned(num_digits));
}
return exp - cached_exp10;
}
@@ -1118,7 +1116,7 @@ int snprintf_float(T value, int precision, float_specs specs,
buffer<char>& buf) {
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
FMT_ASSERT(buf.capacity() > buf.size(), "empty buffer");
static_assert(!std::is_same<T, float>(), "");
static_assert(!std::is_same<T, float>::value, "");
// Subtract 1 to account for the difference in precision since we use %e for
// both general and exponent format.
@@ -1131,7 +1129,7 @@ int snprintf_float(T value, int precision, float_specs specs,
char format[max_format_size];
char* format_ptr = format;
*format_ptr++ = '%';
if (specs.trailing_zeros) *format_ptr++ = '#';
if (specs.showpoint && specs.format == float_format::hex) *format_ptr++ = '#';
if (precision >= 0) {
*format_ptr++ = '.';
*format_ptr++ = '*';
@@ -1153,7 +1151,8 @@ int snprintf_float(T value, int precision, float_specs specs,
"fuzz mode - avoid large allocation inside snprintf");
#endif
// Suppress the warning about a nonliteral format string.
auto snprintf_ptr = FMT_SNPRINTF;
// Cannot use auto becase of a bug in MinGW (#1532).
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
int result = precision >= 0
? snprintf_ptr(begin, capacity, format, precision, value)
: snprintf_ptr(begin, capacity, format, value);
@@ -1161,7 +1160,7 @@ int snprintf_float(T value, int precision, float_specs specs,
buf.reserve(buf.capacity() + 1); // The buffer will grow exponentially.
continue;
}
unsigned size = to_unsigned(result);
auto size = to_unsigned(result);
// Size equal to capacity means that the last character was truncated.
if (size >= capacity) {
buf.reserve(size + offset + 1); // Add 1 for the terminating '\0'.
@@ -1179,7 +1178,7 @@ int snprintf_float(T value, int precision, float_specs specs,
--p;
} while (is_digit(*p));
int fraction_size = static_cast<int>(end - p - 1);
std::memmove(p, p + 1, fraction_size);
std::memmove(p, p + 1, to_unsigned(fraction_size));
buf.resize(size - 1);
return -fraction_size;
}
@@ -1208,12 +1207,67 @@ int snprintf_float(T value, int precision, float_specs specs,
while (*fraction_end == '0') --fraction_end;
// Move the fractional part left to get rid of the decimal point.
fraction_size = static_cast<int>(fraction_end - begin - 1);
std::memmove(begin + 1, begin + 2, fraction_size);
std::memmove(begin + 1, begin + 2, to_unsigned(fraction_size));
}
buf.resize(fraction_size + offset + 1);
buf.resize(to_unsigned(fraction_size) + offset + 1);
return exp - fraction_size;
}
}
// A public domain branchless UTF-8 decoder by Christopher Wellons:
// https://github.com/skeeto/branchless-utf8
/* Decode the next character, c, from buf, reporting errors in e.
*
* Since this is a branchless decoder, four bytes will be read from the
* buffer regardless of the actual length of the next character. This
* means the buffer _must_ have at least three bytes of zero padding
* following the end of the data stream.
*
* Errors are reported in e, which will be non-zero if the parsed
* character was somehow invalid: invalid byte sequence, non-canonical
* encoding, or a surrogate half.
*
* The function returns a pointer to the next character. When an error
* occurs, this pointer will be a guess that depends on the particular
* error, but it will always advance at least one byte.
*/
FMT_FUNC const char* utf8_decode(const char* buf, uint32_t* c, int* e) {
static const char lengths[] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 2, 2, 2, 2, 3, 3, 4, 0};
static const int masks[] = {0x00, 0x7f, 0x1f, 0x0f, 0x07};
static const uint32_t mins[] = {4194304, 0, 128, 2048, 65536};
static const int shiftc[] = {0, 18, 12, 6, 0};
static const int shifte[] = {0, 6, 4, 2, 0};
auto s = reinterpret_cast<const unsigned char*>(buf);
int len = lengths[s[0] >> 3];
// Compute the pointer to the next character early so that the next
// iteration can start working on the next character. Neither Clang
// nor GCC figure out this reordering on their own.
const char* next = buf + len + !len;
// Assume a four-byte character and load four bytes. Unused bits are
// shifted out.
*c = uint32_t(s[0] & masks[len]) << 18;
*c |= uint32_t(s[1] & 0x3f) << 12;
*c |= uint32_t(s[2] & 0x3f) << 6;
*c |= uint32_t(s[3] & 0x3f) << 0;
*c >>= shiftc[len];
// Accumulate the various error conditions.
*e = (*c < mins[len]) << 6; // non-canonical encoding
*e |= ((*c >> 11) == 0x1b) << 7; // surrogate half?
*e |= (*c > 0x10FFFF) << 8; // out of range?
*e |= (s[1] & 0xc0) >> 2;
*e |= (s[2] & 0xc0) >> 4;
*e |= (s[3]) >> 6;
*e ^= 0x2a; // top two bits of each tail byte correct?
*e >>= shifte[len];
return next;
}
} // namespace internal
template <> struct formatter<internal::bigint> {
@@ -1226,7 +1280,7 @@ template <> struct formatter<internal::bigint> {
auto out = ctx.out();
bool first = true;
for (auto i = n.bigits_.size(); i > 0; --i) {
auto value = n.bigits_[i - 1];
auto value = n.bigits_[i - 1u];
if (first) {
out = format_to(out, "{:x}", value);
first = false;
@@ -1240,101 +1294,37 @@ template <> struct formatter<internal::bigint> {
}
};
#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, nullptr, 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, nullptr, 0,
nullptr, nullptr);
if (length == 0) return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0],
length, nullptr, nullptr);
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<char>& 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, nullptr,
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message,
static_cast<uint32_t>(buf.size()), nullptr);
if (result != 0) {
utf16_to_utf8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
internal::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);
auto transcode = [this](const char* p) {
auto cp = uint32_t();
auto error = 0;
p = utf8_decode(p, &cp, &error);
if (error != 0) FMT_THROW(std::runtime_error("invalid utf8"));
if (cp <= 0xFFFF) {
buffer_.push_back(static_cast<wchar_t>(cp));
} else {
cp -= 0x10000;
buffer_.push_back(static_cast<wchar_t>(0xD800 + (cp >> 10)));
buffer_.push_back(static_cast<wchar_t>(0xDC00 + (cp & 0x3FF)));
}
return p;
};
auto p = s.data();
const size_t block_size = 4; // utf8_decode always reads blocks of 4 chars.
if (s.size() >= block_size) {
for (auto end = p + s.size() - block_size + 1; p < end;) p = transcode(p);
}
FMT_CATCH(...) {}
format_error_code(out, error_code, message);
if (auto num_chars_left = s.data() + s.size() - p) {
char buf[2 * block_size - 1] = {};
memcpy(buf, p, to_unsigned(num_chars_left));
p = buf;
do {
p = transcode(p);
} while (p - buf < num_chars_left);
}
buffer_.push_back(0);
}
#endif // FMT_USE_WINDOWS_H
FMT_FUNC void format_system_error(internal::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT {
FMT_TRY {
@@ -1369,20 +1359,37 @@ FMT_FUNC void report_system_error(int error_code,
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;
internal::vformat_to(buffer, format_str,
basic_format_args<buffer_context<char>>(args));
#ifdef _WIN32
auto fd = _fileno(f);
if (_isatty(fd)) {
internal::utf8_to_utf16 u16(string_view(buffer.data(), buffer.size()));
auto written = DWORD();
if (!WriteConsoleW(reinterpret_cast<HANDLE>(_get_osfhandle(fd)),
u16.c_str(), static_cast<DWORD>(u16.size()), &written,
nullptr)) {
FMT_THROW(format_error("failed to write to console"));
}
return;
}
#endif
internal::fwrite_fully(buffer.data(), 1, buffer.size(), f);
}
#ifdef _WIN32
// Print assuming legacy (non-Unicode) encoding.
FMT_FUNC void internal::vprint_mojibake(std::FILE* f, string_view format_str,
format_args args) {
memory_buffer buffer;
internal::vformat_to(buffer, format_str,
basic_format_args<buffer_context<char>>(args));
fwrite_fully(buffer.data(), 1, buffer.size(), f);
}
#endif
FMT_FUNC void vprint(string_view format_str, format_args args) {
vprint(stdout, format_str, args);
}

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,7 @@
#define FMT_LOCALE_H_
#include <locale>
#include "format.h"
FMT_BEGIN_NAMESPACE
@@ -18,16 +19,16 @@ template <typename Char>
typename buffer_context<Char>::iterator vformat_to(
const std::locale& loc, buffer<Char>& buf,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
using range = buffer_range<Char>;
return vformat_to<arg_formatter<range>>(buf, to_string_view(format_str), args,
internal::locale_ref(loc));
}
template <typename Char>
std::basic_string<Char> vformat(const std::locale& loc,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
std::basic_string<Char> vformat(
const std::locale& loc, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(loc, buffer, format_str, args);
return fmt::to_string(buffer);
@@ -37,7 +38,7 @@ std::basic_string<Char> vformat(const std::locale& loc,
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<Char>> args) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return internal::vformat(loc, to_string_view(format_str), args);
}
@@ -46,15 +47,15 @@ inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) {
return internal::vformat(
loc, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
internal::make_args_checked<Args...>(format_str, args...));
}
template <typename S, typename OutputIt, typename... Args,
typename Char = enable_if_t<
internal::is_output_iterator<OutputIt>::value, char_t<S>>>
inline OutputIt vformat_to(OutputIt out, const std::locale& loc,
const S& format_str,
format_args_t<OutputIt, Char> args) {
inline OutputIt vformat_to(
OutputIt out, const std::locale& loc, const S& format_str,
format_args_t<type_identity_t<OutputIt>, Char> args) {
using range = internal::output_range<OutputIt, Char>;
return vformat_to<arg_formatter<range>>(
range(out), to_string_view(format_str), args, internal::locale_ref(loc));

View File

@@ -93,7 +93,9 @@ void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf);
std::basic_ostream<Char> output(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>());
#endif
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value;
buf.resize(buf.size());
@@ -115,7 +117,7 @@ struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(buffer, format_str, args);
internal::write(os, buffer);
@@ -134,7 +136,7 @@ template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
vprint(os, to_string_view(format_str),
{internal::make_args_checked<Args...>(format_str, args...)});
internal::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@@ -1,321 +1,2 @@
// A C++ interface to POSIX functions.
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_POSIX_H_
#define FMT_POSIX_H_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <cerrno>
#include <clocale> // for locale_t
#include <cstdio>
#include <cstdlib> // for strtod_l
#include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
// UWP doesn't provide _pipe.
#if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h>
#endif
#if FMT_HAS_INCLUDE("fcntl.h") && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# include <fcntl.h> // for O_RDONLY
# define FMT_USE_FCNTL 1
#else
# define FMT_USE_FCNTL 0
#endif
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
(result) = (expression); \
} while ((result) == (error_result) && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
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 type aliases 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_; }
};
using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>;
// An error code.
class error_code {
private:
int value_;
public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; }
};
// A buffered file.
class buffered_file {
private:
FILE* file_;
friend class file;
explicit buffered_file(FILE* f) : file_(f) {}
public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT;
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) {
close();
file_ = other.file_;
other.file_ = nullptr;
return *this;
}
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE* get() const FMT_NOEXCEPT { return file_; }
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
}
template <typename... Args>
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, make_format_args(args...));
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// 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
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class file {
private:
int fd_; // File descriptor.
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
public:
// Possible values for the oflag argument to the constructor.
enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
};
// Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
public:
file(const file&) = delete;
void operator=(const file&) = delete;
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) FMT_NOEXCEPT {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
// Closes the file.
FMT_API void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void* buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API std::size_t write(const void* buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
FMT_API static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
FMT_API static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
FMT_API buffered_file fdopen(const char* mode);
};
// Returns the memory page size.
long getpagesize();
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
class Locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC };
static locale_t newlocale(int category_mask, const char* locale, locale_t) {
return _create_locale(category_mask, locale);
}
static void freelocale(locale_t locale) { _free_locale(locale); }
static double strtod_l(const char* nptr, char** endptr, _locale_t locale) {
return _strtod_l(nptr, endptr, locale);
}
# endif
locale_t locale_;
public:
using type = locale_t;
Locale(const Locale&) = delete;
void operator=(const Locale&) = delete;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", nullptr)) {
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
}
~Locale() { freelocale(locale_); }
type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char*& str) const {
char* end = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
#endif // FMT_LOCALE
FMT_END_NAMESPACE
#endif // FMT_POSIX_H_
#include "os.h"
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"

View File

@@ -28,7 +28,7 @@ template <bool IsSigned> struct int_checker {
template <> struct int_checker<true> {
template <typename T> static bool fits_in_int(T value) {
return value >= std::numeric_limits<int>::min() &&
return value >= (std::numeric_limits<int>::min)() &&
value <= max_value<int>();
}
static bool fits_in_int(int) { return true; }
@@ -303,6 +303,8 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
};
template <typename T> struct printf_formatter {
printf_formatter() = delete;
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
@@ -320,6 +322,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
public:
/** The character type for the output. */
using char_type = Char;
using iterator = OutputIt;
using format_arg = basic_format_arg<basic_printf_context>;
template <typename T> using formatter_type = printf_formatter<T>;
@@ -355,6 +358,8 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
internal::locale_ref locale() { return {}; }
format_arg arg(int id) const { return args_.get(id); }
basic_format_parse_context<Char>& parse_context() { return parse_ctx_; }
@@ -406,8 +411,9 @@ basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
}
template <typename OutputIt, typename Char>
int basic_printf_context<OutputIt, Char>::parse_header(
const Char*& it, const Char* end, format_specs& specs) {
int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
const Char* end,
format_specs& specs) {
int arg_index = -1;
char_type c = *it;
if (c >= '0' && c <= '9') {
@@ -476,8 +482,8 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
specs.precision = parse_nonnegative_int(it, end, eh);
} else if (c == '*') {
++it;
specs.precision =
static_cast<int>(visit_format_arg(internal::printf_precision_handler(), get_arg()));
specs.precision = static_cast<int>(
visit_format_arg(internal::printf_precision_handler(), get_arg()));
} else {
specs.precision = 0;
}
@@ -596,7 +602,8 @@ inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vsprintf(
const S& format, basic_format_args<basic_printf_context_t<Char>> args) {
const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
return to_string(buffer);
@@ -615,12 +622,13 @@ template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(format), {make_format_args<context>(args...)});
return vsprintf(to_string_view(format), make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline int vfprintf(std::FILE* f, const S& format,
basic_format_args<basic_printf_context_t<Char>> args) {
inline int vfprintf(
std::FILE* f, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
std::size_t size = buffer.size();
@@ -643,12 +651,13 @@ template <typename S, typename... Args,
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(format),
{make_format_args<context>(args...)});
make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline int vprintf(const S& format,
basic_format_args<basic_printf_context_t<Char>> args) {
inline int vprintf(
const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
return vfprintf(stdout, to_string_view(format), args);
}
@@ -666,12 +675,13 @@ template <typename S, typename... Args,
inline int printf(const S& format_str, const Args&... args) {
using context = basic_printf_context_t<char_t<S>>;
return vprintf(to_string_view(format_str),
{make_format_args<context>(args...)});
make_format_args<context>(args...));
}
template <typename S, typename Char = char_t<S>>
inline int vfprintf(std::basic_ostream<Char>& os, const S& format,
basic_format_args<basic_printf_context_t<Char>> args) {
inline int vfprintf(
std::basic_ostream<Char>& os, const S& format,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
printf(buffer, to_string_view(format), args);
internal::write(os, buffer);
@@ -682,9 +692,9 @@ inline int vfprintf(std::basic_ostream<Char>& os, const S& format,
template <typename ArgFormatter, typename Char,
typename Context =
basic_printf_context<typename ArgFormatter::iterator, Char>>
typename ArgFormatter::iterator vprintf(internal::buffer<Char>& out,
basic_string_view<Char> format_str,
basic_format_args<Context> args) {
typename ArgFormatter::iterator vprintf(
internal::buffer<Char>& out, basic_string_view<Char> format_str,
basic_format_args<type_identity_t<Context>> args) {
typename ArgFormatter::iterator iter(out);
Context(iter, format_str, args).template format<ArgFormatter>();
return iter;
@@ -704,7 +714,7 @@ inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(os, to_string_view(format_str),
{make_format_args<context>(args...)});
make_format_args<context>(args...));
}
FMT_END_NAMESPACE

View File

@@ -12,7 +12,9 @@
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
#include <initializer_list>
#include <type_traits>
#include "format.h"
// output only up to N items from the range.
@@ -104,10 +106,7 @@ struct is_range_<
/// 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,
(void)std::declval<typename std::tuple_element<0, U>::type>(),
int());
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
template <typename> static void check(...);
public:
@@ -360,6 +359,29 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
return {tuple, sep};
}
/**
\rst
Returns an object that formats `initializer_list` with elements separated by
`sep`.
**Example**::
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
// Output: "1, 2, 3"
\endrst
*/
template <typename T>
arg_join<internal::iterator_t<const std::initializer_list<T>>, char> join(
std::initializer_list<T> list, string_view sep) {
return join(std::begin(list), std::end(list), sep);
}
template <typename T>
arg_join<internal::iterator_t<const std::initializer_list<T>>, wchar_t> join(
std::initializer_list<T> list, wstring_view sep) {
return join(std::begin(list), std::end(list), sep);
}
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_

View File

@@ -11,11 +11,9 @@
//
#if !defined(SPDLOG_FMT_EXTERNAL)
#ifdef SPDLOG_HEADER_ONLY
#ifndef FMT_HEADER_ONLY
#if !defined(SPDLOG_COMPILED_LIB) && !defined(FMT_HEADER_ONLY)
#define FMT_HEADER_ONLY
#endif
#endif
#ifndef FMT_USE_WINDOWS_H
#define FMT_USE_WINDOWS_H 0
#endif

14
include/spdlog/fwd.h Normal file
View File

@@ -0,0 +1,14 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
namespace spdlog {
class logger;
class formatter;
namespace sinks {
class sink;
}
} // namespace spdlog

View File

@@ -9,7 +9,7 @@
#include <spdlog/sinks/sink.h>
#include <spdlog/details/backtracer.h>
#include <spdlog/details/pattern_formatter.h>
#include <spdlog/pattern_formatter.h>
#include <cstdio>
@@ -89,6 +89,7 @@ SPDLOG_INLINE void logger::set_formatter(std::unique_ptr<formatter> f)
{
// last element - we can be move it.
(*it)->set_formatter(std::move(f));
break; // to prevent clang-tidy warning
}
else
{

View File

@@ -39,7 +39,7 @@
namespace spdlog {
class logger
class SPDLOG_API logger
{
public:
// Empty logger
@@ -143,6 +143,24 @@ public:
// T can be statically converted to string_view
template<class T, typename std::enable_if<std::is_convertible<const T &, spdlog::string_view_t>::value, T>::type * = nullptr>
void log(source_loc loc, level::level_enum lvl, const T &msg)
{
log(loc, lvl, string_view_t{msg});
}
void log(log_clock::time_point log_time, source_loc loc, level::level_enum lvl, string_view_t msg)
{
bool log_enabled = should_log(lvl);
bool traceback_enabled = tracer_.enabled();
if (!log_enabled && !traceback_enabled)
{
return;
}
details::log_msg log_msg(log_time, loc, name_, lvl, msg);
log_it_(log_msg, log_enabled, traceback_enabled);
}
void log(source_loc loc, level::level_enum lvl, string_view_t msg)
{
bool log_enabled = should_log(lvl);
bool traceback_enabled = tracer_.enabled();

View File

@@ -4,7 +4,7 @@
#pragma once
#ifndef SPDLOG_HEADER_ONLY
#include <spdlog/details/pattern_formatter.h>
#include <spdlog/pattern_formatter.h>
#endif
#include <spdlog/details/fmt_helper.h>
@@ -59,6 +59,12 @@ public:
}
}
template<typename T>
static unsigned int count_digits(T n)
{
return fmt_helper::count_digits(n);
}
~scoped_padder()
{
if (remaining_pad_ >= 0)
@@ -87,10 +93,16 @@ private:
struct null_scoped_padder
{
null_scoped_padder(size_t /*wrapped_size*/, const padding_info & /*padinfo*/, memory_buf_t & /*dest*/) {}
template<typename T>
static unsigned int count_digits(T /* number */)
{
return 0;
}
};
template<typename ScopedPadder>
class name_formatter : public flag_formatter
class name_formatter final : public flag_formatter
{
public:
explicit name_formatter(padding_info padinfo)
@@ -106,7 +118,7 @@ public:
// log level appender
template<typename ScopedPadder>
class level_formatter : public flag_formatter
class level_formatter final : public flag_formatter
{
public:
explicit level_formatter(padding_info padinfo)
@@ -123,7 +135,7 @@ public:
// short log level appender
template<typename ScopedPadder>
class short_level_formatter : public flag_formatter
class short_level_formatter final : public flag_formatter
{
public:
explicit short_level_formatter(padding_info padinfo)
@@ -156,7 +168,7 @@ static int to12h(const tm &t)
static std::array<const char *, 7> days{{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}};
template<typename ScopedPadder>
class a_formatter : public flag_formatter
class a_formatter final : public flag_formatter
{
public:
explicit a_formatter(padding_info padinfo)
@@ -194,7 +206,7 @@ public:
static const std::array<const char *, 12> months{{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}};
template<typename ScopedPadder>
class b_formatter : public flag_formatter
class b_formatter final : public flag_formatter
{
public:
explicit b_formatter(padding_info padinfo)
@@ -214,7 +226,7 @@ static const std::array<const char *, 12> full_months{
{"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}};
template<typename ScopedPadder>
class B_formatter : public flag_formatter
class B_formatter final : public flag_formatter
{
public:
explicit B_formatter(padding_info padinfo)
@@ -638,7 +650,7 @@ public:
void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
{
const auto field_size = fmt_helper::count_digits(msg.thread_id);
const auto field_size = ScopedPadder::count_digits(msg.thread_id);
ScopedPadder p(field_size, padinfo_, dest);
fmt_helper::append_int(msg.thread_id, dest);
}
@@ -656,7 +668,7 @@ public:
void format(const details::log_msg &, const std::tm &, memory_buf_t &dest) override
{
const auto pid = static_cast<uint32_t>(details::os::pid());
auto field_size = fmt_helper::count_digits(pid);
auto field_size = ScopedPadder::count_digits(pid);
ScopedPadder p(field_size, padinfo_, dest);
fmt_helper::append_int(pid, dest);
}
@@ -755,8 +767,16 @@ public:
return;
}
size_t text_size =
padinfo_.enabled() ? std::char_traits<char>::length(msg.source.filename) + fmt_helper::count_digits(msg.source.line) + 1 : 0;
size_t text_size;
if (padinfo_.enabled())
{
// calc text size for padding based on "filename:line"
text_size = std::char_traits<char>::length(msg.source.filename) + ScopedPadder::count_digits(msg.source.line) + 1;
}
else
{
text_size = 0;
}
ScopedPadder p(text_size, padinfo_, dest);
fmt_helper::append_string_view(msg.source.filename, dest);
@@ -828,7 +848,7 @@ public:
return;
}
auto field_size = fmt_helper::count_digits(msg.source.line);
auto field_size = ScopedPadder::count_digits(msg.source.line);
ScopedPadder p(field_size, padinfo_, dest);
fmt_helper::append_int(msg.source.line, dest);
}
@@ -857,7 +877,6 @@ public:
// print elapsed time since last message
template<typename ScopedPadder, typename Units>
class elapsed_formatter final : public flag_formatter
{
public:
@@ -874,7 +893,7 @@ public:
auto delta_units = std::chrono::duration_cast<DurationUnits>(delta);
last_message_time_ = msg.time;
auto delta_count = static_cast<size_t>(delta_units.count());
auto n_digits = static_cast<size_t>(fmt_helper::count_digits(delta_count));
auto n_digits = static_cast<size_t>(ScopedPadder::count_digits(delta_count));
ScopedPadder p(n_digits, padinfo_, dest);
fmt_helper::append_int(delta_count, dest);
}
@@ -933,16 +952,15 @@ public:
dest.push_back(']');
dest.push_back(' ');
#ifndef SPDLOG_NO_NAME
// append logger name if exists
if (msg.logger_name.size() > 0)
{
dest.push_back('[');
// fmt_helper::append_str(*msg.logger_name, dest);
fmt_helper::append_string_view(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();
@@ -974,11 +992,13 @@ private:
} // namespace details
SPDLOG_INLINE pattern_formatter::pattern_formatter(std::string pattern, pattern_time_type time_type, std::string eol)
SPDLOG_INLINE pattern_formatter::pattern_formatter(
std::string pattern, pattern_time_type time_type, std::string eol, custom_flags custom_user_flags)
: pattern_(std::move(pattern))
, eol_(std::move(eol))
, pattern_time_type_(time_type)
, last_log_secs_(0)
, custom_handlers_(std::move(custom_user_flags))
{
std::memset(&cached_tm_, 0, sizeof(cached_tm_));
compile_pattern_(pattern_);
@@ -997,7 +1017,12 @@ SPDLOG_INLINE pattern_formatter::pattern_formatter(pattern_time_type time_type,
SPDLOG_INLINE std::unique_ptr<formatter> pattern_formatter::clone() const
{
return details::make_unique<pattern_formatter>(pattern_, pattern_time_type_, eol_);
custom_flags cloned_custom_formatters;
for (auto &it : custom_handlers_)
{
cloned_custom_formatters[it.first] = it.second->clone();
}
return details::make_unique<pattern_formatter>(pattern_, pattern_time_type_, eol_, std::move(cloned_custom_formatters));
}
SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory_buf_t &dest)
@@ -1017,6 +1042,12 @@ SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, memory
details::fmt_helper::append_string_view(eol_, dest);
}
SPDLOG_INLINE void pattern_formatter::set_pattern(std::string pattern)
{
pattern_ = std::move(pattern);
compile_pattern_(pattern_);
}
SPDLOG_INLINE std::tm pattern_formatter::get_time_(const details::log_msg &msg)
{
if (pattern_time_type_ == pattern_time_type::local)
@@ -1029,9 +1060,19 @@ SPDLOG_INLINE std::tm pattern_formatter::get_time_(const details::log_msg &msg)
template<typename Padder>
SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_info padding)
{
// process custom flags
auto it = custom_handlers_.find(flag);
if (it != custom_handlers_.end())
{
auto custom_handler = it->second->clone();
custom_handler->set_padding_info(padding);
formatters_.push_back(std::move(custom_handler));
return;
}
// process built-in flags
switch (flag)
{
case ('+'): // default formatter
formatters_.push_back(details::make_unique<details::full_formatter>(padding));
break;

View File

@@ -14,6 +14,7 @@
#include <string>
#include <vector>
#include <unordered_map>
namespace spdlog {
namespace details {
@@ -40,13 +41,13 @@ struct padding_info
{
return enabled_;
}
const size_t width_ = 0;
const pad_side side_ = left;
size_t width_ = 0;
pad_side side_ = left;
bool truncate_ = false;
bool enabled_ = false;
};
class flag_formatter
class SPDLOG_API flag_formatter
{
public:
explicit flag_formatter(padding_info padinfo)
@@ -62,11 +63,24 @@ protected:
} // namespace details
class pattern_formatter final : public formatter
class SPDLOG_API custom_flag_formatter : public details::flag_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);
virtual std::unique_ptr<custom_flag_formatter> clone() const = 0;
void set_padding_info(details::padding_info padding)
{
flag_formatter::padinfo_ = padding;
}
};
class SPDLOG_API pattern_formatter final : public formatter
{
public:
using custom_flags = std::unordered_map<char, std::unique_ptr<custom_flag_formatter>>;
explicit pattern_formatter(std::string pattern, pattern_time_type time_type = pattern_time_type::local,
std::string eol = spdlog::details::os::default_eol, custom_flags custom_user_flags = custom_flags());
// use default pattern is not given
explicit pattern_formatter(pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol);
@@ -77,6 +91,14 @@ public:
std::unique_ptr<formatter> clone() const override;
void format(const details::log_msg &msg, memory_buf_t &dest) override;
template<typename T, typename... Args>
pattern_formatter &add_flag(char flag, const Args &... args)
{
custom_handlers_[flag] = details::make_unique<T>(args...);
return *this;
}
void set_pattern(std::string pattern);
private:
std::string pattern_;
std::string eol_;
@@ -84,6 +106,7 @@ private:
std::tm cached_tm_;
std::chrono::seconds last_log_secs_;
std::vector<std::unique_ptr<details::flag_formatter>> formatters_;
custom_flags custom_handlers_;
std::tm get_time_(const details::log_msg &msg);
template<typename Padder>
@@ -92,7 +115,7 @@ private:
// Extract given pad spec (e.g. %8X)
// Advance the given it pass the end of the padding spec found (if any)
// Return padding.
details::padding_info handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end);
static details::padding_info handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end);
void compile_pattern_(const std::string &pattern);
};

View File

@@ -64,7 +64,7 @@ protected:
if (ret < 0)
{
SPDLOG_THROW(spdlog_ex("__android_log_write() failed", ret));
throw_spdlog_ex("__android_log_write() failed", ret);
}
}

View File

@@ -7,7 +7,7 @@
#include <spdlog/sinks/ansicolor_sink.h>
#endif
#include <spdlog/details/pattern_formatter.h>
#include <spdlog/pattern_formatter.h>
#include <spdlog/details/os.h>
namespace spdlog {
@@ -21,20 +21,20 @@ SPDLOG_INLINE ansicolor_sink<ConsoleMutex>::ansicolor_sink(FILE *target_file, co
{
set_color_mode(mode);
colors_[level::trace] = white;
colors_[level::debug] = cyan;
colors_[level::info] = green;
colors_[level::warn] = yellow_bold;
colors_[level::err] = red_bold;
colors_[level::critical] = bold_on_red;
colors_[level::off] = reset;
colors_[level::trace] = to_string_(white);
colors_[level::debug] = to_string_(cyan);
colors_[level::info] = to_string_(green);
colors_[level::warn] = to_string_(yellow_bold);
colors_[level::err] = to_string_(red_bold);
colors_[level::critical] = to_string_(bold_on_red);
colors_[level::off] = to_string_(reset);
}
template<typename ConsoleMutex>
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color(level::level_enum color_level, string_view_t color)
{
std::lock_guard<mutex_t> lock(mutex_);
colors_[color_level] = color;
colors_[color_level] = to_string_(color);
}
template<typename ConsoleMutex>
@@ -43,7 +43,8 @@ SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::log(const details::log_msg &msg
// Wrap the originally formatted message in color codes.
// If color is not supported in the terminal, log as is instead.
std::lock_guard<mutex_t> lock(mutex_);
msg.color_range_start = 0;
msg.color_range_end = 0;
memory_buf_t formatted;
formatter_->format(msg, formatted);
if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
@@ -111,7 +112,7 @@ SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
template<typename ConsoleMutex>
SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::print_ccode_(const string_view_t &color_code)
{
fwrite(color_code.data(), sizeof(string_view_t::char_type), color_code.size(), target_file_);
fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_);
}
template<typename ConsoleMutex>
@@ -120,6 +121,12 @@ SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::print_range_(const memory_buf_t
fwrite(formatted.data() + start, sizeof(char), end - start, target_file_);
}
template<typename ConsoleMutex>
SPDLOG_INLINE std::string ansicolor_sink<ConsoleMutex>::to_string_(const string_view_t &sv)
{
return std::string(sv.data(), sv.size());
}
// ansicolor_stdout_sink
template<typename ConsoleMutex>
SPDLOG_INLINE ansicolor_stdout_sink<ConsoleMutex>::ansicolor_stdout_sink(color_mode mode)

View File

@@ -9,7 +9,7 @@
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <array>
namespace spdlog {
namespace sinks {
@@ -30,7 +30,11 @@ public:
~ansicolor_sink() override = default;
ansicolor_sink(const ansicolor_sink &other) = delete;
ansicolor_sink(ansicolor_sink &&other) = delete;
ansicolor_sink &operator=(const ansicolor_sink &other) = delete;
ansicolor_sink &operator=(ansicolor_sink &&other) = delete;
void set_color(level::level_enum color_level, string_view_t color);
void set_color_mode(color_mode mode);
bool should_color();
@@ -80,9 +84,10 @@ private:
mutex_t &mutex_;
bool should_do_colors_;
std::unique_ptr<spdlog::formatter> formatter_;
std::unordered_map<level::level_enum, string_view_t, level::level_hasher> colors_;
std::array<std::string, level::n_levels> colors_;
void print_ccode_(const string_view_t &color_code);
void print_range_(const memory_buf_t &formatted, size_t start, size_t end);
static std::string to_string_(const string_view_t &sv);
};
template<typename ConsoleMutex>

View File

@@ -8,7 +8,7 @@
#endif
#include <spdlog/common.h>
#include <spdlog/details/pattern_formatter.h>
#include <spdlog/pattern_formatter.h>
#include <memory>

View File

@@ -21,8 +21,14 @@ class base_sink : public sink
public:
base_sink();
explicit base_sink(std::unique_ptr<spdlog::formatter> formatter);
~base_sink() override = default;
base_sink(const base_sink &) = delete;
base_sink(base_sink &&) = delete;
base_sink &operator=(const base_sink &) = delete;
base_sink &operator=(base_sink &&) = delete;
void log(const details::log_msg &msg) final;
void flush() final;
void set_pattern(const std::string &pattern) final;

View File

@@ -56,7 +56,7 @@ public:
{
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
{
SPDLOG_THROW(spdlog_ex("daily_file_sink: Invalid rotation time in ctor"));
throw_spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
}
auto now = log_clock::now();
@@ -66,13 +66,13 @@ public:
if (max_files_ > 0)
{
filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));
filenames_q_.push_back(std::move(filename));
init_filenames_q_();
}
}
const filename_t &filename() const
filename_t filename()
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
return file_helper_.filename();
}
@@ -104,6 +104,29 @@ protected:
}
private:
void init_filenames_q_()
{
using details::os::path_exists;
filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));
std::vector<filename_t> filenames;
auto now = log_clock::now();
while (filenames.size() < max_files_)
{
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
if (!path_exists(filename))
{
break;
}
filenames.emplace_back(filename);
now -= std::chrono::hours(24);
}
for (auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter)
{
filenames_q_.push_back(std::move(*iter));
}
}
tm now_tm(log_clock::time_point tp)
{
time_t tnow = log_clock::to_time_t(tp);
@@ -141,7 +164,7 @@ private:
if (!ok)
{
filenames_q_.push_back(std::move(current_file));
SPDLOG_THROW(spdlog_ex("Failed removing daily file " + filename_to_str(old_filename), errno));
throw_spdlog_ex("Failed removing daily file " + filename_to_str(old_filename), errno);
}
}
filenames_q_.push_back(std::move(current_file));
@@ -167,15 +190,15 @@ using daily_file_sink_st = daily_file_sink<details::null_mutex>;
//
template<typename Factory = spdlog::synchronous_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)
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0)
{
return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate);
return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate, max_files);
}
template<typename Factory = spdlog::synchronous_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)
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0)
{
return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate);
return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate, max_files);
}
} // namespace spdlog

View File

@@ -6,7 +6,7 @@
#include "base_sink.h"
#include <spdlog/details/log_msg.h>
#include <spdlog/details/null_mutex.h>
#include <spdlog/details/pattern_formatter.h>
#include <spdlog/pattern_formatter.h>
#include <algorithm>
#include <memory>

View File

@@ -8,6 +8,7 @@
#include <spdlog/details/null_mutex.h>
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/windows_include.h>
#include <winbase.h>
#include <mutex>

View File

@@ -54,8 +54,9 @@ SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::calc_filename(const filename
}
template<typename Mutex>
SPDLOG_INLINE const filename_t &rotating_file_sink<Mutex>::filename() const
SPDLOG_INLINE filename_t rotating_file_sink<Mutex>::filename()
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
return file_helper_.filename();
}
@@ -99,18 +100,17 @@ SPDLOG_INLINE void rotating_file_sink<Mutex>::rotate_()
}
filename_t target = calc_filename(base_filename_, i);
if (!rename_file(src, target))
if (!rename_file_(src, target))
{
// if failed try again after a small delay.
// this is a workaround to a windows issue, where very high rotation
// rates can cause the rename to fail with permission denied (because of antivirus?).
details::os::sleep_for_millis(100);
if (!rename_file(src, target))
if (!rename_file_(src, target))
{
file_helper_.reopen(true); // truncate the log file anyway to prevent it to grow beyond its limit!
current_size_ = 0;
SPDLOG_THROW(
spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno));
throw_spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
}
}
}
@@ -120,7 +120,7 @@ SPDLOG_INLINE void rotating_file_sink<Mutex>::rotate_()
// delete the target if exists, and rename the src file to target
// return true on success, false otherwise.
template<typename Mutex>
SPDLOG_INLINE bool rotating_file_sink<Mutex>::rename_file(const filename_t &src_filename, const filename_t &target_filename)
SPDLOG_INLINE bool rotating_file_sink<Mutex>::rename_file_(const filename_t &src_filename, const filename_t &target_filename)
{
// try to delete the target file in case it already exists.
(void)details::os::remove(target_filename);

View File

@@ -24,7 +24,7 @@ class rotating_file_sink final : public base_sink<Mutex>
public:
rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open = false);
static filename_t calc_filename(const filename_t &filename, std::size_t index);
const filename_t &filename() const;
filename_t filename();
protected:
void sink_it_(const details::log_msg &msg) override;
@@ -40,7 +40,7 @@ private:
// delete the target if exists, and rename the src file to target
// return true on success, false otherwise.
bool rename_file(const filename_t &src_filename, const filename_t &target_filename);
bool rename_file_(const filename_t &src_filename, const filename_t &target_filename);
filename_t base_filename_;
std::size_t max_size_;
@@ -75,4 +75,4 @@ inline std::shared_ptr<logger> rotating_logger_st(
#ifdef SPDLOG_HEADER_ONLY
#include "rotating_file_sink-inl.h"
#endif
#endif

View File

@@ -9,7 +9,7 @@
namespace spdlog {
namespace sinks {
class sink
class SPDLOG_API sink
{
public:
virtual ~sink() = default;

View File

@@ -8,7 +8,7 @@
#endif
#include <spdlog/details/console_globals.h>
#include <spdlog/details/pattern_formatter.h>
#include <spdlog/pattern_formatter.h>
#include <memory>
namespace spdlog {

View File

@@ -19,8 +19,12 @@ public:
using mutex_t = typename ConsoleMutex::mutex_t;
explicit stdout_sink_base(FILE *file);
~stdout_sink_base() override = default;
stdout_sink_base(const stdout_sink_base &other) = delete;
stdout_sink_base(stdout_sink_base &&other) = delete;
stdout_sink_base &operator=(const stdout_sink_base &other) = delete;
stdout_sink_base &operator=(stdout_sink_base &&other) = delete;
void log(const details::log_msg &msg) override;
void flush() override;

View File

@@ -5,6 +5,7 @@
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/null_mutex.h>
#include <spdlog/details/synchronous_factory.h>
#include <array>
#include <string>
@@ -92,14 +93,14 @@ using syslog_sink_st = syslog_sink<details::null_mutex>;
} // namespace sinks
// Create and register a syslog logger
template<typename Factory = default_factory>
template<typename Factory = spdlog::synchronous_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 = LOG_USER, bool enable_formatting = false)
{
return Factory::template create<sinks::syslog_sink_mt>(logger_name, syslog_ident, syslog_option, syslog_facility, enable_formatting);
}
template<typename Factory = default_factory>
template<typename Factory = spdlog::synchronous_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 = LOG_USER, bool enable_formatting = false)
{

View File

@@ -72,7 +72,7 @@ protected:
if (err)
{
SPDLOG_THROW(spdlog_ex("Failed writing to systemd", errno));
throw_spdlog_ex("Failed writing to systemd", errno);
}
}

View File

@@ -0,0 +1,81 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/common.h>
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/null_mutex.h>
#ifdef _WIN32
#include <spdlog/details/tcp_client-windows.h>
#else
#include <spdlog/details/tcp_client.h>
#endif
#include <mutex>
#include <string>
#include <chrono>
#include <functional>
#pragma once
// Simple tcp client sink
// Connects to remote address and send the formatted log.
// Will attempt to reconnect if connection drops.
// If more complicated behaviour is needed (i.e get responses), you can inherit it and override the sink_it_ method.
namespace spdlog {
namespace sinks {
struct tcp_sink_config
{
std::string server_host;
int server_port;
bool lazy_connect = false; // if true connect on first log call instead of on construction
tcp_sink_config(std::string host, int port)
: server_host{std::move(host)}
, server_port{port}
{}
};
template<typename Mutex>
class tcp_sink : public spdlog::sinks::base_sink<Mutex>
{
public:
// connect to tcp host/port or throw if failed
// host can be hostname or ip address
explicit tcp_sink(tcp_sink_config sink_config)
: config_{std::move(sink_config)}
{
if (!config_.lazy_connect)
{
this->client_.connect(config_.server_host, config_.server_port);
}
}
~tcp_sink() override = default;
protected:
void sink_it_(const spdlog::details::log_msg &msg) override
{
spdlog::memory_buf_t formatted;
spdlog::sinks::base_sink<Mutex>::formatter_->format(msg, formatted);
if (!client_.is_connected())
{
client_.connect(config_.server_host, config_.server_port);
}
client_.send(formatted.data(), formatted.size());
}
void flush_() override {}
tcp_sink_config config_;
details::tcp_client client_;
};
using tcp_sink_mt = tcp_sink<std::mutex>;
using tcp_sink_st = tcp_sink<spdlog::details::null_mutex>;
} // namespace sinks
} // namespace spdlog

View File

@@ -0,0 +1,266 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
// Writing to Windows Event Log requires the registry entries below to be present, with the following modifications:
// 1. <log_name> should be replaced with your log name (e.g. your application name)
// 2. <source_name> should be replaced with the specific source name and the key should be duplicated for
// each source used in the application
//
// Since typically modifications of this kind require elevation, it's better to do it as a part of setup procedure.
// The snippet below uses mscoree.dll as the message file as it exists on most of the Windows systems anyway and
// happens to contain the needed resource.
//
// You can also specify a custom message file if needed.
// Please refer to Event Log functions descriptions in MSDN for more details on custom message files.
/*---------------------------------------------------------------------------------------
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\<log_name>]
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\<log_name>\<source_name>]
"TypesSupported"=dword:00000007
"EventMessageFile"=hex(2):25,00,73,00,79,00,73,00,74,00,65,00,6d,00,72,00,6f,\
00,6f,00,74,00,25,00,5c,00,53,00,79,00,73,00,74,00,65,00,6d,00,33,00,32,00,\
5c,00,6d,00,73,00,63,00,6f,00,72,00,65,00,65,00,2e,00,64,00,6c,00,6c,00,00,\
00
-----------------------------------------------------------------------------------------*/
#pragma once
#include <spdlog/details/null_mutex.h>
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/windows_include.h>
#include <winbase.h>
#include <mutex>
#include <string>
#include <vector>
namespace spdlog {
namespace sinks {
namespace win_eventlog {
namespace internal {
/** Windows error */
struct win32_error : public spdlog_ex
{
/** Formats an error report line: "user-message: error-code (system message)" */
static std::string format(std::string const &user_message, DWORD error_code = GetLastError())
{
std::string system_message;
LPSTR format_message_result{};
auto format_message_succeeded =
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&format_message_result, 0, nullptr);
if (format_message_succeeded && format_message_result)
{
system_message = fmt::format(" ({})", format_message_result);
}
if (format_message_result)
{
LocalFree((HLOCAL)format_message_result);
}
return fmt::format("{}: {}{}", user_message, error_code, system_message);
}
explicit win32_error(std::string const &func_name, DWORD error = GetLastError())
: spdlog_ex(format(func_name, error))
{}
};
/** Wrapper for security identifiers (SID) on Windows */
struct sid_t
{
std::vector<char> buffer_;
public:
sid_t() {}
/** creates a wrapped SID copy */
static sid_t duplicate_sid(PSID psid)
{
if (!::IsValidSid(psid))
{
throw_spdlog_ex("sid_t::sid_t(): invalid SID received");
}
auto const sid_length{::GetLengthSid(psid)};
sid_t result;
result.buffer_.resize(sid_length);
if (!::CopySid(sid_length, (PSID)result.as_sid(), psid))
{
SPDLOG_THROW(win32_error("CopySid"));
}
return result;
}
/** Retrieves pointer to the internal buffer contents as SID* */
SID *as_sid() const
{
return buffer_.empty() ? nullptr : (SID *)buffer_.data();
}
/** Get SID for the current user */
static sid_t get_current_user_sid()
{
/* create and init RAII holder for process token */
struct process_token_t
{
HANDLE token_handle_ = INVALID_HANDLE_VALUE;
explicit process_token_t(HANDLE process)
{
if (!::OpenProcessToken(process, TOKEN_QUERY, &token_handle_))
{
SPDLOG_THROW(win32_error("OpenProcessToken"));
}
}
~process_token_t()
{
::CloseHandle(token_handle_);
}
} current_process_token(::GetCurrentProcess()); // GetCurrentProcess returns pseudohandle, no leak here!
// Get the required size, this is expected to fail with ERROR_INSUFFICIENT_BUFFER and return the token size
DWORD tusize = 0;
if (::GetTokenInformation(current_process_token.token_handle_, TokenUser, NULL, 0, &tusize))
{
SPDLOG_THROW(win32_error("GetTokenInformation should fail"));
}
// get user token
std::vector<unsigned char> buffer(static_cast<size_t>(tusize));
if (!::GetTokenInformation(current_process_token.token_handle_, TokenUser, (LPVOID)buffer.data(), tusize, &tusize))
{
SPDLOG_THROW(win32_error("GetTokenInformation"));
}
// create a wrapper of the SID data as stored in the user token
return sid_t::duplicate_sid(((TOKEN_USER *)buffer.data())->User.Sid);
}
};
struct eventlog
{
static WORD get_event_type(details::log_msg const &msg)
{
switch (msg.level)
{
case level::trace:
case level::debug:
return EVENTLOG_SUCCESS;
case level::info:
return EVENTLOG_INFORMATION_TYPE;
case level::warn:
return EVENTLOG_WARNING_TYPE;
case level::err:
case level::critical:
case level::off:
return EVENTLOG_ERROR_TYPE;
default:
return EVENTLOG_INFORMATION_TYPE;
}
}
static WORD get_event_category(details::log_msg const &msg)
{
return (WORD)msg.level;
}
};
} // namespace internal
/*
* Windows Event Log sink
*/
template<typename Mutex>
class win_eventlog_sink : public base_sink<Mutex>
{
private:
HANDLE hEventLog_{NULL};
internal::sid_t current_user_sid_;
std::string source_;
WORD event_id_;
HANDLE event_log_handle()
{
if (!hEventLog_)
{
hEventLog_ = ::RegisterEventSource(nullptr, source_.c_str());
if (!hEventLog_ || hEventLog_ == (HANDLE)ERROR_ACCESS_DENIED)
{
SPDLOG_THROW(internal::win32_error("RegisterEventSource"));
}
}
return hEventLog_;
}
protected:
void sink_it_(const details::log_msg &msg) override
{
using namespace internal;
memory_buf_t formatted;
base_sink<Mutex>::formatter_->format(msg, formatted);
formatted.push_back('\0');
LPCSTR lp_str = static_cast<LPCSTR>(formatted.data());
auto succeeded = ::ReportEvent(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_,
current_user_sid_.as_sid(), 1, 0, &lp_str, nullptr);
if (!succeeded)
{
SPDLOG_THROW(win32_error("ReportEvent"));
}
}
void flush_() override {}
public:
win_eventlog_sink(std::string const &source, WORD event_id = 1000 /* according to mscoree.dll */)
: source_(source)
, event_id_(event_id)
{
try
{
current_user_sid_ = internal::sid_t::get_current_user_sid();
}
catch (...)
{
// get_current_user_sid() is unlikely to fail and if it does, we can still proceed without
// current_user_sid but in the event log the record will have no user name
}
}
~win_eventlog_sink()
{
if (hEventLog_)
DeregisterEventSource(hEventLog_);
}
};
} // namespace win_eventlog
using win_eventlog_sink_mt = win_eventlog::win_eventlog_sink<std::mutex>;
using win_eventlog_sink_st = win_eventlog::win_eventlog_sink<details::null_mutex>;
} // namespace sinks
} // namespace spdlog

View File

@@ -8,7 +8,7 @@
#endif
#include <spdlog/common.h>
#include <spdlog/details/pattern_formatter.h>
#include <spdlog/pattern_formatter.h>
namespace spdlog {
namespace sinks {
@@ -52,6 +52,8 @@ template<typename ConsoleMutex>
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
{
std::lock_guard<mutex_t> lock(mutex_);
msg.color_range_start = 0;
msg.color_range_end = 0;
memory_buf_t formatted;
formatter_->format(msg, formatted);
if (!in_console_)
@@ -59,7 +61,6 @@ void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
write_to_file_(formatted);
return;
}
if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
{
// before color range
@@ -157,7 +158,7 @@ void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::write_to_file_(const memory_buf_
bool ok = ::WriteFile(out_handle_, formatted.data() + total_written, size - total_written, &bytes_written, nullptr) != 0;
if (!ok || bytes_written == 0)
{
SPDLOG_THROW(spdlog_ex("wincolor_sink: write_to_file_ failed. GetLastError(): " + std::to_string(::GetLastError())));
throw_spdlog_ex("wincolor_sink: write_to_file_ failed. GetLastError(): " + std::to_string(::GetLastError()));
}
total_written += bytes_written;
} while (total_written < size);

View File

@@ -11,7 +11,9 @@
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <array>
#include <spdlog/details/windows_include.h>
#include <wincon.h>
namespace spdlog {
@@ -52,7 +54,7 @@ protected:
bool in_console_;
bool should_do_colors_;
std::unique_ptr<spdlog::formatter> formatter_;
std::unordered_map<level::level_enum, WORD, level::level_hasher> colors_;
std::array<WORD, level::n_levels> colors_;
// set foreground color and return the orig console attributes (for resetting later)
WORD set_foreground_color_(WORD attribs);

View File

@@ -8,7 +8,7 @@
#endif
#include <spdlog/common.h>
#include <spdlog/details/pattern_formatter.h>
#include <spdlog/pattern_formatter.h>
namespace spdlog {

View File

@@ -39,68 +39,66 @@ inline std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs
// Initialize and register a logger,
// formatter and flush level will be set according the global settings.
//
// NOTE:
// Use this function when creating loggers manually.
// Useful for initializing manually created loggers with the global settings.
//
// Example:
// auto console_sink = std::make_shared<spdlog::sinks::stdout_sink_mt>();
// auto console_logger = std::make_shared<spdlog::logger>("console_logger", console_sink);
// spdlog::initialize_logger(console_logger);
void initialize_logger(std::shared_ptr<logger> logger);
// auto mylogger = std::make_shared<spdlog::logger>("mylogger", ...);
// spdlog::initialize_logger(mylogger);
SPDLOG_API void initialize_logger(std::shared_ptr<logger> logger);
// Return an existing logger or nullptr if a logger with such name doesn't
// exist.
// example: spdlog::get("my_logger")->info("hello {}", "world");
std::shared_ptr<logger> get(const std::string &name);
SPDLOG_API std::shared_ptr<logger> get(const std::string &name);
// Set global formatter. Each sink in each logger will get a clone of this object
void set_formatter(std::unique_ptr<spdlog::formatter> formatter);
SPDLOG_API void set_formatter(std::unique_ptr<spdlog::formatter> formatter);
// Set global format string.
// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v");
void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);
SPDLOG_API void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);
// enable global backtrace support
void enable_backtrace(size_t n_messages);
SPDLOG_API void enable_backtrace(size_t n_messages);
// disable global backtrace support
void disable_backtrace();
SPDLOG_API void disable_backtrace();
// call dump backtrace on default logger
void dump_backtrace();
SPDLOG_API void dump_backtrace();
// Set global logging level
void set_level(level::level_enum log_level);
SPDLOG_API void set_level(level::level_enum log_level);
// Set global flush level
void flush_on(level::level_enum log_level);
SPDLOG_API void flush_on(level::level_enum log_level);
// Start/Restart a periodic flusher thread
// Warning: Use only if all your loggers are thread safe!
void flush_every(std::chrono::seconds interval);
SPDLOG_API void flush_every(std::chrono::seconds interval);
// Set global error handler
void set_error_handler(void (*handler)(const std::string &msg));
SPDLOG_API void set_error_handler(void (*handler)(const std::string &msg));
// Register the given logger with the given name
void register_logger(std::shared_ptr<logger> logger);
SPDLOG_API void register_logger(std::shared_ptr<logger> logger);
// Apply a user defined function on all registered loggers
// Example:
// spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->flush();});
void apply_all(const std::function<void(std::shared_ptr<logger>)> &fun);
SPDLOG_API void apply_all(const std::function<void(std::shared_ptr<logger>)> &fun);
// Drop the reference to the given logger
void drop(const std::string &name);
SPDLOG_API void drop(const std::string &name);
// Drop all references from the registry
void drop_all();
SPDLOG_API void drop_all();
// stop any running threads started by spdlog and clean registry loggers
void shutdown();
SPDLOG_API void shutdown();
// Automatic registration of loggers when using spdlog::create() or spdlog::create_async
void set_automatic_registration(bool automatic_registration);
SPDLOG_API void set_automatic_registration(bool automatic_registration);
// API for using default logger (stdout_color_mt),
// e.g: spdlog::info("Message {}", 1);
@@ -117,11 +115,11 @@ void set_automatic_registration(bool automatic_registration);
// set_default_logger() *should not* be used concurrently with the default API.
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
std::shared_ptr<spdlog::logger> default_logger();
SPDLOG_API std::shared_ptr<spdlog::logger> default_logger();
spdlog::logger *default_logger_raw();
SPDLOG_API spdlog::logger *default_logger_raw();
void set_default_logger(std::shared_ptr<spdlog::logger> default_logger);
SPDLOG_API void set_default_logger(std::shared_ptr<spdlog::logger> default_logger);
template<typename... Args>
inline void log(source_loc source, level::level_enum lvl, string_view_t fmt, const Args &... args)

View File

@@ -24,7 +24,7 @@
// This will prevent spdlog from querying the thread id on each log call.
//
// WARNING: If the log pattern contains thread id (i.e, %t) while this flag is
// on, the result is undefined.
// on, zero will be logged as thread id.
//
// #define SPDLOG_NO_THREAD_ID
///////////////////////////////////////////////////////////////////////////////
@@ -38,13 +38,6 @@
// #define SPDLOG_NO_TLS
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment if logger name logging is not needed.
// This will prevent spdlog from copying the logger name on each log call.
//
// #define SPDLOG_NO_NAME
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to avoid spdlog's usage of atomic log levels
// Use only if your code never modifies a logger's log levels concurrently by

View File

@@ -4,7 +4,7 @@
#pragma once
#define SPDLOG_VER_MAJOR 1
#define SPDLOG_VER_MINOR 5
#define SPDLOG_VER_MINOR 6
#define SPDLOG_VER_PATCH 0
#define SPDLOG_VERSION (SPDLOG_VER_MAJOR * 10000 + SPDLOG_VER_MINOR * 100 + SPDLOG_VER_PATCH)

View File

@@ -0,0 +1,43 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="263" height="147" viewBox="0 0 263 147">
<defs>
<linearGradient id="linear-gradient" x1="54.4568" y1="122.5936" x2="251.779" y2="10.2057" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#00adee"/>
<stop offset="1" stop-color="#9f76a6"/>
</linearGradient>
<linearGradient id="linear-gradient-2" x1="80.247" y1="38.7607" x2="241.2622" y2="10.9511" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#ec037c"/>
<stop offset="1" stop-color="#9f76a6"/>
</linearGradient>
<linearGradient id="linear-gradient-3" x1="75.7205" y1="33.5582" x2="127.8253" y2="123.9392" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#ec037c"/>
<stop offset="1" stop-color="#5c2d90"/>
</linearGradient>
<linearGradient id="linear-gradient-4" x1="7.4647" y1="44.578" x2="129.4543" y2="125.0813" gradientUnits="userSpaceOnUse">
<stop offset="0" stop-color="#44c7f4"/>
<stop offset="1" stop-color="#5c2d90"/>
</linearGradient>
</defs>
<g>
<path d="M261.1839,10.3622a9.6784,9.6784,0,0,0-14.7448-8.2463l0-.0006L20.6942,136.0746h0a4.6974,4.6974,0,1,0,4.1326,8.4348h0q0.09-.0467.1784-0.097l230.5273-125.25a9.653,9.653,0,0,0,1.1508-.6253l0.0332-.018-0.0014-.0023A9.6682,9.6682,0,0,0,261.1839,10.3622Z" fill="url(#linear-gradient)"/>
<path d="M261.1839,10.3622A9.6782,9.6782,0,0,0,251.5057.684q-0.2747,0-.5456.0157-0.25.0143-.4975,0.041L76.7981,25.4187A13.7347,13.7347,0,1,0,83.5355,51.983L252.8044,19.9512a9.6363,9.6363,0,0,0,1.0358-.196l0.02-.0039,0-.0008A9.6811,9.6811,0,0,0,261.1839,10.3622Z" fill="url(#linear-gradient-2)"/>
<path d="M145.2028,123.63a17.2372,17.2372,0,0,0-3.0637-9.4254L91.3045,32.3521A13.7366,13.7366,0,0,0,66.1132,42.9507h0a13.6332,13.6332,0,0,0,1.043,2.4984s45.2334,86.37,45.5824,86.9979q0.3089,0.556.6567,1.0861h0A17.32,17.32,0,0,0,145.2028,123.63Z" fill="url(#linear-gradient-3)"/>
<path d="M145.2028,123.63a17.2979,17.2979,0,0,0-7.63-13.9419h0a17.3061,17.3061,0,0,0-2.6994-1.4911L9.5484,38.9679a6.064,6.064,0,0,0-6.5074,10.187l114.3963,88.704A17.3191,17.3191,0,0,0,145.2028,123.63Z" fill="url(#linear-gradient-4)"/>
<g>
<rect x="69" y="51" width="70" height="70"/>
<g>
<rect x="75.038" y="107.8746" width="26.2498" height="4.375" fill="#fff"/>
<g>
<path d="M74.7429,69.4315L76.78,67.5082a2.31,2.31,0,0,0,1.7929,1.0594A1.33,1.33,0,0,0,79.8607,66.97V59.75h3.1456v7.2366a4.2386,4.2386,0,0,1-1.1246,3.2108,4.2989,4.2989,0,0,1-3.1293,1.1572A4.6592,4.6592,0,0,1,74.7429,69.4315Z" fill="#fff"/>
<path d="M83.7394,59.75h9.1761v2.673H86.8688v1.744H92.345v2.4937H86.8688V68.47H92.997v2.6893H83.7394V59.75Z" fill="#fff"/>
<path d="M97.049,62.5208H93.6426V59.75h9.9911v2.7708h-3.4227v8.6383H97.049V62.5208Z" fill="#fff"/>
<path d="M75.0363,73.8257h5.8511A4.2728,4.2728,0,0,1,84,74.8363a2.5675,2.5675,0,0,1,.7335,1.858v0.0326a2.6407,2.6407,0,0,1-1.76,2.5425,2.7686,2.7686,0,0,1,2.2655,2.7871v0.0326c0,1.9558-1.5973,3.1456-4.3191,3.1456H75.0363V73.8257Zm6.5846,3.5206c0-.6357-0.5052-0.9779-1.4343-0.9779h-2.07v2.0047h1.9884c0.9616,0,1.5158-.326,1.5158-0.9942V77.3463ZM80.5289,80.59H78.1166v2.1025h2.4448c0.9779,0,1.5158-.3749,1.5158-1.0431V81.6165C82.0773,80.9972,81.5883,80.59,80.5289,80.59Z" fill="#fff"/>
<path d="M85.7116,73.8257h5.3949a5.0512,5.0512,0,0,1,3.7161,1.2224,3.5623,3.5623,0,0,1,1.01,2.6567v0.0326a3.6146,3.6146,0,0,1-2.3469,3.5205l2.7218,3.9769H92.5733l-2.2981-3.4553H88.8735v3.4553H85.7116V73.8257Zm5.2644,5.4764a1.433,1.433,0,0,0,1.6951-1.3528V77.9167c0-.9128-0.6682-1.3691-1.7114-1.3691H88.8735v2.7545H90.976Z" fill="#fff"/>
<path d="M99.5324,73.7443H102.58l4.8571,11.4905h-3.39l-0.815-2.0536H98.8153L98,85.2348H94.6917Zm2.7707,6.9758-1.2712-3.2271L99.7443,80.72h2.5589Z" fill="#fff"/>
<path d="M107.8117,73.8257h3.1619V85.2348h-3.1619V73.8257Z" fill="#fff"/>
<path d="M111.7558,73.8257h2.95l4.694,6.0306V73.8257h3.1294V85.2348h-2.7545l-4.89-6.2587v6.2587h-3.1293V73.8257Z" fill="#fff"/>
<path d="M122.7274,83.54l1.76-2.1025a5.9106,5.9106,0,0,0,3.7,1.3691c0.8638,0,1.32-.2934,1.32-0.7824V81.9914c0-.489-0.3749-0.7335-1.9395-1.1084-2.4285-.5541-4.3029-1.2387-4.3029-3.5694V77.2811c0-2.1188,1.6788-3.6509,4.417-3.6509a7.1807,7.1807,0,0,1,4.694,1.5158l-1.5809,2.2329a5.6006,5.6006,0,0,0-3.1946-1.1246c-0.766,0-1.1409.31-1.1409,0.7334V77.02c0,0.5216.3912,0.75,1.9884,1.1083,2.6077,0.57,4.2377,1.418,4.2377,3.5531v0.0326c0,2.3307-1.8418,3.7161-4.6126,3.7161A7.9992,7.9992,0,0,1,122.7274,83.54Z" fill="#fff"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@@ -1,183 +0,0 @@
project('spdlog', ['cpp'],
license : 'MIT',
version : run_command(find_program('scripts/extract_version.py')).stdout().strip(),
default_options : [
'warning_level=3',
'cpp_std=c++11',
'buildtype=release',
'b_colorout=always',
],
)
# ------------------------
# --- Dependencies ---
# ------------------------
dep_list = []
compile_args = []
# Threads
dep_list += dependency('threads')
# Check for FMT
if get_option('external_fmt')
if not meson.version().version_compare('>=0.49.0')
warning('Finding fmt can fail with meson versions before 0.49.0')
endif
dep_list += dependency('fmt', fallback : ['fmt', 'fmt_dep'])
compile_args += '-DSPDLOG_FMT_EXTERNAL'
endif
if get_option('no_exceptions')
compile_args += '-DSPDLOG_NO_EXCEPTIONS'
endif
if get_option('wchar_support')
if build_machine.system() != 'windows'
error('wchar_support only supported under windows')
endif
compile_args += '-DSPDLOG_WCHAR_TO_UTF8_SUPPORT'
endif
if get_option('wchar_filenames')
if build_machine.system() != 'windows'
error('wchar_filenames only supported under windows')
endif
compile_args += '-DSPDLOG_WCHAR_FILENAMES'
endif
if get_option('clock_coarse')
if build_machine.system() != 'linux'
error('clock_coarse only supported under linux')
endif
compile_args += '-DSPDLOG_CLOCK_COARSE'
endif
if get_option('prevent_child_fd')
compile_args += '-DSPDLOG_PREVENT_CHILD_FD'
endif
if get_option('no_thread_id')
compile_args += '-DSPDLOG_NO_THREAD_ID'
endif
if get_option('no_tls')
compile_args += '-DSPDLOG_NO_TLS'
endif
if get_option('no_atomic_levels')
compile_args += '-DSPDLOG_NO_ATOMIC_LEVELS'
endif
compile_args_compiled = compile_args + ['-DSPDLOG_COMPILED_LIB']
compile_args_ho = compile_args
# ------------------------------------
# --- Compiled library version ---
# ------------------------------------
spdlog_inc = include_directories('./include')
spdlog_srcs = files([
'src/async.cpp',
'src/color_sinks.cpp',
'src/file_sinks.cpp',
'src/spdlog.cpp',
'src/stdout_sinks.cpp'
])
if not get_option('external_fmt')
spdlog_srcs+= 'src/fmt.cpp'
endif
if get_option('library_type') == 'static'
spdlog = static_library(
'spdlog',
spdlog_srcs,
cpp_args : compile_args_compiled,
include_directories : spdlog_inc,
dependencies : dep_list,
install : not meson.is_subproject()
)
else
spdlog = shared_library('spdlog',
spdlog_srcs,
cpp_args : compile_args_compiled,
include_directories : spdlog_inc,
dependencies : dep_list,
install : not meson.is_subproject(),
version : meson.project_version(),
)
endif
spdlog_dep = declare_dependency(
link_with : spdlog,
include_directories : spdlog_inc,
compile_args : compile_args_compiled,
dependencies : dep_list,
version : meson.project_version(),
)
# ----------------------------------
# --- Header only dependency ---
# ----------------------------------
spdlog_headeronly_dep = declare_dependency(
include_directories : spdlog_inc,
compile_args : compile_args_ho,
dependencies : dep_list,
version : meson.project_version(),
)
# ------------------------
# --- Installation ---
# ------------------------
# Do not install when spdlog is used as a subproject
if not meson.is_subproject()
install_subdir('include/spdlog', install_dir: get_option('includedir'))
pkg = import('pkgconfig')
pkg.generate(spdlog,
name : 'spdlog',
description : 'Fast C++ logging library',
url : 'https://github.com/gabime/spdlog',
extra_cflags : compile_args_compiled
)
endif
# -------------------------------------
# --- Conditionally add subdirs ---
# -------------------------------------
if get_option('enable_tests') or get_option('enable_tests_ho')
subdir('tests')
endif
if get_option('enable_examples')
subdir('example')
endif
if get_option('enable_benchmarks')
subdir('bench')
endif
# -------------------
# --- Summary ---
# -------------------
summary_str = '''spdlog build summary:
- using external fmt: @0@
- building tests: @1@
- building examples: @2@
- building benchmarks: @3@
- library type: @4@
- no exceptions: @5@
'''.format(
get_option('external_fmt'),
get_option('enable_tests'),
get_option('enable_examples'),
get_option('enable_benchmarks'),
get_option('library_type'),
get_option('no_exceptions')
)
message(summary_str)

View File

@@ -1,15 +0,0 @@
option('external_fmt', type: 'boolean', value: false, description: 'Use external fmt package instead of the bundled')
option('enable_examples', type: 'boolean', value: true, description: 'Build examples')
option('enable_benchmarks', type: 'boolean', value: false, description: 'Build benchmarks')
option('enable_tests', type: 'boolean', value: true, description: 'Build tests')
option('enable_tests_ho', type: 'boolean', value: false, description: 'Build header-only tests')
option('library_type', type: 'combo', choices: ['static', 'shared'], value: 'static', description: 'Library build type')
option('no_exceptions', type: 'boolean', value: false, description: 'Disabled exceptions - abort() instead any error')
option('wchar_support', type: 'boolean', value: false, description:'(Windows only) Support wchar api')
option('wchar_filenames', type: 'boolean', value: false, description: '(Windows only) Support wchar filenames')
option('clock_coarse', type: 'boolean', value: false, description: '(Linux only) Use the much faster (but much less accurate) CLOCK_REALTIME_COARSE instead of the regular clock')
option('prevent_child_fd', type: 'boolean', value: false, description: 'Prevent from child processes to inherit log file descriptors')
option('no_thread_id', type: 'boolean', value: false, description: 'prevent spdlog from querying the thread id on each log call if thread id is not needed')
option('no_tls', type: 'boolean', value: false, description: 'prevent spdlog from using thread local storage')
option('no_atomic_levels', type: 'boolean', value: false, description: 'prevent spdlog from using of std::atomic log levels (use only if your code never modifies log levels concurrently')

View File

@@ -1,5 +0,0 @@
#!/bin/bash
cd "$(dirname "$0")"
clang-tidy ../example/example.cpp -- -I ../include

View File

@@ -1,12 +1,16 @@
#!/bin/bash
cd "$(dirname "$0")"
cd "$(dirname "$0")"/..
pwd
echo -n "Running dos2unix "
find .. -name "*\.h" -o -name "*\.cpp"|grep -v bundled|xargs -I {} sh -c "dos2unix '{}' 2>/dev/null; echo -n '.'"
find . -name "*\.h" -o -name "*\.cpp"|grep -v bundled|xargs -I {} sh -c "dos2unix '{}' 2>/dev/null; echo -n '.'"
echo
echo -n "Running clang-format "
find .. -name "*\.h" -o -name "*\.cpp"|grep -v bundled|xargs -I {} sh -c "clang-format -i {}; echo -n '.'"
find . -name "*\.h" -o -name "*\.cpp"|grep -v bundled|xargs -I {} sh -c "clang-format -i {}; echo -n '.'"
echo
echo -n "Running cmake-format "
find . -name "CMakeLists.txt" -o -name "*\.cmake"|grep -v bundled|xargs -I {} sh -c "cmake-format --line-width 120 --tab-size 4 --max-subgroups-hwrap 4 -i {}; echo -n '.'"
echo

View File

@@ -5,8 +5,9 @@
#error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif
#include "spdlog/async.h"
#include "spdlog/async_logger-inl.h"
#include "spdlog/details/periodic_worker-inl.h"
#include "spdlog/details/thread_pool-inl.h"
template class spdlog::details::mpmc_blocking_queue<spdlog::details::async_msg>;
#include <spdlog/async.h>
#include <spdlog/async_logger-inl.h>
#include <spdlog/details/periodic_worker-inl.h>
#include <spdlog/details/thread_pool-inl.h>
template class SPDLOG_API spdlog::details::mpmc_blocking_queue<spdlog::details::async_msg>;

8
src/cfg.cpp Normal file
View File

@@ -0,0 +1,8 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#ifndef SPDLOG_COMPILED_LIB
#error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif
#include <spdlog/cfg/helpers-inl.h>

View File

@@ -7,41 +7,45 @@
#include <mutex>
#include "spdlog/details/null_mutex.h"
#include "spdlog/async.h"
#include <spdlog/details/null_mutex.h>
#include <spdlog/async.h>
//
// color sinks
//
#ifdef _WIN32
#include "spdlog/sinks/wincolor_sink-inl.h"
template class spdlog::sinks::wincolor_sink<spdlog::details::console_mutex>;
template class spdlog::sinks::wincolor_sink<spdlog::details::console_nullmutex>;
template class spdlog::sinks::wincolor_stdout_sink<spdlog::details::console_mutex>;
template class spdlog::sinks::wincolor_stdout_sink<spdlog::details::console_nullmutex>;
template class spdlog::sinks::wincolor_stderr_sink<spdlog::details::console_mutex>;
template class spdlog::sinks::wincolor_stderr_sink<spdlog::details::console_nullmutex>;
#include <spdlog/sinks/wincolor_sink-inl.h>
template class SPDLOG_API spdlog::sinks::wincolor_sink<spdlog::details::console_mutex>;
template class SPDLOG_API spdlog::sinks::wincolor_sink<spdlog::details::console_nullmutex>;
template class SPDLOG_API spdlog::sinks::wincolor_stdout_sink<spdlog::details::console_mutex>;
template class SPDLOG_API spdlog::sinks::wincolor_stdout_sink<spdlog::details::console_nullmutex>;
template class SPDLOG_API spdlog::sinks::wincolor_stderr_sink<spdlog::details::console_mutex>;
template class SPDLOG_API spdlog::sinks::wincolor_stderr_sink<spdlog::details::console_nullmutex>;
#else
#include "spdlog/sinks/ansicolor_sink-inl.h"
template class spdlog::sinks::ansicolor_sink<spdlog::details::console_mutex>;
template class spdlog::sinks::ansicolor_sink<spdlog::details::console_nullmutex>;
template class spdlog::sinks::ansicolor_stdout_sink<spdlog::details::console_mutex>;
template class spdlog::sinks::ansicolor_stdout_sink<spdlog::details::console_nullmutex>;
template class spdlog::sinks::ansicolor_stderr_sink<spdlog::details::console_mutex>;
template class spdlog::sinks::ansicolor_stderr_sink<spdlog::details::console_nullmutex>;
template class SPDLOG_API spdlog::sinks::ansicolor_sink<spdlog::details::console_mutex>;
template class SPDLOG_API spdlog::sinks::ansicolor_sink<spdlog::details::console_nullmutex>;
template class SPDLOG_API spdlog::sinks::ansicolor_stdout_sink<spdlog::details::console_mutex>;
template class SPDLOG_API spdlog::sinks::ansicolor_stdout_sink<spdlog::details::console_nullmutex>;
template class SPDLOG_API spdlog::sinks::ansicolor_stderr_sink<spdlog::details::console_mutex>;
template class SPDLOG_API spdlog::sinks::ansicolor_stderr_sink<spdlog::details::console_nullmutex>;
#endif
// factory methods for color loggers
#include "spdlog/sinks/stdout_color_sinks-inl.h"
template std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt<spdlog::synchronous_factory>(
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt<spdlog::synchronous_factory>(
const std::string &logger_name, color_mode mode);
template std::shared_ptr<spdlog::logger> spdlog::stdout_color_st<spdlog::synchronous_factory>(
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stdout_color_st<spdlog::synchronous_factory>(
const std::string &logger_name, color_mode mode);
template std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt<spdlog::synchronous_factory>(
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt<spdlog::synchronous_factory>(
const std::string &logger_name, color_mode mode);
template std::shared_ptr<spdlog::logger> spdlog::stderr_color_st<spdlog::synchronous_factory>(
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stderr_color_st<spdlog::synchronous_factory>(
const std::string &logger_name, color_mode mode);
template std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt<spdlog::async_factory>(const std::string &logger_name, color_mode mode);
template std::shared_ptr<spdlog::logger> spdlog::stdout_color_st<spdlog::async_factory>(const std::string &logger_name, color_mode mode);
template std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt<spdlog::async_factory>(const std::string &logger_name, color_mode mode);
template std::shared_ptr<spdlog::logger> spdlog::stderr_color_st<spdlog::async_factory>(const std::string &logger_name, color_mode mode);
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt<spdlog::async_factory>(
const std::string &logger_name, color_mode mode);
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stdout_color_st<spdlog::async_factory>(
const std::string &logger_name, color_mode mode);
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt<spdlog::async_factory>(
const std::string &logger_name, color_mode mode);
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stderr_color_st<spdlog::async_factory>(
const std::string &logger_name, color_mode mode);

View File

@@ -5,14 +5,16 @@
#error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif
#include <spdlog/details/null_mutex.h>
#include <spdlog/details/file_helper-inl.h>
#include <spdlog/sinks/basic_file_sink-inl.h>
#include <spdlog/sinks/base_sink-inl.h>
#include <mutex>
#include "spdlog/details/null_mutex.h"
#include "spdlog/details/file_helper-inl.h"
#include "spdlog/sinks/basic_file_sink-inl.h"
template class spdlog::sinks::basic_file_sink<std::mutex>;
template class spdlog::sinks::basic_file_sink<spdlog::details::null_mutex>;
template class SPDLOG_API spdlog::sinks::basic_file_sink<std::mutex>;
template class SPDLOG_API spdlog::sinks::basic_file_sink<spdlog::details::null_mutex>;
#include "spdlog/sinks/rotating_file_sink-inl.h"
template class spdlog::sinks::rotating_file_sink<std::mutex>;
template class spdlog::sinks::rotating_file_sink<spdlog::details::null_mutex>;
#include <spdlog/sinks/rotating_file_sink-inl.h>
template class SPDLOG_API spdlog::sinks::rotating_file_sink<std::mutex>;
template class SPDLOG_API spdlog::sinks::rotating_file_sink<spdlog::details::null_mutex>;

View File

@@ -1,182 +1,189 @@
#ifndef SPDLOG_COMPILED_LIB
#error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif
// Slightly modified version of fmt lib's format.cc source file.
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
#if !defined(SPDLOG_FMT_EXTERNAL)
#include "spdlog/fmt/bundled/format-inl.h"
#ifndef SPDLOG_COMPILED_LIB
#error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif
#if !defined(SPDLOG_FMT_EXTERNAL)
#include <spdlog/fmt/bundled/format-inl.h>
FMT_BEGIN_NAMESPACE
namespace internal {
namespace internal {
template <typename T>
int format_float(char* buf, std::size_t size, const char* format, int precision,
T value) {
template<typename T>
int format_float(char *buf, std::size_t size, const char *format, int precision, T value)
{
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
if (precision > 100000)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
if (precision > 100000)
throw std::runtime_error("fuzz mode - avoid large allocation inside snprintf");
#endif
// Suppress the warning about nonliteral format string.
auto snprintf_ptr = FMT_SNPRINTF;
return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value);
}
struct sprintf_specs {
int precision;
char type;
bool alt : 1;
// Suppress the warning about nonliteral format string.
int (*snprintf_ptr)(char *, size_t, const char *, ...) = FMT_SNPRINTF;
return precision < 0 ? snprintf_ptr(buf, size, format, value) : snprintf_ptr(buf, size, format, precision, value);
}
struct sprintf_specs
{
int precision;
char type;
bool alt : 1;
template <typename Char>
constexpr sprintf_specs(basic_format_specs<Char> specs)
: precision(specs.precision), type(specs.type), alt(specs.alt) {}
template<typename Char>
constexpr sprintf_specs(basic_format_specs<Char> specs)
: precision(specs.precision)
, type(specs.type)
, alt(specs.alt)
{}
constexpr bool has_precision() const { return precision >= 0; }
};
constexpr bool has_precision() const
{
return precision >= 0;
}
};
// This is deprecated and is kept only to preserve ABI compatibility.
template <typename Double>
char* sprintf_format(Double value, internal::buffer<char>& buf,
sprintf_specs specs) {
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
template<typename Double>
char *sprintf_format(Double value, internal::buffer<char> &buf, sprintf_specs specs)
{
// Buffer capacity must be non-zero, otherwise MSVC's vsnprintf_s will fail.
FMT_ASSERT(buf.capacity() != 0, "empty buffer");
// Build format string.
enum { max_format_size = 10 }; // longest format: %#-*.*Lg
char format[max_format_size];
char* format_ptr = format;
*format_ptr++ = '%';
if (specs.alt || !specs.type) *format_ptr++ = '#';
if (specs.precision >= 0) {
*format_ptr++ = '.';
*format_ptr++ = '*';
}
if (std::is_same<Double, long double>::value) *format_ptr++ = 'L';
// Build format string.
enum
{
max_format_size = 10
}; // longest format: %#-*.*Lg
char format[max_format_size];
char *format_ptr = format;
*format_ptr++ = '%';
if (specs.alt || !specs.type)
*format_ptr++ = '#';
if (specs.precision >= 0)
{
*format_ptr++ = '.';
*format_ptr++ = '*';
}
if (std::is_same<Double, long double>::value)
*format_ptr++ = 'L';
char type = specs.type;
char type = specs.type;
if (type == '%')
type = 'f';
else if (type == 0 || type == 'n')
type = 'g';
if (type == '%')
type = 'f';
else if (type == 0 || type == 'n')
type = 'g';
#if FMT_MSC_VER
if (type == 'F') {
// MSVC's printf doesn't support 'F'.
type = 'f';
}
if (type == 'F')
{
// MSVC's printf doesn't support 'F'.
type = 'f';
}
#endif
*format_ptr++ = type;
*format_ptr = '\0';
*format_ptr++ = type;
*format_ptr = '\0';
// Format using snprintf.
char* start = nullptr;
char* decimal_point_pos = nullptr;
for (;;) {
std::size_t buffer_size = buf.capacity();
start = &buf[0];
int result =
format_float(start, buffer_size, format, specs.precision, value);
if (result >= 0) {
unsigned n = internal::to_unsigned(result);
if (n < buf.capacity()) {
// Find the decimal point.
auto p = buf.data(), end = p + n;
if (*p == '+' || *p == '-') ++p;
if (specs.type != 'a' && specs.type != 'A') {
while (p < end && *p >= '0' && *p <= '9') ++p;
if (p < end && *p != 'e' && *p != 'E') {
decimal_point_pos = p;
if (!specs.type) {
// Keep only one trailing zero after the decimal point.
++p;
if (*p == '0') ++p;
while (p != end && *p >= '1' && *p <= '9') ++p;
char* where = p;
while (p != end && *p == '0') ++p;
if (p == end || *p < '0' || *p > '9') {
if (p != end) std::memmove(where, p, to_unsigned(end - p));
n -= static_cast<unsigned>(p - where);
}
}
}
// Format using snprintf.
char *start = nullptr;
char *decimal_point_pos = nullptr;
for (;;)
{
std::size_t buffer_size = buf.capacity();
start = &buf[0];
int result = format_float(start, buffer_size, format, specs.precision, value);
if (result >= 0)
{
unsigned n = internal::to_unsigned(result);
if (n < buf.capacity())
{
// Find the decimal point.
auto p = buf.data(), end = p + n;
if (*p == '+' || *p == '-')
++p;
if (specs.type != 'a' && specs.type != 'A')
{
while (p < end && *p >= '0' && *p <= '9')
++p;
if (p < end && *p != 'e' && *p != 'E')
{
decimal_point_pos = p;
if (!specs.type)
{
// Keep only one trailing zero after the decimal point.
++p;
if (*p == '0')
++p;
while (p != end && *p >= '1' && *p <= '9')
++p;
char *where = p;
while (p != end && *p == '0')
++p;
if (p == end || *p < '0' || *p > '9')
{
if (p != end)
std::memmove(where, p, to_unsigned(end - p));
n -= static_cast<unsigned>(p - where);
}
buf.resize(n);
break; // The buffer is large enough - continue with formatting.
}
buf.reserve(n + 1);
} else {
// If result is negative we ask to increase the capacity by at least 1,
// but as std::vector, the buffer grows exponentially.
buf.reserve(buf.capacity() + 1);
}
}
return decimal_point_pos;
buf.resize(n);
break; // The buffer is large enough - continue with formatting.
}
} // namespace internal
buf.reserve(n + 1);
}
else
{
// If result is negative we ask to increase the capacity by at least 1,
// but as std::vector, the buffer grows exponentially.
buf.reserve(buf.capacity() + 1);
}
}
return decimal_point_pos;
}
} // namespace internal
template FMT_API char* internal::sprintf_format(double, internal::buffer<char>&,
sprintf_specs);
template FMT_API char* internal::sprintf_format(long double,
internal::buffer<char>&,
sprintf_specs);
template FMT_API char *internal::sprintf_format(double, internal::buffer<char> &, sprintf_specs);
template FMT_API char *internal::sprintf_format(long double, internal::buffer<char> &, sprintf_specs);
template struct FMT_API internal::basic_data<void>;
template struct FMT_INSTANTIATION_DEF_API internal::basic_data<void>;
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, internal::float_specs,
internal::buffer<char>&) =
internal::format_float;
int (*instantiate_format_float)(double, int, internal::float_specs, internal::buffer<char> &) = internal::format_float;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API internal::locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
template FMT_API internal::locale_ref::locale_ref(const std::locale &loc);
template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
#endif
// Explicit instantiations for char.
template FMT_API std::string internal::grouping_impl<char>(locale_ref);
template FMT_API char internal::thousands_sep_impl(locale_ref);
template FMT_API char internal::decimal_point_impl(locale_ref);
template FMT_API std::string internal::grouping_impl<char>(locale_ref);
template FMT_API char internal::thousands_sep_impl(locale_ref);
template FMT_API char internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<char>::append(const char*, const char*);
template FMT_API void internal::buffer<char>::append(const char *, const char *);
template FMT_API void internal::arg_map<format_context>::init(
const basic_format_args<format_context>& args);
template FMT_API void internal::arg_map<format_context>::init(const basic_format_args<format_context> &args);
template FMT_API std::string internal::vformat<char>(
string_view, basic_format_args<format_context>);
template FMT_API std::string internal::vformat<char>(string_view, basic_format_args<format_context>);
template FMT_API format_context::iterator internal::vformat_to(
internal::buffer<char>&, string_view, basic_format_args<format_context>);
template FMT_API format_context::iterator internal::vformat_to(internal::buffer<char> &, string_view, basic_format_args<format_context>);
template FMT_API int internal::snprintf_float(double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::snprintf_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(double, int, internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::format_float(long double, int,
internal::float_specs,
internal::buffer<char>&);
template FMT_API int internal::snprintf_float(double, int, internal::float_specs, internal::buffer<char> &);
template FMT_API int internal::snprintf_float(long double, int, internal::float_specs, internal::buffer<char> &);
template FMT_API int internal::format_float(double, int, internal::float_specs, internal::buffer<char> &);
template FMT_API int internal::format_float(long double, int, internal::float_specs, internal::buffer<char> &);
// Explicit instantiations for wchar_t.
template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref);
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
template FMT_API std::string internal::grouping_impl<wchar_t>(locale_ref);
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
template FMT_API wchar_t internal::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
template FMT_API void internal::buffer<wchar_t>::append(const wchar_t *, const wchar_t *);
template FMT_API std::wstring internal::vformat<wchar_t>(
wstring_view, basic_format_args<wformat_context>);
template FMT_API std::wstring internal::vformat<wchar_t>(wstring_view, basic_format_args<wformat_context>);
FMT_END_NAMESPACE
#endif
#endif // !SPDLOG_FMT_EXTERNAL

View File

@@ -5,22 +5,22 @@
#error Please define SPDLOG_COMPILED_LIB to compile this file.
#endif
#include "spdlog/spdlog-inl.h"
#include "spdlog/common-inl.h"
#include "spdlog/details/backtracer-inl.h"
#include "spdlog/details/registry-inl.h"
#include "spdlog/details/os-inl.h"
#include "spdlog/details/pattern_formatter-inl.h"
#include "spdlog/details/log_msg-inl.h"
#include "spdlog/details/log_msg_buffer-inl.h"
#include "spdlog/logger-inl.h"
#include "spdlog/sinks/sink-inl.h"
#include "spdlog/sinks/base_sink-inl.h"
#include "spdlog/details/null_mutex.h"
#include <spdlog/spdlog-inl.h>
#include <spdlog/common-inl.h>
#include <spdlog/details/backtracer-inl.h>
#include <spdlog/details/registry-inl.h>
#include <spdlog/details/os-inl.h>
#include <spdlog/pattern_formatter-inl.h>
#include <spdlog/details/log_msg-inl.h>
#include <spdlog/details/log_msg_buffer-inl.h>
#include <spdlog/logger-inl.h>
#include <spdlog/sinks/sink-inl.h>
#include <spdlog/sinks/base_sink-inl.h>
#include <spdlog/details/null_mutex.h>
#include <mutex>
// template instantiate logger constructor with sinks init list
template spdlog::logger::logger(std::string name, sinks_init_list::iterator begin, sinks_init_list::iterator end);
template class spdlog::sinks::base_sink<std::mutex>;
template class spdlog::sinks::base_sink<spdlog::details::null_mutex>;
template SPDLOG_API spdlog::logger::logger(std::string name, sinks_init_list::iterator begin, sinks_init_list::iterator end);
template class SPDLOG_API spdlog::sinks::base_sink<std::mutex>;
template class SPDLOG_API spdlog::sinks::base_sink<spdlog::details::null_mutex>;

View File

@@ -7,23 +7,23 @@
#include <mutex>
#include "spdlog/details/null_mutex.h"
#include "spdlog/async.h"
#include "spdlog/sinks/stdout_sinks-inl.h"
#include <spdlog/details/null_mutex.h>
#include <spdlog/async.h>
#include <spdlog/sinks/stdout_sinks-inl.h>
template class spdlog::sinks::stdout_sink_base<spdlog::details::console_mutex>;
template class spdlog::sinks::stdout_sink_base<spdlog::details::console_nullmutex>;
template class spdlog::sinks::stdout_sink<spdlog::details::console_mutex>;
template class spdlog::sinks::stdout_sink<spdlog::details::console_nullmutex>;
template class spdlog::sinks::stderr_sink<spdlog::details::console_mutex>;
template class spdlog::sinks::stderr_sink<spdlog::details::console_nullmutex>;
template class SPDLOG_API spdlog::sinks::stdout_sink_base<spdlog::details::console_mutex>;
template class SPDLOG_API spdlog::sinks::stdout_sink_base<spdlog::details::console_nullmutex>;
template class SPDLOG_API spdlog::sinks::stdout_sink<spdlog::details::console_mutex>;
template class SPDLOG_API spdlog::sinks::stdout_sink<spdlog::details::console_nullmutex>;
template class SPDLOG_API spdlog::sinks::stderr_sink<spdlog::details::console_mutex>;
template class SPDLOG_API spdlog::sinks::stderr_sink<spdlog::details::console_nullmutex>;
template std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt<spdlog::synchronous_factory>(const std::string &logger_name);
template std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st<spdlog::synchronous_factory>(const std::string &logger_name);
template std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt<spdlog::synchronous_factory>(const std::string &logger_name);
template std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st<spdlog::synchronous_factory>(const std::string &logger_name);
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt<spdlog::synchronous_factory>(const std::string &logger_name);
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st<spdlog::synchronous_factory>(const std::string &logger_name);
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt<spdlog::synchronous_factory>(const std::string &logger_name);
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st<spdlog::synchronous_factory>(const std::string &logger_name);
template std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt<spdlog::async_factory>(const std::string &logger_name);
template std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st<spdlog::async_factory>(const std::string &logger_name);
template std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt<spdlog::async_factory>(const std::string &logger_name);
template std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st<spdlog::async_factory>(const std::string &logger_name);
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt<spdlog::async_factory>(const std::string &logger_name);
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st<spdlog::async_factory>(const std::string &logger_name);
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt<spdlog::async_factory>(const std::string &logger_name);
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st<spdlog::async_factory>(const std::string &logger_name);

View File

@@ -1,17 +1,25 @@
cmake_minimum_required(VERSION 3.2)
project(spdlog_utests CXX)
if(NOT TARGET spdlog)
# Stand-alone build
find_package(spdlog REQUIRED)
endif()
include(../cmake/utils.cmake)
find_package(PkgConfig)
if(PkgConfig_FOUND)
pkg_check_modules(systemd libsystemd)
pkg_check_modules(systemd libsystemd)
endif()
set(SPDLOG_UTESTS_SOURCES
test_file_helper.cpp
test_file_logging.cpp
test_daily_logger.cpp
test_daily_logger.cpp
test_misc.cpp
test_eventlog.cpp
test_pattern_formatter.cpp
test_async.cpp
test_registry.cpp
@@ -19,42 +27,44 @@ set(SPDLOG_UTESTS_SOURCES
utils.cpp
main.cpp
test_mpmc_q.cpp
test_dup_filter.cpp
test_dup_filter.cpp
test_fmt_helper.cpp
test_stdout_api.cpp
test_backtrace.cpp
test_create_dir.cpp)
test_stdout_api.cpp
test_backtrace.cpp
test_create_dir.cpp
test_cfg.cpp
test_time_point.cpp)
if(NOT SPDLOG_NO_EXCEPTIONS)
list(APPEND SPDLOG_UTESTS_SOURCES test_errors.cpp)
list(APPEND SPDLOG_UTESTS_SOURCES test_errors.cpp)
endif()
if(systemd_FOUND)
list(APPEND SPDLOG_UTESTS_SOURCES test_systemd.cpp)
list(APPEND SPDLOG_UTESTS_SOURCES test_systemd.cpp)
endif()
enable_testing()
function(spdlog_prepare_test test_target spdlog_lib)
add_executable(${test_target} ${SPDLOG_UTESTS_SOURCES})
spdlog_enable_warnings(${test_target})
spdlog_enable_warnings(${test_target})
target_link_libraries(${test_target} PRIVATE ${spdlog_lib})
if(systemd_FOUND)
target_link_libraries(${test_target} PRIVATE ${systemd_LIBRARIES})
endif()
if(SPDLOG_SANITIZE_ADDRESS)
spdlog_enable_sanitizer(${test_target})
endif()
if(systemd_FOUND)
target_link_libraries(${test_target} PRIVATE ${systemd_LIBRARIES})
endif()
if(SPDLOG_SANITIZE_ADDRESS)
spdlog_enable_sanitizer(${test_target})
endif()
add_test(NAME ${test_target} COMMAND ${test_target})
set_tests_properties(${test_target} PROPERTIES RUN_SERIAL ON)
endfunction()
# The compiled library tests
if(SPDLOG_BUILD_TESTS)
if(SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_ALL)
spdlog_prepare_test(spdlog-utests spdlog::spdlog)
endif()
# The header-only library version tests
if(SPDLOG_BUILD_TESTS_HO)
if(SPDLOG_BUILD_TESTS_HO OR SPDLOG_BUILD_ALL)
spdlog_prepare_test(spdlog-utests-ho spdlog::spdlog_header_only)
endif()

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