Compare commits

...

283 Commits

Author SHA1 Message Date
gabime
560df2878a updated version number to 0.17.0 2018-05-21 20:38:13 +03:00
gabime
dc328de72a added test_async to cmake and vcxproj 2018-05-21 16:40:31 +03:00
gabime
6704375be6 updated example visula studio solution 2018-05-21 15:11:28 +03:00
gabime
7a85acc31f removed logger_name field from async_msg 2018-05-20 19:35:01 +03:00
gabime
7cbf832cca comments 2018-05-20 18:30:42 +03:00
gabime
58b1c2f78d Merge branch 'remove-lockfree' of https://github.com/gabime/spdlog into remove-lockfree 2018-05-20 18:19:36 +03:00
gabime
b94b9a8341 format 2018-05-20 18:17:34 +03:00
gabime
6a93f98a1f async test 2018-05-20 18:17:34 +03:00
gabime
b70020d7c3 upadte stats 2018-05-20 18:17:34 +03:00
gabime
ade22934e3 updated bench to show elapsed too 2018-05-20 18:17:34 +03:00
gabime
ee2fd265fd added async tests 2018-05-20 18:17:34 +03:00
gabime
5b84f30b3a fixed flush interval in async helper 2018-05-20 18:15:33 +03:00
gabime
5c38e15dd2 replaced the lockfree queue with bounded, locked queue 2018-05-20 18:10:29 +03:00
gabime
0e86c4eed0 removed debug printf 2018-05-20 18:07:38 +03:00
gabime
e69aafc737 fixed flush interval in async helper 2018-05-20 18:05:37 +03:00
gabime
3dbba6895d replaced the lockfree queue with bounded, locked queue 2018-05-20 18:05:37 +03:00
gabime
8bd16feb36 format 2018-05-20 18:01:47 +03:00
gabime
8e429ae5d6 async test 2018-05-20 18:01:27 +03:00
gabime
e2bf00ada4 upadte stats 2018-05-20 17:50:12 +03:00
gabime
c3dd37c8c1 updated bench to show elapsed too 2018-05-20 17:41:22 +03:00
gabime
1838ad9b3d added async tests 2018-05-20 13:22:44 +03:00
gabime
7cc884fe34 async_log_helper minor fixes 2018-05-20 13:22:34 +03:00
gabime
61ab1af906 merge 2018-05-19 16:14:54 +03:00
gabime
911a2986ce fixed flush interval in async helper 2018-05-19 16:13:35 +03:00
gabime
8321449c44 replaced the lockfree queue with bounded, locked queue 2018-05-19 16:12:25 +03:00
gabime
314afa7dbb removed debug printf 2018-05-19 16:12:22 +03:00
gabime
f57fc1b2fa fixed flush interval in async helper 2018-05-19 16:06:57 +03:00
gabime
a1a71804f4 replaced the lockfree queue with bounded, locked queue 2018-05-19 14:41:53 +03:00
Gabi Melman
b1a58cd342 Dont wait for the queue to drain in async flush 2018-05-17 20:38:43 +03:00
Gabi Melman
efb1af9a73 Update async_logger_impl.h 2018-05-17 20:36:10 +03:00
Gabi Melman
71e93a4f2d Merge pull request #688 from Puasonych/master
Update step_logger
2018-04-27 21:55:44 +03:00
Puasonych
8f3c4218ed Merge remote-tracking branch 'gabime/master' 2018-04-20 16:20:17 +03:00
gabime
217ad75ebd Fixed deault error handler not to use sink but write directly to stderr 2018-04-20 13:02:21 +03:00
Gabi Melman
ce41991c51 Remove unneeded include 2018-04-20 12:59:51 +03:00
Puasonych
1136bd7ce2 Update step_logger
Fixed code duplication
2018-04-18 09:49:41 +03:00
Gabi Melman
805ab9854f Merge pull request #683 from Puasonych/master
Let's add a new type of logger 'step_logger' :)
2018-04-16 15:07:29 +03:00
Gabi Melman
d921a9649c Merge pull request #687 from jhasse/patch-1
Silence warning "value stored to rv is never read"
2018-04-16 14:38:07 +03:00
Puasonych
093edc2dc2 Update step_logger
Test release of a logger step_logger
2018-04-16 12:56:45 +03:00
Jan Niklas Hasse
1b2f6815bf Silence warning "value stored to rv is never read" 2018-04-16 10:58:50 +02:00
Puasonych
f17eb1bb56 Merge remote-tracking branch 'gabime/master' 2018-04-16 08:27:01 +03:00
Gabi Melman
5e08950ed2 Created contrib/sinks directory 2018-04-13 12:45:33 +03:00
Gabi Melman
bce3b75c53 Created contrib directory 2018-04-13 12:44:43 +03:00
Erik
be685337b1 Update file_sinks.h
Removed possible throw of an exception from the destructor
2018-04-12 10:01:02 +05:00
Erik
06580c8333 Update file_sinks.h 2018-04-11 22:15:50 +05:00
Puasonych
6f12242a55 Update .gitignore 2018-04-11 11:41:48 +03:00
Puasonych
11c99892d7 Add new logger: step_logger 2018-04-11 11:40:34 +03:00
Puasonych
31ce7ef3a5 Update .gitignore 2018-04-10 16:57:29 +03:00
gabime
3fdc7996db code formatting 2018-04-09 15:14:13 +03:00
gabime
e9bb008f15 Fixed example 2018-04-09 15:12:51 +03:00
gabime
d352aa0990 Fixed example 2018-04-09 15:11:42 +03:00
gabime
cfb450c059 Fixed eol write in pattern_formatter_impl 2018-04-09 14:14:52 +03:00
Gabi Melman
b416685d6f Fix gcc warning on stat (32 bits) 2018-04-09 02:06:33 +03:00
gabime
64c2fe180b Fixed bug in wrapping colors around level name in default pattern 2018-04-08 18:27:18 +03:00
gabime
309327187a Fixed example makefile for clang 2018-04-06 04:36:22 +03:00
gabime
1dea46e1ab code formatting 2018-04-06 04:06:02 +03:00
Gabi Melman
4abe403544 Update README.md 2018-04-06 04:05:23 +03:00
Gabi Melman
21d437fbf5 Update README.md 2018-04-06 04:04:50 +03:00
Gabi Melman
84c7f927c2 Merge pull request #679 from gabime/color-formatting
Color formatting
2018-04-06 04:00:17 +03:00
gabime
644c81b9fb Added color ranges to formatter tests 2018-04-06 03:42:24 +03:00
gabime
3452892f76 minor renaming 2018-04-06 03:22:27 +03:00
gabime
2f7fdf2663 wincolor color formatting support 2018-04-06 03:06:41 +03:00
gabime
d040ab93ea wincolor color formatting support 2018-04-06 03:04:18 +03:00
gabime
c8610d9a86 support for color formatting 2018-04-06 02:24:07 +03:00
gabime
93d41b2c0e fixed gcc warning about struct stat 2018-03-22 20:35:49 +02:00
gabime
c03ae5fafd Merge branch 'master' of https://github.com/gabime/spdlog 2018-03-22 20:08:33 +02:00
Gabi Melman
b3988d6e1f Merge pull request #665 from kasru/more_bench
More benchmarks
2018-03-21 20:58:11 +02:00
Alexander Kiselev
ad2c7b3959 Add README 2018-03-21 17:15:33 +03:00
Alexander Kiselev
5e1e897d10 Remove p7-bench binary 2018-03-21 16:51:43 +03:00
Alexander Kiselev
37f209079e Update spdlog-bench 2018-03-21 16:50:45 +03:00
Alexander Kiselev
0f66c63f72 Update easyloggingpp, Add plog 2018-03-21 16:46:52 +03:00
Gabi Melman
abde900cd7 Merge pull request #663 from kasru/more_bench
More bench
2018-03-19 21:27:03 +02:00
Alexander Kiselev
fd1c4d7877 Fix: unknown conversion specifier 'Y' 2018-03-19 20:03:47 +03:00
Alexander Kiselev
70ad1aa409 Changes: boost, easylogging, g2log, glog, spdlog. 2018-03-19 19:22:02 +03:00
Alexander Kiselev
c83dd5d60e Added: g3log, log4cplus, log4cpp, p7. Changes: boost, easylogging, g2log, glog, spdlog. 2018-03-19 19:17:26 +03:00
gabime
18c99682a8 fixed clang warning about uninitialized values 2018-03-17 14:08:10 +02:00
gabime
200815892f Fix clang-tidy warnings about missing braces around if and for statements 2018-03-17 13:42:09 +02:00
gabime
7eb6ca6337 formatting 2018-03-17 12:49:45 +02:00
gabime
5a0f90bc82 clang format namespace fix 2018-03-17 12:49:31 +02:00
gabime
2a86cdb203 Merge branch 'master' of https://github.com/gabime/spdlog 2018-03-17 12:47:56 +02:00
gabime
56e4a201ec formatting 2018-03-17 12:47:46 +02:00
gabime
c739e68021 clang format namespace fix 2018-03-17 12:47:04 +02:00
Gabi Melman
fe8a519434 Update logger.h 2018-03-16 22:03:54 +02:00
gabime
4445f6f869 formatting 2018-03-16 17:35:56 +02:00
gabime
4ee89877d5 Changed clang formatting for templates 2018-03-16 17:35:50 +02:00
gabime
ea95ea8295 Fix potential issue #660 2018-03-16 17:20:56 +02:00
gabime
fe5aaf4932 Fixed example 2018-03-16 17:17:33 +02:00
gabime
5afb5dc782 Changed clang formatting for templates 2018-03-16 17:13:50 +02:00
gabime
f4ce52d206 Changed clang formatting for templates 2018-03-16 17:13:22 +02:00
gabime
1108515738 format.sh small fix 2018-03-16 17:04:35 +02:00
gabime
8ee7b772a9 Added -O3 flag to CMakeLists.txt 2018-03-16 14:15:35 +02:00
Gabi Melman
650daf7542 Update common.h
Updated spdlog version macro to 0.16.4-rc
2018-03-09 23:26:28 +02:00
Gabi Melman
1946818292 Update .clang-format 2018-03-09 23:11:48 +02:00
gabime
cbe98c0fd2 clang format 2018-03-09 15:30:48 +02:00
gabime
ad221b0990 Changed function name to level::from_str 2018-03-09 15:27:53 +02:00
gabime
a2653d409f clang-format 2018-03-09 15:26:33 +02:00
gabime
461b5ef28a Fixed missing ; 2018-03-09 15:26:00 +02:00
gabime
7f1f7b6232 Changed function name to level::from_str 2018-03-09 15:24:37 +02:00
gabime
d741f1b654 some clang format fixes 2018-03-09 15:02:15 +02:00
gabime
f0606bcdb7 Merge branch 'master' of https://github.com/gabime/spdlog 2018-03-09 14:29:23 +02:00
gabime
03bc9ebb1f .clang-format 2018-03-09 14:29:20 +02:00
gabime
1f79c01dc4 Use clang-format-5.0 instead of astyle 2018-03-09 14:27:15 +02:00
Gabi Melman
dcf803de73 Merge pull request #622 from fegomes/to_level
New function to convert level_enum from string
2018-03-09 14:27:01 +02:00
fegomes
46f9768599 change of scope the name_to_level variable 2018-03-09 09:04:44 -03:00
gabime
2e098421f1 added .log extension to bench test 2018-03-09 12:08:56 +02:00
fegomes
c21dd874d1 removed class to return size of array. 2018-03-08 19:09:46 -03:00
fegomes
48c8755d06 include test to convert functions and change suggested by @gabime 2018-03-08 19:08:24 -03:00
fegomes
f9750dddee Merge branch 'master' into to_level 2018-03-08 18:16:10 -03:00
Gabi Melman
e8a39c894f Update test_pattern_formatter.cpp 2018-03-06 10:31:36 +02:00
Gabi Melman
97cdbc45e8 Merge pull request #653 from tbastos/master
Fix implicit conversion warnings (-Wsign-conversion)
2018-03-05 22:29:51 +02:00
Thiago Bastos
d044369e3b Fix implicit conversion warnings (-Wsign-conversion) 2018-03-05 20:00:48 +01:00
gabime
84d3c90b93 Fixed g++ 4.9 warnings after the clang-tidy fixes 2018-02-28 00:11:50 +02:00
Gabi Melman
2e973c8b3d Merge pull request #647 from DanielChabrowski/clang-tidy
Applied some of clang-tidy fixes
2018-02-27 23:41:24 +02:00
Daniel Chabrowski
de642b6263 astyle applied 2018-02-25 12:41:18 +01:00
Daniel Chabrowski
5355bd3a8f readability-named-parameter 2018-02-25 12:39:37 +01:00
Daniel Chabrowski
35a843f8b6 modernize-return-braced-init-list 2018-02-25 12:24:21 +01:00
Daniel Chabrowski
17caf77784 google-build-namespaces 2018-02-25 12:12:34 +01:00
Daniel Chabrowski
0c94ce0039 deleted copy op and a little format 2018-02-25 03:35:20 +01:00
Daniel Chabrowski
af50d5ef1f readability-inconsistent-declaration-parameter-name 2018-02-25 02:19:26 +01:00
Daniel Chabrowski
9ce66f2c9a modernize-pass-by-value 2018-02-25 01:58:09 +01:00
Daniel Chabrowski
ad624432d8 google-explicit-constructor 2018-02-25 01:40:46 +01:00
Daniel Chabrowski
1e1ca23101 modernize-use-equals-default 2018-02-25 01:25:15 +01:00
Daniel Chabrowski
e5bbe57f01 cppcoreguidelines-pro-type-member 2018-02-25 01:15:35 +01:00
Daniel Chabrowski
68f91822ed performance-unnecessary-value-param 2018-02-25 00:54:14 +01:00
Daniel Chabrowski
7aed498540 modernize-use-default-member-init 2018-02-25 00:38:54 +01:00
Daniel Chabrowski
d5a3bb5234 readability-else-after-return 2018-02-25 00:24:47 +01:00
Daniel Chabrowski
9ebb9ff318 readability-implicit-bool-cast 2018-02-25 00:16:18 +01:00
Daniel Chabrowski
fb6df0512f modernize-use-override 2018-02-24 23:56:56 +01:00
Daniel Chabrowski
7f4c1bb77c modernize-use-using 2018-02-24 22:35:09 +01:00
gabime
abc0d43995 astyle 2018-02-23 18:33:03 +02:00
gabime
3826ac1433 bumped bundled fmt version to 4.1.0 2018-02-23 18:32:06 +02:00
gabime
d650fa2456 Fixed tests for older gcc compilers 2018-02-23 18:25:39 +02:00
gabime
80163dc6c1 Better support for WinRT 2018-02-23 18:16:43 +02:00
gabime
a8b5bb894e fixed cmake for tests 2018-02-23 16:49:26 +02:00
gabime
3620b31ef2 added pattern formatter tests 2018-02-23 16:40:45 +02:00
gabime
7709fc70eb small fix to pattern_formatter ctor 2018-02-23 15:10:21 +02:00
Gabi Melman
bce33698be Merge pull request #628 from emadwill/eol
Support for custom EOL per formatter -Thanks @emadwill
2018-02-23 15:11:10 +02:00
gabime
04d0dd5987 moved SPDLOG_VERSION macro to common.h 2018-02-23 14:39:41 +02:00
gabime
051048ebd7 Fixed issue #645 (include tweakme.h from common.h) 2018-02-23 14:34:25 +02:00
gabime
7fe3912f12 astyle 2018-02-23 14:29:31 +02:00
gabime
7eb41ccf0f Use -O3 flag in tests 2018-02-23 14:29:14 +02:00
gabime
79d55fd802 Merge branch 'master' of https://github.com/gabime/spdlog 2018-02-23 14:07:45 +02:00
gabime
6df52df5b4 Use emplace_back in pattern_formatter 2018-02-23 14:05:48 +02:00
gabime
bdca50e6a7 Fixed issue #637 2018-02-23 13:26:53 +02:00
Gabi Melman
851b49e147 Update CMakeLists.txt 2018-02-22 13:37:50 +02:00
Gabi Melman
e64847bd09 Merge pull request #644 from grzadr/patch-1
Fix indentation in tests/catch.hpp:2916
2018-02-22 11:44:55 +02:00
grzadr
8e861728a0 Fix indentation in line 2916
Compiling with -Wmisleading-indentation triggers warning:
/data/Git/spdlog/tests/catch.hpp: In member function ‘bool Catch::TestSpec::Filter::matches(const Catch::TestCaseInfo&) const’:
/data/Git/spdlog/tests/catch.hpp:2913:17: warning: this ‘for’ clause does not guard... [-Wmisleading-indentation]
                 for( std::vector<Ptr<Pattern> >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it )
                 ^~~
/data/Git/spdlog/tests/catch.hpp:2916:21: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘for’
                     return true;
                     ^~~~~~
2018-02-22 09:46:54 +01:00
Gabi Melman
8cfd71a9be Merge pull request #635 from DanielChabrowski/cmake_explicit_lang
CMake explicit language
2018-02-12 01:16:49 +02:00
Daniel Chabrowski
5d08bd1709 Bump example's cmake version to 3.1
CMake 3.1 is used in main CMakeLists file, no reason not to use it in example.
Threads::Threads is introduced in cmake 3.1.
2018-02-11 21:16:15 +01:00
Daniel Chabrowski
646a140ed4 Specify CXX language explicitly in CMake
Marking project as CXX will disable detecting C compiler and other checks.
Removed 'INCLUDES DESTINATION' as it made the include path appear doubled.
2018-02-11 21:13:33 +01:00
Gabi Melman
5eef243ab6 Merge pull request #634 from ColinDuquesnoy/patch-1
Fix compilation error with GCC 8
2018-02-10 19:05:33 +02:00
Colin Duquesnoy
11ee6834f6 Fix compilation error with GCC 8
error: need 'typename' before 'std::conditional<std::is_same<char, char>::value, fmt::BasicMemoryWriter<char>, fmt::BasicMemoryWriter<wchar_t> >::type' because 'std::conditional<std::is_same<char, char>::value, fmt::BasicMemoryWriter<char>, fmt::BasicMemoryWriter<wchar_t> >' is a dependent scope
         std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
2018-02-10 15:15:46 +01:00
Gabi Melman
c07e81a049 Merge pull request #632 from bschindler/wincolor_set_color_public
Make set_color public in wincolor_sink to retain configurability
2018-02-08 11:04:29 +02:00
Benjamin Schindler
05b2aabe0e Make set_color public in wincolor_sink to retain configurability 2018-02-08 09:14:45 +01:00
Emad William Farag
55680db160 Support for custom EOL per formatter 2018-02-05 21:51:01 -05:00
Fernando Gomes
f4ffddc942 Merge branch 'master' into to_level 2018-02-05 09:52:30 -02:00
gabime
5ab033fba5 Fix issue #629 2018-02-05 12:20:57 +02:00
Gabi Melman
1dfc8282df Merge pull request #624 from yisonPylkita/modern_cmake_tests
Modern CMake in /tests
2018-02-03 19:10:44 +02:00
gabime
f8aec1bdf1 Merge branch 'master' of https://github.com/gabime/spdlog.git 2018-02-03 18:58:10 +02:00
gabime
51a83da578 Repplaced map with unordered_map for level->colos mapping 2018-02-03 18:57:57 +02:00
yisonPylkita
38ccd51399 Modern CMake in /tests 2018-01-28 00:49:03 +01:00
fegomes
8696ad8739 new function to convert level_enum from string 2018-01-24 23:08:46 -02:00
Gabi Melman
c336470320 Merge pull request #621 from rcarmich/fix_example_spelling_errors
Fixed spelling mistake in example.cpp
2018-01-24 22:42:31 +02:00
Ryan Carmichael
da51d8dfd3 Fixed spelling mistake in example.cpp 2018-01-24 14:18:05 -06:00
Gabi Melman
22fdd3bf0f Merge pull request #615 from Qix-/master
A few small ANSI color improvements
2018-01-18 22:01:46 +02:00
Josh Junon
cae749fc9b clear line after writing log message contents 2018-01-18 15:06:47 +01:00
Josh Junon
58e68725ae rename grey to black 2018-01-18 15:03:26 +01:00
Josh Junon
a59f74e8a2 remove needless zeros in reset code 2018-01-18 15:03:03 +01:00
Gabi Melman
f258af4364 Update CMakeLists.txt 2018-01-12 16:06:30 +02:00
gabime
ccd675a286 version 0.16.3 2018-01-12 14:09:34 +02:00
gabime
5372d58adc comment 2018-01-12 14:09:07 +02:00
Gabi Melman
751520f0cf Merge pull request #610 from joaomoreno/fix609
Use Sleep in Windows instead of sleep_for
2018-01-11 23:09:38 +02:00
Joao Moreno
357a63d914 fix spdlog namespace 2018-01-11 21:58:02 +01:00
Joao Moreno
a938045135 use Sleep in Windows instead of sleep_for
fixes #609
2018-01-11 14:50:47 +01:00
Gabi Melman
32177aa77a Merge pull request #604 from sam-lunt/improve-macros
Ensure marcos always expand to expressions
2018-01-03 18:05:25 +02:00
Gabi Melman
ce1df17262 Merge pull request #605 from sam-lunt/add-flush-on
Add global flush_on function
2018-01-03 18:00:08 +02:00
Sam Lunt
9f8413308a add global flush_on function 2018-01-03 09:36:09 -06:00
Sam Lunt
f25f0e0e40 add (void)0 when logging is disabled 2018-01-03 09:07:58 -06:00
Gabi Melman
a2890f2778 Merge pull request #596 from Broekman/master
Issue fix for spdlog #595. Conversion warning.
2017-12-26 21:22:08 +02:00
Stefan Broekman
de4644b44a Issue fix for spdlog #595. Conversion warning.
See: https://github.com/gabime/spdlog/issues/595

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

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

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

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

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

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

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

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

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

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

108
.clang-format Normal file
View File

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

9
.gitignore vendored
View File

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

View File

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

View File

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

View File

