Compare commits

...

291 Commits

Author SHA1 Message Date
gabime
ed5498a2e9 report unexected exception types 2017-12-20 00:29:15 +02:00
gabime
6dd928dc3c Undo fix #529 (causes a warning under gcc and clang when no args passed) 2017-12-20 00:04:16 +02:00
gabime
de595fe2b7 Fix #529 (SPDLOG_TRACE macro problem) 2017-12-19 23:09:27 +02:00
Gabi Melman
d460c3026a Merge pull request #568 from adubovikov/master
added facilty for syslog
2017-12-19 11:49:51 +02:00
Gabi Melman
88fe218741 Update logger_impl.h 2017-12-06 15:59:27 +02:00
Gabi Melman
b1be7b9fea async log: increased sleep to to 500ms the worker loop 2017-12-05 14:07:13 +02:00
Alexandr Dubovikov
bec6919587 added facilty for syslog 2017-12-04 13:03:40 +01:00
Gabi Melman
2f81ff7f17 Update README.md 2017-12-03 21:01:07 +02:00
Gabi Melman
4db70c2078 Update README.md 2017-12-03 20:58:19 +02:00
gabime
26b390bb19 removed lock from dist_sink::_flush() (moved to base_sink::flush()) 2017-12-02 17:24:02 +02:00
gabime
a9149c6d46 added lock on flush in base_sink 2017-12-02 17:06:59 +02:00
gabime
859b7f1d58 fixed test for visual c++ 2017-12-01 04:42:23 +02:00
gabime
49989e0678 fixed test for gcc 4.8 (no regex) 2017-12-01 04:35:23 +02:00
gabime
70274924b7 fixed test comment 2017-12-01 03:59:35 +02:00
gabime
f5939f9e56 astyle 2017-12-01 03:46:19 +02:00
gabime
84e307521d Fixed test 2017-12-01 03:45:29 +02:00
gabime
3c4a2bf531 Handle file extensions in rotating and daily loggers 2017-12-01 03:40:49 +02:00
gabime
60ce47a814 Removed include_if.cpp from the tests .sln 2017-12-01 01:28:12 +02:00
Gabi Melman
613f024d42 Removed forgotten *_if declarations 2017-11-30 23:42:57 +02:00
gabime
799ba2a57b added SPDLOG_DISABLE_TID_CACHING macro to prevent invalid thread ids after fork 2017-11-26 00:40:47 +02:00
gabime
adbc22096a enable final keyword by default. Can be disabled in tweakme.h for older compilers 2017-11-25 15:53:35 +02:00
gabime
e7cf25a1c0 fixed issue #562 2017-11-25 15:41:55 +02:00
gabime
dcc7b347ca Removed all *_if functions (trace_if, debug_if, info_if,..) because they are redundant and confusing way to preform if 2017-11-25 15:19:41 +02:00
gabime
9a8f5c59e2 Added .idea/ to .gitignore 2017-11-25 15:11:18 +02:00
gabime
c41b6d28b5 astyle 2017-11-24 20:59:58 +02:00
gabime
fd170b0fe1 catch(...) exceptions, report it, and rethrow 2017-11-24 20:58:43 +02:00
Gabi Melman
587b528292 Merge pull request #556 from jpcima/syslog-build
correct include path for sink/syslog_sink.h
2017-11-14 16:29:20 +02:00
Gabi Melman
36c8e79a4a Merge pull request #555 from jpcima/windebug-sink
accept msvc_sink on all compilers, add name windebug_sink (fixes #554)
2017-11-14 16:28:39 +02:00
JP Cimalando
ecec210d0e accept msvc_sink on all compilers, add name windebug_sink (fixes #554) 2017-11-14 14:41:31 +01:00
JP Cimalando
76d2620dad correct include path for sink/syslog_sink.h 2017-11-14 14:25:43 +01:00
gabime
8ca1d84a32 Removed catch(..) from the codebase. Catch only std::exception 2017-11-12 19:46:15 +02:00
Gabi Melman
63d7c64618 Merge pull request #551 from daylanKifky/modified_includes
modified path on quoted #includes
2017-11-11 16:50:06 +02:00
daylanKifky
10772eadae fix wincolor_sink's common.h include 2017-11-11 15:38:08 +01:00
daylanKifky
b220bbb349 fix printf include 2017-11-11 15:21:34 +01:00
daylanKifky
5153b44507 minor fixes 2017-11-11 14:06:01 +01:00
daylanKifky
27e7412640 modified path on quoted #includes
Paths pointing to the root of the library where replaced for ones relatives to each file.

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

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

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

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

However rethrowing these kind of exception is mandatory, otherwise
an abort will be caused by the glibc.
2017-10-25 10:15:27 +02:00
Gabi Melman
69e6af0bf9 Merge pull request #539 from knowledge4igor/fix_cast_warnings
Fix warnings which are caused by C style cast
2017-10-25 04:36:18 +03:00
knowledge4igor
147bdbf591 Fix warnings which are caused by C style cast 2017-10-25 00:40:42 +03:00
Berkus Decker
4974743ee8 Make short month names match in length 2017-10-24 14:10:58 +03:00
Berkus Decker
1c8cc65f6c Fix typos 2017-10-24 14:10:29 +03:00
Berkus Decker
a6b5ef55a4 Fix typo 2017-10-24 13:34:58 +03:00
Berkus Decker
cefea324cb Fix typos 2017-10-24 12:03:59 +03:00
Gabi Melman
7f953c115d Merge pull request #535 from vitor-alves/master
Update README.md
2017-10-17 10:04:18 +03:00
Vitor Alves
20a4143537 Update README.md
Updated install section for Arch Linux. Added yaourt instead of pacman.
2017-10-16 22:23:19 -02:00
gabime
87a44555b6 fixed macro tests 2017-10-13 02:15:49 +03:00
gabime
21ed31844c fixed bug in SPDLOG_TRACE_IF macro and added some related tests 2017-10-13 02:04:31 +03:00
gabime
4a159ad66d Fixed unused variable warning 2017-10-12 19:59:14 +03:00
gabime
19264b8399 Merge branch 'master' of https://github.com/gabime/spdlog 2017-10-12 19:53:07 +03:00
gabime
709948ff4a Fixed issue #527 2017-10-12 19:48:04 +03:00
Gabi Melman
30d5fd16d7 Update README.md 2017-10-12 19:06:01 +03:00
Gabi Melman
9688689938 Merge pull request #531 from fogo/printf
Support for printf-style logging
2017-10-10 13:56:37 +03:00
fogo
e8b7f4194a Moved printf include to fmt.h 2017-10-09 20:05:20 -03:00
fogo
ee525f9bef allow printf style logging
* tests updated for printf style
* tests makefile support printf style
2017-10-05 10:54:06 -03:00
fogo
552d6214e0 updated bundled fmt to contain printf 2017-10-05 08:19:53 -03:00
Gabi Melman
dc8ac4c671 Update tweakme.h 2017-09-29 22:43:48 +03:00
Gabi Melman
cd6a8c9a4c Merge pull request #524 from 2Park/master
Fix SPDLOG_WCHAR_TO_UTF8_SUPPORT wchar_t logging
2017-09-28 21:44:55 +03:00
John Andre Hestad
375b7fdda5 Fix SPDLOG_WCHAR_TO_UTF8_SUPPORT wchar_t logging 2017-09-28 14:19:04 +02:00
Gabi Melman
3a21b765cb Merge pull request #522 from jasonbeach/feat_add_epoch_formatter
add formatter for unix epoch time in seconds.
2017-09-18 09:46:16 +03:00
Jason Beach
f7fabfb2c4 add formatter for unix epoch time in seconds. 2017-09-17 22:11:23 -04:00
Gabi Melman
a4e6d8877c Update README.md 2017-08-29 12:00:10 +03:00
Gabi Melman
e303f02cce Merge pull request #515 from elelel/trace
Compiler-dependent line numbering
2017-08-25 21:34:23 +03:00
El El
b242fb087d Compiler-dependent line numbering 2017-08-25 15:19:29 +00:00
Gabi Melman
02b9f970d3 Merge pull request #514 from stackforce/feature/improve-cmake-lists
Feature/improve cmake lists
2017-08-25 17:11:33 +03:00
Adrian Antonana
e6b9fa577d cmake: set project version to 0.14.0 2017-08-25 09:12:15 +02:00
Adrian Antonana
fe2fa4087d cmake: add some small comments to point out whats being done 2017-08-24 16:57:07 +02:00
Adrian Antonana
ab25004242 cmake: add some commend blocks to clearly differentiate CMakeLists file sections 2017-08-24 16:55:55 +02:00
Adrian Antonana
5504630e46 cmake: improve CMakeLists.txt
* support CMake user registry package registration
* use GNUInstallDirs to set installation firectories
* use spdlog namespace in both build and install interfaces
2017-08-24 16:55:06 +02:00
Gabi Melman
c4d93ae0b5 Merge pull request #513 from Lectem/patch-1
SPDLOG_BUILD_TESTING now depends on BUILD_TESTING
2017-08-23 17:35:04 +03:00
Lectem
47cf62f878 SPDLOG_BUILD_TESTING now depends on BUILD_TESTING
I encountered an issue when using spdlog through add_subdirectory.
Since SPDLOG_BUILD_TESTING is ON by default, it now adds tests to my project, even if BUILD_TESTING (the official CTest variable) is set to OFF.
cmake_dependent_option makes it so that if someone enables BUILD_TESTING then SPDLOG_BUILD_TESTING will be set to ON by default.
This way one can disable all external tests by setting BUILD_TESTING before using add_subdirectory and then setting it back to its original value.
The only change for those using the library directly is that they now use BUILD_TESTING instead of SPDLOG_BUILD_TESTING when configuring.
2017-08-23 15:12:40 +02:00
Gabi Melman
4fba14c79f Version 0.14.0 2017-08-19 18:48:43 +03:00
gabime
cb02b344a3 Fix issue #510 (spelling) 2017-08-19 15:49:16 +03:00
gabime
fced34e3d8 bumped fmt version to 4.0.0 2017-08-19 15:46:01 +03:00
gabime
268222e496 Fixed crash in async logger in gcc 4.8.5 regarding static members in pattern_formatter impl 2017-08-19 15:36:34 +03:00
Gabi Melman
6340c164ce Merge pull request #490 from asit-dhal/issue-488
Issue-488: warning for missing enumeration value
2017-08-17 00:21:44 +03:00
Gabi Melman
f546036e70 Merge pull request #502 from gg7/readme-add-gentoo-package
readme: Add Gentoo package name
2017-08-12 22:43:34 +01:00
George Gaydarov
c69b356c86 readme: Add Gentoo package name 2017-08-12 17:01:47 +01:00
Asit Kumar Dhal
e35c7fd065 Issue-488: warning for missing enumeration value 2017-08-05 00:30:03 +02:00
Gabi Melman
5ed426980b Merge pull request #484 from vgabi94/patch-1
Background color unchanged
2017-07-30 09:52:44 +03:00
Gabriel Vaduva
3173422786 Background color unchanged
Keep the background color unchaged on Windows console when using colored loggers.
2017-07-29 23:07:02 +03:00
Gabi Melman
fc14ac23e9 Merge pull request #478 from snapbug/master
Added TRACE/DEBUG_IF macro definitions when TRACE/DEBUG_ON aren't defined
2017-07-23 10:11:42 +03:00
Matt Crane
13a938fc7f Added TRACE_IF and DEBUG_IF macro definitions when TRACE_ON and DEBUG_ON aren't defined 2017-07-22 21:42:20 -04:00
Gabi Melman
b75da32f4d Merge pull request #473 from asit-dhal/master
fixed length and upper case log level tags(after suggestion)
2017-07-13 01:19:31 +03:00
Asit Kumar Dhal
3a40f0c34d fixed length and upper case log level tags 2017-07-12 20:25:32 +02:00
gabime
4371092309 fixed gcc shadow warnings 2017-06-29 18:14:17 +03:00
Gabi Melman
f06f3f1468 Merge pull request #468 from p-alik/master
code formatting (astyle and dos2unix)
2017-06-29 12:20:32 +03:00
Alexei Pastuchov
8fd16fc45e code formatting (astyle and dos2unix)
it aims to solve the issue #467
2017-06-29 09:51:44 +02:00
Gabi Melman
9a04e37275 Merge pull request #465 from cyres/re_add_color
Add set_color to ansicolor_sink
2017-06-27 09:18:37 +03:00
Cyres
765095db66 Add set_color to ansicolor_sink
It is now possible again to set the color in the ansicolor_sink with set_color
2017-06-27 01:23:35 +02:00
Gabi Melman
431fb423fa Merge pull request #463 from sheldonlyr/threadid-osx
More meaningful thread id for OSX
2017-06-24 11:34:43 +03:00
sheldonlyr
66a2c4993b More meaningful thread id for OSX 2017-06-24 15:38:18 +08:00
Gabi Melman
1e4f14c789 Update README.md 2017-06-18 07:46:02 +03:00
Gabi Melman
c0f0fd713d Merge pull request #460 from asit-dhal/master
conditional logging
2017-06-18 03:13:53 +03:00
Asit Kumar Dhal
97be4532cc trace_if and debug_if macro added 2017-06-18 01:12:11 +02:00
Asit Kumar Dhal
868c0cedb0 Update README file 2017-06-17 20:26:07 +02:00
Asit Kumar Dhal
a767f07ba3 Conditional logging in the example 2017-06-17 19:10:52 +02:00
Asit Kumar Dhal
de0154c525 Test Case for conditional logging 2017-06-17 18:50:21 +02:00
Asit Kumar Dhal
380233b727 mend 2017-06-17 17:24:16 +02:00
Asit Kumar Dhal
b463b06ab5 conditional logging for all levels 2017-06-17 16:07:04 +02:00
Asit Kumar Dhal
9a189badbd conditional logging implementation 2017-06-17 15:54:44 +02:00
Asit Kumar Dhal
ed7c3a83f8 conditional logging implementation 2017-06-17 02:45:24 +02:00
Gabi Melman
d3e013a567 Merge pull request #459 from cneumann/create_async
Add create_async factory functions for async loggers
2017-06-15 22:42:48 +03:00
Carsten Neumann
8ee90d3349 Add create_async factory functions for async loggers 2017-06-15 11:08:44 -05:00
Gabi Melman
e6cbc22da5 Merge pull request #456 from rkollataj/mingw_fix
Fix for MinGW error: 'There are no arguments that depend on a template parameter'
2017-06-09 21:16:03 +03:00
Remigiusz Kołłątaj
2e84ca4fa4 Adding additional build environments for AppVeyor
Signed-off-by: Remigiusz Kołłątaj <remigiusz.kollataj@gmail.com>
2017-06-09 19:11:29 +02:00
Remigiusz Kołłątaj
fab55c8a14 Fix for MinGW error: 'There are no arguments that depend on a template parameter'
Signed-off-by: Remigiusz Kołłątaj <remigiusz.kollataj@gmail.com>
2017-06-09 19:03:11 +02:00
Gabi Melman
9470bdd3ec Update common.h 2017-06-01 03:42:10 +03:00
Gabi Melman
5d5f2f3ac3 Merge pull request #451 from ThePhD/pattern_time
UTC Time
2017-05-31 21:39:18 +03:00
ThePhD
7e09f01847 brace styling 2017-05-31 13:12:21 -04:00
ThePhD
d98d54896b use if statement instead of switch (changes of adding new time specifications outside of standard are probably unlikely anyhow)
pattern_time -> pattern_time_type
ptime variable name -> pattern_time variable name
make sure four spaces used, not tabs
2017-05-31 12:52:12 -04:00
ThePhD
18a0455b91 _pattern was never set... but it was also never used. Intentional by @gabime, or a reflection of a refactor with some data member left behind? 2017-05-30 18:15:30 -04:00
ThePhD
5c5080d304 implement a flag (in this case, an enumeration) that allows control over the type; we make it an enum for possible expansions of time abstractions that might make it into the C++ standard in the future (see Howard Hinnant's date/timezone library) or might be usefully-available from the OS at some point in time 2017-05-30 18:05:25 -04:00
gabime
13fb2550c6 Fixed issue #449 2017-05-21 20:39:54 +03:00
Gabi Melman
c6ad598af9 Merge pull request #450 from stonedreamforest/master
Fixed issues 449
2017-05-21 13:27:13 +03:00
Tennn
f8d509d010 Fixed issues 449
Please check it
2017-05-21 17:44:44 +08:00
gabime
e1d4c4651b Minor formatting 2017-05-21 04:07:20 +03:00
gabime
8d164f47a8 tweakme disable counters by default 2017-05-21 04:03:36 +03:00
gabime
42a56f6669 fixed "final" keyword error 2017-05-21 04:02:32 +03:00
gabime
d56fa23eb9 fixed compilation errors 2017-05-21 03:48:54 +03:00
gabime
095cb1f560 Added _flush() to base_sink 2017-05-21 03:43:41 +03:00
Gabi Melman
e215758b42 Update ansicolor_sink.h 2017-05-21 03:08:03 +03:00
Gabi Melman
87f5a60b90 Update ansicolor_sink.h 2017-05-21 03:06:35 +03:00
gabime
361344912e Refactored message counter a little 2017-05-21 02:45:08 +03:00
gabime
7da3e47ddc fixed color comment 2017-05-21 02:33:19 +03:00
gabime
1f9f9c09a2 fixed flush 2017-05-21 02:25:33 +03:00
gabime
8c240faa7d changed info color to regular insted of bold 2017-05-21 02:11:09 +03:00
gabime
7bbab6889d use data() istead of c_str() in ansicolor_sink 2017-05-21 02:07:43 +03:00
gabime
6cc7e7382e astyle 2017-05-21 01:56:56 +03:00
gabime
40fc5becff fixed compilation 2017-05-21 01:56:41 +03:00
gabime
3899e8a560 fixed compilation warning 2017-05-21 01:54:21 +03:00
gabime
7f84daffd5 fixed compilation 2017-05-21 01:49:06 +03:00
gabime
98addad888 Disable colors if terminal no attached and simplfy ansicolor_sink 2017-05-21 01:36:03 +03:00
Gabi Melman
1ab63ffdca Merge pull request #445 from alzix/message_counter
implement message counter feature
2017-05-18 23:38:57 +03:00
Alexander Zilberkant
ef6eb376d3 disable message counter feature in tweakme.h 2017-05-18 22:48:45 +03:00
Alexander Zilberkant
f29ff77ae7 implement message counter feature
adds %i logger pattern for printing log message sequence ID
2017-05-18 22:46:16 +03:00
Gabi Melman
bf3a415b1d Merge pull request #447 from eliaskosunen/master
Fix OSX builds of details/os.h
2017-05-17 22:05:47 +03:00
Elias Kosunen
c6c5a46560 Fix OSX builds of details/os.h 2017-05-17 21:14:39 +03:00
gabime
def86e6e20 Fixed forgotten inline keyword 2017-05-17 00:17:46 +03:00
gabime
1d6842f0f9 bugfix in color detection and astyle 2017-05-17 00:06:11 +03:00
gabime
eb92cc35df Merge branch 'master' of https://github.com/gabime/spdlog.git 2017-05-17 00:01:52 +03:00
gabime
bd25f59a42 Don't use color escape codes if terminal doesn't support them in ansicolor_sink 2017-05-16 23:35:01 +03:00
Gabi Melman
a39f71dbd4 Merge pull request #444 from alzix/minor-fixes
Minor fixes
2017-05-15 22:40:29 +03:00
Alexander Zilberkant
8329d97d90 fix indentation 2017-05-15 21:38:22 +03:00
Gabi Melman
57dc87732f Merge pull request #443 from alzix/revert-411
Revert "Merge pull request #441 from alzix/count_discarded"
2017-05-15 21:20:44 +03:00
Alexander Zilberkant
6547675e43 Revert "Merge pull request #441 from alzix/count_discarded"
This reverts commit 038733345a, reversing
changes made to 862d2f6f35.
2017-05-15 20:07:24 +03:00
Gabi Melman
13199034f0 Update tweakme.h 2017-05-13 01:25:48 +03:00
Gabi Melman
038733345a Merge pull request #441 from alzix/count_discarded
add an option to warn about discarded messages
2017-05-13 01:22:46 +03:00
Alexander Zilberkant
2b008efb06 disable SPDLOG_ASYNC_COUNT_DISCARDED_MSG 2017-05-13 01:10:58 +03:00
Alexander Zilberkant
0f25b25b20 add async_logger tests
cover discarded messages use-case
2017-05-13 01:01:28 +03:00
Alexander Zilberkant
42258a1059 move discarded message handling to a dedicated function
fix - formatter new discarded message
2017-05-13 00:53:57 +03:00
Gabi Melman
862d2f6f35 Merge pull request #442 from sidyhe/feature
add wide string to utf8 string support
2017-05-12 20:58:07 +03:00
Alexander Zilberkant
0aeaf9e28e add an option to warn about discarded messages
when using async_logger with async_overflow_policy::discard_log_msg each discarded
message will be counted and warning will be printed by the worker thread

this new feature is disabled by default - as it may have a performance hit when discarding messages
2017-05-11 23:52:58 +03:00
Sidyhe
8ee6d38501 add wide string to utf8 string support 2017-05-10 21:02:41 +08:00
Gabi Melman
47006c4e8e Merge pull request #436 from bahostetterlewis/master
Allow compiler to select an strerror_r stringify
2017-05-10 12:57:14 +03:00
Barrett
84a4f56eae Allow compiler to select an strerror_r stringify
On Alpine (and potentially other systems) that don't identify their runtime correctly there is an issue with the string conversion
Specifically, alpine linux and musl where the errno_to_string is incorrectly called.
To fix this I have added two overloaded functions and use auto err to allow the compiler to detect the actual types returned and call the correct method for conversion
2017-05-09 18:31:44 -07:00
gabime
e9b8286714 Merge branch 'master' of https://github.com/gabime/spdlog.git 2017-05-06 15:47:24 +03:00
gabime
81965bc300 Fixed some analyzer warnings 2017-05-06 15:33:12 +03:00
Gabi Melman
52292fb526 Update android_sink.h 2017-05-03 01:18:40 +03:00
Gabi Melman
6927aa1544 Update android_sink.h 2017-05-03 01:17:00 +03:00
Gabi Melman
f0f4a52190 Merge pull request #427 from alzix/android_sink_retry
android sink - add retry mechanism
2017-05-02 22:46:25 +03:00
Alexander Zilberkant
2f205a6dbc android sink - add retry mechanism
- in some cases subsequent calls to __android_log_write() may result with -EAGAIN error code.
  in such cases spdlog will sleep and try again for number of times defined by
  SPDLOG_ANDROID_LOG_NUM_OF_RETRIES

- defeult SPDLOG_ANDROID_LOG_NUM_OF_RETRIES set to 2 - can be overridden at build time
2017-05-02 22:38:50 +03:00
Gabi Melman
4a25802312 Update README.md 2017-04-29 20:11:54 +03:00
Gabi Melman
260a74509a Merge pull request #425 from jcelerier/feature/final_qualifier
Add an optional final qualifier to types
2017-04-28 21:43:01 +03:00
Jean-Michaël Celerier
4da5fa256c add SPDLOG_FINAL information in tweakme.h 2017-04-28 19:25:31 +02:00
Jean-Michaël Celerier
53138c20fb Add an optional final qualifier to types
When building with GCC's -Wfinal-types, a lot of types of spdlog
are marked as being more optimizable if they were marked final.

This patch adds a possibility for the user of the library to `#define SPDLOG_FINAL final`
and enjoy potentially better performance : GCC is then able to replace virtual calls by true
function calls if it can ensure that there are no derived types).

By default SPDLOG_FINAL is defined to nothing to not break existing code that
may be inheriting of some of these types for some reason.
2017-04-28 17:24:55 +02:00
Gabi Melman
9e6d81de08 Merge pull request #422 from theamirocohen/android_log
Android_logger conditionally apply its own formatting
2017-04-26 00:01:34 +03:00
Amir Cohen
45e3e7041d Android_logger apply its own formatting to every print by adding timestamps, severity, thread and process ids, thus the option flag in the ctor 2017-04-25 19:47:47 +03:00
Gabi Melman
8b11ffe163 Merge pull request #413 from odeits/patch-3
Fix typo immediatly immediately
2017-04-07 10:40:21 +03:00
Gabi Melman
0c89beaa58 Merge pull request #415 from odeits/patch-5
fix typos in base_sink.h
2017-04-07 10:39:33 +03:00
Gabi Melman
d35e229c44 Merge pull request #414 from odeits/patch-4
fix typo potentialy to potentially
2017-04-07 10:38:54 +03:00
Gabi Melman
0a70ef8438 Merge pull request #412 from odeits/patch-2
Fix typo in comment Unkown to Unknown
2017-04-07 10:38:23 +03:00
odeits
6670d3b925 fix typos in base_sink.h 2017-04-06 20:16:49 -04:00
odeits
82404f6f65 fix typo potentialy to potentially 2017-04-06 20:13:53 -04:00
odeits
8d5ecc1b58 Fix typo immediatly immediately 2017-04-06 20:12:11 -04:00
odeits
682d2e057f Fix typo in comment Unkown to Unknown 2017-04-06 18:46:52 -04:00
Gabi Melman
15af514951 Update file_helper.h 2017-04-02 13:05:02 +03:00
Gabi Melman
129781fd17 Merge pull request #408 from odeits/patch-1
Fix typo Unkown to Unknown
2017-03-29 23:36:19 +03:00
Gabi Melman
b9e8fd209a Merge pull request #407 from eliaskosunen/master
Update example in README.md
2017-03-29 23:35:16 +03:00
odeits
ea359254d6 Fix typo Unkown to Unknown 2017-03-29 15:53:14 -04:00
Elias Kosunen
a3e84cb347 Update example in README.md 2017-03-29 22:28:37 +03:00
Gabi Melman
51b3cc0aef Merge pull request #405 from o-mdr/master
[#404] Reading past valid address with multisink logger
2017-03-29 18:48:39 +03:00
Oleksii Mandrychenko
d315bba1f8 Initialising members via constructor 2017-03-29 16:06:59 +01:00
Oleksii Mandrychenko
b5d838cc32 - Reverting changes to mpmc queue 2017-03-29 16:04:24 +01:00
Oleksii Mandrychenko
6a41bc40af [#404] Reading past valid address with multisink logger
- Initialising atomic value

See examples at http://stackoverflow.com/q/36320008/706456
This issue was discovered with dr memory tool on Windows platform, Visual Studio 2015 C++ 11
2017-03-29 13:53:11 +01:00
Oleksii Mandrychenko
b638c71d26 [#404] Reading past valid address with multisink logger
- Initialising atomic value

See examples at http://stackoverflow.com/q/36320008/706456
This issue was discovered with dr memory tool on Windows platform, Visual Studio 2015 C++ 11
2017-03-29 11:27:59 +01:00
Gabi Melman
2b5c3615fd Update async_log_helper.h 2017-03-28 03:25:53 +03:00
gabime
f85a08622e version 0.13.0 2017-03-28 02:09:01 +03:00
gabime
0c276beaaf astyle 2017-03-28 02:08:18 +03:00
gabime
5a8cecdfb6 fix unused warning message 2017-03-28 02:05:59 +03:00
gabime
397d4866b3 Fixed issue #396 and added some tests to catch it 2017-03-28 01:54:33 +03:00
Gabi Melman
27df6eb4ca Merge pull request #403 from Falconne/master
Disambiguate fmt logging methods that are using variadic templates.
2017-03-28 00:27:29 +03:00
Anuradha Dissanayake
ad1c18704d Disambiguate fmt logging methods that are using variadic templates.
As variadic template arguments can be zero length, we need to specify that at least one fmt argument is provided, to distinguish these methods from the existing trivial method that takes no fmt arguments.

Without this, static analysers such as ReSharper flag the logging calls as errors.
2017-03-27 08:58:03 +13:00
Gabi Melman
029e6ed40f Merge pull request #399 from devanshdalal/patch-1
Update README.md
2017-03-22 08:41:27 +00:00
Devansh D
a55615c984 Update README.md 2017-03-22 13:52:56 +05:30
Gabi Melman
e8da69ebe1 Merge pull request #394 from zamaudio/spdlog-updatefmt-3.0.1
fmt: update bundled fmt to 3.0.1 (7fa8f8f)
2017-03-20 14:41:06 +02:00
Damien Zammit
8192c13379 fmt: update bundled fmt to 3.0.1 (7fa8f8f)
Signed-off-by: Damien Zammit <damien@zamaudio.com>
2017-03-20 15:25:10 +11:00
Gabi Melman
270c08b275 Merge pull request #377 from tekezo/master
Use double-braces in std::array initialization.
2017-03-02 20:18:55 +02:00
Takayama Fumihiko
a4714a6571 use double-braces in std::array initialization 2017-03-03 01:37:53 +09:00
Gabi Melman
5585299b03 Merge pull request #375 from horar/master
Don't hardcode '.txt.' log file name suffix (resolve #333)
2017-02-28 09:19:07 +02:00
Ľubomír Carik
fd8df5b820 Don't hardcode '.txt.' log file name suffix (resolve #333)
Signed-off-by: Ľubomír Carik <Lubomir.Carik@gmail.com>
2017-02-28 00:59:23 +01:00
Gabi Melman
d7a8db8f63 Update README.md 2017-02-21 12:13:04 +02:00
gabime
93d84e5d59 v0.12.0 2017-02-17 16:40:59 +02:00
Gabi Melman
311937815e Merge pull request #370 from jcelerier/fix/remove_static_global_strings
Replace static global std::string arrays by Meyer singletons.
2017-02-16 15:39:15 +02:00
Jean-Michaël Celerier
ef665e959f Don't deduce return types for days / months
Required for C++11 support
2017-02-16 13:17:13 +01:00
Gabi Melman
20f4428696 Update README.md 2017-02-16 02:12:50 +02:00
Gabi Melman
b65c7bad9f Update README.md 2017-02-16 02:10:18 +02:00
Jean-Michaël Celerier
06b8193a14 Add missing consts 2017-02-15 14:41:57 +01:00
Jean-Michaël Celerier
a626ebbbec Replace static global std::string arrays by Meyer singletons. This improves thread-safety. 2017-02-15 14:31:51 +01:00
Gabi Melman
054eb555ca Moved SPDLOG_VERSION macro to spdlog.h 2017-02-03 15:28:32 +02:00
Gabi Melman
4f50c5d143 Update spdlog.h 2017-02-03 15:27:06 +02:00
Gabi Melman
7481a8ecf7 Update common.h 2017-02-03 15:26:47 +02:00
Gabi Melman
a76a5cfc9c Update README.md 2017-02-03 15:19:23 +02:00
Gabi Melman
904bed92c3 Added SPDLOG_VERSION macro to common.h 2017-02-03 15:11:08 +02:00
Gabi Melman
8650c15749 Updated install options section 2017-02-03 14:44:03 +02:00
Gabi Melman
35865ee54e Merge pull request #348 from w0land/cmake_prefix
Add prefix for BUILD_TESTING cmake option
2017-01-11 11:29:15 +02:00
Bartosz Taczała
50c181ea4b Add prefix for BUILD_TESTING cmake option
This is helpful when using spdlog as a dependency (git submodule) when a
master project is not interested in spdlog tests. Using
"BUILD_TESTING" name may create a confusion.
Extra: BUILD_EXAMPLE variable already have a prefix.
2017-01-11 09:32:55 +01:00
Gabi Melman
2ec188041f Update README.md 2017-01-06 12:39:12 +02:00
Gabi Melman
e7ec922c0a Update async_log_helper.h
removed empty lines
2017-01-06 12:32:25 +02:00
Gabi Melman
38456118d0 Update null_sink.h 2016-12-31 22:38:00 +02:00
gabime
0a3a3f0ee2 Updated comment on thread safety 2016-12-31 17:54:37 +02:00
Gabi Melman
58853d9c95 Update README.md 2016-12-30 12:16:44 +02:00
Gabi Melman
5ee14906c5 Update README.md 2016-12-30 12:16:09 +02:00
gabime
2d873785a5 astyle 2016-12-10 02:05:05 +02:00
gabime
fec467da7b extra blank line between comments 2016-12-10 02:04:20 +02:00
gabime
b5f34c5320 typo in comment 2016-12-10 02:03:13 +02:00
gabime
92db8115b7 option to prevent child processes from inheriting log file desciptors (#define SPDLOG_PREVENT_CHILD_FD) 2016-12-10 01:43:43 +02:00
Gabi Melman
af35f9c086 Merge pull request #330 from PMExtra/master
Use the feature checking macros to detect thread_local support in clang.
2016-12-09 21:47:58 +02:00
Gabi Melman
0e016882d9 Update os.h 2016-12-06 19:47:33 +02:00
PM_Extra
b1a55ca3a4 Use the feature checking macros to detect thread_local support in clang. 2016-12-04 13:42:52 +08:00
Gabi Melman
68cc3e6856 Merge pull request #329 from PMExtra/master
fixed compilation error in clang before version 8
2016-12-02 20:24:12 +02:00
PM_Extra
f7574eb4c7 fixed compilation error in clang before version 8 (does not support thread_local keyword) 2016-12-03 01:40:52 +08:00
gabime
33494049a8 fixed compilation error in 2013 (does not support thread_local keyword) 2016-12-02 17:48:10 +02:00
gabime
5d23e88c16 astyle 2016-12-02 17:40:40 +02:00
gabime
6d8efa8d7f store thread_id in tls 2016-12-02 17:33:49 +02:00
gabime
343904b56d add missing include to widnows _getpid 2016-12-02 17:12:24 +02:00
gabime
cf73f02eaf pid support (added the %P option to formatter) 2016-12-02 17:09:00 +02:00
Gabi Melman
cda27d2bff Merge pull request #322 from onnodb/FixIssue321
Fix compilation errors in "wincolor_sink.h"
2016-11-25 17:13:55 +02:00
Onno Broekmans
b61be7320a Fix compilation errors in "wincolor_sink.h" 2016-11-25 15:47:21 +01:00
Gabi Melman
b2ce64625d Merge pull request #319 from jktjkt/cmake
cmake: misc improvements
2016-11-22 13:39:32 +02:00
Jan Kundrát
f058d3aa74 cmake: use -Wall on GCC and Clang
These checks come from [1]. The `MATCHES` operator is used for clang
because of Apple's special string.

[1] http://stackoverflow.com/questions/10046114/in-cmake-how-can-i-test-if-the-compiler-is-clang/10055571#10055571
2016-11-22 10:31:01 +01:00
Jan Kundrát
1c31800210 cmake: Use a standard option for controlling the tests
As per the docs [1], there's a standard variable for this purpose. This
introduces a behavior change, the tests are now being built by default.

[1] https://cmake.org/cmake/help/v3.0/module/CTest.html
2016-11-22 10:20:13 +01:00
Jan Kundrát
61cdd170fd cmake: List spdlog's content in IDEs
This is a usual CMake way of ensuring that IDEs have a way of showing
all source files which comprise this header-only library. It works in
the Qt Creator, for example.
2016-11-22 10:10:52 +01:00
gabime
d6b34d7b5c Updated to fmt version 796beaaddb5226162fe00c2c55e322d80d26f3d8 2016-11-19 23:43:39 +02:00
gabime
bd6fe569b5 astyle previous commits 2016-11-18 17:17:09 +02:00
gabime
d142f13551 Updated fmto to version def687462c32ec40757e49eb6069f109d50236d6 2016-11-18 17:13:53 +02:00
gabime
e12916c070 Fixed issue #317 (Build error on raspberry pi) 2016-11-18 16:58:57 +02:00
gabime
f6cece206a Merge 2016-11-18 16:58:30 +02:00
gabime
817371b225 Fix issue #317 (Build error on raspberry pi) 2016-11-18 16:56:25 +02:00
Gabi Melman
9eee823041 Fix issue #315 2016-11-14 14:58:10 +02:00
Gabi Melman
4e768c146b Merge pull request #314 from osx2000/master
Compatibility with Oracle Developer Studio 12.5 on  Solaris
2016-11-11 15:38:32 +02:00
osx2000
3cd497ee95 extended conditional compilation to __SUNPRO_CC 2016-11-11 14:28:45 +01:00
osx2000
e9fc4ac095 Fully qualified std::this_thread::yield() 2016-11-11 14:27:07 +01:00
Gabi Melman
9ccb6af2bd Merge pull request #305 from chenhayat/vs2013_fix
Fix Klockwork compilation warning
2016-11-03 16:30:25 +02:00
Chen Hayat
5259b3dbf4 Fix Klockwork compilation warning 2016-11-03 14:19:02 +02:00
Gabi Melman
8c67d6e05e Merge pull request #304 from chenhayat/vs2013_fix
Fix compilation error C2664  on VS2013
2016-11-02 15:59:34 +02:00
Chen Hayat
b4cb1febf2 removed external library changes 2016-11-02 15:43:30 +02:00
Chen Hayat
0c16b9ae1e Remove casting from previous commit and fix the following Klockwork issues:
1. Removing "return" from void functions.
2. Using "const" for operator= argument.
2016-11-01 17:16:07 +02:00
Chen Hayat
83d192b1f1 Fix compilation error C2664 on VS2013
No converting constructor
2016-10-30 17:11:45 +02:00
gabime
87ddb9a6c1 astyle 2016-10-20 12:14:25 +03:00
gabime
6128a87da2 Fix issue #300 2016-10-20 12:11:31 +03:00
Gabi Melman
698783861c Fixed vc warning on x64 build 2016-10-17 12:50:38 +03:00
gabime
f14d1c002b astyle 2016-10-12 23:08:44 +03:00
gabime
0cfdad4d0b Windows console color support. Replaced color param in API with new functions 2016-10-12 15:10:10 +03:00
Gabi Melman
94dbefe9b6 Merge pull request #296 from guoxiao/fix
Fix several compiler warnings
2016-10-09 11:39:49 +03:00
Guo Xiao
73e53c7cb6 Remove extra ';' 2016-10-09 15:36:19 +08:00
Guo Xiao
9b218d4d12 Convert off_t to size_t 2016-10-09 15:36:19 +08:00
Gabi Melman
541dd88a97 Update common.h 2016-10-09 01:55:47 +03:00
Gabi Melman
ca928bc1b0 Fix issue #295
vs 2013 not supports std::atomic_int
2016-10-09 00:41:29 +03:00
Gabi Melman
b642482432 removed empty line 2016-10-08 20:58:29 +03:00
gabime
703a1d9736 added g3log crush example 2016-10-01 18:15:21 +03:00
gabime
6ce507eceb bench: added latency comparison with g3log 2016-10-01 16:55:13 +03:00
gabime
56678a5f6a added set_force_flush(bool) to simple file sink for performance benchmarks 2016-10-01 16:37:33 +03:00
gabime
12800ac466 fixed shadow warnings in gcc 2016-09-30 14:08:31 +03:00
gabime
faa184ce24 Added #ifdef __ANDROID__ to spllog_impl.h 2016-09-29 23:49:03 +03:00
Gabi Melman
fa175d6300 Merge pull request #289 from thomas-frantz/expose_sinks
Exposed logger sinks.
2016-09-25 03:47:36 +03:00
Therenall
66b08294ca Exposed logger sinks. 2016-09-24 15:14:05 -04:00
Gabi Melman
9e37f5cef9 Merge pull request #285 from azadkuh/issue280
update os.h to fix filesize() on older win32
2016-09-20 13:14:44 +03:00
amir zamani
811eeef7a6 update os.h to fix filesize() on older win32
_fstat() always fails under older 32bit WinXP/Win2003 targets.

_filelength() just works for both WinXP SDK and later Win7+ 32bit targets.
2016-09-20 14:13:15 +04:30
68 changed files with 3500 additions and 839 deletions

3
.gitignore vendored
View File

@@ -62,3 +62,6 @@ install_manifest.txt
/tests/tests.VC.db
/tests/tests
/tests/logs/file_helper_test.txt
# idea
.idea/

View File

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

View File

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

View File

@@ -4,27 +4,42 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
## Install
Just copy the source [folder](https://github.com/gabime/spdlog/tree/master/include/spdlog) to your build tree and use a C++11 compiler
#### Just copy the headers:
* Copy the source [folder](https://github.com/gabime/spdlog/tree/master/include/spdlog) to your build tree and use a C++11 compiler.
#### Or use your favorite package manager:
* Ubuntu: `apt-get install libspdlog-dev`
* Homebrew: `brew install spdlog`
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
* Fedora: `yum install spdlog`
* Gentoo: `emerge dev-libs/spdlog`
* Arch Linux: `yaourt -S spdlog-git`
* vcpkg: `vcpkg install spdlog`
## Platforms
* Linux (gcc 4.8.1+, clang 3.5+)
* Windows (visual studio 2013+, cygwin/mingw with g++ 4.9.1+)
* Linux, FreeBSD, Solaris
* Windows (vc 2013+, cygwin/mingw)
* Mac OSX (clang 3.5+)
* Solaris (gcc 5.2.0+)
* Android
##Features
## Features
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
* Headers only, just copy and use.
* Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library.
* Optional printf syntax support.
* Extremely fast asynchronous mode (optional) - using lockfree queues and other tricks to reach millions of calls/sec.
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
* Conditional Logging
* Multi/Single threaded loggers.
* Various log targets:
* Rotating log files.
* Daily log files.
* Console logging (colors supported).
* syslog.
* Windows debugger (```OutputDebugString(..)```)
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
* Severity based filtering - threshold levels can be modified in runtime as well as in compile time.
@@ -74,83 +89,89 @@ int main(int, char*[])
{
try
{
// Multithreaded color console
auto console = spd::stdout_logger_mt("console", true);
// Console logger with color
auto console = spd::stdout_color_mt("console");
console->info("Welcome to spdlog!");
console->info("An info message example {}..", 1);
console->error("Some error message with arg{}..", 1);
// Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12);
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
console->info("Support for floats {:03.2f}", 1.23456);
console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");
console->info("{:>30}", "right aligned");
console->info("{:^30}", "centered");
// Use global registry to retrieve loggers
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
// Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info
console->debug("This message shold not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message shold be displayed..");
// Create basic file logger (not rotated)
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt");
my_logger->info("Some log message");
// Create a file rotating logger with 5mb size max and 3 rotated files
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3);
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile.txt", 1048576 * 5, 3);
for (int i = 0; i < 10; ++i)
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
// Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
// trigger flush if the log severity is error or higher
daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44);
// Customize msg format for all messages
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
rotating_logger->info("This is another message with custom format");
// Compile time debug or trace macros.
// Enabled #ifdef SPDLOG_DEBUG_ON or #ifdef SPDLOG_TRACE_ON
// Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info
console->debug("This message should not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message should be displayed..");
// Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
// Asynchronous logging is very fast..
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
async_example();
// syslog example. linux/osx only..
// syslog example. linux/osx only
syslog_example();
// Log user-defined types example..
// android example. compile with NDK
android_example();
// Log user-defined types example
user_defined_example();
// Change default log error handler
err_handler_example();
// Apply a function on all registered loggers
spd::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->info("End of example."); });
spd::apply_all([&](std::shared_ptr<spd::logger> l)
{
l->info("End of example.");
});
// Release and close all loggers
spdlog::drop_all();
spd::drop_all();
}
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
catch (const spd::spdlog_ex& ex)
{
std::cout << "Log init failed: " << ex.what() << std::endl;
return 1;
}
}
}
void async_example()
{
size_t q_size = 4096; //queue size must be power of 2
spdlog::set_async_mode(q_size);
spd::set_async_mode(q_size);
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
for (int i = 0; i < 100; ++i)
async_file->info("Async message #{}", i);
@@ -188,7 +209,7 @@ void user_defined_example()
//
void err_handler_example()
{
spdlog::set_error_handler([](const std::string& msg) {
spd::set_error_handler([](const std::string& msg) {
std::cerr << "my err handler: " << msg << std::endl;
});
// (or logger->set_error_handler(..) to set for specific logger)

32
appveyor.yml Normal file
View File

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

View File

@@ -2,7 +2,6 @@
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>

32
bench/latency/Makefile Normal file
View File

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

13
bench/latency/compare.sh Executable file
View File

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

View File

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

View File

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

View File

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

35
bench/latency/utils.h Normal file
View File

@@ -0,0 +1,35 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <sstream>
#include <iomanip>
#include <locale>
namespace utils
{
template<typename T>
inline std::string format(const T& value)
{
static std::locale loc("");
std::stringstream ss;
ss.imbue(loc);
ss << value;
return ss.str();
}
template<>
inline std::string format(const double & value)
{
static std::locale loc("");
std::stringstream ss;
ss.imbue(loc);
ss << std::fixed << std::setprecision(1) << value;
return ss.str();
}
}

View File

@@ -46,7 +46,7 @@ int main(int argc, char* argv[])
if (argc > 3)
queue_size = atoi(argv[3]);
cout << "\n*******************************************************************************\n";
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " messages " << endl;
cout << "*******************************************************************************\n";
@@ -64,7 +64,7 @@ int main(int argc, char* argv[])
}
std::cout << endl;
std::cout << "Avg rate: " << format(total_rate/iters) << "/sec" <<std::endl;
}
catch (std::exception &ex)
{

View File

@@ -1,5 +1,5 @@
CXX ?= g++
CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -Wshadow -pedantic -std=gnu++0x -pthread -Wl,--no-as-needed -I../include
CXX_RELEASE_FLAGS = -O3
CXX_DEBUG_FLAGS= -g

View File

@@ -6,6 +6,10 @@
// spdlog usage example
//
//
#define SPDLOG_TRACE_ON
#define SPDLOG_DEBUG_ON
#include "spdlog/spdlog.h"
#include <iostream>
@@ -22,40 +26,33 @@ int main(int, char*[])
{
try
{
// Multithreaded color console
auto console = spd::stdout_logger_mt("console", true);
// Console logger with color
auto console = spd::stdout_color_mt("console");
console->info("Welcome to spdlog!");
console->error("An error message example {}..", 1);
console->error("Some error message with arg{}..", 1);
// Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12);
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
console->info("Support for floats {:03.2f}", 1.23456);
console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");
console->info("{:>30}", "right aligned");
console->info("{:^30}", "centered");
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
// Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info
console->debug("This message shold not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message shold be displayed..");
// Create basic file logger (not rotated)
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt");
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt");
my_logger->info("Some log message");
// Create a file rotating logger with 5mb size max and 3 rotated files
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3);
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
for (int i = 0; i < 10; ++i)
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
// Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
// trigger flush if the log severity is error or higher
daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44);
@@ -64,11 +61,19 @@ int main(int, char*[])
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
rotating_logger->info("This is another message with custom format");
// Compile time debug or trace macros.
// Enabled #ifdef SPDLOG_DEBUG_ON or #ifdef SPDLOG_TRACE_ON
// Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info
console->debug("This message shold not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message shold be displayed..");
// Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
// Asynchronous logging is very fast..
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
async_example();
@@ -107,7 +112,6 @@ void async_example()
size_t q_size = 4096; //queue size must be power of 2
spdlog::set_async_mode(q_size);
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
for (int i = 0; i < 100; ++i)
async_file->info("Async message #{}", i);
}
@@ -143,7 +147,7 @@ struct my_type
}
};
#include <spdlog/fmt/ostr.h> // must be included
#include "spdlog/fmt/ostr.h" // must be included
void user_defined_example()
{
spd::get("console")->info("user defined type: {}", my_type { 14 });

View File

@@ -42,6 +42,7 @@
<ClInclude Include="..\include\spdlog\sinks\sink.h" />
<ClInclude Include="..\include\spdlog\sinks\stdout_sinks.h" />
<ClInclude Include="..\include\spdlog\sinks\syslog_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\wincolor_sink.h" />
<ClInclude Include="..\include\spdlog\spdlog.h" />
<ClInclude Include="..\include\spdlog\tweakme.h" />
</ItemGroup>
@@ -55,13 +56,13 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset>v120</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<PlatformToolset>v120</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>

View File

@@ -15,8 +15,8 @@
// 3. will throw spdlog_ex upon log exceptions
// Upon destruction, logs all remaining messages in the queue before destructing..
#include <spdlog/common.h>
#include <spdlog/logger.h>
#include "common.h"
#include "logger.h"
#include <chrono>
#include <functional>
@@ -31,7 +31,7 @@ namespace details
class async_log_helper;
}
class async_logger :public logger
class async_logger SPDLOG_FINAL :public logger
{
public:
template<class It>
@@ -61,12 +61,17 @@ public:
const std::function<void()>& worker_teardown_cb = nullptr);
//Wait for the queue to be empty, and flush synchronously
//Warning: this can potentialy last forever as we wait it to complete
//Warning: this can potentially last forever as we wait it to complete
void flush() override;
// Error handler
virtual void set_error_handler(log_err_handler) override;
virtual log_err_handler error_handler() override;
protected:
void _sink_it(details::log_msg& msg) override;
void _set_formatter(spdlog::formatter_ptr msg_formatter) override;
void _set_pattern(const std::string& pattern) override;
void _set_pattern(const std::string& pattern, pattern_time_type pattern_time) override;
private:
std::unique_ptr<details::async_log_helper> _async_log_helper;
@@ -74,4 +79,4 @@ private:
}
#include <spdlog/details/async_logger_impl.h>
#include "details/async_logger_impl.h"

View File

@@ -18,7 +18,7 @@
#include <locale>
#endif
#include <spdlog/details/null_mutex.h>
#include "details/null_mutex.h"
//visual studio upto 2013 does not support noexcept nor constexpr
#if defined(_MSC_VER) && (_MSC_VER < 1900)
@@ -29,16 +29,22 @@
#define SPDLOG_CONSTEXPR constexpr
#endif
#if defined(__GNUC__) || defined(__clang__)
#define DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
#define DEPRECATED __declspec(deprecated)
// final keyword support. On by default. See tweakme.h
#if defined(SPDLOG_NO_FINAL)
#define SPDLOG_FINAL
#else
#define DEPRECATED
#define SPDLOG_FINAL final
#endif
#if defined(__GNUC__) || defined(__clang__)
#define SPDLOG_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
#define SPDLOG_DEPRECATED __declspec(deprecated)
#else
#define SPDLOG_DEPRECATED
#endif
#include <spdlog/fmt/fmt.h>
#include "fmt/fmt.h"
namespace spdlog
{
@@ -57,7 +63,7 @@ using formatter_ptr = std::shared_ptr<spdlog::formatter>;
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
using level_t = details::null_atomic_int;
#else
using level_t = std::atomic_int;
using level_t = std::atomic<int>;
#endif
using log_err_handler = std::function<void(const std::string &err_msg)>;
@@ -76,7 +82,10 @@ typedef enum
off = 6
} level_enum;
static const char* level_names[] { "trace", "debug", "info", "warning", "error", "critical", "off" };
#if !defined(SPDLOG_LEVEL_NAMES)
#define SPDLOG_LEVEL_NAMES { "trace", "debug", "info", "warning", "error", "critical", "off" }
#endif
static const char* level_names[] SPDLOG_LEVEL_NAMES;
static const char* short_level_names[] { "T", "D", "I", "W", "E", "C", "O" };
@@ -101,6 +110,15 @@ enum class async_overflow_policy
discard_log_msg // Discard the message it enqueue fails
};
//
// Pattern time - specific time getting to use for pattern_formatter.
// local time by default
//
enum class pattern_time_type
{
local, // log localtime
utc // log utc
};
//
// Log exception

View File

@@ -9,17 +9,15 @@
// If the internal queue of log messages reaches its max size,
// then the client call will block until there is more room.
//
// If the back thread throws during logging, a spdlog::spdlog_ex exception
// will be thrown in client's thread when tries to log the next message
#pragma once
#include <spdlog/common.h>
#include <spdlog/sinks/sink.h>
#include <spdlog/details/mpmc_bounded_q.h>
#include <spdlog/details/log_msg.h>
#include <spdlog/details/os.h>
#include <spdlog/formatter.h>
#include "../common.h"
#include "../sinks/sink.h"
#include "../details/mpmc_bounded_q.h"
#include "../details/log_msg.h"
#include "../details/os.h"
#include "../formatter.h"
#include <chrono>
#include <exception>
@@ -53,6 +51,7 @@ class async_log_helper
size_t thread_id;
std::string txt;
async_msg_type msg_type;
size_t msg_id;
async_msg() = default;
~async_msg() = default;
@@ -62,12 +61,18 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
logger_name(std::move(other.logger_name)),
level(std::move(other.level)),
time(std::move(other.time)),
thread_id(other.thread_id),
txt(std::move(other.txt)),
msg_type(std::move(other.msg_type))
msg_type(std::move(other.msg_type)),
msg_id(other.msg_id)
{}
async_msg(async_msg_type m_type) :msg_type(m_type)
{};
async_msg(async_msg_type m_type):
level(level::info),
thread_id(0),
msg_type(m_type),
msg_id(0)
{}
async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT
{
@@ -77,20 +82,22 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
thread_id = other.thread_id;
txt = std::move(other.txt);
msg_type = other.msg_type;
msg_id = other.msg_id;
return *this;
}
// never copy or assign. should only be moved..
async_msg(const async_msg&) = delete;
async_msg& operator=(async_msg& other) = delete;
async_msg& operator=(const async_msg& other) = delete;
// construct from log_msg
async_msg(const details::log_msg& m) :
async_msg(const details::log_msg& m):
level(m.level),
time(m.time),
thread_id(m.thread_id),
txt(m.raw.data(), m.raw.size()),
msg_type(async_msg_type::log)
msg_type(async_msg_type::log),
msg_id(m.msg_id)
{
#ifndef SPDLOG_NO_NAME
logger_name = *m.logger_name;
@@ -106,6 +113,7 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
msg.time = time;
msg.thread_id = thread_id;
msg.raw << txt;
msg.msg_id = msg_id;
}
};
@@ -135,6 +143,7 @@ public:
void flush(bool wait_for_q);
void set_error_handler(spdlog::log_err_handler err_handler);
private:
formatter_ptr _formatter;
@@ -176,7 +185,7 @@ private:
void handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush);
// sleep,yield or return immediatly using the time passed since last message as a hint
// sleep,yield or return immediately using the time passed since last message as a hint
static void sleep_or_yield(const spdlog::log_clock::time_point& now, const log_clock::time_point& last_op_time);
// wait until the queue is empty
@@ -221,7 +230,8 @@ inline spdlog::details::async_log_helper::~async_log_helper()
_worker_thread.join();
}
catch (...) // don't crash in destructor
{}
{
}
}
@@ -229,8 +239,6 @@ inline spdlog::details::async_log_helper::~async_log_helper()
inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
{
push_msg(async_msg(msg));
}
inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg)
@@ -246,45 +254,48 @@ inline void spdlog::details::async_log_helper::push_msg(details::async_log_helpe
}
while (!_q.enqueue(std::move(new_msg)));
}
}
// optionally wait for the queue be empty and request flush from the sinks
inline void spdlog::details::async_log_helper::flush(bool wait_for_q)
{
push_msg(async_msg(async_msg_type::flush));
if(wait_for_q)
wait_empty_q(); //return only make after the above flush message was processed
if (wait_for_q)
wait_empty_q(); //return when queue is empty
}
inline void spdlog::details::async_log_helper::worker_loop()
{
try
if (_worker_warmup_cb) _worker_warmup_cb();
auto last_pop = details::os::now();
auto last_flush = last_pop;
auto active = true;
while (active)
{
if (_worker_warmup_cb) _worker_warmup_cb();
auto last_pop = details::os::now();
auto last_flush = last_pop;
while(process_next_msg(last_pop, last_flush));
if (_worker_teardown_cb) _worker_teardown_cb();
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
try
{
active = process_next_msg(last_pop, last_flush);
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch(...)
{
_err_handler("Unknown exeption in async logger worker loop.");
}
}
if (_worker_teardown_cb) _worker_teardown_cb();
}
// process next message in the queue
// return true if this thread should still be active (while no terminate msg was received)
inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush)
{
async_msg incoming_async_msg;
if (_q.dequeue(incoming_async_msg))
{
last_pop = details::os::now();
@@ -305,7 +316,7 @@ inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_
_formatter->format(incoming_log_msg);
for (auto &s : _sinks)
{
if(s->should_log( incoming_log_msg.level))
if (s->should_log(incoming_log_msg.level))
{
s->log(incoming_log_msg);
}
@@ -359,32 +370,30 @@ inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_
// yield upto 150 micros
if (time_since_op <= microseconds(100))
return yield();
return std::this_thread::yield();
// sleep for 20 ms upto 200 ms
if (time_since_op <= milliseconds(200))
return sleep_for(milliseconds(20));
// sleep for 200 ms
return sleep_for(milliseconds(200));
// sleep for 500 ms
return sleep_for(milliseconds(500));
}
// wait for the queue to be empty
inline void spdlog::details::async_log_helper::wait_empty_q()
{
auto last_op = details::os::now();
while (_q.approx_size() > 0)
while (!_q.is_empty())
{
sleep_or_yield(details::os::now(), last_op);
}
}
inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler)
{
_err_handler = err_handler;
}

View File

@@ -8,8 +8,8 @@
// Async Logger implementation
// Use an async_sink (queue per logger) to perform the logging in a worker thread
#include <spdlog/details/async_log_helper.h>
#include <spdlog/async_logger.h>
#include "../details/async_log_helper.h"
#include "../async_logger.h"
#include <string>
#include <functional>
@@ -31,13 +31,13 @@ inline spdlog::async_logger::async_logger(const std::string& logger_name,
}
inline spdlog::async_logger::async_logger(const std::string& logger_name,
sinks_init_list sinks,
sinks_init_list sinks_list,
size_t queue_size,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms,
const std::function<void()>& worker_teardown_cb) :
async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
async_logger(logger_name, sinks_list.begin(), sinks_list.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
inline spdlog::async_logger::async_logger(const std::string& logger_name,
sink_ptr single_sink,
@@ -57,15 +57,28 @@ inline void spdlog::async_logger::flush()
_async_log_helper->flush(true);
}
// Error handler
inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_handler)
{
_err_handler = err_handler;
_async_log_helper->set_error_handler(err_handler);
}
inline spdlog::log_err_handler spdlog::async_logger::error_handler()
{
return _err_handler;
}
inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)
{
_formatter = msg_formatter;
_async_log_helper->set_formatter(_formatter);
}
inline void spdlog::async_logger::_set_pattern(const std::string& pattern)
inline void spdlog::async_logger::_set_pattern(const std::string& pattern, pattern_time_type pattern_time)
{
_formatter = std::make_shared<pattern_formatter>(pattern);
_formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
_async_log_helper->set_formatter(_formatter);
}
@@ -74,6 +87,9 @@ inline void spdlog::async_logger::_sink_it(details::log_msg& msg)
{
try
{
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
_incr_msg_counter(msg);
#endif
_async_log_helper->log(msg);
if (_should_flush_on(msg))
_async_log_helper->flush(false); // do async flush
@@ -82,8 +98,10 @@ inline void spdlog::async_logger::_sink_it(details::log_msg& msg)
{
_err_handler(ex.what());
}
catch (...)
catch(...)
{
_err_handler("Unknown exception");
_err_handler("Unknown exception in logger " + _name);
throw;
}
}

View File

@@ -7,16 +7,16 @@
// Helper class for file sink
// When failing to open a file, retry several times(5) with small delay between the tries(10 ms)
// Can be set to auto flush on every line
// Throw spdlog_ex exception on errors
#include <spdlog/details/os.h>
#include <spdlog/details/log_msg.h>
#include "../details/os.h"
#include "../details/log_msg.h"
#include <chrono>
#include <cstdio>
#include <string>
#include <thread>
#include <tuple>
#include <cerrno>
namespace spdlog
@@ -32,7 +32,7 @@ public:
const int open_interval = 10;
explicit file_helper() :
_fd(nullptr)
_fd(nullptr)
{}
file_helper(const file_helper&) = delete;
@@ -85,14 +85,13 @@ public:
void write(const log_msg& msg)
{
size_t msg_size = msg.formatted.size();
auto data = msg.formatted.data();
if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
}
size_t size()
size_t size() const
{
if (!_fd)
throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
@@ -104,15 +103,35 @@ public:
return _filename;
}
static bool file_exists(const filename_t& name)
static bool file_exists(const filename_t& fname)
{
return os::file_exists(fname);
}
return os::file_exists(name);
//
// return basename and extension:
//
// "mylog.txt" => ("mylog", ".txt")
// "mylog" => ("mylog", "")
//
// the starting dot in filenames is ignored (hidden files):
//
// "my_folder/.mylog" => ("my_folder/.mylog")
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
static std::tuple<filename_t, filename_t> split_by_extenstion(const filename_t& fname)
{
auto index = fname.rfind('.');
bool found_ext = index != filename_t::npos && index !=0 && fname[index - 1] != details::os::folder_sep;
if (found_ext)
return std::make_tuple(fname.substr(0, index), fname.substr(index));
else
return std::make_tuple(fname, filename_t());
}
private:
FILE* _fd;
filename_t _filename;
filename_t _filename;
};
}
}

