Compare commits

...

224 Commits

Author SHA1 Message Date
Gabi Melman
100f30043f Update version.h 2021-03-25 21:00:48 +02:00
Gabi Melman
1574b5b0a2 Merge pull request #1889 from stevenlunt/set_string_view
remove constexpr on level_string_views to fix compilation on C++17 fr…
2021-03-25 20:59:37 +02:00
Gabi Melman
012fe99ab1 Update version.h 2021-03-25 20:34:53 +02:00
Gabi Melman
8ff5a3e096 Merge pull request #1890 from prince-chrismc/patch-4
Remove version requirement from fmt find_package
2021-03-25 19:22:51 +02:00
Chris Mc
65317eb019 Remove version requirement from fmt find_package
By introducing 'no module' support this inadvertently triggered the find_package to use the "full version" version selection.

From https://cmake.org/cmake/help/latest/command/find_package.html#version-selection:

> When the [version] argument is given, Config mode will only find a version of the package that claims compatibility with the requested version (see format specification).

FMT does not set this configuration https://github.com/fmtlib/fmt/blob/7.1.3/support/cmake/fmt-config.cmake.in... but regardless it would not be "any compatibility" based on version semantics.

Which causes this error. v1.8.2 builds perfectly fine.

```
CMake Error at CMakeLists.txt:181 (find_package):
  Could not find a configuration file for package "fmt" that is compatible
  with requested version "5.3.0".

  The following configuration files were considered but not accepted:

    /home/proj/build/e98598522b7c484d9220bffc7ec84474515facbc/fmt-config.cmake, version: 7.1.3
```
2021-03-24 21:54:36 -04:00
steven lunt
ac3e26b0ff remove constexpr on level_string_views to fix compilation on C++17 from addition of set_string_view 2021-03-24 18:20:08 -04:00
Gabi Melman
e86f450428 Merge pull request #1885 from ahmedyarub/fix_android_build
Add required libraries for Android CMake build
2021-03-25 00:14:41 +02:00
Gabi Melman
7b2776fdc7 Merge pull request #1888 from stevenlunt/set_string_view
added spdlog::level::set_string_view to enable alternate log level na…
2021-03-24 23:10:11 +02:00
steven lunt
2a16d1d230 added spdlog::level::set_string_view to enable alternate log level names without changing the build via SPDLOG_LEVEL_NAMES 2021-03-24 16:22:54 -04:00
Gabi Melman
53e1c9ab11 Update version number to 1.8.3 2021-03-24 21:49:10 +02:00
Gabi Melman
410abc4626 Added the Windows Event logger to readme features 2021-03-24 01:45:26 +02:00
Ahmed Yarub Hani Al Nuaimi
a2e28443f0 Add required libraries for Android CMake build 2021-03-22 00:08:17 -03:00
Gabi Melman
c1af0a3f21 Merge pull request #1882 from imsherlock/v1.x
add default cases
2021-03-20 21:02:57 +02:00
Ryan Sherlock
bb5e1ee2f9 Removing changes to bundled fmt
Removing changes to the bundled fmt library. The default case
statements will be changed in the upstream library.

Signed-off-by: Ryan Sherlock <sherlock@loftorbital.com>
2021-03-20 09:22:40 -07:00
Ryan Sherlock
3aee89c8fd add default cases
Adding default case for cases where the compilation flag
-Wswitch-default is present on the command line when spdlog is
included in external projects.

Signed-off-by: Ryan Sherlock <ryan.m.sherlock@gmail.com>
2021-03-19 10:06:09 -07:00
gabime
44e1f9f682 Added nonreturn sepcifier to fix #1748 2021-03-17 00:25:26 +02:00
gabime
37d76b961c Fix #1876 2021-03-16 23:39:55 +02:00
gabime
1305663d99 make sure __cplusplus is defined under msvc 2021-03-12 15:10:15 +02:00
gabime
8f4efe57a2 make sure __cplusplus is defined under msvc 2021-03-12 15:09:35 +02:00
gabime
0613dbc4a2 Revert pr #1860 2021-03-04 23:52:50 +02:00
Gabi Melman
0ed0d69368 Update CMakeLists.txt 2021-03-04 23:24:02 +02:00
Gabi Melman
2ffbbee1f6 Merge pull request #1860 from rofferom/msvc-allow-static-runtime
CMake: Add SPDLOG_STATIC_VCRT to choose static MSVC runtime
2021-03-04 23:17:32 +02:00
Romain Roffé
b9d2f2537b CMake: Add SPDLOG_STATIC_VCRT to choose static MSVC runtime 2021-03-04 20:14:18 +01:00
Gabi Melman
69dc173979 Update README.md 2021-03-03 00:17:33 +02:00
Gabi Melman
ded8b5ebd4 Added build2 to package managers section in readme 2021-03-03 00:16:59 +02:00
Gabi Melman
ed58ae9f98 Revert PR #1851 2021-03-02 22:49:11 +02:00
Gabi Melman
f7f790b4b3 Merge pull request #1851 from Klaim/patch-1
Add build2 package support
2021-03-02 22:37:43 +02:00
Klaim (Joël Lamotte)
fe74c80992 Add build2 package support
WARNING: Please do not merge yet! See below.

We are currently in the process of packaging `spdlog` for `build2`, we have it working (even on unsupported Windows, see for example: https://ci.stage.build2.org/@a993b64e-8ba2-422e-97d7-250cdb5828e0?builds=&pv=&tc=*&cf=&mn=&tg=&rs=*)

This change simply adds the necessary information to use this package when using `build2`. Some notes:
 - not sure how to give the info succintly because `build2` allows packages to come from different sources, including the git repository of the package - in doubt I just linked to the future community repository address providing all the info, but the other package manager didn't do it like that so not sure if it's ok for you?; 
 - `build2` distinguish the package (`depends: spdlog <some-version-scheme>`, added in the `manifest` file of a `build2` project) and the target (`spdlog%lib{spdlog}` imported in a `buildfile`) because packages can contain several targets. I was not sure how to formulate that here, so feel free to tell me if I should just provide the name of the package and not more info?

The package isn't available yet so this have to be merged only once it is made available (probably in a few days).
2021-02-27 14:45:55 +01:00
Gabi Melman
fa659bf7ad Update daily_file_sink.h 2021-02-27 11:18:12 +02:00
Gabi Melman
9b41649601 Merge pull request #1849 from concatime/cmake-library-type-option-flag
CMake: Replace custom SPDLOG_BUILD_SHARED by standard BUILD_SHARED_LIBS
2021-02-26 23:35:23 +02:00
Issam E. Maghni
1b3438f5a5 AppVeyor: Use standard BUILD_SHARED_LIBS 2021-02-26 16:26:53 -05:00
Issam E. Maghni
3eed64e5c4 CMake: Replace custom SPDLOG_BUILD_SHARED by standard BUILD_SHARED_LIBS 2021-02-26 16:26:50 -05:00
Gabi Melman
0fac33781d Update daily_file_sink.h 2021-02-26 12:38:49 +02:00
Gabi Melman
3135b6a33d Update comment 2021-02-26 12:33:43 +02:00
Gabi Melman
2686ae2322 Merge pull request #1847 from fawdlstty/v1.x
add daily sink filename format
2021-02-26 12:17:29 +02:00
fawdlstty
a709e29586 fix unique mode compile 2021-02-26 10:18:33 +08:00
fawdlstty
dd46579cb4 fix name 2021-02-26 10:17:43 +08:00
fawdlstty
f4b7210e7b remove externs 2021-02-26 09:28:12 +08:00
fawdlstty
05a0b0d7b0 use fmt::chrono and remove is_fmt flags 2021-02-26 09:21:13 +08:00
fawdlstty
c1f4d7506a replace tab 2021-02-25 23:57:37 +08:00
fawdlstty
b6ba0be550 add daily_logger_format_mt and daily_logger_format_st sink 2021-02-25 23:55:19 +08:00
Gabi Melman
23dfb4e2f9 Merge pull request #1846 from MathiasMagnus/fix-bench
Avoid relying on function name decay mechanics
2021-02-24 11:26:25 +02:00
Máté Ferenc Nagy-Egri
7a10e31982 Avoid relying on function name decay mechanics 2021-02-24 09:45:35 +01:00
Gabi Melman
de89c4fd01 Merge pull request #1835 from jneruda/v1.x
Set default value to registry::err_handler_
2021-02-18 19:49:39 +02:00
Jakub Neruda
5d4956d34b Set default value to registry::err_handler_ 2021-02-17 14:02:20 +01:00
Gabi Melman
42c5eb59c9 Update wincolor_sink-inl.h 2021-02-14 01:49:29 +02:00
Gabi Melman
09cc6e7754 Simplify wincolor_sink::set_foreground_color_ 2021-02-14 01:03:02 +02:00
Gabi Melman
4a5bc41e89 Validate range in wincolor_sink::print_range_ 2021-02-14 00:20:44 +02:00
Gabi Melman
0ade18828d Remove switch statement from wincolor_sink::set_color_mode_impl 2021-02-13 23:53:36 +02:00
Gabi Melman
91046e6ca4 Simplify wincolor sink color mode management 2021-02-13 20:59:10 +02:00
Gabi Melman
17e1ba8ae2 simplifiy and mutex protect set_color_mode in wincolor sink 2021-02-13 20:16:15 +02:00
Gabi Melman
c47c854f15 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2021-02-13 20:06:51 +02:00
Gabi Melman
e931866b35 Removed public color constants from wincolor sink and intensify red and yellow 2021-02-13 20:06:40 +02:00
Gabi Melman
7828a065bf Update stdout_sinks-inl.h 2021-02-13 17:45:00 +02:00
Gabi Melman
3e689e700e Handle return values of win32 color functions in wincolor sink 2021-02-13 17:26:55 +02:00
Gabi Melman
a9964afcf7 Don't throw wincolor redirection failed, to be consistent with the non-redirect behaviour 2021-02-13 16:58:04 +02:00
Gabi Melman
95c19876c6 Skip log in wincolor sink if out_handle_ is null 2021-02-13 16:55:40 +02:00
Gabi Melman
5efccfa5e2 Fix #1828 2021-02-13 16:11:07 +02:00
Gabi Melman
89e737a258 Handle invalid handle in wincolor sink 2021-02-13 15:00:35 +02:00
Gabi Melman
8fbc853b0d Merge pull request #1829 from prateek9623/fix-prevent-msvc-warning-in-cuda
Fix: prevents MSVC warning flags to propagate in CUDA
2021-02-12 10:57:33 +02:00
Prateek Chokse
2e008b319c Fix: prevents MSVC warning flags to propagate in CUDA 2021-02-12 14:13:51 +05:30
Gabi Melman
ff6e3c95f2 Merge pull request #1819 from NukeULater/v1.x
better numeric_limits<>::max/min fix when using SPDLOG_WCHAR_TO_UTF8_SUPPORT
2021-02-06 11:32:14 +02:00
Unknown
7e9385405f better numeric_limits<>::max/min fix when using SPDLOG_WCHAR_TO_UTF8_SUPPORT
NOMINMAX macro doesn't work everytime for some reason
2021-02-06 10:50:29 +02:00
Gabi Melman
592ea36a86 Fix #1811 2021-01-21 21:12:42 +02:00
Gabi Melman
e059ebf99d Merge pull request #1807 from ChristianPanov/patch-1
Initializer list style consistency
2021-01-21 03:02:26 +02:00
Gabi Melman
609480ed78 Merge pull request #1798 from gv-me/constexpr-log-levels
add constexpr to SPDLOG_LEVEL_NAMES declaration
2021-01-21 03:00:49 +02:00
Gabi Melman
4271185936 Merge pull request #1805 from sillykelvin/patch-1
fix compiling error and typo
2021-01-21 02:56:53 +02:00
Christian Panov
aacae62591 Initializer list style consistency 2021-01-20 20:11:34 +02:00
Kelvin Hu
47cbf3828d fix compiling error and typo 2021-01-19 22:03:47 +08:00
Gabi Melman
46d418164d Merge pull request #1787 from sylveon/windows-separator-filenames
Better support for / separators on Windows, improve wchar filename coverage
2021-01-12 00:29:08 +02:00
Gaurav Vaidya
ede8d84884 fix constexpr declaration for c++11 2021-01-11 11:25:27 +01:00
Gaurav Vaidya
53d223b45f add constexpr to SPDLOG_LEVEL_NAMES declaration
for #1791
2021-01-11 11:16:06 +01:00
Charles Milette
ac35dd5a6f Prevent integer overflows in wstr_to_utf8buf and utf8_to_wstrbuf 2021-01-10 18:26:24 -05:00
Charles Milette
9e19012cb0 Remove filename_memory_buf_t from headers 2021-01-10 18:19:28 -05:00
Charles Milette
1234cda3b3 Don't build the example with wide filenames 2021-01-10 18:19:00 -05:00
Gabi Melman
710a0e3a45 Merge pull request #1795 from graydon/truncate-but-still-append
Open files with "ab" mode even if truncating.
2021-01-09 12:20:26 +02:00
Graydon Hoare
b7f24b2456 Open files with "ab" mode even if truncating. 2021-01-09 00:35:32 -08:00
Charles Milette
fc594b551a Prevent win_eventlog_sink from silently discarding errors when wide support is enabled 2021-01-06 22:17:09 -05:00
Charles Milette
f39ccccc0c Fix linker error 2021-01-06 21:52:59 -05:00
Charles Milette
f0a4ddd78b Fix character issues in test_errors.cpp 2021-01-06 20:00:59 -05:00
Charles Milette
c691769e46 Fix other build errors and unit tests 2021-01-06 19:39:47 -05:00
Charles Milette
19dc30567e Fix build errors 2021-01-06 18:55:57 -05:00
Charles Milette
a453bccff0 Better support for / separators on Windows, improve wchar filename coverage 2021-01-06 04:15:14 -05:00
Gabi Melman
aa2053a575 Merge pull request #1774 from Ryan-rsm-McKenzie/v1.x
skip module mode when finding fmt
2020-12-27 14:19:08 +02:00
ryan-rsm-mckenzie
3d8f71c4d2 skip module mode when finding fmt 2020-12-27 01:37:12 -08:00
Gabi Melman
6aaaabbc4d Merge pull request #1773 from Ryan-rsm-McKenzie/v1.x
ensure SPDLOG_FMT_EXTERNAL is honored throughout whole codebase
2020-12-27 11:17:30 +02:00
ryan-rsm-mckenzie
42c36f48ed ensure SPDLOG_FMT_EXTERNAL is honored throughout whole codebase 2020-12-27 00:47:57 -08:00
Gabi Melman
a5f4139102 Merge pull request #1771 from Ryan-rsm-McKenzie/v1.x
ensure stdout color sinks do not leak windows headers with SPDLOG_COMPILED_LIB
2020-12-26 15:23:37 +02:00
ryan-rsm-mckenzie
030d85a9b3 ensure stdout color sinks do not leak windows headers with SPDLOG_COMPILED_LIB 2020-12-26 03:45:12 -08:00
Gabi Melman
adcfb7fb55 Merge pull request #1768 from dominicpoeschko/colorterminal_detection
Changed is_color_terminal to be more generic.
2020-12-20 03:01:42 +02:00
dominic
cec365888a Added check for COLORTERM environment variable to detect if terminal
supports color.
Added alacritty to supported color terminals.
2020-12-19 23:22:31 +01:00
Gabi Melman
55bfa8dd11 Merge pull request #1766 from shimaowo/v1.x
Fix #1765 - Add SPDLOG_DISABLE_DEFAULT_LOGGER as a cmake option
2020-12-19 07:40:39 +02:00
shimaowo
e99759fe45 Fix #1765 - Add SPDLOG_DISABLE_DEFAULT_LOGGER as a cmake option 2020-12-18 14:31:34 -08:00
Gabi Melman
17c6e6ee3f Merge pull request #1760 from iko1/v1.x
fix windows event sink log compilation error with UNICODE preprocessor
2020-12-16 11:41:44 +02:00
Amir Alperin
7fff900a1a Fix error message in exception 2020-12-16 06:57:24 +02:00
Amir Alperin
c67974e4c8 replace deprected wchar string conversion 2020-12-16 01:14:55 +02:00
Gabi Melman
a36696e02e Merge pull request #1763 from BVonk/v1.x
Add Hourly file sink
2020-12-16 01:11:51 +02:00
BVonk
9b80ca6c41 Add files via upload 2020-12-15 17:21:09 +01:00
Amir Alperin
22f514aabf restore ident format 2020-12-13 23:11:25 +02:00
Amir Alperin
211478e13e convert message back to wchar 2020-12-13 10:45:55 +02:00
Amir Alperin
5e33a7e58b fix compilation errors 2020-12-13 10:11:00 +02:00
gabime
b2e31721e8 Update example 2020-12-11 16:58:49 +02:00
gabime
de0dbfa359 version 1.8.2 2020-12-11 16:40:52 +02:00
gabime
f93459579f version 1.9.0 2020-12-11 16:37:37 +02:00
gabime
2b81c40b90 Bump fmt to version 7.1.3 2020-12-11 16:32:39 +02:00
Gabi Melman
233e97c5e4 Merge pull request #1749 from bluescarni/pr/async_test_fix
Increase sleep time on a test case to avoid spurious failures
2020-11-26 11:03:46 +02:00
Francesco Biscani
fc1ce48dc7 Increase sleep time on a test case to avoid spurious failures. 2020-11-26 09:45:39 +01:00
Gabi Melman
fd5562eebe Merge pull request #1744 from ArnaudBienner/ArnaudBienner-readme-patch
📝 README example: Add {:a} format flag to bin_to_hex example
2020-11-23 02:01:34 +02:00
ArnaudBienner
0695d9cb5f 📝 README example: Add {:a} format flag to bin_to_hex example
Add {:a} format flag to bin_to_hex example
2020-11-22 20:54:28 +01:00
Gabi Melman
456b24134d Merge pull request #1742 from jwittbrodt/v1.x
Add missing include
2020-11-20 18:13:49 +02:00
Jonas Wittbrodt
f8ba24afee add missing <algorithm> include 2020-11-19 14:09:33 +01:00
Gabi Melman
eebb921c9f Merge pull request #1735 from o2gy84/v1.x
Ability to get size of messages queue of async thread pool
2020-11-13 12:59:52 +02:00
Могилин Виктор
e17ee87f38 Ability to get size of messages queue of async thread pool 2020-11-13 13:12:32 +03:00
Gabi Melman
18e3f07f7d Fix #1710 2020-11-05 18:27:31 +02:00
Gabi Melman
9ce39a470f Merge pull request #1726 from dkavolis/v1.x
Perfect forwarding for arguments
2020-11-02 04:02:38 +02:00
dkavolis
23572369fc Perfect forwarding for arguments 2020-11-02 00:37:03 +00:00
Gabi Melman
01b350de96 Merge pull request #1712 from ChristofKaufmann/deb-package
Add CPack debian package settings
2020-10-20 22:23:45 +03:00
Christof Kaufmann
365e470a32 Add CPack debian package settings 2020-10-20 19:57:56 +02:00
Gabi Melman
a42b40656c Merge pull request #1711 from ChristofKaufmann/fix-typo
Fix typo in comment
2020-10-20 10:00:43 +03:00
Christof Kaufmann
40160f2a57 Fix typo in comment 2020-10-20 02:49:09 +02:00
Gabi Melman
90b33b1552 Merge pull request #1709 from kitattyor/v1.x
Download googlebenchmark if not found
2020-10-17 17:56:59 +03:00
kitattyor
5567ed01e5 Download googlebenchmark if not found; requires cmake 3.11 2020-10-17 21:08:36 +08:00
gabime
cbe9448650 version 1.8.1 2020-09-30 17:30:06 +03:00
gabime
5b345534dc Minor change in ifdef for clarity 2020-09-30 17:29:44 +03:00
Gabi Melman
c8dc318fb3 Update logger-inl.h 2020-09-29 02:23:03 +03:00
Gabi Melman
23cb1a1080 Update logger-inl.h 2020-09-29 02:20:43 +03:00
Gabi Melman
21cf8d7d3c Merge pull request #1685 from eddelbuettel/feature/r_use_of_reprintf
let R header defines switch to REprintf over fprintf(stdderr)
2020-09-29 02:18:35 +03:00
Dirk Eddelbuettel
3cf4d34094 let R header defines switch to REprintf over fprintf(stdderr) 2020-09-28 17:13:09 -05:00
Gabi Melman
16d78ae5db Update stdout_sinks-inl.h 2020-09-28 17:08:13 +03:00
gabime
62b4b7af83 Fix #1667 2020-09-28 13:39:31 +03:00
Gabi Melman
9799ecac6a Remove redundant size check before calling WriteFile 2020-09-27 21:27:58 +03:00
Gabi Melman
dccb766095 Fix warning about enum usage 2020-09-27 19:08:24 +03:00
gabime
c97983a91c Fix linux build 2020-09-27 18:42:27 +03:00
gabime
680fb07fd5 Updatd WriteFile usage 2020-09-27 18:34:01 +03:00
gabime
cfd0ea197c Simplify WriteFile() usage under windows 2020-09-27 18:32:08 +03:00
gabime
48d4ed9bc0 Fix #1675 2020-09-27 18:27:41 +03:00
gabime
3bed78356e Added cfg tests 2020-09-27 02:10:52 +03:00
gabime
8923922f30 Cleaned level loading from env var 2020-09-27 02:08:24 +03:00
gabime
7542e42e4f removed extra parentheses 2020-09-27 01:05:55 +03:00
Gabi Melman
7a9b23e4f4 Update registry-inl.h 2020-09-26 16:09:43 +03:00
gabime
47253ba2a1 Updated comment 2020-09-26 15:56:57 +03:00
gabime
69b54dd9e4 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2020-09-26 15:49:21 +03:00
gabime
36138617fc small readablilty update in registry 2020-09-26 15:36:57 +03:00
gabime
231ca50700 clang-format 2020-09-26 15:34:05 +03:00
gabime
c7613f3e91 Fixed #1680 2020-09-26 15:32:44 +03:00
gabime
05d5546eb1 Restore example 2020-09-26 15:30:45 +03:00
gabime
cefe67726e wip fix #1680 again 2020-09-26 15:06:53 +03:00
gabime
1ac2dcc537 wip fix #1680 again 2020-09-26 14:41:33 +03:00
gabime
3a68eecb28 Fix issue #1680 2020-09-25 18:19:50 +03:00
gabime
54a8259b42 Fix #1680 2020-09-25 15:03:13 +03:00
gabime
32b6f1619f Added tests for issue #1680 2020-09-25 14:44:01 +03:00
Gabi Melman
99b8c5d379 Update bin_to_hex.h 2020-09-24 10:04:07 +03:00
Gabi Melman
5deb7c55e1 Fix #1676 2020-09-21 03:31:19 +03:00
gabime
9cd25dd216 Remove un needed hasher declaration 2020-09-01 23:49:54 +03:00
gabime
4a9ccf7e38 Fixed chrono wrapper 2020-09-01 12:34:59 +03:00
gabime
2963b9f07f Updated comment 2020-09-01 12:24:11 +03:00
gabime
a4a9bc4d8e version 1.8.0 2020-09-01 12:05:23 +03:00
gabime
a16a029790 Added bundled fmt os.h file 2020-09-01 12:04:36 +03:00
Gabi Melman
97fea81599 Update README.md 2020-08-31 03:51:17 +03:00
gabime
ccffb6ecd6 Fix #1581 2020-08-30 20:44:42 +03:00
gabime
63b5a1a4d8 Fix #1581 2020-08-30 20:44:04 +03:00
gabime
dfc777803a enable the 'n' flag in for backward compatibility with fmt 6.x 2020-08-30 15:58:22 +03:00
gabime
934a9bb23e Fix issue #1617 2020-08-30 15:18:33 +03:00
gabime
7097f7a894 Updated stopwatch example to README.md 2020-08-29 03:50:48 +03:00
gabime
537fd7c4ba Added stopwatch example to README.md 2020-08-29 03:49:18 +03:00
gabime
d5048b8b0c Added stopwatch example to README.md 2020-08-29 03:48:24 +03:00
gabime
0348556aac Added stopwatch example to README.md 2020-08-29 03:46:29 +03:00
gabime
d6329b9dce Added some stopwatch tests 2020-08-29 03:25:16 +03:00
gabime
34f3d29d93 Added stopwatch support 2020-08-29 02:48:43 +03:00
gabime
cd701761f9 clang-format 2020-08-26 00:10:05 +03:00
gabime
23c2c00d69 Bumb fmt version to 7.0.3 2020-08-26 00:02:09 +03:00
Gabi Melman
fa501b46cf Merge pull request #1653 from gk6k6k/patch-1
Update README.md
2020-08-20 12:26:03 +03:00
gk6k6k
38dc0a5c5d Update README.md
Hi,
fedora yum is obsolite log time ago...
https://www.2daygeek.com/comparison-difference-between-dnf-vs-yum/

Works for fedora 31:

[root@Galleon ~]# uname -a
Linux Galleon 5.6.13-200.fc31.x86_64 #1 SMP Thu May 14 23:26:14 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
[root@Galleon ~]#
2020-08-20 11:01:49 +02:00
Gabi Melman
685cc4edbc Revert last commit 2020-08-20 00:00:54 +03:00
Gabi Melman
78369375e3 Update helpers-inl.h 2020-08-19 23:57:47 +03:00
Gabi Melman
6587058f74 Update argv.h 2020-08-19 23:56:56 +03:00
Gabi Melman
30f738e49a Merge pull request #1651 from bareya/make_load_env_levels_inline
Make load_env_levels inline function
2020-08-19 23:55:40 +03:00
Piotr Barejko
726ca01e5c Make load_env_levels inline function 2020-08-19 12:43:41 -07:00
Gabi Melman
83b40b8cda Merge pull request #1640 from dkruempe/v1.x
tcp_client.h bugfix for macOS and add tcp example
2020-08-06 13:06:08 +03:00
Dominik Krümpelmann
db0d0438ff tcp_client.h bugfix for macOS and add tcp example
-> fix the issue that the head is not compilable bc. of missing declaration
-> optimize if/else block in connection function. Else not needed bc. of break in if
2020-08-06 11:40:17 +02:00
Gabi Melman
3c527488e7 Update spdlog-inl.h 2020-07-24 13:06:29 +03:00
Gabi Melman
99abcf6ded Merge pull request #1628 from eyalroz/issue-1627
Fix for issue #1627: Global get_level() and should_log()
2020-07-24 00:31:51 +03:00
Eyal Rozenberg
7009727559 Fix for issue #1627:
* Added: `spdlog::get_level()` API function - like `logger::level()`, except for the name change
* Added: `spdlog::should_log()` API function - like `logger: should_log()`
2020-07-23 23:05:12 +03:00
Gabi Melman
ae02fba141 Merge pull request #1624 from tambry/cmake_range
Bump CMake requirement to 3.10, don't enable C language
2020-07-21 14:28:42 +03:00
Raul Tambre
76cdeb62e3 Don't enable C language in CMake
spdlog doesn't seem to actually require the C language.
Not enabling it results in a significant initial configure time speedup.
2020-07-21 11:39:40 +03:00
Raul Tambre
ae9627c64c Raise CMake requirement to 3.13
* Reduces the range of possible version we'd need to test with.
* Enables newer policies reducing possible deprecation warnings from new policies.
* Allows removing some code for compatibility with older versions.
* Coincides with LLVM's bump to requiring CMake 3.13.
2020-07-20 22:03:50 +03:00
Gabi Melman
58875bdcd7 Update tcp_client-windows.h 2020-07-14 23:15:49 +03:00
Gabi Melman
616caa5d30 Version 1.7.0 2020-07-09 20:24:20 +03:00
Gabi Melman
8236ee3ff6 Update version.h 2020-07-08 19:46:57 +03:00
Gabi Melman
19f2804661 Fix #1611 2020-07-08 18:50:51 +03:00
Gabi Melman
c62ba5f48d Merge pull request #1606 from candrews/fmt-7
fmt 7.0.0 renamed the internal namespace to detail.
2020-07-06 20:41:13 +03:00
Craig Andrews
22bee8128a fmt 7.0.0 renamed the internal namespace to detail.
See: https://github.com/fmtlib/fmt/issues/1538
2020-07-06 13:01:52 -04:00
Gabi Melman
39150eb8c7 Update README.md 2020-06-18 18:43:12 +03:00
Gabi Melman
1b14fa53ef Merge pull request #1591 from Pospelove/cmake-cleanup
Cleanup options-related lines in CMakeLists.txt
2020-06-12 15:29:57 +03:00
Leonid Pospelov
cf55e5d4f8 Cleanup options-related lines in CMakeLists.txt 2020-06-12 14:30:49 +03:00
Gabi Melman
1a1ea028f6 Merge pull request #1587 from podsvirov/increased-tolerance-waiting-time-for-mingw
Twice tolerance waiting time with MinGW
2020-06-10 23:47:19 +03:00
Gabi Melman
814c3445a3 Merge pull request #1586 from podsvirov/fix-shared-non-msvc
Fix shared library building failure on Windows with non MSVC
2020-06-10 23:46:46 +03:00
Konstantin Podsvirov
075dcee042 Twice tolerance waiting time with MinGW
Increased tolerance waiting time for MinGW in dequeue-empty-nowait test
case, because this test case not passed on slow and powerful computers.
2020-06-10 23:44:30 +03:00
Konstantin Podsvirov
fe97a03033 Fix shared library building failure on Windows with non MSVC
For example, now we can built shared library on Window with MinGW.

This changes improve features added in #1467.
2020-06-10 22:56:53 +03:00
Gabi Melman
f593aad786 Update .travis.yml 2020-06-08 18:03:55 +03:00
Gabi Melman
4a8c602a59 Update .travis.yml 2020-06-08 18:03:43 +03:00
Gabi Melman
7143dbc46a Update appveyor.yml 2020-06-08 03:29:58 +03:00
gabime
e69699e12c enable_if 2020-06-08 00:38:30 +03:00
gabime
d6dbdbf27a Revert 7f15fb2a21 since it breaks the ABI 2020-06-07 14:50:57 +03:00
gabime
a0dae55a69 Revert 7f15fb2a21 since it breaks the ABI 2020-06-07 14:38:16 +03:00
Gabi Melman
7f15fb2a21 Merge pull request #1580 from TamasFlorin/custom_rotating_filename
Add support for custom filename calculator in rotating_file_sink.
2020-06-06 22:25:52 +03:00
Tamas Florin
d5aa8db36f Add missing os include for rotating_file_sink. 2020-06-06 21:08:03 +03:00
gabime
357b6c9d8c Added FMT_STRING bench 2020-06-06 14:26:50 +03:00
gabime
b0c4794305 Use default pointer type in enable_if 2020-06-06 14:25:36 +03:00
Tamas Florin
071206ef59 Add support for custom filename calculator in rotating_file_sink. 2020-06-04 13:38:21 +03:00
Gabi Melman
63ab8e6341 Merge pull request #1574 from Tridacnid/tridacnid/FMT_STRING
Add support for FMT_STRING compile time checking.
2020-06-04 11:42:01 +03:00
Joe Burzinski
741b0d6e82 Address code review comments: remove perfect forwarding on FormatString template parameters. 2020-06-03 21:47:48 -05:00
Joe Burzinski
3041faffab Address code review comments: revert perfect forwarding on places that didn't need it, remove negative compilation unit test. 2020-06-02 20:30:25 -05:00
Joe Burzinski
30ee690401 Add support for FMT_STRING compile time checking. Add negative compilation unit test for compilers and c++ standard that support relaxed constexpr. 2020-05-31 13:15:40 -05:00
gabime
22a169bc31 Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x 2020-05-27 00:02:51 +03:00
Gabi Melman
ac19803d03 Merge pull request #1571 from IIFE/filename-deadlock
fix deadlock on filename() call. Thanks @IIFE
2020-05-27 00:01:58 +03:00
IIFEgit
95485ee89b keep filename as it's public 2020-05-26 21:59:57 +01:00
gabime
bc61f69058 Bump fmt version to 6.2.1 2020-05-26 23:47:57 +03:00
IIFEgit
0b86d6a451 fix deadlock on filename() call 2020-05-26 21:35:04 +01:00
gabime
0317731dc9 Fix #1569 2020-05-26 23:34:17 +03:00
Gabi Melman
3dedb52163 Merged again pr #1563 2020-05-20 22:50:35 +03:00
Gabi Melman
ad393b83a2 Revert 01dac453db 2020-05-20 22:39:25 +03:00
Gabi Melman
01dac453db Merge pull request #1563 from vekkuli/fix_ringbuffer_sink_last
Fixed ringbuffer_sink last_raw and last_formatted
2020-05-20 22:30:46 +03:00
Jaakko Rantala
03abdf49a0 Fixed ringbuffer_sink last_raw and last_formatted giving the first lim number of elements instead of last. 2020-05-20 19:05:24 +03:00
87 changed files with 6842 additions and 3530 deletions

View File

@@ -101,10 +101,10 @@ script:
-DCMAKE_CXX_STANDARD=$CPP \
-DSPDLOG_BUILD_EXAMPLE=ON \
-DSPDLOG_BUILD_EXAMPLE_HO=ON \
-DSPDLOG_ENABLE_WARNINGS=ON \
-DSPDLOG_BUILD_WARNINGS=ON \
-DSPDLOG_BUILD_BENCH=OFF \
-DSPDLOG_BUILD_TESTS=ON \
-DSPDLOG_BUILD_TESTS_HO=OFf \
-DSPDLOG_BUILD_TESTS_HO=OFF \
-DSPDLOG_SANITIZE_ADDRESS=$ASAN
- make VERBOSE=1 -j2

View File

@@ -1,14 +1,6 @@
# 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)
cmake_minimum_required(VERSION 3.10)
# ---------------------------------------------------------------------------------------
# Start spdlog project
@@ -38,9 +30,14 @@ if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
# make sure __cplusplus is defined when using msvc
if(MSVC)
string(APPEND CMAKE_CXX_FLAGS " /Zc:__cplusplus")
endif()
set(CMAKE_CXX_EXTENSIONS OFF)
if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN")
if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN" OR CMAKE_SYSTEM_NAME MATCHES "MSYS")
set(CMAKE_CXX_EXTENSIONS ON)
endif()
@@ -108,6 +105,7 @@ 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_DISABLE_DEFAULT_LOGGER "Disable default logger creation" OFF)
# clang-tidy
if(${CMAKE_VERSION} VERSION_GREATER "3.5")
@@ -131,16 +129,16 @@ if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
list(APPEND SPDLOG_SRCS src/fmt.cpp)
endif()
if(WIN32 AND SPDLOG_BUILD_SHARED)
list(APPEND SPDLOG_SRCS ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
endif()
if(SPDLOG_BUILD_SHARED)
if(SPDLOG_BUILD_SHARED OR BUILD_SHARED_LIBS)
if(WIN32)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY)
list(APPEND SPDLOG_SRCS ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
endif()
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)
if(MSVC)
target_compile_options(spdlog PUBLIC
$<$<AND:$<CXX_COMPILER_ID:MSVC>,$<NOT:$<COMPILE_LANGUAGE:CUDA>>>:/wd4251 /wd4275>)
endif()
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
target_compile_definitions(spdlog PRIVATE FMT_EXPORT PUBLIC FMT_SHARED)
@@ -180,7 +178,7 @@ target_link_libraries(spdlog_header_only INTERFACE Threads::Threads)
# ---------------------------------------------------------------------------------------
if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO)
if(NOT TARGET fmt::fmt)
find_package(fmt 5.3.0 REQUIRED)
find_package(fmt CONFIG REQUIRED)
endif()
target_compile_definitions(spdlog PUBLIC SPDLOG_FMT_EXTERNAL)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FMT_EXTERNAL)
@@ -197,52 +195,37 @@ if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO)
set(PKG_CONFIG_REQUIRES fmt) # add dependency to pkg-config
endif()
# ---------------------------------------------------------------------------------------
# Add required libraries for Android CMake build
# ---------------------------------------------------------------------------------------
if (ANDROID)
target_link_libraries(spdlog PUBLIC log)
target_link_libraries(spdlog_header_only INTERFACE log)
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()
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)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_EXCEPTIONS)
if(NOT MSVC)
target_compile_options(spdlog PRIVATE -fno-exceptions)
set(SPDLOG_WCHAR_TO_UTF8_SUPPORT ${SPDLOG_WCHAR_SUPPORT})
foreach(
SPDLOG_OPTION
SPDLOG_WCHAR_TO_UTF8_SUPPORT
SPDLOG_WCHAR_FILENAMES
SPDLOG_NO_EXCEPTIONS
SPDLOG_CLOCK_COARSE
SPDLOG_PREVENT_CHILD_FD
SPDLOG_NO_THREAD_ID
SPDLOG_NO_TLS
SPDLOG_NO_ATOMIC_LEVELS
SPDLOG_DISABLE_DEFAULT_LOGGER)
if(${SPDLOG_OPTION})
target_compile_definitions(spdlog PUBLIC ${SPDLOG_OPTION})
target_compile_definitions(spdlog_header_only INTERFACE ${SPDLOG_OPTION})
endif()
endif()
endforeach()
if(SPDLOG_CLOCK_COARSE)
target_compile_definitions(spdlog PRIVATE SPDLOG_CLOCK_COARSE)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_CLOCK_COARSE)
endif()
if(SPDLOG_PREVENT_CHILD_FD)
target_compile_definitions(spdlog PRIVATE SPDLOG_PREVENT_CHILD_FD)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_PREVENT_CHILD_FD)
endif()
if(SPDLOG_NO_THREAD_ID)
target_compile_definitions(spdlog PRIVATE SPDLOG_NO_THREAD_ID)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_THREAD_ID)
endif()
if(SPDLOG_NO_TLS)
target_compile_definitions(spdlog PRIVATE SPDLOG_NO_TLS)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_TLS)
endif()
if(SPDLOG_NO_ATOMIC_LEVELS)
target_compile_definitions(spdlog PUBLIC SPDLOG_NO_ATOMIC_LEVELS)
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_NO_ATOMIC_LEVELS)
if(SPDLOG_NO_EXCEPTIONS AND NOT MSVC)
target_compile_options(spdlog PRIVATE -fno-exceptions)
endif()
# ---------------------------------------------------------------------------------------
@@ -322,3 +305,4 @@ if(SPDLOG_INSTALL)
# ---------------------------------------------------------------------------------------
include(cmake/spdlogCPack.cmake)
endif()