@@ -8,20 +8,20 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
* 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`
* Homebrew: `brew install spdlog`
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
* Fedora: `yum install spdlog`
* Gentoo: `emerge dev-libs/spdlog`
* Arch Linux: `pacman -S spdlog-git`
* Arch Linux: `yaourt -S spdlog-git`
* vcpkg: `vcpkg install spdlog`
## Platforms
* Linux, FreeBSD, Solaris
* Windows (vc 2013+, cygwin/mingw)
* Windows (vc 2013+, cygwin)
* Mac OSX (clang 3.5+)
* Android
@@ -29,7 +29,8 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
* Headers only, just copy and use.
* Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library.
* Extremely fast asynchronous mode (optional) - using lockfree queues and other tricks to reach millions of calls/sec.
* Optional printf syntax support.
* Asynchronous mode (optional)
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
* Conditional Logging
* Multi/Single threaded loggers.
@@ -53,9 +54,9 @@ Time needed to log 1,000,000 lines in synchronous mode (in seconds, the best of
|threads|boost log 1.54|glog |easylogging |spdlog|
|-------|:-------:|:-----:|----------:|------:|
|1| 4.169s |1.066s |0.975s |0.302s|
|10| 6.180s |3.032s |2.857s |0.968s|
|100| 5.981s |1.139s |4.512s |0.497s|
|1| 4.169s |1.066s |0.975s |0.392s|
|10| 6.180s |3.032s |2.857s |0.773s|
|100| 5.981s |1.139s |4.512s |0.587s|
#### Asynchronous mode
@@ -63,9 +64,9 @@ Time needed to log 1,000,000 lines in asynchronous mode, i.e. the time it takes
|threads|g2log <sup>async logger</sup> |spdlog <sup>async mode</sup>|
|:-------|:-----:|-------------------------:|
|1| 1.850s |0.216s |
|10| 0.943s |0.173s|
|100| 0.959s |0.202s|
|1| 1.850s |0.39s |
|10| 0.943s |0.416s|
|100| 0.959s |0.413s|
@@ -92,19 +93,15 @@ int main(int, char*[])
auto console = spd::stdout_color_mt("console");
console->info("Welcome to spdlog!");
console->error("Some error message with arg{}..", 1);
// Conditional logging example
auto i = 2;
console->warn_if(i != 0, "an important message");
// Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12);
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
console->info("Support for floats {:03.2f}", 1.23456);
console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");
// 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");
// Create basic file logger (not rotated)
@@ -112,26 +109,26 @@ int main(int, char*[])
my_logger->info("Some log message");
// Create a file rotating logger with 5mb size max and 3 rotated files
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3);
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile.txt", 1048576 * 5, 3);
for (int i = 0; i < 10; ++i)
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
// Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
// trigger flush if the log severity is error or higher
daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44);
// Customize msg format for all messages
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
rotating_logger->info("This is another message with custom format");
spd::set_pattern("[%^+++%$] [%H:%M:%S %z] [thread %t] %v");
console->info("This an info message with custom format (and custom color range between the '%^' and '%$')");
console->error("This an error message with custom format (and custom color range between the '%^' and '%$')");
// Runtime log levels
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->debug("This message shold be displayed..");
console->debug("This message should be displayed..");
// Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
@@ -173,7 +170,7 @@ int main(int, char*[])
void async_example()
{
size_t q_size = 4096; //queue size must be power of 2
size_t q_size = 4096;
spd::set_async_mode(q_size);
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
for (int i = 0; i < 100; ++i)

32
appveyor.yml Normal file
View File

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

View File

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

View File

@@ -3,7 +3,16 @@ CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -I../includ
CXX_RELEASE_FLAGS = -O3 -flto -DNDEBUG
binaries=spdlog-bench spdlog-bench-mt spdlog-async spdlog-null-async boost-bench boost-bench-mt glog-bench glog-bench-mt g2log-async easylogging-bench easylogging-bench-mt
# g2log-async
binaries=spdlog-bench spdlog-bench-mt spdlog-async spdlog-null-async \
boost-bench boost-bench-mt \
glog-bench glog-bench-mt \
g3log-async \
p7-bench p7-bench-mt \
log4cpp-bench log4cpp-bench-mt \
log4cplus-bench log4cplus-bench-mt \
easylogging-bench easylogging-bench-mt easylogging-bench-async \
plog-bench plog-bench-mt
all: $(binaries)
@@ -16,13 +25,10 @@ spdlog-bench-mt: spdlog-bench-mt.cpp
spdlog-async: spdlog-async.cpp
$(CXX) spdlog-async.cpp -o spdlog-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
spdlog-null-async: spdlog-null-async.cpp
$(CXX) spdlog-null-async.cpp -o spdlog-null-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
BOOST_FLAGS = -DBOOST_LOG_DYN_LINK -I/usr/include -lboost_log -lboost_log_setup -lboost_filesystem -lboost_system -lboost_thread -lboost_regex -lboost_date_time -lboost_chrono
BOOST_FLAGS = -DBOOST_LOG_DYN_LINK -I$(HOME)/include -I/usr/include -L$(HOME)/lib -lboost_log_setup -lboost_log -lboost_filesystem -lboost_system -lboost_thread -lboost_regex -lboost_date_time -lboost_chrono
boost-bench: boost-bench.cpp
$(CXX) boost-bench.cpp -o boost-bench $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
@@ -30,33 +36,62 @@ boost-bench: boost-bench.cpp
boost-bench-mt: boost-bench-mt.cpp
$(CXX) boost-bench-mt.cpp -o boost-bench-mt $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
GLOG_FLAGS = -lglog
GLOG_FLAGS = -I$(HOME)/include -L$(HOME)/lib -lglog
glog-bench: glog-bench.cpp
$(CXX) glog-bench.cpp -o glog-bench $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
glog-bench-mt: glog-bench-mt.cpp
$(CXX) glog-bench-mt.cpp -o glog-bench-mt $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
G2LOG_FLAGS = -I/home/gabi/devel/g2log/g2log/src -L/home/gabi/devel/g2log/g2log -llib_g2logger
G2LOG_FLAGS = -I$(HOME)/include -L$(HOME)/lib -llib_g2logger
g2log-async: g2log-async.cpp
$(CXX) g2log-async.cpp -o g2log-async $(CXXFLAGS) $(G2LOG_FLAGS) $(CXX_RELEASE_FLAGS)
G3LOG_FLAGS = -I$(HOME)/include -L$(HOME)/lib -lg3logger
g3log-async: g3log-async.cpp
$(CXX) g3log-async.cpp -o g3log-async $(CXXFLAGS) $(G3LOG_FLAGS) $(CXX_RELEASE_FLAGS)
EASYL_FLAGS = -I../../easylogging/src/
P7_FLAGS = -I$(HOME)/P7/Headers -I$(HOME)/include -L$(HOME)/lib -lP7
p7-bench: p7-bench.cpp
$(CXX) p7-bench.cpp -o p7-bench $(CXXFLAGS) $(P7_FLAGS) $(CXX_RELEASE_FLAGS)
p7-bench-mt: p7-bench-mt.cpp
$(CXX) p7-bench-mt.cpp -o p7-bench-mt $(CXXFLAGS) $(P7_FLAGS) $(CXX_RELEASE_FLAGS)
LOG4CPP_FLAGS = -I$(HOME)/include -L$(HOME)/lib -llog4cpp
log4cpp-bench: log4cpp-bench.cpp
$(CXX) log4cpp-bench.cpp -o log4cpp-bench $(CXXFLAGS) $(LOG4CPP_FLAGS) $(CXX_RELEASE_FLAGS)
log4cpp-bench-mt: log4cpp-bench-mt.cpp
$(CXX) log4cpp-bench-mt.cpp -o log4cpp-bench-mt $(CXXFLAGS) $(LOG4CPP_FLAGS) $(CXX_RELEASE_FLAGS)
LOG4CPLUS_FLAGS = -I$(HOME)/include -L$(HOME)/lib -llog4cplus
log4cplus-bench: log4cplus-bench.cpp
$(CXX) log4cplus-bench.cpp -o log4cplus-bench $(CXXFLAGS) $(LOG4CPLUS_FLAGS) $(CXX_RELEASE_FLAGS)
log4cplus-bench-mt: log4cplus-bench-mt.cpp
$(CXX) log4cplus-bench-mt.cpp -o log4cplus-bench-mt $(CXXFLAGS) $(LOG4CPLUS_FLAGS) $(CXX_RELEASE_FLAGS)
EASYL_FLAGS = -I$(HOME)/easyloggingpp/src
easylogging-bench: easylogging-bench.cpp
$(CXX) easylogging-bench.cpp -o easylogging-bench $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
easylogging-bench-mt: easylogging-bench-mt.cpp
$(CXX) easylogging-bench-mt.cpp -o easylogging-bench-mt $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
$(CXX) easylogging-bench-mt.cpp -o easylogging-bench-mt $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
easylogging-bench-async: easylogging-bench-async.cpp
$(CXX) easylogging-bench-async.cpp -o easylogging-bench-async $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
PLOG_FLAGS = -I$(HOME)/include
plog-bench: plog-bench.cpp
$(CXX) plog-bench.cpp -o plog-bench $(CXXFLAGS) $(PLOG_FLAGS) $(CXX_RELEASE_FLAGS)
plog-bench-mt: plog-bench-mt.cpp
$(CXX) plog-bench-mt.cpp -o plog-bench-mt $(CXXFLAGS) $(PLOG_FLAGS) $(CXX_RELEASE_FLAGS)
.PHONY: clean
clean:
rm -f *.o logs/* $(binaries)
rebuild: clean all

14
bench/README.md Normal file
View File

@@ -0,0 +1,14 @@
# loggers
Name | License | Lang. | Year | Platform | Compiler | Dependence | URL
--- | --- | --- | --- | --- | --- | --- | ---
Pantheios | BSD | C++ | 2017 | Windows, Linux, MacOSX | VC++, GCC(3.2+), Intel, Borland, Comeau, Digital Mars, Metrowerks | STLSoft | http://www.pantheios.org/ <br> http://blog.pantheios.org/ <br> https://github.com/synesissoftware/Pantheios <br> http://www.pantheios.org/performance.html
Glog | 3-clause BSD| C++| 2017 | Windows, Linux, MacOSX | VC++, GCC, Clang, intel| Google gflags | https://github.com/google/glog
G3log | Public Domain | C++11 | 2018 | Windows, Linux, MacOSX, iPhoneOS | VC++, GCC, Clang, MinGW | None | https://github.com/KjellKod/g3log <br> https://github.com/KjellKod/g3sinks <br> https://kjellkod.wordpress.com/2014/08/16/presenting-g3log-the-next-version-of-the-next-generation-of-loggers/ <br> https://kjellkod.wordpress.com/2015/06/30/the-worlds-fastest-logger-vs-g3log/
P7 | LGPL | C++, C, C#, Python | 2017 | Windows, Linux | VC++, GCC, Clang, MinGW | None | http://baical.net/p7.html
log4cpp | LGPL | C++ | 2017 | Windows, Linux, MacOSX | VC++, GCC, Sun CC, OpenVMS | Boost | http://log4cpp.sourceforge.net/
log4cplus | 2-clause BSD or Apache 2 | C++ | 2017 | Windows, Linux, MacOSX, Android | VC++, GCC, Clang | Boost | https://github.com/log4cplus/log4cplus <br> https://sourceforge.net/p/log4cplus/wiki/Home/
Easylogging | MIT | C++11 | 2018 | Windows, Linux, MacOSX, iPhoneOS, Android | VC++, GCC, Clang, Intel, MinGW | None | https://github.com/muflihun/easyloggingpp
Spdlog | MIT | C++11 | 2018 | Windows, Linux, MacOSX, iPhoneOS, Android(logcat) | VC++, GCC, Clang, MinGW | None, Headers only library | https://github.com/gabime/spdlog <br> https://github.com/fmtlib/fmt
Boost.Log v2 | Boost | C++ | 2016 | Windows, Linux, MacOSX, iPhoneOS, Android | VC++, GCC, Clang | Boost | https://github.com/boostorg/log <br> http://www.boost.org/doc/libs/1_66_0/libs/log/doc/html/index.html
plog | MPL 2.0 | C++ | 2017 | Windows, Linux, MacOSX, iPhoneOS, Android | gcc, clang, msvc, mingw, mingw-w64, icc, c++builder | None, Headers only library | https://github.com/SergiusTheBest/plog

View File

@@ -3,18 +3,20 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>
#include <vector>
#include <atomic>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/setup/file.hpp>
namespace logging = boost::log;
namespace src = boost::log::sources;
@@ -23,62 +25,62 @@ namespace keywords = boost::log::keywords;
void init()
{
logging::add_file_log
(
keywords::file_name = "logs/boost-sample_%N.log", /*< file name pattern >*/
keywords::auto_flush = false,
keywords::format = "[%TimeStamp%]: %Message%"
);
logging::add_file_log(keywords::file_name = "logs/boost-bench-mt_%N.log", /*< file name pattern >*/
keywords::auto_flush = false, keywords::format = "[%TimeStamp%]: %Message%");
logging::core::get()->set_filter
(
logging::trivial::severity >= logging::trivial::info
);
logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::info);
}
using namespace std;
int main(int argc, char* argv[])
int main(int argc, char *argv[])
{
using namespace std::chrono;
using clock = steady_clock;
int thread_count = 10;
if(argc > 1)
if (argc > 1)
thread_count = atoi(argv[1]);
int howmany = 1000000;
init();
logging::add_common_attributes();
using namespace logging::trivial;
src::severity_logger_mt< severity_level > lg;
src::severity_logger_mt<severity_level> lg;
std::atomic<int > msg_counter {0};
std::atomic<int> msg_counter{0};
vector<thread> threads;
auto start = clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
threads.push_back(std::thread([&]() {
while (true)
{
int counter = ++msg_counter;
if (counter > howmany) break;
if (counter > howmany)
break;
BOOST_LOG_SEV(lg, info) << "boost message #" << counter << ": This is some text for your pleasure";
}
}));
}
for(auto &t:threads)
for (auto &t : threads)
{
t.join();
};
}
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany / deltaf;
std::cout << "Total: " << howmany << std::endl;
std::cout << "Threads: " << thread_count << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl;
return 0;
}

View File

@@ -2,14 +2,18 @@
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <chrono>
#include <iostream>
#include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp>
#include <boost/log/sinks/text_file_backend.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/sources/record_ostream.hpp>
#include <boost/log/sources/severity_logger.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/setup/file.hpp>
namespace logging = boost::log;
namespace src = boost::log::sources;
@@ -18,30 +22,35 @@ namespace keywords = boost::log::keywords;
void init()
{
logging::add_file_log
(
keywords::file_name = "logs/boost-sample_%N.log", /*< file name pattern >*/
keywords::auto_flush = false,
keywords::format = "[%TimeStamp%]: %Message%"
);
logging::add_file_log(keywords::file_name = "logs/boost-bench_%N.log", /*< file name pattern >*/
keywords::auto_flush = false, keywords::format = "[%TimeStamp%]: %Message%");
logging::core::get()->set_filter
(
logging::trivial::severity >= logging::trivial::info
);
logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::info);
}
int main(int argc, char* [])
int main(int, char *[])
{
using namespace std::chrono;
using clock = steady_clock;
int howmany = 1000000;
init();
logging::add_common_attributes();
using namespace logging::trivial;
src::severity_logger_mt< severity_level > lg;
for(int i = 0 ; i < howmany; ++i)
src::severity_logger_mt<severity_level> lg;
auto start = clock::now();
for (int i = 0; i < howmany; ++i)
BOOST_LOG_SEV(lg, info) << "boost message #" << i << ": This is some text for your pleasure";
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany / deltaf;
std::cout << "Total: " << howmany << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl;
return 0;
}

10
bench/easyl-async.conf Normal file
View File

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

10
bench/easyl-mt.conf Normal file
View File

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

View File

@@ -1,5 +1,5 @@
* GLOBAL:
FORMAT = "[%datetime]: %msg"
FORMAT = "[%datetime]: %levshort %msg"
FILENAME = ./logs/easylogging.log
ENABLED = true
TO_FILE = true

View File

@@ -0,0 +1,67 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>
#include <vector>
#define ELPP_THREAD_SAFE
#define ELPP_EXPERIMENTAL_ASYNC
#include "easylogging++.cc"
#include "easylogging++.h"
INITIALIZE_EASYLOGGINGPP
using namespace std;
int main(int argc, char *argv[])
{
using namespace std::chrono;
using clock = steady_clock;
int thread_count = 10;
if (argc > 1)
thread_count = atoi(argv[1]);
int howmany = 1000000;
// Load configuration from file
el::Configurations conf("easyl-async.conf");
el::Loggers::reconfigureLogger("default", conf);
std::atomic<int> msg_counter{0};
vector<thread> threads;
auto start = clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]() {
while (true)
{
int counter = ++msg_counter;
if (counter > howmany)
break;
LOG(INFO) << "easylog message #" << counter << ": This is some text for your pleasure";
}
}));
}
for (auto &t : threads)
{
t.join();
}
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany / deltaf;
std::cout << "Total: " << howmany << std::endl;
std::cout << "Threads: " << thread_count << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl;
return 0;
}

View File

@@ -3,50 +3,64 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>
#include <vector>
#include <atomic>
#define _ELPP_THREAD_SAFE
#define ELPP_THREAD_SAFE
#include "easylogging++.cc"
#include "easylogging++.h"
_INITIALIZE_EASYLOGGINGPP
INITIALIZE_EASYLOGGINGPP
using namespace std;
int main(int argc, char* argv[])
int main(int argc, char *argv[])
{
using namespace std::chrono;
using clock = steady_clock;
int thread_count = 10;
if(argc > 1)
if (argc > 1)
thread_count = atoi(argv[1]);
int howmany = 1000000;
// Load configuration from file
el::Configurations conf("easyl.conf");
el::Configurations conf("easyl-mt.conf");
el::Loggers::reconfigureLogger("default", conf);
std::atomic<int > msg_counter {0};
std::atomic<int> msg_counter{0};
vector<thread> threads;
auto start = clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
threads.push_back(std::thread([&]() {
while (true)
{
int counter = ++msg_counter;
if (counter > howmany) break;
if (counter > howmany)
break;
LOG(INFO) << "easylog message #" << counter << ": This is some text for your pleasure";
}
}));
}
for(auto &t:threads)
for (auto &t : threads)
{
t.join();
};
}
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany / deltaf;
std::cout << "Total: " << howmany << std::endl;
std::cout << "Threads: " << thread_count << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl;
return 0;
}

View File

@@ -3,20 +3,37 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <chrono>
#include <iostream>
#include "easylogging++.cc"
#include "easylogging++.h"
INITIALIZE_EASYLOGGINGPP
_INITIALIZE_EASYLOGGINGPP
int main(int, char* [])
int main(int, char *[])
{
using namespace std::chrono;
using clock = steady_clock;
int howmany = 1000000;
// Load configuration from file
el::Configurations conf("easyl.conf");
el::Loggers::reconfigureLogger("default", conf);
for(int i = 0 ; i < howmany; ++i)
el::Logger *defaultLogger = el::Loggers::getLogger("default");
auto start = clock::now();
for (int i = 0; i < howmany; ++i)
LOG(INFO) << "easylog message #" << i << ": This is some text for your pleasure";
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany / deltaf;
std::cout << "Total: " << howmany << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl;
return 0;
}

View File

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

63
bench/g3log-async.cpp Normal file
View File

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

View File

@@ -3,19 +3,23 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>
#include <vector>
#include <atomic>
#include "glog/logging.h"
using namespace std;
int main(int argc, char* argv[])
int main(int argc, char *argv[])
{
using namespace std::chrono;
using clock = steady_clock;
int thread_count = 10;
if(argc > 1)
if (argc > 1)
thread_count = atoi(argv[1]);
int howmany = 1000000;
@@ -24,27 +28,36 @@ int main(int argc, char* argv[])
FLAGS_log_dir = "logs";
google::InitGoogleLogging(argv[0]);
std::atomic<int > msg_counter {0};
std::atomic<int> msg_counter{0};
vector<thread> threads;
auto start = clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
threads.push_back(std::thread([&]() {
while (true)
{
int counter = ++msg_counter;
if (counter > howmany) break;
if (counter > howmany)
break;
LOG(INFO) << "glog message #" << counter << ": This is some text for your pleasure";
}
}));
}
for(auto &t:threads)
for (auto &t : threads)
{
t.join();
};
}
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany / deltaf;
std::cout << "Total: " << howmany << std::endl;
std::cout << "Threads: " << thread_count << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl;
return 0;
}

View File

@@ -3,19 +3,32 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <chrono>
#include <iostream>
#include "glog/logging.h"
int main(int, char* argv[])
int main(int, char *argv[])
{
int howmany = 1000000;
using namespace std::chrono;
using clock = steady_clock;
int howmany = 1000000;
FLAGS_logtostderr = 0;
FLAGS_log_dir = "logs";
google::InitGoogleLogging(argv[0]);
for(int i = 0 ; i < howmany; ++i)
LOG(INFO) << "glog message # " << i << ": This is some text for your pleasure";
auto start = clock::now();
for (int i = 0; i < howmany; ++i)
LOG(INFO) << "glog message #" << i << ": This is some text for your pleasure";
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany / deltaf;
std::cout << "Total: " << howmany << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl;
return 0;
}

View File

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

View File

@@ -1,32 +1,26 @@
#include <thread>
#include <vector>
#include <atomic>
#include <iostream>
#include <chrono>
#include <algorithm>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <fstream>
#include <cstdio>
#include <map>
#include <numeric>
#include <functional>
#include <thread>
#include "utils.h"
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cstdio>
#include <fstream>
#include <functional>
#include <g3log/g3log.hpp>
#include <g3log/logworker.hpp>
#include <iomanip>
#include <iostream>
#include <map>
#include <numeric>
#include <sstream>
#include <thread>
#include <vector>
namespace
{
namespace {
const uint64_t g_iterations = 1000000;
std::atomic<size_t> g_counter = {0};
void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t>& result)
void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t> &result)
{
while (true)
@@ -45,14 +39,12 @@ void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t>& result)
}
}
void PrintResults(const std::map<size_t, std::vector<uint64_t>>& threads_result, size_t total_us)
void PrintResults(const std::map<size_t, std::vector<uint64_t>> &threads_result, size_t total_us)
{
std::vector<uint64_t> all_measurements;
all_measurements.reserve(g_iterations);
for (auto& t_result : threads_result)
for (auto &t_result : threads_result)
{
all_measurements.insert(all_measurements.end(), t_result.second.begin(), t_result.second.end());
}
@@ -62,13 +54,12 @@ void PrintResults(const std::map<size_t, std::vector<uint64_t>>& threads_result,
// calc avg
auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus<uint64_t>());
auto avg = double(total)/all_measurements.size();
std::cout << "[g3log] worst: " << std::setw(10) << std::right << worst << "\tAvg: " << avg << "\tTotal: " << utils::format(total_us) << " us" << std::endl;
auto avg = double(total) / all_measurements.size();
std::cout << "[g3log] worst: " << std::setw(10) << std::right << worst << "\tAvg: " << avg << "\tTotal: " << utils::format(total_us)
<< " us" << std::endl;
}
}// anonymous
} // namespace
// The purpose of this test is NOT to see how fast
// each thread can possibly write. It is to see what
@@ -78,9 +69,9 @@ void PrintResults(const std::map<size_t, std::vector<uint64_t>>& threads_result,
// an atomic counter is used to give each thread what
// it is to write next. The overhead of atomic
// synchronization between the threads are not counted in the worst case latency
int main(int argc, char** argv)
int main(int argc, char **argv)
{
size_t number_of_threads {0};
size_t number_of_threads{0};
if (argc == 2)
{
number_of_threads = atoi(argv[1]);
@@ -91,7 +82,6 @@ int main(int argc, char** argv)
return 1;
}
std::vector<std::thread> threads(number_of_threads);
std::map<size_t, std::vector<uint64_t>> threads_result;
@@ -102,12 +92,12 @@ int main(int argc, char** argv)
threads_result[idx].reserve(g_iterations);
}
const std::string g_path = "./" ;
const std::string g_prefix_log_name = "g3log-performance-";
const std::string g_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt";
const std::string g_path = "./";
const std::string g_prefix_log_name = "g3log-performance-";
const std::string g_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt";
auto worker = g3::LogWorker::createLogWorker();
auto handle= worker->addDefaultLogger(argv[0], "g3log.txt");
auto handle = worker->addDefaultLogger(argv[0], "g3log.txt");
g3::initializeLogging(worker.get());
auto start_time_application_total = std::chrono::high_resolution_clock::now();
@@ -121,9 +111,8 @@ int main(int argc, char** argv)
}
auto stop_time_application_total = std::chrono::high_resolution_clock::now();
uint64_t total_time_in_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time_application_total - start_time_application_total).count();
uint64_t total_time_in_us =
std::chrono::duration_cast<std::chrono::microseconds>(stop_time_application_total - start_time_application_total).count();
PrintResults(threads_result, total_time_in_us);
return 0;
}

View File

@@ -1,31 +1,26 @@
#include <thread>
#include <vector>
#include <atomic>
#include <iostream>
#include <chrono>
#include "utils.h"
#include <algorithm>
#include <iostream>
#include <atomic>
#include <chrono>
#include <cstdio>
#include <functional>
#include <iostream>
#include <map>
#include <numeric>
#include <functional>
#include "utils.h"
#include <thread>
#include <vector>
#include "spdlog/spdlog.h"
namespace spd = spdlog;
namespace
{
namespace {
const uint64_t g_iterations = 1000000;
std::atomic<size_t> g_counter = {0};
void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t>& result)
void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t> &result)
{
auto logger = spd::get("file_logger");
while (true)
@@ -44,13 +39,12 @@ void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t>& result)
}
}
void PrintResults(const std::map<size_t, std::vector<uint64_t>>& threads_result, size_t total_us)
void PrintResults(const std::map<size_t, std::vector<uint64_t>> &threads_result, size_t total_us)
{
std::vector<uint64_t> all_measurements;
all_measurements.reserve(g_iterations);
for (auto& t_result : threads_result)
for (auto &t_result : threads_result)
{
all_measurements.insert(all_measurements.end(), t_result.second.begin(), t_result.second.end());
}
@@ -60,13 +54,12 @@ void PrintResults(const std::map<size_t, std::vector<uint64_t>>& threads_result,
// calc avg
auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus<uint64_t>());
auto avg = double(total)/all_measurements.size();
std::cout << "[spdlog] worst: " << std::setw(10) << std::right << worst << "\tAvg: " << avg << "\tTotal: " << utils::format(total_us) << " us" << std::endl;
auto avg = double(total) / all_measurements.size();
std::cout << "[spdlog] worst: " << std::setw(10) << std::right << worst << "\tAvg: " << avg << "\tTotal: " << utils::format(total_us)
<< " us" << std::endl;
}
}// anonymous
} // namespace
// The purpose of this test is NOT to see how fast
// each thread can possibly write. It is to see what
@@ -76,9 +69,9 @@ void PrintResults(const std::map<size_t, std::vector<uint64_t>>& threads_result,
// an atomic counter is used to give each thread what
// it is to write next. The overhead of atomic
// synchronization between the threads are not counted in the worst case latency
int main(int argc, char** argv)
int main(int argc, char **argv)
{
size_t number_of_threads {0};
size_t number_of_threads{0};
if (argc == 2)
{
number_of_threads = atoi(argv[1]);
@@ -89,7 +82,6 @@ int main(int argc, char** argv)
return 1;
}
std::vector<std::thread> threads(number_of_threads);
std::map<size_t, std::vector<uint64_t>> threads_result;
@@ -104,8 +96,8 @@ int main(int argc, char** argv)
spdlog::set_async_mode(queue_size);
auto logger = spdlog::create<spd::sinks::simple_file_sink_mt>("file_logger", "spdlog.log", true);
//force flush on every call to compare with g3log
auto s = (spd::sinks::simple_file_sink_mt*)logger->sinks()[0].get();
// force flush on every call to compare with g3log
auto s = (spd::sinks::simple_file_sink_mt *)logger->sinks()[0].get();
s->set_force_flush(true);
auto start_time_application_total = std::chrono::high_resolution_clock::now();
@@ -119,10 +111,9 @@ int main(int argc, char** argv)
}
auto stop_time_application_total = std::chrono::high_resolution_clock::now();
uint64_t total_time_in_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time_application_total - start_time_application_total).count();
uint64_t total_time_in_us =
std::chrono::duration_cast<std::chrono::microseconds>(stop_time_application_total - start_time_application_total).count();
PrintResults(threads_result, total_time_in_us);
return 0;
}

View File

@@ -5,15 +5,14 @@
#pragma once
#include <sstream>
#include <iomanip>
#include <locale>
#include <sstream>
namespace utils
{
namespace utils {
template<typename T>
inline std::string format(const T& value)
inline std::string format(const T &value)
{
static std::locale loc("");
std::stringstream ss;
@@ -23,7 +22,7 @@ inline std::string format(const T& value)
}
template<>
inline std::string format(const double & value)
inline std::string format(const double &value)
{
static std::locale loc("");
std::stringstream ss;
@@ -32,4 +31,4 @@ inline std::string format(const double & value)
return ss.str();
}
}
} // namespace utils

View File

@@ -0,0 +1,78 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <atomic>
#include <chrono>
#include <iostream>
#include <memory>
#include <thread>
#include <vector>
#include "log4cplus/fileappender.h"
#include "log4cplus/helpers/loglog.h"
#include "log4cplus/helpers/property.h"
#include "log4cplus/layout.h"
#include "log4cplus/logger.h"
#include "log4cplus/loggingmacros.h"
#include "log4cplus/ndc.h"
using namespace log4cplus;
int main(int argc, char *argv[])
{
using namespace std::chrono;
using clock = steady_clock;
int thread_count = 10;
if (argc > 1)
thread_count = std::atoi(argv[1]);
int howmany = 1000000;
log4cplus::initialize();
SharedFileAppenderPtr append(new FileAppender(LOG4CPLUS_TEXT("logs/log4cplus-bench-mt.log"), std::ios_base::trunc, true, true));
append->setName(LOG4CPLUS_TEXT("File"));
log4cplus::tstring pattern = LOG4CPLUS_TEXT("%d{%Y-%m-%d %H:%M:%S.%Q}: %p - %m %n");
append->setLayout(std::auto_ptr<Layout>(new PatternLayout(pattern)));
append->getloc();
Logger::getRoot().addAppender(SharedAppenderPtr(append.get()));
Logger root = Logger::getRoot();
std::atomic<int> msg_counter{0};
std::vector<std::thread> threads;
auto start = clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]() {
while (true)
{
int counter = ++msg_counter;
if (counter > howmany)
break;
LOG4CPLUS_INFO(root, "log4cplus message #" << counter << ": This is some text for your pleasure");
}
}));
}
for (auto &t : threads)
{
t.join();
}
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany / deltaf;
std::cout << "Total: " << howmany << std::endl;
std::cout << "Threads: " << thread_count << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl;
log4cplus::Logger::shutdown();
return 0;
}

52
bench/log4cplus-bench.cpp Normal file
View File

@@ -0,0 +1,52 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <chrono>
#include <iostream>
#include <memory>
#include "log4cplus/fileappender.h"
#include "log4cplus/helpers/loglog.h"
#include "log4cplus/helpers/property.h"
#include "log4cplus/layout.h"
#include "log4cplus/logger.h"
#include "log4cplus/loggingmacros.h"
#include "log4cplus/ndc.h"
using namespace log4cplus;
int main(int, char *[])
{
using namespace std::chrono;
using clock = steady_clock;
int howmany = 1000000;
log4cplus::initialize();
SharedFileAppenderPtr append(new FileAppender(LOG4CPLUS_TEXT("logs/log4cplus-bench.log"), std::ios_base::trunc, true, true));
append->setName(LOG4CPLUS_TEXT("File"));
log4cplus::tstring pattern = LOG4CPLUS_TEXT("%d{%Y-%m-%d %H:%M:%S.%Q}: %p - %m %n");
append->setLayout(std::auto_ptr<Layout>(new PatternLayout(pattern)));
append->getloc();
Logger::getRoot().addAppender(SharedAppenderPtr(append.get()));
Logger root = Logger::getRoot();
auto start = clock::now();
for (int i = 0; i < howmany; ++i)
LOG4CPLUS_INFO(root, "log4cplus message #" << i << ": This is some text for your pleasure");
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany / deltaf;
std::cout << "Total: " << howmany << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl;
log4cplus::Logger::shutdown();
return 0;
}

View File

@@ -0,0 +1,74 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <atomic>
#include <chrono>
#include <iostream>
#include <memory>
#include <thread>
#include <vector>
#include "log4cpp/Appender.hh"
#include "log4cpp/BasicLayout.hh"
#include "log4cpp/Category.hh"
#include "log4cpp/FileAppender.hh"
#include "log4cpp/Layout.hh"
#include "log4cpp/PatternLayout.hh"
#include "log4cpp/Priority.hh"
int main(int argc, char *argv[])
{
using namespace std::chrono;
using clock = steady_clock;
int thread_count = 10;
if (argc > 1)
thread_count = std::atoi(argv[1]);
int howmany = 1000000;
log4cpp::Appender *appender = new log4cpp::FileAppender("default", "logs/log4cpp-bench-mt.log");
log4cpp::PatternLayout *layout = new log4cpp::PatternLayout();
layout->setConversionPattern("%d{%Y-%m-%d %H:%M:%S.%l}: %p - %m %n");
appender->setLayout(layout);
log4cpp::Category &root = log4cpp::Category::getRoot();
root.addAppender(appender);
root.setPriority(log4cpp::Priority::INFO);
std::atomic<int> msg_counter{0};
std::vector<std::thread> threads;
auto start = clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]() {
while (true)
{
int counter = ++msg_counter;
if (counter > howmany)
break;
root << log4cpp::Priority::INFO << "log4cpp message #" << counter << ": This is some text for your pleasure";
}
}));
}
for (auto &t : threads)
{
t.join();
}
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany / deltaf;
std::cout << "Total: " << howmany << std::endl;
std::cout << "Threads: " << thread_count << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl;
root.shutdown();
return 0;
}

48
bench/log4cpp-bench.cpp Normal file
View File

@@ -0,0 +1,48 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <chrono>
#include <iostream>
#include <memory>
#include "log4cpp/Appender.hh"
#include "log4cpp/BasicLayout.hh"
#include "log4cpp/Category.hh"
#include "log4cpp/FileAppender.hh"
#include "log4cpp/Layout.hh"
#include "log4cpp/PatternLayout.hh"
#include "log4cpp/Priority.hh"
int main(int, char *[])
{
using namespace std::chrono;
using clock = steady_clock;
int howmany = 1000000;
log4cpp::Appender *appender = new log4cpp::FileAppender("default", "logs/log4cpp-bench.log");
log4cpp::PatternLayout *layout = new log4cpp::PatternLayout();
layout->setConversionPattern("%d{%Y-%m-%d %H:%M:%S.%l}: %p - %m %n");
appender->setLayout(layout);
log4cpp::Category &root = log4cpp::Category::getRoot();
root.addAppender(appender);
root.setPriority(log4cpp::Priority::INFO);
auto start = clock::now();
for (int i = 0; i < howmany; ++i)
root << log4cpp::Priority::INFO << "log4cpp message #" << i << ": This is some text for your pleasure";
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany / deltaf;
std::cout << "Total: " << howmany << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl;
root.shutdown();
return 0;
}

19
bench/mem Executable file
View File

@@ -0,0 +1,19 @@
#!/bin/sh
if [ $# -lt 1 ]; then
echo "usage: $0 <program>"
fi
PROG=$1
if [ ! -x "$PROG" ]; then
echo $PROG not found or not executable.
exit 1
fi
$* &
PID=$!
while `kill -0 $PID 2>/dev/null`; do
ps -eo size,pid,user,pcpu,command --sort -size | awk '{ line=1 ; hr=$1/1024 ; printf("%13.2f Mb ",hr); } { for ( x=4 ; x<=NF ; x++ ) { printf("%s ",$x) } print "" }' | grep -v grep | grep -v $0 | grep $PROG
done

94
bench/p7-bench-mt.cpp Normal file
View File

@@ -0,0 +1,94 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <atomic>
#include <chrono>
#include <functional>
#include <iostream>
#include <memory>
#include <thread>
#include <vector>
#include "P7_Trace.h"
int main(int argc, char *argv[])
{
using namespace std::chrono;
using clock = steady_clock;
int thread_count = 10;
if (argc > 1)
thread_count = std::atoi(argv[1]);
int howmany = 1000000;
IP7_Trace::hModule module = NULL;
// create P7 client object
std::unique_ptr<IP7_Client, std::function<void(IP7_Client *)>> client(
P7_Create_Client(TM("/P7.Pool=1024 /P7.Sink=FileTxt /P7.Dir=logs/p7-bench-mt")), [&](IP7_Client *ptr) {
if (ptr)
ptr->Release();
});
if (!client)
{
std::cout << "Can't create IP7_Client" << std::endl;
return 1;
}
// create P7 trace object 1
std::unique_ptr<IP7_Trace, std::function<void(IP7_Trace *)>> trace(
P7_Create_Trace(client.get(), TM("Trace channel 1")), [&](IP7_Trace *ptr) {
if (ptr)
ptr->Release();
});
if (!trace)
{
std::cout << "Can't create IP7_Trace" << std::endl;
return 1;
}
trace->Register_Thread(TM("Application"), 0);
trace->Register_Module(TM("Main"), &module);
std::atomic<int> msg_counter{0};
std::vector<std::thread> threads;
auto start = clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]() {
trace->Register_Thread(TM("Application"), t + 1);
while (true)
{
int counter = ++msg_counter;
if (counter > howmany)
break;
trace->P7_INFO(module, TM("p7 message #%d: This is some text for your pleasure"), counter);
}
trace->Register_Thread(TM("Application"), t + 1);
}));
}
for (auto &t : threads)
{
t.join();
}
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany / deltaf;
std::cout << "Total: " << howmany << std::endl;
std::cout << "Threads: " << thread_count << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl;
trace->Unregister_Thread(0);
return 0;
}

66
bench/p7-bench.cpp Normal file
View File

@@ -0,0 +1,66 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <chrono>
#include <functional>
#include <iostream>
#include <memory>
#include "P7_Trace.h"
int main(int, char *[])
{
using namespace std::chrono;
using clock = steady_clock;
int howmany = 1000000;
IP7_Trace::hModule module = NULL;
// create P7 client object
std::unique_ptr<IP7_Client, std::function<void(IP7_Client *)>> client(
P7_Create_Client(TM("/P7.Pool=1024 /P7.Sink=FileTxt /P7.Dir=logs/p7-bench")), [&](IP7_Client *ptr) {
if (ptr)
ptr->Release();
});
if (!client)
{
std::cout << "Can't create IP7_Client" << std::endl;
return 1;
}
// create P7 trace object 1
std::unique_ptr<IP7_Trace, std::function<void(IP7_Trace *)>> trace(
P7_Create_Trace(client.get(), TM("Trace channel 1")), [&](IP7_Trace *ptr) {
if (ptr)
ptr->Release();
});
if (!trace)
{
std::cout << "Can't create IP7_Trace" << std::endl;
return 1;
}
trace->Register_Thread(TM("Application"), 0);
trace->Register_Module(TM("Main"), &module);
auto start = clock::now();
for (int i = 0; i < howmany; ++i)
trace->P7_INFO(module, TM("p7 message #%d: This is some text for your pleasure"), i);
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany / deltaf;
std::cout << "Total: " << howmany << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl;
trace->Unregister_Thread(0);
return 0;
}

61
bench/plog-bench-mt.cpp Normal file
View File

@@ -0,0 +1,61 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread>
#include <vector>
#include "plog/Log.h"
using namespace std;
int main(int argc, char *argv[])
{
using namespace std::chrono;
using clock = steady_clock;
int thread_count = 10;
if (argc > 1)
thread_count = atoi(argv[1]);
int howmany = 1000000;
plog::init(plog::debug, "logs/plog-bench-mt.log");
std::atomic<int> msg_counter{0};
vector<thread> threads;
auto start = clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]() {
while (true)
{
int counter = ++msg_counter;
if (counter > howmany)
break;
LOG_INFO << "plog message #" << counter << ": This is some text for your pleasure";
}
}));
}
for (auto &t : threads)
{
t.join();
}
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany / deltaf;
std::cout << "Total: " << howmany << std::endl;
std::cout << "Threads: " << thread_count << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl;
return 0;
}

34
bench/plog-bench.cpp Normal file
View File

@@ -0,0 +1,34 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <chrono>
#include <iostream>
#include <memory>
#include "plog/Log.h"
int main(int, char *[])
{
using namespace std::chrono;
using clock = steady_clock;
int howmany = 1000000;
plog::init(plog::debug, "logs/plog-bench.log");
auto start = clock::now();
for (int i = 0; i < howmany; ++i)
LOG_INFO << "plog message #" << i << ": This is some text for your pleasure";
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany / deltaf;
std::cout << "Total: " << howmany << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << std::endl;
return 0;
}

View File

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

View File

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

View File

@@ -3,18 +3,32 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <chrono>
#include <iostream>
#include "spdlog/spdlog.h"
int main(int, char* [])
int main(int, char *[])
{
int howmany = 1000000;
namespace spd = spdlog;
///Create a file rotating logger with 5mb size max and 3 rotated files
auto logger = spdlog::create<spd::sinks::simple_file_sink_st>("file_logger", "logs/spd-bench-st.txt", false);
using namespace std::chrono;
using clock = steady_clock;
logger->set_pattern("[%Y-%b-%d %T.%e]: %v");
for(int i = 0 ; i < howmany; ++i)
int howmany = 1000000;
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_st>("file_logger", "logs/spdlog-bench.log", false);
logger->set_pattern("[%Y-%m-%d %T.%F]: %L %v");
auto start = clock::now();
for (int i = 0; i < howmany; ++i)
logger->info("spdlog message #{} : This is some text for your pleasure", i);
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany / deltaf;
std::cout << "Total: " << howmany << std::endl;
std::cout << "Delta = " << std::fixed << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << std::fixed << rate << "/sec" << std::endl;
return 0;
}

View File

@@ -6,17 +6,16 @@
//
// bench.cpp : spdlog benchmarks
//
#include "spdlog/async_logger.h"
#include "spdlog/sinks/null_sink.h"
#include "spdlog/spdlog.h"
#include "utils.h"
#include <atomic>
#include <cstdlib> // EXIT_FAILURE
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include "spdlog/spdlog.h"
#include "spdlog/async_logger.h"
#include "spdlog/sinks/null_sink.h"
#include "utils.h"
using namespace std;
using namespace std::chrono;
@@ -24,11 +23,9 @@ using namespace spdlog;
using namespace spdlog::sinks;
using namespace utils;
size_t bench_as(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
int main(int argc, char* argv[])
int main(int argc, char *argv[])
{
int queue_size = 1048576;
@@ -39,14 +36,13 @@ int main(int argc, char* argv[])
try
{
if(argc > 1)
if (argc > 1)
howmany = atoi(argv[1]);
if (argc > 2)
threads = atoi(argv[2]);
threads = atoi(argv[2]);
if (argc > 3)
queue_size = atoi(argv[3]);
cout << "\n*******************************************************************************\n";
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " messages " << endl;
cout << "*******************************************************************************\n";
@@ -55,16 +51,15 @@ int main(int argc, char* argv[])
size_t total_rate = 0;
for(int i = 0; i < iters; ++i)
for (int i = 0; i < iters; ++i)
{
//auto as = spdlog::daily_logger_st("as", "logs/daily_async");
// auto as = spdlog::daily_logger_st("as", "logs/daily_async");
auto as = spdlog::create<null_sink_st>("async(null-sink)");
total_rate+= bench_as(howmany, as, threads);
total_rate += bench_as(howmany, as, threads);
spdlog::drop("async(null-sink)");
}
std::cout << endl;
std::cout << "Avg rate: " << format(total_rate/iters) << "/sec" <<std::endl;
std::cout << "Avg rate: " << format(total_rate / iters) << "/sec" << std::endl;
}
catch (std::exception &ex)
{
@@ -75,37 +70,33 @@ int main(int argc, char* argv[])
return EXIT_SUCCESS;
}
//return rate/sec
// return rate/sec
size_t bench_as(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
{
cout << log->name() << "...\t\t" << flush;
std::atomic<int > msg_counter {0};
std::atomic<int> msg_counter{0};
vector<thread> threads;
auto start = system_clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
for(;;)
threads.push_back(std::thread([&]() {
for (;;)
{
int counter = ++msg_counter;
if (counter > howmany) break;
if (counter > howmany)
break;
log->info("Hello logger: msg number {}", counter);
}
}));
}
for(auto &t:threads)
for (auto &t : threads)
{
t.join();
};
}
auto delta = system_clock::now() - start;
auto delta_d = duration_cast<duration<double>> (delta).count();
auto delta_d = duration_cast<duration<double>>(delta).count();
auto per_sec = size_t(howmany / delta_d);
cout << format(per_sec) << "/sec" << endl;
return per_sec;

View File

@@ -5,15 +5,14 @@
#pragma once
#include <sstream>
#include <iomanip>
#include <locale>
#include <sstream>
namespace utils
{
namespace utils {
template<typename T>
inline std::string format(const T& value)
inline std::string format(const T &value)
{
static std::locale loc("");
std::stringstream ss;
@@ -23,7 +22,7 @@ inline std::string format(const T& value)
}
template<>
inline std::string format(const double & value)
inline std::string format(const double &value)
{
static std::locale loc("");
std::stringstream ss;
@@ -32,4 +31,4 @@ inline std::string format(const double & value)
return ss.str();
}
}
} // namespace utils

View File

@@ -1,49 +1,49 @@
# *************************************************************************/
# * Copyright (c) 2015 Ruslan Baratov. */
# * */
# * Permission is hereby granted, free of charge, to any person obtaining */
# * a copy of this software and associated documentation files (the */
# * "Software"), to deal in the Software without restriction, including */
# * without limitation the rights to use, copy, modify, merge, publish, */
# * distribute, sublicense, and/or sell copies of the Software, and to */
# * permit persons to whom the Software is furnished to do so, subject to */
# * the following conditions: */
# * */
# * The above copyright notice and this permission notice shall be */
# * included in all copies or substantial portions of the Software. */
# * */
# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
# *************************************************************************/
cmake_minimum_required(VERSION 3.0)
project(SpdlogExamples)
if(TARGET spdlog)
# Part of the main project
add_library(spdlog::spdlog ALIAS spdlog)
else()
# Stand-alone build
find_package(spdlog CONFIG REQUIRED)
endif()
find_package(Threads)
add_executable(example example.cpp)
target_link_libraries(example spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT})
add_executable(benchmark bench.cpp)
target_link_libraries(benchmark spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT})
add_executable(multisink multisink.cpp)
target_link_libraries(multisink spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT})
enable_testing()
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
add_test(NAME RunExample COMMAND example)
add_test(NAME RunBenchmark COMMAND benchmark)
# *************************************************************************/
# * Copyright (c) 2015 Ruslan Baratov. */
# * */
# * Permission is hereby granted, free of charge, to any person obtaining */
# * a copy of this software and associated documentation files (the */
# * "Software"), to deal in the Software without restriction, including */
# * without limitation the rights to use, copy, modify, merge, publish, */
# * distribute, sublicense, and/or sell copies of the Software, and to */
# * permit persons to whom the Software is furnished to do so, subject to */
# * the following conditions: */
# * */
# * The above copyright notice and this permission notice shall be */
# * included in all copies or substantial portions of the Software. */
# * */
# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
# *************************************************************************/
cmake_minimum_required(VERSION 3.1)
project(SpdlogExamples CXX)
if(TARGET spdlog)
# Part of the main project
add_library(spdlog::spdlog ALIAS spdlog)
else()
# Stand-alone build
find_package(spdlog CONFIG REQUIRED)
endif()
find_package(Threads REQUIRED)
add_executable(example example.cpp)
target_link_libraries(example spdlog::spdlog Threads::Threads)
add_executable(benchmark bench.cpp)
target_link_libraries(benchmark spdlog::spdlog Threads::Threads)
add_executable(multisink multisink.cpp)
target_link_libraries(multisink spdlog::spdlog Threads::Threads)
enable_testing()
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
add_test(NAME RunExample COMMAND example)
add_test(NAME RunBenchmark COMMAND benchmark)

View File

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

View File

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

View File

@@ -1,32 +1,32 @@
CXX ?= g++
CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
CXX_RELEASE_FLAGS = -O3
CXX_DEBUG_FLAGS= -g
all: example bench
debug: example-debug bench-debug
example: example.cpp
$(CXX) example.cpp -o example $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
bench: bench.cpp
$(CXX) bench.cpp -o bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
example-debug: example.cpp
$(CXX) example.cpp -o example-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
bench-debug: bench.cpp
$(CXX) bench.cpp -o bench-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
clean:
rm -f *.o logs/*.txt example example-debug bench bench-debug
rebuild: clean all
rebuild-debug: clean debug
CXX ?= g++
CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -Wshadow -pedantic -std=gnu++0x -pthread -Wl,--no-as-needed -I../include
CXX_RELEASE_FLAGS = -O3
CXX_DEBUG_FLAGS= -g
all: example bench
debug: example-debug bench-debug
example: example.cpp
$(CXX) example.cpp -o example $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
bench: bench.cpp
$(CXX) bench.cpp -o bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
example-debug: example.cpp
$(CXX) example.cpp -o example-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
bench-debug: bench.cpp
$(CXX) bench.cpp -o bench-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
clean:
rm -f *.o logs/*.txt example example-debug bench bench-debug
rebuild: clean all
rebuild-debug: clean debug

View File

@@ -6,18 +6,17 @@
//
// bench.cpp : spdlog benchmarks
//
#include "spdlog/async_logger.h"
#include "spdlog/sinks/file_sinks.h"
#include "spdlog/sinks/null_sink.h"
#include "spdlog/spdlog.h"
#include "utils.h"
#include <atomic>
#include <cstdlib> // EXIT_FAILURE
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include "spdlog/spdlog.h"
#include "spdlog/async_logger.h"
#include "spdlog/sinks/file_sinks.h"
#include "spdlog/sinks/null_sink.h"
#include "utils.h"
using namespace std;
using namespace std::chrono;
@@ -25,11 +24,10 @@ using namespace spdlog;
using namespace spdlog::sinks;
using namespace utils;
void bench(int howmany, std::shared_ptr<spdlog::logger> log);
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
int main(int argc, char* argv[])
int main(int argc, char *argv[])
{
int queue_size = 1048576;
@@ -41,33 +39,31 @@ int main(int argc, char* argv[])
try
{
if(argc > 1)
if (argc > 1)
howmany = atoi(argv[1]);
if (argc > 2)
threads = atoi(argv[2]);
threads = atoi(argv[2]);
if (argc > 3)
queue_size = atoi(argv[3]);
cout << "*******************************************************************************\n";
cout << "Single thread, " << format(howmany) << " iterations" << endl;
cout << "Single thread, " << format(howmany) << " iterations" << endl;
cout << "*******************************************************************************\n";
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st", file_size, rotating_files);
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
bench(howmany, rotating_st);
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st");
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
bench(howmany, daily_st);
bench(howmany, spdlog::create<null_sink_st>("null_st"));
cout << "\n*******************************************************************************\n";
cout << threads << " threads sharing same logger, " << format(howmany) << " iterations" << endl;
cout << threads << " threads sharing same logger, " << format(howmany) << " iterations" << endl;
cout << "*******************************************************************************\n";
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt", file_size, rotating_files);
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files);
bench_mt(howmany, rotating_mt, threads);
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt");
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
bench_mt(howmany, daily_mt, threads);
bench(howmany, spdlog::create<null_sink_st>("null_mt"));
@@ -75,12 +71,11 @@ int main(int argc, char* argv[])
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations " << endl;
cout << "*******************************************************************************\n";
spdlog::set_async_mode(queue_size);
for(int i = 0; i < 3; ++i)
for (int i = 0; i < 3; ++i)
{
auto as = spdlog::daily_logger_st("as", "logs/daily_async");
auto as = spdlog::daily_logger_st("as", "logs/daily_async.log");
bench_mt(howmany, as, threads);
spdlog::drop("as");
}
@@ -94,7 +89,6 @@ int main(int argc, char* argv[])
return EXIT_SUCCESS;
}
void bench(int howmany, std::shared_ptr<spdlog::logger> log)
{
cout << log->name() << "...\t\t" << flush;
@@ -104,41 +98,37 @@ void bench(int howmany, std::shared_ptr<spdlog::logger> log)
log->info("Hello logger: msg number {}", i);
}
auto delta = system_clock::now() - start;
auto delta_d = duration_cast<duration<double>> (delta).count();
cout << format(int(howmany / delta_d)) << "/sec" << endl;
auto delta_d = duration_cast<duration<double>>(delta).count();
cout << "Elapsed: " << delta_d << "\t" << format(int(howmany / delta_d)) << "/sec" << endl;
}
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
{
cout << log->name() << "...\t\t" << flush;
std::atomic<int > msg_counter {0};
std::atomic<int> msg_counter{0};
vector<thread> threads;
auto start = system_clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
for(;;)
threads.push_back(std::thread([&]() {
for (;;)
{
int counter = ++msg_counter;
if (counter > howmany) break;
if (counter > howmany)
break;
log->info("Hello logger: msg number {}", counter);
}
}));
}
for(auto &t:threads)
for (auto &t : threads)
{
t.join();
};
auto delta = system_clock::now() - start;
auto delta_d = duration_cast<duration<double>> (delta).count();
cout << format(int(howmany / delta_d)) << "/sec" << endl;
auto delta_d = duration_cast<duration<double>>(delta).count();
cout << "Elapsed: " << delta_d << "\t" << format(int(howmany / delta_d)) << "/sec" << endl;
}

View File

@@ -22,7 +22,7 @@ void user_defined_example();
void err_handler_example();
namespace spd = spdlog;
int main(int, char*[])
int main(int, char *[])
{
try
{
@@ -31,9 +31,6 @@ int main(int, char*[])
console->info("Welcome to spdlog!");
console->error("Some error message with arg{}..", 1);
// Conditional logging example
console->info_if(true, "Welcome to spdlog conditional logging!");
// Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12);
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
@@ -41,44 +38,40 @@ int main(int, char*[])
console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");
SPDLOG_DEBUG_IF(console, true, "This is a debug log");
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
// Create basic file logger (not rotated)
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic");
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt");
my_logger->info("Some log message");
// Create a file rotating logger with 5mb size max and 3 rotated files
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3);
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
for (int i = 0; i < 10; ++i)
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
{
rotating_logger->info("{} * {} equals {:>10}", i, i, i * i);
}
// Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
// trigger flush if the log severity is error or higher
daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44);
// Customize msg format for all messages
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
rotating_logger->info("This is another message with custom format");
spd::set_pattern("[%^+++%$] [%H:%M:%S %z] [thread %t] %v");
console->info("This an info message with custom format");
console->error("This an error message with custom format");
// Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info
console->debug("This message shold not be displayed!");
spd::set_level(spd::level::info); // Set global log level to info
console->debug("This message should not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message shold be displayed..");
console->debug("This message should be displayed..");
// Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
SPDLOG_DEBUG_IF(console, true, "This is a debug log");
// Asynchronous logging is very fast..
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
@@ -97,16 +90,13 @@ int main(int, char*[])
err_handler_example();
// Apply a function on all registered loggers
spd::apply_all([&](std::shared_ptr<spdlog::logger> l)
{
l->info("End of example.");
});
spd::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); });
// Release and close all loggers
spdlog::drop_all();
}
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
catch (const spd::spdlog_ex& ex)
catch (const spd::spdlog_ex &ex)
{
std::cout << "Log init failed: " << ex.what() << std::endl;
return 1;
@@ -115,15 +105,16 @@ int main(int, char*[])
void async_example()
{
size_t q_size = 4096; //queue size must be power of 2
size_t q_size = 4096;
spdlog::set_async_mode(q_size);
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log");
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
for (int i = 0; i < 100; ++i)
{
async_file->info("Async message #{}", i);
}
}
//syslog example (linux/osx/freebsd)
// syslog example (linux/osx/freebsd)
void syslog_example()
{
#ifdef SPDLOG_ENABLE_SYSLOG
@@ -148,27 +139,24 @@ struct my_type
{
int i;
template<typename OStream>
friend OStream& operator<<(OStream& os, const my_type &c)
friend OStream &operator<<(OStream &os, const my_type &c)
{
return os << "[my_type i="<<c.i << "]";
return os << "[my_type i=" << c.i << "]";
}
};
#include "spdlog/fmt/ostr.h" // must be included
void user_defined_example()
{
spd::get("console")->info("user defined type: {}", my_type { 14 });
spd::get("console")->info("user defined type: {}", my_type{14});
}
//
//custom error handler
// custom error handler
//
void err_handler_example()
{
//can be set globaly or per logger(logger->set_error_handler(..))
spdlog::set_error_handler([](const std::string& msg)
{
std::cerr << "my err handler: " << msg << std::endl;
});
// can be set globaly or per logger(logger->set_error_handler(..))
spdlog::set_error_handler([](const std::string &msg) { std::cerr << "my err handler: " << msg << std::endl; });
spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
}

View File

@@ -1,10 +1,83 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
# Visual Studio 15
VisualStudioVersion = 15.0.27428.2037
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example", "example.vcxproj", "{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "spdlog", "spdlog", "{319A0767-E66D-4DD0-8BEF-E29E891BA836}"
ProjectSection(SolutionItems) = preProject
..\include\spdlog\async_logger.h = ..\include\spdlog\async_logger.h
..\include\spdlog\common.h = ..\include\spdlog\common.h
..\include\spdlog\formatter.h = ..\include\spdlog\formatter.h
..\include\spdlog\logger.h = ..\include\spdlog\logger.h
..\include\spdlog\spdlog.h = ..\include\spdlog\spdlog.h
..\include\spdlog\tweakme.h = ..\include\spdlog\tweakme.h
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "contrib", "contrib", "{F1B153FB-7638-4F2B-B6FF-F56859D381F9}"
ProjectSection(SolutionItems) = preProject
..\include\spdlog\contrib\README.md = ..\include\spdlog\contrib\README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sinks", "sinks", "{9B3669F7-6D70-4699-9067-451E9298BA53}"
ProjectSection(SolutionItems) = preProject
..\include\spdlog\contrib\sinks\.gitignore = ..\include\spdlog\contrib\sinks\.gitignore
..\include\spdlog\contrib\sinks\step_file_sink.h = ..\include\spdlog\contrib\sinks\step_file_sink.h
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "details", "details", "{579FDBF1-8FCD-4F1D-99F1-540F2EFF95E0}"
ProjectSection(SolutionItems) = preProject
..\include\spdlog\details\async_log_helper.h = ..\include\spdlog\details\async_log_helper.h
..\include\spdlog\details\async_logger_impl.h = ..\include\spdlog\details\async_logger_impl.h
..\include\spdlog\details\file_helper.h = ..\include\spdlog\details\file_helper.h
..\include\spdlog\details\log_msg.h = ..\include\spdlog\details\log_msg.h
..\include\spdlog\details\logger_impl.h = ..\include\spdlog\details\logger_impl.h
..\include\spdlog\details\mpmc_blocking_q.h = ..\include\spdlog\details\mpmc_blocking_q.h
..\include\spdlog\details\null_mutex.h = ..\include\spdlog\details\null_mutex.h
..\include\spdlog\details\os.h = ..\include\spdlog\details\os.h
..\include\spdlog\details\pattern_formatter_impl.h = ..\include\spdlog\details\pattern_formatter_impl.h
..\include\spdlog\details\registry.h = ..\include\spdlog\details\registry.h
..\include\spdlog\details\spdlog_impl.h = ..\include\spdlog\details\spdlog_impl.h
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fmt", "fmt", "{2034E575-9375-4AE7-B667-AF4A359F1483}"
ProjectSection(SolutionItems) = preProject
..\include\spdlog\fmt\fmt.h = ..\include\spdlog\fmt\fmt.h
..\include\spdlog\fmt\ostr.h = ..\include\spdlog\fmt\ostr.h
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "bundled", "bundled", "{16763E99-3CC7-4C46-8F79-259356FF0B37}"
ProjectSection(SolutionItems) = preProject
..\include\spdlog\fmt\bundled\format.cc = ..\include\spdlog\fmt\bundled\format.cc
..\include\spdlog\fmt\bundled\format.h = ..\include\spdlog\fmt\bundled\format.h
..\include\spdlog\fmt\bundled\LICENSE.rst = ..\include\spdlog\fmt\bundled\LICENSE.rst
..\include\spdlog\fmt\bundled\ostream.cc = ..\include\spdlog\fmt\bundled\ostream.cc
..\include\spdlog\fmt\bundled\ostream.h = ..\include\spdlog\fmt\bundled\ostream.h
..\include\spdlog\fmt\bundled\posix.cc = ..\include\spdlog\fmt\bundled\posix.cc
..\include\spdlog\fmt\bundled\posix.h = ..\include\spdlog\fmt\bundled\posix.h
..\include\spdlog\fmt\bundled\printf.cc = ..\include\spdlog\fmt\bundled\printf.cc
..\include\spdlog\fmt\bundled\printf.h = ..\include\spdlog\fmt\bundled\printf.h
..\include\spdlog\fmt\bundled\time.h = ..\include\spdlog\fmt\bundled\time.h
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sinks", "sinks", "{13310FA4-52E7-46BA-B071-B72B1D8E44D9}"
ProjectSection(SolutionItems) = preProject
..\include\spdlog\sinks\android_sink.h = ..\include\spdlog\sinks\android_sink.h
..\include\spdlog\sinks\ansicolor_sink.h = ..\include\spdlog\sinks\ansicolor_sink.h
..\include\spdlog\sinks\base_sink.h = ..\include\spdlog\sinks\base_sink.h
..\include\spdlog\sinks\dist_sink.h = ..\include\spdlog\sinks\dist_sink.h
..\include\spdlog\sinks\file_sinks.h = ..\include\spdlog\sinks\file_sinks.h
..\include\spdlog\sinks\msvc_sink.h = ..\include\spdlog\sinks\msvc_sink.h
..\include\spdlog\sinks\null_sink.h = ..\include\spdlog\sinks\null_sink.h
..\include\spdlog\sinks\ostream_sink.h = ..\include\spdlog\sinks\ostream_sink.h
..\include\spdlog\sinks\sink.h = ..\include\spdlog\sinks\sink.h
..\include\spdlog\sinks\stdout_sinks.h = ..\include\spdlog\sinks\stdout_sinks.h
..\include\spdlog\sinks\syslog_sink.h = ..\include\spdlog\sinks\syslog_sink.h
..\include\spdlog\sinks\wincolor_sink.h = ..\include\spdlog\sinks\wincolor_sink.h
..\include\spdlog\sinks\windebug_sink.h = ..\include\spdlog\sinks\windebug_sink.h
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@@ -23,4 +96,15 @@ Global
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{F1B153FB-7638-4F2B-B6FF-F56859D381F9} = {319A0767-E66D-4DD0-8BEF-E29E891BA836}
{9B3669F7-6D70-4699-9067-451E9298BA53} = {F1B153FB-7638-4F2B-B6FF-F56859D381F9}
{579FDBF1-8FCD-4F1D-99F1-540F2EFF95E0} = {319A0767-E66D-4DD0-8BEF-E29E891BA836}
{2034E575-9375-4AE7-B667-AF4A359F1483} = {319A0767-E66D-4DD0-8BEF-E29E891BA836}
{16763E99-3CC7-4C46-8F79-259356FF0B37} = {2034E575-9375-4AE7-B667-AF4A359F1483}
{13310FA4-52E7-46BA-B071-B72B1D8E44D9} = {319A0767-E66D-4DD0-8BEF-E29E891BA836}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8C12721F-513C-4922-B2F8-B9F2403412B4}
EndGlobalSection
EndGlobal

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
@@ -13,56 +13,23 @@
<ItemGroup>
<ClCompile Include="example.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\include\spdlog\async_logger.h" />
<ClInclude Include="..\include\spdlog\common.h" />
<ClInclude Include="..\include\spdlog\details\async_logger_impl.h" />
<ClInclude Include="..\include\spdlog\details\async_log_helper.h" />
<ClInclude Include="..\include\spdlog\details\file_helper.h" />
<ClInclude Include="..\include\spdlog\details\logger_impl.h" />
<ClInclude Include="..\include\spdlog\details\log_msg.h" />
<ClInclude Include="..\include\spdlog\details\mpmc_bounded_q.h" />
<ClInclude Include="..\include\spdlog\details\null_mutex.h" />
<ClInclude Include="..\include\spdlog\details\os.h" />
<ClInclude Include="..\include\spdlog\details\pattern_formatter_impl.h" />
<ClInclude Include="..\include\spdlog\details\registry.h" />
<ClInclude Include="..\include\spdlog\details\spdlog_impl.h" />
<ClInclude Include="..\include\spdlog\fmt\fmt.h" />
<ClInclude Include="..\include\spdlog\fmt\ostr.h" />
<ClInclude Include="..\include\spdlog\formatter.h" />
<ClInclude Include="..\include\spdlog\logger.h" />
<ClInclude Include="..\include\spdlog\sinks\android_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\ansicolor_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\base_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\dist_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\file_sinks.h" />
<ClInclude Include="..\include\spdlog\sinks\msvc_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\null_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\ostream_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\sink.h" />
<ClInclude Include="..\include\spdlog\sinks\stdout_sinks.h" />
<ClInclude Include="..\include\spdlog\sinks\syslog_sink.h" />
<ClInclude Include="..\include\spdlog\sinks\wincolor_sink.h" />
<ClInclude Include="..\include\spdlog\spdlog.h" />
<ClInclude Include="..\include\spdlog\tweakme.h" />
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>.</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>

View File

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

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

@@ -0,0 +1,157 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
//
// spdlog usage example
//
//
#define SPDLOG_TRACE_ON
#define SPDLOG_DEBUG_ON
#include "spdlog/spdlog.h"
#include <iostream>
#include <memory>
void async_example();
void syslog_example();
void android_example();
void user_defined_example();
void err_handler_example();
namespace spd = spdlog;
int main(int, char *[])
{
try
{
// Console logger with color
auto console = spd::stdout_color_mt("console");
console->info("Welcome to spdlog!");
console->error("Some error message with arg{}..", 1);
// Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12);
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
console->info("Support for floats {:03.2f}", 1.23456);
console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
// Create basic file logger (not rotated)
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt");
my_logger->info("Some log message");
// Create a file rotating logger with 5mb size max and 3 rotated files
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
for (int i = 0; i < 10; ++i)
rotating_logger->info("{} * {} equals {:>10}", i, i, i * i);
// Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
// trigger flush if the log severity is error or higher
daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44);
// Customize msg format for all messages
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
rotating_logger->info("This is another message with custom format");
// Runtime log levels
spd::set_level(spd::level::info); // Set global log level to info
console->debug("This message should not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message should be displayed..");
// Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
// Asynchronous logging is very fast..
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
async_example();
// syslog example. linux/osx only
syslog_example();
// android example. compile with NDK
android_example();
// Log user-defined types example
user_defined_example();
// Change default log error handler
err_handler_example();
// Apply a function on all registered loggers
spd::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); });
// Release and close all loggers
spdlog::drop_all();
}
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
catch (const spd::spdlog_ex &ex)
{
std::cout << "Log init failed: " << ex.what() << std::endl;
return 1;
}
}
void async_example()
{
size_t q_size = 4096; // queue size must be power of 2
spdlog::set_async_mode(q_size);
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
for (int i = 0; i < 100; ++i)
async_file->info("Async message #{}", i);
}
// syslog example (linux/osx/freebsd)
void syslog_example()
{
#ifdef SPDLOG_ENABLE_SYSLOG
std::string ident = "spdlog-example";
auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID);
syslog_logger->warn("This is warning that will end up in syslog.");
#endif
}
// Android example
void android_example()
{
#if defined(__ANDROID__)
std::string tag = "spdlog-android";
auto android_logger = spd::android_logger("android", tag);
android_logger->critical("Use \"adb shell logcat\" to view this message.");
#endif
}
// user defined types logging by implementing operator<<
struct my_type
{
int i;
template<typename OStream>
friend OStream &operator<<(OStream &os, const my_type &c)
{
return os << "[my_type i=" << c.i << "]";
}
};
#include "spdlog/fmt/ostr.h" // must be included
void user_defined_example()
{
spd::get("console")->info("user defined type: {}", my_type{14});
}
//
// custom error handler
//
void err_handler_example()
{
// can be set globaly or per logger(logger->set_error_handler(..))
spdlog::set_error_handler([](const std::string &msg) { std::cerr << "my err handler: " << msg << std::endl; });
spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
}

View File

@@ -4,7 +4,7 @@
#include <memory>
namespace spd = spdlog;
int main(int, char*[])
int main(int, char *[])
{
bool enable_debug = true;
try
@@ -13,24 +13,24 @@ int main(int, char*[])
// This means that the same log_msg is forwarded to multiple sinks;
// Each sink can have it's own log level and a message will be logged.
std::vector<spdlog::sink_ptr> sinks;
sinks.push_back( std::make_shared<spdlog::sinks::stdout_sink_mt>() );
sinks.push_back( std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_regular_file.txt") );
sinks.push_back( std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_debug_file.txt") );
sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_mt>());
sinks.push_back(std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_regular_file.txt"));
sinks.push_back(std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_debug_file.txt"));
spdlog::logger console_multisink("multisink", sinks.begin(), sinks.end() );
console_multisink.set_level( spdlog::level::warn);
spdlog::logger console_multisink("multisink", sinks.begin(), sinks.end());
console_multisink.set_level(spdlog::level::warn);
sinks[0]->set_level( spdlog::level::trace); // console. Allow everything. Default value
sinks[1]->set_level( spdlog::level::trace); // regular file. Allow everything. Default value
sinks[2]->set_level( spdlog::level::off); // regular file. Ignore everything.
sinks[0]->set_level(spdlog::level::trace); // console. Allow everything. Default value
sinks[1]->set_level(spdlog::level::trace); // regular file. Allow everything. Default value
sinks[2]->set_level(spdlog::level::off); // regular file. Ignore everything.
console_multisink.warn("warn: will print only on console and regular file");
if( enable_debug )
if (enable_debug)
{
console_multisink.set_level( spdlog::level::debug); // level of the logger
sinks[1]->set_level( spdlog::level::debug); // regular file
sinks[2]->set_level( spdlog::level::debug); // debug file
console_multisink.set_level(spdlog::level::debug); // level of the logger
sinks[1]->set_level(spdlog::level::debug); // regular file
sinks[2]->set_level(spdlog::level::debug); // debug file
}
console_multisink.debug("Debug: you should see this on console and both files");
@@ -38,10 +38,9 @@ int main(int, char*[])
spdlog::drop_all();
}
// 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)
{
std::cout << "Log init failed: " << ex.what() << std::endl;
return 1;
}
}

View File

@@ -5,15 +5,14 @@
#pragma once
#include <sstream>
#include <iomanip>
#include <locale>
#include <sstream>
namespace utils
{
namespace utils {
template<typename T>
inline std::string format(const T& value)
inline std::string format(const T &value)
{
static std::locale loc("");
std::stringstream ss;
@@ -23,7 +22,7 @@ inline std::string format(const T& value)
}
template<>
inline std::string format(const double & value)
inline std::string format(const double &value)
{
static std::locale loc("");
std::stringstream ss;
@@ -32,4 +31,4 @@ inline std::string format(const double & value)
return ss.str();
}
}
} // namespace utils

9
format.sh Executable file
View File

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

View File

@@ -15,68 +15,58 @@
// 3. will throw spdlog_ex upon log exceptions
// Upon destruction, logs all remaining messages in the queue before destructing..
#include "spdlog/common.h"
#include "spdlog/logger.h"
#include "common.h"
#include "logger.h"
#include <chrono>
#include <functional>
#include <string>
#include <memory>
#include <string>
namespace spdlog
{
namespace spdlog {
namespace details
{
namespace details {
class async_log_helper;
}
class async_logger SPDLOG_FINAL :public logger
class async_logger SPDLOG_FINAL : public logger
{
public:
template<class It>
async_logger(const std::string& name,
const It& begin,
const It& end,
size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()>& worker_teardown_cb = nullptr);
async_logger(const std::string &logger_name, const It &begin, const It &end, size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()> &worker_warmup_cb = nullptr,
const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()> &worker_teardown_cb = nullptr);
async_logger(const std::string& logger_name,
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);
async_logger(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);
async_logger(const std::string& logger_name,
sink_ptr single_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);
async_logger(const std::string &logger_name, sink_ptr single_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);
//Wait for the queue to be empty, and flush synchronously
//Warning: this can potentially last forever as we wait it to complete
// Wait for the queue to be empty, and flush synchronously
// Warning: this can potentially last forever as we wait it to complete
void flush() override;
// Error handler
virtual void set_error_handler(log_err_handler) override;
virtual log_err_handler error_handler() override;
void set_error_handler(log_err_handler) override;
log_err_handler error_handler() override;
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_pattern(const std::string& pattern, pattern_time_type pattern_time) override;
void _set_pattern(const std::string &pattern, pattern_time_type pattern_time) override;
private:
std::unique_ptr<details::async_log_helper> _async_log_helper;
};
}
} // namespace spdlog
#include "spdlog/details/async_logger_impl.h"
#include "details/async_logger_impl.h"

View File

@@ -5,22 +5,27 @@
#pragma once
#include <string>
#include <initializer_list>
#include <chrono>
#include <memory>
#define SPDLOG_VERSION "0.17.0"
#include "tweakme.h"
#include <atomic>
#include <chrono>
#include <exception>
#include<functional>
#include <functional>
#include <initializer_list>
#include <memory>
#include <string>
#include <unordered_map>
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
#include <codecvt>
#include <locale>
#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)
#define SPDLOG_NOEXCEPT throw()
#define SPDLOG_CONSTEXPR
@@ -29,12 +34,14 @@
#define SPDLOG_CONSTEXPR constexpr
#endif
// See tweakme.h
#if !defined(SPDLOG_FINAL)
// 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))
#elif defined(_MSC_VER)
#define SPDLOG_DEPRECATED __declspec(deprecated)
@@ -42,22 +49,19 @@
#define SPDLOG_DEPRECATED
#endif
#include "fmt/fmt.h"
#include "spdlog/fmt/fmt.h"
namespace spdlog
{
namespace spdlog {
class formatter;
namespace sinks
{
namespace sinks {
class sink;
}
using log_clock = std::chrono::system_clock;
using sink_ptr = std::shared_ptr < sinks::sink >;
using sinks_init_list = std::initializer_list < sink_ptr >;
using sink_ptr = std::shared_ptr<sinks::sink>;
using sinks_init_list = std::initializer_list<sink_ptr>;
using formatter_ptr = std::shared_ptr<spdlog::formatter>;
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
using level_t = details::null_atomic_int;
@@ -67,10 +71,9 @@ using level_t = std::atomic<int>;
using log_err_handler = std::function<void(const std::string &err_msg)>;
//Log level enum
namespace level
{
typedef enum
// Log level enum
namespace level {
enum level_enum
{
trace = 0,
debug = 1,
@@ -79,33 +82,51 @@ typedef enum
err = 4,
critical = 5,
off = 6
} level_enum;
};
#if !defined(SPDLOG_LEVEL_NAMES)
#define SPDLOG_LEVEL_NAMES { "trace", "debug", "info", "warning", "error", "critical", "off" };
#define SPDLOG_LEVEL_NAMES \
{ \
"trace", "debug", "info", "warning", "error", "critical", "off" \
}
#endif
static const char* level_names[] SPDLOG_LEVEL_NAMES
static const char *level_names[] SPDLOG_LEVEL_NAMES;
static const char* short_level_names[] { "T", "D", "I", "W", "E", "C", "O" };
static const char *short_level_names[]{"T", "D", "I", "W", "E", "C", "O"};
inline const char* to_str(spdlog::level::level_enum l)
inline const char *to_str(spdlog::level::level_enum l)
{
return level_names[l];
}
inline const char* to_short_str(spdlog::level::level_enum l)
inline const char *to_short_str(spdlog::level::level_enum l)
{
return short_level_names[l];
}
} //level
inline spdlog::level::level_enum from_str(const std::string &name)
{
static std::unordered_map<std::string, level_enum> name_to_level = // map string->level
{{level_names[0], level::trace}, // trace
{level_names[1], level::debug}, // debug
{level_names[2], level::info}, // info
{level_names[3], level::warn}, // warn
{level_names[4], level::err}, // err
{level_names[5], level::critical}, // critical
{level_names[6], level::off}}; // off
auto lvl_it = name_to_level.find(name);
return lvl_it != name_to_level.end() ? lvl_it->second : level::off;
}
using level_hasher = std::hash<int>;
} // namespace level
//
// Async overflow policy - block by default.
//
enum class async_overflow_policy
{
block_retry, // Block / yield / sleep until message can be enqueued
block_retry, // Block / yield / sleep until message can be enqueued
discard_log_msg // Discard the message it enqueue fails
};
@@ -122,29 +143,31 @@ enum class pattern_time_type
//
// Log exception
//
namespace details
{
namespace os
{
namespace details {
namespace os {
std::string errno_str(int err_num);
}
}
class spdlog_ex: public std::exception
} // namespace details
class spdlog_ex : public std::exception
{
public:
spdlog_ex(const std::string& msg):_msg(msg)
{}
spdlog_ex(const std::string& msg, int last_errno)
explicit spdlog_ex(std::string msg)
: _msg(std::move(msg))
{
}
spdlog_ex(const std::string &msg, int last_errno)
{
_msg = msg + ": " + details::os::errno_str(last_errno);
}
const char* what() const SPDLOG_NOEXCEPT override
const char *what() const SPDLOG_NOEXCEPT override
{
return _msg.c_str();
}
private:
std::string _msg;
};
//
@@ -156,5 +179,4 @@ using filename_t = std::wstring;
using filename_t = std::string;
#endif
} //spdlog
} // namespace spdlog

View File

@@ -0,0 +1 @@
Please put here your contribs. Popular contribs will be moved to main tree after stablization

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,163 @@
#pragma once
#include "../../details/file_helper.h"
#include "../../details/null_mutex.h"
#include "../../fmt/fmt.h"
#include "../../sinks/base_sink.h"
#include <algorithm>
#include <cerrno>
#include <chrono>
#include <cstdio>
#include <ctime>
#include <mutex>
#include <string>
// Example for spdlog.h
//
// Create a file logger which creates new files with a specified time step and fixed file size:
//
// std::shared_ptr<logger> step_logger_mt(const std::string &logger_name, const filename_t &filename, unsigned seconds = 60, const
// filename_t &tmp_ext = ".tmp", unsigned max_file_size = std::numeric_limits<unsigned>::max()); std::shared_ptr<logger>
// step_logger_st(const std::string &logger_name, const filename_t &filename, unsigned seconds = 60, const filename_t &tmp_ext = ".tmp",
// unsigned max_file_size = std::numeric_limits<unsigned>::max());
// Example for spdlog_impl.h
// Create a file logger that creates new files with a specified increment
// inline std::shared_ptr<spdlog::logger> spdlog::step_logger_mt(
// const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, const filename_t &tmp_ext, unsigned max_file_size)
// {
// return create<spdlog::sinks::step_file_sink_mt>(logger_name, filename_fmt, seconds, tmp_ext, max_file_size);
// }
// inline std::shared_ptr<spdlog::logger> spdlog::step_logger_st(
// const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, const filename_t &tmp_ext, unsigned max_file_size)
// {
// return create<spdlog::sinks::step_file_sink_st>(logger_name, filename_fmt, seconds, tmp_ext, max_file_size);
// }
namespace spdlog {
namespace sinks {
/*
* Default generator of step log file names.
*/
struct default_step_file_name_calculator
{
// Create filename for the form filename_YYYY-MM-DD_hh-mm-ss.ext
static std::tuple<filename_t, filename_t> calc_filename(const filename_t &filename, const filename_t &tmp_ext)
{
std::tm tm = spdlog::details::os::localtime();
filename_t basename, ext;
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec, tmp_ext);
return std::make_tuple(w.str(), ext);
}
};
/*
* Rotating file sink based on size and a specified time step
*/
template<class Mutex, class FileNameCalc = default_step_file_name_calculator>
class step_file_sink SPDLOG_FINAL : public base_sink<Mutex>
{
public:
step_file_sink(filename_t base_filename, unsigned step_seconds, filename_t tmp_ext, unsigned max_size)
: _base_filename(std::move(base_filename))
, _tmp_ext(std::move(tmp_ext))
, _step_seconds(step_seconds)
, _max_size(max_size)
{
if (step_seconds == 0)
{
throw spdlog_ex("step_file_sink: Invalid time step in ctor");
}
if (max_size == 0)
{
throw spdlog_ex("step_file_sink: Invalid max log size in ctor");
}
_tp = _next_tp();
std::tie(_current_filename, _ext) = FileNameCalc::calc_filename(_base_filename, _tmp_ext);
if (_tmp_ext == _ext)
{
throw spdlog_ex("step_file_sink: The temporary extension matches the specified in ctor");
}
_file_helper.open(_current_filename);
_current_size = _file_helper.size(); // expensive. called only once
}
~step_file_sink()
{
try
{
close_current_file();
}
catch (...)
{
}
}
protected:
void _sink_it(const details::log_msg &msg) override
{
_current_size += msg.formatted.size();
if (std::chrono::system_clock::now() >= _tp || _current_size > _max_size)
{
close_current_file();
std::tie(_current_filename, std::ignore) = FileNameCalc::calc_filename(_base_filename, _tmp_ext);
_file_helper.open(_current_filename);
_tp = _next_tp();
_current_size = msg.formatted.size();
}
_file_helper.write(msg);
}
void _flush() override
{
_file_helper.flush();
}
private:
std::chrono::system_clock::time_point _next_tp()
{
return std::chrono::system_clock::now() + _step_seconds;
}
void close_current_file()
{
using details::os::filename_to_str;
filename_t src = _current_filename, target;
std::tie(target, std::ignore) = details::file_helper::split_by_extenstion(src);
target += _ext;
if (details::file_helper::file_exists(src) && details::os::rename(src, target) != 0)
{
throw spdlog_ex("step_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
}
}
const filename_t _base_filename;
const filename_t _tmp_ext;
const std::chrono::seconds _step_seconds;
const unsigned _max_size;
std::chrono::system_clock::time_point _tp;
filename_t _current_filename;
filename_t _ext;
unsigned _current_size;
details::file_helper _file_helper;
};
using step_file_sink_mt = step_file_sink<std::mutex>;
using step_file_sink_st = step_file_sink<details::null_mutex>;
} // namespace sinks
} // namespace spdlog

View File

@@ -12,26 +12,26 @@
#pragma once
#include "spdlog/common.h"
#include "spdlog/sinks/sink.h"
#include "spdlog/details/mpmc_bounded_q.h"
#include "spdlog/details/log_msg.h"
#include "spdlog/details/os.h"
#include "spdlog/formatter.h"
#include "../common.h"
#include "../details/log_msg.h"
#include "../details/mpmc_blocking_q.h"
#include "../details/os.h"
#include "../formatter.h"
#include "../sinks/sink.h"
#include <chrono>
#include <condition_variable>
#include <exception>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include <utility>
#include <vector>
namespace spdlog
{
namespace details
{
namespace spdlog {
namespace details {
class async_log_helper
{
@@ -43,9 +43,9 @@ class async_log_helper
flush,
terminate
};
struct async_msg
{
std::string logger_name;
{
level::level_enum level;
log_clock::time_point time;
size_t thread_id;
@@ -56,96 +56,77 @@ class async_log_helper
async_msg() = default;
~async_msg() = default;
async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
logger_name(std::move(other.logger_name)),
level(std::move(other.level)),
time(std::move(other.time)),
thread_id(other.thread_id),
txt(std::move(other.txt)),
msg_type(std::move(other.msg_type)),
msg_id(other.msg_id)
{}
async_msg(async_msg_type m_type):
level(level::info),
thread_id(0),
msg_type(m_type),
msg_id(0)
{}
async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT
explicit async_msg(async_msg_type m_type)
: level(level::info)
, thread_id(0)
, msg_type(m_type)
, msg_id(0)
{
logger_name = std::move(other.logger_name);
level = other.level;
time = std::move(other.time);
thread_id = other.thread_id;
txt = std::move(other.txt);
msg_type = other.msg_type;
msg_id = other.msg_id;
return *this;
}
async_msg(async_msg &&other) = default;
async_msg &operator=(async_msg &&other) = default;
// never copy or assign. should only be moved..
async_msg(const async_msg&) = delete;
async_msg& operator=(const async_msg& other) = delete;
async_msg(const async_msg &) = delete;
async_msg &operator=(const async_msg &other) = delete;
// construct from log_msg
async_msg(const details::log_msg& m):
level(m.level),
time(m.time),
thread_id(m.thread_id),
txt(m.raw.data(), m.raw.size()),
msg_type(async_msg_type::log),
msg_id(m.msg_id)
explicit async_msg(const details::log_msg &m)
: level(m.level)
, time(m.time)
, thread_id(m.thread_id)
, txt(m.raw.data(), m.raw.size())
, msg_type(async_msg_type::log)
, msg_id(m.msg_id)
{
#ifndef SPDLOG_NO_NAME
logger_name = *m.logger_name;
#endif
}
// copy into log_msg
void fill_log_msg(log_msg &msg)
void fill_log_msg(log_msg &msg, std::string* logger_name)
{
msg.logger_name = &logger_name;
msg.logger_name = logger_name;
msg.level = level;
msg.time = time;
msg.thread_id = thread_id;
msg.raw.clear();
msg.raw << txt;
msg.msg_id = msg_id;
}
};
public:
using item_type = async_msg;
using q_type = details::mpmc_bounded_queue<item_type>;
using clock = std::chrono::steady_clock;
async_log_helper(formatter_ptr formatter,
const std::vector<sink_ptr>& sinks,
async_log_helper(std::string logger_name,
formatter_ptr formatter,
std::vector<sink_ptr> sinks,
size_t queue_size,
const log_err_handler err_handler,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()>& worker_teardown_cb = nullptr);
std::function<void()> worker_warmup_cb = nullptr,
const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(),
std::function<void()> worker_teardown_cb = nullptr);
void log(const details::log_msg& msg);
void log(const details::log_msg &msg);
// stop logging and join the back thread
~async_log_helper();
void set_formatter(formatter_ptr);
async_log_helper(const async_log_helper &) = delete;
async_log_helper &operator=(const async_log_helper &) = delete;
void flush(bool wait_for_q);
void set_formatter(formatter_ptr msg_formatter);
void flush();
void set_error_handler(spdlog::log_err_handler err_handler);
private:
std::string _logger_name;
formatter_ptr _formatter;
std::vector<std::shared_ptr<sinks::sink>> _sinks;
@@ -154,10 +135,7 @@ private:
log_err_handler _err_handler;
bool _flush_requested;
bool _terminate_requested;
std::chrono::time_point<log_clock> _last_flush;
// overflow policy
const async_overflow_policy _overflow_policy;
@@ -171,62 +149,62 @@ private:
// worker thread teardown callback
const std::function<void()> _worker_teardown_cb;
std::mutex null_mutex_;
// null_mutex null_mutex_;
std::condition_variable_any not_empty_cv_;
std::condition_variable_any not_full_cv_;
// worker thread
std::thread _worker_thread;
void push_msg(async_msg&& new_msg);
void enqueue_msg(async_msg &&new_msg, async_overflow_policy policy);
// worker thread main loop
void worker_loop();
// pop next message from the queue and process it. will set the last_pop to the pop time
// dequeue next message from the queue and process it.
// return false if termination of the queue is required
bool process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush);
bool process_next_msg();
void handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush);
// sleep,yield or return immediately using the time passed since last message as a hint
static void sleep_or_yield(const spdlog::log_clock::time_point& now, const log_clock::time_point& last_op_time);
// wait until the queue is empty
void wait_empty_q();
void handle_flush_interval();
void flush_sinks();
};
}
}
} // namespace details
} // namespace spdlog
///////////////////////////////////////////////////////////////////////////////
// async_sink class implementation
///////////////////////////////////////////////////////////////////////////////
inline spdlog::details::async_log_helper::async_log_helper(
formatter_ptr formatter,
const std::vector<sink_ptr>& sinks,
size_t queue_size,
log_err_handler err_handler,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms,
const std::function<void()>& worker_teardown_cb):
_formatter(formatter),
_sinks(sinks),
_q(queue_size),
_err_handler(err_handler),
_flush_requested(false),
_terminate_requested(false),
_overflow_policy(overflow_policy),
_worker_warmup_cb(worker_warmup_cb),
_flush_interval_ms(flush_interval_ms),
_worker_teardown_cb(worker_teardown_cb),
_worker_thread(&async_log_helper::worker_loop, this)
{}
inline spdlog::details::async_log_helper::async_log_helper(std::string logger_name,
formatter_ptr formatter,
std::vector<sink_ptr> sinks,
size_t queue_size,
log_err_handler err_handler,
const async_overflow_policy overflow_policy,
std::function<void()> worker_warmup_cb,
const std::chrono::milliseconds &flush_interval_ms,
std::function<void()> worker_teardown_cb)
: _logger_name(std::move(logger_name))
, _formatter(std::move(formatter))
, _sinks(std::move(sinks))
, _q(queue_size)
, _err_handler(std::move(err_handler))
, _last_flush(os::now())
, _overflow_policy(overflow_policy)
, _worker_warmup_cb(std::move(worker_warmup_cb))
, _flush_interval_ms(flush_interval_ms)
, _worker_teardown_cb(std::move(worker_teardown_cb))
{
_worker_thread = std::thread(&async_log_helper::worker_loop, this);
}
// Send to the worker thread termination message(level=off)
// and wait for it to finish gracefully
// send to the worker thread terminate message, and join it.
inline spdlog::details::async_log_helper::~async_log_helper()
{
try
{
push_msg(async_msg(async_msg_type::terminate));
enqueue_msg(async_msg(async_msg_type::terminate), async_overflow_policy::block_retry);
_worker_thread.join();
}
catch (...) // don't crash in destructor
@@ -234,47 +212,44 @@ inline spdlog::details::async_log_helper::~async_log_helper()
}
}
//Try to push and block until succeeded (if the policy is not to discard when the queue is full)
inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
// try to push and block until succeeded (if the policy is not to discard when the queue is full)
inline void spdlog::details::async_log_helper::log(const details::log_msg &msg)
{
push_msg(async_msg(msg));
enqueue_msg(async_msg(msg), _overflow_policy);
}
inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg)
inline void spdlog::details::async_log_helper::enqueue_msg(details::async_log_helper::async_msg &&new_msg, async_overflow_policy policy)
{
if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg)
// block until succeeded pushing to the queue
if (policy == async_overflow_policy::block_retry)
{
auto last_op_time = details::os::now();
auto now = last_op_time;
do
{
now = details::os::now();
sleep_or_yield(now, last_op_time);
}
while (!_q.enqueue(std::move(new_msg)));
_q.enqueue(std::move(new_msg));
}
else
{
_q.enqueue_nowait(std::move(new_msg));
}
}
// optionally wait for the queue be empty and request flush from the sinks
inline void spdlog::details::async_log_helper::flush(bool wait_for_q)
inline void spdlog::details::async_log_helper::flush()
{
push_msg(async_msg(async_msg_type::flush));
if (wait_for_q)
wait_empty_q(); //return only make after the above flush message was processed
enqueue_msg(async_msg(async_msg_type::flush), _overflow_policy);
}
inline void spdlog::details::async_log_helper::worker_loop()
{
if (_worker_warmup_cb) _worker_warmup_cb();
auto last_pop = details::os::now();
auto last_flush = last_pop;
if (_worker_warmup_cb)
{
_worker_warmup_cb();
}
auto active = true;
while (active)
{
try
{
active = process_next_msg(last_pop, last_flush);
active = process_next_msg();
}
catch (const std::exception &ex)
{
@@ -282,118 +257,86 @@ inline void spdlog::details::async_log_helper::worker_loop()
}
catch (...)
{
_err_handler("Unknown exception");
_err_handler("Unknown exeption in async logger worker loop.");
}
}
if (_worker_teardown_cb) _worker_teardown_cb();
if (_worker_teardown_cb)
{
_worker_teardown_cb();
}
}
// process next message in the queue
// return true if this thread should still be active (while no terminate msg was received)
inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush)
inline bool spdlog::details::async_log_helper::process_next_msg()
{
async_msg incoming_async_msg;
if (_q.dequeue(incoming_async_msg))
bool dequeued = _q.dequeue_for(incoming_async_msg, std::chrono::seconds(2));
if (!dequeued)
{
last_pop = details::os::now();
switch (incoming_async_msg.msg_type)
{
case async_msg_type::flush:
_flush_requested = true;
break;
case async_msg_type::terminate:
_flush_requested = true;
_terminate_requested = true;
break;
default:
log_msg incoming_log_msg;
incoming_async_msg.fill_log_msg(incoming_log_msg);
_formatter->format(incoming_log_msg);
for (auto &s : _sinks)
{
if (s->should_log(incoming_log_msg.level))
{
s->log(incoming_log_msg);
}
}
}
handle_flush_interval();
return true;
}
// Handle empty queue..
// This is the only place where the queue can terminate or flush to avoid losing messages already in the queue
else
switch (incoming_async_msg.msg_type)
{
auto now = details::os::now();
handle_flush_interval(now, last_flush);
sleep_or_yield(now, last_pop);
return !_terminate_requested;
}
}
case async_msg_type::flush:
flush_sinks();
return true;
// flush all sinks if _flush_interval_ms has expired
inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush)
{
auto should_flush = _flush_requested || (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms);
if (should_flush)
{
case async_msg_type::terminate:
flush_sinks();
return false;
default:
log_msg incoming_log_msg;
incoming_async_msg.fill_log_msg(incoming_log_msg, &_logger_name);
_formatter->format(incoming_log_msg);
for (auto &s : _sinks)
s->flush();
now = last_flush = details::os::now();
_flush_requested = false;
{
if (s->should_log(incoming_log_msg.level))
{
s->log(incoming_log_msg);
}
}
handle_flush_interval();
return true;
}
assert(false);
return true; // should not be reached
}
inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter)
{
_formatter = msg_formatter;
}
// spin, yield or sleep. use the time passed since last message as a hint
inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_clock::time_point& now, const spdlog::log_clock::time_point& last_op_time)
{
using namespace std::this_thread;
using std::chrono::milliseconds;
using std::chrono::microseconds;
auto time_since_op = now - last_op_time;
// spin upto 50 micros
if (time_since_op <= microseconds(50))
return;
// yield upto 150 micros
if (time_since_op <= microseconds(100))
return std::this_thread::yield();
// sleep for 20 ms upto 200 ms
if (time_since_op <= milliseconds(200))
return sleep_for(milliseconds(20));
// sleep for 200 ms
return sleep_for(milliseconds(200));
}
// wait for the queue to be empty
inline void spdlog::details::async_log_helper::wait_empty_q()
{
auto last_op = details::os::now();
while (_q.approx_size() > 0)
{
sleep_or_yield(details::os::now(), last_op);
}
_formatter = std::move(msg_formatter);
}
inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler)
{
_err_handler = err_handler;
_err_handler = std::move(err_handler);
}
// flush all sinks if _flush_interval_ms has expired.
inline void spdlog::details::async_log_helper::handle_flush_interval()
{
if (_flush_interval_ms == std::chrono::milliseconds::zero())
{
return;
}
auto delta = details::os::now() - _last_flush;
;
if (delta >= _flush_interval_ms)
{
flush_sinks();
}
}
// flush all sinks if _flush_interval_ms has expired. only called if queue is empty
inline void spdlog::details::async_log_helper::flush_sinks()
{
for (auto &s : _sinks)
{
s->flush();
}
_last_flush = os::now();
}

View File

@@ -8,53 +8,43 @@
// Async Logger implementation
// Use an async_sink (queue per logger) to perform the logging in a worker thread
#include "spdlog/details/async_log_helper.h"
#include "spdlog/async_logger.h"
#include "../async_logger.h"
#include "../details/async_log_helper.h"
#include <string>
#include <functional>
#include <chrono>
#include <functional>
#include <memory>
#include <string>
template<class It>
inline spdlog::async_logger::async_logger(const std::string& logger_name,
const It& begin,
const It& 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) :
logger(logger_name, begin, end),
_async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, _err_handler, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb))
inline spdlog::async_logger::async_logger(const std::string &logger_name, const It &begin, const It &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)
: logger(logger_name, begin, end)
, _async_log_helper(new details::async_log_helper(
logger_name, _formatter, _sinks, queue_size, _err_handler, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb))
{
}
inline spdlog::async_logger::async_logger(const std::string& logger_name,
sinks_init_list sinks_list,
size_t queue_size,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms,
const std::function<void()>& worker_teardown_cb) :
async_logger(logger_name, sinks_list.begin(), sinks_list.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
inline spdlog::async_logger::async_logger(const std::string& logger_name,
sink_ptr single_sink,
size_t queue_size,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms,
const std::function<void()>& worker_teardown_cb) :
async_logger(logger_name,
inline spdlog::async_logger::async_logger(const std::string &logger_name, sinks_init_list sinks_list, size_t queue_size,
const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb,
const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb)
: async_logger(logger_name, sinks_list.begin(), sinks_list.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms,
worker_teardown_cb)
{
single_sink
}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
}
inline spdlog::async_logger::async_logger(const std::string &logger_name, sink_ptr single_sink, size_t queue_size,
const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb,
const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb)
: async_logger(
logger_name, {std::move(single_sink)}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb)
{
}
inline void spdlog::async_logger::flush()
{
_async_log_helper->flush(true);
_async_log_helper->flush();
}
// Error handler
@@ -62,37 +52,36 @@ inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_
{
_err_handler = err_handler;
_async_log_helper->set_error_handler(err_handler);
}
inline spdlog::log_err_handler spdlog::async_logger::error_handler()
{
return _err_handler;
}
inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)
{
_formatter = msg_formatter;
_async_log_helper->set_formatter(_formatter);
}
inline void spdlog::async_logger::_set_pattern(const std::string& pattern, pattern_time_type pattern_time)
inline void spdlog::async_logger::_set_pattern(const std::string &pattern, pattern_time_type pattern_time)
{
_formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
_async_log_helper->set_formatter(_formatter);
}
inline void spdlog::async_logger::_sink_it(details::log_msg& msg)
inline void spdlog::async_logger::_sink_it(details::log_msg &msg)
{
try
{
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed);
_incr_msg_counter(msg);
#endif
_async_log_helper->log(msg);
if (_should_flush_on(msg))
_async_log_helper->flush(false); // do async flush
{
_async_log_helper->flush(); // do async flush
}
}
catch (const std::exception &ex)
{
@@ -100,6 +89,7 @@ inline void spdlog::async_logger::_sink_it(details::log_msg& msg)
}
catch (...)
{
_err_handler("Unknown exception");
_err_handler("Unknown exception in logger " + _name);
throw;
}
}

View File

@@ -9,19 +9,18 @@
// When failing to open a file, retry several times(5) with small delay between the tries(10 ms)
// Throw spdlog_ex exception on errors
#include "spdlog/details/os.h"
#include "spdlog/details/log_msg.h"
#include "../details/log_msg.h"
#include "../details/os.h"
#include <cerrno>
#include <chrono>
#include <cstdio>
#include <string>
#include <thread>
#include <cerrno>
#include <tuple>
namespace spdlog
{
namespace details
{
namespace spdlog {
namespace details {
class file_helper
{
@@ -30,31 +29,29 @@ public:
const int open_tries = 5;
const int open_interval = 10;
explicit file_helper() :
_fd(nullptr)
{}
explicit file_helper() = default;
file_helper(const file_helper&) = delete;
file_helper& operator=(const file_helper&) = delete;
file_helper(const file_helper &) = delete;
file_helper &operator=(const file_helper &) = delete;
~file_helper()
{
close();
}
void open(const filename_t& fname, bool truncate = false)
void open(const filename_t &fname, bool truncate = false)
{
close();
auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
_filename = fname;
for (int tries = 0; tries < open_tries; ++tries)
{
if (!os::fopen_s(&_fd, fname, mode))
{
return;
}
std::this_thread::sleep_for(std::chrono::milliseconds(open_interval));
details::os::sleep_for_millis(open_interval);
}
throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno);
@@ -63,9 +60,10 @@ public:
void reopen(bool truncate)
{
if (_filename.empty())
{
throw spdlog_ex("Failed re opening file - was not opened before");
}
open(_filename, truncate);
}
void flush()
@@ -75,43 +73,79 @@ public:
void close()
{
if (_fd)
if (_fd != nullptr)
{
std::fclose(_fd);
_fd = nullptr;
}
}
void write(const log_msg& msg)
void write(const log_msg &msg)
{
size_t msg_size = msg.formatted.size();
auto data = msg.formatted.data();
if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
{
throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
}
}
size_t size()
size_t size() const
{
if (!_fd)
if (_fd == nullptr)
{
throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
}
return os::filesize(_fd);
}
const filename_t& filename() const
const filename_t &filename() const
{
return _filename;
}
static bool file_exists(const filename_t& name)
static bool file_exists(const filename_t &fname)
{
return os::file_exists(fname);
}
return os::file_exists(name);
//
// return file path and its extension:
//
// "mylog.txt" => ("mylog", ".txt")
// "mylog" => ("mylog", "")
// "mylog." => ("mylog.", "")
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
//
// the starting dot in filenames is ignored (hidden files):
//
// ".mylog" => (".mylog". "")
// "my_folder/.mylog" => ("my_folder/.mylog", "")
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
static std::tuple<filename_t, filename_t> split_by_extenstion(const spdlog::filename_t &fname)
{
auto ext_index = fname.rfind('.');
// no valid extension found - return whole path and empty string as extension
if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
{
return std::make_tuple(fname, spdlog::filename_t());
}
// treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
auto folder_index = fname.rfind(details::os::folder_sep);
if (folder_index != fname.npos && folder_index >= ext_index - 1)
{
return std::make_tuple(fname, spdlog::filename_t());
}
// finally - return a valid base and extension tuple
return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
}
private:
FILE* _fd;
FILE *_fd{nullptr};
filename_t _filename;
};
}
}
} // namespace details
} // namespace spdlog

View File

@@ -5,24 +5,20 @@
#pragma once
#include "spdlog/common.h"
#include "spdlog/details/os.h"
#include "../common.h"
#include "../details/os.h"
#include <string>
#include <utility>
namespace spdlog
{
namespace details
{
namespace spdlog {
namespace details {
struct log_msg
{
log_msg() = default;
log_msg(const std::string *loggers_name, level::level_enum lvl) :
logger_name(loggers_name),
level(lvl),
msg_id(0)
log_msg(const std::string *loggers_name, level::level_enum lvl)
: logger_name(loggers_name)
, level(lvl)
{
#ifndef SPDLOG_NO_DATETIME
time = os::now();
@@ -33,18 +29,20 @@ struct log_msg
#endif
}
log_msg(const log_msg& other) = delete;
log_msg& operator=(log_msg&& other) = delete;
log_msg(log_msg&& other) = delete;
log_msg(const log_msg &other) = delete;
log_msg &operator=(log_msg &&other) = delete;
log_msg(log_msg &&other) = delete;
const std::string *logger_name;
const std::string *logger_name{nullptr};
level::level_enum level;
log_clock::time_point time;
size_t thread_id;
fmt::MemoryWriter raw;
fmt::MemoryWriter formatted;
size_t msg_id;
size_t msg_id{0};
// wrap this range with color codes
size_t color_range_start{0};
size_t color_range_end{0};
};
}
}
} // namespace details
} // namespace spdlog

View File

@@ -5,69 +5,67 @@
#pragma once
#include "spdlog/logger.h"
#include "spdlog/sinks/stdout_sinks.h"
#include "../logger.h"
#include <memory>
#include <string>
// create logger with given name, sinks and the default pattern formatter
// all other ctors will call this one
template<class It>
inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end):
_name(logger_name),
_sinks(begin, end),
_formatter(std::make_shared<pattern_formatter>("%+")),
_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
inline spdlog::logger::logger(std::string logger_name, const It &begin, const It &end)
: _name(std::move(logger_name))
, _sinks(begin, end)
, _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
{
_err_handler = [this](const std::string &msg)
{
this->_default_err_handler(msg);
};
_err_handler = [this](const std::string &msg) { this->_default_err_handler(msg); };
}
// ctor with sinks as init list
inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list):
logger(logger_name, sinks_list.begin(), sinks_list.end())
{}
inline spdlog::logger::logger(const std::string &logger_name, sinks_init_list sinks_list)
: logger(logger_name, sinks_list.begin(), sinks_list.end())
{
}
// ctor with single sink
inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink):
logger(logger_name,
inline spdlog::logger::logger(const std::string &logger_name, spdlog::sink_ptr single_sink)
: logger(logger_name, {std::move(single_sink)})
{
single_sink
})
{}
}
inline spdlog::logger::~logger() = default;
inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter)
{
_set_formatter(msg_formatter);
_set_formatter(std::move(msg_formatter));
}
inline void spdlog::logger::set_pattern(const std::string& pattern, pattern_time_type pattern_time)
inline void spdlog::logger::set_pattern(const std::string &pattern, pattern_time_type pattern_time)
{
_set_pattern(pattern, pattern_time);
}
template <typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args)
template<typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args)
{
if (!should_log(lvl)) return;
if (!should_log(lvl))
{
return;
}
try
{
details::log_msg log_msg(&_name, lvl);
#if defined(SPDLOG_FMT_PRINTF)
fmt::printf(log_msg.raw, fmt, args...);
#else
log_msg.raw.write(fmt, args...);
#endif
_sink_it(log_msg);
}
catch (const std::exception &ex)
@@ -76,14 +74,18 @@ inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Ar
}
catch (...)
{
_err_handler("Unknown exception");
_err_handler("Unknown exception in logger " + _name);
throw;
}
}
template <typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char* msg)
template<typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char *msg)
{
if (!should_log(lvl)) return;
if (!should_log(lvl))
{
return;
}
try
{
details::log_msg log_msg(&_name, lvl);
@@ -96,15 +98,18 @@ inline void spdlog::logger::log(level::level_enum lvl, const char* msg)
}
catch (...)
{
_err_handler("Unknown exception");
_err_handler("Unknown exception in logger " + _name);
throw;
}
}
template<typename T>
inline void spdlog::logger::log(level::level_enum lvl, const T& msg)
inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
{
if (!should_log(lvl)) return;
if (!should_log(lvl))
{
return;
}
try
{
details::log_msg log_msg(&_name, lvl);
@@ -117,226 +122,97 @@ inline void spdlog::logger::log(level::level_enum lvl, const T& msg)
}
catch (...)
{
_err_handler("Unknown exception");
_err_handler("Unknown exception in logger " + _name);
throw;
}
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::trace(const char* fmt, const Arg1 &arg1, const Args&... args)
template<typename Arg1, typename... Args>
inline void spdlog::logger::trace(const char *fmt, const Arg1 &arg1, const Args &... args)
{
log(level::trace, fmt, arg1, args...);
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::debug(const char* fmt, const Arg1 &arg1, const Args&... args)
template<typename Arg1, typename... Args>
inline void spdlog::logger::debug(const char *fmt, const Arg1 &arg1, const Args &... args)
{
log(level::debug, fmt, arg1, args...);
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::info(const char* fmt, const Arg1 &arg1, const Args&... args)
template<typename Arg1, typename... Args>
inline void spdlog::logger::info(const char *fmt, const Arg1 &arg1, const Args &... args)
{
log(level::info, fmt, arg1, args...);
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::warn(const char* fmt, const Arg1 &arg1, const Args&... args)
template<typename Arg1, typename... Args>
inline void spdlog::logger::warn(const char *fmt, const Arg1 &arg1, const Args &... args)
{
log(level::warn, fmt, arg1, args...);
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::error(const char* fmt, const Arg1 &arg1, const Args&... args)
template<typename Arg1, typename... Args>
inline void spdlog::logger::error(const char *fmt, const Arg1 &arg1, const Args &... args)
{
log(level::err, fmt, arg1, args...);
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::critical(const char* fmt, const Arg1 &arg1, const Args&... args)
template<typename Arg1, typename... Args>
inline void spdlog::logger::critical(const char *fmt, const Arg1 &arg1, const Args &... args)
{
log(level::critical, fmt, arg1, args...);
}
template <typename... Args>
inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const char* msg)
{
if (flag)
{
log(lvl, msg);
}
}
template<typename T>
inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const T& msg)
{
if (flag)
{
log(lvl, msg);
}
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::trace_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
{
if (flag)
{
log(level::trace, fmt, arg1, args...);
}
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::debug_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
{
if (flag)
{
log(level::debug, fmt, arg1, args...);
}
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::info_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
{
if (flag)
{
log(level::info, fmt, arg1, args...);
}
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::warn_if(const bool flag, const char* fmt, const Arg1& arg1, const Args&... args)
{
if (flag)
{
log(level::warn, fmt, arg1, args...);
}
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::error_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
{
if (flag)
{
log(level::err, fmt, arg1, args...);
}
}
template <typename Arg1, typename... Args>
inline void spdlog::logger::critical_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
{
if (flag)
{
log(level::critical, fmt, arg1, args...);
}
}
template<typename T>
inline void spdlog::logger::trace(const T& msg)
inline void spdlog::logger::trace(const T &msg)
{
log(level::trace, msg);
}
template<typename T>
inline void spdlog::logger::debug(const T& msg)
inline void spdlog::logger::debug(const T &msg)
{
log(level::debug, msg);
}
template<typename T>
inline void spdlog::logger::info(const T& msg)
inline void spdlog::logger::info(const T &msg)
{
log(level::info, msg);
}
template<typename T>
inline void spdlog::logger::warn(const T& msg)
inline void spdlog::logger::warn(const T &msg)
{
log(level::warn, msg);
}
template<typename T>
inline void spdlog::logger::error(const T& msg)
inline void spdlog::logger::error(const T &msg)
{
log(level::err, msg);
}
template<typename T>
inline void spdlog::logger::critical(const T& msg)
inline void spdlog::logger::critical(const T &msg)
{
log(level::critical, msg);
}
template<typename T>
inline void spdlog::logger::trace_if(const bool flag, const T& msg)
{
if (flag)
{
log(level::trace, msg);
}
}
template<typename T>
inline void spdlog::logger::debug_if(const bool flag, const T& msg)
{
if (flag)
{
log(level::debug, msg);
}
}
template<typename T>
inline void spdlog::logger::info_if(const bool flag, const T& msg)
{
if (flag)
{
log(level::info, msg);
}
}
template<typename T>
inline void spdlog::logger::warn_if(const bool flag, const T& msg)
{
if (flag)
{
log(level::warn, msg);
}
}
template<typename T>
inline void spdlog::logger::error_if(const bool flag, const T& msg)
{
if (flag)
{
log(level::err, msg);
}
}
template<typename T>
inline void spdlog::logger::critical_if(const bool flag, const T& msg)
{
if (flag)
{
log(level::critical, msg);
}
}
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
#include <codecvt>
#include <locale>
template <typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* msg)
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;
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)
template<typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args)
{
fmt::WMemoryWriter wWriter;
@@ -344,128 +220,48 @@ inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* fmt, const
log(lvl, wWriter.c_str());
}
template <typename... Args>
inline void spdlog::logger::trace(const wchar_t* fmt, const Args&... args)
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)
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)
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)
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)
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)
template<typename... Args>
inline void spdlog::logger::critical(const wchar_t *fmt, const Args &... args)
{
log(level::critical, fmt, args...);
}
//
// conditional logging
//
template <typename... Args>
inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const wchar_t* msg)
{
if (flag)
{
log(lvl, msg);
}
}
template <typename... Args>
inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(lvl, fmt, args);
}
}
template <typename... Args>
inline void spdlog::logger::trace_if(const bool flag, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(level::trace, fmt, args...);
}
}
template <typename... Args>
inline void spdlog::logger::debug_if(const bool flag, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(level::debug, fmt, args...);
}
}
template <typename... Args>
inline void spdlog::logger::info_if(const bool flag, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(level::info, fmt, args...);
}
}
template <typename... Args>
inline void spdlog::logger::warn_if(const bool flag, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(level::warn, fmt, args...);
}
}
template <typename... Args>
inline void spdlog::logger::error_if(const bool flag, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(level::err, fmt, args...);
}
}
template <typename... Args>
inline void spdlog::logger::critical_if(const bool flag, const wchar_t* fmt, const Args&... args)
{
if (flag)
{
log(level::critical, fmt, args...);
}
}
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
//
// name and level
//
inline const std::string& spdlog::logger::name() const
inline const std::string &spdlog::logger::name() const
{
return _name;
}
@@ -477,7 +273,7 @@ inline void spdlog::logger::set_level(spdlog::level::level_enum log_level)
inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler)
{
_err_handler = err_handler;
_err_handler = std::move(err_handler);
}
inline spdlog::log_err_handler spdlog::logger::error_handler()
@@ -485,7 +281,6 @@ inline spdlog::log_err_handler spdlog::logger::error_handler()
return _err_handler;
}
inline void spdlog::logger::flush_on(level::level_enum log_level)
{
_flush_level.store(log_level);
@@ -504,51 +299,56 @@ inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) cons
//
// protected virtual called at end of each user log call (if enabled) by the line_logger
//
inline void spdlog::logger::_sink_it(details::log_msg& msg)
inline void spdlog::logger::_sink_it(details::log_msg &msg)
{
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed);
_incr_msg_counter(msg);
#endif
_formatter->format(msg);
for (auto &sink : _sinks)
{
if( sink->should_log( msg.level))
if (sink->should_log(msg.level))
{
sink->log(msg);
}
}
if(_should_flush_on(msg))
if (_should_flush_on(msg))
{
flush();
}
}
inline void spdlog::logger::_set_pattern(const std::string& pattern, pattern_time_type pattern_time)
inline void spdlog::logger::_set_pattern(const std::string &pattern, pattern_time_type pattern_time)
{
_formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
}
inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter)
{
_formatter = msg_formatter;
_formatter = std::move(msg_formatter);
}
inline void spdlog::logger::flush()
{
for (auto& sink : _sinks)
for (auto &sink : _sinks)
{
sink->flush();
}
}
inline void spdlog::logger::_default_err_handler(const std::string &msg)
{
auto now = time(nullptr);
if (now - _last_err_time < 60)
{
return;
}
_last_err_time = now;
auto tm_time = details::os::localtime(now);
char date_buf[100];
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
details::log_msg err_msg;
err_msg.formatted.write("[*** LOG ERROR ***] [{}] [{}] [{}]{}", name(), msg, date_buf, details::os::eol);
sinks::stderr_sink_mt::instance()->log(err_msg);
_last_err_time = now;
fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), msg);
}
inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg)
@@ -557,7 +357,12 @@ inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg)
return (msg.level >= flush_level) && (msg.level != level::off);
}
inline const std::vector<spdlog::sink_ptr>& spdlog::logger::sinks() const
inline void spdlog::logger::_incr_msg_counter(details::log_msg &msg)
{
msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed);
}
inline const std::vector<spdlog::sink_ptr> &spdlog::logger::sinks() const
{
return _sinks;
}

View File

@@ -0,0 +1,84 @@
#pragma once
//
// Copyright(c) 2018 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
// async log helper :
// multi producer-multi consumer blocking queue
// enqueue(..) - will block until room found to put the new message
// enqueue_nowait(..) - will return immediatly with false if no room left in the queue
// dequeue_for(..) - will block until the queue is not empty or timeout passed
#include <condition_variable>
#include <mutex>
#include <queue>
namespace spdlog {
namespace details {
template<typename T>
class mpmc_bounded_queue
{
public:
using item_type = T;
explicit mpmc_bounded_queue(size_t max_items)
: max_items_(max_items)
{
}
// try to enqueue and block if no room left
void enqueue(T &&item)
{
{
std::unique_lock<std::mutex> lock(queue_mutex_);
pop_cv_.wait(lock, [this] { return this->q_.size() < this->max_items_; });
q_.push(std::move(item));
}
push_cv_.notify_one();
}
// try to enqueue and return immdeialty false if no room left
bool enqueue_nowait(T &&item)
{
{
std::unique_lock<std::mutex> lock(queue_mutex_);
if (q_.size() == this->max_items_)
{
return false;
}
q_.push(std::forward<T>(item));
}
push_cv_.notify_one();
return true;
}
// try to dequeue item. if no item found. wait upto timeout and try again
// Return true, if succeeded dequeue item, false otherwise
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
{
{
std::unique_lock<std::mutex> lock(queue_mutex_);
if (!push_cv_.wait_for(lock, wait_duration, [this] { return this->q_.size() > 0; }))
{
return false;
}
popped_item = std::move(q_.front());
q_.pop();
}
pop_cv_.notify_one();
return true;
}
private:
size_t max_items_;
std::mutex queue_mutex_;
std::condition_variable push_cv_;
std::condition_variable pop_cv_;
std::queue<T> q_;
};
} // namespace details
} // namespace spdlog

View File

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

View File

@@ -8,10 +8,8 @@
#include <atomic>
// null, no cost dummy "mutex" and dummy "atomic" int
namespace spdlog
{
namespace details
{
namespace spdlog {
namespace details {
struct null_mutex
{
void lock() {}
@@ -27,8 +25,10 @@ struct null_atomic_int
int value;
null_atomic_int() = default;
null_atomic_int(int val):value(val)
{}
explicit null_atomic_int(int val)
: value(val)
{
}
int load(std::memory_order) const
{
@@ -41,5 +41,5 @@ struct null_atomic_int
}
};
}
}
} // namespace details
} // namespace spdlog

View File

@@ -4,32 +4,32 @@
//
#pragma once
#include "spdlog/common.h"
#include "../common.h"
#include <algorithm>
#include <chrono>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <functional>
#include <string>
#include <chrono>
#include <thread>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <sys/stat.h>
#include <sys/types.h>
#include <thread>
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX //prevent windows redefining min/max
#define NOMINMAX // prevent windows redefining min/max
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <io.h> // _get_osfhandle and _isatty support
#include <process.h> // _get_pid support
#include <io.h> // _get_osfhandle and _isatty support
#include <windows.h>
#ifdef __MINGW32__
#include <share.h>
@@ -37,8 +37,8 @@
#else // unix
#include <unistd.h>
#include <fcntl.h>
#include <unistd.h>
#ifdef __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
@@ -47,19 +47,15 @@
#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
#endif
#endif //unix
#endif // unix
#ifndef __has_feature // Clang - feature checking macros.
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#ifndef __has_feature // Clang - feature checking macros.
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif
namespace spdlog
{
namespace details
{
namespace os
{
namespace spdlog {
namespace details {
namespace os {
inline spdlog::log_clock::time_point now()
{
@@ -68,14 +64,11 @@ inline spdlog::log_clock::time_point now()
timespec ts;
::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
return std::chrono::time_point<log_clock, typename log_clock::duration>(
std::chrono::duration_cast<typename log_clock::duration>(
std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
std::chrono::duration_cast<typename log_clock::duration>(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
#else
return log_clock::now();
#endif
}
inline std::tm localtime(const std::time_t &time_tt)
{
@@ -96,7 +89,6 @@ inline std::tm localtime()
return localtime(now_t);
}
inline std::tm gmtime(const std::time_t &time_tt)
{
@@ -115,24 +107,19 @@ inline std::tm gmtime()
std::time_t now_t = time(nullptr);
return gmtime(now_t);
}
inline bool operator==(const std::tm& tm1, const std::tm& tm2)
inline bool operator==(const std::tm &tm1, const std::tm &tm2)
{
return (tm1.tm_sec == tm2.tm_sec &&
tm1.tm_min == tm2.tm_min &&
tm1.tm_hour == tm2.tm_hour &&
tm1.tm_mday == tm2.tm_mday &&
tm1.tm_mon == tm2.tm_mon &&
tm1.tm_year == tm2.tm_year &&
tm1.tm_isdst == tm2.tm_isdst);
return (tm1.tm_sec == tm2.tm_sec && tm1.tm_min == tm2.tm_min && tm1.tm_hour == tm2.tm_hour && tm1.tm_mday == tm2.tm_mday &&
tm1.tm_mon == tm2.tm_mon && tm1.tm_year == tm2.tm_year && tm1.tm_isdst == tm2.tm_isdst);
}
inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
inline bool operator!=(const std::tm &tm1, const std::tm &tm2)
{
return !(tm1 == tm2);
}
// eol definition
#if !defined (SPDLOG_EOL)
#if !defined(SPDLOG_EOL)
#ifdef _WIN32
#define SPDLOG_EOL "\r\n"
#else
@@ -140,25 +127,35 @@ inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
#endif
#endif
SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL;
SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1;
SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
// folder separator
#ifdef _WIN32
SPDLOG_CONSTEXPR static const char folder_sep = '\\';
#else
SPDLOG_CONSTEXPR static const char folder_sep = '/';
#endif
inline void prevent_child_fd(FILE *f)
{
#ifdef _WIN32
#if !defined(__cplusplus_winrt)
auto file_handle = (HANDLE)_get_osfhandle(_fileno(f));
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
throw spdlog_ex("SetHandleInformation failed", errno);
#endif
#else
auto fd = fileno(f);
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
{
throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno);
}
#endif
}
//fopen_s on non windows for writing
inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode)
// fopen_s on non windows for writing
inline bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
{
#ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES
@@ -166,18 +163,19 @@ inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode
#else
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
#endif
#else //unix
#else // unix
*fp = fopen((filename.c_str()), mode.c_str());
#endif
#ifdef SPDLOG_PREVENT_CHILD_FD
if (*fp != nullptr)
{
prevent_child_fd(*fp);
}
#endif
return *fp == nullptr;
}
inline int remove(const filename_t &filename)
{
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
@@ -187,7 +185,7 @@ inline int remove(const filename_t &filename)
#endif
}
inline int rename(const filename_t& filename1, const filename_t& filename2)
inline int rename(const filename_t &filename1, const filename_t &filename2)
{
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
return _wrename(filename1.c_str(), filename2.c_str());
@@ -196,9 +194,8 @@ inline int rename(const filename_t& filename1, const filename_t& filename2)
#endif
}
//Return if file exists
inline bool file_exists(const filename_t& filename)
// Return if file exists
inline bool file_exists(const filename_t &filename)
{
#ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES
@@ -207,54 +204,59 @@ inline bool file_exists(const filename_t& filename)
auto attribs = GetFileAttributesA(filename.c_str());
#endif
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;
return (stat(filename.c_str(), &buffer) == 0);
#endif
}
//Return file size according to open FILE* object
// Return file size according to open FILE* object
inline size_t filesize(FILE *f)
{
if (f == nullptr)
{
throw spdlog_ex("Failed getting file size. fd is null");
#ifdef _WIN32
}
#if defined(_WIN32) && !defined(__CYGWIN__)
int fd = _fileno(f);
#if _WIN64 //64 bits
#if _WIN64 // 64 bits
struct _stat64 st;
if (_fstat64(fd, &st) == 0)
{
return st.st_size;
}
#else //windows 32 bits
#else // windows 32 bits
long ret = _filelength(fd);
if (ret >= 0)
{
return static_cast<size_t>(ret);
}
#endif
#else // unix
int fd = fileno(f);
//64 bits(but not in osx, where fstat64 is deprecated)
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__))
// 64 bits(but not in osx or cygwin, where fstat64 is deprecated)
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__)
struct stat64 st;
if (fstat64(fd, &st) == 0)
{
return static_cast<size_t>(st.st_size);
#else // unix 32 bits or osx
}
#else // unix 32 bits or cygwin
struct stat st;
if (fstat(fd, &st) == 0)
{
return static_cast<size_t>(st.st_size);
}
#endif
#endif
throw spdlog_ex("Failed getting file size from fd", errno);
}
//Return utc offset in minutes or throw spdlog_ex on failure
inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
// Return utc offset in minutes or throw spdlog_ex on failure
inline int utc_minutes_offset(const std::tm &tm = details::os::localtime())
{
#ifdef _WIN32
@@ -270,9 +272,13 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
int offset = -tzinfo.Bias;
if (tm.tm_isdst)
{
offset -= tzinfo.DaylightBias;
}
else
{
offset -= tzinfo.StandardBias;
}
return offset;
#else
@@ -280,23 +286,22 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
struct helper
{
static long int calculate_gmt_offset(const std::tm & localtm = details::os::localtime(), const std::tm & gmtm = details::os::gmtime())
static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), const std::tm &gmtm = details::os::gmtime())
{
int local_year = localtm.tm_year + (1900 - 1);
int gmt_year = gmtm.tm_year + (1900 - 1);
long int days = (
// difference in day of year
localtm.tm_yday - gmtm.tm_yday
// difference in day of year
localtm.tm_yday -
gmtm.tm_yday
// + intervening leap days
+ ((local_year >> 2) - (gmt_year >> 2))
- (local_year / 100 - gmt_year / 100)
+ ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
// + intervening leap days
+ ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
// + difference in years * 365 */
+ (long int)(local_year - gmt_year) * 365
);
// + difference in years * 365 */
+ (long int)(local_year - gmt_year) * 365);
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
@@ -306,26 +311,26 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
}
};
long int offset_seconds = helper::calculate_gmt_offset(tm);
auto offset_seconds = helper::calculate_gmt_offset(tm);
#else
long int offset_seconds = tm.tm_gmtoff;
auto offset_seconds = tm.tm_gmtoff;
#endif
return static_cast<int>(offset_seconds / 60);
#endif
}
//Return current thread id as size_t
//It exists because the std::this_thread::get_id() is much slower(espcially under VS 2013)
// Return current thread id as size_t
// It exists because the std::this_thread::get_id() is much slower(especially under VS 2013)
inline size_t _thread_id()
{
#ifdef _WIN32
return static_cast<size_t>(::GetCurrentThreadId());
return static_cast<size_t>(::GetCurrentThreadId());
#elif __linux__
# if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
# define SYS_gettid __NR_gettid
# endif
return static_cast<size_t>(syscall(SYS_gettid));
#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
#define SYS_gettid __NR_gettid
#endif
return static_cast<size_t>(syscall(SYS_gettid));
#elif __FreeBSD__
long tid;
thr_self(&tid);
@@ -334,42 +339,51 @@ inline size_t _thread_id()
uint64_t tid;
pthread_threadid_np(nullptr, &tid);
return static_cast<size_t>(tid);
#else //Default to standard C++11 (other Unix)
#else // Default to standard C++11 (other Unix)
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
#endif
}
//Return current thread id as size_t (from thread local storage)
// Return current thread id as size_t (from thread local storage)
inline size_t thread_id()
{
#if defined(_MSC_VER) && (_MSC_VER < 1900) || defined(__clang__) && !__has_feature(cxx_thread_local)
#if defined(SPDLOG_DISABLE_TID_CACHING) || (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt) || \
(defined(__clang__) && !__has_feature(cxx_thread_local))
return _thread_id();
#else
#else // cache thread id in tls
static thread_local const size_t tid = _thread_id();
return tid;
#endif
}
// This is avoid msvc issue in sleep_for that happens if the clock changes.
// See https://github.com/gabime/spdlog/issues/609
inline void sleep_for_millis(int milliseconds)
{
#if defined(_WIN32)
::Sleep(milliseconds);
#else
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
#endif
}
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
#define SPDLOG_FILENAME_T(s) L ## s
inline std::string filename_to_str(const filename_t& filename)
#define SPDLOG_FILENAME_T(s) L##s
inline std::string filename_to_str(const filename_t &filename)
{
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c;
return c.to_bytes(filename);
}
#else
#define SPDLOG_FILENAME_T(s) s
inline std::string filename_to_str(const filename_t& filename)
inline std::string filename_to_str(const filename_t &filename)
{
return filename;
}
#endif
inline std::string errno_to_string(char[256], char* res)
inline std::string errno_to_string(char[256], char *res)
{
return std::string(res);
}
@@ -380,10 +394,7 @@ inline std::string errno_to_string(char buf[256], int res)
{
return std::string(buf);
}
else
{
return "Unknown error";
}
return "Unknown error";
}
// Return errno string (thread safe)
@@ -394,21 +405,29 @@ inline std::string errno_str(int err_num)
#ifdef _WIN32
if (strerror_s(buf, buf_size, err_num) == 0)
{
return std::string(buf);
}
else
{
return "Unknown error";
}
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \
((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \
((_POSIX_C_SOURCE >= 200112L) && !defined(_GNU_SOURCE)) // posix version
if (strerror_r(err_num, buf, buf_size) == 0)
{
return std::string(buf);
}
else
{
return "Unknown error";
}
#else // gnu version (might not use the given buf, so its retval pointer must be used)
#else // gnu version (might not use the given buf, so its retval pointer must be used)
auto err = strerror_r(err_num, buf, buf_size); // let compiler choose type
return errno_to_string(buf, err); // use overloading to select correct stringify function
return errno_to_string(buf, err); // use overloading to select correct stringify function
#endif
}
@@ -416,26 +435,21 @@ inline int pid()
{
#ifdef _WIN32
return ::_getpid();
return static_cast<int>(::GetCurrentProcessId());
#else
return static_cast<int>(::getpid());
#endif
}
// Detrmine if the terminal supports colors
// 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"
};
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)
@@ -443,27 +457,23 @@ inline bool is_color_terminal()
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;
});
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)
inline bool in_terminal(FILE *file)
{
#ifdef _WIN32
return _isatty(_fileno(file)) ? true : false;
return _isatty(_fileno(file)) != 0;
#else
return isatty(fileno(file)) ? true : false;
return isatty(fileno(file)) != 0;
#endif
}
} //os
} //details
} //spdlog
} // namespace os
} // namespace details
} // namespace spdlog

View File

@@ -5,11 +5,12 @@
#pragma once
#include "spdlog/formatter.h"
#include "spdlog/details/log_msg.h"
#include "spdlog/details/os.h"
#include "spdlog/fmt/fmt.h"
#include "../details/log_msg.h"
#include "../details/os.h"
#include "../fmt/fmt.h"
#include "../formatter.h"
#include <array>
#include <chrono>
#include <ctime>
#include <memory>
@@ -18,47 +19,40 @@
#include <thread>
#include <utility>
#include <vector>
#include <array>
namespace spdlog
{
namespace details
{
namespace spdlog {
namespace details {
class flag_formatter
{
public:
virtual ~flag_formatter()
{}
virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0;
virtual ~flag_formatter() = default;
virtual void format(details::log_msg &msg, const std::tm &tm_time) = 0;
};
///////////////////////////////////////////////////////////////////////
// name & level pattern appenders
///////////////////////////////////////////////////////////////////////
namespace
class name_formatter : public flag_formatter
{
class name_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
void format(details::log_msg &msg, const std::tm &) override
{
msg.formatted << *msg.logger_name;
}
};
}
// log level appender
class level_formatter:public flag_formatter
class level_formatter : public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
void format(details::log_msg &msg, const std::tm &) override
{
msg.formatted << level::to_str(msg.level);
}
};
// short log level appender
class short_level_formatter:public flag_formatter
class short_level_formatter : public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
void format(details::log_msg &msg, const std::tm &) override
{
msg.formatted << level::to_short_str(msg.level);
}
@@ -68,171 +62,166 @@ class short_level_formatter:public flag_formatter
// Date time pattern appenders
///////////////////////////////////////////////////////////////////////
static const char* ampm(const tm& t)
static const char *ampm(const tm &t)
{
return t.tm_hour >= 12 ? "PM" : "AM";
}
static int to12h(const tm& t)
static int to12h(const tm &t)
{
return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour;
}
//Abbreviated weekday name
static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
class a_formatter:public flag_formatter
// Abbreviated weekday name
static const std::string days[]{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
class a_formatter : public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
void format(details::log_msg &msg, const std::tm &tm_time) override
{
msg.formatted << days[tm_time.tm_wday];
}
};
//Full weekday name
static const std::string full_days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
class A_formatter:public flag_formatter
// Full weekday name
static const std::string full_days[]{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
class A_formatter : public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
void format(details::log_msg &msg, const std::tm &tm_time) override
{
msg.formatted << full_days[tm_time.tm_wday];
}
};
//Abbreviated month
static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" };
class b_formatter:public flag_formatter
// Abbreviated month
static const std::string months[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"};
class b_formatter : public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
void format(details::log_msg &msg, const std::tm &tm_time) override
{
msg.formatted << months[tm_time.tm_mon];
}
};
//Full month name
static const std::string full_months[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
class B_formatter:public flag_formatter
// Full month name
static const std::string full_months[]{
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
class B_formatter : public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
void format(details::log_msg &msg, const std::tm &tm_time) override
{
msg.formatted << full_months[tm_time.tm_mon];
}
};
//write 2 ints seperated by sep with padding of 2
static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, char sep)
// write 2 ints separated by sep with padding of 2
static fmt::MemoryWriter &pad_n_join(fmt::MemoryWriter &w, int v1, int v2, char sep)
{
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0');
return w;
}
//write 3 ints seperated by sep with padding of 2
static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v3, char sep)
// write 3 ints separated by sep with padding of 2
static fmt::MemoryWriter &pad_n_join(fmt::MemoryWriter &w, int v1, int v2, int v3, char sep)
{
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0');
return w;
}
//Date and time representation (Thu Aug 23 15:35:46 2014)
class c_formatter SPDLOG_FINAL:public flag_formatter
// Date and time representation (Thu Aug 23 15:35:46 2014)
class c_formatter SPDLOG_FINAL : public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
void format(details::log_msg &msg, const std::tm &tm_time) override
{
msg.formatted << days[tm_time.tm_wday] << ' ' << months[tm_time.tm_mon] << ' ' << tm_time.tm_mday << ' ';
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << tm_time.tm_year + 1900;
}
};
// year - 2 digit
class C_formatter SPDLOG_FINAL:public flag_formatter
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 << fmt::pad(tm_time.tm_year % 100, 2, '0');
}
};
// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
class D_formatter SPDLOG_FINAL:public flag_formatter
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
{
pad_n_join(msg.formatted, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_year % 100, '/');
}
};
// year - 4 digit
class Y_formatter SPDLOG_FINAL:public flag_formatter
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
{
msg.formatted << tm_time.tm_year + 1900;
}
};
// month 1-12
class m_formatter SPDLOG_FINAL: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
{
msg.formatted << fmt::pad(tm_time.tm_mon + 1, 2, '0');
}
};
// day of month 1-31
class d_formatter SPDLOG_FINAL: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
{
msg.formatted << fmt::pad(tm_time.tm_mday, 2, '0');
}
};
// hours in 24 format 0-23
class H_formatter SPDLOG_FINAL:public flag_formatter
// hours in 24 format 0-23
class H_formatter SPDLOG_FINAL : public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
void format(details::log_msg &msg, const std::tm &tm_time) override
{
msg.formatted << fmt::pad(tm_time.tm_hour, 2, '0');
}
};
// hours in 12 format 1-12
class I_formatter SPDLOG_FINAL:public flag_formatter
// hours in 12 format 1-12
class I_formatter SPDLOG_FINAL : public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
void format(details::log_msg &msg, const std::tm &tm_time) override
{
msg.formatted << fmt::pad(to12h(tm_time), 2, '0');
}
};
// minutes 0-59
class M_formatter SPDLOG_FINAL:public flag_formatter
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
{
msg.formatted << fmt::pad(tm_time.tm_min, 2, '0');
}
};
// seconds 0-59
class S_formatter SPDLOG_FINAL: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
{
msg.formatted << fmt::pad(tm_time.tm_sec, 2, '0');
}
};
// milliseconds
class e_formatter SPDLOG_FINAL: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
{
auto duration = msg.time.time_since_epoch();
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
@@ -241,9 +230,9 @@ class e_formatter SPDLOG_FINAL:public flag_formatter
};
// microseconds
class f_formatter SPDLOG_FINAL: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
{
auto duration = msg.time.time_since_epoch();
auto micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count() % 1000000;
@@ -252,9 +241,9 @@ class f_formatter SPDLOG_FINAL:public flag_formatter
};
// nanoseconds
class F_formatter SPDLOG_FINAL: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
{
auto duration = msg.time.time_since_epoch();
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % 1000000000;
@@ -262,55 +251,63 @@ class F_formatter SPDLOG_FINAL:public flag_formatter
}
};
// AM/PM
class p_formatter SPDLOG_FINAL:public flag_formatter
class E_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 &) override
{
auto duration = msg.time.time_since_epoch();
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
msg.formatted << seconds;
}
};
// AM/PM
class p_formatter SPDLOG_FINAL : public flag_formatter
{
void format(details::log_msg &msg, const std::tm &tm_time) override
{
msg.formatted << ampm(tm_time);
}
};
// 12 hour clock 02:55:02 pm
class r_formatter SPDLOG_FINAL:public flag_formatter
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
{
pad_n_join(msg.formatted, to12h(tm_time), tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << ampm(tm_time);
}
};
// 24-hour HH:MM time, equivalent to %H:%M
class R_formatter SPDLOG_FINAL:public flag_formatter
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
{
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, ':');
}
};
// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
class T_formatter SPDLOG_FINAL:public flag_formatter
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
{
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':');
}
};
// ISO 8601 offset from UTC in timezone (+-HH:MM)
class z_formatter SPDLOG_FINAL:public flag_formatter
class z_formatter SPDLOG_FINAL : public flag_formatter
{
public:
const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
z_formatter():_last_update(std::chrono::seconds(0)), _offset_minutes(0)
{}
z_formatter(const z_formatter&) = delete;
z_formatter& operator=(const z_formatter&) = delete;
z_formatter() = default;
z_formatter(const z_formatter &) = delete;
z_formatter &operator=(const z_formatter &) = delete;
void format(details::log_msg& msg, const std::tm& tm_time) override
void format(details::log_msg &msg, const std::tm &tm_time) override
{
#ifdef _WIN32
int total_minutes = get_cached_offset(msg, tm_time);
@@ -336,14 +333,14 @@ public:
msg.formatted << sign;
pad_n_join(msg.formatted, h, m, ':');
}
private:
log_clock::time_point _last_update;
int _offset_minutes;
log_clock::time_point _last_update{std::chrono::seconds(0)};
int _offset_minutes{0};
std::mutex _mutex;
int get_cached_offset(const log_msg& msg, const std::tm& tm_time)
int get_cached_offset(const log_msg &msg, const std::tm &tm_time)
{
using namespace std::chrono;
std::lock_guard<std::mutex> l(_mutex);
if (msg.time - _last_update >= cache_refresh)
{
@@ -354,72 +351,97 @@ private:
}
};
// Thread id
class t_formatter SPDLOG_FINAL: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
{
msg.formatted << msg.thread_id;
}
};
// Current pid
class pid_formatter SPDLOG_FINAL: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
{
msg.formatted << details::os::pid();
}
};
class v_formatter SPDLOG_FINAL:public flag_formatter
// message counter formatter
class i_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
{
msg.formatted << fmt::pad(msg.msg_id, 6, '0');
}
};
class v_formatter SPDLOG_FINAL : public flag_formatter
{
void format(details::log_msg &msg, const std::tm &) override
{
msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
}
};
class ch_formatter SPDLOG_FINAL:public flag_formatter
class ch_formatter SPDLOG_FINAL : public flag_formatter
{
public:
explicit ch_formatter(char ch): _ch(ch)
{}
void format(details::log_msg& msg, const std::tm&) override
explicit ch_formatter(char ch)
: _ch(ch)
{
}
void format(details::log_msg &msg, const std::tm &) override
{
msg.formatted << _ch;
}
private:
char _ch;
};
//aggregate user chars to display as is
class aggregate_formatter SPDLOG_FINAL:public flag_formatter
// aggregate user chars to display as is
class aggregate_formatter SPDLOG_FINAL : public flag_formatter
{
public:
aggregate_formatter()
{}
aggregate_formatter() = default;
void add_ch(char ch)
{
_str += ch;
}
void format(details::log_msg& msg, const std::tm&) override
void format(details::log_msg &msg, const std::tm &) override
{
msg.formatted << _str;
}
private:
std::string _str;
};
// mark the color range. expect it to be in the form of "%^colored text%$"
class color_start_formatter SPDLOG_FINAL : public flag_formatter
{
void format(details::log_msg &msg, const std::tm &) override
{
msg.color_range_start = msg.formatted.size();
}
};
class color_stop_formatter SPDLOG_FINAL : public flag_formatter
{
void format(details::log_msg &msg, const std::tm &) override
{
msg.color_range_end = msg.formatted.size();
}
};
// Full info formatter
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
class full_formatter SPDLOG_FINAL: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
{
#ifndef SPDLOG_NO_DATETIME
auto duration = msg.time.time_since_epoch();
@@ -438,7 +460,6 @@ class full_formatter SPDLOG_FINAL:public flag_formatter
level::to_str(msg.level),
msg.raw.str());*/
// Faster (albeit uglier) way to format the line (5.6 million lines/sec under 10 threads)
msg.formatted << '[' << static_cast<unsigned int>(tm_time.tm_year + 1900) << '-'
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_mon + 1), 2, '0') << '-'
@@ -448,7 +469,7 @@ class full_formatter SPDLOG_FINAL:public flag_formatter
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_sec), 2, '0') << '.'
<< fmt::pad(static_cast<unsigned int>(millis), 3, '0') << "] ";
//no datetime needed
// no datetime needed
#else
(void)tm_time;
#endif
@@ -457,25 +478,28 @@ class full_formatter SPDLOG_FINAL:public flag_formatter
msg.formatted << '[' << *msg.logger_name << "] ";
#endif
msg.formatted << '[' << level::to_str(msg.level) << "] ";
msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
msg.formatted << '[';
// wrap the level name with color
msg.color_range_start = msg.formatted.size();
msg.formatted << level::to_str(msg.level);
msg.color_range_end = msg.formatted.size();
msg.formatted << "] " << fmt::StringRef(msg.raw.data(), msg.raw.size());
}
};
}
}
} // namespace details
} // namespace spdlog
///////////////////////////////////////////////////////////////////////////////
// pattern_formatter inline impl
///////////////////////////////////////////////////////////////////////////////
inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern, pattern_time_type pattern_time)
: _pattern_time(pattern_time)
inline spdlog::pattern_formatter::pattern_formatter(const std::string &pattern, pattern_time_type pattern_time, std::string eol)
: _eol(std::move(eol))
, _pattern_time(pattern_time)
{
compile_pattern(pattern);
}
inline void spdlog::pattern_formatter::compile_pattern(const std::string& pattern)
inline void spdlog::pattern_formatter::compile_pattern(const std::string &pattern)
{
auto end = pattern.end();
std::unique_ptr<details::aggregate_formatter> user_chars;
@@ -483,26 +507,33 @@ inline void spdlog::pattern_formatter::compile_pattern(const std::string& patter
{
if (*it == '%')
{
if (user_chars) //append user chars found so far
if (user_chars) // append user chars found so far
{
_formatters.push_back(std::move(user_chars));
}
// if(
if (++it != end)
{
handle_flag(*it);
}
else
{
break;
}
}
else // chars not following the % sign should be displayed as is
{
if (!user_chars)
{
user_chars = std::unique_ptr<details::aggregate_formatter>(new details::aggregate_formatter());
}
user_chars->add_ch(*it);
}
}
if (user_chars) //append raw chars found so far
if (user_chars) // append raw chars found so far
{
_formatters.push_back(std::move(user_chars));
}
}
inline void spdlog::pattern_formatter::handle_flag(char flag)
{
@@ -510,145 +541,156 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
{
// logger name
case 'n':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::name_formatter()));
_formatters.emplace_back(new details::name_formatter());
break;
case 'l':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::level_formatter()));
_formatters.emplace_back(new details::level_formatter());
break;
case 'L':
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::short_level_formatter()));
_formatters.emplace_back(new details::short_level_formatter());
break;
case('t'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::t_formatter()));
case ('t'):
_formatters.emplace_back(new details::t_formatter());
break;
case('v'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::v_formatter()));
case ('v'):
_formatters.emplace_back(new details::v_formatter());
break;
case('a'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::a_formatter()));
case ('a'):
_formatters.emplace_back(new details::a_formatter());
break;
case('A'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::A_formatter()));
case ('A'):
_formatters.emplace_back(new details::A_formatter());
break;
case('b'):
case('h'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::b_formatter()));
case ('b'):
case ('h'):
_formatters.emplace_back(new details::b_formatter());
break;
case('B'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::B_formatter()));
case ('B'):
_formatters.emplace_back(new details::B_formatter());
break;
case('c'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::c_formatter()));
case ('c'):
_formatters.emplace_back(new details::c_formatter());
break;
case('C'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::C_formatter()));
case ('C'):
_formatters.emplace_back(new details::C_formatter());
break;
case('Y'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::Y_formatter()));
case ('Y'):
_formatters.emplace_back(new details::Y_formatter());
break;
case('D'):
case('x'):
case ('D'):
case ('x'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::D_formatter()));
_formatters.emplace_back(new details::D_formatter());
break;
case('m'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::m_formatter()));
case ('m'):
_formatters.emplace_back(new details::m_formatter());
break;
case('d'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::d_formatter()));
case ('d'):
_formatters.emplace_back(new details::d_formatter());
break;
case('H'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::H_formatter()));
case ('H'):
_formatters.emplace_back(new details::H_formatter());
break;
case('I'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::I_formatter()));
case ('I'):
_formatters.emplace_back(new details::I_formatter());
break;
case('M'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::M_formatter()));
case ('M'):
_formatters.emplace_back(new details::M_formatter());
break;
case('S'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::S_formatter()));
case ('S'):
_formatters.emplace_back(new details::S_formatter());
break;
case('e'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::e_formatter()));
case ('e'):
_formatters.emplace_back(new details::e_formatter());
break;
case('f'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::f_formatter()));
case ('f'):
_formatters.emplace_back(new details::f_formatter());
break;
case('F'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::F_formatter()));
case ('F'):
_formatters.emplace_back(new details::F_formatter());
break;
case('p'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::p_formatter()));
case ('E'):
_formatters.emplace_back(new details::E_formatter());
break;
case('r'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::r_formatter()));
case ('p'):
_formatters.emplace_back(new details::p_formatter());
break;
case('R'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::R_formatter()));
case ('r'):
_formatters.emplace_back(new details::r_formatter());
break;
case('T'):
case('X'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::T_formatter()));
case ('R'):
_formatters.emplace_back(new details::R_formatter());
break;
case('z'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::z_formatter()));
case ('T'):
case ('X'):
_formatters.emplace_back(new details::T_formatter());
break;
case ('z'):
_formatters.emplace_back(new details::z_formatter());
break;
case ('+'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::full_formatter()));
_formatters.emplace_back(new details::full_formatter());
break;
case ('P'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::pid_formatter()));
_formatters.emplace_back(new details::pid_formatter());
break;
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
case ('i'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::i_formatter()));
_formatters.emplace_back(new details::i_formatter());
break;
#endif
default: //Unknown flag appears as is
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter('%')));
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter(flag)));
case ('^'):
_formatters.emplace_back(new details::color_start_formatter());
break;
case ('$'):
_formatters.emplace_back(new details::color_stop_formatter());
break;
default: // Unknown flag appears as is
_formatters.emplace_back(new details::ch_formatter('%'));
_formatters.emplace_back(new details::ch_formatter(flag));
break;
}
}
inline std::tm spdlog::pattern_formatter::get_time(details::log_msg& msg)
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));
}
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
@@ -660,6 +702,6 @@ inline void spdlog::pattern_formatter::format(details::log_msg& msg)
{
f->format(msg, tm_time);
}
//write eol
msg.formatted.write(details::os::eol, details::os::eol_size);
// write eol
msg.formatted << _eol;
}

View File

@@ -10,10 +10,10 @@
// If user requests a non existing logger, nullptr will be returned
// This class is thread safe
#include "spdlog/details/null_mutex.h"
#include "spdlog/logger.h"
#include "spdlog/async_logger.h"
#include "spdlog/common.h"
#include "../async_logger.h"
#include "../common.h"
#include "../details/null_mutex.h"
#include "../logger.h"
#include <chrono>
#include <functional>
@@ -22,13 +22,14 @@
#include <string>
#include <unordered_map>
namespace spdlog
{
namespace details
{
template <class Mutex> class registry_t
namespace spdlog {
namespace details {
template<class Mutex>
class registry_t
{
public:
registry_t<Mutex>(const registry_t<Mutex> &) = delete;
registry_t<Mutex> &operator=(const registry_t<Mutex> &) = delete;
void register_logger(std::shared_ptr<logger> logger)
{
@@ -38,8 +39,7 @@ public:
_loggers[logger_name] = logger;
}
std::shared_ptr<logger> get(const std::string& logger_name)
std::shared_ptr<logger> get(const std::string &logger_name)
{
std::lock_guard<Mutex> lock(_mutex);
auto found = _loggers.find(logger_name);
@@ -47,46 +47,64 @@ public:
}
template<class It>
std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
std::shared_ptr<logger> create(const std::string &logger_name, const It &sinks_begin, const It &sinks_end)
{
std::lock_guard<Mutex> lock(_mutex);
throw_if_exists(logger_name);
std::shared_ptr<logger> new_logger;
if (_async_mode)
new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb);
{
new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy,
_worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb);
}
else
{
new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end);
}
if (_formatter)
{
new_logger->set_formatter(_formatter);
}
if (_err_handler)
{
new_logger->set_error_handler(_err_handler);
}
new_logger->set_level(_level);
new_logger->flush_on(_flush_level);
//Add to registry
// Add to registry
_loggers[logger_name] = 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::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);
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);
new_logger->flush_on(_flush_level);
//Add to registry
// Add to registry
_loggers[logger_name] = new_logger;
return new_logger;
}
@@ -95,10 +113,12 @@ public:
{
std::lock_guard<Mutex> lock(_mutex);
for (auto &l : _loggers)
{
fun(l.second);
}
}
void drop(const std::string& logger_name)
void drop(const std::string &logger_name)
{
std::lock_guard<Mutex> lock(_mutex);
_loggers.erase(logger_name);
@@ -109,58 +129,83 @@ public:
std::lock_guard<Mutex> lock(_mutex);
_loggers.clear();
}
std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks)
std::shared_ptr<logger> create(const std::string &logger_name, sinks_init_list sinks)
{
return create(logger_name, sinks.begin(), sinks.end());
}
std::shared_ptr<logger> create(const std::string& logger_name, sink_ptr sink)
std::shared_ptr<logger> create(const std::string &logger_name, sink_ptr sink)
{
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)
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());
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)
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 });
return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, {sink});
}
void formatter(formatter_ptr f)
{
std::lock_guard<Mutex> lock(_mutex);
_formatter = f;
for (auto& l : _loggers)
for (auto &l : _loggers)
{
l.second->set_formatter(_formatter);
}
}
void set_pattern(const std::string& pattern)
void set_pattern(const std::string &pattern)
{
std::lock_guard<Mutex> lock(_mutex);
_formatter = std::make_shared<pattern_formatter>(pattern);
for (auto& l : _loggers)
for (auto &l : _loggers)
{
l.second->set_formatter(_formatter);
}
}
void set_level(level::level_enum log_level)
{
std::lock_guard<Mutex> lock(_mutex);
for (auto& l : _loggers)
for (auto &l : _loggers)
{
l.second->set_level(log_level);
}
_level = log_level;
}
void flush_on(level::level_enum log_level)
{
std::lock_guard<Mutex> lock(_mutex);
for (auto &l : _loggers)
{
l.second->flush_on(log_level);
}
_flush_level = log_level;
}
void set_error_handler(log_err_handler handler)
{
for (auto& l : _loggers)
for (auto &l : _loggers)
{
l.second->set_error_handler(handler);
}
_err_handler = handler;
}
void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb,
const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb)
{
std::lock_guard<Mutex> lock(_mutex);
_async_mode = true;
@@ -177,38 +222,42 @@ public:
_async_mode = false;
}
static registry_t<Mutex>& instance()
static registry_t<Mutex> &instance()
{
static registry_t<Mutex> s_instance;
return s_instance;
}
private:
registry_t<Mutex>() {}
registry_t<Mutex>(const registry_t<Mutex>&) = delete;
registry_t<Mutex>& operator=(const registry_t<Mutex>&) = delete;
registry_t<Mutex>() = default;
void throw_if_exists(const std::string &logger_name)
{
if (_loggers.find(logger_name) != _loggers.end())
{
throw spdlog_ex("logger with name '" + logger_name + "' already exists");
}
}
Mutex _mutex;
std::unordered_map <std::string, std::shared_ptr<logger>> _loggers;
std::unordered_map<std::string, std::shared_ptr<logger>> _loggers;
formatter_ptr _formatter;
level::level_enum _level = level::info;
level::level_enum _flush_level = level::off;
log_err_handler _err_handler;
bool _async_mode = false;
size_t _async_q_size = 0;
async_overflow_policy _overflow_policy = async_overflow_policy::block_retry;
std::function<void()> _worker_warmup_cb = nullptr;
std::chrono::milliseconds _flush_interval_ms;
std::function<void()> _worker_teardown_cb = nullptr;
std::function<void()> _worker_warmup_cb;
std::chrono::milliseconds _flush_interval_ms{std::chrono::milliseconds::zero()};
std::function<void()> _worker_teardown_cb;
};
#ifdef SPDLOG_NO_REGISTRY_MUTEX
typedef registry_t<spdlog::details::null_mutex> registry;
using registry = registry_t<spdlog::details::null_mutex>;
#else
typedef registry_t<std::mutex> registry;
using registry = registry_t<std::mutex>;
#endif
}
}
} // namespace details
} // namespace spdlog

View File

@@ -8,23 +8,22 @@
//
// Global registry functions
//
#include "spdlog/spdlog.h"
#include "spdlog/details/registry.h"
#include "spdlog/sinks/file_sinks.h"
#include "spdlog/sinks/stdout_sinks.h"
#include "../details/registry.h"
#include "../sinks/file_sinks.h"
#include "../sinks/stdout_sinks.h"
#include "../spdlog.h"
#ifdef SPDLOG_ENABLE_SYSLOG
#include "spdlog/sinks/syslog_sink.h"
#include "../sinks/syslog_sink.h"
#endif
#ifdef _WIN32
#include "spdlog/sinks/wincolor_sink.h"
#if defined _WIN32 && !defined(__cplusplus_winrt)
#include "../sinks/wincolor_sink.h"
#else
#include "spdlog/sinks/ansicolor_sink.h"
#include "../sinks/ansicolor_sink.h"
#endif
#ifdef __ANDROID__
#include "spdlog/sinks/android_sink.h"
#include "../sinks/android_sink.h"
#endif
#include <chrono>
@@ -34,10 +33,10 @@
inline void spdlog::register_logger(std::shared_ptr<logger> logger)
{
return details::registry::instance().register_logger(logger);
return details::registry::instance().register_logger(std::move(logger));
}
inline std::shared_ptr<spdlog::logger> spdlog::get(const std::string& name)
inline std::shared_ptr<spdlog::logger> spdlog::get(const std::string &name)
{
return details::registry::instance().get(name);
}
@@ -48,58 +47,61 @@ inline void spdlog::drop(const std::string &name)
}
// Create multi/single threaded simple file logger
inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate)
inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate)
{
return create<spdlog::sinks::simple_file_sink_mt>(logger_name, filename, truncate);
}
inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate)
inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate)
{
return create<spdlog::sinks::simple_file_sink_st>(logger_name, filename, truncate);
}
// Create multi/single threaded rotating file logger
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
{
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
}
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
{
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
}
// Create file logger which creates new file at midnight):
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute)
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(
const std::string &logger_name, const filename_t &filename, int hour, int minute)
{
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, hour, minute);
}
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour, int minute)
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(
const std::string &logger_name, const filename_t &filename, int hour, int minute)
{
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, hour, minute);
}
//
// stdout/stderr loggers
//
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name)
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string &logger_name)
{
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name)
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string &logger_name)
{
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name)
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string &logger_name)
{
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name)
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string &logger_name)
{
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance());
}
@@ -107,53 +109,53 @@ inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::strin
//
// stdout/stderr color loggers
//
#ifdef _WIN32
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
#if defined _WIN32 && !defined(__cplusplus_winrt)
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string &logger_name)
{
auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string &logger_name)
{
auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string &logger_name)
{
auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string &logger_name)
{
auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
#else //ansi terminal colors
#else // ansi terminal colors
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_stdout_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string &logger_name)
{
auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string &logger_name)
{
auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string &logger_name)
{
auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink);
@@ -162,71 +164,78 @@ inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string
#ifdef SPDLOG_ENABLE_SYSLOG
// Create syslog logger
inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option)
inline std::shared_ptr<spdlog::logger> spdlog::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
#ifdef __ANDROID__
inline std::shared_ptr<spdlog::logger> spdlog::android_logger(const std::string& logger_name, const std::string& tag)
inline std::shared_ptr<spdlog::logger> spdlog::android_logger(const std::string &logger_name, const std::string &tag)
{
return create<spdlog::sinks::android_sink>(logger_name, tag);
}
#endif
// Create and register a logger a single sink
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const spdlog::sink_ptr& sink)
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string &logger_name, const spdlog::sink_ptr &sink)
{
return details::registry::instance().create(logger_name, sink);
}
//Create logger with multiple sinks
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, spdlog::sinks_init_list sinks)
// Create logger with multiple sinks
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string &logger_name, spdlog::sinks_init_list sinks)
{
return details::registry::instance().create(logger_name, sinks);
}
template <typename Sink, typename... Args>
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, Args... args)
template<typename Sink, typename... Args>
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string &logger_name, Args... args)
{
sink_ptr sink = std::make_shared<Sink>(args...);
return details::registry::instance().create(logger_name, { sink });
return details::registry::instance().create(logger_name, {sink});
}
template<class It>
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string &logger_name, const It &sinks_begin, const It &sinks_end)
{
return details::registry::instance().create(logger_name, sinks_begin, sinks_end);
}
// Create and register an async logger with a single sink
inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, const sink_ptr& sink, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
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);
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 )
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);
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)
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);
return details::registry::instance().create_async(
logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks_begin, sinks_end);
}
inline void spdlog::set_formatter(spdlog::formatter_ptr f)
{
details::registry::instance().formatter(f);
details::registry::instance().formatter(std::move(f));
}
inline void spdlog::set_pattern(const std::string& format_string)
inline void spdlog::set_pattern(const std::string &format_string)
{
return details::registry::instance().set_pattern(format_string);
}
@@ -236,13 +245,19 @@ inline void spdlog::set_level(level::level_enum log_level)
return details::registry::instance().set_level(log_level);
}
inline void spdlog::set_error_handler(log_err_handler handler)
inline void spdlog::flush_on(level::level_enum log_level)
{
return details::registry::instance().set_error_handler(handler);
return details::registry::instance().flush_on(log_level);
}
inline void spdlog::set_error_handler(log_err_handler handler)
{
return details::registry::instance().set_error_handler(std::move(handler));
}
inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy,
const std::function<void()> &worker_warmup_cb, const std::chrono::milliseconds &flush_interval_ms,
const std::function<void()> &worker_teardown_cb)
{
details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb);
}
@@ -254,7 +269,7 @@ inline void spdlog::set_sync_mode()
inline void spdlog::apply_all(std::function<void(std::shared_ptr<logger>)> fun)
{
details::registry::instance().apply_all(fun);
details::registry::instance().apply_all(std::move(fun));
}
inline void spdlog::drop_all()

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

@@ -72,9 +72,11 @@
// Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available.
FMT_MAYBE_UNUSED
static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
return fmt::internal::Null<>();
}
FMT_MAYBE_UNUSED
static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
return fmt::internal::Null<>();
}
@@ -121,7 +123,7 @@ typedef void (*FormatFunc)(Writer &, int, StringRef);
// Buffer should be at least of size 1.
int safe_strerror(
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer");
FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer");
class StrError {
private:
@@ -159,6 +161,11 @@ int safe_strerror(
ERANGE : result;
}
#ifdef __c2__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
// Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(internal::Null<>) {
errno = 0;
@@ -166,13 +173,15 @@ int safe_strerror(
return errno;
}
#ifdef __c2__
# pragma clang diagnostic pop
#endif
public:
StrError(int err_code, char *&buf, std::size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
int run() {
// Suppress a warning about unused strerror_r.
strerror_r(0, FMT_NULL, "");
return handle(strerror_r(error_code_, buffer_, buffer_size_));
}
};
@@ -396,51 +405,6 @@ FMT_FUNC void format_system_error(
fmt::format_error_code(out, error_code, message); // 'fmt::' is for bcc32.
}
template <typename Char>
void internal::ArgMap<Char>::init(const ArgList &args) {
if (!map_.empty())
return;
typedef internal::NamedArg<Char> NamedArg;
const NamedArg *named_arg = FMT_NULL;
bool use_values =
args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE;
if (use_values) {
for (unsigned i = 0;/*nothing*/; ++i) {
internal::Arg::Type arg_type = args.type(i);
switch (arg_type) {
case internal::Arg::NONE:
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.values_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/;
}
}
return;
}
for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) {
internal::Arg::Type arg_type = args.type(i);
if (arg_type == internal::Arg::NAMED_ARG) {
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
}
}
for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) {
switch (args.args_[i].type) {
case internal::Arg::NONE:
return;
case internal::Arg::NAMED_ARG:
named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
map_.push_back(Pair(named_arg->name, *named_arg));
break;
default:
/*nothing*/;
}
}
}
template <typename Char>
void internal::FixedBuffer<Char>::grow(std::size_t) {
FMT_THROW(std::runtime_error("buffer overflow"));
@@ -502,8 +466,6 @@ template struct internal::BasicData<void>;
template void internal::FixedBuffer<char>::grow(std::size_t);
template void internal::ArgMap<char>::init(const ArgList &args);
template FMT_API int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, double value);
@@ -516,8 +478,6 @@ template FMT_API int internal::CharTraits<char>::format_float(
template void internal::FixedBuffer<wchar_t>::grow(std::size_t);
template void internal::ArgMap<wchar_t>::init(const ArgList &args);
template FMT_API int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, double value);

File diff suppressed because it is too large Load Diff

View File

@@ -17,72 +17,84 @@ namespace fmt {
namespace internal {
template <class Char>
class FormatBuf : public std::basic_streambuf<Char> {
private:
typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
template<class Char>
class FormatBuf : public std::basic_streambuf<Char>
{
private:
typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
Buffer<Char> &buffer_;
Buffer<Char> &buffer_;
public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer) {}
public:
FormatBuf(Buffer<Char> &buffer)
: buffer_(buffer)
{
}
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.
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()))
buffer_.push_back(static_cast<Char>(ch));
return ch;
}
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE
{
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<Char>(ch));
return ch;
}
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE {
buffer_.append(s, s + count);
return count;
}
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE
{
buffer_.append(s, s + count);
return count;
}
};
Yes &convert(std::ostream &);
struct DummyStream : std::ostream {
DummyStream(); // Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream.
void operator<<(Null<>);
struct DummyStream : std::ostream
{
DummyStream(); // Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream.
template<typename T>
typename EnableIf<sizeof(T) == 0>::type operator<<(const T &);
};
No &operator<<(std::ostream &, int);
template<typename T>
struct ConvertToIntImpl<T, true> {
// Convert to int only if T doesn't have an overloaded operator<<.
enum {
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
struct ConvertToIntImpl<T, true>
{
// Convert to int only if T doesn't have an overloaded operator<<.
enum
{
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
};
// Write the content of w to os.
FMT_API void write(std::ostream &os, Writer &w);
} // namespace internal
} // namespace internal
// Formats a value.
template <typename Char, typename ArgFormatter_, typename T>
void format_arg(BasicFormatter<Char, ArgFormatter_> &f,
const Char *&format_str, const T &value) {
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
template<typename Char, typename ArgFormatter_, typename T>
void format_arg(BasicFormatter<Char, ArgFormatter_> &f, const Char *&format_str, const T &value)
{
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
internal::FormatBuf<Char> format_buf(buffer);
std::basic_ostream<Char> output(&format_buf);
output << value;
internal::FormatBuf<Char> format_buf(buffer);
std::basic_ostream<Char> output(&format_buf);
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value;
BasicStringRef<Char> str(&buffer[0], buffer.size());
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
format_str = f.format(format_str, MakeArg(str));
BasicStringRef<Char> str(&buffer[0], buffer.size());
typedef internal::MakeArg<BasicFormatter<Char>> MakeArg;
format_str = f.format(format_str, MakeArg(str));
}
/**
@@ -96,10 +108,10 @@ void format_arg(BasicFormatter<Char, ArgFormatter_> &f,
*/
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(void, print, std::ostream &, CStringRef)
} // namespace fmt
} // namespace fmt
#ifdef FMT_HEADER_ONLY
# include "ostream.cc"
#include "ostream.cc"
#endif
#endif // FMT_OSTREAM_H_
#endif // FMT_OSTREAM_H_

View File

@@ -12,54 +12,55 @@
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#undef __STRICT_ANSI__
#endif
#include <errno.h>
#include <fcntl.h> // for O_RDONLY
#include <locale.h> // for locale_t
#include <fcntl.h> // for O_RDONLY
#include <locale.h> // for locale_t
#include <stdio.h>
#include <stdlib.h> // for strtod_l
#include <stdlib.h> // for strtod_l
#include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
#if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#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)
#define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) call
# ifdef _WIN32
#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
#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)
#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)
#define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
@@ -67,109 +68,128 @@
namespace fmt {
// An error code.
class ErrorCode {
private:
int value_;
class ErrorCode
{
private:
int value_;
public:
explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {}
public:
explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; }
int get() const FMT_NOEXCEPT
{
return value_;
}
};
// A buffered file.
class BufferedFile {
private:
FILE *file_;
class BufferedFile
{
private:
FILE *file_;
friend class 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;
};
explicit BufferedFile(FILE *f)
: file_(f)
{
}
public:
// A "move constructor" for moving from a temporary.
BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {}
// Constructs a BufferedFile object which doesn't represent any file.
BufferedFile() FMT_NOEXCEPT : file_(FMT_NULL) {}
// A "move constructor" for moving from an lvalue.
BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) {
f.file_ = FMT_NULL;
}
// Destroys the object closing the file it represents if any.
FMT_API ~BufferedFile() FMT_NOEXCEPT;
// A "move assignment operator" for moving from a temporary.
BufferedFile &operator=(Proxy p) {
close();
file_ = p.file;
return *this;
}
#if !FMT_USE_RVALUE_REFERENCES
// Emulate a move constructor and a move assignment operator if rvalue
// references are not supported.
// A "move assignment operator" for moving from an lvalue.
BufferedFile &operator=(BufferedFile &other) {
close();
file_ = other.file_;
other.file_ = FMT_NULL;
return *this;
}
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy
{
FILE *file;
};
// Returns a proxy object for moving from a temporary:
// BufferedFile file = BufferedFile(...);
operator Proxy() FMT_NOEXCEPT {
Proxy p = {file_};
file_ = FMT_NULL;
return p;
}
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);
private:
FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
public:
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = FMT_NULL;
}
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;
}
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);
// Opens a file.
FMT_API BufferedFile(CStringRef filename, CStringRef mode);
// Closes the file.
FMT_API void close();
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE *get() const FMT_NOEXCEPT { return file_; }
// 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;
// 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)
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.
@@ -178,190 +198,226 @@ public:
// 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.
class File
{
private:
int fd_; // File descriptor.
// Constructs a File object with a given descriptor.
explicit File(int fd) : fd_(fd) {}
// 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.
};
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) {}
// 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);
// 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.
// 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;
};
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) {}
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 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 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;
}
// 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;
}
// 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);
private:
FMT_DISALLOW_COPY_AND_ASSIGN(File);
public:
File(File &&other) FMT_NOEXCEPT : fd_(other.fd_) {
other.fd_ = -1;
}
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;
}
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;
// 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_; }
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT
{
return fd_;
}
// Closes the file.
FMT_API void close();
// 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;
// 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 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);
// 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);
// 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);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT;
// 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 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);
// 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
#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;
class Locale
{
private:
#ifdef _MSC_VER
typedef _locale_t locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC };
enum
{
LC_NUMERIC_MASK = LC_NUMERIC
};
static locale_t newlocale(int category_mask, const char *locale, locale_t) {
return _create_locale(category_mask, locale);
}
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 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
static double strtod_l(const char *nptr, char **endptr, _locale_t locale)
{
return _strtod_l(nptr, endptr, locale);
}
#endif
locale_t locale_;
locale_t locale_;
FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
public:
typedef locale_t Type;
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_); }
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_; }
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;
}
// 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
#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; }
inline fmt::BufferedFile &move(fmt::BufferedFile &f)
{
return f;
}
inline fmt::File &move(fmt::File &f)
{
return f;
}
} // namespace std
#endif
#endif // FMT_POSIX_H_
#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

@@ -0,0 +1,723 @@
/*
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
#include <algorithm> // std::fill_n
#include <limits> // std::numeric_limits
#include "ostream.h"
namespace fmt {
namespace internal {
// Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers.
template<bool IsSigned>
struct IntChecker
{
template<typename T>
static bool fits_in_int(T value)
{
unsigned max = std::numeric_limits<int>::max();
return value <= max;
}
static bool fits_in_int(bool)
{
return true;
}
};
template<>
struct IntChecker<true>
{
template<typename T>
static bool fits_in_int(T value)
{
return value >= std::numeric_limits<int>::min() && value <= std::numeric_limits<int>::max();
}
static bool fits_in_int(int)
{
return true;
}
};
class PrecisionHandler : public ArgVisitor<PrecisionHandler, int>
{
public:
void report_unhandled_arg()
{
FMT_THROW(FormatError("precision is not integer"));
}
template<typename T>
int visit_any_int(T value)
{
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
FMT_THROW(FormatError("number is too big"));
return static_cast<int>(value);
}
};
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt : public ArgVisitor<IsZeroInt, bool>
{
public:
template<typename T>
bool visit_any_int(T value)
{
return value == 0;
}
};
// returns the default type for format specific "%s"
class DefaultType : public ArgVisitor<DefaultType, char>
{
public:
char visit_char(int)
{
return 'c';
}
char visit_bool(bool)
{
return 's';
}
char visit_pointer(const void *)
{
return 'p';
}
template<typename T>
char visit_any_int(T)
{
return 'd';
}
template<typename T>
char visit_any_double(T)
{
return 'g';
}
char visit_unhandled_arg()
{
return 's';
}
};
template<typename T, typename U>
struct is_same
{
enum
{
value = 0
};
};
template<typename T>
struct is_same<T, T>
{
enum
{
value = 1
};
};
// An argument visitor that converts an integer argument to T for printf,
// if T is an integral type. If T is void, the argument is converted to
// corresponding signed or unsigned type depending on the type specifier:
// 'd' and 'i' - signed, other - unsigned)
template<typename T = void>
class ArgConverter : public ArgVisitor<ArgConverter<T>, void>
{
private:
internal::Arg &arg_;
wchar_t type_;
FMT_DISALLOW_COPY_AND_ASSIGN(ArgConverter);
public:
ArgConverter(internal::Arg &arg, wchar_t type)
: arg_(arg)
, type_(type)
{
}
void visit_bool(bool value)
{
if (type_ != 's')
visit_any_int(value);
}
void visit_char(int value)
{
if (type_ != 's')
visit_any_int(value);
}
template<typename U>
void visit_any_int(U value)
{
bool is_signed = type_ == 'd' || type_ == 'i';
if (type_ == 's')
{
is_signed = std::numeric_limits<U>::is_signed;
}
using internal::Arg;
typedef typename internal::Conditional<is_same<T, void>::value, U, T>::type TargetType;
if (const_check(sizeof(TargetType) <= sizeof(int)))
{
// Extra casts are used to silence warnings.
if (is_signed)
{
arg_.type = Arg::INT;
arg_.int_value = static_cast<int>(static_cast<TargetType>(value));
}
else
{
arg_.type = Arg::UINT;
typedef typename internal::MakeUnsigned<TargetType>::Type Unsigned;
arg_.uint_value = static_cast<unsigned>(static_cast<Unsigned>(value));
}
}
else
{
if (is_signed)
{
arg_.type = Arg::LONG_LONG;
// glibc's printf doesn't sign extend arguments of smaller types:
// std::printf("%lld", -42); // prints "4294967254"
// but we don't have to do the same because it's a UB.
arg_.long_long_value = static_cast<LongLong>(value);
}
else
{
arg_.type = Arg::ULONG_LONG;
arg_.ulong_long_value = static_cast<typename internal::MakeUnsigned<U>::Type>(value);
}
}
}
};
// Converts an integer argument to char for printf.
class CharConverter : public ArgVisitor<CharConverter, void>
{
private:
internal::Arg &arg_;
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
public:
explicit CharConverter(internal::Arg &arg)
: arg_(arg)
{
}
template<typename T>
void visit_any_int(T value)
{
arg_.type = internal::Arg::CHAR;
arg_.int_value = static_cast<char>(value);
}
};
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
class WidthHandler : public ArgVisitor<WidthHandler, unsigned>
{
private:
FormatSpec &spec_;
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
public:
explicit WidthHandler(FormatSpec &spec)
: spec_(spec)
{
}
void report_unhandled_arg()
{
FMT_THROW(FormatError("width is not integer"));
}
template<typename T>
unsigned visit_any_int(T value)
{
typedef typename internal::IntTraits<T>::MainType UnsignedType;
UnsignedType width = static_cast<UnsignedType>(value);
if (internal::is_negative(value))
{
spec_.align_ = ALIGN_LEFT;
width = 0 - width;
}
unsigned int_max = std::numeric_limits<int>::max();
if (width > int_max)
FMT_THROW(FormatError("number is too big"));
return static_cast<unsigned>(width);
}
};
} // namespace internal
/**
\rst
A ``printf`` argument formatter based on the `curiously recurring template
pattern <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_.
To use `~fmt::BasicPrintfArgFormatter` define a subclass that implements some
or all of the visit methods with the same signatures as the methods in
`~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`.
Pass the subclass as the *Impl* template parameter. When a formatting
function processes an argument, it will dispatch to a visit method
specific to the argument type. For example, if the argument type is
``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,
then a corresponding method of `~fmt::BasicPrintfArgFormatter` or its
superclass will be called.
\endrst
*/
template<typename Impl, typename Char, typename Spec>
class BasicPrintfArgFormatter : public internal::ArgFormatterBase<Impl, Char, Spec>
{
private:
void write_null_pointer()
{
this->spec().type_ = 0;
this->write("(nil)");
}
typedef internal::ArgFormatterBase<Impl, Char, Spec> Base;
public:
/**
\rst
Constructs an argument formatter object.
*writer* is a reference to the output writer and *spec* contains format
specifier information for standard argument types.
\endrst
*/
BasicPrintfArgFormatter(BasicWriter<Char> &w, Spec &s)
: internal::ArgFormatterBase<Impl, Char, Spec>(w, s)
{
}
/** Formats an argument of type ``bool``. */
void visit_bool(bool value)
{
Spec &fmt_spec = this->spec();
if (fmt_spec.type_ != 's')
return this->visit_any_int(value);
fmt_spec.type_ = 0;
this->write(value);
}
/** Formats a character. */
void visit_char(int value)
{
const Spec &fmt_spec = this->spec();
BasicWriter<Char> &w = this->writer();
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
w.write_int(value, fmt_spec);
typedef typename BasicWriter<Char>::CharPtr CharPtr;
CharPtr out = CharPtr();
if (fmt_spec.width_ > 1)
{
Char fill = ' ';
out = w.grow_buffer(fmt_spec.width_);
if (fmt_spec.align_ != ALIGN_LEFT)
{
std::fill_n(out, fmt_spec.width_ - 1, fill);
out += fmt_spec.width_ - 1;
}
else
{
std::fill_n(out + 1, fmt_spec.width_ - 1, fill);
}
}
else
{
out = w.grow_buffer(1);
}
*out = static_cast<Char>(value);
}
/** Formats a null-terminated C string. */
void visit_cstring(const char *value)
{
if (value)
Base::visit_cstring(value);
else if (this->spec().type_ == 'p')
write_null_pointer();
else
this->write("(null)");
}
/** Formats a pointer. */
void visit_pointer(const void *value)
{
if (value)
return Base::visit_pointer(value);
this->spec().type_ = 0;
write_null_pointer();
}
/** Formats an argument of a custom (user-defined) type. */
void visit_custom(internal::Arg::CustomValue c)
{
BasicFormatter<Char> formatter(ArgList(), this->writer());
const Char format_str[] = {'}', 0};
const Char *format = format_str;
c.format(&formatter, c.value, &format);
}
};
/** The default printf argument formatter. */
template<typename Char>
class PrintfArgFormatter : public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>
{
public:
/** Constructs an argument formatter object. */
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>(w, s)
{
}
};
/** This template formats data and writes the output to a writer. */
template<typename Char, typename ArgFormatter = PrintfArgFormatter<Char>>
class PrintfFormatter : private internal::FormatterBase
{
private:
BasicWriter<Char> &writer_;
void parse_flags(FormatSpec &spec, const Char *&s);
// Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument.
internal::Arg get_arg(const Char *s, unsigned arg_index = (std::numeric_limits<unsigned>::max)());
// Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char *&s, FormatSpec &spec);
public:
/**
\rst
Constructs a ``PrintfFormatter`` object. References to the arguments and
the writer are stored in the formatter object so make sure they have
appropriate lifetimes.
\endrst
*/
explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w)
: FormatterBase(al)
, writer_(w)
{
}
/** Formats stored arguments and writes the output to the writer. */
void format(BasicCStringRef<Char> format_str);
};
template<typename Char, typename AF>
void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s)
{
for (;;)
{
switch (*s++)
{
case '-':
spec.align_ = ALIGN_LEFT;
break;
case '+':
spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
break;
case '0':
spec.fill_ = '0';
break;
case ' ':
spec.flags_ |= SIGN_FLAG;
break;
case '#':
spec.flags_ |= HASH_FLAG;
break;
default:
--s;
return;
}
}
}
template<typename Char, typename AF>
internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s, unsigned arg_index)
{
(void)s;
const char *error = FMT_NULL;
internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ? next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
if (error)
FMT_THROW(FormatError(!*s ? "invalid format string" : error));
return arg;
}
template<typename Char, typename AF>
unsigned PrintfFormatter<Char, AF>::parse_header(const Char *&s, FormatSpec &spec)
{
unsigned arg_index = std::numeric_limits<unsigned>::max();
Char c = *s;
if (c >= '0' && c <= '9')
{
// Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s).
unsigned value = internal::parse_nonnegative_int(s);
if (*s == '$') // value is an argument index
{
++s;
arg_index = value;
}
else
{
if (c == '0')
spec.fill_ = '0';
if (value != 0)
{
// Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now.
spec.width_ = value;
return arg_index;
}
}
}
parse_flags(spec, s);
// Parse width.
if (*s >= '0' && *s <= '9')
{
spec.width_ = internal::parse_nonnegative_int(s);
}
else if (*s == '*')
{
++s;
spec.width_ = internal::WidthHandler(spec).visit(get_arg(s));
}
return arg_index;
}
template<typename Char, typename AF>
void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
{
const Char *start = format_str.c_str();
const Char *s = start;
while (*s)
{
Char c = *s++;
if (c != '%')
continue;
if (*s == c)
{
write(writer_, start, s);
start = ++s;
continue;
}
write(writer_, start, s - 1);
FormatSpec spec;
spec.align_ = ALIGN_RIGHT;
// Parse argument index, flags and width.
unsigned arg_index = parse_header(s, spec);
// Parse precision.
if (*s == '.')
{
++s;
if ('0' <= *s && *s <= '9')
{
spec.precision_ = static_cast<int>(internal::parse_nonnegative_int(s));
}
else if (*s == '*')
{
++s;
spec.precision_ = internal::PrecisionHandler().visit(get_arg(s));
}
else
{
spec.precision_ = 0;
}
}
using internal::Arg;
Arg arg = get_arg(s, arg_index);
if (spec.flag(HASH_FLAG) && internal::IsZeroInt().visit(arg))
spec.flags_ &= ~internal::to_unsigned<int>(HASH_FLAG);
if (spec.fill_ == '0')
{
if (arg.type <= Arg::LAST_NUMERIC_TYPE)
spec.align_ = ALIGN_NUMERIC;
else
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
}
// Parse length and convert the argument to the required type.
using internal::ArgConverter;
switch (*s++)
{
case 'h':
if (*s == 'h')
ArgConverter<signed char>(arg, *++s).visit(arg);
else
ArgConverter<short>(arg, *s).visit(arg);
break;
case 'l':
if (*s == 'l')
ArgConverter<fmt::LongLong>(arg, *++s).visit(arg);
else
ArgConverter<long>(arg, *s).visit(arg);
break;
case 'j':
ArgConverter<intmax_t>(arg, *s).visit(arg);
break;
case 'z':
ArgConverter<std::size_t>(arg, *s).visit(arg);
break;
case 't':
ArgConverter<std::ptrdiff_t>(arg, *s).visit(arg);
break;
case 'L':
// printf produces garbage when 'L' is omitted for long double, no
// need to do the same.
break;
default:
--s;
ArgConverter<void>(arg, *s).visit(arg);
}
// Parse type.
if (!*s)
FMT_THROW(FormatError("invalid format string"));
spec.type_ = static_cast<char>(*s++);
if (spec.type_ == 's')
{
// set the format type to the default if 's' is specified
spec.type_ = internal::DefaultType().visit(arg);
}
if (arg.type <= Arg::LAST_INTEGER_TYPE)
{
// Normalize type.
switch (spec.type_)
{
case 'i':
case 'u':
spec.type_ = 'd';
break;
case 'c':
// TODO: handle wchar_t
internal::CharConverter(arg).visit(arg);
break;
}
}
start = s;
// Format argument.
AF(writer_, spec).visit(arg);
}
write(writer_, start, s);
}
inline void printf(Writer &w, CStringRef format, ArgList args)
{
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
Formats arguments and returns the result as a string.
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
inline std::string sprintf(CStringRef format, ArgList args)
{
MemoryWriter w;
printf(w, format, args);
return w.str();
}
FMT_VARIADIC(std::string, sprintf, CStringRef)
inline std::wstring sprintf(WCStringRef format, ArgList args)
{
WMemoryWriter w;
printf(w, format, args);
return w.str();
}
FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef)
/**
\rst
Prints formatted data to the file *f*.
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args);
FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
/**
\rst
Prints formatted data to ``stdout``.
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
inline int printf(CStringRef format, ArgList args)
{
return fprintf(stdout, format, args);
}
FMT_VARIADIC(int, printf, CStringRef)
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args)
{
MemoryWriter w;
printf(w, format_str, args);
internal::write(os, w);
return static_cast<int>(w.size());
}
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY
#include "printf.cc"
#endif
#endif // FMT_PRINTF_H_

View File

@@ -14,130 +14,175 @@
#include <ctime>
#ifdef _MSC_VER
# pragma warning(push)
# pragma warning(disable: 4702) // unreachable code
# pragma warning(disable: 4996) // "deprecated" functions
#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;
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));
}
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;
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<>(); }
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<>();
}
} // namespace internal
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) {
struct LocalTime {
std::time_t time_;
std::tm tm_;
inline std::tm localtime(std::time_t time)
{
struct LocalTime
{
std::time_t time_;
std::tm tm_;
LocalTime(std::time_t t): time_(t) {}
LocalTime(std::time_t t)
: time_(t)
{
}
bool run() {
using namespace fmt::internal;
return handle(localtime_r(&time_, &tm_));
}
bool run()
{
using namespace fmt::internal;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm *tm) { return tm != FMT_NULL; }
bool handle(std::tm *tm)
{
return tm != FMT_NULL;
}
bool handle(internal::Null<>) {
using namespace fmt::internal;
return fallback(localtime_s(&tm_, &time_));
}
bool handle(internal::Null<>)
{
using namespace fmt::internal;
return fallback(localtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
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();
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_;
inline std::tm gmtime(std::time_t time)
{
struct GMTime
{
std::time_t time_;
std::tm tm_;
GMTime(std::time_t t): time_(t) {}
GMTime(std::time_t t)
: time_(t)
{
}
bool run() {
using namespace fmt::internal;
return handle(gmtime_r(&time_, &tm_));
}
bool run()
{
using namespace fmt::internal;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm *tm) { return tm != FMT_NULL; }
bool handle(std::tm *tm)
{
return tm != FMT_NULL;
}
bool handle(internal::Null<>) {
using namespace fmt::internal;
return fallback(gmtime_s(&tm_, &time_));
}
bool handle(internal::Null<>)
{
using namespace fmt::internal;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
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();
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
} // namespace fmt
#ifdef _MSC_VER
# pragma warning(pop)
#pragma warning(pop)
#endif
#endif // FMT_TIME_H_
#endif // FMT_TIME_H_

View File

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

View File

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

View File

@@ -5,43 +5,42 @@
#pragma once
#include "spdlog/details/log_msg.h"
#include "details/log_msg.h"
#include <vector>
#include <string>
#include <memory>
#include <string>
#include <vector>
namespace spdlog
{
namespace details
{
namespace spdlog {
namespace details {
class flag_formatter;
}
class formatter
{
public:
virtual ~formatter() {}
virtual void format(details::log_msg& msg) = 0;
virtual ~formatter() = default;
virtual void format(details::log_msg &msg) = 0;
};
class pattern_formatter SPDLOG_FINAL : public formatter
{
public:
explicit pattern_formatter(const std::string& pattern, pattern_time_type pattern_time = pattern_time_type::local);
pattern_formatter(const pattern_formatter&) = delete;
pattern_formatter& operator=(const pattern_formatter&) = delete;
void format(details::log_msg& msg) override;
explicit pattern_formatter(const std::string &pattern, pattern_time_type pattern_time = pattern_time_type::local,
std::string eol = spdlog::details::os::default_eol);
pattern_formatter(const pattern_formatter &) = delete;
pattern_formatter &operator=(const pattern_formatter &) = delete;
void format(details::log_msg &msg) override;
private:
const std::string _eol;
const std::string _pattern;
const pattern_time_type _pattern_time;
std::vector<std::unique_ptr<details::flag_formatter>> _formatters;
std::tm get_time(details::log_msg& msg);
std::tm get_time(details::log_msg &msg);
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"
} // namespace spdlog
#include "details/pattern_formatter_impl.h"

View File

@@ -12,111 +12,131 @@
// 2. Format the message using the formatter function
// 3. Pass the formatted message to its sinks to performa the actual logging
#include "spdlog/sinks/base_sink.h"
#include "spdlog/common.h"
#include "common.h"
#include "sinks/base_sink.h"
#include <vector>
#include <memory>
#include <string>
#include <vector>
namespace spdlog
{
namespace spdlog {
class logger
{
public:
logger(const std::string& logger_name, sink_ptr single_sink);
logger(const std::string& name, sinks_init_list);
logger(const std::string &name, sink_ptr single_sink);
logger(const std::string &name, sinks_init_list sinks);
template<class It>
logger(const std::string& name, const It& begin, const It& end);
logger(std::string name, const It &begin, const It &end);
virtual ~logger();
logger(const logger&) = delete;
logger& operator=(const logger&) = delete;
logger(const logger &) = delete;
logger &operator=(const logger &) = delete;
template <typename... Args> void log(level::level_enum lvl, const char* fmt, const Args&... args);
template <typename... Args> void log(level::level_enum lvl, const char* msg);
template <typename Arg1, typename... Args> void trace(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void debug(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void info(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void warn(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void error(const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void critical(const char* fmt, const Arg1&, const Args&... args);
template<typename... Args>
void log(level::level_enum lvl, const char *fmt, const Args &... args);
template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const char* fmt, const Args&... args);
template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const char* msg);
template <typename Arg1, typename... Args> void trace_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void debug_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void info_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void warn_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void error_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
template <typename Arg1, typename... Args> void critical_if(const bool flag, const char* fmt, const Arg1&, const Args&... args);
template<typename... Args>
void log(level::level_enum lvl, const char *msg);
template<typename Arg1, typename... Args>
void trace(const char *fmt, const Arg1 &, const Args &... args);
template<typename Arg1, typename... Args>
void debug(const char *fmt, const Arg1 &, const Args &... args);
template<typename Arg1, typename... Args>
void info(const char *fmt, const Arg1 &, const Args &... args);
template<typename Arg1, typename... Args>
void warn(const char *fmt, const Arg1 &, const Args &... args);
template<typename Arg1, typename... Args>
void error(const char *fmt, const Arg1 &, const Args &... args);
template<typename Arg1, typename... Args>
void critical(const char *fmt, const Arg1 &, const Args &... args);
#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);
template<typename... Args>
void log(level::level_enum lvl, const wchar_t *msg);
template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const wchar_t* msg);
template <typename... Args> void log_if(const bool flag, level::level_enum lvl, const wchar_t* fmt, const Args&... args);
template <typename... Args> void trace_if(const bool flag, const wchar_t* fmt, const Args&... args);
template <typename... Args> void debug_if(const bool flag, const wchar_t* fmt, const Args&... args);
template <typename... Args> void info_if(const bool flag, const wchar_t* fmt, const Args&... args);
template <typename... Args> void warn_if(const bool flag, const wchar_t* fmt, const Args&... args);
template <typename... Args> void error_if(const bool flag, const wchar_t* fmt, const Args&... args);
template <typename... Args> void critical_if(const bool flag, const wchar_t* fmt, const Args&... args);
template<typename... Args>
void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args);
template<typename... Args>
void trace(const wchar_t *fmt, const Args &... args);
template<typename... Args>
void debug(const wchar_t *fmt, const Args &... args);
template<typename... Args>
void info(const wchar_t *fmt, const Args &... args);
template<typename... Args>
void warn(const wchar_t *fmt, const Args &... args);
template<typename... Args>
void error(const wchar_t *fmt, const Args &... args);
template<typename... Args>
void critical(const wchar_t *fmt, const Args &... args);
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
template <typename T> void log(level::level_enum lvl, const T&);
template <typename T> void trace(const T&);
template <typename T> void debug(const T&);
template <typename T> void info(const T&);
template <typename T> void warn(const T&);
template <typename T> void error(const T&);
template <typename T> void critical(const T&);
template<typename T>
void log(level::level_enum lvl, const T &);
template <typename T> void log_if(const bool flag, level::level_enum lvl, const T&);
template <typename T> void trace_if(const bool flag, const T&);
template <typename T> void debug_if(const bool flag, const T&);
template <typename T> void info_if(const bool flag, const T&);
template <typename T> void warn_if(const bool flag, const T&);
template <typename T> void error_if(const bool flag, const T&);
template <typename T> void critical_if(const bool flag, const T&);
template<typename T>
void trace(const T &msg);
bool should_log(level::level_enum) const;
void set_level(level::level_enum);
template<typename T>
void debug(const T &msg);
template<typename T>
void info(const T &msg);
template<typename T>
void warn(const T &msg);
template<typename T>
void error(const T &msg);
template<typename T>
void critical(const T &msg);
bool should_log(level::level_enum msg_level) const;
void set_level(level::level_enum log_level);
level::level_enum level() const;
const std::string& name() const;
void set_pattern(const std::string&, pattern_time_type = pattern_time_type::local);
void set_formatter(formatter_ptr);
const std::string &name() const;
void set_pattern(const std::string &pattern, pattern_time_type pattern_time = pattern_time_type::local);
void set_formatter(formatter_ptr msg_formatter);
// automatically call flush() if message level >= log_level
void flush_on(level::level_enum log_level);
virtual void flush();
const std::vector<sink_ptr>& sinks() const;
const std::vector<sink_ptr> &sinks() const;
// error handler
virtual void set_error_handler(log_err_handler);
virtual void set_error_handler(log_err_handler err_handler);
virtual log_err_handler error_handler();
protected:
virtual void _sink_it(details::log_msg&);
virtual void _set_pattern(const std::string&, pattern_time_type);
virtual void _set_formatter(formatter_ptr);
virtual void _sink_it(details::log_msg &msg);
virtual void _set_pattern(const std::string &pattern, pattern_time_type pattern_time);
virtual void _set_formatter(formatter_ptr msg_formatter);
// default error handler: print the error to stderr with the max rate of 1 message/minute
virtual void _default_err_handler(const std::string &msg);
// 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 &msg);
// increment the message count (only if defined(SPDLOG_ENABLE_MESSAGE_COUNTER))
void _incr_msg_counter(details::log_msg &msg);
const std::string _name;
std::vector<sink_ptr> _sinks;
@@ -127,6 +147,6 @@ protected:
std::atomic<time_t> _last_err_time;
std::atomic<size_t> _msg_counter;
};
}
} // namespace spdlog
#include "spdlog/details/logger_impl.h"
#include "details/logger_impl.h"

View File

@@ -7,33 +7,36 @@
#if defined(__ANDROID__)
#include "spdlog/sinks/sink.h"
#include "../details/os.h"
#include "sink.h"
#include <android/log.h>
#include <chrono>
#include <mutex>
#include <string>
#include <android/log.h>
#include <thread>
#include <chrono>
#if !defined(SPDLOG_ANDROID_RETRIES)
#define SPDLOG_ANDROID_RETRIES 2
#endif
namespace spdlog
{
namespace sinks
{
namespace spdlog {
namespace sinks {
/*
* Android sink (logging using __android_log_write)
* __android_log_write is thread-safe. No lock is needed.
*/
* Android sink (logging using __android_log_write)
* __android_log_write is thread-safe. No lock is needed.
*/
class android_sink : public sink
{
public:
explicit android_sink(const std::string& tag = "spdlog", bool use_raw_msg = false): _tag(tag), _use_raw_msg(use_raw_msg) {}
explicit android_sink(const std::string &tag = "spdlog", bool use_raw_msg = false)
: _tag(tag)
, _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 char *msg_output = (_use_raw_msg ? msg.raw.c_str() : msg.formatted.c_str());
@@ -41,9 +44,9 @@ public:
// See system/core/liblog/logger_write.c for explanation of return value
int ret = __android_log_write(priority, _tag.c_str(), msg_output);
int retry_count = 0;
while ((ret == -11/*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES))
while ((ret == -11 /*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES))
{
std::this_thread::sleep_for(std::chrono::milliseconds(5));
details::os::sleep_for_millis(5);
ret = __android_log_write(priority, _tag.c_str(), msg_output);
retry_count++;
}
@@ -54,14 +57,12 @@ public:
}
}
void flush() override
{
}
void flush() override {}
private:
static android_LogPriority convert_to_android(spdlog::level::level_enum level)
{
switch(level)
switch (level)
{
case spdlog::level::trace:
return ANDROID_LOG_VERBOSE;
@@ -84,7 +85,7 @@ private:
bool _use_raw_msg;
};
}
}
} // namespace sinks
} // namespace spdlog
#endif

View File

@@ -5,60 +5,61 @@
#pragma once
#include "spdlog/sinks/base_sink.h"
#include "spdlog/common.h"
#include "spdlog/details/os.h"
#include "../common.h"
#include "../details/os.h"
#include "base_sink.h"
#include <string>
#include <map>
#include <unordered_map>
namespace spdlog
{
namespace sinks
{
namespace spdlog {
namespace sinks {
/**
* This sink prefixes the output with an ANSI escape sequence color code depending on the severity
* of the message.
* If no color terminal detected, omit the escape codes.
*/
template <class Mutex>
class ansicolor_sink: public base_sink<Mutex>
template<class Mutex>
class ansicolor_sink : public base_sink<Mutex>
{
public:
ansicolor_sink(FILE* file): target_file_(file)
explicit ansicolor_sink(FILE *file)
: target_file_(file)
{
should_do_colors_ = details::os::in_terminal(file) && details::os::is_color_terminal();
colors_[level::trace] = cyan;
colors_[level::trace] = white;
colors_[level::debug] = cyan;
colors_[level::info] = reset;
colors_[level::info] = green;
colors_[level::warn] = yellow + bold;
colors_[level::err] = red + bold;
colors_[level::critical] = bold + on_red;
colors_[level::off] = reset;
}
virtual ~ansicolor_sink()
~ansicolor_sink() override
{
_flush();
}
void set_color(level::level_enum color_level, const std::string& color)
void set_color(level::level_enum color_level, const std::string &color)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
colors_[color_level] = color;
}
/// Formatting codes
const std::string reset = "\033[00m";
const std::string reset = "\033[m";
const std::string bold = "\033[1m";
const std::string dark = "\033[2m";
const std::string underline = "\033[4m";
const std::string blink = "\033[5m";
const std::string reverse = "\033[7m";
const std::string concealed = "\033[8m";
const std::string clear_line = "\033[K";
// Foreground colors
const std::string grey = "\033[30m";
const std::string black = "\033[30m";
const std::string red = "\033[31m";
const std::string green = "\033[32m";
const std::string yellow = "\033[33m";
@@ -68,7 +69,7 @@ public:
const std::string white = "\033[37m";
/// Background colors
const std::string on_grey = "\033[40m";
const std::string on_black = "\033[40m";
const std::string on_red = "\033[41m";
const std::string on_green = "\033[42m";
const std::string on_yellow = "\033[43m";
@@ -78,20 +79,24 @@ public:
const std::string on_white = "\033[47m";
protected:
virtual void _sink_it(const details::log_msg& msg) override
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_)
if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
{
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_);
// before color range
_print_range(msg, 0, msg.color_range_start);
// in color range
_print_ccode(colors_[msg.level]);
_print_range(msg, msg.color_range_start, msg.color_range_end);
_print_ccode(reset);
// after color range
_print_range(msg, msg.color_range_end, msg.formatted.size());
}
else
{
fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), target_file_);
_print_range(msg, 0, msg.formatted.size());
}
_flush();
}
@@ -100,34 +105,46 @@ protected:
{
fflush(target_file_);
}
FILE* target_file_;
private:
void _print_ccode(const std::string &color_code)
{
fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_);
}
void _print_range(const details::log_msg &msg, size_t start, size_t end)
{
fwrite(msg.formatted.data() + start, sizeof(char), end - start, target_file_);
}
FILE *target_file_;
bool should_do_colors_;
std::map<level::level_enum, std::string> colors_;
};
template<class Mutex>
class ansicolor_stdout_sink: public ansicolor_sink<Mutex>
{
public:
ansicolor_stdout_sink(): ansicolor_sink<Mutex>(stdout)
{}
std::unordered_map<level::level_enum, std::string, level::level_hasher> colors_;
};
template<class Mutex>
class ansicolor_stderr_sink: public ansicolor_sink<Mutex>
class ansicolor_stdout_sink : public ansicolor_sink<Mutex>
{
public:
ansicolor_stderr_sink(): ansicolor_sink<Mutex>(stderr)
{}
ansicolor_stdout_sink()
: ansicolor_sink<Mutex>(stdout)
{
}
};
typedef ansicolor_stdout_sink<std::mutex> ansicolor_stdout_sink_mt;
typedef ansicolor_stdout_sink<details::null_mutex> ansicolor_stdout_sink_st;
using ansicolor_stdout_sink_mt = ansicolor_stdout_sink<std::mutex>;
using ansicolor_stdout_sink_st = ansicolor_stdout_sink<details::null_mutex>;
typedef ansicolor_stderr_sink<std::mutex> ansicolor_stderr_sink_mt;
typedef ansicolor_stderr_sink<details::null_mutex> ansicolor_stderr_sink_st;
template<class Mutex>
class ansicolor_stderr_sink : public ansicolor_sink<Mutex>
{
public:
ansicolor_stderr_sink()
: ansicolor_sink<Mutex>(stderr)
{
}
};
using ansicolor_stderr_sink_mt = ansicolor_stderr_sink<std::mutex>;
using ansicolor_stderr_sink_st = ansicolor_stderr_sink<details::null_mutex>;
} // namespace sinks
} // namespace spdlog