View File

@@ -5,8 +5,8 @@
#pragma once
#include <spdlog/common.h>
#include <spdlog/details/os.h>
#include "../common.h"
#include "../details/os.h"
#include <string>
@@ -19,7 +19,10 @@ namespace details
struct log_msg
{
log_msg() = default;
log_msg(const std::string *loggers_name, level::level_enum lvl) : logger_name(loggers_name), level(lvl)
log_msg(const std::string *loggers_name, level::level_enum lvl) :
logger_name(loggers_name),
level(lvl),
msg_id(0)
{
#ifndef SPDLOG_NO_DATETIME
time = os::now();
@@ -41,6 +44,7 @@ struct log_msg
size_t thread_id;
fmt::MemoryWriter raw;
fmt::MemoryWriter formatted;
size_t msg_id;
};
}
}

View File

@@ -5,24 +5,24 @@
#pragma once
#include <spdlog/logger.h>
#include <spdlog/sinks/stdout_sinks.h>
#include "../logger.h"
#include "../sinks/stdout_sinks.h"
#include <memory>
#include <string>
// create logger with given name, sinks and the default pattern formatter
// all other ctors will call this one
template<class It>
inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end):
_name(logger_name),
_sinks(begin, end),
_formatter(std::make_shared<pattern_formatter>("%+"))
_formatter(std::make_shared<pattern_formatter>("%+")),
_level(level::info),
_flush_level(level::off),
_last_err_time(0),
_msg_counter(1) // message counter will start from 1. 0-message id will be reserved for controll messages
{
_level = level::info;
_flush_level = level::off;
_last_err_time = 0;
_err_handler = [this](const std::string &msg)
{
this->_default_err_handler(msg);
@@ -52,12 +52,11 @@ inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter)
_set_formatter(msg_formatter);
}
inline void spdlog::logger::set_pattern(const std::string& pattern)
inline void spdlog::logger::set_pattern(const std::string& pattern, pattern_time_type pattern_time)
{
_set_pattern(pattern);
_set_pattern(pattern, pattern_time);
}
template <typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args)
{
@@ -66,16 +65,22 @@ inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Ar
try
{
details::log_msg log_msg(&_name, lvl);
#if defined(SPDLOG_FMT_PRINTF)
fmt::printf(log_msg.raw, fmt, args...);
#else
log_msg.raw.write(fmt, args...);
#endif
_sink_it(log_msg);
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
catch(...)
{
_err_handler("Unknown exception");
_err_handler("Unknown exception in logger " + _name);
throw;
}
}
@@ -93,11 +98,6 @@ inline void spdlog::logger::log(level::level_enum lvl, const char* msg)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
}
template<typename T>
@@ -114,48 +114,43 @@ inline void spdlog::logger::log(level::level_enum lvl, const T& msg)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
}
template <typename... Args>
inline void spdlog::logger::trace(const char* fmt, const Args&... args)
template <typename Arg1, typename... Args>
inline void spdlog::logger::trace(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::trace, fmt, args...);
log(level::trace, fmt, arg1, args...);
}
template <typename... Args>
inline void spdlog::logger::debug(const char* fmt, const Args&... args)
template <typename Arg1, typename... Args>
inline void spdlog::logger::debug(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::debug, fmt, args...);
log(level::debug, fmt, arg1, args...);
}
template <typename... Args>
inline void spdlog::logger::info(const char* fmt, const Args&... args)
template <typename Arg1, typename... Args>
inline void spdlog::logger::info(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::info, fmt, args...);
log(level::info, fmt, arg1, args...);
}
template <typename... Args>
inline void spdlog::logger::warn(const char* fmt, const Args&... args)
template <typename Arg1, typename... Args>
inline void spdlog::logger::warn(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::warn, fmt, args...);
log(level::warn, fmt, arg1, args...);
}
template <typename... Args>
inline void spdlog::logger::error(const char* fmt, const Args&... args)
template <typename Arg1, typename... Args>
inline void spdlog::logger::error(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::err, fmt, args...);
log(level::err, fmt, arg1, args...);
}
template <typename... Args>
inline void spdlog::logger::critical(const char* fmt, const Args&... args)
template <typename Arg1, typename... Args>
inline void spdlog::logger::critical(const char* fmt, const Arg1 &arg1, const Args&... args)
{
log(level::critical, fmt, args...);
log(level::critical, fmt, arg1, args...);
}
@@ -199,6 +194,67 @@ inline void spdlog::logger::critical(const T& msg)
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
#include <codecvt>
#include <locale>
template <typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* msg)
{
std::wstring_convert<std::codecvt_utf8<wchar_t> > conv;
log(lvl, conv.to_bytes(msg));
}
template <typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* fmt, const Args&... args)
{
fmt::WMemoryWriter wWriter;
wWriter.write(fmt, args...);
log(lvl, wWriter.c_str());
}
template <typename... Args>
inline void spdlog::logger::trace(const wchar_t* fmt, const Args&... args)
{
log(level::trace, fmt, args...);
}
template <typename... Args>
inline void spdlog::logger::debug(const wchar_t* fmt, const Args&... args)
{
log(level::debug, fmt, args...);
}
template <typename... Args>
inline void spdlog::logger::info(const wchar_t* fmt, const Args&... args)
{
log(level::info, fmt, args...);
}
template <typename... Args>
inline void spdlog::logger::warn(const wchar_t* fmt, const Args&... args)
{
log(level::warn, fmt, args...);
}
template <typename... Args>
inline void spdlog::logger::error(const wchar_t* fmt, const Args&... args)
{
log(level::err, fmt, args...);
}
template <typename... Args>
inline void spdlog::logger::critical(const wchar_t* fmt, const Args&... args)
{
log(level::critical, fmt, args...);
}
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
//
// name and level
@@ -244,6 +300,9 @@ inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) cons
//
inline void spdlog::logger::_sink_it(details::log_msg& msg)
{
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
_incr_msg_counter(msg);
#endif
_formatter->format(msg);
for (auto &sink : _sinks)
{
@@ -257,9 +316,9 @@ inline void spdlog::logger::_sink_it(details::log_msg& msg)
flush();
}
inline void spdlog::logger::_set_pattern(const std::string& pattern)
inline void spdlog::logger::_set_pattern(const std::string& pattern, pattern_time_type pattern_time)
{
_formatter = std::make_shared<pattern_formatter>(pattern);
_formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
}
inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter)
{
@@ -291,3 +350,14 @@ inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg)
const auto flush_level = _flush_level.load(std::memory_order_relaxed);
return (msg.level >= flush_level) && (msg.level != level::off);
}
inline void spdlog::logger::_incr_msg_counter(details::log_msg &msg)
{
msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed);
}
inline const std::vector<spdlog::sink_ptr>& spdlog::logger::sinks() const
{
return _sinks;
}