View File

@@ -25,12 +25,14 @@ $ cmake .. && make -j
* Homebrew: `brew install spdlog`
* MacPorts: `sudo port install spdlog`
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
* Fedora: `yum install spdlog`
* Fedora: `dnf install spdlog`
* Gentoo: `emerge dev-libs/spdlog`
* Arch Linux: `pacman -S spdlog`
* vcpkg: `vcpkg install spdlog`
* conan: `spdlog/[>=1.4.1]`
* conda: `conda install -c conda-forge spdlog`
* build2: ```depends: spdlog ^1.8.2```
## Features
@@ -45,7 +47,8 @@ $ cmake .. && make -j
* Daily log files.
* Console logging (colors supported).
* syslog.
* Windows debugger (```OutputDebugString(..)```)
* Windows event log.
* Windows debugger (```OutputDebugString(..)```).
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
* 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.
@@ -56,7 +59,6 @@ $ cmake .. && make -j
#### Basic usage
```c++
#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"
int main()
{
@@ -79,10 +81,6 @@ int main()
// define SPDLOG_ACTIVE_LEVEL to desired level
SPDLOG_TRACE("Some trace message with param {}", 42);
SPDLOG_DEBUG("Some debug message");
// Set the default logger to file logger
auto file_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic.txt");
spdlog::set_default_logger(file_logger);
}
```
@@ -169,6 +167,20 @@ spdlog::flush_every(std::chrono::seconds(3));
```
---
#### Stopwatch
```c++
// Stopwatch support for spdlog
#include "spdlog/stopwatch.h"
void stopwatch_example()
{
spdlog::stopwatch sw;
spdlog::debug("Elapsed {}", sw);
spdlog::debug("Elapsed {:.3}", sw);
}
```
---
#### Log binary data in hex
```c++
@@ -179,6 +191,7 @@ spdlog::flush_every(std::chrono::seconds(3));
// {: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.
#include "spdlog/fmt/bin_to_hex.h"
@@ -418,6 +431,6 @@ Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1
---
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>
Thanks to [JetBrains](https://www.jetbrains.com/?from=spdlog) for donating product licenses to help develop **spdlog** <a href="https://www.jetbrains.com/?from=spdlog"><img src="logos/jetbrains-variant-4.svg" width="94" align="center" /></a>

View File

@@ -4,32 +4,52 @@ environment:
matrix:
- GENERATOR: '"Visual Studio 14 2015"'
BUILD_TYPE: Debug
WCHAR: 'OFF'
BUILD_SHARED: 'OFF'
WCHAR: 'OFF'
WCHAR_FILES: 'OFF'
BUILD_EXAMPLE: 'ON'
- GENERATOR: '"Visual Studio 14 2015"'
BUILD_TYPE: Release
WCHAR: 'ON'
BUILD_SHARED: 'OFF'
WCHAR: 'ON'
WCHAR_FILES: 'OFF'
BUILD_EXAMPLE: 'ON'
- GENERATOR: '"Visual Studio 14 2015 Win64"'
BUILD_TYPE: Debug
WCHAR: 'ON'
BUILD_SHARED: 'OFF'
WCHAR: 'ON'
WCHAR_FILES: 'OFF'
BUILD_EXAMPLE: 'ON'
- GENERATOR: '"Visual Studio 14 2015 Win64"'
BUILD_TYPE: Release
WCHAR: 'ON'
BUILD_SHARED: 'OFF'
WCHAR: 'ON'
WCHAR_FILES: 'OFF'
BUILD_EXAMPLE: 'ON'
- GENERATOR: '"Visual Studio 15 2017 Win64"'
BUILD_TYPE: Debug
BUILD_SHARED: 'OFF'
WCHAR: 'ON'
BUILD_SHARED: 'OFF'
WCHAR_FILES: 'OFF'
BUILD_EXAMPLE: 'ON'
- GENERATOR: '"Visual Studio 15 2017 Win64"'
BUILD_TYPE: Release
WCHAR: 'OFF'
BUILD_SHARED: 'OFF'
WCHAR: 'OFF'
WCHAR_FILES: 'OFF'
BUILD_EXAMPLE: 'ON'
- GENERATOR: '"Visual Studio 15 2017 Win64"'
BUILD_TYPE: Release
WCHAR: 'OFF'
BUILD_SHARED: 'ON'
WCHAR: 'OFF'
WCHAR_FILES: 'OFF'
BUILD_EXAMPLE: 'ON'
- GENERATOR: '"Visual Studio 15 2017 Win64"'
BUILD_TYPE: Release
BUILD_SHARED: 'ON'
WCHAR: 'ON'
WCHAR_FILES: 'ON'
BUILD_EXAMPLE: 'OFF'
build_script:
- cmd: >-
set
@@ -40,7 +60,7 @@ build_script:
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 -G %GENERATOR% -D CMAKE_BUILD_TYPE=%BUILD_TYPE% -D BUILD_SHARED_LIBS=%BUILD_SHARED% -D SPDLOG_WCHAR_SUPPORT=%WCHAR% -D SPDLOG_WCHAR_FILENAMES=%WCHAR_FILES% -D SPDLOG_BUILD_EXAMPLE=%BUILD_EXAMPLE% -D SPDLOG_BUILD_EXAMPLE_HO=%BUILD_EXAMPLE% -D SPDLOG_BUILD_TESTS=ON -D SPDLOG_BUILD_TESTS_HO=OFF -D SPDLOG_BUILD_WARNINGS=ON ..
cmake --build . --config %BUILD_TYPE%

View File

@@ -1,6 +1,6 @@
# 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.10)
project(spdlog_bench CXX)
if(NOT TARGET spdlog)
@@ -9,7 +9,24 @@ if(NOT TARGET spdlog)
endif()
find_package(Threads REQUIRED)
find_package(benchmark CONFIG REQUIRED)
find_package(benchmark CONFIG)
if (NOT benchmark_FOUND)
message(STATUS "Using CMake Version ${CMAKE_VERSION}")
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.11.0")
# User can fetch googlebenchmark
message(STATUS "Downloading GoogleBenchmark")
include(FetchContent)
set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE INTERNAL "")
# Do not build and run googlebenchmark tests
FetchContent_Declare(googlebenchmark
GIT_REPOSITORY https://github.com/google/benchmark.git
GIT_TAG v1.5.2)
FetchContent_MakeAvailable(googlebenchmark)
else()
message(FATAL_ERROR "GoogleBenchmark is missing. Use CMake >= 3.11 or download it")
endif()
endif()
add_executable(bench bench.cpp)
spdlog_enable_warnings(bench)

View File

@@ -9,7 +9,12 @@
#include "spdlog/spdlog.h"
#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#ifdef SPDLOG_FMT_EXTERNAL
#include <fmt/locale.h>
#else
#include "spdlog/fmt/bundled/locale.h"
#endif
#include "utils.h"
#include <atomic>
@@ -29,7 +34,7 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4996) // disable fopen warning under msvc
#endif // _MSC_VER
#endif // _MSC_VER
int count_lines(const char *filename)
{
@@ -48,14 +53,14 @@ int count_lines(const char *filename)
void verify_file(const char *filename, int expected_count)
{
spdlog::info("Verifying {} to contain {:n} line..", filename, expected_count);
spdlog::info("Verifying {} to contain {} line..", filename, expected_count);
auto count = count_lines(filename);
if (count != expected_count)
{
spdlog::error("Test failed. {} has {:n} lines instead of {:n}", filename, count, expected_count);
spdlog::error("Test failed. {} has {} lines instead of {}", filename, count, expected_count);
exit(1);
}
spdlog::info("Line count OK ({:n})\n", count);
spdlog::info("Line count OK ({})\n", count);
}
#ifdef _MSC_VER
@@ -98,11 +103,12 @@ int main(int argc, char *argv[])
auto slot_size = sizeof(spdlog::details::async_msg);
spdlog::info("-------------------------------------------------");
spdlog::info("Messages : {:n}", howmany);
spdlog::info("Threads : {:n}", threads);
spdlog::info("Queue : {:n} slots", queue_size);
spdlog::info("Queue memory : {:n} x {} = {:n} KB ", queue_size, slot_size, (queue_size * slot_size) / 1024);
spdlog::info("Total iters : {:n}", iters);
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Messages : {:L}", howmany));
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Threads : {:L}", threads));
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Queue : {:L} slots", queue_size));
spdlog::info(fmt::format(
std::locale("en_US.UTF-8"), "Queue memory : {:L} x {:L} = {:L} KB ", queue_size, slot_size, (queue_size * slot_size) / 1024));
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Total iters : {:L}", iters));
spdlog::info("-------------------------------------------------");
const char *filename = "logs/basic_async.log";
@@ -175,5 +181,5 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> logger, int thread_co
auto delta = high_resolution_clock::now() - start;
auto delta_d = duration_cast<duration<double>>(delta).count();
spdlog::info("Elapsed: {} secs\t {:n}/sec", delta_d, int(howmany / delta_d));
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Elapsed: {} secs\t {:L}/sec", delta_d, int(howmany / delta_d)));
}

View File

@@ -12,6 +12,12 @@
#include "spdlog/sinks/null_sink.h"
#include "spdlog/sinks/rotating_file_sink.h"
#ifdef SPDLOG_FMT_EXTERNAL
#include <fmt/locale.h>
#else
#include "spdlog/fmt/bundled/locale.h"
#endif
#include "utils.h"
#include <atomic>
#include <cstdlib> // EXIT_FAILURE
@@ -32,7 +38,7 @@ 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);
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Multi threaded: {:L} threads, {:L} messages", threads, iters));
spdlog::info("**************************************************************");
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
@@ -68,7 +74,7 @@ void bench_threaded_logging(size_t threads, int iters)
void bench_single_threaded(int iters)
{
spdlog::info("**************************************************************");
spdlog::info("Single threaded: {:n} messages", iters);
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Single threaded: {} messages", iters));
spdlog::info("**************************************************************");
auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true);
@@ -152,7 +158,8 @@ void bench(int howmany, std::shared_ptr<spdlog::logger> log)
auto delta = high_resolution_clock::now() - start;
auto delta_d = duration_cast<duration<double>>(delta).count();
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
spdlog::info(
fmt::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d)));
spdlog::drop(log->name());
}
@@ -182,7 +189,8 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, size_t thread_co
auto delta = high_resolution_clock::now() - start;
auto delta_d = duration_cast<duration<double>>(delta).count();
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
spdlog::info(
fmt::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d)));
spdlog::drop(log->name());
}
@@ -205,7 +213,7 @@ void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log)
auto delta_d = duration_cast<duration<double>>(delta).count();
spdlog::drop(log->name());
spdlog::set_default_logger(std::move(orig_default));
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16}/sec", log->name(), delta_d, int(howmany / delta_d));
}
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
@@ -232,7 +240,7 @@ void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
auto delta_d = duration_cast<duration<double>>(delta).count();
spdlog::drop(log->name());
spdlog::set_default_logger(std::move(orig_default));
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16}/sec", log->name(), delta_d, int(howmany / delta_d));
}
*/

View File

@@ -34,14 +34,14 @@ void bench_formatters()
for (auto &flag : all_flags)
{
auto pattern = std::string("%") + flag;
benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern);
// pattern = std::string("%16") + flag;
// benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
// benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern);
//
// // bench center padding
// pattern = std::string("%=16") + flag;
// benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
// benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern);
}
// complex patterns
@@ -52,7 +52,7 @@ void bench_formatters()
};
for (auto &pattern : patterns)
{
benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern)->Iterations(2500000);
benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern)->Iterations(2500000);
}
}
@@ -73,7 +73,7 @@ int main(int argc, char *argv[])
}
else
{
benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern);
}
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();

View File

@@ -39,6 +39,16 @@ void bench_logger(benchmark::State &state, std::shared_ptr<spdlog::logger> logge
}
}
void bench_logger_fmt_string(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
{
int i = 0;
for (auto _ : state)
{
logger->info(FMT_STRING("Hello logger: msg number {}..............."), ++i);
;
}
}
void bench_disabled_macro(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
{
int i = 0;
@@ -88,6 +98,7 @@ int main(int argc, char *argv[])
auto null_logger_st = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_st>());
benchmark::RegisterBenchmark("null_sink_st (500_bytes c_str)", bench_c_string, std::move(null_logger_st));
benchmark::RegisterBenchmark("null_sink_st", bench_logger, null_logger_st);
benchmark::RegisterBenchmark("null_sink_fmt_string", bench_logger_fmt_string, null_logger_st);
// with backtrace of 64
auto tracing_null_logger_st = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_st>());
tracing_null_logger_st->enable_backtrace(64);

View File

@@ -18,23 +18,37 @@ set(CPACK_PACKAGE_RELOCATABLE ON CACHE BOOL "Build relocatable package")
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries")
set(CPACK_DEBIAN_PACKAGE_SECTION "libs")
set(CPACK_RPM_PACKAGE_URL ${CPACK_PROJECT_URL})
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE ${CPACK_PROJECT_URL})
set(CPACK_RPM_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.")
set(CPACK_DEBIAN_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}")
set(CPACK_DEBIAN_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
else()
set(CPACK_RPM_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}")
set(CPACK_DEBIAN_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}")
set(CPACK_RPM_PACKAGE_NAME "${PROJECT_NAME}")
set(CPACK_DEBIAN_PACKAGE_NAME "${PROJECT_NAME}")
endif()
if(CPACK_RPM_PACKAGE_RELEASE)
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}-${CPACK_RPM_PACKAGE_RELEASE}")
endif()
if(CPACK_DEBIAN_PACKAGE_RELEASE)
set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}-${CPACK_DEBIAN_PACKAGE_RELEASE}")
endif()
if(CPACK_RPM_PACKAGE_ARCHITECTURE)
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.${CPACK_RPM_PACKAGE_ARCHITECTURE}")
endif()
if(CPACK_DEBIAN_PACKAGE_ARCHITECTURE)
set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
endif()
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.rpm")
set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.deb")
if(NOT CPACK_PACKAGE_RELOCATABLE)
# Depend on pkgconfig rpm to create the system pkgconfig folder