View File

@@ -10,41 +10,40 @@
// all locking is taken care of here so no locking needed by the implementers..
//
#include "spdlog/sinks/sink.h"
#include "spdlog/formatter.h"
#include "spdlog/common.h"
#include "spdlog/details/log_msg.h"
#include "../common.h"
#include "../details/log_msg.h"
#include "../formatter.h"
#include "sink.h"
#include <mutex>
namespace spdlog
{
namespace sinks
{
namespace spdlog {
namespace sinks {
template<class Mutex>
class base_sink:public sink
class base_sink : public sink
{
public:
base_sink():_mutex() {}
virtual ~base_sink() = default;
base_sink() = default;
base_sink(const base_sink&) = delete;
base_sink& operator=(const base_sink&) = delete;
base_sink(const base_sink &) = delete;
base_sink &operator=(const base_sink &) = delete;
void log(const details::log_msg& msg) SPDLOG_FINAL override
void log(const details::log_msg &msg) SPDLOG_FINAL override
{
std::lock_guard<Mutex> lock(_mutex);
_sink_it(msg);
}
void flush() SPDLOG_FINAL override
{
std::lock_guard<Mutex> lock(_mutex);
_flush();
}
protected:
virtual void _sink_it(const details::log_msg& msg) = 0;
virtual void _sink_it(const details::log_msg &msg) = 0;
virtual void _flush() = 0;
Mutex _mutex;
};
}
}
} // namespace sinks
} // namespace spdlog