View File

@@ -43,7 +43,7 @@ Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once
#include <spdlog/common.h>
#include "../common.h"
#include <atomic>
#include <utility>
@@ -61,11 +61,11 @@ public:
using item_type = T;
mpmc_bounded_queue(size_t buffer_size)
:max_size_(buffer_size),
buffer_(new cell_t [buffer_size]),
buffer_(new cell_t[buffer_size]),
buffer_mask_(buffer_size - 1)
{
//queue size must be power of two
if(!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0)))
if (!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0)))
throw spdlog_ex("async logger queue size must be power of two");
for (size_t i = 0; i != buffer_size; i += 1)
@@ -76,7 +76,7 @@ public:
~mpmc_bounded_queue()
{
delete [] buffer_;
delete[] buffer_;
}
@@ -88,7 +88,7 @@ public:
{
cell = &buffer_[pos & buffer_mask_];
size_t seq = cell->sequence_.load(std::memory_order_acquire);
intptr_t dif = (intptr_t)seq - (intptr_t)pos;
intptr_t dif = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos);
if (dif == 0)
{
if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
@@ -117,7 +117,7 @@ public:
cell = &buffer_[pos & buffer_mask_];
size_t seq =
cell->sequence_.load(std::memory_order_acquire);
intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1);
intptr_t dif = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos + 1);
if (dif == 0)
{
if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
@@ -133,14 +133,18 @@ public:
return true;
}
size_t approx_size()
bool is_empty()
{
size_t first_pos = dequeue_pos_.load(std::memory_order_relaxed);
size_t last_pos = enqueue_pos_.load(std::memory_order_relaxed);
if (last_pos <= first_pos)
return 0;
auto size = last_pos - first_pos;
return size < max_size_ ? size : max_size_;
size_t front, front1, back;
// try to take a consistent snapshot of front/tail.
do
{
front = enqueue_pos_.load(std::memory_order_acquire);
back = dequeue_pos_.load(std::memory_order_acquire);
front1 = enqueue_pos_.load(std::memory_order_relaxed);
}
while (front != front1);
return back == front;
}
private:
@@ -153,7 +157,7 @@ private:
size_t const max_size_;
static size_t const cacheline_size = 64;
typedef char cacheline_pad_t [cacheline_size];
typedef char cacheline_pad_t[cacheline_size];
cacheline_pad_t pad0_;
cell_t* const buffer_;

View File