View File

@@ -59,3 +59,4 @@ function(spdlog_enable_sanitizer target_name)
target_compile_options(${target_name} PRIVATE -fno-omit-frame-pointer)
target_link_libraries(${target_name} PRIVATE -fsanitize=address,undefined -fuse-ld=gold)
endfunction()

View File

@@ -1,5 +1,5 @@
#define APSTUDIO_READONLY_SYMBOLS
#include "winres.h"
#include <windows.h>
#undef APSTUDIO_READONLY_SYMBOLS
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US

View File

@@ -1,6 +1,6 @@
# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.10)
project(spdlog_examples CXX)
if(NOT TARGET spdlog)

View File

@@ -13,6 +13,7 @@ void rotating_example();
void daily_example();
void async_example();
void binary_example();
void stopwatch_example();
void trace_example();
void multi_sink_example();
void user_defined_example();
@@ -21,7 +22,7 @@ void syslog_example();
void custom_flags_example();
#include "spdlog/spdlog.h"
#include "spdlog/cfg/env.h" // for loading levels from the environment variable
#include "spdlog/cfg/env.h" // support for loading levels from the environment variable
int main(int, char *[])
{
@@ -71,6 +72,7 @@ int main(int, char *[])
user_defined_example();
err_handler_example();
trace_example();
stopwatch_example();
custom_flags_example();
// Flush all *registered* loggers using a worker thread every 3 seconds.
@@ -192,6 +194,16 @@ void trace_example()
SPDLOG_LOGGER_TRACE(logger, "another trace message");
}
// stopwatch example
#include "spdlog/stopwatch.h"
#include <thread>
void stopwatch_example()
{
spdlog::stopwatch sw;
std::this_thread::sleep_for(std::chrono::milliseconds(123));
spdlog::info("Stopwatch: {} seconds", sw);
}
// A logger with multiple sinks (stdout and file) - each with a different format and log level.
void multi_sink_example()
{

View File

@@ -35,7 +35,7 @@ template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
struct async_factory_impl
{
template<typename Sink, typename... SinkArgs>
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&... args)
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&...args)
{
auto &registry_inst = details::registry::instance();
@@ -61,13 +61,13 @@ using async_factory = async_factory_impl<async_overflow_policy::block>;
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
template<typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&... sink_args)
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&...sink_args)
{
return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
}
template<typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&... sink_args)
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&...sink_args)
{
return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
}

View File

@@ -21,7 +21,7 @@ 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)
inline void load_argv_levels(int argc, const char **argv)
{
const std::string spdlog_level_prefix = "SPDLOG_LEVEL=";
for (int i = 1; i < argc; i++)
@@ -30,13 +30,12 @@ void load_argv_levels(int argc, const char **argv)
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));
helpers::load_levels(levels_string);
}
}
}
void load_argv_levels(int argc, char **argv)
inline void load_argv_levels(int argc, char **argv)
{
load_argv_levels(argc, const_cast<const char **>(argv));
}

View File

@@ -17,7 +17,7 @@
// export SPDLOG_LEVEL=debug
//
// turn off all logging except for logger1:
// export SPDLOG_LEVEL="off,logger1=debug"
// export SPDLOG_LEVEL="*=off,logger1=debug"
//
// turn off all logging except for logger1 and logger2:
@@ -25,11 +25,13 @@
namespace spdlog {
namespace cfg {
void load_env_levels()
inline 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));
if (!env_val.empty())
{
helpers::load_levels(env_val);
}
}
} // namespace cfg

View File

@@ -11,6 +11,7 @@
#include <spdlog/details/os.h>
#include <spdlog/details/registry.h>
#include <algorithm>
#include <string>
#include <utility>
#include <sstream>
@@ -78,24 +79,40 @@ inline std::unordered_map<std::string, std::string> extract_key_vals_(const std:
return rv;
}
SPDLOG_INLINE log_levels extract_levels(const std::string &input)
SPDLOG_INLINE void load_levels(const std::string &input)
{
if (input.empty() || input.size() > 512)
{
return;
}
auto key_vals = extract_key_vals_(input);
log_levels rv;
std::unordered_map<std::string, level::level_enum> levels;
level::level_enum global_level = level::info;
bool global_level_found = false;
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
// ignore unrecognized level names
if (level == level::off && level_name != "off")
{
level = level::info;
continue;
}
if (logger_name.empty()) // no logger name indicate global level
{
global_level_found = true;
global_level = level;
}
else
{
levels[logger_name] = level;
}
rv.set(logger_name, level);
}
return rv;
details::registry::instance().set_levels(std::move(levels), global_level_found ? &global_level : nullptr);
}
} // namespace helpers

View File

@@ -3,7 +3,8 @@
#pragma once
#include <spdlog/cfg/log_levels.h>
#include <spdlog/common.h>
#include <unordered_map>
namespace spdlog {
namespace cfg {
@@ -17,7 +18,7 @@ namespace helpers {
// 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);
SPDLOG_API void load_levels(const std::string &txt);
} // namespace helpers
} // namespace cfg

View File

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

@@ -9,15 +9,21 @@
namespace spdlog {
namespace level {
static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES;
SPDLOG_INLINE string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
SPDLOG_INLINE const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
{
return level_string_views[l];
}
SPDLOG_INLINE void set_string_view(spdlog::level::level_enum l, const string_view_t &s) SPDLOG_NOEXCEPT
{
level_string_views[l] = s;
}
SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
{
return short_level_names[l];

View File

@@ -88,7 +88,9 @@ class sink;
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
using filename_t = std::wstring;
#define SPDLOG_FILENAME_T(s) L##s
// allow macro expansion to occur in SPDLOG_FILENAME_T
#define SPDLOG_FILENAME_T_INNER(s) L##s
#define SPDLOG_FILENAME_T(s) SPDLOG_FILENAME_T_INNER(s)
#else
using filename_t = std::string;
#define SPDLOG_FILENAME_T(s) s
@@ -101,6 +103,7 @@ using err_handler = std::function<void(const std::string &err_msg)>;
using string_view_t = fmt::basic_string_view<char>;
using wstring_view_t = fmt::basic_string_view<wchar_t>;
using memory_buf_t = fmt::basic_memory_buffer<char, 250>;
using wmemory_buf_t = fmt::basic_memory_buffer<wchar_t, 250>;
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
#ifndef _WIN32
@@ -163,11 +166,11 @@ enum level_enum
}
#endif
SPDLOG_API string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
SPDLOG_API const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
SPDLOG_API void set_string_view(spdlog::level::level_enum l, const string_view_t &s) 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
//
@@ -204,8 +207,8 @@ 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);
[[noreturn]] SPDLOG_API void throw_spdlog_ex(const std::string &msg, int last_errno);
[[noreturn]] SPDLOG_API void throw_spdlog_ex(std::string msg);
struct source_loc
{
@@ -232,7 +235,7 @@ namespace details {
using std::make_unique;
#else
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args &&... args)
std::unique_ptr<T> make_unique(Args &&...args)
{
static_assert(!std::is_array<T>::value, "arrays not supported");
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));

View File

@@ -29,12 +29,27 @@ SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate)
{
close();
filename_ = fname;
auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
auto *mode = SPDLOG_FILENAME_T("ab");
auto *trunc_mode = SPDLOG_FILENAME_T("wb");
for (int tries = 0; tries < open_tries_; ++tries)
{
// create containing folder if not exists already.
os::create_dir(os::dir_name(fname));
if (truncate)
{
// Truncate by opening-and-closing a tmp file in "wb" mode, always
// opening the actual log-we-write-to in "ab" mode, since that
// interacts more politely with eternal processes that might
// rotate/truncate the file underneath us.
std::FILE *tmp;
if (os::fopen_s(&tmp, fname, trunc_mode))
{
continue;
}
std::fclose(tmp);
}
if (!os::fopen_s(&fd_, fname, mode))
{
return;
@@ -118,7 +133,7 @@ SPDLOG_INLINE std::tuple<filename_t, filename_t> file_helper::split_by_extension
}
// treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
auto folder_index = fname.rfind(details::os::folder_sep);
auto folder_index = fname.find_last_of(details::os::folder_seps_filename);
if (folder_index != filename_t::npos && folder_index >= ext_index - 1)
{
return std::make_tuple(fname, filename_t());

View File

@@ -34,7 +34,15 @@ template<typename T>
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 int>(fmt::internal::count_digits(static_cast<count_type>(n)));
return static_cast<unsigned int>(fmt::
// fmt 7.0.0 renamed the internal namespace to detail.
// See: https://github.com/fmtlib/fmt/issues/1538
#if FMT_VERSION < 70000
internal
#else
detail
#endif
::count_digits(static_cast<count_type>(n)));
}
inline void pad2(int n, memory_buf_t &dest)

View File

@@ -26,7 +26,9 @@ SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other)
update_string_views();
}
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT : log_msg{other}, buffer{std::move(other.buffer)}
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT
: log_msg{other}
, buffer{std::move(other.buffer)}
{
update_string_views();
}

View File

@@ -110,6 +110,12 @@ public:
return q_.overrun_counter();
}
size_t size()
{
std::unique_lock<std::mutex> lock(queue_mutex_);
return q_.size();
}
private:
std::mutex queue_mutex_;
std::condition_variable push_cv_;

View File

@@ -397,17 +397,26 @@ SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
#ifdef _WIN32
return true;
#else
static constexpr std::array<const char *, 14> terms = {
{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"}};
const char *env_p = std::getenv("TERM");
if (env_p == nullptr)
{
return false;
}
static const bool result = []() {
const char *env_colorterm_p = std::getenv("COLORTERM");
if (env_colorterm_p != nullptr)
{
return true;
}
static constexpr std::array<const char *, 15> terms = {{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux",
"msys", "putty", "rxvt", "screen", "vt100", "xterm", "alacritty"}};
const char *env_term_p = std::getenv("TERM");
if (env_term_p == nullptr)
{
return false;
}
return std::any_of(terms.begin(), terms.end(), [&](const char *term) { return std::strstr(env_term_p, term) != nullptr; });
}();
static const bool result =
std::any_of(terms.begin(), terms.end(), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
return result;
#endif
}
@@ -427,7 +436,7 @@ SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
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)()))
if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) / 2 - 1)
{
throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
}
@@ -459,6 +468,41 @@ SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
throw_spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
}
SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target)
{
if (str.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) - 1)
{
throw_spdlog_ex("UTF-8 string is too big to be converted to UTF-16");
}
int str_size = static_cast<int>(str.size());
if (str_size == 0)
{
target.resize(0);
return;
}
int result_size = static_cast<int>(target.capacity());
if (str_size + 1 > result_size)
{
result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, NULL, 0);
}
if (result_size > 0)
{
target.resize(result_size);
result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, target.data(), result_size);
if (result_size > 0)
{
target.resize(result_size);
return;
}
}
throw_spdlog_ex(fmt::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError()));
}
#endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
// return true on success
@@ -489,15 +533,10 @@ SPDLOG_INLINE bool create_dir(filename_t path)
return false;
}
#ifdef _WIN32
// support forward slash in windows
std::replace(path.begin(), path.end(), '/', folder_sep);
#endif
size_t search_offset = 0;
do
{
auto token_pos = path.find(folder_sep, search_offset);
auto token_pos = path.find_first_of(folder_seps_filename, search_offset);
// treat the entire path as a folder if no folder separator not found
if (token_pos == filename_t::npos)
{
@@ -523,11 +562,7 @@ SPDLOG_INLINE bool create_dir(filename_t path)
// "abc///" => "abc//"
SPDLOG_INLINE filename_t dir_name(filename_t path)
{
#ifdef _WIN32
// support forward slash in windows
std::replace(path.begin(), path.end(), '/', folder_sep);
#endif
auto pos = path.find_last_of(folder_sep);
auto pos = path.find_last_of(folder_seps_filename);
return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
}

View File

@@ -32,11 +32,16 @@ SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT;
SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
// folder separator
#if !defined(SPDLOG_FOLDER_SEPS)
#ifdef _WIN32
static const char folder_sep = '\\';
#define SPDLOG_FOLDER_SEPS "\\/"
#else
SPDLOG_CONSTEXPR static const char folder_sep = '/';
#define SPDLOG_FOLDER_SEPS "/"
#endif
#endif
SPDLOG_CONSTEXPR static const char folder_seps[] = SPDLOG_FOLDER_SEPS;
SPDLOG_CONSTEXPR static const filename_t::value_type folder_seps_filename[] = SPDLOG_FILENAME_T(SPDLOG_FOLDER_SEPS);
// fopen_s on non windows for writing
SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
@@ -84,7 +89,9 @@ SPDLOG_API bool is_color_terminal() 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);
SPDLOG_API void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
SPDLOG_API void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target);
#endif
// Return directory name from given path or empty string

View File

@@ -67,7 +67,11 @@ SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr<logger> new_logge
new_logger->set_error_handler(err_handler_);
}
new_logger->set_level(levels_.get(new_logger->name()));
// set new level according to previously configured level or default level
auto it = log_levels_.find(new_logger->name());
auto new_level = it != log_levels_.end() ? it->second : global_log_level_;
new_logger->set_level(new_level);
new_logger->flush_on(flush_level_);
if (backtrace_n_messages_ > 0)
@@ -171,7 +175,7 @@ SPDLOG_INLINE void registry::set_level(level::level_enum log_level)
{
l.second->set_level(log_level);
}
levels_.set_default(log_level);
global_log_level_ = log_level;
}
SPDLOG_INLINE void registry::flush_on(level::level_enum log_level)
@@ -263,14 +267,24 @@ SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_registrat
automatic_registration_ = automatic_registration;
}
SPDLOG_INLINE void registry::update_levels(cfg::log_levels levels)
SPDLOG_INLINE void registry::set_levels(log_levels levels, level::level_enum *global_level)
{
std::lock_guard<std::mutex> lock(logger_map_mutex_);
levels_ = std::move(levels);
for (auto &l : loggers_)
log_levels_ = std::move(levels);
auto global_level_requested = global_level != nullptr;
global_log_level_ = global_level_requested ? *global_level : global_log_level_;
for (auto &logger : loggers_)
{
auto &logger = l.second;
logger->set_level(levels_.get(logger->name()));
auto logger_entry = log_levels_.find(logger.first);
if (logger_entry != log_levels_.end())
{
logger.second->set_level(logger_entry->second);
}
else if (global_level_requested)
{
logger.second->set_level(*global_level);
}
}
}

View File