View File

@@ -5,39 +5,39 @@
#pragma once
#include "spdlog/details/log_msg.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/sinks/base_sink.h"
#include "spdlog/sinks/sink.h"
#include "../details/log_msg.h"
#include "../details/null_mutex.h"
#include "base_sink.h"
#include "sink.h"
#include <algorithm>
#include <mutex>
#include <memory>
#include <mutex>
#include <vector>
// Distribution sink (mux). Stores a vector of sinks which get called when log is called
namespace spdlog
{
namespace sinks
{
namespace spdlog {
namespace sinks {
template<class Mutex>
class dist_sink: public base_sink<Mutex>
class dist_sink : public base_sink<Mutex>
{
public:
explicit dist_sink() :_sinks() {}
dist_sink(const dist_sink&) = delete;
dist_sink& operator=(const dist_sink&) = delete;
virtual ~dist_sink() = default;
explicit dist_sink()
: _sinks()
{
}
dist_sink(const dist_sink &) = delete;
dist_sink &operator=(const dist_sink &) = delete;
protected:
std::vector<std::shared_ptr<sink>> _sinks;
void _sink_it(const details::log_msg& msg) override
void _sink_it(const details::log_msg &msg) override
{
for (auto &sink : _sinks)
{
if( sink->should_log( msg.level))
if (sink->should_log(msg.level))
{
sink->log(msg);
}
@@ -46,14 +46,11 @@ protected:
void _flush() override
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
for (auto &sink : _sinks)
sink->flush();
}
public:
void add_sink(std::shared_ptr<sink> sink)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
@@ -67,7 +64,8 @@ public:
}
};
typedef dist_sink<std::mutex> dist_sink_mt;
typedef dist_sink<details::null_mutex> dist_sink_st;
}
}
using dist_sink_mt = dist_sink<std::mutex>;
using dist_sink_st = dist_sink<details::null_mutex>;
} // namespace sinks
} // namespace spdlog