@@ -4,16 +4,19 @@
//
#pragma once
#include <spdlog/common.h>
#include "../common.h"
#include <cstdio>
#include <ctime>
#include <functional>
#include <string>
#include <stdio.h>
#include <string.h>
#include <chrono>
#include <thread>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <sys/stat.h>
#include <sys/types.h>
#ifdef _WIN32
@@ -25,27 +28,32 @@
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <process.h> // _get_pid support
#include <io.h> // _get_osfhandle and _isatty support
#ifdef __MINGW32__
#include <share.h>
#endif
#include <sys/types.h>
#else // unix
#elif __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
#include <unistd.h>
#include <chrono>
#include <fcntl.h>
#ifdef __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
#elif __FreeBSD__
#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
#else
#include <thread>
#endif
#endif //unix
#ifndef __has_feature // Clang - feature checking macros.
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif
namespace spdlog
{
namespace details
@@ -137,6 +145,28 @@ SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1;
// folder separator
#ifdef _WIN32
SPDLOG_CONSTEXPR static const char folder_sep = '\\';
#else
SPDLOG_CONSTEXPR static const char folder_sep = '/';
#endif
inline void prevent_child_fd(FILE *f)
{
#ifdef _WIN32
auto file_handle = (HANDLE)_get_osfhandle(_fileno(f));
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
throw spdlog_ex("SetHandleInformation failed", errno);
#else
auto fd = fileno(f);
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno);
#endif
}
//fopen_s on non windows for writing
inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode)
{
@@ -146,13 +176,18 @@ inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode
#else
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
#endif
return *fp == nullptr;
#else
#else //unix
*fp = fopen((filename.c_str()), mode.c_str());
return *fp == nullptr;
#endif
#ifdef SPDLOG_PREVENT_CHILD_FD
if (*fp != nullptr)
prevent_child_fd(*fp);
#endif
return *fp == nullptr;
}
inline int remove(const filename_t &filename)
{
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
@@ -184,7 +219,7 @@ inline bool file_exists(const filename_t& filename)
return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
#else //common linux/unix all have the stat system call
struct stat buffer;
return (stat (filename.c_str(), &buffer) == 0);
return (stat(filename.c_str(), &buffer) == 0);
#endif
}
@@ -196,7 +231,7 @@ inline size_t filesize(FILE *f)
{
if (f == nullptr)
throw spdlog_ex("Failed getting file size. fd is null");
#ifdef _WIN32
#if defined ( _WIN32) && !defined(__CYGWIN__)
int fd = _fileno(f);
#if _WIN64 //64 bits
struct _stat64 st;
@@ -204,22 +239,22 @@ inline size_t filesize(FILE *f)
return st.st_size;
#else //windows 32 bits
struct _stat st;
if (_fstat(fd, &st) == 0)
return st.st_size;
long ret = _filelength(fd);
if (ret >= 0)
return static_cast<size_t>(ret);
#endif
#else // unix
int fd = fileno(f);
//64 bits(but not in osx, where fstat64 is deprecated)
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__))
//64 bits(but not in osx or cygwin, where fstat64 is deprecated)
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__)
struct stat64 st;
if (fstat64(fd, &st) == 0)
return st.st_size;
#else // unix 32 bits or osx
return static_cast<size_t>(st.st_size);
#else // unix 32 bits or cygwin
struct stat st;
if (fstat(fd, &st) == 0)
return st.st_size;
return static_cast<size_t>(st.st_size);
#endif
#endif
throw spdlog_ex("Failed getting file size from fd", errno);
@@ -291,8 +326,8 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
}
//Return current thread id as size_t
//It exists because the std::this_thread::get_id() is much slower(espcially under VS 2013)
inline size_t thread_id()
//It exists because the std::this_thread::get_id() is much slower(especially under VS 2013)
inline size_t _thread_id()
{
#ifdef _WIN32
return static_cast<size_t>(::GetCurrentThreadId());
@@ -305,9 +340,24 @@ inline size_t thread_id()
long tid;
thr_self(&tid);
return static_cast<size_t>(tid);
#else //Default to standard C++11 (OSX and other Unix)
#elif __APPLE__
uint64_t tid;
pthread_threadid_np(nullptr, &tid);
return static_cast<size_t>(tid);
#else //Default to standard C++11 (other Unix)
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
#endif
}
//Return current thread id as size_t (from thread local storage)
inline size_t thread_id()
{
#if defined(SPDLOG_DISABLE_TID_CACHING) || (defined(_MSC_VER) && (_MSC_VER < 1900)) || (defined(__clang__) && !__has_feature(cxx_thread_local))
return _thread_id();
#else // cache thread id in tls
static thread_local const size_t tid = _thread_id();
return tid;
#endif
}
@@ -329,6 +379,22 @@ inline std::string filename_to_str(const filename_t& filename)
}
#endif
inline std::string errno_to_string(char[256], char* res)
{
return std::string(res);
}
inline std::string errno_to_string(char buf[256], int res)
{
if (res == 0)
{
return std::string(buf);
}
else
{
return "Unknown error";
}
}
// Return errno string (thread safe)
inline std::string errno_str(int err_num)
@@ -337,24 +403,77 @@ inline std::string errno_str(int err_num)
SPDLOG_CONSTEXPR auto buf_size = sizeof(buf);
#ifdef _WIN32
if(strerror_s(buf, buf_size, err_num) == 0)
if (strerror_s(buf, buf_size, err_num) == 0)
return std::string(buf);
else
return "Unkown error";
return "Unknown error";
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || \
((_POSIX_C_SOURCE >= 200112L) && ! _GNU_SOURCE) // posix version
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \
((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version
if (strerror_r(err_num, buf, buf_size) == 0)
return std::string(buf);
else
return "Unkown error";
return "Unknown error";
#else // gnu version (might not use the given buf, so its retval pointer must be used)
return std::string(strerror_r(err_num, buf, buf_size));
auto err = strerror_r(err_num, buf, buf_size); // let compiler choose type
return errno_to_string(buf, err); // use overloading to select correct stringify function
#endif
}
inline int pid()
{
#ifdef _WIN32
return ::_getpid();
#else
return static_cast<int>(::getpid());
#endif
}
// Determine if the terminal supports colors
// Source: https://github.com/agauniyal/rang/
inline bool is_color_terminal()
{
#ifdef _WIN32
return true;
#else
static constexpr const char* Terms[] =
{
"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm",
"linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"
};
const char *env_p = std::getenv("TERM");
if (env_p == nullptr)
{
return false;
}
static const bool result = std::any_of(
std::begin(Terms), std::end(Terms), [&](const char* term)
{
return std::strstr(env_p, term) != nullptr;
});
return result;
#endif
}
// Detrmine if the terminal attached
// Source: https://github.com/agauniyal/rang/
inline bool in_terminal(FILE* file)
{
#ifdef _WIN32
return _isatty(_fileno(file)) ? true : false;
#else
return isatty(fileno(file)) ? true : false;
#endif
}
} //os
} //details
} //spdlog

View File

@@ -5,10 +5,10 @@
#pragma once
#include <spdlog/formatter.h>
#include <spdlog/details/log_msg.h>
#include <spdlog/details/os.h>
#include <spdlog/fmt/fmt.h>
#include "../formatter.h"
#include "../details/log_msg.h"
#include "../details/os.h"
#include "../fmt/fmt.h"
#include <chrono>
#include <ctime>
@@ -18,6 +18,7 @@
#include <thread>
#include <utility>
#include <vector>
#include <array>
namespace spdlog
{
@@ -98,7 +99,7 @@ class A_formatter:public flag_formatter
};
//Abbreviated month
static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" };
static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec" };
class b_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
@@ -118,14 +119,14 @@ class B_formatter:public flag_formatter
};
//write 2 ints seperated by sep with padding of 2
//write 2 ints separated by sep with padding of 2
static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, char sep)
{
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0');
return w;
}
//write 3 ints seperated by sep with padding of 2
//write 3 ints separated by sep with padding of 2
static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v3, char sep)
{
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0');
@@ -134,7 +135,7 @@ static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v
//Date and time representation (Thu Aug 23 15:35:46 2014)
class c_formatter:public flag_formatter
class c_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -145,7 +146,7 @@ class c_formatter:public flag_formatter
// year - 2 digit
class C_formatter:public flag_formatter
class C_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -156,7 +157,7 @@ class C_formatter:public flag_formatter
// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
class D_formatter:public flag_formatter
class D_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -166,7 +167,7 @@ class D_formatter:public flag_formatter
// year - 4 digit
class Y_formatter:public flag_formatter
class Y_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -175,7 +176,7 @@ class Y_formatter:public flag_formatter
};
// month 1-12
class m_formatter:public flag_formatter
class m_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -184,7 +185,7 @@ class m_formatter:public flag_formatter
};
// day of month 1-31
class d_formatter:public flag_formatter
class d_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -193,7 +194,7 @@ class d_formatter:public flag_formatter
};
// hours in 24 format 0-23
class H_formatter:public flag_formatter
class H_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -202,7 +203,7 @@ class H_formatter:public flag_formatter
};
// hours in 12 format 1-12
class I_formatter:public flag_formatter
class I_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -211,7 +212,7 @@ class I_formatter:public flag_formatter
};
// minutes 0-59
class M_formatter:public flag_formatter
class M_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -220,7 +221,7 @@ class M_formatter:public flag_formatter
};
// seconds 0-59
class S_formatter:public flag_formatter
class S_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -229,7 +230,7 @@ class S_formatter:public flag_formatter
};
// milliseconds
class e_formatter:public flag_formatter
class e_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
@@ -240,7 +241,7 @@ class e_formatter:public flag_formatter
};
// microseconds
class f_formatter:public flag_formatter
class f_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
@@ -251,7 +252,7 @@ class f_formatter:public flag_formatter
};
// nanoseconds
class F_formatter:public flag_formatter
class F_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
@@ -261,8 +262,18 @@ class F_formatter:public flag_formatter
}
};
class E_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
auto duration = msg.time.time_since_epoch();
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
msg.formatted << seconds;
}
};
// AM/PM
class p_formatter:public flag_formatter
class p_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -272,7 +283,7 @@ class p_formatter:public flag_formatter
// 12 hour clock 02:55:02 pm
class r_formatter:public flag_formatter
class r_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -281,7 +292,7 @@ class r_formatter:public flag_formatter
};
// 24-hour HH:MM time, equivalent to %H:%M
class R_formatter:public flag_formatter
class R_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -290,7 +301,7 @@ class R_formatter:public flag_formatter
};
// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
class T_formatter:public flag_formatter
class T_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -298,14 +309,13 @@ class T_formatter:public flag_formatter
}
};
// ISO 8601 offset from UTC in timezone (+-HH:MM)
class z_formatter:public flag_formatter
class z_formatter SPDLOG_FINAL:public flag_formatter
{
public:
const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
z_formatter():_last_update(std::chrono::seconds(0))
z_formatter():_last_update(std::chrono::seconds(0)), _offset_minutes(0)
{}
z_formatter(const z_formatter&) = delete;
z_formatter& operator=(const z_formatter&) = delete;
@@ -356,8 +366,8 @@ private:
//Thread id
class t_formatter:public flag_formatter
// Thread id
class t_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
@@ -365,8 +375,25 @@ class t_formatter:public flag_formatter
}
};
// Current pid
class pid_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << details::os::pid();
}
};
class v_formatter:public flag_formatter
// message counter formatter
class i_formatter SPDLOG_FINAL :public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << fmt::pad(msg.msg_id, 6, '0');
}
};
class v_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
@@ -374,7 +401,7 @@ class v_formatter:public flag_formatter
}
};
class ch_formatter:public flag_formatter
class ch_formatter SPDLOG_FINAL:public flag_formatter
{
public:
explicit ch_formatter(char ch): _ch(ch)
@@ -389,7 +416,7 @@ private:
//aggregate user chars to display as is
class aggregate_formatter:public flag_formatter
class aggregate_formatter SPDLOG_FINAL:public flag_formatter
{
public:
aggregate_formatter()
@@ -408,7 +435,7 @@ private:
// Full info formatter
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
class full_formatter:public flag_formatter
class full_formatter SPDLOG_FINAL:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -453,12 +480,15 @@ class full_formatter:public flag_formatter
}
};
}
}
///////////////////////////////////////////////////////////////////////////////
// pattern_formatter inline impl
///////////////////////////////////////////////////////////////////////////////
inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern)
inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern, pattern_time_type pattern_time)
: _pattern_time(pattern_time)
{
compile_pattern(pattern);
}
@@ -586,6 +616,10 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::F_formatter()));
break;
case('E'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::E_formatter()));
break;
case('p'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::p_formatter()));
break;
@@ -611,19 +645,35 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::full_formatter()));
break;
default: //Unkown flag appears as is
case ('P'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::pid_formatter()));
break;
case ('i'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::i_formatter()));
break;
default: //Unknown flag appears as is
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter('%')));
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter(flag)));
break;
}
}
inline std::tm spdlog::pattern_formatter::get_time(details::log_msg& msg)
{
if (_pattern_time == pattern_time_type::local)
return details::os::localtime(log_clock::to_time_t(msg.time));
else
return details::os::gmtime(log_clock::to_time_t(msg.time));
}
inline void spdlog::pattern_formatter::format(details::log_msg& msg)
{
#ifndef SPDLOG_NO_DATETIME
auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time));
auto tm_time = get_time(msg);
#else
std::tm tm_time;
#endif

View File