@@ -9,7 +9,6 @@
// This class is thread safe
#include <spdlog/common.h>
#include <spdlog/cfg/log_levels.h>
#include <chrono>
#include <functional>
@@ -28,6 +27,7 @@ class periodic_worker;
class SPDLOG_API registry
{
public:
using log_levels = std::unordered_map<std::string, level::level_enum>;
registry(const registry &) = delete;
registry &operator=(const registry &) = delete;
@@ -80,7 +80,8 @@ public:
void set_automatic_registration(bool automatic_registration);
void update_levels(cfg::log_levels levels);
// set levels for all existing/future loggers. global_level can be null if should not set.
void set_levels(log_levels levels, level::level_enum *global_level);
static registry &instance();
@@ -90,13 +91,15 @@ private:
void throw_if_exists_(const std::string &logger_name);
void register_logger_(std::shared_ptr<logger> new_logger);
bool set_level_from_cfg_(logger *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_;
log_levels log_levels_;
std::unique_ptr<formatter> formatter_;
spdlog::level::level_enum global_log_level_ = level::info;
level::level_enum flush_level_ = level::off;
void (*err_handler_)(const std::string &msg);
void (*err_handler_)(const std::string &msg) = nullptr;
std::shared_ptr<thread_pool> tp_;
std::unique_ptr<periodic_worker> periodic_flusher_;
std::shared_ptr<logger> default_logger_;

View File

@@ -13,7 +13,7 @@ class logger;
struct synchronous_factory
{
template<typename Sink, typename... SinkArgs>
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... args)
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&...args)
{
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
auto new_logger = std::make_shared<spdlog::logger>(std::move(logger_name), std::move(sink));

View File

@@ -8,8 +8,8 @@
#include <spdlog/common.h>
#include <spdlog/details/os.h>
#include <windows.h>
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>

View File

@@ -75,7 +75,11 @@ public:
int last_errno = 0;
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next)
{
int const flags = SOCK_CLOEXEC;
#if defined(SOCK_CLOEXEC)
const int flags = SOCK_CLOEXEC;
#else
const int flags = 0;
#endif
socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
if (socket_ == -1)
{
@@ -87,12 +91,9 @@ public:
{
break;
}
else
{
last_errno = errno;
::close(socket_);
socket_ = -1;
}
last_errno = errno;
::close(socket_);
socket_ = -1;
}
::freeaddrinfo(addrinfo_result);
if (socket_ == -1)

View File

@@ -68,6 +68,11 @@ size_t SPDLOG_INLINE thread_pool::overrun_counter()
return q_.overrun_counter();
}
size_t SPDLOG_INLINE thread_pool::queue_size()
{
return q_.size();
}
void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
{
if (overflow_policy == async_overflow_policy::block)

View File

@@ -97,6 +97,7 @@ public:
void post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy);
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
size_t overrun_counter();
size_t queue_size();
private:
q_type q_;

View File

@@ -9,7 +9,7 @@
//
// Support for logging binary data as hex
// format flags:
// format flags, any combination of the followng:
// {:X} - print in uppercase.
// {:s} - don't separate each byte with space.
// {:p} - don't print the position on each line start.
@@ -92,7 +92,7 @@ struct formatter<spdlog::details::dump_info<T>>
auto parse(ParseContext &ctx) -> decltype(ctx.begin())
{
auto it = ctx.begin();
while (*it && *it != '}')
while (it != ctx.end() && *it != '}')
{
switch (*it)
{

View File

@@ -48,7 +48,7 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
// From fits in To without any problem.
} else {
// From does not always fit in To, resort to a dynamic check.
if (from < T::min() || from > T::max()) {
if (from < (T::min)() || from > (T::max)()) {
// outside range.
ec = 1;
return {};
@@ -72,43 +72,27 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
static_assert(F::is_integer, "From must be integral");
static_assert(T::is_integer, "To must be integral");
if (F::is_signed && !T::is_signed) {
if (detail::const_check(F::is_signed && !T::is_signed)) {
// From may be negative, not allowed!
if (fmt::internal::is_negative(from)) {
if (fmt::detail::is_negative(from)) {
ec = 1;
return {};
}
// From is positive. Can it always fit in To?
if (F::digits <= T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
ec = 1;
return {};
}
if (F::digits > T::digits &&
from > static_cast<From>(detail::max_value<To>())) {
ec = 1;
return {};
}
}
if (!F::is_signed && T::is_signed) {
// can from be held in To?
if (F::digits < T::digits) {
// yes, From always fits in To.
} else {
// from may not fit in To, we have to do a dynamic check
if (from > static_cast<From>(T::max())) {
// outside range.
ec = 1;
return {};
}
}
if (!F::is_signed && T::is_signed && F::digits >= T::digits &&
from > static_cast<From>(detail::max_value<To>())) {
ec = 1;
return {};
}
// reaching here means all is ok for lossless conversion.
return static_cast<To>(from);
} // function
return static_cast<To>(from); // Lossless conversion.
}
template <typename To, typename From,
FMT_ENABLE_IF(std::is_same<From, To>::value)>
@@ -141,7 +125,7 @@ FMT_CONSTEXPR To safe_float_conversion(const From from, int& ec) {
// catch the only happy case
if (std::isfinite(from)) {
if (from >= T::lowest() && from <= T::max()) {
if (from >= T::lowest() && from <= (T::max)()) {
return static_cast<To>(from);
}
// not within range.
@@ -190,17 +174,16 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
// safe conversion to IntermediateRep
IntermediateRep count =
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
if (ec) {
return {};
}
if (ec) return {};
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
const auto max1 = internal::max_value<IntermediateRep>() / Factor::num;
if (detail::const_check(Factor::num != 1)) {
const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
if (count > max1) {
ec = 1;
return {};
}
const auto min1 = std::numeric_limits<IntermediateRep>::min() / Factor::num;
const auto min1 =
(std::numeric_limits<IntermediateRep>::min)() / Factor::num;
if (count < min1) {
ec = 1;
return {};
@@ -208,17 +191,9 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
count *= Factor::num;
}
// this can't go wrong, right? den>0 is checked earlier.
if (Factor::den != 1) {
count /= Factor::den;
}
// convert to the to type, safely
using ToRep = typename To::rep;
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
if (ec) {
return {};
}
return To{tocount};
if (detail::const_check(Factor::den != 1)) count /= Factor::den;
auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
return ec ? To() : To(tocount);
}
/**
@@ -269,7 +244,7 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
// multiply with Factor::num without overflow or underflow
if (Factor::num != 1) {
constexpr auto max1 = internal::max_value<IntermediateRep>() /
constexpr auto max1 = detail::max_value<IntermediateRep>() /
static_cast<IntermediateRep>(Factor::num);
if (count > max1) {
ec = 1;
@@ -306,12 +281,12 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
// Usage: f FMT_NOMACRO()
#define FMT_NOMACRO
namespace internal {
namespace detail {
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); }
} // namespace internal
} // namespace detail
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) {
@@ -322,22 +297,22 @@ inline std::tm localtime(std::time_t time) {
dispatcher(std::time_t t) : time_(t) {}
bool run() {
using namespace fmt::internal;
using namespace fmt::detail;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm* tm) { return tm != nullptr; }
bool handle(internal::null<>) {
using namespace fmt::internal;
bool handle(detail::null<>) {
using namespace fmt::detail;
return fallback(localtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER
bool fallback(internal::null<>) {
using namespace fmt::internal;
bool fallback(detail::null<>) {
using namespace fmt::detail;
std::tm* tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
@@ -350,6 +325,11 @@ inline std::tm localtime(std::time_t time) {
return lt.tm_;
}
inline std::tm localtime(
std::chrono::time_point<std::chrono::system_clock> time_point) {
return localtime(std::chrono::system_clock::to_time_t(time_point));
}
// Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time) {
struct dispatcher {
@@ -359,21 +339,21 @@ inline std::tm gmtime(std::time_t time) {
dispatcher(std::time_t t) : time_(t) {}
bool run() {
using namespace fmt::internal;
using namespace fmt::detail;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm* tm) { return tm != nullptr; }
bool handle(internal::null<>) {
using namespace fmt::internal;
bool handle(detail::null<>) {
using namespace fmt::detail;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER
bool fallback(internal::null<>) {
bool fallback(detail::null<>) {
std::tm* tm = std::gmtime(&time_);
if (tm) tm_ = *tm;
return tm != nullptr;
@@ -386,17 +366,33 @@ inline std::tm gmtime(std::time_t time) {
return gt.tm_;
}
namespace internal {
inline std::size_t strftime(char* str, std::size_t count, const char* format,
const std::tm* time) {
inline std::tm gmtime(
std::chrono::time_point<std::chrono::system_clock> time_point) {
return gmtime(std::chrono::system_clock::to_time_t(time_point));
}
namespace detail {
inline size_t strftime(char* str, size_t count, const char* format,
const std::tm* time) {
return std::strftime(str, count, format, time);
}
inline std::size_t strftime(wchar_t* str, std::size_t count,
const wchar_t* format, const std::tm* time) {
inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
const std::tm* time) {
return std::wcsftime(str, count, format, time);
}
} // namespace internal
} // namespace detail
template <typename Char>
struct formatter<std::chrono::time_point<std::chrono::system_clock>, Char>
: formatter<std::tm, Char> {
template <typename FormatContext>
auto format(std::chrono::time_point<std::chrono::system_clock> val,
FormatContext& ctx) -> decltype(ctx.out()) {
std::tm time = localtime(val);
return formatter<std::tm, Char>::format(time, ctx);
}
};
template <typename Char> struct formatter<std::tm, Char> {
template <typename ParseContext>
@@ -405,7 +401,7 @@ template <typename Char> struct formatter<std::tm, Char> {
if (it != ctx.end() && *it == ':') ++it;
auto end = it;
while (end != ctx.end() && *end != '}') ++end;
tm_format.reserve(internal::to_unsigned(end - it + 1));
tm_format.reserve(detail::to_unsigned(end - it + 1));
tm_format.append(it, end);
tm_format.push_back('\0');
return end;
@@ -414,11 +410,10 @@ template <typename Char> struct formatter<std::tm, Char> {
template <typename FormatContext>
auto format(const std::tm& tm, FormatContext& ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buf;
std::size_t start = buf.size();
size_t start = buf.size();
for (;;) {
std::size_t size = buf.capacity() - start;
std::size_t count =
internal::strftime(&buf[start], size, &tm_format[0], &tm);
size_t size = buf.capacity() - start;
size_t count = detail::strftime(&buf[start], size, &tm_format[0], &tm);
if (count != 0) {
buf.resize(start + count);
break;
@@ -430,7 +425,7 @@ template <typename Char> struct formatter<std::tm, Char> {
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
const size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
return std::copy(buf.begin(), buf.end(), ctx.out());
@@ -439,7 +434,7 @@ template <typename Char> struct formatter<std::tm, Char> {
basic_memory_buffer<Char> tm_format;
};
namespace internal {
namespace detail {
template <typename Period> FMT_CONSTEXPR const char* get_units() {
return nullptr;
}
@@ -768,19 +763,25 @@ OutputIt format_duration_value(OutputIt out, Rep val, int precision) {
return format_to(out, std::is_floating_point<Rep>::value ? fp_f : format,
val);
}
template <typename Char, typename OutputIt>
OutputIt copy_unit(string_view unit, OutputIt out, Char) {
return std::copy(unit.begin(), unit.end(), out);
}
template <typename OutputIt>
OutputIt copy_unit(string_view unit, OutputIt out, wchar_t) {
// This works when wchar_t is UTF-32 because units only contain characters
// that have the same representation in UTF-16 and UTF-32.
utf8_to_utf16 u(unit);
return std::copy(u.c_str(), u.c_str() + u.size(), out);
}
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);
}
if (const char* unit = get_units<Period>())
return copy_unit(string_view(unit), out, Char());
const Char num_f[] = {'[', '{', '}', ']', 's', 0};
if (Period::den == 1) return format_to(out, num_f, Period::num);
if (const_check(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);
}
@@ -874,9 +875,9 @@ struct chrono_formatter {
if (isnan(value)) return write_nan();
uint32_or_64_or_128_t<int> n =
to_unsigned(to_nonnegative_int(value, max_value<int>()));
int num_digits = internal::count_digits(n);
int num_digits = detail::count_digits(n);
if (width > num_digits) out = std::fill_n(out, width - num_digits, '0');
out = format_decimal<char_type>(out, n, num_digits);
out = format_decimal<char_type>(out, n, num_digits).end;
}
void write_nan() { std::copy_n("nan", 3, out); }
@@ -1004,14 +1005,14 @@ struct chrono_formatter {
out = format_duration_unit<char_type, Period>(out);
}
};
} // namespace internal
} // namespace detail
template <typename Rep, typename Period, typename Char>
struct formatter<std::chrono::duration<Rep, Period>, Char> {
private:
basic_format_specs<Char> specs;
int precision;
using arg_ref_type = internal::arg_ref<Char>;
using arg_ref_type = detail::arg_ref<Char>;
arg_ref_type width_ref;
arg_ref_type precision_ref;
mutable basic_string_view<Char> format_str;
@@ -1032,7 +1033,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
return arg_ref_type(arg_id);
}
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
FMT_CONSTEXPR arg_ref_type make_arg_ref(detail::auto_id) {
return arg_ref_type(context.next_arg_id());
}
@@ -1062,17 +1063,17 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
auto begin = ctx.begin(), end = ctx.end();
if (begin == end || *begin == '}') return {begin, begin};
spec_handler handler{*this, ctx, format_str};
begin = internal::parse_align(begin, end, handler);
begin = detail::parse_align(begin, end, handler);
if (begin == end) return {begin, begin};
begin = internal::parse_width(begin, end, handler);
begin = detail::parse_width(begin, end, handler);
if (begin == end) return {begin, begin};
if (*begin == '.') {
if (std::is_floating_point<Rep>::value)
begin = internal::parse_precision(begin, end, handler);
begin = detail::parse_precision(begin, end, handler);
else
handler.on_error("precision not allowed for this argument type");
}
end = parse_chrono_format(begin, end, internal::chrono_format_checker());
end = parse_chrono_format(begin, end, detail::chrono_format_checker());
return {begin, end};
}
@@ -1083,7 +1084,7 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
-> decltype(ctx.begin()) {
auto range = do_parse(ctx);
format_str = basic_string_view<Char>(
&*range.begin, internal::to_unsigned(range.end - range.begin));
&*range.begin, detail::to_unsigned(range.end - range.begin));
return range.end;
}
@@ -1094,23 +1095,21 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
// is not specified.
basic_memory_buffer<Char> buf;
auto out = std::back_inserter(buf);
using range = internal::output_range<decltype(ctx.out()), Char>;
internal::basic_writer<range> w(range(ctx.out()));
internal::handle_dynamic_spec<internal::width_checker>(specs.width,
width_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>(
precision, precision_ref, ctx);
detail::handle_dynamic_spec<detail::width_checker>(specs.width, width_ref,
ctx);
detail::handle_dynamic_spec<detail::precision_checker>(precision,
precision_ref, ctx);
if (begin == end || *begin == '}') {
out = internal::format_duration_value<Char>(out, d.count(), precision);
internal::format_duration_unit<Char, Period>(out);
out = detail::format_duration_value<Char>(out, d.count(), precision);
detail::format_duration_unit<Char, Period>(out);
} else {
internal::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
detail::chrono_formatter<FormatContext, decltype(out), Rep, Period> f(
ctx, out, d);
f.precision = precision;
parse_chrono_format(begin, end, f);
}
w.write(buf.data(), buf.size(), specs);
return w.out();
return detail::write(
ctx.out(), basic_string_view<Char>(buf.data(), buf.size()), specs);
}
};

View File

@@ -198,7 +198,7 @@ struct rgb {
uint8_t b;
};
namespace internal {
namespace detail {
// color is a struct of either a rgb color or a terminal color.
struct color_type {
@@ -221,7 +221,7 @@ struct color_type {
uint32_t rgb_color;
} value;
};
} // namespace internal
} // namespace detail
// Experimental text formatting support.
class text_style {
@@ -298,11 +298,11 @@ class text_style {
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT {
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT {
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
@@ -313,7 +313,7 @@ class text_style {
private:
FMT_CONSTEXPR text_style(bool is_foreground,
internal::color_type text_color) FMT_NOEXCEPT
detail::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems() {
@@ -326,23 +326,23 @@ class text_style {
}
}
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
FMT_NOEXCEPT;
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
FMT_NOEXCEPT;
internal::color_type foreground_color;
internal::color_type background_color;
detail::color_type foreground_color;
detail::color_type background_color;
bool set_foreground_color;
bool set_background_color;
emphasis ems;
};
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/true, foreground);
}
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/false, background);
}
@@ -350,21 +350,21 @@ FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs;
}
namespace internal {
namespace detail {
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
const char* esc) FMT_NOEXCEPT {
// If we have a terminal color, we need to output another escape code
// sequence.
if (!text_color.is_rgb) {
bool is_background = esc == internal::data::background_color;
bool is_background = esc == detail::data::background_color;
uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with
// 10 more.
if (is_background) value += 10u;
std::size_t index = 0;
size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
@@ -398,7 +398,7 @@ template <typename Char> struct ansi_color_escape {
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
em_codes[3] = 9;
std::size_t index = 0;
size_t index = 0;
for (int i = 0; i < 4; ++i) {
if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b');
@@ -429,14 +429,14 @@ template <typename Char> struct ansi_color_escape {
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
internal::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, internal::data::foreground_color);
detail::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, detail::data::foreground_color);
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
internal::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, internal::data::background_color);
detail::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, detail::data::background_color);
}
template <typename Char>
@@ -455,71 +455,71 @@ inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
}
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::reset_color, stream);
fputs(detail::data::reset_color, stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
fputs(internal::data::wreset_color, stream);
fputs(detail::data::wreset_color, stream);
}
template <typename Char>
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
const char* begin = data::reset_color;
const char* end = begin + sizeof(data::reset_color) - 1;
buffer.append(begin, end);
}
template <typename Char>
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<Char>> args) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
auto emphasis = internal::make_emphasis<Char>(ts.get_emphasis());
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
has_style = true;
auto foreground =
internal::make_foreground_color<Char>(ts.get_foreground());
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
has_style = true;
auto background =
internal::make_background_color<Char>(ts.get_background());
auto background = detail::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
internal::vformat_to(buf, format_str, args);
if (has_style) internal::reset_color<Char>(buf);
detail::vformat_to(buf, format_str, args);
if (has_style) detail::reset_color<Char>(buf);
}
} // namespace internal
} // namespace detail
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<Char>> args) {
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
internal::vformat_to(buf, ts, to_string_view(format), args);
detail::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0));
internal::fputs(buf.data(), f);
detail::fputs(buf.data(), f);
}
/**
\rst
Formats a string and prints it to the specified file stream using ANSI
escape sequences to specify text formatting.
Example:
**Example**::
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
internal::check_format_string<Args...>(format_str);
using context = buffer_context<char_t<S>>;
format_arg_store<context, Args...> as{args...};
vprint(f, ts, format_str, basic_format_args<context>(as));
vprint(f, ts, format_str,
fmt::make_args_checked<Args...>(format_str, args...));
}
/**
@@ -530,7 +530,7 @@ void print(std::FILE* f, const text_style& ts, const S& format_str,
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...);
}
@@ -540,7 +540,7 @@ inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
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);
detail::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf);
}
@@ -560,7 +560,42 @@ 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...));
fmt::make_args_checked<Args...>(format_str, args...));
}
/**
Formats a string with the given text_style and writes the output to ``out``.
*/
template <typename OutputIt, typename Char,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
OutputIt vformat_to(
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf);
}
/**
\rst
Formats arguments with the given text_style, writes the result to the output
iterator ``out`` and returns the iterator past the end of the output range.
**Example**::
std::vector<char> out;
fmt::format_to(std::back_inserter(out),
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
\endrst
*/
template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
detail::is_string<S>::value>
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@@ -13,7 +13,33 @@
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
namespace detail {
// A compile-time string which is compiled into fast formatting code.
class compiled_string {};
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
/**
\rst
Converts a string literal *s* into a format string that will be parsed at
compile time and converted into efficient formatting code. Requires C++17
``constexpr if`` compiler support.
**Example**::
// Converts 42 into std::string using the most efficient method and no
// runtime format string processing.
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst
*/
#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string)
template <typename T, typename... Tail>
const T& first(const T& value, const Tail&...) {
return value;
}
// Part of a compiled format string. It can be either literal text or a
// replacement field.
@@ -62,13 +88,15 @@ template <typename Char> struct part_counter {
if (begin != end) ++num_parts;
}
FMT_CONSTEXPR void on_arg_id() { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(int) { ++num_parts; }
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char>) { ++num_parts; }
FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; }
FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; }
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
return ++num_parts, 0;
}
FMT_CONSTEXPR void on_replacement_field(const Char*) {}
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) {
// Find the matching brace.
unsigned brace_counter = 0;
@@ -116,25 +144,28 @@ class format_string_compiler : public error_handler {
handler_(part::make_text({begin, to_unsigned(end - begin)}));
}
FMT_CONSTEXPR void on_arg_id() {
FMT_CONSTEXPR int on_arg_id() {
part_ = part::make_arg_index(parse_context_.next_arg_id());
return 0;
}
FMT_CONSTEXPR void on_arg_id(int id) {
FMT_CONSTEXPR int on_arg_id(int id) {
parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id);
return 0;
}
FMT_CONSTEXPR void on_arg_id(basic_string_view<Char> id) {
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) {
part_ = part::make_arg_name(id);
return 0;
}
FMT_CONSTEXPR void on_replacement_field(const Char* ptr) {
FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) {
part_.arg_id_end = ptr;
handler_(part_);
}
FMT_CONSTEXPR const Char* on_format_specs(const Char* begin,
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) {
auto repl = typename part::replacement();
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
@@ -160,23 +191,24 @@ FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
format_string_compiler<Char, PartHandler>(format_str, handler));
}
template <typename Range, typename Context, typename Id>
template <typename OutputIt, typename Context, typename Id>
void format_arg(
basic_format_parse_context<typename Range::value_type>& parse_ctx,
basic_format_parse_context<typename Context::char_type>& parse_ctx,
Context& ctx, Id arg_id) {
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, &parse_ctx), ctx.arg(arg_id)));
ctx.advance_to(visit_format_arg(
arg_formatter<OutputIt, typename Context::char_type>(ctx, &parse_ctx),
ctx.arg(arg_id)));
}
// vformat_to is defined in a subnamespace to prevent ADL.
namespace cf {
template <typename Context, typename Range, typename CompiledFormat>
auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
-> typename Context::iterator {
template <typename Context, typename OutputIt, typename CompiledFormat>
auto vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) -> typename Context::iterator {
using char_type = typename Context::char_type;
basic_format_parse_context<char_type> parse_ctx(
to_string_view(cf.format_str_));
Context ctx(out.begin(), args);
Context ctx(out, args);
const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts);
@@ -197,12 +229,12 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
case format_part_t::kind::arg_index:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.arg_index);
detail::format_arg<OutputIt>(parse_ctx, ctx, value.arg_index);
break;
case format_part_t::kind::arg_name:
advance_to(parse_ctx, part.arg_id_end);
internal::format_arg<Range>(parse_ctx, ctx, value.str);
detail::format_arg<OutputIt>(parse_ctx, ctx, value.str);
break;
case format_part_t::kind::replacement: {
@@ -226,7 +258,9 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to(
visit_format_arg(arg_formatter<Range>(ctx, nullptr, &specs), arg));
visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>(
ctx, nullptr, &specs),
arg));
break;
}
}
@@ -240,7 +274,7 @@ struct basic_compiled_format {};
template <typename S, typename = void>
struct compiled_format_base : basic_compiled_format {
using char_type = char_t<S>;
using parts_container = std::vector<internal::format_part<char_type>>;
using parts_container = std::vector<detail::format_part<char_type>>;
parts_container compiled_parts;
@@ -305,7 +339,7 @@ struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
const parts_container& parts() const {
static FMT_CONSTEXPR_DECL const auto compiled_parts =
compile_to_parts<char_type, num_format_parts>(
internal::to_string_view(S()));
detail::to_string_view(S()));
return compiled_parts.data;
}
};
@@ -318,8 +352,8 @@ class compiled_format : private compiled_format_base<S> {
private:
basic_string_view<char_type> format_str_;
template <typename Context, typename Range, typename CompiledFormat>
friend auto cf::vformat_to(Range out, CompiledFormat& cf,
template <typename Context, typename OutputIt, typename CompiledFormat>
friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) ->
typename Context::iterator;
@@ -334,7 +368,8 @@ template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get(const T& first, const Args&... rest) {
constexpr const auto& get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
@@ -359,8 +394,7 @@ template <typename Char> struct text {
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const {
// TODO: reserve
return copy_str<Char>(data.begin(), data.end(), out);
return write<Char>(out, data);
}
};
@@ -373,32 +407,18 @@ constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
return {{&s[pos], size}};
}
template <typename Char, typename OutputIt, typename T,
std::enable_if_t<std::is_integral_v<T>, int> = 0>
OutputIt format_default(OutputIt out, T value) {
// TODO: reserve
format_int fi(value);
return std::copy(fi.data(), fi.data() + fi.size(), out);
}
template <typename Char> struct code_unit {
Char value;
using char_type = Char;
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, double value) {
writer w(out);
w.write(value);
return w.out();
}
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, value);
}
};
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, Char value) {
*out++ = value;
return out;
}
template <typename Char, typename OutputIt>
OutputIt format_default(OutputIt out, const Char* value) {
auto length = std::char_traits<Char>::length(value);
return copy_str<Char>(value, value + length, out);
}
template <typename Char>
struct is_compiled_format<code_unit<Char>> : std::true_type {};
// A replacement field that refers to argument N.
template <typename Char, typename T, int N> struct field {
@@ -408,13 +428,32 @@ template <typename Char, typename T, int N> struct field {
OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
return format_default<Char>(out, arg);
return write<Char>(out, arg);
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field {
using char_type = Char;
mutable formatter<T, Char> fmt;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`.
const T& arg = get<N>(args...);
const auto& vargs =
make_format_args<basic_format_context<OutputIt, Char>>(args...);
basic_format_context<OutputIt, Char> ctx(out, vargs);
return fmt.format(arg, ctx);
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
template <typename L, typename R> struct concat {
L lhs;
R rhs;
@@ -450,7 +489,8 @@ constexpr auto compile_format_string(S format_str);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS != to_string_view(format_str).size()) {
if constexpr (POS !=
basic_string_view<typename S::char_type>(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
@@ -462,6 +502,22 @@ constexpr auto parse_tail(T head, S format_str) {
}
}
template <typename T, typename Char> struct parse_specs_result {
formatter<T, Char> fmt;
size_t end;
int next_arg_id;
};
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int arg_id) {
str.remove_prefix(pos);
auto ctx = basic_format_parse_context<Char>(str, {}, arg_id + 1);
auto f = formatter<T, Char>();
auto end = f.parse(ctx);
return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()};
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
template <typename Args, size_t POS, int ID, typename S>
@@ -475,12 +531,13 @@ constexpr auto compile_format_string(S format_str) {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else if constexpr (str[POS + 1] == '}') {
using type = get_type<ID, Args>;
if constexpr (std::is_same<type, int>::value) {
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
format_str);
} else {
return unknown_format();
}
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
format_str);
} else if constexpr (str[POS + 1] == ':') {
using type = get_type<ID, Args>;
constexpr auto result = parse_specs<type>(str, POS + 2, ID);
return parse_tail<Args, result.end, result.next_arg_id>(
spec_field<char_type, type, ID>{result.fmt}, format_str);
} else {
return unknown_format();
}
@@ -490,104 +547,153 @@ constexpr auto compile_format_string(S format_str) {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else {
constexpr auto end = parse_text(str, POS + 1);
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
if constexpr (end - POS > 1) {
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
} else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
format_str);
}
}
}
#endif // __cpp_if_constexpr
} // namespace internal
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
FMT_ENABLE_IF(is_compile_string<S>::value ||
detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) {
constexpr basic_string_view<typename S::char_type> str = format_str;
if constexpr (str.size() == 0) {
return internal::make_text(str, 0, 0);
return detail::make_text(str, 0, 0);
} else {
constexpr auto result =
internal::compile_format_string<internal::type_list<Args...>, 0, 0>(
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
internal::unknown_format>()) {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
detail::unknown_format>()) {
return detail::compiled_format<S, Args...>(to_string_view(format_str));
} else {
return result;
}
}
}
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
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...);
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
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...);
}
# else
#else
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto compile(S format_str) -> internal::compiled_format<S, Args...> {
return internal::compiled_format<S, Args...>(to_string_view(format_str));
constexpr auto compile(S format_str) -> detail::compiled_format<S, Args...> {
return detail::compiled_format<S, Args...>(to_string_view(format_str));
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
#endif // __cpp_if_constexpr
// Compiles the format string which must be a string literal.
template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N])
-> internal::compiled_format<const Char*, Args...> {
return internal::compiled_format<const Char*, Args...>(
-> detail::compiled_format<const Char*, Args...> {
return detail::compiled_format<const Char*, Args...>(
basic_string_view<Char>(format_str, N - 1));
}
} // namespace detail
// DEPRECATED! use FMT_COMPILE instead.
template <typename... Args>
FMT_DEPRECATED auto compile(const Args&... args)
-> decltype(detail::compile(args...)) {
return detail::compile(args...);
}
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) {
basic_memory_buffer<Char> buffer;
using range = buffer_range<Char>;
using context = buffer_context<Char>;
internal::cf::vformat_to<context>(range(buffer), cf,
make_format_args<context>(args...));
cf.format(detail::buffer_appender<Char>(buffer), args...);
return to_string(buffer);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<internal::basic_compiled_format,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
return cf.format(out, args...);
}
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using context = buffer_context<Char>;
detail::cf::vformat_to<context>(detail::buffer_appender<Char>(buffer), cf,
make_format_args<context>(args...));
return to_string(buffer);
}
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) {
#ifdef __cpp_if_constexpr
if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr basic_string_view<typename S::char_type> str = S();
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
return fmt::to_string(detail::first(args...));
}
#endif
constexpr auto compiled = detail::compile<Args...>(S());
return format(compiled, std::forward<Args>(args)...);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
using char_type = typename CompiledFormat::char_type;
using range = internal::output_range<OutputIt, char_type>;
using context = format_context_t<OutputIt, char_type>;
return internal::cf::vformat_to<context>(range(out), cf,
make_format_args<context>(args...));
return detail::cf::vformat_to<context>(out, cf,
make_format_args<context>(args...));
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
const CompiledFormat& cf,
const Args&... args) {
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
OutputIt format_to(OutputIt out, const S&, const Args&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
return format_to(out, compiled, args...);
}
template <typename OutputIt, typename CompiledFormat, typename... Args>
auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf,
const Args&... args) ->
typename std::enable_if<
detail::is_output_iterator<OutputIt,
typename CompiledFormat::char_type>::value &&
std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value,
format_to_n_result<OutputIt>>::type {
auto it =
format_to(internal::truncating_iterator<OutputIt>(out, n), cf, args...);
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
return {it.base(), it.count()};
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S&,
const Args&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), compiled,
args...);
return {it.base(), it.count()};
}
template <typename CompiledFormat, typename... Args>
std::size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(internal::counting_iterator(), cf, args...).count();
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(detail::counting_iterator(), cf, args...).count();
}
FMT_END_NAMESPACE

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -14,63 +14,49 @@
FMT_BEGIN_NAMESPACE
namespace internal {
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<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));
}
namespace detail {
template <typename Char>
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);
detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
return fmt::to_string(buffer);
}
} // namespace internal
} // namespace detail
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<type_identity_t<Char>>> args) {
return internal::vformat(loc, to_string_view(format_str), args);
return detail::vformat(loc, to_string_view(format_str), args);
}
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) {
return internal::vformat(
loc, to_string_view(format_str),
internal::make_args_checked<Args...>(format_str, args...));
return detail::vformat(loc, to_string_view(format_str),
fmt::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>>>
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
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));
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
return detail::get_iterator(buf);
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(internal::is_output_iterator<OutputIt>::value&&
internal::is_string<S>::value)>
inline OutputIt format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) {
internal::check_format_string<Args...>(format_str);
using context = format_context_t<OutputIt, char_t<S>>;
format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str),
basic_format_args<context>(as));
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
inline auto format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to(out, loc, to_string_view(format_str), vargs);
}
FMT_END_NAMESPACE

View File

@@ -0,0 +1,480 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_OS_H_
#define FMT_OS_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 <cstddef>
#include <cstdio>
#include <cstdlib> // for strtod_l
#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(__APPLE__) || \
defined(__linux__)) && \
(!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_; }
};
#ifdef _WIN32
namespace detail {
// A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively.
class utf16_to_utf8 {
private:
memory_buffer buffer_;
public:
utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(wstring_view s);
operator string_view() const { return string_view(&buffer_[0], size()); }
size_t size() const { return buffer_.size() - 1; }
const char* c_str() const { return &buffer_[0]; }
std::string str() const { return std::string(&buffer_[0], size()); }
// Performs conversion returning a system error code instead of
// throwing exception on conversion error. This method may still throw
// in case of memory allocation error.
FMT_API int convert(wstring_view s);
};
FMT_API void format_windows_error(buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT;
} // namespace detail
/** A Windows error. */
class windows_error : public system_error {
private:
FMT_API void init(int error_code, string_view format_str, format_args args);
public:
/**
\rst
Constructs a :class:`fmt::windows_error` object with the description
of the form
.. parsed-literal::
*<message>*: *<system-message>*
where *<message>* is the formatted message and *<system-message>* is the
system message corresponding to the error code.
*error_code* is a Windows error code as given by ``GetLastError``.
If *error_code* is not a valid error code such as -1, the system message
will look like "error -1".
**Example**::
// This throws a windows_error with the description
// cannot open file 'madeup': The system cannot find the file specified.
// or similar (system message may vary).
const char *filename = "madeup";
LPOFSTRUCT of = LPOFSTRUCT();
HFILE file = OpenFile(filename, &of, OF_READ);
if (file == HFILE_ERROR) {
throw fmt::windows_error(GetLastError(),
"cannot open file '{}'", filename);
}
\endrst
*/
template <typename... Args>
windows_error(int error_code, string_view message, const Args&... args) {
init(error_code, message, make_format_args(args...));
}
};
// Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code,
string_view message) FMT_NOEXCEPT;
#endif // _WIN32
// 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.
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
APPEND = FMT_POSIX(O_APPEND) // Open in append mode.
};
// 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 size_t read(void* buffer, size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API size_t write(const void* buffer, 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();
namespace detail {
struct buffer_size {
size_t value = 0;
buffer_size operator=(size_t val) const {
auto bs = buffer_size();
bs.value = val;
return bs;
}
};
struct ostream_params {
int oflag = file::WRONLY | file::CREATE;
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
ostream_params() {}
template <typename... T>
ostream_params(T... params, int oflag) : ostream_params(params...) {
this->oflag = oflag;
}
template <typename... T>
ostream_params(T... params, detail::buffer_size bs)
: ostream_params(params...) {
this->buffer_size = bs.value;
}
};
} // namespace detail
static constexpr detail::buffer_size buffer_size;
// A fast output stream which is not thread-safe.
class ostream final : private detail::buffer<char> {
private:
file file_;
void flush() {
if (size() == 0) return;
file_.write(data(), size());
clear();
}
FMT_API void grow(size_t) override final;
ostream(cstring_view path, const detail::ostream_params& params)
: file_(path, params.oflag) {
set(new char[params.buffer_size], params.buffer_size);
}
public:
ostream(ostream&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.set(nullptr, 0);
}
~ostream() {
flush();
delete[] data();
}
template <typename... T>
friend ostream output_file(cstring_view path, T... params);
void close() {
flush();
file_.close();
}
template <typename S, typename... Args>
void print(const S& format_str, const Args&... args) {
format_to(detail::buffer_appender<char>(*this), format_str, args...);
}
};
/**
Opens a file for writing. Supported parameters passed in `params`:
* ``<integer>``: Output flags (``file::WRONLY | file::CREATE`` by default)
* ``buffer_size=<integer>``: Output buffer size
*/
template <typename... T>
inline ostream output_file(cstring_view path, T... params) {
return {path, detail::ostream_params(params...)};
}
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
class locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
static void freelocale(locale_t loc) { _free_locale(loc); }
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
return _strtod_l(nptr, endptr, loc);
}
# endif
locale_t locale_;
public:
using type = locale_t;
locale(const locale&) = delete;
void operator=(const locale&) = delete;
locale() {
# ifndef _WIN32
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
# else
locale_ = _create_locale(LC_NUMERIC, "C");
# endif
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;
}
};
using Locale FMT_DEPRECATED_ALIAS = locale;
#endif // FMT_LOCALE
FMT_END_NAMESPACE
#endif // FMT_OS_H_

View File

@@ -9,10 +9,15 @@
#define FMT_OSTREAM_H_
#include <ostream>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
template <typename Char> class basic_printf_parse_context;
template <typename OutputIt, typename Char> class basic_printf_context;
namespace detail {
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
private:
@@ -44,17 +49,27 @@ template <class Char> class formatbuf : public std::basic_streambuf<Char> {
}
};
struct converter {
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
};
template <typename Char> struct test_stream : std::basic_ostream<Char> {
private:
// Hide all operator<< from std::basic_ostream<Char>.
void_t<> operator<<(null<>);
void_t<> operator<<(const Char*);
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
!std::is_enum<T>::value)>
void_t<> operator<<(T);
void_t<> operator<<(converter);
};
// Hide insertion operators for built-in types.
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
// Checks if T has a user-defined operator<< (e.g. not a member of
// std::ostream).
template <typename T, typename Char> class is_streamable {
@@ -75,7 +90,7 @@ template <typename T, typename Char> class is_streamable {
// Write the content of buf to os.
template <typename Char>
void write(std::basic_ostream<Char>& os, buffer<Char>& buf) {
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size();
@@ -93,34 +108,53 @@ 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 !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>());
#endif
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
#endif
output << value;
buf.resize(buf.size());
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
buf.try_resize(buf.size());
}
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: formatter<basic_string_view<Char>, Char> {
template <typename Context>
auto format(const T& value, Context& ctx) -> decltype(ctx.out()) {
: private formatter<basic_string_view<Char>, Char> {
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
-> decltype(ctx.begin()) {
return formatter<basic_string_view<Char>, Char>::parse(ctx);
}
template <typename ParseCtx,
FMT_ENABLE_IF(std::is_same<
ParseCtx, basic_printf_parse_context<Char>>::value)>
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size());
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
}
template <typename OutputIt>
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale());
return std::copy(buffer.begin(), buffer.end(), ctx.out());
}
};
} // namespace internal
} // namespace detail
template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
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);
detail::vformat_to(buffer, format_str, args);
detail::write_buffer(os, buffer);
}
/**
@@ -133,10 +167,10 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
typename Char = enable_if_t<detail::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...));
fmt::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@@ -1,2 +1,2 @@
#include "os.h"
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"

View File

@@ -14,7 +14,7 @@
#include "ostream.h"
FMT_BEGIN_NAMESPACE
namespace internal {
namespace detail {
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
@@ -90,11 +90,11 @@ template <typename T, typename Context> class arg_converter {
if (const_check(sizeof(target_type) <= sizeof(int))) {
// Extra casts are used to silence warnings.
if (is_signed) {
arg_ = internal::make_arg<Context>(
arg_ = detail::make_arg<Context>(
static_cast<int>(static_cast<target_type>(value)));
} else {
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
arg_ = internal::make_arg<Context>(
arg_ = detail::make_arg<Context>(
static_cast<unsigned>(static_cast<unsigned_type>(value)));
}
} else {
@@ -102,9 +102,9 @@ template <typename T, typename Context> class arg_converter {
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_ = internal::make_arg<Context>(static_cast<long long>(value));
arg_ = detail::make_arg<Context>(static_cast<long long>(value));
} else {
arg_ = internal::make_arg<Context>(
arg_ = detail::make_arg<Context>(
static_cast<typename make_unsigned_or_bool<U>::type>(value));
}
}
@@ -133,7 +133,7 @@ template <typename Context> class char_converter {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
void operator()(T value) {
arg_ = internal::make_arg<Context>(
arg_ = detail::make_arg<Context>(
static_cast<typename Context::char_type>(value));
}
@@ -141,6 +141,13 @@ template <typename Context> class char_converter {
void operator()(T) {} // No conversion needed for non-integral types.
};
// An argument visitor that return a pointer to a C string if argument is a
// string or null otherwise.
template <typename Char> struct get_cstring {
template <typename T> const Char* operator()(T) { return nullptr; }
const Char* operator()(const Char* s) { return s; }
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
template <typename Char> class printf_width_handler {
@@ -155,7 +162,7 @@ template <typename Char> class printf_width_handler {
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
unsigned operator()(T value) {
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
if (internal::is_negative(value)) {
if (detail::is_negative(value)) {
specs_.align = align::left;
width = 0 - width;
}
@@ -172,23 +179,25 @@ template <typename Char> class printf_width_handler {
};
template <typename Char, typename Context>
void printf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(std::back_inserter(buf), format, args).format();
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(buffer_appender<Char>(buf), format, args).format();
}
} // namespace detail
template <typename OutputIt, typename Char, typename Context>
internal::truncating_iterator<OutputIt> printf(
internal::truncating_iterator<OutputIt> it, basic_string_view<Char> format,
basic_format_args<Context> args) {
return Context(it, format, args).format();
// For printing into memory_buffer.
template <typename Char, typename Context>
FMT_DEPRECATED void printf(detail::buffer<Char>& buf,
basic_string_view<Char> format,
basic_format_args<Context> args) {
return detail::vprintf(buf, format, args);
}
} // namespace internal
using internal::printf; // For printing into memory_buffer.
template <typename Range> class printf_arg_formatter;
using detail::vprintf;
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
};
template <typename OutputIt, typename Char> class basic_printf_context;
/**
@@ -196,15 +205,15 @@ template <typename OutputIt, typename Char> class basic_printf_context;
The ``printf`` argument formatter.
\endrst
*/
template <typename Range>
class printf_arg_formatter : public internal::arg_formatter_base<Range> {
template <typename OutputIt, typename Char>
class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
public:
using iterator = typename Range::iterator;
using iterator = OutputIt;
private:
using char_type = typename Range::value_type;
using base = internal::arg_formatter_base<Range>;
using context_type = basic_printf_context<iterator, char_type>;
using char_type = Char;
using base = detail::arg_formatter_base<OutputIt, Char>;
using context_type = basic_printf_context<OutputIt, Char>;
context_type& context_;
@@ -229,9 +238,9 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
\endrst
*/
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
: base(Range(iter), &specs, internal::locale_ref()), context_(ctx) {}
: base(iter, &specs, detail::locale_ref()), context_(ctx) {}
template <typename T, FMT_ENABLE_IF(fmt::internal::is_integral<T>::value)>
template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)>
iterator operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
@@ -246,7 +255,11 @@ class printf_arg_formatter : public internal::arg_formatter_base<Range> {
return (*this)(static_cast<int>(value));
fmt_specs.sign = sign::none;
fmt_specs.alt = false;
fmt_specs.align = align::right;
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
// align::numeric needs to be overwritten here since the '0' flag is
// ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right;
return base::operator()(value);
} else {
return base::operator()(value);
@@ -312,18 +325,21 @@ template <typename T> struct printf_formatter {
template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
internal::format_value(internal::get_container(ctx.out()), value);
detail::format_value(detail::get_container(ctx.out()), value);
return ctx.out();
}
};
/** This template formats data and writes the output to a writer. */
/**
This template formats data and writes the output through an output iterator.
*/
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>;
using parse_context_type = basic_printf_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
private:
@@ -331,7 +347,7 @@ template <typename OutputIt, typename Char> class basic_printf_context {
OutputIt out_;
basic_format_args<basic_printf_context> args_;
basic_format_parse_context<Char> parse_ctx_;
parse_context_type parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it,
const Char* end);
@@ -346,9 +362,8 @@ template <typename OutputIt, typename Char> class basic_printf_context {
public:
/**
\rst
Constructs a ``printf_context`` object. References to the arguments and
the writer are stored in the context object so make sure they have
appropriate lifetimes.
Constructs a ``printf_context`` object. References to the arguments are
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
@@ -358,18 +373,18 @@ 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 {}; }
detail::locale_ref locale() { return {}; }
format_arg arg(int id) const { return args_.get(id); }
basic_format_parse_context<Char>& parse_context() { return parse_ctx_; }
parse_context_type& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message);
}
/** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter = printf_arg_formatter<buffer_range<Char>>>
template <typename ArgFormatter = printf_arg_formatter<OutputIt, Char>>
OutputIt format();
};
@@ -389,7 +404,9 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
specs.fill[0] = '0';
break;
case ' ':
specs.sign = sign::space;
if (specs.sign != sign::plus) {
specs.sign = sign::space;
}
break;
case '#':
specs.alt = true;
@@ -407,7 +424,7 @@ basic_printf_context<OutputIt, Char>::get_arg(int arg_index) {
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
return internal::get_arg(*this, arg_index);
return detail::get_arg(*this, arg_index);
}
template <typename OutputIt, typename Char>
@@ -419,7 +436,7 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
internal::error_handler eh;
detail::error_handler eh;
int value = parse_nonnegative_int(it, end, eh);
if (it != end && *it == '$') { // value is an argument index
++it;
@@ -438,12 +455,12 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
// Parse width.
if (it != end) {
if (*it >= '0' && *it <= '9') {
internal::error_handler eh;
detail::error_handler eh;
specs.width = parse_nonnegative_int(it, end, eh);
} else if (*it == '*') {
++it;
specs.width = static_cast<int>(visit_format_arg(
internal::printf_width_handler<char_type>(specs), get_arg()));
detail::printf_width_handler<char_type>(specs), get_arg()));
}
}
return arg_index;
@@ -471,38 +488,52 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
// Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs);
if (arg_index == 0) on_error("argument index out of range");
if (arg_index == 0) on_error("argument not found");
// Parse precision.
if (it != end && *it == '.') {
++it;
c = it != end ? *it : 0;
if ('0' <= c && c <= '9') {
internal::error_handler eh;
detail::error_handler eh;
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()));
visit_format_arg(detail::printf_precision_handler(), get_arg()));
} else {
specs.precision = 0;
}
}
format_arg arg = get_arg(arg_index);
if (specs.alt && visit_format_arg(internal::is_zero_int(), arg))
// For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral())
specs.fill[0] =
' '; // Ignore '0' flag for non-numeric types or if '-' present.
if (specs.precision >= 0 && arg.type() == detail::type::cstring_type) {
auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char());
arg = detail::make_arg<basic_printf_context>(basic_string_view<Char>(
str,
detail::to_unsigned(nul != str_end ? nul - str : specs.precision)));
}
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
specs.alt = false;
if (specs.fill[0] == '0') {
if (arg.is_arithmetic())
if (arg.is_arithmetic() && specs.align != align::left)
specs.align = align::numeric;
else
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types.
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
// flag is also present.
}
// Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0;
char_type t = it != end ? *it : 0;
using internal::convert_arg;
using detail::convert_arg;
switch (c) {
case 'h':
if (t == 'h') {
@@ -526,7 +557,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
convert_arg<intmax_t>(arg, t);
break;
case 'z':
convert_arg<std::size_t>(arg, t);
convert_arg<size_t>(arg, t);
break;
case 't':
convert_arg<std::ptrdiff_t>(arg, t);
@@ -551,7 +582,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
specs.type = 'd';
break;
case 'c':
visit_format_arg(internal::char_converter<basic_printf_context>(arg),
visit_format_arg(detail::char_converter<basic_printf_context>(arg),
arg);
break;
}
@@ -560,15 +591,14 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
start = it;
// Format argument.
visit_format_arg(ArgFormatter(out, specs, *this), arg);
out = visit_format_arg(ArgFormatter(out, specs, *this), arg);
}
return std::copy(start, it, out);
}
template <typename Char>
using basic_printf_context_t =
basic_printf_context<std::back_insert_iterator<internal::buffer<Char>>,
Char>;
basic_printf_context<detail::buffer_appender<Char>, Char>;
using printf_context = basic_printf_context_t<char>;
using wprintf_context = basic_printf_context_t<wchar_t>;
@@ -605,7 +635,7 @@ inline std::basic_string<Char> vsprintf(
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);
vprintf(buffer, to_string_view(format), args);
return to_string(buffer);
}
@@ -619,7 +649,7 @@ inline std::basic_string<Char> vsprintf(
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
typename Char = enable_if_t<detail::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...));
@@ -630,8 +660,8 @@ 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();
vprintf(buffer, to_string_view(format), args);
size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1
: static_cast<int>(size);
@@ -647,7 +677,7 @@ inline int vfprintf(
\endrst
*/
template <typename S, typename... Args,
typename Char = enable_if_t<internal::is_string<S>::value, char_t<S>>>
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
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),
@@ -671,7 +701,7 @@ inline int vprintf(
\endrst
*/
template <typename S, typename... Args,
FMT_ENABLE_IF(internal::is_string<S>::value)>
FMT_ENABLE_IF(detail::is_string<S>::value)>
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),
@@ -683,8 +713,8 @@ 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);
vprintf(buffer, to_string_view(format), args);
detail::write_buffer(os, buffer);
return static_cast<int>(buffer.size());
}
@@ -693,7 +723,7 @@ 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,
detail::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>();

View File

@@ -33,7 +33,7 @@ template <typename Char> struct formatting_base {
template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
static FMT_CONSTEXPR_DECL const size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
// range.
Char prefix;
@@ -54,7 +54,7 @@ struct formatting_tuple : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};
namespace internal {
namespace detail {
template <typename RangeT, typename OutputIterator>
OutputIterator copy(const RangeT& range, OutputIterator out) {
@@ -118,26 +118,24 @@ template <typename T> class is_tuple_like_ {
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>;
template <std::size_t... N> using index_sequence = std::index_sequence<N...>;
template <std::size_t N>
using make_index_sequence = std::make_index_sequence<N>;
template <size_t... N> using index_sequence = std::index_sequence<N...>;
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
#else
template <typename T, T... N> struct integer_sequence {
using value_type = T;
static FMT_CONSTEXPR std::size_t size() { return sizeof...(N); }
static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
};
template <std::size_t... N>
using index_sequence = integer_sequence<std::size_t, N...>;
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
template <typename T, std::size_t N, T... Ns>
template <typename T, size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
template <typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
template <std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;
template <size_t N>
using make_index_sequence = make_integer_sequence<size_t, N>;
#endif
template <class Tuple, class F, size_t... Is>
@@ -159,6 +157,9 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
}
template <typename Range>
using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>;
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
typename std::decay<Arg>::type>::value)>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
@@ -184,12 +185,11 @@ FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'";
}
} // namespace internal
} // namespace detail
template <typename T> struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
};
template <typename TupleT, typename Char>
@@ -202,17 +202,17 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
out = internal::copy(formatting.delimiter, out);
out = detail::copy(formatting.delimiter, out);
}
out = format_to(out,
internal::format_str_quoted(
detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v),
v);
++i;
}
formatting_tuple<Char>& formatting;
std::size_t& i;
size_t& i;
typename std::add_lvalue_reference<decltype(
std::declval<FormatContext>().out())>::type out;
};
@@ -228,14 +228,14 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
template <typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
std::size_t i = 0;
internal::copy(formatting.prefix, out);
size_t i = 0;
detail::copy(formatting.prefix, out);
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
detail::for_each(values, format_each<FormatContext>{formatting, i, out});
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
internal::copy(formatting.postfix, out);
detail::copy(formatting.postfix, out);
return ctx.out();
}
@@ -243,15 +243,23 @@ struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_range_<T>::value &&
!internal::is_like_std_string<T>::value &&
detail::is_range_<T>::value && !detail::is_like_std_string<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<internal::std_string_view<Char>, T>::value;
!std::is_constructible<detail::std_string_view<Char>, T>::value;
};
template <typename RangeT, typename Char>
struct formatter<RangeT, Char,
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
template <typename T, typename Char>
struct formatter<
T, Char,
enable_if_t<fmt::is_range<T, Char>::value
// Workaround a bug in MSVC 2017 and earlier.
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
&&
(has_formatter<detail::value_type<T>, format_context>::value ||
detail::has_fallback_formatter<detail::value_type<T>,
format_context>::value)
#endif
>> {
formatting_range<Char> formatting;
template <typename ParseContext>
@@ -260,17 +268,18 @@ struct formatter<RangeT, Char,
}
template <typename FormatContext>
typename FormatContext::iterator format(const RangeT& values,
FormatContext& ctx) {
auto out = internal::copy(formatting.prefix, ctx.out());
std::size_t i = 0;
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
auto out = detail::copy(formatting.prefix, ctx.out());
size_t i = 0;
auto it = values.begin();
auto end = values.end();
for (; it != end; ++it) {
if (i > 0) {
if (formatting.add_prepostfix_space) *out++ = ' ';
out = internal::copy(formatting.delimiter, out);
out = detail::copy(formatting.delimiter, out);
}
out = format_to(out,
internal::format_str_quoted(
detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it),
*it);
if (++i > formatting.range_length_limit) {
@@ -279,11 +288,11 @@ struct formatter<RangeT, Char,
}
}
if (formatting.add_prepostfix_space) *out++ = ' ';
return internal::copy(formatting.postfix, out);
return detail::copy(formatting.postfix, out);
}
};
template <typename Char, typename... T> struct tuple_arg_join : internal::view {
template <typename Char, typename... T> struct tuple_arg_join : detail::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
@@ -301,14 +310,14 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
template <typename FormatContext>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
return format(value, ctx, internal::make_index_sequence<sizeof...(T)>{});
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
}
private:
template <typename FormatContext, size_t... N>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
internal::index_sequence<N...>) {
detail::index_sequence<N...>) {
return format_args(value, ctx, std::get<N>(value.tuple)...);
}
@@ -371,14 +380,14 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
\endrst
*/
template <typename T>
arg_join<internal::iterator_t<const std::initializer_list<T>>, char> join(
std::initializer_list<T> list, string_view sep) {
arg_join<const T*, const 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) {
arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list,
wstring_view sep) {
return join(std::begin(list), std::end(list), sep);
}

View File

@@ -0,0 +1,20 @@
//
// Copyright(c) 2016 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
//
// include bundled or external copy of fmtlib's chrono support
//
#if !defined(SPDLOG_FMT_EXTERNAL)
#ifdef SPDLOG_HEADER_ONLY
#ifndef FMT_HEADER_ONLY
#define FMT_HEADER_ONLY
#endif
#endif
#include <spdlog/fmt/bundled/chrono.h>
#else
#include <fmt/chrono.h>
#endif

View File

@@ -17,6 +17,8 @@
#ifndef FMT_USE_WINDOWS_H
#define FMT_USE_WINDOWS_H 0
#endif
// enable the 'n' flag in for backward compatibility with fmt 6.x
#define FMT_DEPRECATED_N_SPECIFIER
#include <spdlog/fmt/bundled/core.h>
#include <spdlog/fmt/bundled/format.h>
#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib

View File

@@ -247,7 +247,11 @@ SPDLOG_INLINE void logger::err_handler_(const std::string &msg)
auto tm_time = details::os::localtime(system_clock::to_time_t(now));
char date_buf[64];
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
fprintf(stderr, "[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
#if defined(USING_R) && defined(R_R_H) // if in R environment
REprintf("[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
#else
std::fprintf(stderr, "[*** LOG ERROR #%04zu ***] [%s] [%s] {%s}\n", err_counter, date_buf, name().c_str(), msg.c_str());
#endif
}
}
} // namespace spdlog

View File

@@ -73,65 +73,60 @@ public:
void swap(spdlog::logger &other) SPDLOG_NOEXCEPT;
template<typename... Args>
void log(source_loc loc, level::level_enum lvl, string_view_t fmt, const Args &... args)
// FormatString is a type derived from fmt::compile_string
template<typename FormatString, typename std::enable_if<fmt::is_compile_string<FormatString>::value, int>::type = 0, typename... Args>
void log(source_loc loc, level::level_enum lvl, const FormatString &fmt, Args&&...args)
{
bool log_enabled = should_log(lvl);
bool traceback_enabled = tracer_.enabled();
if (!log_enabled && !traceback_enabled)
{
return;
}
SPDLOG_TRY
{
memory_buf_t buf;
fmt::format_to(buf, fmt, args...);
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
log_it_(log_msg, log_enabled, traceback_enabled);
}
SPDLOG_LOGGER_CATCH()
log_(loc, lvl, fmt, std::forward<Args>(args)...);
}
// FormatString is NOT a type derived from fmt::compile_string but is a string_view_t or can be implicitly converted to one
template<typename... Args>
void log(level::level_enum lvl, string_view_t fmt, const Args &... args)
void log(source_loc loc, level::level_enum lvl, string_view_t fmt, Args&&...args)
{
log(source_loc{}, lvl, fmt, args...);
log_(loc, lvl, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void trace(string_view_t fmt, const Args &... args)
template<typename FormatString, typename... Args>
void log(level::level_enum lvl, const FormatString &fmt, Args&&...args)
{
log(level::trace, fmt, args...);
log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void debug(string_view_t fmt, const Args &... args)
template<typename FormatString, typename... Args>
void trace(const FormatString &fmt, Args&&...args)
{
log(level::debug, fmt, args...);
log(level::trace, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void info(string_view_t fmt, const Args &... args)
template<typename FormatString, typename... Args>
void debug(const FormatString &fmt, Args&&...args)
{
log(level::info, fmt, args...);
log(level::debug, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void warn(string_view_t fmt, const Args &... args)
template<typename FormatString, typename... Args>
void info(const FormatString &fmt, Args&&...args)
{
log(level::warn, fmt, args...);
log(level::info, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void error(string_view_t fmt, const Args &... args)
template<typename FormatString, typename... Args>
void warn(const FormatString &fmt, Args&&...args)
{
log(level::err, fmt, args...);
log(level::warn, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
void critical(string_view_t fmt, const Args &... args)
template<typename FormatString, typename... Args>
void error(const FormatString &fmt, Args&&...args)
{
log(level::critical, fmt, args...);
log(level::err, fmt, std::forward<Args>(args)...);
}
template<typename FormatString, typename... Args>
void critical(const FormatString &fmt, Args&&...args)
{
log(level::critical, fmt, std::forward<Args>(args)...);
}
template<typename T>
@@ -140,8 +135,9 @@ public:
log(source_loc{}, lvl, msg);
}
// T can be statically converted to string_view
template<class T, typename std::enable_if<std::is_convertible<const T &, spdlog::string_view_t>::value, T>::type * = nullptr>
// T can be statically converted to string_view and isn't a fmt::compile_string
template<class T, typename std::enable_if<
std::is_convertible<const T &, spdlog::string_view_t>::value && !fmt::is_compile_string<T>::value, int>::type = 0>
void log(source_loc loc, level::level_enum lvl, const T &msg)
{
log(loc, lvl, string_view_t{msg});
@@ -181,7 +177,7 @@ public:
// T cannot be statically converted to string_view or wstring_view
template<class T, typename std::enable_if<!std::is_convertible<const T &, spdlog::string_view_t>::value &&
!is_convertible_to_wstring_view<const T &>::value,
T>::type * = nullptr>
int>::type = 0>
void log(source_loc loc, level::level_enum lvl, const T &msg)
{
log(loc, lvl, "{}", msg);
@@ -229,7 +225,7 @@ public:
#else
template<typename... Args>
void log(source_loc loc, level::level_enum lvl, wstring_view_t fmt, const Args &... args)
void log(source_loc loc, level::level_enum lvl, wstring_view_t fmt, Args&&...args)
{
bool log_enabled = should_log(lvl);
bool traceback_enabled = tracer_.enabled();
@@ -241,7 +237,7 @@ public:
{
// format to wmemory_buffer and convert to utf8
fmt::wmemory_buffer wbuf;
fmt::format_to(wbuf, fmt, args...);
fmt::format_to(wbuf, fmt, std::forward<Args>(args)...);
memory_buf_t buf;
details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf);
@@ -251,50 +247,8 @@ public:
SPDLOG_LOGGER_CATCH()
}
template<typename... Args>
void log(level::level_enum lvl, wstring_view_t fmt, const Args &... args)
{
log(source_loc{}, lvl, fmt, args...);
}
template<typename... Args>
void trace(wstring_view_t fmt, const Args &... args)
{
log(level::trace, fmt, args...);
}
template<typename... Args>
void debug(wstring_view_t fmt, const Args &... args)
{
log(level::debug, fmt, args...);
}
template<typename... Args>
void info(wstring_view_t fmt, const Args &... args)
{
log(level::info, fmt, args...);
}
template<typename... Args>
void warn(wstring_view_t fmt, const Args &... args)
{
log(level::warn, fmt, args...);
}
template<typename... Args>
void error(wstring_view_t fmt, const Args &... args)
{
log(level::err, fmt, args...);
}
template<typename... Args>
void critical(wstring_view_t fmt, const Args &... args)
{
log(level::critical, fmt, args...);
}
// T can be statically converted to wstring_view
template<class T, typename std::enable_if<is_convertible_to_wstring_view<const T &>::value, T>::type * = nullptr>
template<class T, typename std::enable_if<is_convertible_to_wstring_view<const T &>::value, int>::type = 0>
void log(source_loc loc, level::level_enum lvl, const T &msg)
{
bool log_enabled = should_log(lvl);
@@ -370,6 +324,26 @@ protected:
err_handler custom_err_handler_{nullptr};
details::backtracer tracer_;
// common implementation for after templated public api has been resolved
template<typename FormatString, typename... Args>
void log_(source_loc loc, level::level_enum lvl, const FormatString &fmt, Args&&...args)
{
bool log_enabled = should_log(lvl);
bool traceback_enabled = tracer_.enabled();
if (!log_enabled && !traceback_enabled)
{
return;
}
SPDLOG_TRY
{
memory_buf_t buf;
fmt::format_to(buf, fmt, std::forward<Args>(args)...);
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
log_it_(log_msg, log_enabled, traceback_enabled);
}
SPDLOG_LOGGER_CATCH()
}
// log the given message (if the given log level is high enough),
// and save backtrace (if backtrace is enabled).
void log_it_(const details::log_msg &log_msg, bool log_enabled, bool traceback_enabled);

View File

@@ -13,11 +13,13 @@
#include <spdlog/fmt/fmt.h>
#include <spdlog/formatter.h>
#include <algorithm>
#include <array>
#include <chrono>
#include <ctime>
#include <cctype>
#include <cstring>
#include <iterator>
#include <memory>
#include <mutex>
#include <string>
@@ -45,12 +47,12 @@ public:
return;
}
if (padinfo_.side_ == padding_info::left)
if (padinfo_.side_ == padding_info::pad_side::left)
{
pad_it(remaining_pad_);
remaining_pad_ = 0;
}
else if (padinfo_.side_ == padding_info::center)
else if (padinfo_.side_ == padding_info::pad_side::center)
{
auto half_pad = remaining_pad_ / 2;
auto reminder = remaining_pad_ & 1;
@@ -127,7 +129,7 @@ public:
void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
{
string_view_t &level_name = level::to_string_view(msg.level);
const string_view_t &level_name = level::to_string_view(msg.level);
ScopedPadder p(level_name.size(), padinfo_, dest);
fmt_helper::append_string_view(level_name, dest);
}
@@ -814,11 +816,31 @@ public:
: flag_formatter(padinfo)
{}
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4127) // consider using 'if constexpr' instead
#endif // _MSC_VER
static const char *basename(const char *filename)
{
const char *rv = std::strrchr(filename, os::folder_sep);
return rv != nullptr ? rv + 1 : filename;
// if the size is 2 (1 character + null terminator) we can use the more efficient strrchr
// the branch will be elided by optimizations
if (sizeof(os::folder_seps) == 2)
{
const char *rv = std::strrchr(filename, os::folder_seps[0]);
return rv != nullptr ? rv + 1 : filename;
}
else
{
const std::reverse_iterator<const char*> begin(filename + std::strlen(filename));
const std::reverse_iterator<const char*> end(filename);
const auto it = std::find_first_of(begin, end, std::begin(os::folder_seps), std::end(os::folder_seps) - 1);
return it != end ? it.base() : filename;
}
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif // _MSC_VER
void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
{
@@ -1246,9 +1268,24 @@ SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_i
default: // Unknown flag appears as is
auto unknown_flag = details::make_unique<details::aggregate_formatter>();
unknown_flag->add_ch('%');
unknown_flag->add_ch(flag);
formatters_.push_back((std::move(unknown_flag)));
if (!padding.truncate_)
{
unknown_flag->add_ch('%');
unknown_flag->add_ch(flag);
formatters_.push_back((std::move(unknown_flag)));
}
// fix issue #1617 (prev char was '!' and should have been treated as funcname flag instead of truncating flag)
// spdlog::set_pattern("[%10!] %v") => "[ main] some message"
// spdlog::set_pattern("[%3!!] %v") => "[mai] some message"
else
{
padding.truncate_ = false;
formatters_.push_back(details::make_unique<details::source_funcname_formatter<Padder>>(padding));
unknown_flag->add_ch(flag);
formatters_.push_back((std::move(unknown_flag)));
}
break;
}
}
@@ -1270,15 +1307,15 @@ SPDLOG_INLINE details::padding_info pattern_formatter::handle_padspec_(std::stri
switch (*it)
{
case '-':
side = padding_info::right;
side = padding_info::pad_side::right;
++it;
break;
case '=':
side = padding_info::center;
side = padding_info::pad_side::center;
++it;
break;
default:
side = details::padding_info::left;
side = details::padding_info::pad_side::left;
break;
}

View File

@@ -22,7 +22,7 @@ namespace details {
// padding information.
struct padding_info
{
enum pad_side
enum class pad_side
{
left,
right,
@@ -42,7 +42,7 @@ struct padding_info
return enabled_;
}
size_t width_ = 0;
pad_side side_ = left;
pad_side side_ = pad_side::left;
bool truncate_ = false;
bool enabled_ = false;
};
@@ -92,9 +92,9 @@ public:
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)
pattern_formatter &add_flag(char flag, Args&&...args)
{
custom_handlers_[flag] = details::make_unique<T>(args...);
custom_handlers_[flag] = details::make_unique<T>(std::forward<Args>(args)...);
return *this;
}
void set_pattern(std::string pattern);

View File

@@ -106,6 +106,8 @@ SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
case color_mode::never:
should_do_colors_ = false;
return;
default:
should_do_colors_ = false;
}
}

View File

@@ -7,6 +7,7 @@
#include <spdlog/details/file_helper.h>
#include <spdlog/details/null_mutex.h>
#include <spdlog/fmt/fmt.h>
#include <spdlog/fmt/chrono.h>
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/os.h>
#include <spdlog/details/circular_q.h>
@@ -36,6 +37,23 @@ struct daily_filename_calculator
}
};
/*
* Generator of daily log file names with strftime format.
* Usages:
* auto sink = std::make_shared<spdlog::sinks::daily_file_format_sink_mt>("myapp-%Y-%m-%d:%H:%M:%S.log", hour, minute);"
* auto logger = spdlog::daily_logger_format_mt("loggername, "myapp-%Y-%m-%d:%X.log", hour, minute)"
*
*/
struct daily_filename_format_calculator
{
static filename_t calc_filename (const filename_t &filename, const tm &now_tm)
{
// generate fmt datetime format string, e.g. {:%Y-%m-%d}.
filename_t fmt_filename = fmt::format(SPDLOG_FILENAME_T ("{{:{}}}"), filename);
return fmt::format(fmt_filename, now_tm);
}
};
/*
* Rotating file sink based on date.
* If truncate != false , the created file will be truncated.
@@ -155,7 +173,7 @@ private:
using details::os::filename_to_str;
using details::os::remove_if_exists;
filename_t current_file = filename();
filename_t current_file = file_helper_.filename();
if (filenames_q_.full())
{
auto old_filename = std::move(filenames_q_.front());
@@ -182,6 +200,8 @@ private:
using daily_file_sink_mt = daily_file_sink<std::mutex>;
using daily_file_sink_st = daily_file_sink<details::null_mutex>;
using daily_file_format_sink_mt = daily_file_sink<std::mutex, daily_filename_format_calculator>;
using daily_file_format_sink_st = daily_file_sink<details::null_mutex, daily_filename_format_calculator>;
} // namespace sinks
@@ -195,10 +215,24 @@ inline std::shared_ptr<logger> daily_logger_mt(
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_format_mt(
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_format_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, uint16_t max_files = 0)
{
return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate, max_files);
}
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> daily_logger_format_st(
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_format_sink_st>(logger_name, filename, hour, minute, truncate, max_files);
}
} // namespace spdlog

View File

@@ -64,7 +64,7 @@ protected:
{
memory_buf_t buf;
fmt::format_to(buf, "Skipped {} duplicate messages..", skip_counter_);
details::log_msg skipped_msg{msg.logger_name, msg.level, string_view_t{buf.data(), buf.size()}};
details::log_msg skipped_msg{msg.logger_name, level::info, string_view_t{buf.data(), buf.size()}};
dist_sink<Mutex>::sink_it_(skipped_msg);
}

View File

@@ -0,0 +1,194 @@
// 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/details/file_helper.h>
#include <spdlog/details/null_mutex.h>
#include <spdlog/fmt/fmt.h>
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/os.h>
#include <spdlog/details/circular_q.h>
#include <spdlog/details/synchronous_factory.h>
#include <chrono>
#include <cstdio>
#include <ctime>
#include <mutex>
#include <string>
namespace spdlog {
namespace sinks {
/*
* Generator of Hourly log file names in format basename.YYYY-MM-DD-HH.ext
*/
struct hourly_filename_calculator
{
// Create filename for the form basename.YYYY-MM-DD-H
static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
{
filename_t basename, ext;
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
return fmt::format(
SPDLOG_FILENAME_T("{}_{:04d}{:02d}{:02d}_{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, now_tm.tm_hour, ext);
}
};
/*
* Rotating file sink based on time.
* If truncate != false , the created file will be truncated.
* If max_files > 0, retain only the last max_files and delete previous.
*/
template<typename Mutex, typename FileNameCalc = hourly_filename_calculator>
class hourly_file_sink final : public base_sink<Mutex>
{
public:
// create hourly file sink which rotates on given time
hourly_file_sink(filename_t base_filename, bool truncate = false, uint16_t max_files = 0)
: base_filename_(std::move(base_filename))
, truncate_(truncate)
, max_files_(max_files)
, filenames_q_()
{
auto now = log_clock::now();
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
file_helper_.open(filename, truncate_);
rotation_tp_ = next_rotation_tp_();
if (max_files_ > 0)
{
init_filenames_q_();
}
}
filename_t filename()
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
return file_helper_.filename();
}
protected:
void sink_it_(const details::log_msg &msg) override
{
auto time = msg.time;
bool should_rotate = time >= rotation_tp_;
if (should_rotate)
{
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));
file_helper_.open(filename, truncate_);
rotation_tp_ = next_rotation_tp_();
}
memory_buf_t formatted;
base_sink<Mutex>::formatter_->format(msg, formatted);
file_helper_.write(formatted);
// Do the cleaning only at the end because it might throw on failure.
if (should_rotate && max_files_ > 0)
{
delete_old_();
}
}
void flush_() override
{
file_helper_.flush();
}
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(1);
}
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);
return spdlog::details::os::localtime(tnow);
}
log_clock::time_point next_rotation_tp_()
{
auto now = log_clock::now();
tm date = now_tm(now);
date.tm_min = 0;
date.tm_sec = 0;
auto rotation_time = log_clock::from_time_t(std::mktime(&date));
if (rotation_time > now)
{
return rotation_time;
}
return {rotation_time + std::chrono::hours(1)};
}
// Delete the file N rotations ago.
// Throw spdlog_ex on failure to delete the old file.
void delete_old_()
{
using details::os::filename_to_str;
using details::os::remove_if_exists;
filename_t current_file = file_helper_.filename();
if (filenames_q_.full())
{
auto old_filename = std::move(filenames_q_.front());
filenames_q_.pop_front();
bool ok = remove_if_exists(old_filename) == 0;
if (!ok)
{
filenames_q_.push_back(std::move(current_file));
SPDLOG_THROW(spdlog_ex("Failed removing hourly file " + filename_to_str(old_filename), errno));
}
}
filenames_q_.push_back(std::move(current_file));
}
filename_t base_filename_;
log_clock::time_point rotation_tp_;
details::file_helper file_helper_;
bool truncate_;
uint16_t max_files_;
details::circular_q<filename_t> filenames_q_;
};
using hourly_file_sink_mt = hourly_file_sink<std::mutex>;
using hourly_file_sink_st = hourly_file_sink<details::null_mutex>;
} // namespace sinks
//
// factory functions
//
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> hourly_logger_mt(
const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0)
{
return Factory::template create<sinks::hourly_file_sink_mt>(logger_name, filename, truncate, max_files);
}
template<typename Factory = spdlog::synchronous_factory>
inline std::shared_ptr<logger> hourly_logger_st(
const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0)
{
return Factory::template create<sinks::hourly_file_sink_st>(logger_name, filename, truncate, max_files);
}
} // namespace spdlog

View File

@@ -8,12 +8,13 @@
#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>
// Avoid including windows.h (https://stackoverflow.com/a/30741042)
extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char *lpOutputString);
namespace spdlog {
namespace sinks {
/*
@@ -23,12 +24,11 @@ template<typename Mutex>
class msvc_sink : public base_sink<Mutex>
{
public:
explicit msvc_sink() {}
msvc_sink() = default;
protected:
void sink_it_(const details::log_msg &msg) override
{
memory_buf_t formatted;
base_sink<Mutex>::formatter_->format(msg, formatted);
OutputDebugStringA(fmt::to_string(formatted).c_str());

View File

@@ -28,10 +28,11 @@ public:
std::vector<details::log_msg_buffer> last_raw(size_t lim = 0)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
auto n_items = lim > 0 ? (std::min)(lim, q_.size()) : q_.size();
auto items_available = q_.size();
auto n_items = lim > 0 ? (std::min)(lim, items_available) : items_available;
std::vector<details::log_msg_buffer> ret;
ret.reserve(n_items);
for (size_t i = 0; i < n_items; i++)
for (size_t i = (items_available - n_items); i < items_available; i++)
{
ret.push_back(q_.at(i));
}
@@ -41,10 +42,11 @@ public:
std::vector<std::string> last_formatted(size_t lim = 0)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
auto n_items = lim > 0 ? (std::min)(lim, q_.size()) : q_.size();
auto items_available = q_.size();
auto n_items = lim > 0 ? (std::min)(lim, items_available) : items_available;
std::vector<std::string> ret;
ret.reserve(n_items);
for (size_t i = 0; i < n_items; i++)
for (size_t i = (items_available - n_items); i < items_available; i++)
{
memory_buf_t formatted;
base_sink<Mutex>::formatter_->format(q_.at(i), formatted);

View File

@@ -11,6 +11,15 @@
#include <spdlog/pattern_formatter.h>
#include <memory>
#ifdef _WIN32
// under windows using fwrite to non-binary stream results in \r\r\n (see issue #1675)
// so instead we use ::FileWrite
#include <spdlog/details/windows_include.h>
#include <fileapi.h> // WriteFile (..)
#include <io.h> // _get_osfhandle(..)
#include <stdio.h> // _fileno(..)
#endif // WIN32
namespace spdlog {
namespace sinks {
@@ -20,16 +29,48 @@ SPDLOG_INLINE stdout_sink_base<ConsoleMutex>::stdout_sink_base(FILE *file)
: mutex_(ConsoleMutex::mutex())
, file_(file)
, formatter_(details::make_unique<spdlog::pattern_formatter>())
{}
{
#ifdef _WIN32
// get windows handle from the FILE* object
handle_ = (HANDLE)::_get_osfhandle(::_fileno(file_));
// don't throw to support cases where no console is attached,
// and let the log method to do nothing if (handle_ == INVALID_HANDLE_VALUE).
// throw only if non stdout/stderr target is requested (probably regular file and not console).
if (handle_ == INVALID_HANDLE_VALUE && file != stdout && file != stderr)
{
throw_spdlog_ex("spdlog::stdout_sink_base: _get_osfhandle() failed", errno);
}
#endif // WIN32
}
template<typename ConsoleMutex>
SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &msg)
{
#ifdef _WIN32
if (handle_ == INVALID_HANDLE_VALUE)
{
return;
}
std::lock_guard<mutex_t> lock(mutex_);
memory_buf_t formatted;
formatter_->format(msg, formatted);
fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
fflush(file_); // flush every line to terminal
::fflush(file_); // flush in case there is somthing in this file_ already
auto size = static_cast<DWORD>(formatted.size());
DWORD bytes_written = 0;
bool ok = ::WriteFile(handle_, formatted.data(), size, &bytes_written, nullptr) != 0;
if (!ok)
{
throw_spdlog_ex("stdout_sink_base: WriteFile() failed. GetLastError(): " + std::to_string(::GetLastError()));
}
#else
std::lock_guard<mutex_t> lock(mutex_);
memory_buf_t formatted;
formatter_->format(msg, formatted);
::fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
::fflush(file_); // flush every line to terminal
#endif // WIN32
}
template<typename ConsoleMutex>

View File

@@ -8,6 +8,10 @@
#include <spdlog/sinks/sink.h>
#include <cstdio>
#ifdef _WIN32
#include <spdlog/details/windows_include.h>
#endif
namespace spdlog {
namespace sinks {
@@ -36,6 +40,9 @@ protected:
mutex_t &mutex_;
FILE *file_;
std::unique_ptr<spdlog::formatter> formatter_;
#ifdef _WIN32
HANDLE handle_;
#endif // WIN32
};
template<typename ConsoleMutex>

View File

@@ -57,7 +57,7 @@ struct win32_error : public spdlog_ex
LPSTR format_message_result{};
auto format_message_succeeded =
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
::FormatMessageA(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)
@@ -203,7 +203,7 @@ private:
{
if (!hEventLog_)
{
hEventLog_ = ::RegisterEventSource(nullptr, source_.c_str());
hEventLog_ = ::RegisterEventSourceA(nullptr, source_.c_str());
if (!hEventLog_ || hEventLog_ == (HANDLE)ERROR_ACCESS_DENIED)
{
SPDLOG_THROW(internal::win32_error("RegisterEventSource"));
@@ -218,13 +218,23 @@ protected:
{
using namespace internal;
bool succeeded;
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);
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
wmemory_buf_t buf;
details::os::utf8_to_wstrbuf(string_view_t(formatted.data(), formatted.size()), buf);
LPCWSTR lp_wstr = buf.data();
succeeded = ::ReportEventW(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_,
current_user_sid_.as_sid(), 1, 0, &lp_wstr, nullptr);
#else
LPCSTR lp_str = formatted.data();
succeeded = ::ReportEventA(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);
#endif
if (!succeeded)
{

View File

@@ -7,30 +7,30 @@
#include <spdlog/sinks/wincolor_sink.h>
#endif
#include <spdlog/details/windows_include.h>
#include <wincon.h>
#include <spdlog/common.h>
#include <spdlog/pattern_formatter.h>
namespace spdlog {
namespace sinks {
template<typename ConsoleMutex>
SPDLOG_INLINE wincolor_sink<ConsoleMutex>::wincolor_sink(HANDLE out_handle, color_mode mode)
SPDLOG_INLINE wincolor_sink<ConsoleMutex>::wincolor_sink(void *out_handle, color_mode mode)
: out_handle_(out_handle)
, mutex_(ConsoleMutex::mutex())
, formatter_(details::make_unique<spdlog::pattern_formatter>())
{
// check if out_handle is points to the actual console.
// ::GetConsoleMode() should return 0 if it is redirected or not valid console handle.
DWORD console_mode;
in_console_ = ::GetConsoleMode(out_handle, &console_mode) != 0;
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; // red bold
colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background
set_color_mode_impl(mode);
// set level colors
colors_[level::trace] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; // white
colors_[level::debug] = FOREGROUND_GREEN | FOREGROUND_BLUE; // cyan
colors_[level::info] = FOREGROUND_GREEN; // green
colors_[level::warn] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; // intense yellow
colors_[level::err] = FOREGROUND_RED | FOREGROUND_INTENSITY; // intense red
colors_[level::critical] =
BACKGROUND_RED | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; // intense white on red background
colors_[level::off] = 0;
}
@@ -42,7 +42,7 @@ SPDLOG_INLINE wincolor_sink<ConsoleMutex>::~wincolor_sink()
// change the color for the given level
template<typename ConsoleMutex>
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color(level::level_enum level, WORD color)
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color(level::level_enum level, std::uint16_t color)
{
std::lock_guard<mutex_t> lock(mutex_);
colors_[level] = color;
@@ -51,31 +51,30 @@ void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color(level::level_enum leve
template<typename ConsoleMutex>
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
{
if (out_handle_ == nullptr || out_handle_ == INVALID_HANDLE_VALUE)
{
return;
}
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_)
{
write_to_file_(formatted);
return;
}
if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
{
// before color range
print_range_(formatted, 0, msg.color_range_start);
// in color range
auto orig_attribs = set_foreground_color_(colors_[msg.level]);
auto orig_attribs = static_cast<WORD>(set_foreground_color_(colors_[msg.level]));
print_range_(formatted, msg.color_range_start, msg.color_range_end);
// reset to orig colors
::SetConsoleTextAttribute(out_handle_, orig_attribs);
::SetConsoleTextAttribute(static_cast<HANDLE>(out_handle_), orig_attribs);
print_range_(formatted, msg.color_range_end, formatted.size());
}
else // print without colors if color range is invalid (or color is disabled)
{
print_range_(formatted, 0, formatted.size());
write_to_file_(formatted);
}
}
@@ -102,66 +101,63 @@ void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_formatter(std::unique_ptr<sp
template<typename ConsoleMutex>
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
{
switch (mode)
std::lock_guard<mutex_t> lock(mutex_);
set_color_mode_impl(mode);
}
template<typename ConsoleMutex>
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color_mode_impl(color_mode mode)
{
if (mode == color_mode::automatic)
{
case color_mode::always:
case color_mode::automatic:
should_do_colors_ = true;
break;
case color_mode::never:
should_do_colors_ = false;
break;
default:
should_do_colors_ = true;
// should do colors only if out_handle_ points to actual console.
DWORD console_mode;
bool in_console = ::GetConsoleMode(static_cast<HANDLE>(out_handle_), &console_mode) != 0;
should_do_colors_ = in_console;
}
else
{
should_do_colors_ = mode == color_mode::always ? true : false;
}
}
// set foreground color and return the orig console attributes (for resetting later)
template<typename ConsoleMutex>
WORD SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_foreground_color_(WORD attribs)
std::uint16_t SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_foreground_color_(std::uint16_t attribs)
{
CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
::GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info);
WORD back_color = orig_buffer_info.wAttributes;
// retrieve the current background color
back_color &= static_cast<WORD>(~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY));
// keep the background color unchanged
::SetConsoleTextAttribute(out_handle_, attribs | back_color);
return orig_buffer_info.wAttributes; // return orig attribs
if (!::GetConsoleScreenBufferInfo(static_cast<HANDLE>(out_handle_), &orig_buffer_info))
{
// just return white if failed getting console info
return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
}
// change only the foreground bits (lowest 4 bits)
auto new_attribs = static_cast<WORD>(attribs) | (orig_buffer_info.wAttributes & 0xfff0);
auto ignored = ::SetConsoleTextAttribute(static_cast<HANDLE>(out_handle_), static_cast<WORD>(new_attribs));
(void)(ignored);
return static_cast<std::uint16_t>(orig_buffer_info.wAttributes); // return orig attribs
}
// print a range of formatted message to console
template<typename ConsoleMutex>
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::print_range_(const memory_buf_t &formatted, size_t start, size_t end)
{
auto size = static_cast<DWORD>(end - start);
::WriteConsoleA(out_handle_, formatted.data() + start, size, nullptr, nullptr);
if (end > start)
{
auto size = static_cast<DWORD>(end - start);
auto ignored = ::WriteConsoleA(static_cast<HANDLE>(out_handle_), formatted.data() + start, size, nullptr, nullptr);
(void)(ignored);
}
}
template<typename ConsoleMutex>
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::write_to_file_(const memory_buf_t &formatted)
{
if (out_handle_ == nullptr) // no console and no file redirect
{
return;
}
auto size = static_cast<DWORD>(formatted.size());
if (size == 0)
{
return;
}
DWORD total_written = 0;
do
{
DWORD bytes_written = 0;
bool ok = ::WriteFile(out_handle_, formatted.data() + total_written, size - total_written, &bytes_written, nullptr) != 0;
if (!ok || bytes_written == 0)
{
throw_spdlog_ex("wincolor_sink: write_to_file_ failed. GetLastError(): " + std::to_string(::GetLastError()));
}
total_written += bytes_written;
} while (total_written < size);
DWORD bytes_written = 0;
auto ignored = ::WriteFile(static_cast<HANDLE>(out_handle_), formatted.data(), size, &bytes_written, nullptr);
(void)(ignored);
}
// wincolor_stdout_sink
@@ -175,6 +171,5 @@ template<typename ConsoleMutex>
SPDLOG_INLINE wincolor_stderr_sink<ConsoleMutex>::wincolor_stderr_sink(color_mode mode)
: wincolor_sink<ConsoleMutex>(::GetStdHandle(STD_ERROR_HANDLE), mode)
{}
} // namespace sinks
} // namespace spdlog

View File

@@ -12,9 +12,7 @@
#include <mutex>
#include <string>
#include <array>
#include <spdlog/details/windows_include.h>
#include <wincon.h>
#include <cstdint>
namespace spdlog {
namespace sinks {
@@ -25,22 +23,15 @@ namespace sinks {
template<typename ConsoleMutex>
class wincolor_sink : public sink
{
public:
const WORD BOLD = FOREGROUND_INTENSITY;
const WORD RED = FOREGROUND_RED;
const WORD GREEN = FOREGROUND_GREEN;
const WORD CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE;
const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN;
wincolor_sink(HANDLE out_handle, color_mode mode);
public:
wincolor_sink(void *out_handle, color_mode mode);
~wincolor_sink() override;
wincolor_sink(const wincolor_sink &other) = delete;
wincolor_sink &operator=(const wincolor_sink &other) = delete;
// change the color for the given level
void set_color(level::level_enum level, WORD color);
void set_color(level::level_enum level, std::uint16_t color);
void log(const details::log_msg &msg) final override;
void flush() final override;
void set_pattern(const std::string &pattern) override final;
@@ -49,21 +40,22 @@ public:
protected:
using mutex_t = typename ConsoleMutex::mutex_t;
HANDLE out_handle_;
mutex_t &mutex_;
bool in_console_;
void *out_handle_;
mutex_t &mutex_;
bool should_do_colors_;
std::unique_ptr<spdlog::formatter> formatter_;
std::array<WORD, level::n_levels> colors_;
std::array<std::uint16_t, level::n_levels> colors_;
// set foreground color and return the orig console attributes (for resetting later)
WORD set_foreground_color_(WORD attribs);
std::uint16_t set_foreground_color_(std::uint16_t attribs);
// print a range of formatted message to console
void print_range_(const memory_buf_t &formatted, size_t start, size_t end);
// in case we are redirected to file (not in console mode)
void write_to_file_(const memory_buf_t &formatted);
void set_color_mode_impl(color_mode mode);
};
template<typename ConsoleMutex>
@@ -85,7 +77,6 @@ using wincolor_stdout_sink_st = wincolor_stdout_sink<details::console_nullmutex>
using wincolor_stderr_sink_mt = wincolor_stderr_sink<details::console_mutex>;
using wincolor_stderr_sink_st = wincolor_stderr_sink<details::console_nullmutex>;
} // namespace sinks
} // namespace spdlog

View File

@@ -47,6 +47,16 @@ SPDLOG_INLINE void dump_backtrace()
default_logger_raw()->dump_backtrace();
}
SPDLOG_INLINE level::level_enum get_level()
{
return default_logger_raw()->level();
}
SPDLOG_INLINE bool should_log(level::level_enum log_level)
{
return default_logger_raw()->should_log(log_level);
}
SPDLOG_INLINE void set_level(level::level_enum log_level)
{
details::registry::instance().set_level(log_level);
@@ -112,4 +122,4 @@ SPDLOG_INLINE void set_default_logger(std::shared_ptr<spdlog::logger> default_lo
details::registry::instance().set_default_logger(std::move(default_logger));
}
} // namespace spdlog
} // namespace spdlog

View File

@@ -31,7 +31,7 @@ using default_factory = synchronous_factory;
// Example:
// spdlog::create<daily_file_sink_st>("logger_name", "dailylog_filename", 11, 59);
template<typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... sink_args)
inline std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&...sink_args)
{
return default_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
}
@@ -67,9 +67,15 @@ SPDLOG_API void disable_backtrace();
// call dump backtrace on default logger
SPDLOG_API void dump_backtrace();
// Get global logging level
SPDLOG_API level::level_enum get_level();
// Set global logging level
SPDLOG_API void set_level(level::level_enum log_level);
// Determine whether the default logger should log messages with a certain level
SPDLOG_API bool should_log(level::level_enum lvl);
// Set global flush level
SPDLOG_API void flush_on(level::level_enum log_level);
@@ -105,7 +111,7 @@ SPDLOG_API void set_automatic_registration(bool automatic_registration);
//
// The default logger object can be accessed using the spdlog::default_logger():
// For example, to add another sink to it:
// spdlog::default_logger()->sinks()->push_back(some_sink);
// spdlog::default_logger()->sinks().push_back(some_sink);
//
// The default logger can replaced using spdlog::set_default_logger(new_logger).
// For example, to replace it with a file logger.
@@ -121,52 +127,52 @@ SPDLOG_API spdlog::logger *default_logger_raw();
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)
template<typename FormatString, typename... Args>
inline void log(source_loc source, level::level_enum lvl, const FormatString &fmt, Args&&...args)
{
default_logger_raw()->log(source, lvl, fmt, args...);
default_logger_raw()->log(source, lvl, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
inline void log(level::level_enum lvl, string_view_t fmt, const Args &... args)
template<typename FormatString, typename... Args>
inline void log(level::level_enum lvl, const FormatString &fmt, Args&&...args)
{
default_logger_raw()->log(source_loc{}, lvl, fmt, args...);
default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
}
template<typename... Args>
inline void trace(string_view_t fmt, const Args &... args)
template<typename FormatString, typename... Args>
inline void trace(const FormatString &fmt, Args&&...args)
{
default_logger_raw()->trace(fmt, args...);
default_logger_raw()->trace(fmt, std::forward<Args>(args)...);
}
template<typename... Args>
inline void debug(string_view_t fmt, const Args &... args)
template<typename FormatString, typename... Args>
inline void debug(const FormatString &fmt, Args&&...args)
{
default_logger_raw()->debug(fmt, args...);
default_logger_raw()->debug(fmt, std::forward<Args>(args)...);
}
template<typename... Args>
inline void info(string_view_t fmt, const Args &... args)
template<typename FormatString, typename... Args>
inline void info(const FormatString &fmt, Args&&...args)
{
default_logger_raw()->info(fmt, args...);
default_logger_raw()->info(fmt, std::forward<Args>(args)...);
}
template<typename... Args>
inline void warn(string_view_t fmt, const Args &... args)
template<typename FormatString, typename... Args>
inline void warn(const FormatString &fmt, Args&&...args)
{
default_logger_raw()->warn(fmt, args...);
default_logger_raw()->warn(fmt, std::forward<Args>(args)...);
}
template<typename... Args>
inline void error(string_view_t fmt, const Args &... args)
template<typename FormatString, typename... Args>
inline void error(const FormatString &fmt, Args&&...args)
{
default_logger_raw()->error(fmt, args...);
default_logger_raw()->error(fmt, std::forward<Args>(args)...);
}
template<typename... Args>
inline void critical(string_view_t fmt, const Args &... args)
template<typename FormatString, typename... Args>
inline void critical(const FormatString &fmt, Args&&...args)
{
default_logger_raw()->critical(fmt, args...);
default_logger_raw()->critical(fmt, std::forward<Args>(args)...);
}
template<typename T>
@@ -217,57 +223,6 @@ inline void critical(const T &msg)
default_logger_raw()->critical(msg);
}
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
template<typename... Args>
inline void log(source_loc source, level::level_enum lvl, wstring_view_t fmt, const Args &... args)
{
default_logger_raw()->log(source, lvl, fmt, args...);
}
template<typename... Args>
inline void log(level::level_enum lvl, wstring_view_t fmt, const Args &... args)
{
default_logger_raw()->log(lvl, fmt, args...);
}
template<typename... Args>
inline void trace(wstring_view_t fmt, const Args &... args)
{
default_logger_raw()->trace(fmt, args...);
}
template<typename... Args>
inline void debug(wstring_view_t fmt, const Args &... args)
{
default_logger_raw()->debug(fmt, args...);
}
template<typename... Args>
inline void info(wstring_view_t fmt, const Args &... args)
{
default_logger_raw()->info(fmt, args...);
}
template<typename... Args>
inline void warn(wstring_view_t fmt, const Args &... args)
{
default_logger_raw()->warn(fmt, args...);
}
template<typename... Args>
inline void error(wstring_view_t fmt, const Args &... args)
{
default_logger_raw()->error(fmt, args...);
}
template<typename... Args>
inline void critical(wstring_view_t fmt, const Args &... args)
{
default_logger_raw()->critical(fmt, args...);
}
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
} // namespace spdlog
//

View File

@@ -0,0 +1,61 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/fmt/fmt.h>
// Stopwatch support for spdlog (using std::chrono::steady_clock).
// Displays elapsed seconds since construction as double.
//
// Usage:
//
// spdlog::stopwatch sw;
// ...
// spdlog::debug("Elapsed: {} seconds", sw); => "Elapsed 0.005116733 seconds"
// spdlog::info("Elapsed: {:.6} seconds", sw); => "Elapsed 0.005163 seconds"
//
//
// If other units are needed (e.g. millis instead of double), include "fmt/chrono.h" and use "duration_cast<..>(sw.elapsed())":
//
// #include <spdlog/fmt/chrono.h>
//..
// using std::chrono::duration_cast;
// using std::chrono::milliseconds;
// spdlog::info("Elapsed {}", duration_cast<milliseconds>(sw.elapsed())); => "Elapsed 5ms"
namespace spdlog {
class stopwatch
{
using clock = std::chrono::steady_clock;
std::chrono::time_point<clock> start_tp_;
public:
stopwatch()
: start_tp_{clock::now()}
{}
std::chrono::duration<double> elapsed() const
{
return std::chrono::duration<double>(clock::now() - start_tp_);
}
void reset()
{
start_tp_ = clock ::now();
}
};
} // namespace spdlog
// Support for fmt formatting (e.g. "{:012.9}" or just "{}")
namespace fmt {
template<>
struct formatter<spdlog::stopwatch> : formatter<double>
{
template<typename FormatContext>
auto format(const spdlog::stopwatch &sw, FormatContext &ctx) -> decltype(ctx.out())
{
return formatter<double>::format(sw.elapsed().count(), ctx);
}
};
} // namespace fmt

View File

@@ -58,6 +58,14 @@
// #define SPDLOG_EOL ";-)\n"
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to override default folder separators ("/" or "\\/" under
// Linux/Windows). Each character in the string is treated as a different
// separator.
//
// #define SPDLOG_FOLDER_SEPS "\\"
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to use your own copy of the fmt library instead of spdlog's copy.
// In this case spdlog will try to include <fmt/format.h> so set your -I flag

View File

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

View File

@@ -9,181 +9,95 @@
#if !defined(SPDLOG_FMT_EXTERNAL)
#include <spdlog/fmt/bundled/format-inl.h>
FMT_BEGIN_NAMESPACE
namespace internal {
namespace detail {
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");
template <typename T>
int format_float(char* buf, std::size_t size, const char* format, int precision,
T value) {
#ifdef FMT_FUZZ
if (precision > 100000)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
#endif
// 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);
// 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 FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x)
FMT_NOEXCEPT;
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x)
FMT_NOEXCEPT;
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");
// 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;
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';
}
#endif
*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);
}
}
}
}
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;
// DEPRECATED! This function exists for ABI compatibility.
template <typename Char>
typename basic_format_context<std::back_insert_iterator<buffer<Char>>,
Char>::iterator
vformat_to(buffer<Char>& buf, basic_string_view<Char> format_str,
basic_format_args<basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
type_identity_t<Char>>>
args) {
using iterator = std::back_insert_iterator<buffer<char>>;
using context = basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
type_identity_t<Char>>;
auto out = iterator(buf);
format_handler<iterator, Char, context> h(out, format_str, args, {});
parse_format_string<false>(format_str, h);
return out;
}
} // namespace internal
template basic_format_context<std::back_insert_iterator<buffer<char>>,
char>::iterator
vformat_to(buffer<char>&, string_view,
basic_format_args<basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<char>>>,
type_identity_t<char>>>);
} // namespace detail
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_INSTANTIATION_DEF_API internal::basic_data<void>;
template struct FMT_INSTANTIATION_DEF_API detail::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, detail::float_specs,
detail::buffer<char>&) = detail::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 detail::locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale detail::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 detail::grouping_impl<char>(locale_ref);
template FMT_API char detail::thousands_sep_impl(locale_ref);
template FMT_API char detail::decimal_point_impl(locale_ref);
template FMT_API void internal::buffer<char>::append(const char *, const char *);
template FMT_API void detail::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 detail::vformat_to(
detail::buffer<char>&, string_view,
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
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 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 detail::snprintf_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::snprintf_float(long double, int,
detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(long double, int, detail::float_specs,
detail::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 detail::grouping_impl<wchar_t>(locale_ref);
template FMT_API wchar_t detail::thousands_sep_impl(locale_ref);
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
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 void detail::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
FMT_END_NAMESPACE
#endif // !SPDLOG_FMT_EXTERNAL
#endif // !SPDLOG_FMT_EXTERNAL

View File

@@ -1,5 +1,4 @@
cmake_minimum_required(VERSION 3.2)
cmake_minimum_required(VERSION 3.10)
project(spdlog_utests CXX)
if(NOT TARGET spdlog)
@@ -33,7 +32,8 @@ set(SPDLOG_UTESTS_SOURCES
test_backtrace.cpp
test_create_dir.cpp
test_cfg.cpp
test_time_point.cpp)
test_time_point.cpp
test_stopwatch.cpp)
if(NOT SPDLOG_NO_EXCEPTIONS)
list(APPEND SPDLOG_UTESTS_SOURCES test_errors.cpp)

View File

@@ -3,6 +3,8 @@
#include "spdlog/sinks/basic_file_sink.h"
#include "test_sink.h"
#define TEST_FILENAME "test_logs/async_test.log"
TEST_CASE("basic async test ", "[async]")
{
auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
@@ -87,7 +89,7 @@ TEST_CASE("async periodic flush", "[async]")
auto test_sink = std::static_pointer_cast<spdlog::sinks::test_sink_mt>(logger->sinks()[0]);
spdlog::flush_every(std::chrono::seconds(1));
std::this_thread::sleep_for(std::chrono::milliseconds(1100));
std::this_thread::sleep_for(std::chrono::milliseconds(1700));
REQUIRE(test_sink->flush_counter() == 1);
spdlog::flush_every(std::chrono::seconds(0));
spdlog::drop_all();
@@ -149,7 +151,7 @@ TEST_CASE("to_file", "[async]")
prepare_logdir();
size_t messages = 1024;
size_t tp_threads = 1;
std::string filename = "test_logs/async_test.log";
spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME);
{
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
auto tp = std::make_shared<spdlog::details::thread_pool>(messages, tp_threads);
@@ -161,8 +163,8 @@ TEST_CASE("to_file", "[async]")
}
}
require_message_count(filename, messages);
auto contents = file_contents(filename);
require_message_count(TEST_FILENAME, messages);
auto contents = file_contents(TEST_FILENAME);
using spdlog::details::os::default_eol;
REQUIRE(ends_with(contents, fmt::format("Hello message #1023{}", default_eol)));
}
@@ -172,7 +174,7 @@ TEST_CASE("to_file multi-workers", "[async]")
prepare_logdir();
size_t messages = 1024 * 10;
size_t tp_threads = 10;
std::string filename = "test_logs/async_test.log";
spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME);
{
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
auto tp = std::make_shared<spdlog::details::thread_pool>(messages, tp_threads);
@@ -184,5 +186,5 @@ TEST_CASE("to_file multi-workers", "[async]")
}
}
require_message_count(filename, messages);
require_message_count(TEST_FILENAME, messages);
}

View File

@@ -1,3 +1,4 @@
#include "includes.h"
#include "test_sink.h"
@@ -41,21 +42,23 @@ TEST_CASE("argv2", "[cfg]")
auto l1 = spdlog::create<test_sink_st>("l1");
REQUIRE(l1->level() == spdlog::level::warn);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::trace);
spdlog::set_level(spdlog::level::info);
}
TEST_CASE("argv3", "[cfg]")
{
spdlog::set_level(spdlog::level::trace);
spdlog::drop("l1");
const char *argv[] = {"ignore", "SPDLOG_LEVEL="};
const char *argv[] = {"ignore", "SPDLOG_LEVEL=junk_name=warn"};
load_argv_levels(2, argv);
auto l1 = spdlog::create<test_sink_st>("l1");
REQUIRE(l1->level() == spdlog::level::info);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
REQUIRE(l1->level() == spdlog::level::trace);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::trace);
}
TEST_CASE("argv4", "[cfg]")
{
spdlog::set_level(spdlog::level::info);
spdlog::drop("l1");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=junk"};
load_argv_levels(2, argv);
@@ -65,6 +68,7 @@ TEST_CASE("argv4", "[cfg]")
TEST_CASE("argv5", "[cfg]")
{
spdlog::set_level(spdlog::level::info);
spdlog::drop("l1");
const char *argv[] = {"ignore", "ignore", "SPDLOG_LEVEL=l1=warn,trace"};
load_argv_levels(3, argv);
@@ -91,3 +95,89 @@ TEST_CASE("argv7", "[cfg]")
REQUIRE(spdlog::default_logger()->level() == spdlog::level::err);
spdlog::set_level(spdlog::level::info);
}
TEST_CASE("level-not-set-test1", "[cfg]")
{
spdlog::drop("l1");
const char *argv[] = {"ignore", ""};
load_argv_levels(2, argv);
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
l1->set_level(spdlog::level::trace);
REQUIRE(l1->level() == spdlog::level::trace);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
}
TEST_CASE("level-not-set-test2", "[cfg]")
{
spdlog::drop("l1");
spdlog::drop("l2");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=trace"};
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
l1->set_level(spdlog::level::warn);
auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
l2->set_level(spdlog::level::warn);
load_argv_levels(2, argv);
REQUIRE(l1->level() == spdlog::level::trace);
REQUIRE(l2->level() == spdlog::level::warn);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
}
TEST_CASE("level-not-set-test3", "[cfg]")
{
spdlog::drop("l1");
spdlog::drop("l2");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=trace"};
load_argv_levels(2, argv);
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
REQUIRE(l1->level() == spdlog::level::trace);
REQUIRE(l2->level() == spdlog::level::info);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
}
TEST_CASE("level-not-set-test4", "[cfg]")
{
spdlog::drop("l1");
spdlog::drop("l2");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=trace,warn"};
load_argv_levels(2, argv);
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
REQUIRE(l1->level() == spdlog::level::trace);
REQUIRE(l2->level() == spdlog::level::warn);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::warn);
}
TEST_CASE("level-not-set-test5", "[cfg]")
{
spdlog::drop("l1");
spdlog::drop("l2");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=junk,warn"};
load_argv_levels(2, argv);
auto l1 = spdlog::create<spdlog::sinks::test_sink_st>("l1");
auto l2 = spdlog::create<spdlog::sinks::test_sink_st>("l2");
REQUIRE(l1->level() == spdlog::level::warn);
REQUIRE(l2->level() == spdlog::level::warn);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::warn);
}
TEST_CASE("restore-to-default", "[cfg]")
{
spdlog::drop("l1");
spdlog::drop("l2");
const char *argv[] = {"ignore", "SPDLOG_LEVEL=info"};
load_argv_levels(2, argv);
REQUIRE(spdlog::default_logger()->level() == spdlog::level::info);
}

View File

@@ -6,7 +6,7 @@
using spdlog::details::os::create_dir;
using spdlog::details::os::path_exists;
bool try_create_dir(const char *path, const char *normalized_path)
bool try_create_dir(const spdlog::filename_t &path, const spdlog::filename_t &normalized_path)
{
auto rv = create_dir(path);
REQUIRE(rv == true);
@@ -17,24 +17,24 @@ TEST_CASE("create_dir", "[create_dir]")
{
prepare_logdir();
REQUIRE(try_create_dir("test_logs/dir1/dir1", "test_logs/dir1/dir1"));
REQUIRE(try_create_dir("test_logs/dir1/dir1", "test_logs/dir1/dir1")); // test existing
REQUIRE(try_create_dir("test_logs/dir1///dir2//", "test_logs/dir1/dir2"));
REQUIRE(try_create_dir("./test_logs/dir1/dir3", "test_logs/dir1/dir3"));
REQUIRE(try_create_dir("test_logs/../test_logs/dir1/dir4", "test_logs/dir1/dir4"));
REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs/dir1/dir1"), SPDLOG_FILENAME_T("test_logs/dir1/dir1")));
REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs/dir1/dir1"), SPDLOG_FILENAME_T("test_logs/dir1/dir1"))); // test existing
REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs/dir1///dir2//"), SPDLOG_FILENAME_T("test_logs/dir1/dir2")));
REQUIRE(try_create_dir(SPDLOG_FILENAME_T("./test_logs/dir1/dir3"), SPDLOG_FILENAME_T("test_logs/dir1/dir3")));
REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs/../test_logs/dir1/dir4"), SPDLOG_FILENAME_T("test_logs/dir1/dir4")));
#ifdef WIN32
// test backslash folder separator
REQUIRE(try_create_dir("test_logs\\dir1\\dir222", "test_logs\\dir1\\dir222"));
REQUIRE(try_create_dir("test_logs\\dir1\\dir223\\", "test_logs\\dir1\\dir223\\"));
REQUIRE(try_create_dir(".\\test_logs\\dir1\\dir2\\dir99\\..\\dir23", "test_logs\\dir1\\dir2\\dir23"));
REQUIRE(try_create_dir("test_logs\\..\\test_logs\\dir1\\dir5", "test_logs\\dir1\\dir5"));
REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs\\dir1\\dir222"), SPDLOG_FILENAME_T("test_logs\\dir1\\dir222")));
REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs\\dir1\\dir223\\"), SPDLOG_FILENAME_T("test_logs\\dir1\\dir223\\")));
REQUIRE(try_create_dir(SPDLOG_FILENAME_T(".\\test_logs\\dir1\\dir2\\dir99\\..\\dir23"), SPDLOG_FILENAME_T("test_logs\\dir1\\dir2\\dir23")));
REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs\\..\\test_logs\\dir1\\dir5"), SPDLOG_FILENAME_T("test_logs\\dir1\\dir5")));
#endif
}
TEST_CASE("create_invalid_dir", "[create_dir]")
{
REQUIRE(create_dir("") == false);
REQUIRE(create_dir(SPDLOG_FILENAME_T("")) == false);
REQUIRE(create_dir(spdlog::filename_t{}) == false);
#ifdef __linux__
REQUIRE(create_dir("/proc/spdlog-utest") == false);
@@ -44,37 +44,31 @@ TEST_CASE("create_invalid_dir", "[create_dir]")
TEST_CASE("dir_name", "[create_dir]")
{
using spdlog::details::os::dir_name;
REQUIRE(dir_name("").empty());
REQUIRE(dir_name("dir").empty());
REQUIRE(dir_name(SPDLOG_FILENAME_T("")).empty());
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir")).empty());
#ifdef WIN32
REQUIRE(dir_name(R"(dir\)") == "dir");
REQUIRE(dir_name(R"(dir\\\)") == R"(dir\\)");
REQUIRE(dir_name(R"(dir\file)") == "dir");
REQUIRE(dir_name(R"(dir/file)") == "dir");
REQUIRE(dir_name(R"(dir\file.txt)") == "dir");
REQUIRE(dir_name(R"(dir/file)") == "dir");
REQUIRE(dir_name(R"(dir\file.txt\)") == R"(dir\file.txt)");
REQUIRE(dir_name(R"(dir/file.txt/)") == R"(dir\file.txt)");
REQUIRE(dir_name(R"(\dir\file.txt)") == R"(\dir)");
REQUIRE(dir_name(R"(/dir/file.txt)") == R"(\dir)");
REQUIRE(dir_name(R"(\\dir\file.txt)") == R"(\\dir)");
REQUIRE(dir_name(R"(//dir/file.txt)") == R"(\\dir)");
REQUIRE(dir_name(R"(..\file.txt)") == "..");
REQUIRE(dir_name(R"(../file.txt)") == "..");
REQUIRE(dir_name(R"(.\file.txt)") == ".");
REQUIRE(dir_name(R"(./file.txt)") == ".");
REQUIRE(dir_name(R"(c:\\a\b\c\d\file.txt)") == R"(c:\\a\b\c\d)");
REQUIRE(dir_name(R"(c://a/b/c/d/file.txt)") == R"(c:\\a\b\c\d)");
#else
REQUIRE(dir_name("dir/") == "dir");
REQUIRE(dir_name("dir///") == "dir//");
REQUIRE(dir_name("dir/file") == "dir");
REQUIRE(dir_name("dir/file.txt") == "dir");
REQUIRE(dir_name("dir/file.txt/") == "dir/file.txt");
REQUIRE(dir_name("/dir/file.txt") == "/dir");
REQUIRE(dir_name("//dir/file.txt") == "//dir");
REQUIRE(dir_name("../file.txt") == "..");
REQUIRE(dir_name("./file.txt") == ".");
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\)")) == SPDLOG_FILENAME_T("dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\\\)")) == SPDLOG_FILENAME_T(R"(dir\\)"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\file)")) == SPDLOG_FILENAME_T("dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir/file)")) == SPDLOG_FILENAME_T("dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\file.txt)")) == SPDLOG_FILENAME_T("dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir/file)")) == SPDLOG_FILENAME_T("dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\file.txt\)")) == SPDLOG_FILENAME_T(R"(dir\file.txt)"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(\dir\file.txt)")) == SPDLOG_FILENAME_T(R"(\dir)"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(\\dir\file.txt)")) == SPDLOG_FILENAME_T(R"(\\dir)"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(..\file.txt)")) == SPDLOG_FILENAME_T(".."));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(.\file.txt)")) == SPDLOG_FILENAME_T("."));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(c:\\a\b\c\d\file.txt)")) == SPDLOG_FILENAME_T(R"(c:\\a\b\c\d)"));
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(c://a/b/c/d/file.txt)")) == SPDLOG_FILENAME_T(R"(c://a/b/c/d)"));
#endif
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/")) == SPDLOG_FILENAME_T("dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir///")) == SPDLOG_FILENAME_T("dir//"));
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/file")) == SPDLOG_FILENAME_T("dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/file.txt")) == SPDLOG_FILENAME_T("dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/file.txt/")) == SPDLOG_FILENAME_T("dir/file.txt"));
REQUIRE(dir_name(SPDLOG_FILENAME_T("/dir/file.txt")) == SPDLOG_FILENAME_T("/dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T("//dir/file.txt")) == SPDLOG_FILENAME_T("//dir"));
REQUIRE(dir_name(SPDLOG_FILENAME_T("../file.txt")) == SPDLOG_FILENAME_T(".."));
REQUIRE(dir_name(SPDLOG_FILENAME_T("./file.txt")) == SPDLOG_FILENAME_T("."));
}

View File

@@ -3,6 +3,8 @@
*/
#include "includes.h"
using filename_memory_buf_t = fmt::basic_memory_buffer<spdlog::filename_t::value_type, 250>;
TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]")
{
using sink_type = spdlog::sinks::daily_file_sink<std::mutex, spdlog::sinks::daily_filename_calculator>;
@@ -10,10 +12,10 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]")
prepare_logdir();
// calculate filename (time based)
std::string basename = "test_logs/daily_dateonly";
spdlog::filename_t basename = SPDLOG_FILENAME_T("test_logs/daily_dateonly");
std::tm tm = spdlog::details::os::localtime();
spdlog::memory_buf_t w;
fmt::format_to(w, "{}_{:04d}-{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
filename_memory_buf_t w;
fmt::format_to(w, SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
for (int i = 0; i < 10; ++i)
@@ -23,7 +25,13 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]")
}
logger->flush();
#ifdef SPDLOG_WCHAR_FILENAMES
spdlog::memory_buf_t buf;
spdlog::details::os::wstr_to_utf8buf(fmt::to_string(w), buf);
auto filename = fmt::to_string(buf);
#else
auto filename = fmt::to_string(w);
#endif
require_message_count(filename, 10);
}
@@ -31,8 +39,8 @@ struct custom_daily_file_name_calculator
{
static spdlog::filename_t calc_filename(const spdlog::filename_t &basename, const tm &now_tm)
{
spdlog::memory_buf_t w;
fmt::format_to(w, "{}{:04d}{:02d}{:02d}", basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday);
filename_memory_buf_t w;
fmt::format_to(w, SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday);
return fmt::to_string(w);
}
};
@@ -44,10 +52,10 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger]")
prepare_logdir();
// calculate filename (time based)
std::string basename = "test_logs/daily_dateonly";
spdlog::filename_t basename = SPDLOG_FILENAME_T("test_logs/daily_dateonly");
std::tm tm = spdlog::details::os::localtime();
spdlog::memory_buf_t w;
fmt::format_to(w, "{}{:04d}{:02d}{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
filename_memory_buf_t w;
fmt::format_to(w, SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
for (int i = 0; i < 10; ++i)
@@ -57,7 +65,13 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger]")
logger->flush();
#ifdef SPDLOG_WCHAR_FILENAMES
spdlog::memory_buf_t buf;
spdlog::details::os::wstr_to_utf8buf(fmt::to_string(w), buf);
auto filename = fmt::to_string(buf);
#else
auto filename = fmt::to_string(w);
#endif
require_message_count(filename, 10);
}
@@ -67,20 +81,20 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger]")
TEST_CASE("rotating_file_sink::calc_filename1", "[rotating_file_sink]]")
{
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename("rotated.txt", 3);
REQUIRE(filename == "rotated.3.txt");
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename(SPDLOG_FILENAME_T("rotated.txt"), 3);
REQUIRE(filename == SPDLOG_FILENAME_T("rotated.3.txt"));
}
TEST_CASE("rotating_file_sink::calc_filename2", "[rotating_file_sink]]")
{
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename("rotated", 3);
REQUIRE(filename == "rotated.3");
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename(SPDLOG_FILENAME_T("rotated"), 3);
REQUIRE(filename == SPDLOG_FILENAME_T("rotated.3"));
}
TEST_CASE("rotating_file_sink::calc_filename3", "[rotating_file_sink]]")
{
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename("rotated.txt", 0);
REQUIRE(filename == "rotated.txt");
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename(SPDLOG_FILENAME_T("rotated.txt"), 0);
REQUIRE(filename == SPDLOG_FILENAME_T("rotated.txt"));
}
// regex supported only from gcc 4.9 and above
@@ -91,10 +105,10 @@ TEST_CASE("rotating_file_sink::calc_filename3", "[rotating_file_sink]]")
TEST_CASE("daily_file_sink::daily_filename_calculator", "[daily_file_sink]]")
{
// daily_YYYY-MM-DD_hh-mm.txt
auto filename = spdlog::sinks::daily_filename_calculator::calc_filename("daily.txt", spdlog::details::os::localtime());
auto filename = spdlog::sinks::daily_filename_calculator::calc_filename(SPDLOG_FILENAME_T("daily.txt"), spdlog::details::os::localtime());
// date regex based on https://www.regular-expressions.info/dates.html
std::regex re(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])\.txt$)");
std::smatch match;
std::basic_regex<spdlog::filename_t::value_type> re(SPDLOG_FILENAME_T(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])\.txt$)"));
std::match_results<spdlog::filename_t::const_iterator> match;
REQUIRE(std::regex_match(filename, match, re));
}
#endif
@@ -116,7 +130,7 @@ static void test_rotate(int days_to_run, uint16_t max_days, uint16_t expected_n_
prepare_logdir();
std::string basename = "test_logs/daily_rotate.txt";
spdlog::filename_t basename = SPDLOG_FILENAME_T("test_logs/daily_rotate.txt");
daily_file_sink_st sink{basename, 2, 30, true, max_days};
// simulate messages with 24 intervals

View File

@@ -5,6 +5,9 @@
#include <iostream>
#define SIMPLE_LOG "test_logs/simple_log.txt"
#define SIMPLE_ASYNC_LOG "test_logs/simple_async_log.txt"
class failing_sink : public spdlog::sinks::base_sink<std::mutex>
{
protected:
@@ -22,7 +25,7 @@ protected:
TEST_CASE("default_error_handler", "[errors]]")
{
prepare_logdir();
std::string filename = "test_logs/simple_log.txt";
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("test-error", filename, true);
logger->set_pattern("%v");
@@ -31,8 +34,8 @@ TEST_CASE("default_error_handler", "[errors]]")
logger->flush();
using spdlog::details::os::default_eol;
REQUIRE(file_contents(filename) == fmt::format("Test message 2{}", default_eol));
REQUIRE(count_lines(filename) == 1);
REQUIRE(file_contents(SIMPLE_LOG) == fmt::format("Test message 2{}", default_eol));
REQUIRE(count_lines(SIMPLE_LOG) == 1);
}
struct custom_ex
@@ -40,7 +43,7 @@ struct custom_ex
TEST_CASE("custom_error_handler", "[errors]]")
{
prepare_logdir();
std::string filename = "test_logs/simple_log.txt";
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename, true);
logger->flush_on(spdlog::level::info);
logger->set_error_handler([=](const std::string &) { throw custom_ex(); });
@@ -48,7 +51,7 @@ TEST_CASE("custom_error_handler", "[errors]]")
REQUIRE_THROWS_AS(logger->info("Bad format msg {} {}", "xxx"), custom_ex);
logger->info("Good message #2");
require_message_count(filename, 2);
require_message_count(SIMPLE_LOG, 2);
}
TEST_CASE("default_error_handler2", "[errors]]")
@@ -72,7 +75,7 @@ TEST_CASE("async_error_handler", "[errors]]")
prepare_logdir();
std::string err_msg("log failed with some msg");
std::string filename = "test_logs/simple_async_log.txt";
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_ASYNC_LOG);
{
spdlog::init_thread_pool(128, 1);
auto logger = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("logger", filename, true);
@@ -90,7 +93,7 @@ TEST_CASE("async_error_handler", "[errors]]")
spdlog::drop("logger"); // force logger to drain the queue and shutdown
}
spdlog::init_thread_pool(128, 1);
require_message_count(filename, 2);
require_message_count(SIMPLE_ASYNC_LOG, 2);
REQUIRE(file_contents("test_logs/custom_err.txt") == err_msg);
}
@@ -100,7 +103,7 @@ TEST_CASE("async_error_handler2", "[errors]]")
prepare_logdir();
std::string err_msg("This is async handler error message");
{
spdlog::details::os::create_dir("test_logs");
spdlog::details::os::create_dir(SPDLOG_FILENAME_T("test_logs"));
spdlog::init_thread_pool(128, 1);
auto logger = spdlog::create_async<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string &) {

View File

@@ -24,20 +24,20 @@ static void test_single_print(std::function<void(std::string const &)> do_log, s
REQUIRE(CloseEventLog(handle_));
}
}
} event_log{::OpenEventLog(nullptr, TEST_SOURCE)};
} event_log{::OpenEventLogA(nullptr, TEST_SOURCE)};
REQUIRE(event_log.handle_);
DWORD read_bytes{}, size_needed{};
auto ok =
::ReadEventLog(event_log.handle_, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ, 0, &read_bytes, 0, &read_bytes, &size_needed);
::ReadEventLogA(event_log.handle_, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ, 0, &read_bytes, 0, &read_bytes, &size_needed);
REQUIRE(!ok);
REQUIRE(::GetLastError() == ERROR_INSUFFICIENT_BUFFER);
std::vector<char> record_buffer(size_needed);
PEVENTLOGRECORD record = (PEVENTLOGRECORD)record_buffer.data();
ok = ::ReadEventLog(
ok = ::ReadEventLogA(
event_log.handle_, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ, 0, record, size_needed, &read_bytes, &size_needed);
REQUIRE(ok);

View File

@@ -3,6 +3,8 @@
*/
#include "includes.h"
#define TEST_FILENAME "test_logs/file_helper_test.txt"
using spdlog::details::file_helper;
static void write_with_helper(file_helper &helper, size_t howmany)
@@ -18,7 +20,7 @@ TEST_CASE("file_helper_filename", "[file_helper::filename()]]")
prepare_logdir();
file_helper helper;
std::string target_filename = "test_logs/file_helper_test.txt";
spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME);
helper.open(target_filename);
REQUIRE(helper.filename() == target_filename);
}
@@ -26,7 +28,7 @@ TEST_CASE("file_helper_filename", "[file_helper::filename()]]")
TEST_CASE("file_helper_size", "[file_helper::size()]]")
{
prepare_logdir();
std::string target_filename = "test_logs/file_helper_test.txt";
spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME);
size_t expected_size = 123;
{
file_helper helper;
@@ -34,13 +36,13 @@ TEST_CASE("file_helper_size", "[file_helper::size()]]")
write_with_helper(helper, expected_size);
REQUIRE(static_cast<size_t>(helper.size()) == expected_size);
}
REQUIRE(get_filesize(target_filename) == expected_size);
REQUIRE(get_filesize(TEST_FILENAME) == expected_size);
}
TEST_CASE("file_helper_reopen", "[file_helper::reopen()]]")
{
prepare_logdir();
std::string target_filename = "test_logs/file_helper_test.txt";
spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME);
file_helper helper;
helper.open(target_filename);
write_with_helper(helper, 12);
@@ -52,7 +54,7 @@ TEST_CASE("file_helper_reopen", "[file_helper::reopen()]]")
TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
{
prepare_logdir();
std::string target_filename = "test_logs/file_helper_test.txt";
spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME);
size_t expected_size = 14;
file_helper helper;
helper.open(target_filename);
@@ -62,16 +64,12 @@ TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
REQUIRE(helper.size() == expected_size);
}
static void test_split_ext(const char *fname, const char *expect_base, const char *expect_ext)
static void test_split_ext(const spdlog::filename_t::value_type *fname, const spdlog::filename_t::value_type *expect_base, const spdlog::filename_t::value_type *expect_ext)
{
spdlog::filename_t filename(fname);
spdlog::filename_t expected_base(expect_base);
spdlog::filename_t expected_ext(expect_ext);
#ifdef _WIN32 // replace folder sep
std::replace(filename.begin(), filename.end(), '/', '\\');
std::replace(expected_base.begin(), expected_base.end(), '/', '\\');
#endif
spdlog::filename_t basename;
spdlog::filename_t ext;
std::tie(basename, ext) = file_helper::split_by_extension(filename);
@@ -81,22 +79,22 @@ static void test_split_ext(const char *fname, const char *expect_base, const cha
TEST_CASE("file_helper_split_by_extension", "[file_helper::split_by_extension()]]")
{
test_split_ext("mylog.txt", "mylog", ".txt");
test_split_ext(".mylog.txt", ".mylog", ".txt");
test_split_ext(".mylog", ".mylog", "");
test_split_ext("/aaa/bb.d/mylog", "/aaa/bb.d/mylog", "");
test_split_ext("/aaa/bb.d/mylog.txt", "/aaa/bb.d/mylog", ".txt");
test_split_ext("aaa/bbb/ccc/mylog.txt", "aaa/bbb/ccc/mylog", ".txt");
test_split_ext("aaa/bbb/ccc/mylog.", "aaa/bbb/ccc/mylog.", "");
test_split_ext("aaa/bbb/ccc/.mylog.txt", "aaa/bbb/ccc/.mylog", ".txt");
test_split_ext("/aaa/bbb/ccc/mylog.txt", "/aaa/bbb/ccc/mylog", ".txt");
test_split_ext("/aaa/bbb/ccc/.mylog", "/aaa/bbb/ccc/.mylog", "");
test_split_ext("../mylog.txt", "../mylog", ".txt");
test_split_ext(".././mylog.txt", ".././mylog", ".txt");
test_split_ext(".././mylog.txt/xxx", ".././mylog.txt/xxx", "");
test_split_ext("/mylog.txt", "/mylog", ".txt");
test_split_ext("//mylog.txt", "//mylog", ".txt");
test_split_ext("", "", "");
test_split_ext(".", ".", "");
test_split_ext("..txt", ".", ".txt");
test_split_ext(SPDLOG_FILENAME_T("mylog.txt"), SPDLOG_FILENAME_T("mylog"), SPDLOG_FILENAME_T(".txt"));
test_split_ext(SPDLOG_FILENAME_T(".mylog.txt"), SPDLOG_FILENAME_T(".mylog"), SPDLOG_FILENAME_T(".txt"));
test_split_ext(SPDLOG_FILENAME_T(".mylog"), SPDLOG_FILENAME_T(".mylog"), SPDLOG_FILENAME_T(""));
test_split_ext(SPDLOG_FILENAME_T("/aaa/bb.d/mylog"), SPDLOG_FILENAME_T("/aaa/bb.d/mylog"), SPDLOG_FILENAME_T(""));
test_split_ext(SPDLOG_FILENAME_T("/aaa/bb.d/mylog.txt"), SPDLOG_FILENAME_T("/aaa/bb.d/mylog"), SPDLOG_FILENAME_T(".txt"));
test_split_ext(SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog.txt"), SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog"), SPDLOG_FILENAME_T(".txt"));
test_split_ext(SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog."), SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog."), SPDLOG_FILENAME_T(""));
test_split_ext(SPDLOG_FILENAME_T("aaa/bbb/ccc/.mylog.txt"), SPDLOG_FILENAME_T("aaa/bbb/ccc/.mylog"), SPDLOG_FILENAME_T(".txt"));
test_split_ext(SPDLOG_FILENAME_T("/aaa/bbb/ccc/mylog.txt"), SPDLOG_FILENAME_T("/aaa/bbb/ccc/mylog"), SPDLOG_FILENAME_T(".txt"));
test_split_ext(SPDLOG_FILENAME_T("/aaa/bbb/ccc/.mylog"), SPDLOG_FILENAME_T("/aaa/bbb/ccc/.mylog"), SPDLOG_FILENAME_T(""));
test_split_ext(SPDLOG_FILENAME_T("../mylog.txt"), SPDLOG_FILENAME_T("../mylog"), SPDLOG_FILENAME_T(".txt"));
test_split_ext(SPDLOG_FILENAME_T(".././mylog.txt"), SPDLOG_FILENAME_T(".././mylog"), SPDLOG_FILENAME_T(".txt"));
test_split_ext(SPDLOG_FILENAME_T(".././mylog.txt/xxx"), SPDLOG_FILENAME_T(".././mylog.txt/xxx"), SPDLOG_FILENAME_T(""));
test_split_ext(SPDLOG_FILENAME_T("/mylog.txt"), SPDLOG_FILENAME_T("/mylog"), SPDLOG_FILENAME_T(".txt"));
test_split_ext(SPDLOG_FILENAME_T("//mylog.txt"), SPDLOG_FILENAME_T("//mylog"), SPDLOG_FILENAME_T(".txt"));
test_split_ext(SPDLOG_FILENAME_T(""), SPDLOG_FILENAME_T(""), SPDLOG_FILENAME_T(""));
test_split_ext(SPDLOG_FILENAME_T("."), SPDLOG_FILENAME_T("."), SPDLOG_FILENAME_T(""));
test_split_ext(SPDLOG_FILENAME_T("..txt"), SPDLOG_FILENAME_T("."), SPDLOG_FILENAME_T(".txt"));
}

View File

@@ -3,10 +3,13 @@
*/
#include "includes.h"
#define SIMPLE_LOG "test_logs/simple_log"
#define ROTATING_LOG "test_logs/rotating_log"
TEST_CASE("simple_file_logger", "[simple_logger]]")
{
prepare_logdir();
std::string filename = "test_logs/simple_log";
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
@@ -15,29 +18,29 @@ TEST_CASE("simple_file_logger", "[simple_logger]]")
logger->info("Test message {}", 2);
logger->flush();
require_message_count(filename, 2);
require_message_count(SIMPLE_LOG, 2);
using spdlog::details::os::default_eol;
REQUIRE(file_contents(filename) == fmt::format("Test message 1{}Test message 2{}", default_eol, default_eol));
REQUIRE(file_contents(SIMPLE_LOG) == fmt::format("Test message 1{}Test message 2{}", default_eol, default_eol));
}
TEST_CASE("flush_on", "[flush_on]]")
{
prepare_logdir();
std::string filename = "test_logs/simple_log";
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
logger->set_level(spdlog::level::trace);
logger->flush_on(spdlog::level::info);
logger->trace("Should not be flushed");
REQUIRE(count_lines(filename) == 0);
REQUIRE(count_lines(SIMPLE_LOG) == 0);
logger->info("Test message {}", 1);
logger->info("Test message {}", 2);
require_message_count(filename, 3);
require_message_count(SIMPLE_LOG, 3);
using spdlog::details::os::default_eol;
REQUIRE(file_contents(filename) ==
REQUIRE(file_contents(SIMPLE_LOG) ==
fmt::format("Should not be flushed{}Test message 1{}Test message 2{}", default_eol, default_eol, default_eol));
}
@@ -45,7 +48,7 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
{
prepare_logdir();
size_t max_size = 1024 * 10;
std::string basename = "test_logs/rotating_log";
spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG);
auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 0);
for (int i = 0; i < 10; ++i)
@@ -54,14 +57,14 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
}
logger->flush();
require_message_count(basename, 10);
require_message_count(ROTATING_LOG, 10);
}
TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
{
prepare_logdir();
size_t max_size = 1024 * 10;
std::string basename = "test_logs/rotating_log";
spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG);
{
// make an initial logger to create the first output file
@@ -83,7 +86,7 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
logger->flush();
require_message_count(basename, 10);
require_message_count(ROTATING_LOG, 10);
for (int i = 0; i < 1000; i++)
{
@@ -92,7 +95,6 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
}
logger->flush();
REQUIRE(get_filesize(basename) <= max_size);
auto filename1 = basename + ".1";
REQUIRE(get_filesize(filename1) <= max_size);
REQUIRE(get_filesize(ROTATING_LOG) <= max_size);
REQUIRE(get_filesize(ROTATING_LOG ".1") <= max_size);
}

View File

@@ -8,11 +8,13 @@
#error "Invalid SPDLOG_ACTIVE_LEVEL in test. Should be SPDLOG_LEVEL_DEBUG"
#endif
#define TEST_FILENAME "test_logs/simple_log"
TEST_CASE("debug and trace w/o format string", "[macros]]")
{
prepare_logdir();
std::string filename = "test_logs/simple_log";
spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME);
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
@@ -23,8 +25,8 @@ TEST_CASE("debug and trace w/o format string", "[macros]]")
logger->flush();
using spdlog::details::os::default_eol;
REQUIRE(ends_with(file_contents(filename), fmt::format("Test message 2{}", default_eol)));
REQUIRE(count_lines(filename) == 1);
REQUIRE(ends_with(file_contents(TEST_FILENAME), fmt::format("Test message 2{}", default_eol)));
REQUIRE(count_lines(TEST_FILENAME) == 1);
spdlog::set_default_logger(logger);
@@ -32,8 +34,8 @@ TEST_CASE("debug and trace w/o format string", "[macros]]")
SPDLOG_DEBUG("Test message {}", 4);
logger->flush();
require_message_count(filename, 2);
REQUIRE(ends_with(file_contents(filename), fmt::format("Test message 4{}", default_eol)));
require_message_count(TEST_FILENAME, 2);
REQUIRE(ends_with(file_contents(TEST_FILENAME), fmt::format("Test message 4{}", default_eol)));
}
TEST_CASE("disable param evaluation", "[macros]")

View File

@@ -55,6 +55,14 @@ TEST_CASE("level_to_string_view", "[convert_to_string_view")
REQUIRE(spdlog::level::to_string_view(spdlog::level::off) == "off");
}
TEST_CASE("set_level_to_string_view", "[set_string_view")
{
spdlog::level::set_string_view(spdlog::level::info, "INF");
REQUIRE(spdlog::level::to_string_view(spdlog::level::info) == "INF");
spdlog::level::set_string_view(spdlog::level::info, "info"); // set it back
REQUIRE(spdlog::level::to_string_view(spdlog::level::info) == "info");
}
TEST_CASE("to_short_c_str", "[convert_to_short_c_str]")
{
REQUIRE(std::string(spdlog::level::to_short_c_str(spdlog::level::trace)) == "T");

View File

@@ -10,7 +10,7 @@ static milliseconds millis_from(const test_clock::time_point &tp0)
TEST_CASE("dequeue-empty-nowait", "[mpmc_blocking_q]")
{
size_t q_size = 100;
milliseconds tolerance_wait(10);
milliseconds tolerance_wait(20);
spdlog::details::mpmc_blocking_queue<int> q(q_size);
int popped_item = 0;

View File

@@ -5,7 +5,7 @@ using spdlog::memory_buf_t;
// log to str and return it
template<typename... Args>
static std::string log_to_str(const std::string &msg, const Args &... args)
static std::string log_to_str(const std::string &msg, const Args &...args)
{
std::ostringstream oss;
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
@@ -227,7 +227,7 @@ TEST_CASE("paddinng_truncate", "[pattern_formatter]")
REQUIRE(log_to_str("123456", "%0!v", spdlog::pattern_time_type::local, "\n") == "\n");
}
TEST_CASE("paddinng_truncate_funcname", "[pattern_formatter]")
TEST_CASE("padding_truncate_funcname", "[pattern_formatter]")
{
spdlog::sinks::test_sink_st test_sink;
@@ -237,13 +237,28 @@ TEST_CASE("paddinng_truncate_funcname", "[pattern_formatter]")
spdlog::details::log_msg msg1{spdlog::source_loc{"ignored", 1, "func"}, "test_logger", spdlog::level::info, "message"};
test_sink.log(msg1);
REQUIRE(test_sink.lines()[0] == "message [ func]");
spdlog::details::log_msg msg2{spdlog::source_loc{"ignored", 1, "function"}, "test_logger", spdlog::level::info, "message"};
test_sink.log(msg2);
REQUIRE(test_sink.lines()[1] == "message [funct]");
}
auto lines = test_sink.lines();
REQUIRE(lines[0] == "message [ func]");
REQUIRE(lines[1] == "message [funct]");
TEST_CASE("padding_funcname", "[pattern_formatter]")
{
spdlog::sinks::test_sink_st test_sink;
const char *pattern = "%v [%10!]";
auto formatter = std::unique_ptr<spdlog::formatter>(new spdlog::pattern_formatter(pattern));
test_sink.set_formatter(std::move(formatter));
spdlog::details::log_msg msg1{spdlog::source_loc{"ignored", 1, "func"}, "test_logger", spdlog::level::info, "message"};
test_sink.log(msg1);
REQUIRE(test_sink.lines()[0] == "message [ func]");
spdlog::details::log_msg msg2{spdlog::source_loc{"ignored", 1, "func567890123"}, "test_logger", spdlog::level::info, "message"};
test_sink.log(msg2);
REQUIRE(test_sink.lines()[1] == "message [func567890123]");
}
TEST_CASE("clone-default-formatter", "[pattern_formatter]")
@@ -357,7 +372,7 @@ TEST_CASE("clone-custom_formatter", "[pattern_formatter]")
//
#ifdef _WIN32
static const char *const test_path = "\\a\\b\\myfile.cpp";
static const char *const test_path = "\\a\\b\\c/myfile.cpp";
#else
static const char *const test_path = "/a/b//myfile.cpp";
#endif

View File

@@ -103,7 +103,7 @@ TEST_CASE("disable automatic registration", "[registry]")
spdlog::set_level(log_level);
// but disable automatic registration
spdlog::set_automatic_registration(false);
auto logger1 = spdlog::create<spdlog::sinks::daily_file_sink_st>(tested_logger_name, "filename", 11, 59);
auto logger1 = spdlog::create<spdlog::sinks::daily_file_sink_st>(tested_logger_name, SPDLOG_FILENAME_T("filename"), 11, 59);
auto logger2 = spdlog::create_async<spdlog::sinks::stdout_color_sink_mt>(tested_logger_name2);
// loggers should not be part of the registry
REQUIRE_FALSE(spdlog::get(tested_logger_name));

35
tests/test_stopwatch.cpp Normal file
View File

@@ -0,0 +1,35 @@
#include "includes.h"
#include "test_sink.h"
#include "spdlog/stopwatch.h"
TEST_CASE("stopwatch1", "[stopwatch]")
{
using std::chrono::milliseconds;
milliseconds wait_ms(250);
milliseconds tolerance_ms(250);
spdlog::stopwatch sw;
std::this_thread::sleep_for(wait_ms);
REQUIRE(sw.elapsed() >= wait_ms);
REQUIRE(sw.elapsed() <= wait_ms + tolerance_ms);
}
TEST_CASE("stopwatch2", "[stopwatch]")
{
using spdlog::sinks::test_sink_st;
std::chrono::duration<double> wait_duration(0.250);
std::chrono::duration<double> tolerance_duration(0.250);
auto test_sink = std::make_shared<test_sink_st>();
spdlog::stopwatch sw;
spdlog::logger logger("test-stopwatch", test_sink);
logger.set_pattern("%v");
std::this_thread::sleep_for(wait_duration);
logger.info("{}", sw);
auto val = std::stod(test_sink->lines()[0]);
REQUIRE(val >= wait_duration.count());
REQUIRE(val <= (wait_duration + tolerance_duration).count());
}

View File

@@ -1,6 +1,8 @@
#include "includes.h"
#ifndef _WIN32
#ifdef _WIN32
#include <Windows.h>
#else
#include <sys/types.h>
#include <dirent.h>
#endif
@@ -82,7 +84,7 @@ bool ends_with(std::string const &value, std::string const &ending)
std::size_t count_files(const std::string &folder)
{
size_t counter = 0;
WIN32_FIND_DATA ffd;
WIN32_FIND_DATAA ffd;
// Start iterating over the files in the folder directory.
HANDLE hFind = ::FindFirstFileA((folder + "\\*").c_str(), &ffd);
@@ -92,7 +94,7 @@ std::size_t count_files(const std::string &folder)
{
if (ffd.cFileName[0] != '.')
counter++;
} while (::FindNextFile(hFind, &ffd) != 0);
} while (::FindNextFileA(hFind, &ffd) != 0);
::FindClose(hFind);
}
else