View File

@@ -5,31 +5,30 @@
#pragma once
#include "spdlog/sinks/base_sink.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/details/file_helper.h"
#include "spdlog/fmt/fmt.h"
#include "../details/file_helper.h"
#include "../details/null_mutex.h"
#include "../fmt/fmt.h"
#include "base_sink.h"
#include <algorithm>
#include <cerrno>
#include <chrono>
#include <cstdio>
#include <ctime>
#include <mutex>
#include <string>
#include <cerrno>
namespace spdlog
{
namespace sinks
{
namespace spdlog {
namespace sinks {
/*
* Trivial file sink with single file as target
*/
template<class Mutex>
class simple_file_sink SPDLOG_FINAL : public base_sink < Mutex >
class simple_file_sink SPDLOG_FINAL : public base_sink<Mutex>
{
public:
explicit simple_file_sink(const filename_t &filename, bool truncate = false):_force_flush(false)
explicit simple_file_sink(const filename_t &filename, bool truncate = false)
: _force_flush(false)
{
_file_helper.open(filename, truncate);
}
@@ -40,46 +39,64 @@ public:
}
protected:
void _sink_it(const details::log_msg& msg) override
void _sink_it(const details::log_msg &msg) override
{
_file_helper.write(msg);
if(_force_flush)
if (_force_flush)
{
_file_helper.flush();
}
}
void _flush() override
{
_file_helper.flush();
}
private:
details::file_helper _file_helper;
bool _force_flush;
};
typedef simple_file_sink<std::mutex> simple_file_sink_mt;
typedef simple_file_sink<details::null_mutex> simple_file_sink_st;
using simple_file_sink_mt = simple_file_sink<std::mutex>;
using simple_file_sink_st = simple_file_sink<details::null_mutex>;
/*
* Rotating file sink based on size
*/
template<class Mutex>
class rotating_file_sink SPDLOG_FINAL : public base_sink < Mutex >
class rotating_file_sink SPDLOG_FINAL : public base_sink<Mutex>
{
public:
rotating_file_sink(const filename_t &base_filename,
std::size_t max_size, std::size_t max_files) :
_base_filename(base_filename),
_max_size(max_size),
_max_files(max_files),
_current_size(0),
_file_helper()
rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files)
: _base_filename(std::move(base_filename))
, _max_size(max_size)
, _max_files(max_files)
{
_file_helper.open(calc_filename(_base_filename, 0));
_current_size = _file_helper.size(); //expensive. called only once
_current_size = _file_helper.size(); // expensive. called only once
}
// calc filename according to index and file extension if exists.
// e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
static filename_t calc_filename(const filename_t &filename, std::size_t index)
{
typename std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
if (index != 0u)
{
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:
void _sink_it(const details::log_msg& msg) override
void _sink_it(const details::log_msg &msg) override
{
_current_size += msg.formatted.size();
if (_current_size > _max_size)
@@ -96,22 +113,11 @@ protected:
}
private:
static filename_t calc_filename(const filename_t& filename, std::size_t index)
{
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
if (index)
w.write(SPDLOG_FILENAME_T("{}.{}"), filename, index);
else
w.write(SPDLOG_FILENAME_T("{}"), filename);
return w.str();
}
// Rotate files:
// log.txt -> log.txt.1
// log.txt.1 -> log.txt.2
// log.txt.2 -> log.txt.3
// lo3.txt.3 -> delete
// log.txt -> log.1.txt
// log.1.txt -> log.2.txt
// log.2.txt -> log.3.txt
// log.3.txt -> delete
void _rotate()
{
using details::os::filename_to_str;
@@ -128,13 +134,14 @@ private:
throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target), errno);
}
}
if (details::file_helper::file_exists(src) && details::os::rename(src, target))
if (details::file_helper::file_exists(src) && details::os::rename(src, target) != 0)
{
throw spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
}
}
_file_helper.reopen(true);
}
filename_t _base_filename;
std::size_t _max_size;
std::size_t _max_files;
@@ -142,35 +149,40 @@ private:
details::file_helper _file_helper;
};
typedef rotating_file_sink<std::mutex> rotating_file_sink_mt;
typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
using rotating_file_sink_mt = rotating_file_sink<std::mutex>;
using rotating_file_sink_st = rotating_file_sink<details::null_mutex>;
/*
* Default generator of daily log file names.
*/
struct default_daily_file_name_calculator
{
// Create filename for the form basename.YYYY-MM-DD_hh-mm
static filename_t calc_filename(const filename_t& basename)
// Create filename for the form filename.YYYY-MM-DD_hh-mm.ext
static filename_t calc_filename(const filename_t &filename)
{
std::tm tm = spdlog::details::os::localtime();
filename_t basename, ext;
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, ext);
return w.str();
}
};
/*
* Generator of daily log file names in format basename.YYYY-MM-DD
* Generator of daily log file names in format basename.YYYY-MM-DD.ext
*/
struct dateonly_daily_file_name_calculator
{
// Create filename for the form basename.YYYY-MM-DD
static filename_t calc_filename(const filename_t& basename)
static filename_t calc_filename(const filename_t &filename)
{
std::tm tm = spdlog::details::os::localtime();
filename_t basename, ext;
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, ext);
return w.str();
}
};
@@ -179,26 +191,25 @@ struct dateonly_daily_file_name_calculator
* Rotating file sink based on date. rotates at midnight
*/
template<class Mutex, class FileNameCalc = default_daily_file_name_calculator>
class daily_file_sink SPDLOG_FINAL :public base_sink < Mutex >
class daily_file_sink SPDLOG_FINAL : public base_sink<Mutex>
{
public:
//create daily file sink which rotates on given time
daily_file_sink(
const filename_t& base_filename,
int rotation_hour,
int rotation_minute) : _base_filename(base_filename),
_rotation_h(rotation_hour),
_rotation_m(rotation_minute)
// create daily file sink which rotates on given time
daily_file_sink(filename_t base_filename, int rotation_hour, int rotation_minute)
: _base_filename(std::move(base_filename))
, _rotation_h(rotation_hour)
, _rotation_m(rotation_minute)
{
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
{
throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
}
_rotation_tp = _next_rotation_tp();
_file_helper.open(FileNameCalc::calc_filename(_base_filename));
}
protected:
void _sink_it(const details::log_msg& msg) override
void _sink_it(const details::log_msg &msg) override
{
if (std::chrono::system_clock::now() >= _rotation_tp)
{
@@ -224,9 +235,10 @@ private:
date.tm_sec = 0;
auto rotation_time = std::chrono::system_clock::from_time_t(std::mktime(&date));
if (rotation_time > now)
{
return rotation_time;
else
return std::chrono::system_clock::time_point(rotation_time + std::chrono::hours(24));
}
return {rotation_time + std::chrono::hours(24)};
}
filename_t _base_filename;
@@ -236,7 +248,8 @@ private:
details::file_helper _file_helper;
};
typedef daily_file_sink<std::mutex> daily_file_sink_mt;
typedef daily_file_sink<details::null_mutex> daily_file_sink_st;
}
}
using daily_file_sink_mt = daily_file_sink<std::mutex>;
using daily_file_sink_st = daily_file_sink<details::null_mutex>;
} // namespace sinks
} // namespace spdlog