@@ -10,10 +10,10 @@
// If user requests a non existing logger, nullptr will be returned
// This class is thread safe
#include <spdlog/details/null_mutex.h>
#include <spdlog/logger.h>
#include <spdlog/async_logger.h>
#include <spdlog/common.h>
#include "../details/null_mutex.h"
#include "../logger.h"
#include "../async_logger.h"
#include "../common.h"
#include <chrono>
#include <functional>
@@ -71,6 +71,26 @@ public:
return new_logger;
}
template<class It>
std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, const It& sinks_begin, const It& sinks_end)
{
std::lock_guard<Mutex> lock(_mutex);
throw_if_exists(logger_name);
auto new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb);
if (_formatter)
new_logger->set_formatter(_formatter);
if (_err_handler)
new_logger->set_error_handler(_err_handler);
new_logger->set_level(_level);
//Add to registry
_loggers[logger_name] = new_logger;
return new_logger;
}
void apply_all(std::function<void(std::shared_ptr<logger>)> fun)
{
std::lock_guard<Mutex> lock(_mutex);
@@ -99,6 +119,15 @@ public:
return create(logger_name, { sink });
}
std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, sinks_init_list sinks)
{
return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks.begin(), sinks.end());
}
std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, sink_ptr sink)
{
return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, { sink });
}
void formatter(formatter_ptr f)
{

View File

@@ -8,13 +8,24 @@
//
// Global registry functions
//
#include <spdlog/spdlog.h>
#include <spdlog/details/registry.h>
#include <spdlog/sinks/file_sinks.h>
#include <spdlog/sinks/stdout_sinks.h>
#include <spdlog/sinks/syslog_sink.h>
#include <spdlog/sinks/ansicolor_sink.h>
#include <spdlog/sinks/android_sink.h>
#include "../spdlog.h"
#include "../details/registry.h"
#include "../sinks/file_sinks.h"
#include "../sinks/stdout_sinks.h"
#ifdef SPDLOG_ENABLE_SYSLOG
#include "../sinks/syslog_sink.h"
#endif
#ifdef _WIN32
#include "../sinks/wincolor_sink.h"
#else
#include "../sinks/ansicolor_sink.h"
#endif
#ifdef __ANDROID__
#include "../sinks/android_sink.h"
#endif
#include <chrono>
#include <functional>
@@ -50,62 +61,114 @@ inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_st(const std::string
// Create multi/single threaded rotating file logger
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
{
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files);
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
}
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
{
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files);
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
}
// Create file logger which creates new file at midnight):
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute)
{
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute);
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, hour, minute);
}
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour, int minute)
{
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute);
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, hour, minute);
}
// Create stdout/stderr loggers (with optinal color support)
inline std::shared_ptr<spdlog::logger> create_console_logger(const std::string& logger_name, spdlog::sink_ptr sink, bool color)
//
// stdout/stderr loggers
//
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name)
{
if (color) //use color wrapper sink
sink = std::make_shared<spdlog::sinks::ansicolor_sink>(sink);
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name)
{
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name)
{
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name)
{
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance());
}
//
// stdout/stderr color loggers
//
#ifdef _WIN32
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name, bool color)
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
{
return create_console_logger(logger_name, sinks::stdout_sink_mt::instance(), color);
auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name, bool color)
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
{
return create_console_logger(logger_name, sinks::stdout_sink_st::instance(), color);
auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name, bool color)
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
{
return create_console_logger(logger_name, sinks::stderr_sink_mt::instance(), color);
auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name, bool color)
#else //ansi terminal colors
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
{
return create_console_logger(logger_name, sinks::stderr_sink_st::instance(), color);
auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
#ifdef SPDLOG_ENABLE_SYSLOG
// Create syslog logger
inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option)
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
{
return create<spdlog::sinks::syslog_sink>(logger_name, syslog_ident, syslog_option);
auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
#endif
#if defined(__ANDROID__)
#ifdef SPDLOG_ENABLE_SYSLOG
// Create syslog logger
inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option, int syslog_facility)
{
return create<spdlog::sinks::syslog_sink>(logger_name, syslog_ident, syslog_option, syslog_facility);
}
#endif
#ifdef __ANDROID__
inline std::shared_ptr<spdlog::logger> spdlog::android_logger(const std::string& logger_name, const std::string& tag)
{
return create<spdlog::sinks::android_sink>(logger_name, tag);
@@ -140,6 +203,24 @@ inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_
return details::registry::instance().create(logger_name, sinks_begin, sinks_end);
}
// Create and register an async logger with a single sink
inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, const sink_ptr& sink, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
{
return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sink);
}
// Create and register an async logger with multiple sinks
inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb )
{
return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks);
}
template<class It>
inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, const It& sinks_begin, const It& sinks_end, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
{
return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks_begin, sinks_end);
}
inline void spdlog::set_formatter(spdlog::formatter_ptr f)
{
details::registry::instance().formatter(f);

View File

@@ -0,0 +1,23 @@
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -25,9 +25,7 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// Commented out by spdlog to use header only
// #include "fmt/format.h"
// #include "fmt/printf.h"
#include "format.h"
#include <string.h>
@@ -43,6 +41,9 @@
#endif
#if FMT_USE_WINDOWS_H
# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
# define WIN32_LEAN_AND_MEAN
# endif
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
# include <windows.h>
# else
@@ -52,8 +53,6 @@
# endif
#endif
using fmt::internal::Arg;
#if FMT_EXCEPTIONS
# define FMT_TRY try
# define FMT_CATCH(x) catch (x)
@@ -82,9 +81,9 @@ static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
namespace fmt {
FMT_FUNC internal::RuntimeError::~RuntimeError() throw() {}
FMT_FUNC FormatError::~FormatError() throw() {}
FMT_FUNC SystemError::~SystemError() throw() {}
FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {}
FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {}
FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {}
namespace {
@@ -172,7 +171,8 @@ int safe_strerror(
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
int run() {
strerror_r(0, 0, ""); // Suppress a warning about unused strerror_r.
// Suppress a warning about unused strerror_r.
strerror_r(0, FMT_NULL, "");
return handle(strerror_r(error_code_, buffer_, buffer_size_));
}
};
@@ -213,16 +213,6 @@ void report_error(FormatFunc func, int error_code,
}
} // namespace
namespace internal {
// This method is used to preserve binary compatibility with fmt 3.0.
// It can be removed in 4.0.
FMT_FUNC void format_system_error(
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
fmt::format_system_error(out, error_code, message);
}
} // namespace internal
FMT_FUNC void SystemError::init(
int err_code, CStringRef format_str, ArgList args) {
error_code_ = err_code;
@@ -313,7 +303,7 @@ FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) {
FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
int s_size = static_cast<int>(s.size());
int length = MultiByteToWideChar(
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, 0, 0);
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
if (length == 0)
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
buffer_.resize(length + 1);
@@ -335,12 +325,13 @@ FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) {
if (s.size() > INT_MAX)
return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, 0, 0, 0, 0);
int length = WideCharToMultiByte(
CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
if (length == 0)
return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, 0, 0);
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL);
if (length == 0)
return GetLastError();
buffer_[length] = 0;
@@ -363,9 +354,10 @@ FMT_FUNC void internal::format_windows_error(
buffer.resize(INLINE_BUFFER_SIZE);
for (;;) {
wchar_t *system_message = &buffer[0];
int result = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
0, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
system_message, static_cast<uint32_t>(buffer.size()), 0);
int result = FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
system_message, static_cast<uint32_t>(buffer.size()), FMT_NULL);
if (result != 0) {
UTF16ToUTF8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
@@ -409,7 +401,7 @@ void internal::ArgMap<Char>::init(const ArgList &args) {
if (!map_.empty())
return;
typedef internal::NamedArg<Char> NamedArg;
const NamedArg *named_arg = 0;
const NamedArg *named_arg = FMT_NULL;
bool use_values =
args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE;
if (use_values) {
@@ -454,14 +446,14 @@ void internal::FixedBuffer<Char>::grow(std::size_t) {
FMT_THROW(std::runtime_error("buffer overflow"));
}
FMT_FUNC Arg internal::FormatterBase::do_get_arg(
FMT_FUNC internal::Arg internal::FormatterBase::do_get_arg(
unsigned arg_index, const char *&error) {
Arg arg = args_[arg_index];
internal::Arg arg = args_[arg_index];
switch (arg.type) {
case Arg::NONE:
case internal::Arg::NONE:
error = "argument index out of range";
break;
case Arg::NAMED_ARG:
case internal::Arg::NAMED_ARG:
arg = *static_cast<const internal::Arg*>(arg.pointer);
break;
default:
@@ -502,16 +494,6 @@ FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) {
std::fputs(RESET_COLOR, stdout);
}
template <typename Char>
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args);
FMT_FUNC int fprintf(std::FILE *f, CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
std::size_t size = w.size();
return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
}
#ifndef FMT_HEADER_ONLY
template struct internal::BasicData<void>;
@@ -522,13 +504,11 @@ template void internal::FixedBuffer<char>::grow(std::size_t);
template void internal::ArgMap<char>::init(const ArgList &args);
template void PrintfFormatter<char>::format(CStringRef format);
template int internal::CharTraits<char>::format_float(
template FMT_API int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, double value);
template int internal::CharTraits<char>::format_float(
template FMT_API int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, long double value);
@@ -538,13 +518,11 @@ template void internal::FixedBuffer<wchar_t>::grow(std::size_t);
template void internal::ArgMap<wchar_t>::init(const ArgList &args);
template void PrintfFormatter<wchar_t>::format(WCStringRef format);
template int internal::CharTraits<wchar_t>::format_float(
template FMT_API int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, double value);
template int internal::CharTraits<wchar_t>::format_float(
template FMT_API int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, long double value);

View File

@@ -38,7 +38,10 @@
#include <stdexcept>
#include <string>
#include <vector>
#include <utility>
#include <utility> // for std::pair
// The fmt library version in the form major * 10000 + minor * 100 + patch.
#define FMT_VERSION 40000
#ifdef _SECURE_SCL
# define FMT_SECURE_SCL _SECURE_SCL
@@ -104,6 +107,7 @@ typedef __int64 intmax_t;
#endif
#if defined(__clang__) && !defined(FMT_ICC_VERSION)
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
# pragma clang diagnostic ignored "-Wpadded"
@@ -152,10 +156,6 @@ typedef __int64 intmax_t;
# endif
#endif
#if FMT_USE_RVALUE_REFERENCES
# include <utility> // for std::move
#endif
// Check if exceptions are disabled.
#if defined(__GNUC__) && !defined(__EXCEPTIONS)
# define FMT_EXCEPTIONS 0
@@ -180,20 +180,50 @@ typedef __int64 intmax_t;
# define FMT_USE_NOEXCEPT 0
#endif
#ifndef FMT_NOEXCEPT
# if FMT_EXCEPTIONS
# if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
FMT_MSC_VER >= 1900
# define FMT_NOEXCEPT noexcept
# else
# define FMT_NOEXCEPT throw()
# endif
# define FMT_DETECTED_NOEXCEPT noexcept
#else
# define FMT_DETECTED_NOEXCEPT throw()
#endif
#ifndef FMT_NOEXCEPT
# if FMT_EXCEPTIONS
# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT
# else
# define FMT_NOEXCEPT
# endif
#endif
// This is needed because GCC still uses throw() in its headers when exceptions
// are disabled.
#if FMT_GCC_VERSION
# define FMT_DTOR_NOEXCEPT FMT_DETECTED_NOEXCEPT
#else
# define FMT_DTOR_NOEXCEPT FMT_NOEXCEPT
#endif
#ifndef FMT_OVERRIDE
# if (defined(FMT_USE_OVERRIDE) && FMT_USE_OVERRIDE) || FMT_HAS_FEATURE(cxx_override) || \
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
FMT_MSC_VER >= 1900
# define FMT_OVERRIDE override
# else
# define FMT_OVERRIDE
# endif
#endif
#ifndef FMT_NULL
# if FMT_HAS_FEATURE(cxx_nullptr) || \
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
FMT_MSC_VER >= 1600
# define FMT_NULL nullptr
# else
# define FMT_NULL NULL
# endif
#endif
// A macro to disallow the copy constructor and operator= functions
// This should be used in the private: declarations for a class
#ifndef FMT_USE_DELETED_FUNCTIONS
@@ -213,6 +243,20 @@ typedef __int64 intmax_t;
TypeName& operator=(const TypeName&)
#endif
#ifndef FMT_USE_DEFAULTED_FUNCTIONS
# define FMT_USE_DEFAULTED_FUNCTIONS 0
#endif
#ifndef FMT_DEFAULTED_COPY_CTOR
# if FMT_USE_DEFAULTED_FUNCTIONS || FMT_HAS_FEATURE(cxx_defaulted_functions) || \
(FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800
# define FMT_DEFAULTED_COPY_CTOR(TypeName) \
TypeName(const TypeName&) = default;
# else
# define FMT_DEFAULTED_COPY_CTOR(TypeName)
# endif
#endif
#ifndef FMT_USE_USER_DEFINED_LITERALS
// All compilers which support UDLs also support variadic templates. This
// makes the fmt::literals implementation easier. However, an explicit check
@@ -225,23 +269,38 @@ typedef __int64 intmax_t;
(!defined(FMT_ICC_VERSION) || FMT_ICC_VERSION >= 1500)
#endif
#ifndef FMT_USE_EXTERN_TEMPLATES
# define FMT_USE_EXTERN_TEMPLATES \
(FMT_CLANG_VERSION >= 209 || (FMT_GCC_VERSION >= 303 && FMT_HAS_GXX_CXX11))
#endif
#ifdef FMT_HEADER_ONLY
// If header only do not use extern templates.
# undef FMT_USE_EXTERN_TEMPLATES
# define FMT_USE_EXTERN_TEMPLATES 0
#endif
#ifndef FMT_ASSERT
# define FMT_ASSERT(condition, message) assert((condition) && message)
#endif
#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz)
# define FMT_BUILTIN_CLZ(n) __builtin_clz(n)
#endif
// __builtin_clz is broken in clang with Microsoft CodeGen:
// https://github.com/fmtlib/fmt/issues/519
#ifndef _MSC_VER
# if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz)
# define FMT_BUILTIN_CLZ(n) __builtin_clz(n)
# endif
#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll)
# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n)
# if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll)
# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n)
# endif
#endif
// Some compilers masquerade as both MSVC and GCC-likes or
// otherwise support __builtin_clz and __builtin_clzll, so
// only define FMT_BUILTIN_CLZ using the MSVC intrinsics
// if the clz and clzll builtins are not available.
#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL)
#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(_MANAGED)
# include <intrin.h> // _BitScanReverse, _BitScanReverse64
namespace fmt
@@ -387,8 +446,11 @@ public:
static bool isnegative(double x)
{
using namespace fmt::internal;
if (const_check(sizeof(signbit(x)) == sizeof(int)))
if (const_check(sizeof(signbit(x)) == sizeof(bool) ||
sizeof(signbit(x)) == sizeof(int)))
{
return signbit(x) != 0;
}
if (x < 0) return true;
if (!isnotanumber(x)) return false;
int dec = 0, sign = 0;
@@ -420,7 +482,9 @@ typedef BasicWriter<wchar_t> WWriter;
template <typename Char>
class ArgFormatter;
template <typename Impl, typename Char>
struct FormatSpec;
template <typename Impl, typename Char, typename Spec = fmt::FormatSpec>
class BasicPrintfArgFormatter;
template <typename CharType,
@@ -429,7 +493,8 @@ class BasicFormatter;
/**
\rst
A string reference. It can be constructed from a C string or ``std::string``.
A string reference. It can be constructed from a C string or
``std::basic_string``.
You can use one of the following typedefs for common character types:
@@ -473,10 +538,12 @@ public:
/**
\rst
Constructs a string reference from an ``std::string`` object.
Constructs a string reference from a ``std::basic_string`` object.
\endrst
*/
BasicStringRef(const std::basic_string<Char> &s)
template <typename Allocator>
BasicStringRef(
const std::basic_string<Char, std::char_traits<Char>, Allocator> &s)
: data_(s.c_str()), size_(s.size()) {}
/**
@@ -543,7 +610,7 @@ typedef BasicStringRef<wchar_t> WStringRef;
/**
\rst
A reference to a null terminated string. It can be constructed from a C
string or ``std::string``.
string or ``std::basic_string``.
You can use one of the following typedefs for common character types:
@@ -577,10 +644,13 @@ public:
/**
\rst
Constructs a string reference from an ``std::string`` object.
Constructs a string reference from a ``std::basic_string`` object.
\endrst
*/
BasicCStringRef(const std::basic_string<Char> &s) : data_(s.c_str()) {}
template <typename Allocator>
BasicCStringRef(
const std::basic_string<Char, std::char_traits<Char>, Allocator> &s)
: data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char *c_str() const
@@ -598,7 +668,8 @@ class FormatError : public std::runtime_error
public:
explicit FormatError(CStringRef message)
: std::runtime_error(message.c_str()) {}
~FormatError() throw();
FormatError(const FormatError &ferr) : std::runtime_error(ferr) {}
FMT_API ~FormatError() FMT_DTOR_NOEXCEPT;
};
namespace internal
@@ -666,7 +737,7 @@ protected:
std::size_t size_;
std::size_t capacity_;
Buffer(T *ptr = 0, std::size_t capacity = 0)
Buffer(T *ptr = FMT_NULL, std::size_t capacity = 0)
: ptr_(ptr), size_(0), capacity_(capacity) {}
/**
@@ -740,7 +811,8 @@ template <typename T>
template <typename U>
void Buffer<T>::append(const U *begin, const U *end)
{
std::size_t new_size = size_ + internal::to_unsigned(end - begin);
FMT_ASSERT(end >= begin, "negative value");
std::size_t new_size = size_ + (end - begin);
if (new_size > capacity_)
grow(new_size);
std::uninitialized_copy(begin, end,
@@ -766,7 +838,7 @@ private:
}
protected:
void grow(std::size_t size);
void grow(std::size_t size) FMT_OVERRIDE;
public:
explicit MemoryBuffer(const Allocator &alloc = Allocator())
@@ -828,7 +900,7 @@ void MemoryBuffer<T, SIZE, Allocator>::grow(std::size_t size)
std::size_t new_capacity = this->capacity_ + this->capacity_ / 2;
if (size > new_capacity)
new_capacity = size;
T *new_ptr = this->allocate(new_capacity);
T *new_ptr = this->allocate(new_capacity, FMT_NULL);
// The following code doesn't throw, so the raw pointer above doesn't leak.
std::uninitialized_copy(this->ptr_, this->ptr_ + this->size_,
make_ptr(new_ptr, new_capacity));
@@ -851,7 +923,7 @@ public:
FixedBuffer(Char *array, std::size_t size) : fmt::Buffer<Char>(array, size) {}
protected:
FMT_API void grow(std::size_t size);
FMT_API void grow(std::size_t size) FMT_OVERRIDE;
};
template <typename Char>
@@ -891,6 +963,15 @@ public:
const char *format, unsigned width, int precision, T value);
};
#if FMT_USE_EXTERN_TEMPLATES
extern template int CharTraits<char>::format_float<double>
(char *buffer, std::size_t size,
const char* format, unsigned width, int precision, double value);
extern template int CharTraits<char>::format_float<long double>
(char *buffer, std::size_t size,
const char* format, unsigned width, int precision, long double value);
#endif
template <>
class CharTraits<wchar_t> : public BasicCharTraits<wchar_t>
{
@@ -909,6 +990,15 @@ public:
const wchar_t *format, unsigned width, int precision, T value);
};
#if FMT_USE_EXTERN_TEMPLATES
extern template int CharTraits<wchar_t>::format_float<double>
(wchar_t *buffer, std::size_t size,
const wchar_t* format, unsigned width, int precision, double value);
extern template int CharTraits<wchar_t>::format_float<long double>
(wchar_t *buffer, std::size_t size,
const wchar_t* format, unsigned width, int precision, long double value);
#endif
// Checks if a number is negative - used to avoid warnings.
template <bool IsSigned>
struct SignChecker
@@ -972,13 +1062,7 @@ struct FMT_API BasicData
static const char DIGITS[];
};
#ifndef FMT_USE_EXTERN_TEMPLATES
// Clang doesn't have a feature check for extern templates so we check
// for variadic templates which were introduced in the same version.
# define FMT_USE_EXTERN_TEMPLATES (__clang__ && FMT_USE_VARIADIC_TEMPLATES)
#endif
#if FMT_USE_EXTERN_TEMPLATES && !defined(FMT_HEADER_ONLY)
#if FMT_USE_EXTERN_TEMPLATES
extern template struct BasicData<void>;
#endif
@@ -1087,7 +1171,8 @@ inline void format_decimal(Char *buffer, UInt value, unsigned num_digits,
template <typename UInt, typename Char>
inline void format_decimal(Char *buffer, UInt value, unsigned num_digits)
{
return format_decimal(buffer, value, num_digits, NoThousandsSep());
format_decimal(buffer, value, num_digits, NoThousandsSep());
return;
}
#ifndef _WIN32
@@ -1218,6 +1303,8 @@ struct Arg : Value
template <typename Char>
struct NamedArg;
template <typename Char, typename T>
struct NamedArgWithType;
template <typename T = void>
struct Null {};
@@ -1273,7 +1360,10 @@ struct ConvertToIntImpl2<T, true>
template<typename T>
struct ConvertToInt
{
enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) };
enum
{
enable_conversion = sizeof(fmt::internal::convert(get<T>())) == sizeof(Yes)
};
enum { value = ConvertToIntImpl2<T, enable_conversion>::value };
};
@@ -1321,7 +1411,7 @@ struct Not<false>
};
template <typename T>
struct False
struct FalseType
{
enum { value = 0 };
};
@@ -1348,7 +1438,7 @@ inline fmt::StringRef thousands_sep(...)
#define FMT_CONCAT(a, b) a##b
#if FMT_GCC_VERSION >= 407
#if FMT_GCC_VERSION >= 303
# define FMT_UNUSED __attribute__((unused))
#else
# define FMT_UNUSED
@@ -1370,7 +1460,7 @@ inline fmt::StringRef thousands_sep(...)
template <typename Formatter, typename Char, typename T>
void format_arg(Formatter &, const Char *, const T &)
{
FMT_STATIC_ASSERT(False<T>::value,
FMT_STATIC_ASSERT(FalseType<T>::value,
"Cannot format argument. To enable the use of ostream "
"operator<< include fmt/ostream.h. Otherwise provide "
"an overload of format_arg.");
@@ -1529,16 +1619,10 @@ public:
}
template <typename T>
MakeValue(const T &value,
typename EnableIf<ConvertToInt<T>::value, int>::type = 0)
static typename EnableIf<Not<ConvertToInt<T>::value>::value, uint64_t>::type
type(const T &)
{
int_value = value;
}
template <typename T>
static uint64_t type(const T &)
{
return ConvertToInt<T>::value ? Arg::INT : Arg::CUSTOM;
return Arg::CUSTOM;
}
// Additional template param `Char_` is needed here because make_type always
@@ -1548,12 +1632,22 @@ public:
{
pointer = &value;
}
template <typename Char_, typename T>
MakeValue(const NamedArgWithType<Char_, T> &value)
{
pointer = &value;
}
template <typename Char_>
static uint64_t type(const NamedArg<Char_> &)
{
return Arg::NAMED_ARG;
}
template <typename Char_, typename T>
static uint64_t type(const NamedArgWithType<Char_, T> &)
{
return Arg::NAMED_ARG;
}
};
template <typename Formatter>
@@ -1583,11 +1677,19 @@ struct NamedArg : Arg
: Arg(MakeArg< BasicFormatter<Char> >(value)), name(argname) {}
};
template <typename Char, typename T>
struct NamedArgWithType : NamedArg<Char>
{
NamedArgWithType(BasicStringRef<Char> argname, const T &value)
: NamedArg<Char>(argname, value) {}
};
class RuntimeError : public std::runtime_error
{
protected:
RuntimeError() : std::runtime_error("") {}
~RuntimeError() throw();
RuntimeError(const RuntimeError &rerr) : std::runtime_error(rerr) {}
FMT_API ~RuntimeError() FMT_DTOR_NOEXCEPT;
};
template <typename Char>
@@ -1614,10 +1716,7 @@ private:
internal::Arg::Type type(unsigned index) const
{
unsigned shift = index * 4;
uint64_t mask = 0xf;
return static_cast<internal::Arg::Type>(
(types_ & (mask << shift)) >> shift);
return type(types_, index);
}
template <typename Char>
@@ -1634,6 +1733,11 @@ public:
ArgList(ULongLong types, const internal::Arg *args)
: types_(types), args_(args) {}
uint64_t types() const
{
return types_;
}
/** Returns the argument at specified index. */
internal::Arg operator[](unsigned index) const
{
@@ -1663,6 +1767,14 @@ public:
}
return args_[index];
}
static internal::Arg::Type type(uint64_t types, unsigned index)
{
unsigned shift = index * 4;
uint64_t mask = 0xf;
return static_cast<internal::Arg::Type>(
(types & (mask << shift)) >> shift);
}
};
#define FMT_DISPATCH(call) static_cast<Impl*>(this)->call
@@ -1884,6 +1996,10 @@ struct TypeSpec : EmptySpec
{
return TYPE;
}
char type_prefix() const
{
return TYPE;
}
char fill() const
{
return ' ';
@@ -1943,6 +2059,10 @@ struct AlignTypeSpec : AlignSpec
{
return TYPE;
}
char type_prefix() const
{
return TYPE;
}
};
// A full format specifier.
@@ -1968,6 +2088,10 @@ struct FormatSpec : AlignSpec
{
return type_;
}
char type_prefix() const
{
return type_;
}
};
// An integer format specifier.
@@ -2145,7 +2269,7 @@ private:
public:
FMT_API void init(const ArgList &args);
const internal::Arg* find(const fmt::BasicStringRef<Char> &name) const
const internal::Arg *find(const fmt::BasicStringRef<Char> &name) const
{
// The list is unsorted, so just return the first matching name.
for (typename MapType::const_iterator it = map_.begin(), end = map_.end();
@@ -2154,16 +2278,16 @@ public:
if (it->first == name)
return &it->second;
}
return 0;
return FMT_NULL;
}
};
template <typename Impl, typename Char>
template <typename Impl, typename Char, typename Spec = fmt::FormatSpec>
class ArgFormatterBase : public ArgVisitor<Impl, void>
{
private:
BasicWriter<Char> &writer_;
FormatSpec &spec_;
Spec &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatterBase);
@@ -2174,12 +2298,15 @@ private:
writer_.write_int(reinterpret_cast<uintptr_t>(p), spec_);
}
// workaround MSVC two-phase lookup issue
typedef internal::Arg Arg;
protected:
BasicWriter<Char> &writer()
{
return writer_;
}
FormatSpec &spec()
Spec &spec()
{
return spec_;
}
@@ -2193,12 +2320,14 @@ protected:
void write(const char *value)
{
Arg::StringValue<char> str = {value, value != 0 ? std::strlen(value) : 0};
Arg::StringValue<char> str = {value, value ? std::strlen(value) : 0};
writer_.write_str(str, spec_);
}
public:
ArgFormatterBase(BasicWriter<Char> &w, FormatSpec &s)
typedef Spec SpecType;
ArgFormatterBase(BasicWriter<Char> &w, Spec &s)
: writer_(w), spec_(s) {}
template <typename T>
@@ -2216,7 +2345,10 @@ public:
void visit_bool(bool value)
{
if (spec_.type_)
return visit_any_int(value);
{
visit_any_int(value);
return;
}
write(value);
}
@@ -2233,29 +2365,29 @@ public:
typedef typename BasicWriter<Char>::CharPtr CharPtr;
Char fill = internal::CharTraits<Char>::cast(spec_.fill());
CharPtr out = CharPtr();
const unsigned CHAR_WIDTH = 1;
if (spec_.width_ > CHAR_WIDTH)
const unsigned CHAR_SIZE = 1;
if (spec_.width_ > CHAR_SIZE)
{
out = writer_.grow_buffer(spec_.width_);
if (spec_.align_ == ALIGN_RIGHT)
{
std::uninitialized_fill_n(out, spec_.width_ - CHAR_WIDTH, fill);
out += spec_.width_ - CHAR_WIDTH;
std::uninitialized_fill_n(out, spec_.width_ - CHAR_SIZE, fill);
out += spec_.width_ - CHAR_SIZE;
}
else if (spec_.align_ == ALIGN_CENTER)
{
out = writer_.fill_padding(out, spec_.width_,
internal::const_check(CHAR_WIDTH), fill);
internal::const_check(CHAR_SIZE), fill);
}
else
{
std::uninitialized_fill_n(out + CHAR_WIDTH,
spec_.width_ - CHAR_WIDTH, fill);
std::uninitialized_fill_n(out + CHAR_SIZE,
spec_.width_ - CHAR_SIZE, fill);
}
}
else
{
out = writer_.grow_buffer(CHAR_WIDTH);
out = writer_.grow_buffer(CHAR_SIZE);
}
*out = internal::CharTraits<Char>::cast(value);
}
@@ -2267,14 +2399,15 @@ public:
write(value);
}
void visit_string(Arg::StringValue<char> value)
// Qualification with "internal" here and below is a workaround for nvcc.
void visit_string(internal::Arg::StringValue<char> value)
{
writer_.write_str(value, spec_);
}
using ArgVisitor<Impl, void>::visit_wstring;
void visit_wstring(Arg::StringValue<Char> value)
void visit_wstring(internal::Arg::StringValue<Char> value)
{
writer_.write_str(value, spec_);
}
@@ -2361,8 +2494,8 @@ protected:
will be called.
\endrst
*/
template <typename Impl, typename Char>
class BasicArgFormatter : public internal::ArgFormatterBase<Impl, Char>
template <typename Impl, typename Char, typename Spec = fmt::FormatSpec>
class BasicArgFormatter : public internal::ArgFormatterBase<Impl, Char, Spec>
{
private:
BasicFormatter<Char, Impl> &formatter_;
@@ -2378,8 +2511,8 @@ public:
\endrst
*/
BasicArgFormatter(BasicFormatter<Char, Impl> &formatter,
FormatSpec &spec, const Char *fmt)
: internal::ArgFormatterBase<Impl, Char>(formatter.writer(), spec),
Spec &spec, const Char *fmt)
: internal::ArgFormatterBase<Impl, Char, Spec>(formatter.writer(), spec),
formatter_(formatter), format_(fmt) {}
/** Formats an argument of a custom (user-defined) type. */
@@ -2391,13 +2524,15 @@ public:
/** The default argument formatter. */
template <typename Char>
class ArgFormatter : public BasicArgFormatter<ArgFormatter<Char>, Char>
class ArgFormatter :
public BasicArgFormatter<ArgFormatter<Char>, Char, FormatSpec>
{
public:
/** Constructs an argument formatter object. */
ArgFormatter(BasicFormatter<Char> &formatter,
FormatSpec &spec, const Char *fmt)
: BasicArgFormatter<ArgFormatter<Char>, Char>(formatter, spec, fmt) {}
: BasicArgFormatter<ArgFormatter<Char>,
Char, FormatSpec>(formatter, spec, fmt) {}
};
/** This template formats data and writes the output to a writer. */
@@ -2482,10 +2617,10 @@ inline uint64_t make_type(const T &arg)
return MakeValue< BasicFormatter<char> >::type(arg);
}
template <unsigned N, bool/*IsPacked*/= (N < ArgList::MAX_PACKED_ARGS)>
template <std::size_t N, bool/*IsPacked*/= (N < ArgList::MAX_PACKED_ARGS)>
struct ArgArray;
template <unsigned N>
template <std::size_t N>
struct ArgArray<N, true/*IsPacked*/>
{
typedef Value Type[N > 0 ? N : 1];
@@ -2505,7 +2640,7 @@ static Value make(const T &value)
}
};
template <unsigned N>
template <std::size_t N>
struct ArgArray<N, false/*IsPacked*/>
{
typedef Arg Type[N + 1]; // +1 for the list end Arg::NONE
@@ -2653,7 +2788,7 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT))
class SystemError : public internal::RuntimeError
{
private:
void init(int err_code, CStringRef format_str, ArgList args);
FMT_API void init(int err_code, CStringRef format_str, ArgList args);
protected:
int error_code_;
@@ -2685,9 +2820,10 @@ public:
{
init(error_code, message, ArgList());
}
FMT_DEFAULTED_COPY_CTOR(SystemError)
FMT_VARIADIC_CTOR(SystemError, init, int, CStringRef)
~SystemError() throw();
FMT_API ~SystemError() FMT_DTOR_NOEXCEPT;
int error_code() const
{
@@ -2816,16 +2952,16 @@ private:
void write_int(T value, Spec spec);
// Formats a floating-point number (double or long double).
template <typename T>
void write_double(T value, const FormatSpec &spec);
template <typename T, typename Spec>
void write_double(T value, const Spec &spec);
// Writes a formatted string.
template <typename StrChar>
CharPtr write_str(const StrChar *s, std::size_t size, const AlignSpec &spec);
template <typename StrChar>
template <typename StrChar, typename Spec>
void write_str(const internal::Arg::StringValue<StrChar> &str,
const FormatSpec &spec);
const Spec &spec);
// This following methods are private to disallow writing wide characters
// and strings to a char stream. If you want to print a wide string as a
@@ -2845,10 +2981,10 @@ private:
template<typename T>
void append_float_length(Char *&, T) {}
template <typename Impl, typename Char_>
template <typename Impl, typename Char_, typename Spec_>
friend class internal::ArgFormatterBase;
template <typename Impl, typename Char_>
template <typename Impl, typename Char_, typename Spec_>
friend class BasicPrintfArgFormatter;
protected:
@@ -3077,9 +3213,9 @@ typename BasicWriter<Char>::CharPtr BasicWriter<Char>::write_str(
}
template <typename Char>
template <typename StrChar>
template <typename StrChar, typename Spec>
void BasicWriter<Char>::write_str(
const internal::Arg::StringValue<StrChar> &s, const FormatSpec &spec)
const internal::Arg::StringValue<StrChar> &s, const Spec &spec)
{
// Check if StrChar is convertible to Char.
internal::CharTraits<Char>::convert(StrChar());
@@ -3231,7 +3367,7 @@ void BasicWriter<Char>::write_int(T value, Spec spec)
if (spec.flag(HASH_FLAG))
{
prefix[prefix_size++] = '0';
prefix[prefix_size++] = spec.type();
prefix[prefix_size++] = spec.type_prefix();
}
unsigned num_digits = 0;
do
@@ -3258,7 +3394,7 @@ void BasicWriter<Char>::write_int(T value, Spec spec)
if (spec.flag(HASH_FLAG))
{
prefix[prefix_size++] = '0';
prefix[prefix_size++] = spec.type();
prefix[prefix_size++] = spec.type_prefix();
}
unsigned num_digits = 0;
do
@@ -3299,7 +3435,7 @@ void BasicWriter<Char>::write_int(T value, Spec spec)
{
unsigned num_digits = internal::count_digits(abs_value);
fmt::StringRef sep = "";
#ifndef ANDROID
#if !(defined(ANDROID) || defined(__ANDROID__))
sep = internal::thousands_sep(std::localeconv());
#endif
unsigned size = static_cast<unsigned>(
@@ -3316,8 +3452,8 @@ void BasicWriter<Char>::write_int(T value, Spec spec)
}
template <typename Char>
template <typename T>
void BasicWriter<Char>::write_double(T value, const FormatSpec &spec)
template <typename T, typename Spec>
void BasicWriter<Char>::write_double(T value, const Spec &spec)
{
// Check type.
char type = spec.type();
@@ -3437,7 +3573,7 @@ void BasicWriter<Char>::write_double(T value, const FormatSpec &spec)
// Format using snprintf.
Char fill = internal::CharTraits<Char>::cast(spec.fill());
unsigned n = 0;
Char *start = 0;
Char *start = FMT_NULL;
for (;;)
{
std::size_t buffer_size = buffer_.capacity() - offset;
@@ -3875,15 +4011,15 @@ inline void format_decimal(char *&buffer, T value)
\endrst
*/
template <typename T>
inline internal::NamedArg<char> arg(StringRef name, const T &arg)
inline internal::NamedArgWithType<char, T> arg(StringRef name, const T &arg)
{
return internal::NamedArg<char>(name, arg);
return internal::NamedArgWithType<char, T>(name, arg);
}
template <typename T>
inline internal::NamedArg<wchar_t> arg(WStringRef name, const T &arg)
inline internal::NamedArgWithType<wchar_t, T> arg(WStringRef name, const T &arg)
{
return internal::NamedArg<wchar_t>(name, arg);
return internal::NamedArgWithType<wchar_t, T>(name, arg);
}
// The following two functions are deleted intentionally to disable
@@ -4104,7 +4240,7 @@ inline internal::Arg BasicFormatter<Char, AF>::get_arg(
template <typename Char, typename AF>
inline internal::Arg BasicFormatter<Char, AF>::parse_arg_index(const Char *&s)
{
const char *error = 0;
const char *error = FMT_NULL;
internal::Arg arg = *s < '0' || *s > '9' ?
next_arg(error) : get_arg(internal::parse_nonnegative_int(s), error);
if (error)
@@ -4126,7 +4262,7 @@ inline internal::Arg BasicFormatter<Char, AF>::parse_arg_name(const Char *&s)
c = *++s;
}
while (internal::is_name_start(c) || ('0' <= c && c <= '9'));
const char *error = 0;
const char *error = FMT_NULL;
internal::Arg arg = get_arg(BasicStringRef<Char>(start, s - start), error);
if (error)
FMT_THROW(FormatError(error));
@@ -4139,7 +4275,7 @@ const Char *BasicFormatter<Char, ArgFormatter>::format(
{
using internal::Arg;
const Char *s = format_str;
FormatSpec spec;
typename ArgFormatter::SpecType spec;
if (*s == ':')
{
if (arg.type == Arg::CUSTOM)
@@ -4352,6 +4488,74 @@ void BasicFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
}
write(writer_, start, s);
}
template <typename Char, typename It>
struct ArgJoin
{
It first;
It last;
BasicCStringRef<Char> sep;
ArgJoin(It first, It last, const BasicCStringRef<Char>& sep) :
first(first),
last(last),
sep(sep) {}
};
template <typename It>
ArgJoin<char, It> join(It first, It last, const BasicCStringRef<char>& sep)
{
return ArgJoin<char, It>(first, last, sep);
}
template <typename It>
ArgJoin<wchar_t, It> join(It first, It last, const BasicCStringRef<wchar_t>& sep)
{
return ArgJoin<wchar_t, It>(first, last, sep);
}
#if FMT_HAS_GXX_CXX11
template <typename Range>
auto join(const Range& range, const BasicCStringRef<char>& sep)
-> ArgJoin<char, decltype(std::begin(range))>
{
return join(std::begin(range), std::end(range), sep);
}
template <typename Range>
auto join(const Range& range, const BasicCStringRef<wchar_t>& sep)
-> ArgJoin<wchar_t, decltype(std::begin(range))>
{
return join(std::begin(range), std::end(range), sep);
}
#endif
template <typename ArgFormatter, typename Char, typename It>
void format_arg(fmt::BasicFormatter<Char, ArgFormatter> &f,
const Char *&format_str, const ArgJoin<Char, It>& e)
{
const Char* end = format_str;
if (*end == ':')
++end;
while (*end && *end != '}')
++end;
if (*end != '}')
FMT_THROW(FormatError("missing '}' in format string"));
It it = e.first;
if (it != e.last)
{
const Char* save = format_str;
f.format(format_str, internal::MakeArg<fmt::BasicFormatter<Char, ArgFormatter> >(*it++));
while (it != e.last)
{
f.writer().write(e.sep);
format_str = save;
f.format(format_str, internal::MakeArg<fmt::BasicFormatter<Char, ArgFormatter> >(*it++));
}
}
format_str = end + 1;
}
} // namespace fmt
#if FMT_USE_USER_DEFINED_LITERALS
@@ -4379,7 +4583,7 @@ struct UdlArg
const Char *str;
template <typename T>
NamedArg<Char> operator=(T &&value) const
NamedArgWithType<Char, T> operator=(T &&value) const
{
return {str, std::forward<T>(value)};
}

View File

@@ -7,8 +7,7 @@
For the license information refer to format.h.
*/
// Commented out by spdlog to use header only
// #include "fmt/ostream.h"
#include "ostream.h"
namespace fmt {

View File

@@ -10,8 +10,7 @@
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
// Commented out by spdlog to use header only
// #include "fmt/format.h"
#include "format.h"
#include <ostream>
namespace fmt
@@ -28,32 +27,29 @@ private:
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
Buffer<Char> &buffer_;
Char *start_;
public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0])
{
this->setp(start_, start_ + buffer_.capacity());
}
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer) {}
int_type overflow(int_type ch = traits_type::eof())
protected:
// The put-area is actually always empty. This makes the implementation
// simpler and has the advantage that the streambuf and the buffer are always
// in sync and sputc never writes into uninitialized memory. The obvious
// disadvantage is that each call to sputc always results in a (virtual) call
// to overflow. There is no disadvantage here for sputn since this always
// results in a call to xsputn.
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE
{
if (!traits_type::eq_int_type(ch, traits_type::eof()))
{
size_t buf_size = size();
buffer_.resize(buf_size);
buffer_.reserve(buf_size * 2);
start_ = &buffer_[0];
start_[buf_size] = traits_type::to_char_type(ch);
this->setp(start_+ buf_size + 1, start_ + buf_size * 2);
}
buffer_.push_back(static_cast<Char>(ch));
return ch;
}
size_t size() const
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE
{
return to_unsigned(this->pptr() - start_);
buffer_.append(s, s + count);
return count;
}
};
@@ -79,12 +75,12 @@ struct ConvertToIntImpl<T, true>
};
// Write the content of w to os.
void write(std::ostream &os, Writer &w);
FMT_API void write(std::ostream &os, Writer &w);
} // namespace internal
// Formats a value.
template <typename Char, typename ArgFormatter, typename T>
void format_arg(BasicFormatter<Char, ArgFormatter> &f,
template <typename Char, typename ArgFormatter_, typename T>
void format_arg(BasicFormatter<Char, ArgFormatter_> &f,
const Char *&format_str, const T &value)
{
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
@@ -93,7 +89,7 @@ void format_arg(BasicFormatter<Char, ArgFormatter> &f,
std::basic_ostream<Char> output(&format_buf);
output << value;
BasicStringRef<Char> str(&buffer[0], format_buf.size());
BasicStringRef<Char> str(&buffer[0], buffer.size());
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
format_str = f.format(format_str, MakeArg(str));
}

View File

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

View File

@@ -0,0 +1,424 @@
/*
A C++ interface to POSIX functions.
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_POSIX_H_
#define FMT_POSIX_H_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <errno.h>
#include <fcntl.h> // for O_RDONLY
#include <locale.h> // for locale_t
#include <stdio.h>
#include <stdlib.h> // for strtod_l
#include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
#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)
namespace fmt
{
// An error code.
class ErrorCode
{
private:
int value_;
public:
explicit ErrorCode(int value = 0) FMT_NOEXCEPT :
value_(value) {}
int get() const FMT_NOEXCEPT
{
return value_;
}
};
// A buffered file.
class BufferedFile
{
private:
FILE *file_;
friend class File;
explicit BufferedFile(FILE *f) : file_(f) {}
public:
// Constructs a BufferedFile object which doesn't represent any file.
BufferedFile() FMT_NOEXCEPT :
file_(FMT_NULL) {}
// Destroys the object closing the file it represents if any.
FMT_API ~BufferedFile() FMT_NOEXCEPT;
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy
{
FILE *file;
};
public:
// A "move constructor" for moving from a temporary.
BufferedFile(Proxy p) FMT_NOEXCEPT :
file_(p.file) {}
// A "move constructor" for moving from an lvalue.
BufferedFile(BufferedFile &f) FMT_NOEXCEPT :
file_(f.file_)
{
f.file_ = FMT_NULL;
}
// A "move assignment operator" for moving from a temporary.
BufferedFile &operator=(Proxy p)
{
close();
file_ = p.file;
return *this;
}
// A "move assignment operator" for moving from an lvalue.
BufferedFile &operator=(BufferedFile &other)
{
close();
file_ = other.file_;
other.file_ = FMT_NULL;
return *this;
}
// Returns a proxy object for moving from a temporary:
// BufferedFile file = BufferedFile(...);
operator Proxy() FMT_NOEXCEPT
{
Proxy p = {file_};
file_ = FMT_NULL;
return p;
}
#else
private:
FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
public:
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT :
file_(other.file_)
{
other.file_ = FMT_NULL;
}
BufferedFile& operator=(BufferedFile &&other)
{
close();
file_ = other.file_;
other.file_ = FMT_NULL;
return *this;
}
#endif
// Opens a file.
FMT_API BufferedFile(CStringRef filename, CStringRef 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 print(CStringRef format_str, const ArgList &args)
{
fmt::print(file_, format_str, args);
}
FMT_VARIADIC(void, print, CStringRef)
};
// A file. Closed file is represented by a File object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::SystemError in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class File
{
private:
int fd_; // File descriptor.
// Constructs a File object with a given descriptor.
explicit File(int fd) : fd_(fd) {}
public:
// Possible values for the oflag argument to the constructor.
enum
{
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
};
// Constructs a File object which doesn't represent any file.
File() FMT_NOEXCEPT :
fd_(-1) {}
// Opens a file and constructs a File object representing this file.
FMT_API File(CStringRef path, int oflag);
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy
{
int fd;
};
public:
// A "move constructor" for moving from a temporary.
File(Proxy p) FMT_NOEXCEPT :
fd_(p.fd) {}
// A "move constructor" for moving from an lvalue.
File(File &other) FMT_NOEXCEPT :
fd_(other.fd_)
{
other.fd_ = -1;
}
// A "move assignment operator" for moving from a temporary.
File &operator=(Proxy p)
{
close();
fd_ = p.fd;
return *this;
}
// A "move assignment operator" for moving from an lvalue.
File &operator=(File &other)
{
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Returns a proxy object for moving from a temporary:
// File file = File(...);
operator Proxy() FMT_NOEXCEPT
{
Proxy p = {fd_};
fd_ = -1;
return p;
}
#else
private:
FMT_DISALLOW_COPY_AND_ASSIGN(File);
public:
File(File &&other) FMT_NOEXCEPT :
fd_(other.fd_)
{
other.fd_ = -1;
}
File& operator=(File &&other)
{
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
#endif
// 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 LongLong size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void *buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API std::size_t write(const void *buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
FMT_API static File dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd, ErrorCode &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 BufferedFile object associated with this file and detaches
// this File object from the file.
FMT_API BufferedFile fdopen(const char *mode);
};
// Returns the memory page size.
long getpagesize();
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
!defined(__ANDROID__) && !defined(__CYGWIN__)
# define FMT_LOCALE
#endif
#ifdef FMT_LOCALE
// A "C" numeric locale.
class Locale
{
private:
# ifdef _MSC_VER
typedef _locale_t locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC };
static locale_t newlocale(int category_mask, const char *locale, locale_t)
{
return _create_locale(category_mask, locale);
}
static void freelocale(locale_t locale)
{
_free_locale(locale);
}
static double strtod_l(const char *nptr, char **endptr, _locale_t locale)
{
return _strtod_l(nptr, endptr, locale);
}
# endif
locale_t locale_;
FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
public:
typedef locale_t Type;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL))
{
if (!locale_)
FMT_THROW(fmt::SystemError(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 = FMT_NULL;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
#endif // FMT_LOCALE
} // namespace fmt
#if !FMT_USE_RVALUE_REFERENCES
namespace std
{
// For compatibility with C++98.
inline fmt::BufferedFile &move(fmt::BufferedFile &f)
{
return f;
}
inline fmt::File &move(fmt::File &f)
{
return f;
}
}
#endif
#endif // FMT_POSIX_H_

View File

@@ -0,0 +1,32 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#include "format.h"
#include "printf.h"
namespace fmt {
template <typename Char>
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args);
FMT_FUNC int fprintf(std::FILE *f, CStringRef format, ArgList args) {
MemoryWriter w;
printf(w, format, args);
std::size_t size = w.size();
return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
}
#ifndef FMT_HEADER_ONLY
template void PrintfFormatter<char>::format(CStringRef format);
template void PrintfFormatter<wchar_t>::format(WCStringRef format);
#endif // FMT_HEADER_ONLY
} // namespace fmt

View File

@@ -13,7 +13,7 @@
#include <algorithm> // std::fill_n
#include <limits> // std::numeric_limits
#include "fmt/ostream.h"
#include "ostream.h"
namespace fmt
{
@@ -80,6 +80,43 @@ public:
}
};
// returns the default type for format specific "%s"
class DefaultType : public ArgVisitor<DefaultType, char>
{
public:
char visit_char(int)
{
return 'c';
}
char visit_bool(bool)
{
return 's';
}
char visit_pointer(const void *)
{
return 'p';
}
template <typename T>
char visit_any_int(T)
{
return 'd';
}
template <typename T>
char visit_any_double(T)
{
return 'g';
}
char visit_unhandled_arg()
{
return 's';
}
};
template <typename T, typename U>
struct is_same
{
@@ -115,10 +152,21 @@ public:
visit_any_int(value);
}
void visit_char(char value)
{
if (type_ != 's')
visit_any_int(value);
}
template <typename U>
void visit_any_int(U value)
{
bool is_signed = type_ == 'd' || type_ == 'i';
if (type_ == 's')
{
is_signed = std::numeric_limits<U>::is_signed;
}
using internal::Arg;
typedef typename internal::Conditional<
is_same<T, void>::value, U, T>::type TargetType;
@@ -228,8 +276,9 @@ public:
superclass will be called.
\endrst
*/
template <typename Impl, typename Char>
class BasicPrintfArgFormatter : public internal::ArgFormatterBase<Impl, Char>
template <typename Impl, typename Char, typename Spec>
class BasicPrintfArgFormatter :
public internal::ArgFormatterBase<Impl, Char, Spec>
{
private:
void write_null_pointer()
@@ -238,7 +287,7 @@ private:
this->write("(nil)");
}
typedef internal::ArgFormatterBase<Impl, Char> Base;
typedef internal::ArgFormatterBase<Impl, Char, Spec> Base;
public:
/**
@@ -248,13 +297,13 @@ public:
specifier information for standard argument types.
\endrst
*/
BasicPrintfArgFormatter(BasicWriter<Char> &writer, FormatSpec &spec)
: internal::ArgFormatterBase<Impl, Char>(writer, spec) {}
BasicPrintfArgFormatter(BasicWriter<Char> &w, Spec &s)
: internal::ArgFormatterBase<Impl, Char, Spec>(w, s) {}
/** Formats an argument of type ``bool``. */
void visit_bool(bool value)
{
FormatSpec &fmt_spec = this->spec();
Spec &fmt_spec = this->spec();
if (fmt_spec.type_ != 's')
return this->visit_any_int(value);
fmt_spec.type_ = 0;
@@ -264,7 +313,7 @@ public:
/** Formats a character. */
void visit_char(int value)
{
const FormatSpec &fmt_spec = this->spec();
const Spec &fmt_spec = this->spec();
BasicWriter<Char> &w = this->writer();
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
w.write_int(value, fmt_spec);
@@ -323,13 +372,13 @@ public:
/** The default printf argument formatter. */
template <typename Char>
class PrintfArgFormatter
: public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char>
class PrintfArgFormatter :
public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>
{
public:
/** Constructs an argument formatter object. */
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char>(w, s) {}
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>(w, s) {}
};
/** This template formats data and writes the output to a writer. */
@@ -358,11 +407,11 @@ public:
appropriate lifetimes.
\endrst
*/
explicit PrintfFormatter(const ArgList &args, BasicWriter<Char> &w)
: FormatterBase(args), writer_(w) {}
explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w)
: FormatterBase(al), writer_(w) {}
/** Formats stored arguments and writes the output to the writer. */
FMT_API void format(BasicCStringRef<Char> format_str);
void format(BasicCStringRef<Char> format_str);
};
template <typename Char, typename AF>
@@ -399,7 +448,7 @@ internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s,
unsigned arg_index)
{
(void)s;
const char *error = 0;
const char *error = FMT_NULL;
internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ?
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
if (error)
@@ -486,6 +535,10 @@ void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
++s;
spec.precision_ = internal::PrecisionHandler().visit(get_arg(s));
}
else
{
spec.precision_ = 0;
}
}
using internal::Arg;
@@ -538,6 +591,13 @@ void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
if (!*s)
FMT_THROW(FormatError("invalid format string"));
spec.type_ = static_cast<char>(*s++);
if (spec.type_ == 's')
{
// set the format type to the default if 's' is specified
spec.type_ = internal::DefaultType().visit(arg);
}
if (arg.type <= Arg::LAST_INTEGER_TYPE)
{
// Normalize type.
@@ -562,11 +622,17 @@ void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
write(writer_, start, s);
}
template <typename Char>
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args)
inline void printf(Writer &w, CStringRef format, ArgList args)
{
PrintfFormatter<Char>(args, w).format(format);
PrintfFormatter<char>(args, w).format(format);
}
FMT_VARIADIC(void, printf, Writer &, CStringRef)
inline void printf(WWriter &w, WCStringRef format, ArgList args)
{
PrintfFormatter<wchar_t>(args, w).format(format);
}
FMT_VARIADIC(void, printf, WWriter &, WCStringRef)
/**
\rst
@@ -639,4 +705,8 @@ inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args)
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY
# include "printf.cc"
#endif
#endif // FMT_PRINTF_H_

View File

@@ -0,0 +1,183 @@
/*
Formatting library for C++ - time formatting
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_TIME_H_
#define FMT_TIME_H_
#include "format.h"
#include <ctime>
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4702) // unreachable code
# pragma warning(disable: 4996) // "deprecated" functions
#endif
namespace fmt
{
template <typename ArgFormatter>
void format_arg(BasicFormatter<char, ArgFormatter> &f,
const char *&format_str, const std::tm &tm)
{
if (*format_str == ':')
++format_str;
const char *end = format_str;
while (*end && *end != '}')
++end;
if (*end != '}')
FMT_THROW(FormatError("missing '}' in format string"));
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format;
format.append(format_str, end + 1);
format[format.size() - 1] = '\0';
Buffer<char> &buffer = f.writer().buffer();
std::size_t start = buffer.size();
for (;;)
{
std::size_t size = buffer.capacity() - start;
std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm);
if (count != 0)
{
buffer.resize(start + count);
break;
}
if (size >= format.size() * 256)
{
// If the buffer is 256 times larger than the format string, assume
// that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases:
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
format_str = end + 1;
}
namespace internal
{
inline Null<> localtime_r(...)
{
return Null<>();
}
inline Null<> localtime_s(...)
{
return Null<>();
}
inline Null<> gmtime_r(...)
{
return Null<>();
}
inline Null<> gmtime_s(...)
{
return Null<>();
}
}
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time)
{
struct LocalTime
{
std::time_t time_;
std::tm tm_;
LocalTime(std::time_t t): time_(t) {}
bool run()
{
using namespace fmt::internal;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm *tm)
{
return tm != FMT_NULL;
}
bool handle(internal::Null<>)
{
using namespace fmt::internal;
return fallback(localtime_s(&tm_, &time_));
}
bool fallback(int res)
{
return res == 0;
}
bool fallback(internal::Null<>)
{
using namespace fmt::internal;
std::tm *tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != FMT_NULL;
}
};
LocalTime lt(time);
if (lt.run())
return lt.tm_;
// Too big time values may be unsupported.
FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm();
}
// Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time)
{
struct GMTime
{
std::time_t time_;
std::tm tm_;
GMTime(std::time_t t): time_(t) {}
bool run()
{
using namespace fmt::internal;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm *tm)
{
return tm != FMT_NULL;
}
bool handle(internal::Null<>)
{
using namespace fmt::internal;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res)
{
return res == 0;
}
bool fallback(internal::Null<>)
{
std::tm *tm = std::gmtime(&time_);
if (tm != FMT_NULL) tm_ = *tm;
return tm != FMT_NULL;
}
};
GMTime gt(time);
if (gt.run())
return gt.tm_;
// Too big time values may be unsupported.
FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm();
}
} //namespace fmt
#ifdef _MSC_VER
# pragma warning(pop)
#endif
#endif // FMT_TIME_H_

View File

@@ -18,11 +18,17 @@
#ifndef FMT_USE_WINDOWS_H
#define FMT_USE_WINDOWS_H 0
#endif
#include <spdlog/fmt/bundled/format.h>
#include "bundled/format.h"
#if defined(SPDLOG_FMT_PRINTF)
#include "bundled/printf.h"
#endif
#else //external fmtlib
#include <fmt/format.h>
#if defined(SPDLOG_FMT_PRINTF)
#include <fmt/printf.h>
#endif
#endif

View File

@@ -8,8 +8,8 @@
// include external or bundled copy of fmtlib's ostream support
//
#if !defined(SPDLOG_FMT_EXTERNAL)
#include <spdlog/fmt/fmt.h>
#include <spdlog/fmt/bundled/ostream.h>
#include "fmt.h"
#include "bundled/ostream.h"
#else
#include <fmt/ostream.h>
#endif

View File

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

View File

@@ -5,15 +5,15 @@
#pragma once
// Thread safe logger
// Thread safe logger (except for set_pattern(..), set_formatter(..) and set_error_handler())
// Has name, log level, vector of std::shared sink pointers and formatter
// Upon each log write the logger:
// 1. Checks if its log level is enough to log the message
// 2. Format the message using the formatter function
// 3. Pass the formatted message to its sinks to performa the actual logging
#include <spdlog/sinks/base_sink.h>
#include <spdlog/common.h>
#include "sinks/base_sink.h"
#include "common.h"
#include <vector>
#include <memory>
@@ -37,12 +37,24 @@ public:
template <typename... Args> void log(level::level_enum lvl, const char* fmt, const Args&... args);
template <typename... Args> void log(level::level_enum lvl, const char* msg);
template <typename... Args> void trace(const char* fmt, const Args&... args);
template <typename... Args> void debug(const char* fmt, const Args&... args);
template <typename... Args> void info(const char* fmt, const Args&... args);
template <typename... Args> void warn(const char* fmt, const Args&... args);
template <typename... Args> void error(const char* fmt, const Args&... args);
template <typename... Args> void critical(const char* fmt, const Args&... args);
template <typename Arg1, typename... Args> void trace(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void debug(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void info(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void warn(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void error(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void critical(const char* fmt, const Arg1&, const Args&... args);
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
template <typename... Args> void log(level::level_enum lvl, const wchar_t* msg);
template <typename... Args> void log(level::level_enum lvl, const wchar_t* fmt, const Args&... args);
template <typename... Args> void trace(const wchar_t* fmt, const Args&... args);
template <typename... Args> void debug(const wchar_t* fmt, const Args&... args);
template <typename... Args> void info(const wchar_t* fmt, const Args&... args);
template <typename... Args> void warn(const wchar_t* fmt, const Args&... args);
template <typename... Args> void error(const wchar_t* fmt, const Args&... args);
template <typename... Args> void critical(const wchar_t* fmt, const Args&... args);
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
template <typename T> void log(level::level_enum lvl, const T&);
template <typename T> void trace(const T&);
@@ -56,21 +68,23 @@ public:
void set_level(level::level_enum);
level::level_enum level() const;
const std::string& name() const;
void set_pattern(const std::string&);
void set_pattern(const std::string&, pattern_time_type = pattern_time_type::local);
void set_formatter(formatter_ptr);
// error handler
void set_error_handler(log_err_handler);
log_err_handler error_handler();
// automatically call flush() if message level >= log_level
void flush_on(level::level_enum log_level);
virtual void flush();
const std::vector<sink_ptr>& sinks() const;
// error handler
virtual void set_error_handler(log_err_handler);
virtual log_err_handler error_handler();
protected:
virtual void _sink_it(details::log_msg&);
virtual void _set_pattern(const std::string&);
virtual void _set_pattern(const std::string&, pattern_time_type);
virtual void _set_formatter(formatter_ptr);
// default error handler: print the error to stderr with the max rate of 1 message/minute
@@ -79,6 +93,9 @@ protected:
// return true if the given message level should trigger a flush
bool _should_flush_on(const details::log_msg&);
// increment the message count (only if defined(SPDLOG_ENABLE_MESSAGE_COUNTER))
void _incr_msg_counter(details::log_msg &msg);
const std::string _name;
std::vector<sink_ptr> _sinks;
formatter_ptr _formatter;
@@ -86,9 +103,8 @@ protected:
spdlog::level_t _flush_level;
log_err_handler _err_handler;
std::atomic<time_t> _last_err_time;
std::atomic<size_t> _msg_counter;
};
}
#include <spdlog/details/logger_impl.h>
#include "details/logger_impl.h"

View File

@@ -7,11 +7,17 @@
#if defined(__ANDROID__)
#include <spdlog/sinks/sink.h>
#include "sink.h"
#include <mutex>
#include <string>
#include <android/log.h>
#include <thread>
#include <chrono>
#if !defined(SPDLOG_ANDROID_RETRIES)
#define SPDLOG_ANDROID_RETRIES 2
#endif
namespace spdlog
{
@@ -25,15 +31,23 @@ namespace sinks
class android_sink : public sink
{
public:
explicit android_sink(const std::string& tag = "spdlog"): _tag(tag) {}
explicit android_sink(const std::string& tag = "spdlog", bool use_raw_msg = false): _tag(tag), _use_raw_msg(use_raw_msg) {}
void log(const details::log_msg& msg) override
{
const android_LogPriority priority = convert_to_android(msg.level);
const char *msg_output = (_use_raw_msg ? msg.raw.c_str() : msg.formatted.c_str());
// See system/core/liblog/logger_write.c for explanation of return value
const int ret = __android_log_write(
priority, _tag.c_str(), msg.formatted.c_str()
);
int ret = __android_log_write(priority, _tag.c_str(), msg_output);
int retry_count = 0;
while ((ret == -11/*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES))
{
std::this_thread::sleep_for(std::chrono::milliseconds(5));
ret = __android_log_write(priority, _tag.c_str(), msg_output);
retry_count++;
}
if (ret < 0)
{
throw spdlog_ex("__android_log_write() failed", ret);
@@ -67,6 +81,7 @@ private:
}
std::string _tag;
bool _use_raw_msg;
};
}

View File

@@ -1,12 +1,13 @@
//
// Copyright(c) 2016 Kevin M. Godby (a modified version by spdlog).
// Copyright(c) 2017 spdlog authors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include <spdlog/sinks/base_sink.h>
#include <spdlog/common.h>
#include "base_sink.h"
#include "../common.h"
#include "../details/os.h"
#include <string>
#include <map>
@@ -17,99 +18,115 @@ namespace sinks
{
/**
* @brief The ansi_color_sink is a decorator around another sink and prefixes
* the output with an ANSI escape sequence color code depending on the severity
* This sink prefixes the output with an ANSI escape sequence color code depending on the severity
* of the message.
* If no color terminal detected, omit the escape codes.
*/
class ansicolor_sink : public sink
template <class Mutex>
class ansicolor_sink: public base_sink<Mutex>
{
public:
ansicolor_sink(sink_ptr wrapped_sink);
virtual ~ansicolor_sink();
ansicolor_sink(FILE* file): target_file_(file)
{
should_do_colors_ = details::os::in_terminal(file) && details::os::is_color_terminal();
colors_[level::trace] = cyan;
colors_[level::debug] = cyan;
colors_[level::info] = reset;
colors_[level::warn] = yellow + bold;
colors_[level::err] = red + bold;
colors_[level::critical] = bold + on_red;
colors_[level::off] = reset;
}
virtual ~ansicolor_sink()
{
_flush();
}
ansicolor_sink(const ansicolor_sink& other) = delete;
ansicolor_sink& operator=(const ansicolor_sink& other) = delete;
virtual void log(const details::log_msg& msg) override;
virtual void flush() override;
void set_color(level::level_enum level, const std::string& color);
void set_color(level::level_enum color_level, const std::string& color)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
colors_[color_level] = color;
}
/// Formatting codes
const std::string reset = "\033[00m";
const std::string bold = "\033[1m";
const std::string dark = "\033[2m";
const std::string underline = "\033[4m";
const std::string blink = "\033[5m";
const std::string reverse = "\033[7m";
const std::string concealed = "\033[8m";
const std::string reset = "\033[00m";
const std::string bold = "\033[1m";
const std::string dark = "\033[2m";
const std::string underline = "\033[4m";
const std::string blink = "\033[5m";
const std::string reverse = "\033[7m";
const std::string concealed = "\033[8m";
// Foreground colors
const std::string grey = "\033[30m";
const std::string red = "\033[31m";
const std::string green = "\033[32m";
const std::string yellow = "\033[33m";
const std::string blue = "\033[34m";
const std::string magenta = "\033[35m";
const std::string cyan = "\033[36m";
const std::string white = "\033[37m";
const std::string grey = "\033[30m";
const std::string red = "\033[31m";
const std::string green = "\033[32m";
const std::string yellow = "\033[33m";
const std::string blue = "\033[34m";
const std::string magenta = "\033[35m";
const std::string cyan = "\033[36m";
const std::string white = "\033[37m";
/// Background colors
const std::string on_grey = "\033[40m";
const std::string on_red = "\033[41m";
const std::string on_green = "\033[42m";
const std::string on_yellow = "\033[43m";
const std::string on_blue = "\033[44m";
const std::string on_grey = "\033[40m";
const std::string on_red = "\033[41m";
const std::string on_green = "\033[42m";
const std::string on_yellow = "\033[43m";
const std::string on_blue = "\033[44m";
const std::string on_magenta = "\033[45m";
const std::string on_cyan = "\033[46m";
const std::string on_white = "\033[47m";
const std::string on_cyan = "\033[46m";
const std::string on_white = "\033[47m";
protected:
sink_ptr sink_;
virtual void _sink_it(const details::log_msg& msg) override
{
// Wrap the originally formatted message in color codes.
// If color is not supported in the terminal, log as is instead.
if (should_do_colors_)
{
const std::string& prefix = colors_[msg.level];
fwrite(prefix.data(), sizeof(char), prefix.size(), target_file_);
fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), target_file_);
fwrite(reset.data(), sizeof(char), reset.size(), target_file_);
}
else
{
fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), target_file_);
}
_flush();
}
void _flush() override
{
fflush(target_file_);
}
FILE* target_file_;
bool should_do_colors_;
std::map<level::level_enum, std::string> colors_;
};
inline ansicolor_sink::ansicolor_sink(sink_ptr wrapped_sink) : sink_(wrapped_sink)
{
colors_[level::trace] = cyan;
colors_[level::debug] = cyan;
colors_[level::info] = bold;
colors_[level::warn] = yellow + bold;
colors_[level::err] = red + bold;
colors_[level::critical] = bold + on_red;
colors_[level::off] = reset;
}
inline void ansicolor_sink::log(const details::log_msg& msg)
template<class Mutex>
class ansicolor_stdout_sink: public ansicolor_sink<Mutex>
{
// Wrap the originally formatted message in color codes
const std::string& prefix = colors_[msg.level];
const std::string& s = msg.formatted.str();
const std::string& suffix = reset;
details::log_msg m;
m.level = msg.level;
m.logger_name = msg.logger_name;
m.time = msg.time;
m.thread_id = msg.thread_id;
m.formatted << prefix << s << suffix;
sink_->log(m);
}
public:
ansicolor_stdout_sink(): ansicolor_sink<Mutex>(stdout)
{}
};
inline void ansicolor_sink::flush()
template<class Mutex>
class ansicolor_stderr_sink: public ansicolor_sink<Mutex>
{
sink_->flush();
}
public:
ansicolor_stderr_sink(): ansicolor_sink<Mutex>(stderr)
{}
};
inline void ansicolor_sink::set_color(level::level_enum level, const std::string& color)
{
colors_[level] = color;
}
typedef ansicolor_stdout_sink<std::mutex> ansicolor_stdout_sink_mt;
typedef ansicolor_stdout_sink<details::null_mutex> ansicolor_stdout_sink_st;
inline ansicolor_sink::~ansicolor_sink()
{
flush();
}
typedef ansicolor_stderr_sink<std::mutex> ansicolor_stderr_sink_mt;
typedef ansicolor_stderr_sink<details::null_mutex> ansicolor_stderr_sink_st;
} // namespace sinks
} // namespace spdlog

