Compare commits

..

229 Commits

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

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

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

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

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

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

5
.gitignore vendored
View File

@@ -61,4 +61,7 @@ install_manifest.txt
/tests/tests.VC.VC.opendb /tests/tests.VC.VC.opendb
/tests/tests.VC.db /tests/tests.VC.db
/tests/tests /tests/tests
/tests/logs/file_helper_test.txt /tests/logs/*
# idea
.idea/

View File

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

View File

@@ -1,87 +1,116 @@
# #
# Copyright(c) 2015 Ruslan Baratov. # Copyright(c) 2015 Ruslan Baratov.
# Distributed under the MIT License (http://opensource.org/licenses/MIT) # Distributed under the MIT License (http://opensource.org/licenses/MIT)
# #
cmake_minimum_required(VERSION 3.1) cmake_minimum_required(VERSION 3.1)
project(spdlog VERSION 1.0.0) project(spdlog VERSION 0.16.2)
include(CTest) include(CTest)
include(CMakeDependentOption)
set(CMAKE_CXX_STANDARD 11) include(GNUInstallDirs)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
#---------------------------------------------------------------------------------------
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") # compiler config
set(CMAKE_CXX_FLAGS "-Wall ${CMAKE_CXX_FLAGS}") #---------------------------------------------------------------------------------------
endif() set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_library(spdlog INTERFACE)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF) set(CMAKE_CXX_FLAGS "-Wall ${CMAKE_CXX_FLAGS}")
option(SPDLOG_BUILD_TESTING "Build spdlog tests" ON) endif()
target_include_directories( #---------------------------------------------------------------------------------------
spdlog # spdlog target
INTERFACE #---------------------------------------------------------------------------------------
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>" add_library(spdlog INTERFACE)
"$<INSTALL_INTERFACE:include>"
) option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF)
cmake_dependent_option(SPDLOG_BUILD_TESTING
set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include") "Build spdlog tests" ON
"BUILD_TESTING" OFF
if(SPDLOG_BUILD_EXAMPLES) )
add_subdirectory(example)
endif() target_include_directories(
spdlog
if(SPDLOG_BUILD_TESTING) INTERFACE
add_subdirectory(tests) "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
endif() "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
### Install ###
# * https://github.com/forexample/package-example set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include")
set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
if(SPDLOG_BUILD_EXAMPLES)
set(config_install_dir "lib/cmake/${PROJECT_NAME}") add_subdirectory(example)
set(include_install_dir "include") endif()
set(pkgconfig_install_dir "lib/pkgconfig")
if(SPDLOG_BUILD_TESTING)
set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake") add_subdirectory(tests)
set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake") endif()
set(pkg_config "${generated_dir}/${PROJECT_NAME}.pc")
set(targets_export_name "${PROJECT_NAME}Targets") #---------------------------------------------------------------------------------------
set(namespace "${PROJECT_NAME}::") # Install/export targets and files
#---------------------------------------------------------------------------------------
include(CMakePackageConfigHelpers) # set files and directories
write_basic_package_version_file( set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
"${version_config}" COMPATIBILITY SameMajorVersion 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")
# Note: use 'targets_export_name' set(project_config "${PROJECT_NAME}Config.cmake")
configure_file("cmake/Config.cmake.in" "${project_config}" @ONLY) set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc")
configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY) set(targets_export_name "${PROJECT_NAME}Targets")
set(namespace "${PROJECT_NAME}::")
install(
TARGETS spdlog # generate package version file
EXPORT "${targets_export_name}" include(CMakePackageConfigHelpers)
INCLUDES DESTINATION "${include_install_dir}" write_basic_package_version_file(
) "${version_config}" COMPATIBILITY SameMajorVersion
)
install(DIRECTORY "include/spdlog" DESTINATION "${include_install_dir}")
# configure pkg config file
install( configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY)
FILES "${project_config}" "${version_config}"
DESTINATION "${config_install_dir}" # install targets
) install(
TARGETS spdlog
install( EXPORT "${targets_export_name}"
FILES "${pkg_config}" INCLUDES DESTINATION "${include_install_dir}"
DESTINATION "${pkgconfig_install_dir}" )
)
# install headers
install( install(
EXPORT "${targets_export_name}" DIRECTORY "${HEADER_BASE}/${PROJECT_NAME}"
NAMESPACE "${namespace}" DESTINATION "${include_install_dir}"
DESTINATION "${config_install_dir}" )
)
# install project version file
file(GLOB_RECURSE spdlog_include_SRCS "${HEADER_BASE}/*.h") install(
add_custom_target(spdlog_headers_for_ide SOURCES ${spdlog_include_SRCS}) 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,17 +4,18 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
## Install ## Install
#### Just copy or clone the headers: #### Just copy the headers:
* Copy the source [folder](https://github.com/gabime/spdlog/tree/master/include/spdlog) to your build tree and use a C++11 compiler. * Copy the source [folder](https://github.com/gabime/spdlog/tree/master/include/spdlog) to your build tree and use a C++11 compiler.
#### Or use your favourite package manager: #### Or use your favorite package manager:
* Ubuntu: `apt-get install libspdlog-dev` * Ubuntu: `apt-get install libspdlog-dev`
* Homebrew: `brew install spdlog` * Homebrew: `brew install spdlog`
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean` * FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
* Fedora: `yum install spdlog` * Fedora: `yum install spdlog`
* Arch Linux: `pacman -S spdlog-git` * Gentoo: `emerge dev-libs/spdlog`
* Arch Linux: `yaourt -S spdlog-git`
* vcpkg: `vcpkg install spdlog` * vcpkg: `vcpkg install spdlog`
@@ -24,12 +25,14 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
* Mac OSX (clang 3.5+) * Mac OSX (clang 3.5+)
* Android * Android
##Features ## Features
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below). * Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
* Headers only, just copy and use. * Headers only, just copy and use.
* Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library. * Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library.
* Optional printf syntax support.
* Extremely fast asynchronous mode (optional) - using lockfree queues and other tricks to reach millions of calls/sec. * 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. * [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
* Conditional Logging
* Multi/Single threaded loggers. * Multi/Single threaded loggers.
* Various log targets: * Various log targets:
* Rotating log files. * Rotating log files.
@@ -90,15 +93,15 @@ int main(int, char*[])
auto console = spd::stdout_color_mt("console"); auto console = spd::stdout_color_mt("console");
console->info("Welcome to spdlog!"); console->info("Welcome to spdlog!");
console->error("Some error message with arg{}..", 1); console->error("Some error message with arg{}..", 1);
// Formatting examples // Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12); 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->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("Support for floats {:03.2f}", 1.23456);
console->info("Positional args are {1} {0}..", "too", "supported"); console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned"); console->info("{:<30}", "left aligned");
// 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"); spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
// Create basic file logger (not rotated) // Create basic file logger (not rotated)
@@ -106,12 +109,12 @@ int main(int, char*[])
my_logger->info("Some log message"); my_logger->info("Some log message");
// Create a file rotating logger with 5mb size max and 3 rotated files // 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) for (int i = 0; i < 10; ++i)
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i); rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
// Create a daily logger - a new file is created every day on 2:30am // 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 // trigger flush if the log severity is error or higher
daily_logger->flush_on(spd::level::err); daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44); daily_logger->info(123.44);
@@ -121,11 +124,11 @@ int main(int, char*[])
rotating_logger->info("This is another message with custom format"); rotating_logger->info("This is another message with custom format");
// Runtime log levels // Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info spd::set_level(spd::level::info); //Set global log level to info
console->debug("This message shold not be displayed!"); console->debug("This message should not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message shold be displayed.."); console->debug("This message should be displayed..");
// Compile time log levels // Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON // define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
@@ -149,13 +152,13 @@ int main(int, char*[])
err_handler_example(); err_handler_example();
// Apply a function on all registered loggers // Apply a function on all registered loggers
spd::apply_all([&](std::shared_ptr<spdlog::logger> l) spd::apply_all([&](std::shared_ptr<spd::logger> l)
{ {
l->info("End of example."); l->info("End of example.");
}); });
// Release and close all loggers // 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) catch (const spd::spdlog_ex& ex)
@@ -168,7 +171,7 @@ int main(int, char*[])
void async_example() void async_example()
{ {
size_t q_size = 4096; //queue size must be power of 2 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"); auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
for (int i = 0; i < 100; ++i) for (int i = 0; i < 100; ++i)
async_file->info("Async message #{}", i); async_file->info("Async message #{}", i);
@@ -206,7 +209,7 @@ void user_defined_example()
// //
void err_handler_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; std::cerr << "my err handler: " << msg << std::endl;
}); });
// (or logger->set_error_handler(..) to set for specific logger) // (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

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

View File

@@ -6,6 +6,10 @@
// spdlog usage example // spdlog usage example
// //
// //
#define SPDLOG_TRACE_ON
#define SPDLOG_DEBUG_ON
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include <iostream> #include <iostream>
@@ -27,6 +31,7 @@ int main(int, char*[])
console->info("Welcome to spdlog!"); console->info("Welcome to spdlog!");
console->error("Some error message with arg{}..", 1); console->error("Some error message with arg{}..", 1);
// Formatting examples // Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12); 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->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
@@ -34,21 +39,20 @@ int main(int, char*[])
console->info("Positional args are {1} {0}..", "too", "supported"); console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned"); console->info("{:<30}", "left aligned");
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function"); spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
// Create basic file logger (not rotated) // 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"); my_logger->info("Some log message");
// Create a file rotating logger with 5mb size max and 3 rotated files // 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) for (int i = 0; i < 10; ++i)
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i); rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
// Create a daily logger - a new file is created every day on 2:30am // 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 // trigger flush if the log severity is error or higher
daily_logger->flush_on(spd::level::err); daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44); daily_logger->info(123.44);
@@ -69,6 +73,7 @@ int main(int, char*[])
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23); SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23); SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
// Asynchronous logging is very fast.. // Asynchronous logging is very fast..
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous.. // Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
async_example(); async_example();
@@ -107,7 +112,6 @@ void async_example()
size_t q_size = 4096; //queue size must be power of 2 size_t q_size = 4096; //queue size must be power of 2
spdlog::set_async_mode(q_size); spdlog::set_async_mode(q_size);
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt"); auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
for (int i = 0; i < 100; ++i) for (int i = 0; i < 100; ++i)
async_file->info("Async message #{}", 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() void user_defined_example()
{ {
spd::get("console")->info("user defined type: {}", my_type { 14 }); spd::get("console")->info("user defined type: {}", my_type { 14 });

View File

@@ -15,8 +15,8 @@
// 3. will throw spdlog_ex upon log exceptions // 3. will throw spdlog_ex upon log exceptions
// Upon destruction, logs all remaining messages in the queue before destructing.. // Upon destruction, logs all remaining messages in the queue before destructing..
#include <spdlog/common.h> #include "common.h"
#include <spdlog/logger.h> #include "logger.h"
#include <chrono> #include <chrono>
#include <functional> #include <functional>
@@ -31,7 +31,7 @@ namespace details
class async_log_helper; class async_log_helper;
} }
class async_logger :public logger class async_logger SPDLOG_FINAL :public logger
{ {
public: public:
template<class It> template<class It>
@@ -61,12 +61,17 @@ public:
const std::function<void()>& worker_teardown_cb = nullptr); const std::function<void()>& worker_teardown_cb = nullptr);
//Wait for the queue to be empty, and flush synchronously //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; void flush() override;
// Error handler
virtual void set_error_handler(log_err_handler) override;
virtual log_err_handler error_handler() override;
protected: protected:
void _sink_it(details::log_msg& msg) override; void _sink_it(details::log_msg& msg) override;
void _set_formatter(spdlog::formatter_ptr msg_formatter) override; void _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: private:
std::unique_ptr<details::async_log_helper> _async_log_helper; 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> #include <locale>
#endif #endif
#include <spdlog/details/null_mutex.h> #include "details/null_mutex.h"
//visual studio upto 2013 does not support noexcept nor constexpr //visual studio upto 2013 does not support noexcept nor constexpr
#if defined(_MSC_VER) && (_MSC_VER < 1900) #if defined(_MSC_VER) && (_MSC_VER < 1900)
@@ -29,6 +29,13 @@
#define SPDLOG_CONSTEXPR constexpr #define SPDLOG_CONSTEXPR constexpr
#endif #endif
// final keyword support. On by default. See tweakme.h
#if defined(SPDLOG_NO_FINAL)
#define SPDLOG_FINAL
#else
#define SPDLOG_FINAL final
#endif
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#define SPDLOG_DEPRECATED __attribute__((deprecated)) #define SPDLOG_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
@@ -37,8 +44,7 @@
#define SPDLOG_DEPRECATED #define SPDLOG_DEPRECATED
#endif #endif
#include "fmt/fmt.h"
#include <spdlog/fmt/fmt.h>
namespace spdlog namespace spdlog
{ {
@@ -76,7 +82,10 @@ typedef enum
off = 6 off = 6
} level_enum; } 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" }; 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 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 // Log exception

View File

@@ -9,17 +9,15 @@
// If the internal queue of log messages reaches its max size, // If the internal queue of log messages reaches its max size,
// then the client call will block until there is more room. // 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 #pragma once
#include <spdlog/common.h> #include "../common.h"
#include <spdlog/sinks/sink.h> #include "../sinks/sink.h"
#include <spdlog/details/mpmc_bounded_q.h> #include "../details/mpmc_bounded_q.h"
#include <spdlog/details/log_msg.h> #include "../details/log_msg.h"
#include <spdlog/details/os.h> #include "../details/os.h"
#include <spdlog/formatter.h> #include "../formatter.h"
#include <chrono> #include <chrono>
#include <exception> #include <exception>
@@ -53,6 +51,7 @@ class async_log_helper
size_t thread_id; size_t thread_id;
std::string txt; std::string txt;
async_msg_type msg_type; async_msg_type msg_type;
size_t msg_id;
async_msg() = default; async_msg() = default;
~async_msg() = default; ~async_msg() = default;
@@ -62,11 +61,17 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
logger_name(std::move(other.logger_name)), logger_name(std::move(other.logger_name)),
level(std::move(other.level)), level(std::move(other.level)),
time(std::move(other.time)), time(std::move(other.time)),
thread_id(other.thread_id),
txt(std::move(other.txt)), 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 async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT
@@ -77,6 +82,7 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
thread_id = other.thread_id; thread_id = other.thread_id;
txt = std::move(other.txt); txt = std::move(other.txt);
msg_type = other.msg_type; msg_type = other.msg_type;
msg_id = other.msg_id;
return *this; return *this;
} }
@@ -85,12 +91,13 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
async_msg& operator=(const async_msg& other) = delete; async_msg& operator=(const async_msg& other) = delete;
// construct from log_msg // construct from log_msg
async_msg(const details::log_msg& m) : async_msg(const details::log_msg& m):
level(m.level), level(m.level),
time(m.time), time(m.time),
thread_id(m.thread_id), thread_id(m.thread_id),
txt(m.raw.data(), m.raw.size()), 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 #ifndef SPDLOG_NO_NAME
logger_name = *m.logger_name; logger_name = *m.logger_name;
@@ -106,6 +113,7 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
msg.time = time; msg.time = time;
msg.thread_id = thread_id; msg.thread_id = thread_id;
msg.raw << txt; msg.raw << txt;
msg.msg_id = msg_id;
} }
}; };
@@ -135,6 +143,7 @@ public:
void flush(bool wait_for_q); void flush(bool wait_for_q);
void set_error_handler(spdlog::log_err_handler err_handler);
private: private:
formatter_ptr _formatter; formatter_ptr _formatter;
@@ -176,7 +185,7 @@ private:
void handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush); void handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush);
// sleep,yield or return 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); 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 // wait until the queue is empty
@@ -221,7 +230,8 @@ inline spdlog::details::async_log_helper::~async_log_helper()
_worker_thread.join(); _worker_thread.join();
} }
catch (...) // don't crash in destructor catch (...) // don't crash in destructor
{} {
}
} }
@@ -250,28 +260,34 @@ inline void spdlog::details::async_log_helper::push_msg(details::async_log_helpe
inline void spdlog::details::async_log_helper::flush(bool wait_for_q) inline void spdlog::details::async_log_helper::flush(bool wait_for_q)
{ {
push_msg(async_msg(async_msg_type::flush)); push_msg(async_msg(async_msg_type::flush));
if(wait_for_q) if (wait_for_q)
wait_empty_q(); //return only make after the above flush message was processed wait_empty_q(); //return when queue is empty
} }
inline void spdlog::details::async_log_helper::worker_loop() 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(); try
auto last_pop = details::os::now(); {
auto last_flush = last_pop; active = process_next_msg(last_pop, last_flush);
while(process_next_msg(last_pop, last_flush)); }
if (_worker_teardown_cb) _worker_teardown_cb(); catch (const std::exception &ex)
} {
catch (const std::exception &ex) _err_handler(ex.what());
{ }
_err_handler(ex.what()); catch(...)
} {
catch (...) _err_handler("Unknown exeption in async logger worker loop.");
{ }
_err_handler("Unknown exception");
} }
if (_worker_teardown_cb) _worker_teardown_cb();
} }
// process next message in the queue // process next message in the queue
@@ -300,7 +316,7 @@ inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_
_formatter->format(incoming_log_msg); _formatter->format(incoming_log_msg);
for (auto &s : _sinks) 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); s->log(incoming_log_msg);
} }
@@ -360,19 +376,24 @@ inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_
if (time_since_op <= milliseconds(200)) if (time_since_op <= milliseconds(200))
return sleep_for(milliseconds(20)); return sleep_for(milliseconds(20));
// sleep for 200 ms // sleep for 500 ms
return sleep_for(milliseconds(200)); return sleep_for(milliseconds(500));
} }
// wait for the queue to be empty // wait for the queue to be empty
inline void spdlog::details::async_log_helper::wait_empty_q() inline void spdlog::details::async_log_helper::wait_empty_q()
{ {
auto last_op = details::os::now(); auto last_op = details::os::now();
while (_q.approx_size() > 0) while (!_q.is_empty())
{ {
sleep_or_yield(details::os::now(), last_op); 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 // Async Logger implementation
// Use an async_sink (queue per logger) to perform the logging in a worker thread // Use an async_sink (queue per logger) to perform the logging in a worker thread
#include <spdlog/details/async_log_helper.h> #include "../details/async_log_helper.h"
#include <spdlog/async_logger.h> #include "../async_logger.h"
#include <string> #include <string>
#include <functional> #include <functional>
@@ -57,15 +57,28 @@ inline void spdlog::async_logger::flush()
_async_log_helper->flush(true); _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) inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)
{ {
_formatter = msg_formatter; _formatter = msg_formatter;
_async_log_helper->set_formatter(_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); _async_log_helper->set_formatter(_formatter);
} }
@@ -74,6 +87,9 @@ inline void spdlog::async_logger::_sink_it(details::log_msg& msg)
{ {
try try
{ {
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
_incr_msg_counter(msg);
#endif
_async_log_helper->log(msg); _async_log_helper->log(msg);
if (_should_flush_on(msg)) if (_should_flush_on(msg))
_async_log_helper->flush(false); // do async flush _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()); _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 // Helper class for file sink
// When failing to open a file, retry several times(5) with small delay between the tries(10 ms) // When failing to open a file, retry several times(5) with small delay between the tries(10 ms)
// Can be set to auto flush on every line
// Throw spdlog_ex exception on errors // Throw spdlog_ex exception on errors
#include <spdlog/details/os.h> #include "../details/os.h"
#include <spdlog/details/log_msg.h> #include "../details/log_msg.h"
#include <chrono> #include <chrono>
#include <cstdio> #include <cstdio>
#include <string> #include <string>
#include <thread> #include <thread>
#include <tuple>
#include <cerrno> #include <cerrno>
namespace spdlog namespace spdlog
@@ -85,14 +85,13 @@ public:
void write(const log_msg& msg) void write(const log_msg& msg)
{ {
size_t msg_size = msg.formatted.size(); size_t msg_size = msg.formatted.size();
auto data = msg.formatted.data(); auto data = msg.formatted.data();
if (std::fwrite(data, 1, msg_size, _fd) != msg_size) 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) if (!_fd)
throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename)); throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
@@ -104,12 +103,41 @@ public:
return _filename; return _filename;
} }
static bool file_exists(const filename_t& name) static bool file_exists(const filename_t& fname)
{ {
return os::file_exists(fname);
return os::file_exists(name);
} }
//
// return file path and its extension:
//
// "mylog.txt" => ("mylog", ".txt")
// "mylog" => ("mylog", "")
// "mylog." => ("mylog.", "")
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
//
// the starting dot in filenames is ignored (hidden files):
//
// ".mylog" => (".mylog". "")
// "my_folder/.mylog" => ("my_folder/.mylog", "")
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
static std::tuple<filename_t, filename_t> split_by_extenstion(const spdlog::filename_t& fname)
{
auto ext_index = fname.rfind('.');
// no valid extension found - return whole path and empty string as extension
if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
return std::make_tuple(fname, spdlog::filename_t());
// treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
//auto folder_index = fname.find('\\', ext_index);
auto folder_index = fname.rfind(details::os::folder_sep);
if (folder_index != fname.npos && folder_index >= ext_index - 1)
return std::make_tuple(fname, spdlog::filename_t());
// finally - return a valid base and extnetion tuple
return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
}
private: private:
FILE* _fd; FILE* _fd;
filename_t _filename; filename_t _filename;

View File

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

View File

@@ -5,24 +5,24 @@
#pragma once #pragma once
#include <spdlog/logger.h> #include "../logger.h"
#include <spdlog/sinks/stdout_sinks.h> #include "../sinks/stdout_sinks.h"
#include <memory> #include <memory>
#include <string> #include <string>
// create logger with given name, sinks and the default pattern formatter // create logger with given name, sinks and the default pattern formatter
// all other ctors will call this one // all other ctors will call this one
template<class It> template<class It>
inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end): inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end):
_name(logger_name), _name(logger_name),
_sinks(begin, end), _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) _err_handler = [this](const std::string &msg)
{ {
this->_default_err_handler(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); _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> template <typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args) inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args)
{ {
@@ -66,16 +65,22 @@ inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Ar
try try
{ {
details::log_msg log_msg(&_name, lvl); 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...); log_msg.raw.write(fmt, args...);
#endif
_sink_it(log_msg); _sink_it(log_msg);
} }
catch (const std::exception &ex) catch (const std::exception &ex)
{ {
_err_handler(ex.what()); _err_handler(ex.what());
} }
catch (...) catch(...)
{ {
_err_handler("Unknown exception"); _err_handler("Unknown exception in logger " + _name);
throw;
} }
} }
@@ -93,11 +98,11 @@ inline void spdlog::logger::log(level::level_enum lvl, const char* msg)
{ {
_err_handler(ex.what()); _err_handler(ex.what());
} }
catch (...) catch (...)
{ {
_err_handler("Unknown exception"); _err_handler("Unknown exception in logger " + _name);
} throw;
}
} }
template<typename T> template<typename T>
@@ -114,48 +119,48 @@ inline void spdlog::logger::log(level::level_enum lvl, const T& msg)
{ {
_err_handler(ex.what()); _err_handler(ex.what());
} }
catch (...) catch (...)
{ {
_err_handler("Unknown exception"); _err_handler("Unknown exception in logger " + _name);
} throw;
}
} }
template <typename... Args> template <typename Arg1, typename... Args>
inline void spdlog::logger::trace(const char* fmt, const Args&... 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> template <typename Arg1, typename... Args>
inline void spdlog::logger::debug(const char* fmt, const Args&... 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> template <typename Arg1, typename... Args>
inline void spdlog::logger::info(const char* fmt, const Args&... 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 Arg1, typename... Args>
template <typename... Args> inline void spdlog::logger::warn(const char* fmt, const Arg1 &arg1, const Args&... args)
inline void spdlog::logger::warn(const char* fmt, const Args&... args)
{ {
log(level::warn, fmt, args...); log(level::warn, fmt, arg1, args...);
} }
template <typename... Args> template <typename Arg1, typename... Args>
inline void spdlog::logger::error(const char* fmt, const Args&... 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> template <typename Arg1, typename... Args>
inline void spdlog::logger::critical(const char* fmt, const Args&... 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 +204,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 // name and level
@@ -244,6 +310,9 @@ inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) cons
// //
inline void spdlog::logger::_sink_it(details::log_msg& msg) inline void spdlog::logger::_sink_it(details::log_msg& msg)
{ {
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
_incr_msg_counter(msg);
#endif
_formatter->format(msg); _formatter->format(msg);
for (auto &sink : _sinks) for (auto &sink : _sinks)
{ {
@@ -257,9 +326,9 @@ inline void spdlog::logger::_sink_it(details::log_msg& msg)
flush(); 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) inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter)
{ {
@@ -292,7 +361,13 @@ inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg)
return (msg.level >= flush_level) && (msg.level != level::off); 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 inline const std::vector<spdlog::sink_ptr>& spdlog::logger::sinks() const
{ {
return _sinks; return _sinks;
} }

View File

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

View File

@@ -4,7 +4,7 @@
// //
#pragma once #pragma once
#include <spdlog/common.h> #include "../common.h"
#include <cstdio> #include <cstdio>
#include <ctime> #include <ctime>
@@ -12,8 +12,9 @@
#include <string> #include <string>
#include <chrono> #include <chrono>
#include <thread> #include <thread>
#include <stdio.h> #include <algorithm>
#include <string.h> #include <cstring>
#include <cstdlib>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
@@ -28,7 +29,7 @@
#endif #endif
#include <windows.h> #include <windows.h>
#include <process.h> // _get_pid support #include <process.h> // _get_pid support
#include <io.h> // _get_osfhandle support #include <io.h> // _get_osfhandle and _isatty support
#ifdef __MINGW32__ #ifdef __MINGW32__
#include <share.h> #include <share.h>
@@ -142,6 +143,16 @@ inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL; SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL;
SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1; SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1;
// folder separator
#ifdef _WIN32
SPDLOG_CONSTEXPR static const char folder_sep = '\\';
#else
SPDLOG_CONSTEXPR static const char folder_sep = '/';
#endif
inline void prevent_child_fd(FILE *f) inline void prevent_child_fd(FILE *f)
{ {
#ifdef _WIN32 #ifdef _WIN32
@@ -150,7 +161,7 @@ inline void prevent_child_fd(FILE *f)
throw spdlog_ex("SetHandleInformation failed", errno); throw spdlog_ex("SetHandleInformation failed", errno);
#else #else
auto fd = fileno(f); auto fd = fileno(f);
if(fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno); throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno);
#endif #endif
} }
@@ -170,7 +181,7 @@ inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode
#endif #endif
#ifdef SPDLOG_PREVENT_CHILD_FD #ifdef SPDLOG_PREVENT_CHILD_FD
if(*fp != nullptr) if (*fp != nullptr)
prevent_child_fd(*fp); prevent_child_fd(*fp);
#endif #endif
return *fp == nullptr; return *fp == nullptr;
@@ -208,7 +219,7 @@ inline bool file_exists(const filename_t& filename)
return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY)); return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
#else //common linux/unix all have the stat system call #else //common linux/unix all have the stat system call
struct stat buffer; struct stat buffer;
return (stat (filename.c_str(), &buffer) == 0); return (stat(filename.c_str(), &buffer) == 0);
#endif #endif
} }
@@ -220,7 +231,7 @@ inline size_t filesize(FILE *f)
{ {
if (f == nullptr) if (f == nullptr)
throw spdlog_ex("Failed getting file size. fd is null"); throw spdlog_ex("Failed getting file size. fd is null");
#ifdef _WIN32 #if defined ( _WIN32) && !defined(__CYGWIN__)
int fd = _fileno(f); int fd = _fileno(f);
#if _WIN64 //64 bits #if _WIN64 //64 bits
struct _stat64 st; struct _stat64 st;
@@ -235,12 +246,12 @@ inline size_t filesize(FILE *f)
#else // unix #else // unix
int fd = fileno(f); int fd = fileno(f);
//64 bits(but not in osx, where fstat64 is deprecated) //64 bits(but not in osx or cygwin, where fstat64 is deprecated)
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) #if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__)
struct stat64 st; struct stat64 st;
if (fstat64(fd, &st) == 0) if (fstat64(fd, &st) == 0)
return static_cast<size_t>(st.st_size); return static_cast<size_t>(st.st_size);
#else // unix 32 bits or osx #else // unix 32 bits or cygwin
struct stat st; struct stat st;
if (fstat(fd, &st) == 0) if (fstat(fd, &st) == 0)
return static_cast<size_t>(st.st_size); return static_cast<size_t>(st.st_size);
@@ -315,7 +326,7 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
} }
//Return current thread id as size_t //Return current thread id as size_t
//It exists because the std::this_thread::get_id() is much slower(espcially under VS 2013) //It exists because the std::this_thread::get_id() is much slower(especially under VS 2013)
inline size_t _thread_id() inline size_t _thread_id()
{ {
#ifdef _WIN32 #ifdef _WIN32
@@ -329,7 +340,11 @@ inline size_t _thread_id()
long tid; long tid;
thr_self(&tid); thr_self(&tid);
return static_cast<size_t>(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())); return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
#endif #endif
} }
@@ -337,17 +352,17 @@ inline size_t _thread_id()
//Return current thread id as size_t (from thread local storage) //Return current thread id as size_t (from thread local storage)
inline size_t thread_id() inline size_t thread_id()
{ {
#if defined(_MSC_VER) && (_MSC_VER < 1900) || defined(__clang__) && !__has_feature(cxx_thread_local) #if defined(SPDLOG_DISABLE_TID_CACHING) || (defined(_MSC_VER) && (_MSC_VER < 1900)) || (defined(__clang__) && !__has_feature(cxx_thread_local))
return _thread_id(); return _thread_id();
#else #else // cache thread id in tls
static thread_local const size_t tid = _thread_id(); static thread_local const size_t tid = _thread_id();
return tid; return tid;
#endif #endif
} }
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) // wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
#define SPDLOG_FILENAME_T(s) L ## s #define SPDLOG_FILENAME_T(s) L ## s
@@ -364,6 +379,22 @@ inline std::string filename_to_str(const filename_t& filename)
} }
#endif #endif
inline std::string errno_to_string(char[256], char* res)
{
return std::string(res);
}
inline std::string errno_to_string(char buf[256], int res)
{
if (res == 0)
{
return std::string(buf);
}
else
{
return "Unknown error";
}
}
// Return errno string (thread safe) // Return errno string (thread safe)
inline std::string errno_str(int err_num) inline std::string errno_str(int err_num)
@@ -372,10 +403,10 @@ inline std::string errno_str(int err_num)
SPDLOG_CONSTEXPR auto buf_size = sizeof(buf); SPDLOG_CONSTEXPR auto buf_size = sizeof(buf);
#ifdef _WIN32 #ifdef _WIN32
if(strerror_s(buf, buf_size, err_num) == 0) if (strerror_s(buf, buf_size, err_num) == 0)
return std::string(buf); return std::string(buf);
else else
return "Unkown error"; return "Unknown error";
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \ #elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \
((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version ((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version
@@ -383,10 +414,11 @@ inline std::string errno_str(int err_num)
if (strerror_r(err_num, buf, buf_size) == 0) if (strerror_r(err_num, buf, buf_size) == 0)
return std::string(buf); return std::string(buf);
else else
return "Unkown error"; return "Unknown error";
#else // gnu version (might not use the given buf, so its retval pointer must be used) #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 #endif
} }
@@ -401,6 +433,47 @@ inline int pid()
} }
// 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 } //os
} //details } //details
} //spdlog } //spdlog

View File

@@ -5,10 +5,10 @@
#pragma once #pragma once
#include <spdlog/formatter.h> #include "../formatter.h"
#include <spdlog/details/log_msg.h> #include "../details/log_msg.h"
#include <spdlog/details/os.h> #include "../details/os.h"
#include <spdlog/fmt/fmt.h> #include "../fmt/fmt.h"
#include <chrono> #include <chrono>
#include <ctime> #include <ctime>
@@ -79,72 +79,54 @@ static int to12h(const tm& t)
} }
//Abbreviated weekday name //Abbreviated weekday name
using days_array = std::array<std::string, 7>; static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
static const days_array& days()
{
static const days_array arr{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
return arr;
}
class a_formatter:public flag_formatter class a_formatter:public flag_formatter
{ {
void format(details::log_msg& msg, const std::tm& tm_time) override void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
msg.formatted << days()[tm_time.tm_wday]; msg.formatted << days[tm_time.tm_wday];
} }
}; };
//Full weekday name //Full weekday name
static const days_array& full_days() static const std::string full_days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
{
static const days_array arr{ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
return arr;
}
class A_formatter:public flag_formatter class A_formatter:public flag_formatter
{ {
void format(details::log_msg& msg, const std::tm& tm_time) override void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
msg.formatted << full_days()[tm_time.tm_wday]; msg.formatted << full_days[tm_time.tm_wday];
} }
}; };
//Abbreviated month //Abbreviated month
using months_array = std::array<std::string, 12>; static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec" };
static const months_array& months()
{
static const months_array arr{ "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" };
return arr;
}
class b_formatter:public flag_formatter class b_formatter:public flag_formatter
{ {
void format(details::log_msg& msg, const std::tm& tm_time) override void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
msg.formatted << months()[tm_time.tm_mon]; msg.formatted << months[tm_time.tm_mon];
} }
}; };
//Full month name //Full month name
static const months_array& full_months() static const std::string full_months[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
{
static const months_array arr{ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
return arr;
}
class B_formatter:public flag_formatter class B_formatter:public flag_formatter
{ {
void format(details::log_msg& msg, const std::tm& tm_time) override void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
msg.formatted << full_months()[tm_time.tm_mon]; msg.formatted << full_months[tm_time.tm_mon];
} }
}; };
//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) 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'); w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0');
return w; 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) 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'); w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0');
@@ -153,18 +135,18 @@ 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) //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 void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
msg.formatted << days()[tm_time.tm_wday] << ' ' << months()[tm_time.tm_mon] << ' ' << tm_time.tm_mday << ' '; msg.formatted << days[tm_time.tm_wday] << ' ' << months[tm_time.tm_mon] << ' ' << tm_time.tm_mday << ' ';
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << tm_time.tm_year + 1900; pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << tm_time.tm_year + 1900;
} }
}; };
// year - 2 digit // 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 void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
@@ -175,7 +157,7 @@ class C_formatter:public flag_formatter
// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01 // 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 void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
@@ -185,7 +167,7 @@ class D_formatter:public flag_formatter
// year - 4 digit // 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 void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
@@ -194,7 +176,7 @@ class Y_formatter:public flag_formatter
}; };
// month 1-12 // 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 void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
@@ -203,7 +185,7 @@ class m_formatter:public flag_formatter
}; };
// day of month 1-31 // 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 void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
@@ -212,7 +194,7 @@ class d_formatter:public flag_formatter
}; };
// hours in 24 format 0-23 // 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 void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
@@ -221,7 +203,7 @@ class H_formatter:public flag_formatter
}; };
// hours in 12 format 1-12 // 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 void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
@@ -230,7 +212,7 @@ class I_formatter:public flag_formatter
}; };
// minutes 0-59 // 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 void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
@@ -239,7 +221,7 @@ class M_formatter:public flag_formatter
}; };
// seconds 0-59 // 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 void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
@@ -248,7 +230,7 @@ class S_formatter:public flag_formatter
}; };
// milliseconds // 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 void format(details::log_msg& msg, const std::tm&) override
{ {
@@ -259,7 +241,7 @@ class e_formatter:public flag_formatter
}; };
// microseconds // 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 void format(details::log_msg& msg, const std::tm&) override
{ {
@@ -270,7 +252,7 @@ class f_formatter:public flag_formatter
}; };
// nanoseconds // 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 void format(details::log_msg& msg, const std::tm&) override
{ {
@@ -280,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 // 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 void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
@@ -291,7 +283,7 @@ class p_formatter:public flag_formatter
// 12 hour clock 02:55:02 pm // 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 void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
@@ -300,7 +292,7 @@ class r_formatter:public flag_formatter
}; };
// 24-hour HH:MM time, equivalent to %H:%M // 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 void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
@@ -309,7 +301,7 @@ class R_formatter:public flag_formatter
}; };
// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S // 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 void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
@@ -317,14 +309,13 @@ class T_formatter:public flag_formatter
} }
}; };
// ISO 8601 offset from UTC in timezone (+-HH:MM) // ISO 8601 offset from UTC in timezone (+-HH:MM)
class z_formatter:public flag_formatter class z_formatter SPDLOG_FINAL:public flag_formatter
{ {
public: public:
const std::chrono::seconds cache_refresh = std::chrono::seconds(5); 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(const z_formatter&) = delete;
z_formatter& operator=(const z_formatter&) = delete; z_formatter& operator=(const z_formatter&) = delete;
@@ -376,7 +367,7 @@ private:
// Thread id // Thread id
class t_formatter:public flag_formatter class t_formatter SPDLOG_FINAL:public flag_formatter
{ {
void format(details::log_msg& msg, const std::tm&) override void format(details::log_msg& msg, const std::tm&) override
{ {
@@ -385,7 +376,7 @@ class t_formatter:public flag_formatter
}; };
// Current pid // Current pid
class pid_formatter:public flag_formatter class pid_formatter SPDLOG_FINAL:public flag_formatter
{ {
void format(details::log_msg& msg, const std::tm&) override void format(details::log_msg& msg, const std::tm&) override
{ {
@@ -393,8 +384,16 @@ class pid_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:public flag_formatter class v_formatter SPDLOG_FINAL:public flag_formatter
{ {
void format(details::log_msg& msg, const std::tm&) override void format(details::log_msg& msg, const std::tm&) override
{ {
@@ -402,7 +401,7 @@ class v_formatter:public flag_formatter
} }
}; };
class ch_formatter:public flag_formatter class ch_formatter SPDLOG_FINAL:public flag_formatter
{ {
public: public:
explicit ch_formatter(char ch): _ch(ch) explicit ch_formatter(char ch): _ch(ch)
@@ -417,7 +416,7 @@ private:
//aggregate user chars to display as is //aggregate user chars to display as is
class aggregate_formatter:public flag_formatter class aggregate_formatter SPDLOG_FINAL:public flag_formatter
{ {
public: public:
aggregate_formatter() aggregate_formatter()
@@ -436,7 +435,7 @@ private:
// Full info formatter // Full info formatter
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v // 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 void format(details::log_msg& msg, const std::tm& tm_time) override
{ {
@@ -488,7 +487,8 @@ class full_formatter:public flag_formatter
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// pattern_formatter inline impl // 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); compile_pattern(pattern);
} }
@@ -616,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())); _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::F_formatter()));
break; break;
case('E'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::E_formatter()));
break;
case('p'): case('p'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::p_formatter())); _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::p_formatter()));
break; break;
@@ -645,19 +649,31 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::pid_formatter())); _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::pid_formatter()));
break; break;
default: //Unkown flag appears as is
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('%')));
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter(flag))); _formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter(flag)));
break; 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) inline void spdlog::pattern_formatter::format(details::log_msg& msg)
{ {
#ifndef SPDLOG_NO_DATETIME #ifndef SPDLOG_NO_DATETIME
auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time)); auto tm_time = get_time(msg);
#else #else
std::tm tm_time; std::tm tm_time;
#endif #endif

View File

@@ -10,10 +10,10 @@
// If user requests a non existing logger, nullptr will be returned // If user requests a non existing logger, nullptr will be returned
// This class is thread safe // This class is thread safe
#include <spdlog/details/null_mutex.h> #include "../details/null_mutex.h"
#include <spdlog/logger.h> #include "../logger.h"
#include <spdlog/async_logger.h> #include "../async_logger.h"
#include <spdlog/common.h> #include "../common.h"
#include <chrono> #include <chrono>
#include <functional> #include <functional>
@@ -71,6 +71,26 @@ public:
return new_logger; 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) void apply_all(std::function<void(std::shared_ptr<logger>)> fun)
{ {
std::lock_guard<Mutex> lock(_mutex); std::lock_guard<Mutex> lock(_mutex);
@@ -99,6 +119,15 @@ public:
return create(logger_name, { sink }); return create(logger_name, { sink });
} }
std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, sinks_init_list sinks)
{
return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks.begin(), sinks.end());
}
std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, sink_ptr sink)
{
return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, { sink });
}
void formatter(formatter_ptr f) void formatter(formatter_ptr f)
{ {

View File

@@ -8,23 +8,23 @@
// //
// Global registry functions // Global registry functions
// //
#include <spdlog/spdlog.h> #include "../spdlog.h"
#include <spdlog/details/registry.h> #include "../details/registry.h"
#include <spdlog/sinks/file_sinks.h> #include "../sinks/file_sinks.h"
#include <spdlog/sinks/stdout_sinks.h> #include "../sinks/stdout_sinks.h"
#ifdef SPDLOG_ENABLE_SYSLOG #ifdef SPDLOG_ENABLE_SYSLOG
#include <spdlog/sinks/syslog_sink.h> #include "../sinks/syslog_sink.h"
#endif #endif
#ifdef _WIN32 #ifdef _WIN32
#include <spdlog/sinks/wincolor_sink.h> #include "../sinks/wincolor_sink.h"
#else #else
#include <spdlog/sinks/ansicolor_sink.h> #include "../sinks/ansicolor_sink.h"
#endif #endif
#ifdef __ANDROID__ #ifdef __ANDROID__
#include <spdlog/sinks/android_sink.h> #include "../sinks/android_sink.h"
#endif #endif
#include <chrono> #include <chrono>
@@ -61,23 +61,23 @@ inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_st(const std::string
// Create multi/single threaded rotating file logger // 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) 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) 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): // 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) 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) 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);
} }
@@ -137,34 +137,34 @@ inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name) inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
{ {
auto sink = std::make_shared<spdlog::sinks::ansicolor_sink>(spdlog::sinks::stdout_sink_mt::instance()); auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink); return spdlog::details::registry::instance().create(logger_name, sink);
} }
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name) inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
{ {
auto sink = std::make_shared<spdlog::sinks::ansicolor_sink>(spdlog::sinks::stdout_sink_st::instance()); auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink); return spdlog::details::registry::instance().create(logger_name, sink);
} }
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name) inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
{ {
auto sink = std::make_shared<spdlog::sinks::ansicolor_sink>(spdlog::sinks::stderr_sink_mt::instance()); auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink); return spdlog::details::registry::instance().create(logger_name, sink);
} }
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name) inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
{ {
auto sink = std::make_shared<spdlog::sinks::ansicolor_sink>(spdlog::sinks::stderr_sink_st::instance()); auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink); return spdlog::details::registry::instance().create(logger_name, sink);
} }
#endif #endif
#ifdef SPDLOG_ENABLE_SYSLOG #ifdef SPDLOG_ENABLE_SYSLOG
// Create syslog logger // 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::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); return create<spdlog::sinks::syslog_sink>(logger_name, syslog_ident, syslog_option, syslog_facility);
} }
#endif #endif
@@ -203,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); 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) inline void spdlog::set_formatter(spdlog::formatter_ptr f)
{ {
details::registry::instance().formatter(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

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,17 +1,16 @@
/* /*
Formatting library for C++ - std::ostream support Formatting library for C++ - std::ostream support
Copyright (c) 2012 - 2016, Victor Zverovich Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved. All rights reserved.
For the license information refer to format.h. For the license information refer to format.h.
*/ */
#ifndef FMT_OSTREAM_H_ #ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_ #define FMT_OSTREAM_H_
// commented out by spdlog #include "format.h"
// #include "format.h"
#include <ostream> #include <ostream>
namespace fmt namespace fmt
@@ -21,45 +20,42 @@ namespace internal
{ {
template <class Char> template <class Char>
class FormatBuf: public std::basic_streambuf<Char> class FormatBuf : public std::basic_streambuf<Char>
{ {
private: private:
typedef typename std::basic_streambuf<Char>::int_type int_type; typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type; typedef typename std::basic_streambuf<Char>::traits_type traits_type;
Buffer<Char> &buffer_; Buffer<Char> &buffer_;
Char *start_;
public: public:
FormatBuf(Buffer<Char> &buffer): buffer_(buffer), start_(&buffer[0]) FormatBuf(Buffer<Char> &buffer) : buffer_(buffer) {}
{
this->setp(start_, start_ + buffer_.capacity());
}
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())) if (!traits_type::eq_int_type(ch, traits_type::eof()))
{ buffer_.push_back(static_cast<Char>(ch));
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);
}
return 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;
} }
}; };
Yes &convert(std::ostream &); Yes &convert(std::ostream &);
struct DummyStream: std::ostream struct DummyStream : std::ostream
{ {
DummyStream(); // Suppress a bogus warning in MSVC. DummyStream(); // Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream. // Hide all operator<< overloads from std::ostream.
@@ -79,12 +75,12 @@ struct ConvertToIntImpl<T, true>
}; };
// Write the content of w to os. // 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 } // namespace internal
// Formats a value. // Formats a value.
template <typename Char, typename ArgFormatter, typename T> template <typename Char, typename ArgFormatter_, typename T>
void format_arg(BasicFormatter<Char, ArgFormatter> &f, void format_arg(BasicFormatter<Char, ArgFormatter_> &f,
const Char *&format_str, const T &value) const Char *&format_str, const T &value)
{ {
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer; internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
@@ -93,20 +89,20 @@ void format_arg(BasicFormatter<Char, ArgFormatter> &f,
std::basic_ostream<Char> output(&format_buf); std::basic_ostream<Char> output(&format_buf);
output << value; output << value;
BasicStringRef<Char> str(&buffer[0], format_buf.size()); BasicStringRef<Char> str(&buffer[0], buffer.size());
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg; typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
format_str = f.format(format_str, MakeArg(str)); format_str = f.format(format_str, MakeArg(str));
} }
/** /**
\rst \rst
Prints formatted data to the stream *os*. Prints formatted data to the stream *os*.
**Example**:: **Example**::
print(cerr, "Don't {}!", "panic"); print(cerr, "Don't {}!", "panic");
\endrst \endrst
*/ */
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args); FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(void, print, std::ostream &, CStringRef) FMT_VARIADIC(void, print, std::ostream &, CStringRef)
} // namespace fmt } // namespace fmt

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