View File

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

View File

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

View File

@@ -5,27 +5,28 @@
#pragma once
#include "spdlog/details/null_mutex.h"
#include "spdlog/sinks/base_sink.h"
#include "../details/null_mutex.h"
#include "base_sink.h"
#include <ostream>
#include <mutex>
#include <ostream>
namespace spdlog
{
namespace sinks
{
namespace spdlog {
namespace sinks {
template<class Mutex>
class ostream_sink: public base_sink<Mutex>
class ostream_sink : public base_sink<Mutex>
{
public:
explicit ostream_sink(std::ostream& os, bool force_flush=false) :_ostream(os), _force_flush(force_flush) {}
ostream_sink(const ostream_sink&) = delete;
ostream_sink& operator=(const ostream_sink&) = delete;
virtual ~ostream_sink() = default;
explicit ostream_sink(std::ostream &os, bool force_flush = false)
: _ostream(os)
, _force_flush(force_flush)
{
}
ostream_sink(const ostream_sink &) = delete;
ostream_sink &operator=(const ostream_sink &) = delete;
protected:
void _sink_it(const details::log_msg& msg) override
void _sink_it(const details::log_msg &msg) override
{
_ostream.write(msg.formatted.data(), msg.formatted.size());
if (_force_flush)
@@ -37,11 +38,12 @@ protected:
_ostream.flush();
}
std::ostream& _ostream;
std::ostream &_ostream;
bool _force_flush;
};
typedef ostream_sink<std::mutex> ostream_sink_mt;
typedef ostream_sink<details::null_mutex> ostream_sink_st;
}
}
using ostream_sink_mt = ostream_sink<std::mutex>;
using ostream_sink_st = ostream_sink<details::null_mutex>;
} // namespace sinks
} // namespace spdlog