View File

@@ -5,15 +5,15 @@
#pragma once
//
// base sink templated over a mutex (either dummy or realy)
// concrete implementation should only overrid the _sink_it method.
// base sink templated over a mutex (either dummy or real)
// concrete implementation should only override the _sink_it method.
// all locking is taken care of here so no locking needed by the implementers..
//
#include <spdlog/sinks/sink.h>
#include <spdlog/formatter.h>
#include <spdlog/common.h>
#include <spdlog/details/log_msg.h>
#include "sink.h"
#include "../formatter.h"
#include "../common.h"
#include "../details/log_msg.h"
#include <mutex>
@@ -31,14 +31,20 @@ public:
base_sink(const base_sink&) = delete;
base_sink& operator=(const base_sink&) = delete;
void log(const details::log_msg& msg) override
void log(const details::log_msg& msg) SPDLOG_FINAL override
{
std::lock_guard<Mutex> lock(_mutex);
_sink_it(msg);
}
void flush() SPDLOG_FINAL override
{
std::lock_guard<Mutex> lock(_mutex);
_flush();
}
protected:
virtual void _sink_it(const details::log_msg& msg) = 0;
virtual void _flush() = 0;
Mutex _mutex;
};
}

View File

@@ -5,10 +5,10 @@
#pragma once
#include <spdlog/details/log_msg.h>
#include <spdlog/details/null_mutex.h>
#include <spdlog/sinks/base_sink.h>
#include <spdlog/sinks/sink.h>
#include "../details/log_msg.h"
#include "../details/null_mutex.h"
#include "base_sink.h"
#include "sink.h"
#include <algorithm>
#include <mutex>
@@ -44,14 +44,15 @@ protected:
}
}
public:
void flush() override
void _flush() override
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
for (auto &sink : _sinks)
sink->flush();
}
public:
void add_sink(std::shared_ptr<sink> sink)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);