@@ -1,11 +1,11 @@
/* /*
Formatting library for C++ Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved. All rights reserved.
For the license information refer to format.h. For the license information refer to format.h.
*/ */
#ifndef FMT_PRINTF_H_ #ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_ #define FMT_PRINTF_H_
@@ -52,7 +52,7 @@ struct IntChecker<true>
} }
}; };
class PrecisionHandler: public ArgVisitor<PrecisionHandler, int> class PrecisionHandler : public ArgVisitor<PrecisionHandler, int>
{ {
public: public:
void report_unhandled_arg() void report_unhandled_arg()
@@ -70,7 +70,7 @@ public:
}; };
// IsZeroInt::visit(arg) returns true iff arg is a zero integer. // IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt: public ArgVisitor<IsZeroInt, bool> class IsZeroInt : public ArgVisitor<IsZeroInt, bool>
{ {
public: public:
template <typename T> template <typename T>
@@ -80,22 +80,53 @@ 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> template <typename T, typename U>
struct is_same struct is_same
{ {
enum enum { value = 0 };
{
value = 0
};
}; };
template <typename T> template <typename T>
struct is_same<T, T> struct is_same<T, T>
{ {
enum enum { value = 1 };
{
value = 1
};
}; };
// An argument visitor that converts an integer argument to T for printf, // An argument visitor that converts an integer argument to T for printf,
@@ -103,7 +134,7 @@ struct is_same<T, T>
// corresponding signed or unsigned type depending on the type specifier: // corresponding signed or unsigned type depending on the type specifier:
// 'd' and 'i' - signed, other - unsigned) // 'd' and 'i' - signed, other - unsigned)
template <typename T = void> template <typename T = void>
class ArgConverter: public ArgVisitor<ArgConverter<T>, void> class ArgConverter : public ArgVisitor<ArgConverter<T>, void>
{ {
private: private:
internal::Arg &arg_; internal::Arg &arg_;
@@ -113,8 +144,7 @@ private:
public: public:
ArgConverter(internal::Arg &arg, wchar_t type) ArgConverter(internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type) : arg_(arg), type_(type) {}
{}
void visit_bool(bool value) void visit_bool(bool value)
{ {
@@ -122,10 +152,21 @@ public:
visit_any_int(value); visit_any_int(value);
} }
void visit_char(char value)
{
if (type_ != 's')
visit_any_int(value);
}
template <typename U> template <typename U>
void visit_any_int(U value) void visit_any_int(U value)
{ {
bool is_signed = type_ == 'd' || type_ == 'i'; bool is_signed = type_ == 'd' || type_ == 'i';
if (type_ == 's')
{
is_signed = std::numeric_limits<U>::is_signed;
}
using internal::Arg; using internal::Arg;
typedef typename internal::Conditional< typedef typename internal::Conditional<
is_same<T, void>::value, U, T>::type TargetType; is_same<T, void>::value, U, T>::type TargetType;
@@ -165,7 +206,7 @@ public:
}; };
// Converts an integer argument to char for printf. // Converts an integer argument to char for printf.
class CharConverter: public ArgVisitor<CharConverter, void> class CharConverter : public ArgVisitor<CharConverter, void>
{ {
private: private:
internal::Arg &arg_; internal::Arg &arg_;
@@ -173,8 +214,7 @@ private:
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
public: public:
explicit CharConverter(internal::Arg &arg): arg_(arg) explicit CharConverter(internal::Arg &arg) : arg_(arg) {}
{}
template <typename T> template <typename T>
void visit_any_int(T value) void visit_any_int(T value)
@@ -186,7 +226,7 @@ public:
// Checks if an argument is a valid printf width specifier and sets // Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative. // left alignment if it is negative.
class WidthHandler: public ArgVisitor<WidthHandler, unsigned> class WidthHandler : public ArgVisitor<WidthHandler, unsigned>
{ {
private: private:
FormatSpec &spec_; FormatSpec &spec_;
@@ -194,8 +234,7 @@ private:
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
public: public:
explicit WidthHandler(FormatSpec &spec): spec_(spec) explicit WidthHandler(FormatSpec &spec) : spec_(spec) {}
{}
void report_unhandled_arg() void report_unhandled_arg()
{ {
@@ -221,24 +260,25 @@ public:
} // namespace internal } // namespace internal
/** /**
\rst \rst
A ``printf`` argument formatter based on the `curiously recurring template A ``printf`` argument formatter based on the `curiously recurring template
pattern <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_. pattern <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_.
To use `~fmt::BasicPrintfArgFormatter` define a subclass that implements some To use `~fmt::BasicPrintfArgFormatter` define a subclass that implements some
or all of the visit methods with the same signatures as the methods in or all of the visit methods with the same signatures as the methods in
`~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`. `~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`.
Pass the subclass as the *Impl* template parameter. When a formatting Pass the subclass as the *Impl* template parameter. When a formatting
function processes an argument, it will dispatch to a visit method function processes an argument, it will dispatch to a visit method
specific to the argument type. For example, if the argument type is specific to the argument type. For example, if the argument type is
``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass ``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass
will be called. If the subclass doesn't contain a method with this signature, will be called. If the subclass doesn't contain a method with this signature,
then a corresponding method of `~fmt::BasicPrintfArgFormatter` or its then a corresponding method of `~fmt::BasicPrintfArgFormatter` or its
superclass will be called. superclass will be called.
\endrst \endrst
*/ */
template <typename Impl, typename Char> template <typename Impl, typename Char, typename Spec>
class BasicPrintfArgFormatter: public internal::ArgFormatterBase<Impl, Char> class BasicPrintfArgFormatter :
public internal::ArgFormatterBase<Impl, Char, Spec>
{ {
private: private:
void write_null_pointer() void write_null_pointer()
@@ -247,24 +287,23 @@ private:
this->write("(nil)"); this->write("(nil)");
} }
typedef internal::ArgFormatterBase<Impl, Char> Base; typedef internal::ArgFormatterBase<Impl, Char, Spec> Base;
public: public:
/** /**
\rst \rst
Constructs an argument formatter object. Constructs an argument formatter object.
*writer* is a reference to the output writer and *spec* contains format *writer* is a reference to the output writer and *spec* contains format
specifier information for standard argument types. specifier information for standard argument types.
\endrst \endrst
*/ */
BasicPrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s) BasicPrintfArgFormatter(BasicWriter<Char> &w, Spec &s)
: internal::ArgFormatterBase<Impl, Char>(w, s) : internal::ArgFormatterBase<Impl, Char, Spec>(w, s) {}
{}
/** Formats an argument of type ``bool``. */ /** Formats an argument of type ``bool``. */
void visit_bool(bool value) void visit_bool(bool value)
{ {
FormatSpec &fmt_spec = this->spec(); Spec &fmt_spec = this->spec();
if (fmt_spec.type_ != 's') if (fmt_spec.type_ != 's')
return this->visit_any_int(value); return this->visit_any_int(value);
fmt_spec.type_ = 0; fmt_spec.type_ = 0;
@@ -274,7 +313,7 @@ public:
/** Formats a character. */ /** Formats a character. */
void visit_char(int value) void visit_char(int value)
{ {
const FormatSpec &fmt_spec = this->spec(); const Spec &fmt_spec = this->spec();
BasicWriter<Char> &w = this->writer(); BasicWriter<Char> &w = this->writer();
if (fmt_spec.type_ && fmt_spec.type_ != 'c') if (fmt_spec.type_ && fmt_spec.type_ != 'c')
w.write_int(value, fmt_spec); w.write_int(value, fmt_spec);
@@ -325,7 +364,7 @@ public:
void visit_custom(internal::Arg::CustomValue c) void visit_custom(internal::Arg::CustomValue c)
{ {
BasicFormatter<Char> formatter(ArgList(), this->writer()); BasicFormatter<Char> formatter(ArgList(), this->writer());
const Char format_str[] = { '}', 0 }; const Char format_str[] = {'}', 0};
const Char *format = format_str; const Char *format = format_str;
c.format(&formatter, c.value, &format); c.format(&formatter, c.value, &format);
} }
@@ -333,19 +372,18 @@ public:
/** The default printf argument formatter. */ /** The default printf argument formatter. */
template <typename Char> template <typename Char>
class PrintfArgFormatter class PrintfArgFormatter :
: public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char> public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>
{ {
public: public:
/** Constructs an argument formatter object. */ /** Constructs an argument formatter object. */
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s) 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. */ /** This template formats data and writes the output to a writer. */
template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> > template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> >
class PrintfFormatter: private internal::FormatterBase class PrintfFormatter : private internal::FormatterBase
{ {
private: private:
BasicWriter<Char> &writer_; BasicWriter<Char> &writer_;
@@ -363,18 +401,17 @@ private:
public: public:
/** /**
\rst \rst
Constructs a ``PrintfFormatter`` object. References to the arguments and Constructs a ``PrintfFormatter`` object. References to the arguments and
the writer are stored in the formatter object so make sure they have the writer are stored in the formatter object so make sure they have
appropriate lifetimes. appropriate lifetimes.
\endrst \endrst
*/ */
explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w) explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w)
: FormatterBase(al), writer_(w) : FormatterBase(al), writer_(w) {}
{}
/** Formats stored arguments and writes the output to the writer. */ /** 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> template <typename Char, typename AF>
@@ -498,6 +535,10 @@ void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
++s; ++s;
spec.precision_ = internal::PrecisionHandler().visit(get_arg(s)); spec.precision_ = internal::PrecisionHandler().visit(get_arg(s));
} }
else
{
spec.precision_ = 0;
}
} }
using internal::Arg; using internal::Arg;
@@ -550,6 +591,13 @@ void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
if (!*s) if (!*s)
FMT_THROW(FormatError("invalid format string")); FMT_THROW(FormatError("invalid format string"));
spec.type_ = static_cast<char>(*s++); 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) if (arg.type <= Arg::LAST_INTEGER_TYPE)
{ {
// Normalize type. // Normalize type.
@@ -574,20 +622,26 @@ void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
write(writer_, start, s); write(writer_, start, s);
} }
template <typename Char> inline void printf(Writer &w, CStringRef format, ArgList args)
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> 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 \rst
Formats arguments and returns the result as a string. Formats arguments and returns the result as a string.
**Example**:: **Example**::
std::string message = fmt::sprintf("The answer is %d", 42); std::string message = fmt::sprintf("The answer is %d", 42);
\endrst \endrst
*/ */
inline std::string sprintf(CStringRef format, ArgList args) inline std::string sprintf(CStringRef format, ArgList args)
{ {
@@ -606,26 +660,26 @@ inline std::wstring sprintf(WCStringRef format, ArgList args)
FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef) FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef)
/** /**
\rst \rst
Prints formatted data to the file *f*. Prints formatted data to the file *f*.
**Example**:: **Example**::
fmt::fprintf(stderr, "Don't %s!", "panic"); fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst \endrst
*/ */
FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args); FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args);
FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef) FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
/** /**
\rst \rst
Prints formatted data to ``stdout``. Prints formatted data to ``stdout``.
**Example**:: **Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23); fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst \endrst
*/ */
inline int printf(CStringRef format, ArgList args) inline int printf(CStringRef format, ArgList args)
{ {
return fprintf(stdout, format, args); return fprintf(stdout, format, args);
@@ -633,14 +687,14 @@ inline int printf(CStringRef format, ArgList args)
FMT_VARIADIC(int, printf, CStringRef) FMT_VARIADIC(int, printf, CStringRef)
/** /**
\rst \rst
Prints formatted data to the stream *os*. Prints formatted data to the stream *os*.
**Example**:: **Example**::
fprintf(cerr, "Don't %s!", "panic"); fprintf(cerr, "Don't %s!", "panic");
\endrst \endrst
*/ */
inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args) inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args)
{ {
MemoryWriter w; MemoryWriter w;

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 #ifndef FMT_USE_WINDOWS_H
#define FMT_USE_WINDOWS_H 0 #define FMT_USE_WINDOWS_H 0
#endif #endif
#include <spdlog/fmt/bundled/format.h> #include "bundled/format.h"
#if defined(SPDLOG_FMT_PRINTF)
#include "bundled/printf.h"
#endif
#else //external fmtlib #else //external fmtlib
#include <fmt/format.h> #include <fmt/format.h>
#if defined(SPDLOG_FMT_PRINTF)
#include <fmt/printf.h>
#endif
#endif #endif

View File

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

View File

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

View File

@@ -12,8 +12,8 @@
// 2. Format the message using the formatter function // 2. Format the message using the formatter function
// 3. Pass the formatted message to its sinks to performa the actual logging // 3. Pass the formatted message to its sinks to performa the actual logging
#include <spdlog/sinks/base_sink.h> #include "sinks/base_sink.h"
#include <spdlog/common.h> #include "common.h"
#include <vector> #include <vector>
#include <memory> #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* fmt, const Args&... args);
template <typename... Args> void log(level::level_enum lvl, const char* msg); 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 Arg1, typename... Args> void trace(const char* fmt, const Arg1&, const Args&... args);
template <typename... Args> void debug(const char* fmt, const Args&... args); template <typename Arg1, typename... Args> void debug(const char* fmt, const Arg1&, const Args&... args);
template <typename... Args> void info(const char* fmt, const Args&... args); template <typename Arg1, typename... Args> void info(const char* fmt, const Arg1&, const Args&... args);
template <typename... Args> void warn(const char* fmt, const Args&... args); template <typename Arg1, typename... Args> void warn(const char* fmt, const Arg1&, const Args&... args);
template <typename... Args> void error(const char* fmt, const Args&... args); template <typename Arg1, typename... Args> void error(const char* fmt, const Arg1&, const Args&... args);
template <typename... Args> void critical(const char* fmt, 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 log(level::level_enum lvl, const T&);
template <typename T> void trace(const T&); template <typename T> void trace(const T&);
@@ -56,13 +68,9 @@ public:
void set_level(level::level_enum); void set_level(level::level_enum);
level::level_enum level() const; level::level_enum level() const;
const std::string& name() const; const std::string& name() const;
void set_pattern(const std::string&); void set_pattern(const std::string&, pattern_time_type = pattern_time_type::local);
void set_formatter(formatter_ptr); 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 // automatically call flush() if message level >= log_level
void flush_on(level::level_enum log_level); void flush_on(level::level_enum log_level);
@@ -70,9 +78,13 @@ public:
const std::vector<sink_ptr>& sinks() const; const std::vector<sink_ptr>& sinks() const;
// error handler
virtual void set_error_handler(log_err_handler);
virtual log_err_handler error_handler();
protected: protected:
virtual void _sink_it(details::log_msg&); 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); virtual void _set_formatter(formatter_ptr);
// default error handler: print the error to stderr with the max rate of 1 message/minute // default error handler: print the error to stderr with the max rate of 1 message/minute
@@ -81,6 +93,9 @@ protected:
// return true if the given message level should trigger a flush // return true if the given message level should trigger a flush
bool _should_flush_on(const details::log_msg&); 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; const std::string _name;
std::vector<sink_ptr> _sinks; std::vector<sink_ptr> _sinks;
formatter_ptr _formatter; formatter_ptr _formatter;
@@ -88,7 +103,8 @@ protected:
spdlog::level_t _flush_level; spdlog::level_t _flush_level;
log_err_handler _err_handler; log_err_handler _err_handler;
std::atomic<time_t> _last_err_time; 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__) #if defined(__ANDROID__)
#include <spdlog/sinks/sink.h> #include "sink.h"
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <android/log.h> #include <android/log.h>
#include <thread>
#include <chrono>
#if !defined(SPDLOG_ANDROID_RETRIES)
#define SPDLOG_ANDROID_RETRIES 2
#endif
namespace spdlog namespace spdlog
{ {
@@ -25,15 +31,23 @@ namespace sinks
class android_sink : public sink class android_sink : public sink
{ {
public: 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 void log(const details::log_msg& msg) override
{ {
const android_LogPriority priority = convert_to_android(msg.level); 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 // See system/core/liblog/logger_write.c for explanation of return value
const int ret = __android_log_write( int ret = __android_log_write(priority, _tag.c_str(), msg_output);
priority, _tag.c_str(), msg.formatted.c_str() 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) if (ret < 0)
{ {
throw spdlog_ex("__android_log_write() failed", ret); throw spdlog_ex("__android_log_write() failed", ret);
@@ -67,6 +81,7 @@ private:
} }
std::string _tag; 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) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#pragma once #pragma once
#include <spdlog/sinks/base_sink.h> #include "base_sink.h"
#include <spdlog/common.h> #include "../common.h"
#include "../details/os.h"
#include <string> #include <string>
#include <map> #include <map>
@@ -17,99 +18,115 @@ namespace sinks
{ {
/** /**
* @brief The ansi_color_sink is a decorator around another sink and prefixes * This sink prefixes the output with an ANSI escape sequence color code depending on the severity
* the output with an ANSI escape sequence color code depending on the severity
* of the message. * of the message.
* If no color terminal detected, omit the escape codes.
*/ */
class ansicolor_sink : public sink template <class Mutex>
class ansicolor_sink: public base_sink<Mutex>
{ {
public: public:
ansicolor_sink(sink_ptr wrapped_sink); ansicolor_sink(FILE* file): target_file_(file)
virtual ~ansicolor_sink(); {
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; void set_color(level::level_enum color_level, const std::string& color)
ansicolor_sink& operator=(const ansicolor_sink& other) = delete; {
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
virtual void log(const details::log_msg& msg) override; colors_[color_level] = color;
virtual void flush() override; }
void set_color(level::level_enum color_level, const std::string& color);
/// Formatting codes /// Formatting codes
const std::string reset = "\033[00m"; const std::string reset = "\033[00m";
const std::string bold = "\033[1m"; const std::string bold = "\033[1m";
const std::string dark = "\033[2m"; const std::string dark = "\033[2m";
const std::string underline = "\033[4m"; const std::string underline = "\033[4m";
const std::string blink = "\033[5m"; const std::string blink = "\033[5m";
const std::string reverse = "\033[7m"; const std::string reverse = "\033[7m";
const std::string concealed = "\033[8m"; const std::string concealed = "\033[8m";
// Foreground colors // Foreground colors
const std::string grey = "\033[30m"; const std::string grey = "\033[30m";
const std::string red = "\033[31m"; const std::string red = "\033[31m";
const std::string green = "\033[32m"; const std::string green = "\033[32m";
const std::string yellow = "\033[33m"; const std::string yellow = "\033[33m";
const std::string blue = "\033[34m"; const std::string blue = "\033[34m";
const std::string magenta = "\033[35m"; const std::string magenta = "\033[35m";
const std::string cyan = "\033[36m"; const std::string cyan = "\033[36m";
const std::string white = "\033[37m"; const std::string white = "\033[37m";
/// Background colors /// Background colors
const std::string on_grey = "\033[40m"; const std::string on_grey = "\033[40m";
const std::string on_red = "\033[41m"; const std::string on_red = "\033[41m";
const std::string on_green = "\033[42m"; const std::string on_green = "\033[42m";
const std::string on_yellow = "\033[43m"; const std::string on_yellow = "\033[43m";
const std::string on_blue = "\033[44m"; const std::string on_blue = "\033[44m";
const std::string on_magenta = "\033[45m"; const std::string on_magenta = "\033[45m";
const std::string on_cyan = "\033[46m"; const std::string on_cyan = "\033[46m";
const std::string on_white = "\033[47m"; const std::string on_white = "\033[47m";
protected: 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_; 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 public:
const std::string& prefix = colors_[msg.level]; ansicolor_stdout_sink(): ansicolor_sink<Mutex>(stdout)
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);
}
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 color_level, const std::string& color) typedef ansicolor_stdout_sink<std::mutex> ansicolor_stdout_sink_mt;
{ typedef ansicolor_stdout_sink<details::null_mutex> ansicolor_stdout_sink_st;
colors_[color_level] = color;
}
inline ansicolor_sink::~ansicolor_sink() typedef ansicolor_stderr_sink<std::mutex> ansicolor_stderr_sink_mt;
{ typedef ansicolor_stderr_sink<details::null_mutex> ansicolor_stderr_sink_st;
flush();
}
} // namespace sinks } // namespace sinks
} // namespace spdlog } // namespace spdlog

View File

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

View File

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

View File

@@ -5,10 +5,10 @@
#pragma once #pragma once
#include <spdlog/sinks/base_sink.h> #include "base_sink.h"
#include <spdlog/details/null_mutex.h> #include "../details/null_mutex.h"
#include <spdlog/details/file_helper.h> #include "../details/file_helper.h"
#include <spdlog/fmt/fmt.h> #include "../fmt/fmt.h"
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
@@ -26,17 +26,14 @@ namespace sinks
* Trivial file sink with single file as target * Trivial file sink with single file as target
*/ */
template<class Mutex> template<class Mutex>
class simple_file_sink : public base_sink < Mutex > class simple_file_sink SPDLOG_FINAL : public base_sink < Mutex >
{ {
public: public:
explicit simple_file_sink(const filename_t &filename, bool truncate = false):_force_flush(false) explicit simple_file_sink(const filename_t &filename, bool truncate = false):_force_flush(false)
{ {
_file_helper.open(filename, truncate); _file_helper.open(filename, truncate);
} }
void flush() override
{
_file_helper.flush();
}
void set_force_flush(bool force_flush) void set_force_flush(bool force_flush)
{ {
_force_flush = force_flush; _force_flush = force_flush;
@@ -49,6 +46,10 @@ protected:
if(_force_flush) if(_force_flush)
_file_helper.flush(); _file_helper.flush();
} }
void _flush() override
{
_file_helper.flush();
}
private: private:
details::file_helper _file_helper; details::file_helper _file_helper;
bool _force_flush; bool _force_flush;
@@ -61,25 +62,37 @@ typedef simple_file_sink<details::null_mutex> simple_file_sink_st;
* Rotating file sink based on size * Rotating file sink based on size
*/ */
template<class Mutex> template<class Mutex>
class rotating_file_sink : public base_sink < Mutex > class rotating_file_sink SPDLOG_FINAL : public base_sink < Mutex >
{ {
public: public:
rotating_file_sink(const filename_t &base_filename, const filename_t &extension, rotating_file_sink(const filename_t &base_filename,
std::size_t max_size, std::size_t max_files ) : std::size_t max_size, std::size_t max_files) :
_base_filename(base_filename), _base_filename(base_filename),
_extension(extension),
_max_size(max_size), _max_size(max_size),
_max_files(max_files), _max_files(max_files),
_current_size(0), _current_size(0),
_file_helper() _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 _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: protected:
@@ -94,31 +107,26 @@ protected:
_file_helper.write(msg); _file_helper.write(msg);
} }
private: void _flush() override
static filename_t calc_filename(const filename_t& filename, std::size_t index, const filename_t& extension)
{ {
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w; _file_helper.flush();
if (index)
w.write(SPDLOG_FILENAME_T("{}.{}.{}"), filename, index, extension);
else
w.write(SPDLOG_FILENAME_T("{}.{}"), filename, extension);
return w.str();
} }
private:
// Rotate files: // Rotate files:
// log.txt -> log.1.txt // log.txt -> log.1.txt
// log.1.txt -> log2.txt // log.1.txt -> log.2.txt
// log.2.txt -> log3.txt // log.2.txt -> log.3.txt
// log.3.txt -> delete // log.3.txt -> delete
void _rotate() void _rotate()
{ {
using details::os::filename_to_str; using details::os::filename_to_str;
_file_helper.close(); _file_helper.close();
for (auto i = _max_files; i > 0; --i) for (auto i = _max_files; i > 0; --i)
{ {
filename_t src = calc_filename(_base_filename, i - 1, _extension); filename_t src = calc_filename(_base_filename, i - 1);
filename_t target = calc_filename(_base_filename, i, _extension); filename_t target = calc_filename(_base_filename, i);
if (details::file_helper::file_exists(target)) if (details::file_helper::file_exists(target))
{ {
@@ -135,7 +143,6 @@ private:
_file_helper.reopen(true); _file_helper.reopen(true);
} }
filename_t _base_filename; filename_t _base_filename;
filename_t _extension;
std::size_t _max_size; std::size_t _max_size;
std::size_t _max_files; std::size_t _max_files;
std::size_t _current_size; std::size_t _current_size;
@@ -150,27 +157,31 @@ typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
*/ */
struct default_daily_file_name_calculator struct default_daily_file_name_calculator
{ {
// Create filename for the form basename.YYYY-MM-DD_hh-mm.extension // Create filename for the form filename.YYYY-MM-DD_hh-mm.ext
static filename_t calc_filename(const filename_t& basename, const filename_t& extension) static filename_t calc_filename(const filename_t& filename)
{ {
std::tm tm = spdlog::details::os::localtime(); 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; 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(); 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 struct dateonly_daily_file_name_calculator
{ {
// Create filename for the form basename.YYYY-MM-DD.extension // Create filename for the form basename.YYYY-MM-DD
static filename_t calc_filename(const filename_t& basename, const filename_t& extension) static filename_t calc_filename(const filename_t& filename)
{ {
std::tm tm = spdlog::details::os::localtime(); 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; 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(); return w.str();
} }
}; };
@@ -179,41 +190,40 @@ struct dateonly_daily_file_name_calculator
* Rotating file sink based on date. rotates at midnight * Rotating file sink based on date. rotates at midnight
*/ */
template<class Mutex, class FileNameCalc = default_daily_file_name_calculator> 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: public:
//create daily file sink which rotates on given time //create daily file sink which rotates on given time
daily_file_sink( daily_file_sink(
const filename_t& base_filename, const filename_t& base_filename,
const filename_t& extension,
int rotation_hour, int rotation_hour,
int rotation_minute) : _base_filename(base_filename), int rotation_minute) : _base_filename(base_filename),
_extension(extension),
_rotation_h(rotation_hour), _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) if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor"); throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
_rotation_tp = _next_rotation_tp(); _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: protected:
void _sink_it(const details::log_msg& msg) override void _sink_it(const details::log_msg& msg) override
{ {
if (std::chrono::system_clock::now() >= _rotation_tp) 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(); _rotation_tp = _next_rotation_tp();
} }
_file_helper.write(msg); _file_helper.write(msg);
} }
void _flush() override
{
_file_helper.flush();
}
private: private:
std::chrono::system_clock::time_point _next_rotation_tp() std::chrono::system_clock::time_point _next_rotation_tp()
{ {
@@ -231,7 +241,6 @@ private:
} }
filename_t _base_filename; filename_t _base_filename;
filename_t _extension;
int _rotation_h; int _rotation_h;
int _rotation_m; int _rotation_m;
std::chrono::system_clock::time_point _rotation_tp; std::chrono::system_clock::time_point _rotation_tp;

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,7 @@
#pragma once #pragma once
#include <spdlog/details/log_msg.h> #include "../details/log_msg.h"
namespace spdlog namespace spdlog
{ {

View File

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

View File

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

View File

@@ -5,9 +5,9 @@
#pragma once #pragma once
#include <spdlog/sinks/base_sink.h> #include "base_sink.h"
#include <spdlog/details/null_mutex.h> #include "../details/null_mutex.h"
#include <spdlog/common.h> #include "../common.h"
#include <mutex> #include <mutex>
#include <string> #include <string>
@@ -44,12 +44,13 @@ public:
virtual ~wincolor_sink() virtual ~wincolor_sink()
{ {
flush(); this->flush();
} }
wincolor_sink(const wincolor_sink& other) = delete; wincolor_sink(const wincolor_sink& other) = delete;
wincolor_sink& operator=(const wincolor_sink& other) = delete; wincolor_sink& operator=(const wincolor_sink& other) = delete;
protected:
virtual void _sink_it(const details::log_msg& msg) override virtual void _sink_it(const details::log_msg& msg) override
{ {
auto color = colors_[msg.level]; auto color = colors_[msg.level];
@@ -58,7 +59,7 @@ public:
SetConsoleTextAttribute(out_handle_, orig_attribs); //reset to orig colors SetConsoleTextAttribute(out_handle_, orig_attribs); //reset to orig colors
} }
virtual void flush() override virtual void _flush() override
{ {
// windows console always flushed? // windows console always flushed?
} }
@@ -79,7 +80,11 @@ private:
{ {
CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info; CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info); GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info);
SetConsoleTextAttribute(out_handle_, attribs); 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 return orig_buffer_info.wAttributes; //return orig attribs
} }
}; };

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

@@ -7,11 +7,11 @@
#pragma once #pragma once
#define SPDLOG_VERSION "0.12.0" #define SPDLOG_VERSION "0.16.2"
#include <spdlog/tweakme.h> #include "tweakme.h"
#include <spdlog/common.h> #include "common.h"
#include <spdlog/logger.h> #include "logger.h"
#include <memory> #include <memory>
#include <functional> #include <functional>
@@ -69,7 +69,7 @@ void set_sync_mode();
// //
// Create and register multi/single threaded basic file logger. // 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_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); std::shared_ptr<logger> basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate = false);
@@ -106,14 +106,14 @@ std::shared_ptr<logger> stderr_color_st(const std::string& logger_name);
// Create and register a syslog logger // Create and register a syslog logger
// //
#ifdef SPDLOG_ENABLE_SYSLOG #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 #endif
#if defined(__ANDROID__) #if defined(__ANDROID__)
std::shared_ptr<logger> android_logger(const std::string& logger_name, const std::string& tag = "spdlog"); std::shared_ptr<logger> android_logger(const std::string& logger_name, const std::string& tag = "spdlog");
#endif #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); std::shared_ptr<logger> create(const std::string& logger_name, const sink_ptr& sink);
// Create and register a logger with multiple sinks // Create and register a logger with multiple sinks
@@ -124,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 // Create and register a logger with templated sink type
// Example: // 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> template <typename Sink, typename... Args>
std::shared_ptr<spdlog::logger> create(const std::string& logger_name, 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 // Register the given logger with the given name
void register_logger(std::shared_ptr<logger> logger); void register_logger(std::shared_ptr<logger> logger);
@@ -147,7 +154,7 @@ void drop_all();
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// //
// Trace & Debug can be switched on/off at compile time for zero cost debug statements. // Trace & Debug can be switched on/off at compile time for zero cost debug 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. // SPDLOG_TRACE(..) will also print current file and line.
// //
// Example: // Example:
@@ -160,7 +167,11 @@ void drop_all();
#ifdef SPDLOG_TRACE_ON #ifdef SPDLOG_TRACE_ON
#define SPDLOG_STR_H(x) #x #define SPDLOG_STR_H(x) #x
#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x) #define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x)
#define SPDLOG_TRACE(logger, ...) logger->trace("[" __FILE__ " line #" SPDLOG_STR_HELPER(__LINE__) "] " __VA_ARGS__) #ifdef _MSC_VER
#define SPDLOG_TRACE(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 #else
#define SPDLOG_TRACE(logger, ...) #define SPDLOG_TRACE(logger, ...)
#endif #endif
@@ -171,8 +182,7 @@ void drop_all();
#define SPDLOG_DEBUG(logger, ...) #define SPDLOG_DEBUG(logger, ...)
#endif #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. // 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. // 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 // 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). // 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. // 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. // Uncomment if logger name logging is not needed.
// This will prevent spdlog from copying the logger name on each log call. // 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()). // 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.. // Note that upon creating a logger the registry is modified by spdlog..
// //
// #define SPDLOG_NO_REGISTRY_MUTEX // #define SPDLOG_NO_REGISTRY_MUTEX
@@ -73,12 +83,14 @@
// #define SPDLOG_NO_ATOMIC_LEVELS // #define SPDLOG_NO_ATOMIC_LEVELS
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable usage of wchar_t for file names on Windows. // Uncomment to enable usage of wchar_t for file names on Windows.
// //
// #define SPDLOG_WCHAR_FILENAMES // #define SPDLOG_WCHAR_FILENAMES
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment to override default eol ("\n" or "\r\n" under Linux/Windows) // 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) // Uncomment to enable syslog (disabled by default)
// //
@@ -101,8 +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 // Uncomment to prevent child processes from inheriting log file descriptors
// //
// #define SPDLOG_PREVENT_CHILD_FD // #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) add_library(catch INTERFACE)
target_include_directories(catch INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) 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}) add_executable(catch_tests ${catch_tests})
target_link_libraries(catch_tests spdlog ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(catch_tests spdlog ${CMAKE_THREAD_LIBS_INIT})

View File

@@ -1,5 +1,11 @@
CXX ?= g++ 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 LDPFALGS = -pthread
CPP_FILES := $(wildcard *.cpp) CPP_FILES := $(wildcard *.cpp)

View File

@@ -6,6 +6,19 @@
#include<iostream> #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() override
{}
};
TEST_CASE("default_error_handler", "[errors]]") TEST_CASE("default_error_handler", "[errors]]")
{ {
prepare_logdir(); 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); auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
logger->set_pattern("%v"); logger->set_pattern("%v");
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {} {}", 1); logger->info("Test message {} {}", 1);
logger->info("Test message {}", 2); logger->info("Test message {}", 2);
#else
logger->info("Test message %d %d", 1);
logger->info("Test message %d", 2);
#endif
logger->flush(); logger->flush();
REQUIRE(file_contents(filename) == std::string("Test message 2\n")); REQUIRE(file_contents(filename) == std::string("Test message 2\n"));
@@ -22,7 +40,10 @@ TEST_CASE("default_error_handler", "[errors]]")
} }
struct custom_ex {};
struct custom_ex
{};
TEST_CASE("custom_error_handler", "[errors]]") TEST_CASE("custom_error_handler", "[errors]]")
{ {
prepare_logdir(); prepare_logdir();
@@ -34,11 +55,26 @@ TEST_CASE("custom_error_handler", "[errors]]")
throw custom_ex(); throw custom_ex();
}); });
logger->info("Good message #1"); logger->info("Good message #1");
#if !defined(SPDLOG_FMT_PRINTF)
REQUIRE_THROWS_AS(logger->info("Bad format msg {} {}", "xxx"), custom_ex); 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"); logger->info("Good message #2");
REQUIRE(count_lines(filename) == 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]]") TEST_CASE("async_error_handler", "[errors]]")
{ {
prepare_logdir(); prepare_logdir();
@@ -54,7 +90,11 @@ TEST_CASE("async_error_handler", "[errors]]")
ofs << err_msg; ofs << err_msg;
}); });
logger->info("Good message #1"); logger->info("Good message #1");
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Bad format msg {} {}", "xxx"); logger->info("Bad format msg {} {}", "xxx");
#else
logger->info("Bad format msg %s %s", "xxx");
#endif
logger->info("Good message #2"); logger->info("Good message #2");
spdlog::drop("logger"); //force logger to drain the queue and shutdown spdlog::drop("logger"); //force logger to drain the queue and shutdown
spdlog::set_sync_mode(); spdlog::set_sync_mode();
@@ -62,3 +102,25 @@ TEST_CASE("async_error_handler", "[errors]]")
REQUIRE(count_lines(filename) == 2); REQUIRE(count_lines(filename) == 2);
REQUIRE(file_contents("logs/custom_err.txt") == err_msg); 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

@@ -75,4 +75,44 @@ TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
static void test_split_ext(const char* fname, const char* expect_base, const char* expect_ext)
{
spdlog::filename_t filename(fname);
spdlog::filename_t expected_base(expect_base);
spdlog::filename_t expected_ext(expect_ext);
#ifdef _WIN32 // replace folder sep
std::replace(filename.begin(), filename.end(), '/', '\\');
std::replace(expected_base.begin(), expected_base.end(), '/', '\\');
#endif
spdlog::filename_t basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion(filename);
REQUIRE(basename == expected_base);
REQUIRE(ext == expected_ext);
}
TEST_CASE("file_helper_split_by_extenstion", "[file_helper::split_by_extenstion()]]")
{
test_split_ext("mylog.txt", "mylog", ".txt");
test_split_ext(".mylog.txt", ".mylog", ".txt");
test_split_ext(".mylog", ".mylog", "");
test_split_ext("/aaa/bb.d/mylog", "/aaa/bb.d/mylog", "");
test_split_ext("/aaa/bb.d/mylog.txt", "/aaa/bb.d/mylog", ".txt");
test_split_ext("aaa/bbb/ccc/mylog.txt", "aaa/bbb/ccc/mylog", ".txt");
test_split_ext("aaa/bbb/ccc/mylog.", "aaa/bbb/ccc/mylog.", "");
test_split_ext("aaa/bbb/ccc/.mylog.txt", "aaa/bbb/ccc/.mylog", ".txt");
test_split_ext("/aaa/bbb/ccc/mylog.txt", "/aaa/bbb/ccc/mylog", ".txt");
test_split_ext("/aaa/bbb/ccc/.mylog", "/aaa/bbb/ccc/.mylog", "");
test_split_ext("../mylog.txt", "../mylog", ".txt");
test_split_ext(".././mylog.txt", ".././mylog", ".txt");
test_split_ext(".././mylog.txt/xxx", ".././mylog.txt/xxx", "");
test_split_ext("/mylog.txt", "/mylog", ".txt");
test_split_ext("//mylog.txt", "//mylog", ".txt");
test_split_ext("", "", "");
test_split_ext(".", ".", "");
test_split_ext("..txt", ".", ".txt");
}

View File

@@ -7,14 +7,18 @@
TEST_CASE("simple_file_logger", "[simple_logger]]") TEST_CASE("simple_file_logger", "[simple_logger]]")
{ {
prepare_logdir(); 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); auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
logger->set_pattern("%v"); logger->set_pattern("%v");
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", 1); logger->info("Test message {}", 1);
logger->info("Test message {}", 2); logger->info("Test message {}", 2);
#else
logger->info("Test message %d", 1);
logger->info("Test message %d", 2);
#endif
logger->flush(); logger->flush();
REQUIRE(file_contents(filename) == std::string("Test message 1\nTest message 2\n")); REQUIRE(file_contents(filename) == std::string("Test message 1\nTest message 2\n"));
REQUIRE(count_lines(filename) == 2); REQUIRE(count_lines(filename) == 2);
@@ -24,7 +28,7 @@ TEST_CASE("simple_file_logger", "[simple_logger]]")
TEST_CASE("flush_on", "[flush_on]]") TEST_CASE("flush_on", "[flush_on]]")
{ {
prepare_logdir(); 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); auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
logger->set_pattern("%v"); logger->set_pattern("%v");
@@ -33,8 +37,13 @@ TEST_CASE("flush_on", "[flush_on]]")
logger->trace("Should not be flushed"); logger->trace("Should not be flushed");
REQUIRE(count_lines(filename) == 0); REQUIRE(count_lines(filename) == 0);
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", 1); logger->info("Test message {}", 1);
logger->info("Test message {}", 2); logger->info("Test message {}", 2);
#else
logger->info("Test message %d", 1);
logger->info("Test message %d", 2);
#endif
logger->flush(); logger->flush();
REQUIRE(file_contents(filename) == std::string("Should not be flushed\nTest message 1\nTest message 2\n")); REQUIRE(file_contents(filename) == std::string("Should not be flushed\nTest message 1\nTest message 2\n"));
REQUIRE(count_lines(filename) == 3); REQUIRE(count_lines(filename) == 3);
@@ -47,10 +56,16 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0); auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0);
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; ++i)
{
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", i); logger->info("Test message {}", i);
#else
logger->info("Test message %d", i);
#endif
}
logger->flush(); logger->flush();
auto filename = basename + ".txt"; auto filename = basename;
REQUIRE(count_lines(filename) == 10); REQUIRE(count_lines(filename) == 10);
} }
@@ -64,14 +79,20 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
logger->info("Test message {}", i); logger->info("Test message {}", i);
logger->flush(); logger->flush();
auto filename = basename + ".txt"; auto filename = basename;
REQUIRE(count_lines(filename) == 10); REQUIRE(count_lines(filename) == 10);
for (int i = 0; i < 1000; i++) for (int i = 0; i < 1000; i++)
{
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", i); logger->info("Test message {}", i);
#else
logger->info("Test message %d", i);
#endif
}
logger->flush(); logger->flush();
REQUIRE(get_filesize(filename) <= 1024); REQUIRE(get_filesize(filename) <= 1024);
auto filename1 = basename + ".1.txt"; auto filename1 = basename + ".1";
REQUIRE(get_filesize(filename1) <= 1024); REQUIRE(get_filesize(filename1) <= 1024);
} }
@@ -83,12 +104,18 @@ TEST_CASE("daily_logger", "[daily_logger]]")
std::string basename = "logs/daily_log"; std::string basename = "logs/daily_log";
std::tm tm = spdlog::details::os::localtime(); std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w; 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); auto logger = spdlog::daily_logger_mt("logger", basename, 0, 0);
logger->flush_on(spdlog::level::info); logger->flush_on(spdlog::level::info);
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; ++i)
{
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", i); logger->info("Test message {}", i);
#else
logger->info("Test message %d", i);
#endif
}
auto filename = w.str(); auto filename = w.str();
REQUIRE(count_lines(filename) == 10); REQUIRE(count_lines(filename) == 10);
@@ -106,11 +133,17 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger_dateonly]]")
std::string basename = "logs/daily_dateonly"; std::string basename = "logs/daily_dateonly";
std::tm tm = spdlog::details::os::localtime(); std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w; 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) for (int i = 0; i < 10; ++i)
{
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", i); logger->info("Test message {}", i);
#else
logger->info("Test message %d", i);
#endif
}
logger->flush(); logger->flush();
auto filename = w.str(); auto filename = w.str();
REQUIRE(count_lines(filename) == 10); REQUIRE(count_lines(filename) == 10);
@@ -118,11 +151,11 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger_dateonly]]")
struct custom_daily_file_name_calculator 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(); std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w; 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(); return w.str();
} }
}; };
@@ -138,14 +171,78 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]")
std::string basename = "logs/daily_dateonly"; std::string basename = "logs/daily_dateonly";
std::tm tm = spdlog::details::os::localtime(); std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w; 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) for (int i = 0; i < 10; ++i)
{
#if !defined(SPDLOG_FMT_PRINTF)
logger->info("Test message {}", i); logger->info("Test message {}", i);
#else
logger->info("Test message %d", i);
#endif
}
logger->flush(); logger->flush();
auto filename = w.str(); auto filename = w.str();
REQUIRE(count_lines(filename) == 10); 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 <ostream>
#include <chrono> #include <chrono>
#include <exception> #include <exception>
#include "catch.hpp" #include "catch.hpp"
#include "utils.h" #include "utils.h"
#define SPDLOG_TRACE_ON
#define SPDLOG_DEBUG_ON
#include "../include/spdlog/spdlog.h" #include "../include/spdlog/spdlog.h"
#include "../include/spdlog/sinks/null_sink.h" #include "../include/spdlog/sinks/null_sink.h"
#include "../include/spdlog/sinks/ostream_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="format.cpp" />
<ClCompile Include="main.cpp" /> <ClCompile Include="main.cpp" />
<ClCompile Include="registry.cpp" /> <ClCompile Include="registry.cpp" />
<ClCompile Include="test_macros.cpp" />
<ClCompile Include="utils.cpp" /> <ClCompile Include="utils.cpp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

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

View File

@@ -1,14 +1,17 @@
#include "includes.h" #include "includes.h"
void prepare_logdir() void prepare_logdir()
{ {
spdlog::drop_all(); spdlog::drop_all();
#ifdef _WIN32 #ifdef _WIN32
auto rv = system("del /F /Q logs\\*"); system("if not exist logs mkdir logs");
system("del /F /Q logs\\*");
#else #else
auto rv = system("rm -f logs/*"); auto rv = system("mkdir -p logs");
#endif rv = system("rm -f logs/*");
(void)rv; (void)rv;
#endif
} }
@@ -43,3 +46,11 @@ std::size_t get_filesize(const std::string& filename)
return static_cast<std::size_t>(ifs.tellg()); 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); std::size_t get_filesize(const std::string& filename);
bool ends_with(std::string const & value, std::string const & ending);