View File

@@ -3,25 +3,18 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include "spdlog/details/log_msg.h"
#include "../details/log_msg.h"
namespace spdlog
{
namespace sinks
{
namespace spdlog {
namespace sinks {
class sink
{
public:
sink()
{
_level = level::trace;
}
virtual ~sink() = default;
virtual ~sink() {}
virtual void log(const details::log_msg& msg) = 0;
virtual void log(const details::log_msg &msg) = 0;
virtual void flush() = 0;
bool should_log(level::level_enum msg_level) const;
@@ -29,8 +22,7 @@ public:
level::level_enum level() const;
private:
level_t _level;
level_t _level{level::trace};
};
inline bool sink::should_log(level::level_enum msg_level) const
@@ -48,6 +40,5 @@ inline level::level_enum sink::level() const
return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed));
}
}
}
} // namespace sinks
} // namespace spdlog

View File

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

View File

@@ -5,22 +5,19 @@
#pragma once
#include "spdlog/common.h"
#include "../common.h"
#ifdef SPDLOG_ENABLE_SYSLOG
#include "spdlog/sinks/sink.h"
#include "spdlog/details/log_msg.h"
#include "../details/log_msg.h"
#include "sink.h"
#include <array>
#include <string>
#include <syslog.h>
namespace spdlog
{
namespace sinks
{
namespace spdlog {
namespace sinks {
/**
* Sink that write to syslog using the `syscall()` library call.
*
@@ -30,41 +27,39 @@ class syslog_sink : public sink
{
public:
//
syslog_sink(const std::string& ident = "", int syslog_option=0, int syslog_facility=LOG_USER):
_ident(ident)
syslog_sink(const std::string &ident = "", int syslog_option = 0, int syslog_facility = LOG_USER)
: _ident(ident)
{
_priorities[static_cast<int>(level::trace)] = LOG_DEBUG;
_priorities[static_cast<int>(level::debug)] = LOG_DEBUG;
_priorities[static_cast<int>(level::info)] = LOG_INFO;
_priorities[static_cast<int>(level::warn)] = LOG_WARNING;
_priorities[static_cast<int>(level::err)] = LOG_ERR;
_priorities[static_cast<int>(level::critical)] = LOG_CRIT;
_priorities[static_cast<int>(level::off)] = LOG_INFO;
_priorities[static_cast<size_t>(level::trace)] = LOG_DEBUG;
_priorities[static_cast<size_t>(level::debug)] = LOG_DEBUG;
_priorities[static_cast<size_t>(level::info)] = LOG_INFO;
_priorities[static_cast<size_t>(level::warn)] = LOG_WARNING;
_priorities[static_cast<size_t>(level::err)] = LOG_ERR;
_priorities[static_cast<size_t>(level::critical)] = LOG_CRIT;
_priorities[static_cast<size_t>(level::off)] = LOG_INFO;
//set ident to be program name if empty
::openlog(_ident.empty()? nullptr:_ident.c_str(), syslog_option, syslog_facility);
// set ident to be program name if empty
::openlog(_ident.empty() ? nullptr : _ident.c_str(), syslog_option, syslog_facility);
}
~syslog_sink()
~syslog_sink() override
{
::closelog();
}
syslog_sink(const syslog_sink&) = delete;
syslog_sink& operator=(const syslog_sink&) = delete;
syslog_sink(const syslog_sink &) = delete;
syslog_sink &operator=(const syslog_sink &) = delete;
void log(const details::log_msg &msg) override
{
::syslog(syslog_prio_from_level(msg), "%s", msg.raw.str().c_str());
}
void flush() override
{
}
void flush() override {}
private:
std::array<int, 7> _priorities;
//must store the ident because the man says openlog might use the pointer as is and not a string copy
// must store the ident because the man says openlog might use the pointer as is and not a string copy
const std::string _ident;
//
@@ -72,10 +67,10 @@ private:
//
int syslog_prio_from_level(const details::log_msg &msg) const
{
return _priorities[static_cast<int>(msg.level)];
return _priorities[static_cast<size_t>(msg.level)];
}
};
}
}
} // namespace sinks
} // namespace spdlog
#endif

View File

@@ -5,75 +5,87 @@
#pragma once
#include "spdlog/sinks/base_sink.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/common.h"
#include "../common.h"
#include "../details/null_mutex.h"
#include "base_sink.h"
#include <mutex>
#include <string>
#include <map>
#include <unordered_map>
#include <wincon.h>
namespace spdlog
{
namespace sinks
{
namespace spdlog {
namespace sinks {
/*
* Windows color console sink. Uses WriteConsoleA to write to the console with colors
*/
template<class Mutex>
class wincolor_sink: public base_sink<Mutex>
class wincolor_sink : public base_sink<Mutex>
{
public:
const WORD BOLD = FOREGROUND_INTENSITY;
const WORD RED = FOREGROUND_RED;
const WORD GREEN = FOREGROUND_GREEN;
const WORD CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE;
const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN;
wincolor_sink(HANDLE std_handle): out_handle_(std_handle)
wincolor_sink(HANDLE std_handle)
: out_handle_(std_handle)
{
colors_[level::trace] = CYAN;
colors_[level::trace] = WHITE;
colors_[level::debug] = CYAN;
colors_[level::info] = WHITE | BOLD;
colors_[level::info] = GREEN;
colors_[level::warn] = YELLOW | BOLD;
colors_[level::err] = RED | BOLD; // red bold
colors_[level::err] = RED | BOLD; // red bold
colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background
colors_[level::off] = 0;
}
virtual ~wincolor_sink()
~wincolor_sink() override
{
this->flush();
}
wincolor_sink(const wincolor_sink& other) = delete;
wincolor_sink& operator=(const wincolor_sink& other) = delete;
wincolor_sink(const wincolor_sink &other) = delete;
wincolor_sink &operator=(const wincolor_sink &other) = delete;
protected:
virtual void _sink_it(const details::log_msg& msg) override
{
auto color = colors_[msg.level];
auto orig_attribs = set_console_attribs(color);
WriteConsoleA(out_handle_, msg.formatted.data(), static_cast<DWORD>(msg.formatted.size()), nullptr, nullptr);
SetConsoleTextAttribute(out_handle_, orig_attribs); //reset to orig colors
}
virtual void _flush() override
{
// windows console always flushed?
}
// change the color for the given level
// change the color for the given level
void set_color(level::level_enum level, WORD color)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
colors_[level] = color;
}
protected:
void _sink_it(const details::log_msg &msg) override
{
if (msg.color_range_end > msg.color_range_start)
{
// before color range
_print_range(msg, 0, msg.color_range_start);
// in color range
auto orig_attribs = set_console_attribs(colors_[msg.level]);
_print_range(msg, msg.color_range_start, msg.color_range_end);
::SetConsoleTextAttribute(out_handle_, orig_attribs); // reset to orig colors
// after color range
_print_range(msg, msg.color_range_end, msg.formatted.size());
}
else // print without colors if color range is invalid
{
_print_range(msg, 0, msg.formatted.size());
}
}
void _flush() override
{
// windows console always flushed?
}
private:
HANDLE out_handle_;
std::map<level::level_enum, WORD> colors_;
std::unordered_map<level::level_enum, WORD, level::level_hasher> colors_;
// set color and return the orig console attributes (for resetting later)
WORD set_console_attribs(WORD attribs)
@@ -82,10 +94,17 @@ private:
GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info);
WORD back_color = orig_buffer_info.wAttributes;
// retrieve the current background color
back_color &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
back_color &= static_cast<WORD>(~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY));
// keep the background color unchanged
SetConsoleTextAttribute(out_handle_, attribs | back_color);
return orig_buffer_info.wAttributes; //return orig attribs
return orig_buffer_info.wAttributes; // return orig attribs
}
// print a range of formatted message to console
void _print_range(const details::log_msg &msg, size_t start, size_t end)
{
DWORD size = static_cast<DWORD>(end - start);
WriteConsoleA(out_handle_, msg.formatted.data() + start, size, nullptr, nullptr);
}
};
@@ -93,29 +112,33 @@ private:
// windows color console to stdout
//
template<class Mutex>
class wincolor_stdout_sink: public wincolor_sink<Mutex>
class wincolor_stdout_sink : public wincolor_sink<Mutex>
{
public:
wincolor_stdout_sink() : wincolor_sink<Mutex>(GetStdHandle(STD_OUTPUT_HANDLE))
{}
wincolor_stdout_sink()
: wincolor_sink<Mutex>(GetStdHandle(STD_OUTPUT_HANDLE))
{
}
};
typedef wincolor_stdout_sink<std::mutex> wincolor_stdout_sink_mt;
typedef wincolor_stdout_sink<details::null_mutex> wincolor_stdout_sink_st;
using wincolor_stdout_sink_mt = wincolor_stdout_sink<std::mutex>;
using wincolor_stdout_sink_st = wincolor_stdout_sink<details::null_mutex>;
//
// windows color console to stderr
//
template<class Mutex>
class wincolor_stderr_sink: public wincolor_sink<Mutex>
class wincolor_stderr_sink : public wincolor_sink<Mutex>
{
public:
wincolor_stderr_sink() : wincolor_sink<Mutex>(GetStdHandle(STD_ERROR_HANDLE))
{}
wincolor_stderr_sink()
: wincolor_sink<Mutex>(GetStdHandle(STD_ERROR_HANDLE))
{
}
};
typedef wincolor_stderr_sink<std::mutex> wincolor_stderr_sink_mt;
typedef wincolor_stderr_sink<details::null_mutex> wincolor_stderr_sink_st;
using wincolor_stderr_sink_mt = wincolor_stderr_sink<std::mutex>;
using wincolor_stderr_sink_st = wincolor_stderr_sink<details::null_mutex>;
}
}
} // namespace sinks
} // namespace spdlog