View File

@@ -5,10 +5,10 @@
#pragma once
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/null_mutex.h>
#include <spdlog/details/file_helper.h>
#include <spdlog/fmt/fmt.h>
#include "base_sink.h"
#include "../details/null_mutex.h"
#include "../details/file_helper.h"
#include "../fmt/fmt.h"
#include <algorithm>
#include <chrono>
@@ -26,25 +26,33 @@ namespace sinks
* Trivial file sink with single file as target
*/
template<class Mutex>
class simple_file_sink : public base_sink < Mutex >
class simple_file_sink SPDLOG_FINAL : public base_sink < Mutex >
{
public:
explicit simple_file_sink(const filename_t &filename, bool truncate = false)
explicit simple_file_sink(const filename_t &filename, bool truncate = false):_force_flush(false)
{
_file_helper.open(filename, truncate);
}
void flush() override
void set_force_flush(bool force_flush)
{
_file_helper.flush();
_force_flush = force_flush;
}
protected:
void _sink_it(const details::log_msg& msg) override
{
_file_helper.write(msg);
if(_force_flush)
_file_helper.flush();
}
void _flush() override
{
_file_helper.flush();
}
private:
details::file_helper _file_helper;
bool _force_flush;
};
typedef simple_file_sink<std::mutex> simple_file_sink_mt;
@@ -54,25 +62,37 @@ typedef simple_file_sink<details::null_mutex> simple_file_sink_st;
* Rotating file sink based on size
*/
template<class Mutex>
class rotating_file_sink : public base_sink < Mutex >
class rotating_file_sink SPDLOG_FINAL : public base_sink < Mutex >
{
public:
rotating_file_sink(const filename_t &base_filename, const filename_t &extension,
std::size_t max_size, std::size_t max_files ) :
rotating_file_sink(const filename_t &base_filename,
std::size_t max_size, std::size_t max_files) :
_base_filename(base_filename),
_extension(extension),
_max_size(max_size),
_max_files(max_files),
_current_size(0),
_file_helper()
{
_file_helper.open(calc_filename(_base_filename, 0, _extension));
_file_helper.open(calc_filename(_base_filename, 0));
_current_size = _file_helper.size(); //expensive. called only once
}
void flush() override
// calc filename according to index and file extension if exists.
// e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
static filename_t calc_filename(const filename_t& filename, std::size_t index)
{
_file_helper.flush();
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
if (index)
{
filename_t basename, ext;
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
w.write(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
}
else
{
w.write(SPDLOG_FILENAME_T("{}"), filename);
}
return w.str();
}
protected:
@@ -87,31 +107,26 @@ protected:
_file_helper.write(msg);
}
private:
static filename_t calc_filename(const filename_t& filename, std::size_t index, const filename_t& extension)
void _flush() override
{
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
if (index)
w.write(SPDLOG_FILENAME_T("{}.{}.{}"), filename, index, extension);
else
w.write(SPDLOG_FILENAME_T("{}.{}"), filename, extension);
return w.str();
_file_helper.flush();
}
private:
// Rotate files:
// log.txt -> log.1.txt
// log.1.txt -> log2.txt
// log.2.txt -> log3.txt
// log.1.txt -> log.2.txt
// log.2.txt -> log.3.txt
// log.3.txt -> delete
void _rotate()
{
using details::os::filename_to_str;
_file_helper.close();
for (auto i = _max_files; i > 0; --i)
{
filename_t src = calc_filename(_base_filename, i - 1, _extension);
filename_t target = calc_filename(_base_filename, i, _extension);
filename_t src = calc_filename(_base_filename, i - 1);
filename_t target = calc_filename(_base_filename, i);
if (details::file_helper::file_exists(target))
{
@@ -128,7 +143,6 @@ private:
_file_helper.reopen(true);
}
filename_t _base_filename;
filename_t _extension;
std::size_t _max_size;
std::size_t _max_files;
std::size_t _current_size;
@@ -143,27 +157,31 @@ typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
*/
struct default_daily_file_name_calculator
{
// Create filename for the form basename.YYYY-MM-DD_hh-mm.extension
static filename_t calc_filename(const filename_t& basename, const filename_t& extension)
// Create filename for the form filename.YYYY-MM-DD_hh-mm.ext
static filename_t calc_filename(const filename_t& filename)
{
std::tm tm = spdlog::details::os::localtime();
filename_t basename, ext;
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}.{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, extension);
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, ext);
return w.str();
}
};
/*
* Generator of daily log file names in format basename.YYYY-MM-DD.extension
* Generator of daily log file names in format basename.YYYY-MM-DD.ext
*/
struct dateonly_daily_file_name_calculator
{
// Create filename for the form basename.YYYY-MM-DD.extension
static filename_t calc_filename(const filename_t& basename, const filename_t& extension)
// Create filename for the form basename.YYYY-MM-DD
static filename_t calc_filename(const filename_t& filename)
{
std::tm tm = spdlog::details::os::localtime();
filename_t basename, ext;
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}.{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, extension);
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, ext);
return w.str();
}
};
@@ -172,41 +190,40 @@ struct dateonly_daily_file_name_calculator
* Rotating file sink based on date. rotates at midnight
*/
template<class Mutex, class FileNameCalc = default_daily_file_name_calculator>
class daily_file_sink :public base_sink < Mutex >
class daily_file_sink SPDLOG_FINAL :public base_sink < Mutex >
{
public:
//create daily file sink which rotates on given time
daily_file_sink(
const filename_t& base_filename,
const filename_t& extension,
int rotation_hour,
int rotation_minute) : _base_filename(base_filename),
_extension(extension),
_rotation_h(rotation_hour),
_rotation_m(rotation_minute)
_rotation_m(rotation_minute)
{
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
_rotation_tp = _next_rotation_tp();
_file_helper.open(FileNameCalc::calc_filename(_base_filename, _extension));
_file_helper.open(FileNameCalc::calc_filename(_base_filename));
}
void flush() override
{
_file_helper.flush();
}
protected:
void _sink_it(const details::log_msg& msg) override
{
if (std::chrono::system_clock::now() >= _rotation_tp)
{
_file_helper.open(FileNameCalc::calc_filename(_base_filename, _extension));
_file_helper.open(FileNameCalc::calc_filename(_base_filename));
_rotation_tp = _next_rotation_tp();
}
_file_helper.write(msg);
}
void _flush() override
{
_file_helper.flush();
}
private:
std::chrono::system_clock::time_point _next_rotation_tp()
{
@@ -224,7 +241,6 @@ private:
}
filename_t _base_filename;
filename_t _extension;
int _rotation_h;
int _rotation_m;
std::chrono::system_clock::time_point _rotation_tp;

View File

@@ -5,12 +5,12 @@
#pragma once
#if defined(_MSC_VER)
#if defined(_WIN32)
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/null_mutex.h>
#include "base_sink.h"
#include "../details/null_mutex.h"
#include <WinBase.h>
#include <winbase.h>
#include <mutex>
#include <string>
@@ -30,15 +30,16 @@ public:
{
}
void flush() override
{
}
protected:
void _sink_it(const details::log_msg& msg) override
{
OutputDebugStringA(msg.formatted.c_str());
}
void _flush() override
{}
};
typedef msvc_sink<std::mutex> msvc_sink_mt;

View File

@@ -5,8 +5,8 @@
#pragma once
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/null_mutex.h>
#include "base_sink.h"
#include "../details/null_mutex.h"
#include <mutex>
@@ -22,12 +22,12 @@ protected:
void _sink_it(const details::log_msg&) override
{}
void flush() override
void _flush() override
{}
};
typedef null_sink<details::null_mutex> null_sink_st;
typedef null_sink<std::mutex> null_sink_mt;
typedef null_sink<details::null_mutex> null_sink_mt;
}
}

View File

@@ -5,8 +5,8 @@
#pragma once
#include <spdlog/details/null_mutex.h>
#include <spdlog/sinks/base_sink.h>
#include "../details/null_mutex.h"
#include "base_sink.h"
#include <ostream>
#include <mutex>
@@ -32,7 +32,7 @@ protected:
_ostream.flush();
}
void flush() override
void _flush() override
{
_ostream.flush();
}

View File