View File

@@ -0,0 +1,27 @@
//
// 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>;
using windebug_sink_mt = msvc_sink_mt;
using windebug_sink_st = msvc_sink_st;
} // namespace sinks
} // namespace spdlog
#endif

View File

@@ -7,43 +7,43 @@
#pragma once
#define SPDLOG_VERSION "0.14.0"
#include "common.h"
#include "logger.h"
#include "spdlog/tweakme.h"
#include "spdlog/common.h"
#include "spdlog/logger.h"
#include <memory>
#include <functional>
#include <chrono>
#include <functional>
#include <memory>
#include <string>
namespace spdlog
{
namespace spdlog {
//
// Return an existing logger or nullptr if a logger with such name doesn't exist.
// example: spdlog::get("my_logger")->info("hello {}", "world");
//
std::shared_ptr<logger> get(const std::string& name);
std::shared_ptr<logger> get(const std::string &name);
//
// Set global formatting
// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v");
//
void set_pattern(const std::string& format_string);
void set_pattern(const std::string &format_string);
void set_formatter(formatter_ptr f);
//
// Set global logging level for
// Set global logging level
//
void set_level(level::level_enum log_level);
//
// Set global flush level
//
void flush_on(level::level_enum log_level);
//
// Set global error handler
//
void set_error_handler(log_err_handler);
void set_error_handler(log_err_handler handler);
//
// Turn on async mode (off by default) and set the queue size for each async_logger.
@@ -53,7 +53,7 @@ void set_error_handler(log_err_handler);
//
// async_overflow_policy (optional, block_retry by default):
// async_overflow_policy::block_retry - if queue is full, block until queue has room for the new log entry.
// async_overflow_policy::discard_log_msg - never block and discard any new messages when queue overflows.
// async_overflow_policy::discard_log_msg - never block and discard any new messages when queue overflows.
//
// worker_warmup_cb (optional):
// callback function that will be called in worker thread upon start (can be used to init stuff like thread affinity)
@@ -61,80 +61,98 @@ void set_error_handler(log_err_handler);
// worker_teardown_cb (optional):
// callback function that will be called in worker thread upon exit
//
void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr);
void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()> &worker_warmup_cb = nullptr,
const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()> &worker_teardown_cb = nullptr);
// Turn off async mode
void set_sync_mode();
//
// Create and register multi/single threaded basic file logger.
// Basic logger simply writes to given file without any limitations or rotations.
//
std::shared_ptr<logger> basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate = false);
std::shared_ptr<logger> basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate = false);
std::shared_ptr<logger> basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false);
std::shared_ptr<logger> basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false);
//
// Create and register multi/single threaded rotating file logger
//
std::shared_ptr<logger> rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files);
std::shared_ptr<logger> rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files);
std::shared_ptr<logger> rotating_logger_mt(
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files);
std::shared_ptr<logger> rotating_logger_st(
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files);
//
// Create file logger which creates new file on the given time (default in midnight):
// Create file logger which creates new file on the given time (default in midnight):
//
std::shared_ptr<logger> daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0);
std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0);
std::shared_ptr<logger> daily_logger_mt(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0);
std::shared_ptr<logger> daily_logger_st(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0);
//
// Create and register stdout/stderr loggers
//
std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name);
std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name);
std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name);
std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name);
std::shared_ptr<logger> stdout_logger_mt(const std::string &logger_name);
std::shared_ptr<logger> stdout_logger_st(const std::string &logger_name);
std::shared_ptr<logger> stderr_logger_mt(const std::string &logger_name);
std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name);
//
// Create and register colored stdout/stderr loggers
//
std::shared_ptr<logger> stdout_color_mt(const std::string& logger_name);
std::shared_ptr<logger> stdout_color_st(const std::string& logger_name);
std::shared_ptr<logger> stderr_color_mt(const std::string& logger_name);
std::shared_ptr<logger> stderr_color_st(const std::string& logger_name);
std::shared_ptr<logger> stdout_color_mt(const std::string &logger_name);
std::shared_ptr<logger> stdout_color_st(const std::string &logger_name);
std::shared_ptr<logger> stderr_color_mt(const std::string &logger_name);
std::shared_ptr<logger> stderr_color_st(const std::string &logger_name);
//
// Create and register a syslog logger
//
#ifdef SPDLOG_ENABLE_SYSLOG
std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0);
std::shared_ptr<logger> syslog_logger(
const std::string &logger_name, const std::string &ident = "", int syslog_option = 0, int syslog_facilty = (1 << 3));
#endif
#if defined(__ANDROID__)
std::shared_ptr<logger> android_logger(const std::string& logger_name, const std::string& tag = "spdlog");
std::shared_ptr<logger> android_logger(const std::string &logger_name, const std::string &tag = "spdlog");
#endif
// Create and register a logger with a single sink
std::shared_ptr<logger> create(const std::string& logger_name, const sink_ptr& sink);
std::shared_ptr<logger> create(const std::string &logger_name, const sink_ptr &sink);
// Create and register a logger with multiple sinks
std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks);
template<class It>
std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end);
std::shared_ptr<logger> create(const std::string &logger_name, sinks_init_list sinks);
template<class It>
std::shared_ptr<logger> create(const std::string &logger_name, const It &sinks_begin, const It &sinks_end);
// Create and register a logger with templated sink type
// Example:
// spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename");
template <typename Sink, typename... Args>
std::shared_ptr<spdlog::logger> create(const std::string& logger_name, Args...);
template<typename Sink, typename... Args>
std::shared_ptr<spdlog::logger> create(const std::string &logger_name, Args... 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);
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);
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);
std::shared_ptr<logger> create_async(const std::string &logger_name, const It &sinks_begin, const It &sinks_end, size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()> &worker_warmup_cb = nullptr,
const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()> &worker_teardown_cb = nullptr);
// Register the given logger with the given name
void register_logger(std::shared_ptr<logger> logger);
@@ -150,11 +168,10 @@ void drop(const std::string &name);
// Drop all references from the registry
void drop_all();
///////////////////////////////////////////////////////////////////////////////
//
// Trace & Debug can be switched on/off at compile time for zero cost debug statements.
// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in teakme.h to enable.
// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in tweakme.h to enable.
// SPDLOG_TRACE(..) will also print current file and line.
//
// Example:
@@ -162,28 +179,26 @@ void drop_all();
// SPDLOG_TRACE(my_logger, "some trace message");
// SPDLOG_TRACE(my_logger, "another trace message {} {}", 1, 2);
// SPDLOG_DEBUG(my_logger, "some debug message {} {}", 3, 4);
// SPDLOG_DEBUG_IF(my_logger, true, "some debug message {} {}", 3, 4);
///////////////////////////////////////////////////////////////////////////////
#ifdef SPDLOG_TRACE_ON
#define SPDLOG_STR_H(x) #x
#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x)
#define SPDLOG_TRACE(logger, ...) logger->trace("[" __FILE__ " line #" SPDLOG_STR_HELPER(__LINE__) "] " __VA_ARGS__)
#define SPDLOG_TRACE_IF(logger, flag, ...) logger->trace_if(flag, "[" __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, ...)
#define SPDLOG_TRACE_IF(logger, flag, ...)
#define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ] " __VA_ARGS__)
#endif
#else
#define SPDLOG_TRACE(logger, ...) (void)0
#endif
#ifdef SPDLOG_DEBUG_ON
#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__)
#define SPDLOG_DEBUG_IF(logger, flag, ...) logger->debug_if(flag, __VA_ARGS__)
#else
#define SPDLOG_DEBUG(logger, ...)
#define SPDLOG_DEBUG_IF(logger, flag, ...)
#define SPDLOG_DEBUG(logger, ...) (void)0
#endif
}
} // namespace spdlog
#include "spdlog/details/spdlog_impl.h"
#include "details/spdlog_impl.h"

View File

@@ -11,7 +11,6 @@
//
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Under Linux, the much faster CLOCK_REALTIME_COARSE clock can be used.
// This clock is less accurate - can be off by dozens of millis - depending on the kernel HZ.
@@ -20,10 +19,9 @@
// #define SPDLOG_CLOCK_COARSE
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment if date/time logging is not needed and never appear in the log pattern.
// This will prevent spdlog from quering the clock on each log call.
// This will prevent spdlog from querying the clock on each log call.
//
// WARNING: If the log pattern contains any date/time while this flag is on, the result is undefined.
// You must set new pattern(spdlog::set_pattern(..") without any date/time in it
@@ -31,20 +29,27 @@
// #define SPDLOG_NO_DATETIME
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// 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.
//
// #define SPDLOG_NO_THREAD_ID
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to prevent spdlog from caching thread ids in thread local storage.
// By default spdlog saves thread ids in tls to gain a few micros for each call.
//
// WARNING: if your program forks, UNCOMMENT this flag to prevent undefined thread ids in the children logs.
//
// #define SPDLOG_DISABLE_TID_CACHING
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment if logger name logging is not needed.
// This will prevent spdlog from copying the logger name on each log call.
// This will prevent spdlog from copying the logger name on each log call.
//
// #define SPDLOG_NO_NAME
///////////////////////////////////////////////////////////////////////////////
@@ -56,16 +61,14 @@
// #define SPDLOG_TRACE_ON
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to avoid locking in the registry operations (spdlog::get(), spdlog::drop() spdlog::register()).
// Use only if your code never modifes concurrently the registry.
// Use only if your code never modifies concurrently the registry.
// Note that upon creating a logger the registry is modified by spdlog..
//
// #define SPDLOG_NO_REGISTRY_MUTEX
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to avoid spdlog's usage of atomic log levels
// Use only if your code never modifies a logger's log levels concurrently by different threads.
@@ -73,21 +76,18 @@
// #define SPDLOG_NO_ATOMIC_LEVELS
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable usage of wchar_t for file names on Windows.
//
// #define SPDLOG_WCHAR_FILENAMES
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to override default eol ("\n" or "\r\n" under Linux/Windows)
//
// #define SPDLOG_EOL ";-)\n"
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to use your own copy of the fmt library instead of spdlog's copy.
// In this case spdlog will try to include <fmt/format.h> so set your -I flag accordingly.
@@ -95,6 +95,12 @@
// #define SPDLOG_FMT_EXTERNAL
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// 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)
@@ -102,40 +108,36 @@
// #define SPDLOG_ENABLE_SYSLOG
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable wchar_t support (convert to utf8)
//
// #define SPDLOG_WCHAR_TO_UTF8_SUPPORT
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to prevent child processes from inheriting log file descriptors
//
// #define SPDLOG_PREVENT_CHILD_FD
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to mark some types as final, allowing more optimizations in release
// 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_FINAL final
// #define SPDLOG_NO_FINAL
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable message counting feature. Adds %i logger pattern that
// prints log message sequence id.
// 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 enable user defined tag names
// Uncomment to customize level names (e.g. "MT TRACE")
//
// #define SPDLOG_LEVEL_NAMES { " TRACE", " DEBUG", " INFO",
// " WARNING", " ERROR", "CRITICAL", "OFF" };
///////////////////////////////////////////////////////////////////////////////
// #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING", "MY ERROR", "MY CRITICAL", "OFF" }
///////////////////////////////////////////////////////////////////////////////

View File

@@ -1,19 +1,24 @@
#
# Tests
#
enable_testing()
find_package(Threads)
# Build Catch unit tests
add_library(catch INTERFACE)
target_include_directories(catch INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
file(GLOB catch_tests LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp *.h *.hpp)
add_executable(catch_tests ${catch_tests})
target_link_libraries(catch_tests spdlog ${CMAKE_THREAD_LIBS_INIT})
add_test(NAME catch_tests COMMAND catch_tests)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
project(spdlog-utests CXX)
enable_testing()
find_package(Threads REQUIRED)
set(SPDLOG_UTESTS_SOURCES
errors.cpp
file_helper.cpp
file_log.cpp
test_misc.cpp
test_pattern_formatter.cpp
test_async.cpp
includes.h
registry.cpp
test_macros.cpp
utils.cpp
utils.h
main.cpp)
add_executable(${PROJECT_NAME} ${SPDLOG_UTESTS_SOURCES})
target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads)
target_link_libraries(${PROJECT_NAME} PRIVATE spdlog)
add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME})
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")

View File

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

View File

@@ -2913,7 +2913,7 @@ namespace Catch {
for( std::vector<Ptr<Pattern> >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it )
if( !(*it)->matches( testCase ) )
return false;
return true;
return true;
}
};

23
tests/catch.license Normal file
View File

@@ -0,0 +1,23 @@
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

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