@@ -6,7 +6,7 @@
#pragma once
#include <spdlog/details/log_msg.h>
#include "../details/log_msg.h"
namespace spdlog
{
@@ -15,7 +15,10 @@ namespace sinks
class sink
{
public:
sink(): _level( level::trace ) {}
sink()
{
_level = level::trace;
}
virtual ~sink() {}
virtual void log(const details::log_msg& msg) = 0;

View File

@@ -5,8 +5,8 @@
#pragma once
#include <spdlog/details/null_mutex.h>
#include <spdlog/sinks/base_sink.h>
#include "../details/null_mutex.h"
#include "base_sink.h"
#include <cstdio>
#include <memory>
@@ -18,24 +18,25 @@ namespace sinks
{
template <class Mutex>
class stdout_sink : public base_sink<Mutex>
class stdout_sink SPDLOG_FINAL : public base_sink<Mutex>
{
using MyType = stdout_sink<Mutex>;
public:
stdout_sink() {}
stdout_sink()
{}
static std::shared_ptr<MyType> instance()
{
static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
return instance;
}
protected:
void _sink_it(const details::log_msg& msg) override
{
fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stdout);
flush();
_flush();
}
void flush() override
void _flush() override
{
fflush(stdout);
}
@@ -46,24 +47,25 @@ typedef stdout_sink<std::mutex> stdout_sink_mt;
template <class Mutex>
class stderr_sink : public base_sink<Mutex>
class stderr_sink SPDLOG_FINAL : public base_sink<Mutex>
{
using MyType = stderr_sink<Mutex>;
public:
stderr_sink() {}
stderr_sink()
{}
static std::shared_ptr<MyType> instance()
{
static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
return instance;
}
protected:
void _sink_it(const details::log_msg& msg) override
{
fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stderr);
flush();
_flush();
}
void flush() override
void _flush() override
{
fflush(stderr);
}

View File

@@ -5,12 +5,12 @@
#pragma once
#include <spdlog/common.h>
#include "../common.h"
#ifdef SPDLOG_ENABLE_SYSLOG
#include <spdlog/sinks/sink.h>
#include <spdlog/details/log_msg.h>
#include "sink.h"
#include "../details/log_msg.h"
#include <array>
#include <string>

View File

@@ -0,0 +1,121 @@
//
// Copyright(c) 2016 spdlog
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include "base_sink.h"
#include "../details/null_mutex.h"
#include "../common.h"
#include <mutex>
#include <string>
#include <map>
#include <wincon.h>
namespace spdlog
{
namespace sinks
{
/*
* Windows color console sink. Uses WriteConsoleA to write to the console with colors
*/
template<class Mutex>
class wincolor_sink: public base_sink<Mutex>
{
public:
const WORD BOLD = FOREGROUND_INTENSITY;
const WORD RED = FOREGROUND_RED;
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 std_handle): out_handle_(std_handle)
{
colors_[level::trace] = CYAN;
colors_[level::debug] = CYAN;
colors_[level::info] = WHITE | BOLD;
colors_[level::warn] = YELLOW | BOLD;
colors_[level::err] = RED | BOLD; // red bold
colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background
colors_[level::off] = 0;
}
virtual ~wincolor_sink()
{
this->flush();
}
wincolor_sink(const wincolor_sink& other) = delete;
wincolor_sink& operator=(const wincolor_sink& other) = delete;
protected:
virtual void _sink_it(const details::log_msg& msg) override
{
auto color = colors_[msg.level];
auto orig_attribs = set_console_attribs(color);
WriteConsoleA(out_handle_, msg.formatted.data(), static_cast<DWORD>(msg.formatted.size()), nullptr, nullptr);
SetConsoleTextAttribute(out_handle_, orig_attribs); //reset to orig colors
}
virtual void _flush() override
{
// windows console always flushed?
}
// change the color for the given level
void set_color(level::level_enum level, WORD color)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
colors_[level] = color;
}
private:
HANDLE out_handle_;
std::map<level::level_enum, WORD> colors_;
// set color and return the orig console attributes (for resetting later)
WORD set_console_attribs(WORD 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 &= ~(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
}
};
//
// windows color console to stdout
//
template<class Mutex>
class wincolor_stdout_sink: public wincolor_sink<Mutex>
{
public:
wincolor_stdout_sink() : wincolor_sink<Mutex>(GetStdHandle(STD_OUTPUT_HANDLE))
{}
};
typedef wincolor_stdout_sink<std::mutex> wincolor_stdout_sink_mt;
typedef wincolor_stdout_sink<details::null_mutex> wincolor_stdout_sink_st;
//
// windows color console to stderr
//
template<class Mutex>
class wincolor_stderr_sink: public wincolor_sink<Mutex>
{
public:
wincolor_stderr_sink() : wincolor_sink<Mutex>(GetStdHandle(STD_ERROR_HANDLE))
{}
};
typedef wincolor_stderr_sink<std::mutex> wincolor_stderr_sink_mt;
typedef wincolor_stderr_sink<details::null_mutex> wincolor_stderr_sink_st;
}
}

View File

@@ -0,0 +1,29 @@
//
// Copyright(c) 2017 Alexander Dalshov.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#if defined(_WIN32)
#include "msvc_sink.h"
namespace spdlog
{
namespace sinks
{
/*
* Windows debug sink (logging using OutputDebugStringA, synonym for msvc_sink)
*/
template<class Mutex>
using windebug_sink = msvc_sink<Mutex>;
typedef msvc_sink_mt windebug_sink_mt;
typedef msvc_sink_st windebug_sink_st;
}
}
#endif

View File

@@ -2,15 +2,16 @@
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
// spdlog main header file.
// see example.cpp for usage example
#pragma once
#include <spdlog/tweakme.h>
#include <spdlog/common.h>
#include <spdlog/logger.h>
#define SPDLOG_VERSION "0.14.0"
#include "tweakme.h"
#include "common.h"
#include "logger.h"
#include <memory>
#include <functional>
@@ -68,7 +69,7 @@ void set_sync_mode();
//
// Create and register multi/single threaded basic file logger.
// Basic logger simply writes to given file without any limitatons or rotations.
// Basic logger simply writes to given file without any limitations or rotations.
//
std::shared_ptr<logger> basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate = false);
std::shared_ptr<logger> basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate = false);
@@ -88,24 +89,31 @@ std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const fi
//
// Create and register stdout/stderr loggers
//
std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name, bool color = false);
std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name, bool color = false);
std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name, bool color = false);
std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name, bool color = false);
std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name);
std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name);
std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name);
std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name);
//
// Create and register colored stdout/stderr loggers
//
std::shared_ptr<logger> stdout_color_mt(const std::string& logger_name);
std::shared_ptr<logger> stdout_color_st(const std::string& logger_name);
std::shared_ptr<logger> stderr_color_mt(const std::string& logger_name);
std::shared_ptr<logger> stderr_color_st(const std::string& logger_name);
//
// Create and register a syslog logger
//
#ifdef SPDLOG_ENABLE_SYSLOG
std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0);
std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0, int syslog_facilty = (1<<3));
#endif
#if defined(__ANDROID__)
std::shared_ptr<logger> android_logger(const std::string& logger_name, const std::string& tag = "spdlog");
#endif
// Create and register a logger a single sink
// Create and register a logger with a single sink
std::shared_ptr<logger> create(const std::string& logger_name, const sink_ptr& sink);
// Create and register a logger with multiple sinks
@@ -116,10 +124,17 @@ std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_b
// Create and register a logger with templated sink type
// Example:
// spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename", "txt");
// spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename");
template <typename Sink, typename... Args>
std::shared_ptr<spdlog::logger> create(const std::string& logger_name, Args...);
// Create and register an async logger with a single sink
std::shared_ptr<logger> create_async(const std::string& logger_name, const sink_ptr& sink, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr);
// Create and register an async logger with multiple sinks
std::shared_ptr<logger> create_async(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr);
template<class It>
std::shared_ptr<logger> create_async(const std::string& logger_name, const It& sinks_begin, const It& sinks_end, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr);
// Register the given logger with the given name
void register_logger(std::shared_ptr<logger> logger);
@@ -139,7 +154,7 @@ void drop_all();
///////////////////////////////////////////////////////////////////////////////
//
// Trace & Debug can be switched on/off at compile time for zero cost debug statements.
// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in teakme.h to enable.
// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in tweakme.h to enable.
// SPDLOG_TRACE(..) will also print current file and line.
//
// Example:
@@ -152,7 +167,11 @@ void drop_all();
#ifdef SPDLOG_TRACE_ON
#define SPDLOG_STR_H(x) #x
#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x)
#define SPDLOG_TRACE(logger, ...) logger->trace("[" __FILE__ " line #" SPDLOG_STR_HELPER(__LINE__) "] " __VA_ARGS__)
#ifdef _MSC_VER
#define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__)
#else
#define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ] " __VA_ARGS__)
#endif
#else
#define SPDLOG_TRACE(logger, ...)
#endif
@@ -163,8 +182,7 @@ void drop_all();
#define SPDLOG_DEBUG(logger, ...)
#endif
}
#include <spdlog/details/spdlog_impl.h>
#include "details/spdlog_impl.h"

View File

@@ -23,7 +23,7 @@
///////////////////////////////////////////////////////////////////////////////
// Uncomment if date/time logging is not needed and never appear in the log pattern.
// This will prevent spdlog from quering the clock on each log call.
// This will prevent spdlog from querying the clock on each log call.
//
// WARNING: If the log pattern contains any date/time while this flag is on, the result is undefined.
// You must set new pattern(spdlog::set_pattern(..") without any date/time in it
@@ -34,7 +34,7 @@
///////////////////////////////////////////////////////////////////////////////
// Uncomment if thread id logging is not needed (i.e. no %t in the log pattern).
// This will prevent spdlog from quering the thread id on each log call.
// This will prevent spdlog from querying the thread id on each log call.
//
// WARNING: If the log pattern contains thread id (i.e, %t) while this flag is on, the result is undefined.
//
@@ -42,6 +42,16 @@
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to prevent spdlog from caching thread ids in thread local storage.
// By default spdlog saves thread ids in tls to gain a few micros for each call.
//
// WARNING: if your program forks, UNCOMMENT this flag to prevent undefined thread ids in the children logs.
//
// #define SPDLOG_DISABLE_TID_CACHING
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment if logger name logging is not needed.
// This will prevent spdlog from copying the logger name on each log call.
@@ -59,7 +69,7 @@
///////////////////////////////////////////////////////////////////////////////
// Uncomment to avoid locking in the registry operations (spdlog::get(), spdlog::drop() spdlog::register()).
// Use only if your code never modifes concurrently the registry.
// Use only if your code never modifies concurrently the registry.
// Note that upon creating a logger the registry is modified by spdlog..
//
// #define SPDLOG_NO_REGISTRY_MUTEX
@@ -73,12 +83,14 @@
// #define SPDLOG_NO_ATOMIC_LEVELS
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable usage of wchar_t for file names on Windows.
//
// #define SPDLOG_WCHAR_FILENAMES
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to override default eol ("\n" or "\r\n" under Linux/Windows)
//
@@ -94,6 +106,14 @@
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to use printf-style messages in your logs instead of the usual
// format-style used by default.
//
// #define SPDLOG_FMT_PRINTF
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable syslog (disabled by default)
//
@@ -101,3 +121,40 @@
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable wchar_t support (convert to utf8)
//
// #define SPDLOG_WCHAR_TO_UTF8_SUPPORT
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to prevent child processes from inheriting log file descriptors
//
// #define SPDLOG_PREVENT_CHILD_FD
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment if your compiler doesn't support the "final" keyword.
// The final keyword allows more optimizations in release
// mode with recent compilers. See GCC's documentation for -Wsuggest-final-types
// for instance.
//
// #define SPDLOG_NO_FINAL
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable message counting feature.
// Use the %i in the logger pattern to display log message sequence id.
//
// #define SPDLOG_ENABLE_MESSAGE_COUNTER
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to customize level names (e.g. "MT TRACE")
//
// #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING", "MY ERROR", "MY CRITICAL", "OFF" }
///////////////////////////////////////////////////////////////////////////////

View File

@@ -10,7 +10,7 @@ find_package(Threads)
add_library(catch INTERFACE)
target_include_directories(catch INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
file(GLOB catch_tests LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp)
file(GLOB catch_tests LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp *.h *.hpp)
add_executable(catch_tests ${catch_tests})
target_link_libraries(catch_tests spdlog ${CMAKE_THREAD_LIBS_INIT})

View File

@@ -1,5 +1,11 @@
CXX ?= g++
CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -O2 -I../include
ifeq ($(STYLE),printf)
$(info *** PRINTF STYLE ***)
CXXFLAGS = -DSPDLOG_FMT_PRINTF -Wall -pedantic -std=c++11 -pthread -O2 -I../include
else
$(info *** FORMAT STYLE ***)
CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -O2 -I../include
endif
LDPFALGS = -pthread
CPP_FILES := $(wildcard *.cpp)

View File

@@ -6,6 +6,19 @@
#include<iostream>
class failing_sink: public spdlog::sinks::sink
{
void log(const spdlog::details::log_msg& msg) override
{
throw std::runtime_error("some error happened during log");
}
void flush()
{}
};
TEST_CASE("default_error_handler", "[errors]]")
{
prepare_logdir();
@@ -13,8 +26,13 @@ TEST_CASE("default_error_handler", "[errors]]")
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
logger->set_pattern("%v");
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {} {}", 1);
logger->info("Test message {}", 2);
#else
logger->info("Test message %d %d", 1);
logger->info("Test message %d", 2);
#endif
logger->flush();
REQUIRE(file_contents(filename) == std::string("Test message 2\n"));
@@ -22,23 +40,41 @@ TEST_CASE("default_error_handler", "[errors]]")
}
struct custom_ex {};
struct custom_ex
{};
TEST_CASE("custom_error_handler", "[errors]]")
{
prepare_logdir();
std::string filename = "logs/simple_log.txt";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
logger->flush_on(spdlog::level::info);
logger->flush_on(spdlog::level::info);
logger->set_error_handler([=](const std::string& msg)
{
throw custom_ex();
});
logger->info("Good message #1");
#if !defined(SPDLOG_FMT_PRINTF)
REQUIRE_THROWS_AS(logger->info("Bad format msg {} {}", "xxx"), custom_ex);
#else
REQUIRE_THROWS_AS(logger->info("Bad format msg %s %s", "xxx"), custom_ex);
#endif
logger->info("Good message #2");
REQUIRE(count_lines(filename) == 2);
}
TEST_CASE("default_error_handler2", "[errors]]")
{
auto logger = spdlog::create<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string& msg)
{
throw custom_ex();
});
REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex);
}
TEST_CASE("async_error_handler", "[errors]]")
{
prepare_logdir();
@@ -54,7 +90,11 @@ TEST_CASE("async_error_handler", "[errors]]")
ofs << err_msg;
});
logger->info("Good message #1");
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Bad format msg {} {}", "xxx");
#else
logger->info("Bad format msg %s %s", "xxx");
#endif
logger->info("Good message #2");
spdlog::drop("logger"); //force logger to drain the queue and shutdown
spdlog::set_sync_mode();
@@ -62,3 +102,25 @@ TEST_CASE("async_error_handler", "[errors]]")
REQUIRE(count_lines(filename) == 2);
REQUIRE(file_contents("logs/custom_err.txt") == err_msg);
}
// Make sure async error handler is executed
TEST_CASE("async_error_handler2", "[errors]]")
{
prepare_logdir();
std::string err_msg("This is async handler error message");
spdlog::set_async_mode(128);
{
auto logger = spdlog::create<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string& msg)
{
std::ofstream ofs("logs/custom_err2.txt");
if (!ofs) throw std::runtime_error("Failed open logs/custom_err2.txt");
ofs << err_msg;
});
logger->info("Hello failure");
spdlog::drop("failed_logger"); //force logger to drain the queue and shutdown
spdlog::set_sync_mode();
}
REQUIRE(file_contents("logs/custom_err2.txt") == err_msg);
}

View File

@@ -12,7 +12,7 @@ static void write_with_helper(file_helper &helper, size_t howmany)
log_msg msg;
msg.formatted << std::string(howmany, '1');
helper.write(msg);
helper.flush();
helper.flush();
}
@@ -45,7 +45,7 @@ TEST_CASE("file_helper_exists", "[file_helper::file_exists()]]")
{
prepare_logdir();
REQUIRE(!file_helper::file_exists(target_filename));
file_helper helper;
file_helper helper;
helper.open(target_filename);
REQUIRE(file_helper::file_exists(target_filename));
}
@@ -65,7 +65,7 @@ TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
{
prepare_logdir();
size_t expected_size = 14;
file_helper helper;
file_helper helper;
helper.open(target_filename);
write_with_helper(helper, expected_size);
REQUIRE(helper.size() == expected_size);
@@ -73,6 +73,77 @@ TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
REQUIRE(helper.size() == expected_size);
}
TEST_CASE("file_helper_split_by_extenstion", "[file_helper::split_by_extenstion()]]")
{
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion("mylog.txt");
REQUIRE(basename == "mylog");
REQUIRE(ext == ".txt");
}
TEST_CASE("file_helper_split_by_extenstion2", "[file_helper::split_by_extenstion()]]")
{
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion("mylog");
REQUIRE(basename == "mylog");
REQUIRE(ext == "");
}
TEST_CASE("file_helper_split_by_extenstion3", "[file_helper::split_by_extenstion()]]")
{
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion("mylog.xyz.txt");
REQUIRE(basename == "mylog.xyz");
REQUIRE(ext == ".txt");
}
TEST_CASE("file_helper_split_by_extenstion4", "[file_helper::split_by_extenstion()]]")
{
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion("mylog.xyz....txt");
REQUIRE(basename == "mylog.xyz...");
REQUIRE(ext == ".txt");
}
TEST_CASE("file_helper_split_by_extenstion5", "[file_helper::split_by_extenstion(hidden_file)]]")
{
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion(".mylog");
REQUIRE(basename == ".mylog");
REQUIRE(ext == "");
}
TEST_CASE("file_helper_split_by_extenstion6", "[file_helper::split_by_extenstion(hidden_file)]]")
{
#ifdef _WIN32
auto filename = "folder\\.mylog";
auto expected_basename = "folder\\.mylog";
#else
auto filename = "folder/.mylog";
auto expected_basename = "folder/.mylog";
#endif
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion(filename);
REQUIRE(basename == expected_basename);
REQUIRE(ext == "");
}
TEST_CASE("file_helper_split_by_extenstion7", "[file_helper::split_by_extenstion(hidden_file)]]")
{
#ifdef _WIN32
auto filename = "folder\\.mylog.txt";
auto expected_basename = "folder\\.mylog";
#else
auto filename = "folder/.mylog.txt";
auto expected_basename = "folder/.mylog";
#endif
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion(filename);
REQUIRE(basename == expected_basename);
REQUIRE(ext == ".txt");
}

View File

@@ -7,14 +7,18 @@
TEST_CASE("simple_file_logger", "[simple_logger]]")
{
prepare_logdir();
std::string filename = "logs/simple_log.txt";
std::string filename = "logs/simple_log";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", 1);
logger->info("Test message {}", 2);
#else
logger->info("Test message %d", 1);
logger->info("Test message %d", 2);
#endif
logger->flush();
REQUIRE(file_contents(filename) == std::string("Test message 1\nTest message 2\n"));
REQUIRE(count_lines(filename) == 2);
@@ -23,21 +27,26 @@ TEST_CASE("simple_file_logger", "[simple_logger]]")
TEST_CASE("flush_on", "[flush_on]]")
{
prepare_logdir();
std::string filename = "logs/simple_log.txt";
prepare_logdir();
std::string filename = "logs/simple_log";
auto logger = spdlog::create<spdlog::sinks::simple_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);
auto logger = spdlog::create<spdlog::sinks::simple_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);
logger->info("Test message {}", 1);
logger->info("Test message {}", 2);
logger->flush();
REQUIRE(file_contents(filename) == std::string("Should not be flushed\nTest message 1\nTest message 2\n"));
REQUIRE(count_lines(filename) == 3);
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", 1);
logger->info("Test message {}", 2);
#else
logger->info("Test message %d", 1);
logger->info("Test message %d", 2);
#endif
logger->flush();
REQUIRE(file_contents(filename) == std::string("Should not be flushed\nTest message 1\nTest message 2\n"));
REQUIRE(count_lines(filename) == 3);
}
TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
@@ -45,12 +54,18 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
prepare_logdir();
std::string basename = "logs/rotating_log";
auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);
logger->flush();
auto filename = basename + ".txt";
for (int i = 0; i < 10; ++i)
{
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", i);
#else
logger->info("Test message %d", i);
#endif
}
logger->flush();
auto filename = basename;
REQUIRE(count_lines(filename) == 10);
}
@@ -64,14 +79,20 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
logger->info("Test message {}", i);
logger->flush();
auto filename = basename + ".txt";
auto filename = basename;
REQUIRE(count_lines(filename) == 10);
for (int i = 0; i < 1000; i++)
{
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", i);
#else
logger->info("Test message %d", i);
#endif
}
logger->flush();
REQUIRE(get_filesize(filename) <= 1024);
auto filename1 = basename + ".1.txt";
auto filename1 = basename + ".1";
REQUIRE(get_filesize(filename1) <= 1024);
}
@@ -83,12 +104,18 @@ TEST_CASE("daily_logger", "[daily_logger]]")
std::string basename = "logs/daily_log";
std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w;
w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
auto logger = spdlog::daily_logger_mt("logger", basename, 0, 0);
logger->flush_on(spdlog::level::info);
for (int i = 0; i < 10; ++i)
{
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", i);
#else
logger->info("Test message %d", i);
#endif
}
auto filename = w.str();
REQUIRE(count_lines(filename) == 10);
@@ -106,23 +133,29 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger_dateonly]]")
std::string basename = "logs/daily_dateonly";
std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w;
w.write("{}_{:04d}-{:02d}-{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
w.write("{}_{:04d}-{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0);
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
for (int i = 0; i < 10; ++i)
{
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", i);
logger->flush();
#else
logger->info("Test message %d", i);
#endif
}
logger->flush();
auto filename = w.str();
REQUIRE(count_lines(filename) == 10);
}
struct custom_daily_file_name_calculator
{
static spdlog::filename_t calc_filename(const spdlog::filename_t& basename, const spdlog::filename_t& extension)
static spdlog::filename_t calc_filename(const spdlog::filename_t& basename)
{
std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w;
w.write("{}{:04d}{:02d}{:02d}.{}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, extension);
w.write("{}{:04d}{:02d}{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
return w.str();
}
};
@@ -138,14 +171,78 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]")
std::string basename = "logs/daily_dateonly";
std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w;
w.write("{}{:04d}{:02d}{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
w.write("{}{:04d}{:02d}{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0);
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
for (int i = 0; i < 10; ++i)
{
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", i);
#else
logger->info("Test message %d", i);
#endif
}
logger->flush();
auto filename = w.str();
logger->flush();
auto filename = w.str();
REQUIRE(count_lines(filename) == 10);
}
/*
* File name calculations
*/
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");
}
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");
}
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");
}
// regex supported only from gcc 4.9 and above
#if defined (_MSC_VER) || !(__GNUC__ <= 4 && __GNUC_MINOR__ < 9)
#include <regex>
TEST_CASE("daily_file_sink::default_daily_file_name_calculator1", "[daily_file_sink]]")
{
// daily_YYYY-MM-DD_hh-mm.txt
auto filename = spdlog::sinks::default_daily_file_name_calculator::calc_filename("daily.txt");
std::regex re(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])_\d\d-[0-5][0-9].txt$)");
std::smatch match;
REQUIRE(std::regex_match(filename, match, re));
}
TEST_CASE("daily_file_sink::default_daily_file_name_calculator2", "[daily_file_sink]]")
{
// daily_YYYY-MM-DD_hh-mm.txt
auto filename = spdlog::sinks::default_daily_file_name_calculator::calc_filename("daily");
std::regex re(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])_\d\d-[0-5][0-9]$)");
std::smatch match;
REQUIRE(std::regex_match(filename, match, re));
}
TEST_CASE("daily_file_sink::dateonly_daily_file_name_calculator", "[daily_file_sink]]")
{
// daily_YYYY-MM-DD_hh-mm.txt
auto filename = spdlog::sinks::dateonly_daily_file_name_calculator::calc_filename("daily.txt");
// 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;
REQUIRE(std::regex_match(filename, match, re));
}
#endif

View File

@@ -6,10 +6,12 @@
#include <ostream>
#include <chrono>
#include <exception>
#include "catch.hpp"
#include "utils.h"
#define SPDLOG_TRACE_ON
#define SPDLOG_DEBUG_ON
#include "../include/spdlog/spdlog.h"
#include "../include/spdlog/sinks/null_sink.h"
#include "../include/spdlog/sinks/ostream_sink.h"

50
tests/test_macros.cpp Normal file
View File

@@ -0,0 +1,50 @@
/*
* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
*/
#include "includes.h"
TEST_CASE("debug and trace w/o format string", "[macros]]")
{
prepare_logdir();
std::string filename = "logs/simple_log";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
logger->set_level(spdlog::level::trace);
SPDLOG_TRACE(logger, "Test message 1");
//SPDLOG_DEBUG(logger, "Test message 2");
SPDLOG_DEBUG(logger, "Test message 2");
logger->flush();
REQUIRE(ends_with(file_contents(filename), "Test message 2\n"));
REQUIRE(count_lines(filename) == 2);
}
TEST_CASE("debug and trace with format strings", "[macros]]")
{
prepare_logdir();
std::string filename = "logs/simple_log";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
logger->set_level(spdlog::level::trace);
#if !defined(SPDLOG_FMT_PRINTF)
SPDLOG_TRACE(logger, "Test message {}", 1);
//SPDLOG_DEBUG(logger, "Test message 2");
SPDLOG_DEBUG(logger, "Test message {}", 222);
#else
SPDLOG_TRACE(logger, "Test message %d", 1);
//SPDLOG_DEBUG(logger, "Test message 2");
SPDLOG_DEBUG(logger, "Test message %d", 222);
#endif
logger->flush();
REQUIRE(ends_with(file_contents(filename), "Test message 222\n"));
REQUIRE(count_lines(filename) == 2);
}

View File

@@ -131,6 +131,7 @@
<ClCompile Include="format.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="registry.cpp" />
<ClCompile Include="test_macros.cpp" />
<ClCompile Include="utils.cpp" />
</ItemGroup>
<ItemGroup>

View File

@@ -36,6 +36,9 @@
<ClCompile Include="errors.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="test_macros.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="includes.h">

View File

@@ -1,14 +1,17 @@
#include "includes.h"
void prepare_logdir()
{
spdlog::drop_all();
#ifdef _WIN32
auto rv = system("del /F /Q logs\\*");
system("if not exist logs mkdir logs");
system("del /F /Q logs\\*");
#else
auto rv = system("rm -f logs/*");
#endif
auto rv = system("mkdir -p logs");
rv = system("rm -f logs/*");
(void)rv;
#endif
}
@@ -43,3 +46,11 @@ std::size_t get_filesize(const std::string& filename)
return static_cast<std::size_t>(ifs.tellg());
}
// source: https://stackoverflow.com/a/2072890/192001
bool ends_with(std::string const & value, std::string const & ending)
{
if (ending.size() > value.size()) return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}

View File

@@ -13,3 +13,4 @@ std::size_t count_lines(const std::string& filename);
std::size_t get_filesize(const std::string& filename);
bool ends_with(std::string const & value, std::string const & ending);