Compare commits

...

193 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
110 changed files with 5253 additions and 3457 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
...

8
.gitignore vendored
View File

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

View File

@@ -4,7 +4,7 @@
# #
cmake_minimum_required(VERSION 3.1) cmake_minimum_required(VERSION 3.1)
project(spdlog VERSION 0.16.1) project(spdlog VERSION 0.17.0 LANGUAGES CXX)
include(CTest) include(CTest)
include(CMakeDependentOption) include(CMakeDependentOption)
include(GNUInstallDirs) include(GNUInstallDirs)
@@ -16,7 +16,7 @@ set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
set(CMAKE_CXX_FLAGS "-Wall ${CMAKE_CXX_FLAGS}") set(CMAKE_CXX_FLAGS "-Wall -O3 ${CMAKE_CXX_FLAGS}")
endif() endif()
#--------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------
@@ -73,7 +73,6 @@ configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY)
install( install(
TARGETS spdlog TARGETS spdlog
EXPORT "${targets_export_name}" EXPORT "${targets_export_name}"
INCLUDES DESTINATION "${include_install_dir}"
) )
# install headers # install headers

View File

@@ -21,7 +21,7 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
## Platforms ## Platforms
* Linux, FreeBSD, Solaris * Linux, FreeBSD, Solaris
* Windows (vc 2013+, cygwin/mingw) * Windows (vc 2013+, cygwin)
* Mac OSX (clang 3.5+) * Mac OSX (clang 3.5+)
* Android * Android
@@ -30,7 +30,7 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
* Headers only, just copy and use. * Headers only, just copy and use.
* Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library. * Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library.
* Optional printf syntax support. * Optional printf syntax support.
* Extremely fast asynchronous mode (optional) - using lockfree queues and other tricks to reach millions of calls/sec. * Asynchronous mode (optional)
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting. * [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
* Conditional Logging * Conditional Logging
* Multi/Single threaded loggers. * Multi/Single threaded loggers.
@@ -54,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| |threads|boost log 1.54|glog |easylogging |spdlog|
|-------|:-------:|:-----:|----------:|------:| |-------|:-------:|:-----:|----------:|------:|
|1| 4.169s |1.066s |0.975s |0.302s| |1| 4.169s |1.066s |0.975s |0.392s|
|10| 6.180s |3.032s |2.857s |0.968s| |10| 6.180s |3.032s |2.857s |0.773s|
|100| 5.981s |1.139s |4.512s |0.497s| |100| 5.981s |1.139s |4.512s |0.587s|
#### Asynchronous mode #### Asynchronous mode
@@ -64,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>| |threads|g2log <sup>async logger</sup> |spdlog <sup>async mode</sup>|
|:-------|:-----:|-------------------------:| |:-------|:-----:|-------------------------:|
|1| 1.850s |0.216s | |1| 1.850s |0.39s |
|10| 0.943s |0.173s| |10| 0.943s |0.416s|
|100| 0.959s |0.202s| |100| 0.959s |0.413s|
@@ -120,9 +120,9 @@ int main(int, char*[])
daily_logger->info(123.44); daily_logger->info(123.44);
// Customize msg format for all messages // Customize msg format for all messages
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***"); spd::set_pattern("[%^+++%$] [%H:%M:%S %z] [thread %t] %v");
rotating_logger->info("This is another message with custom format"); 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 // Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info spd::set_level(spd::level::info); //Set global log level to info
@@ -170,7 +170,7 @@ int main(int, char*[])
void async_example() void async_example()
{ {
size_t q_size = 4096; //queue size must be power of 2 size_t q_size = 4096;
spd::set_async_mode(q_size); spd::set_async_mode(q_size);
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt"); auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
for (int i = 0; i < 100; ++i) for (int i = 0; i < 100; ++i)

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 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) all: $(binaries)
@@ -16,13 +25,10 @@ spdlog-bench-mt: spdlog-bench-mt.cpp
spdlog-async: spdlog-async.cpp spdlog-async: spdlog-async.cpp
$(CXX) spdlog-async.cpp -o spdlog-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS) $(CXX) spdlog-async.cpp -o spdlog-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
spdlog-null-async: spdlog-null-async.cpp spdlog-null-async: spdlog-null-async.cpp
$(CXX) spdlog-null-async.cpp -o spdlog-null-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS) $(CXX) spdlog-null-async.cpp -o spdlog-null-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
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_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-bench: boost-bench.cpp boost-bench: boost-bench.cpp
$(CXX) boost-bench.cpp -o boost-bench $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS) $(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 boost-bench-mt: boost-bench-mt.cpp
$(CXX) boost-bench-mt.cpp -o boost-bench-mt $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS) $(CXX) boost-bench-mt.cpp -o boost-bench-mt $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
GLOG_FLAGS = -I$(HOME)/include -L$(HOME)/lib -lglog
GLOG_FLAGS = -lglog
glog-bench: glog-bench.cpp glog-bench: glog-bench.cpp
$(CXX) glog-bench.cpp -o glog-bench $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS) $(CXX) glog-bench.cpp -o glog-bench $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
glog-bench-mt: glog-bench-mt.cpp glog-bench-mt: glog-bench-mt.cpp
$(CXX) glog-bench-mt.cpp -o glog-bench-mt $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS) $(CXX) glog-bench-mt.cpp -o glog-bench-mt $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
G2LOG_FLAGS = -I$(HOME)/include -L$(HOME)/lib -llib_g2logger
G2LOG_FLAGS = -I/home/gabi/devel/g2log/g2log/src -L/home/gabi/devel/g2log/g2log -llib_g2logger
g2log-async: g2log-async.cpp g2log-async: g2log-async.cpp
$(CXX) g2log-async.cpp -o g2log-async $(CXXFLAGS) $(G2LOG_FLAGS) $(CXX_RELEASE_FLAGS) $(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 easylogging-bench: easylogging-bench.cpp
$(CXX) easylogging-bench.cpp -o easylogging-bench $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS) $(CXX) easylogging-bench.cpp -o easylogging-bench $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
easylogging-bench-mt: easylogging-bench-mt.cpp 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 .PHONY: clean
clean: clean:
rm -f *.o logs/* $(binaries) rm -f *.o logs/* $(binaries)
rebuild: clean all 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) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread> #include <thread>
#include <vector> #include <vector>
#include <atomic>
#include <boost/log/core.hpp> #include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp> #include <boost/log/expressions.hpp>
#include <boost/log/sinks/text_file_backend.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/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 logging = boost::log;
namespace src = boost::log::sources; namespace src = boost::log::sources;
@@ -23,62 +25,62 @@ namespace keywords = boost::log::keywords;
void init() void init()
{ {
logging::add_file_log logging::add_file_log(keywords::file_name = "logs/boost-bench-mt_%N.log", /*< file name pattern >*/
( keywords::auto_flush = false, keywords::format = "[%TimeStamp%]: %Message%");
keywords::file_name = "logs/boost-sample_%N.log", /*< file name pattern >*/
keywords::auto_flush = false,
keywords::format = "[%TimeStamp%]: %Message%"
);
logging::core::get()->set_filter logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::info);
(
logging::trivial::severity >= logging::trivial::info
);
} }
using namespace std; 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; int thread_count = 10;
if(argc > 1) if (argc > 1)
thread_count = atoi(argv[1]); thread_count = atoi(argv[1]);
int howmany = 1000000; int howmany = 1000000;
init(); init();
logging::add_common_attributes(); logging::add_common_attributes();
using namespace logging::trivial; 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; vector<thread> threads;
auto start = clock::now();
for (int t = 0; t < thread_count; ++t) for (int t = 0; t < thread_count; ++t)
{ {
threads.push_back(std::thread([&]() threads.push_back(std::thread([&]() {
{
while (true) while (true)
{ {
int counter = ++msg_counter; 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"; 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(); 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; return 0;
} }

View File

@@ -2,14 +2,18 @@
// Copyright(c) 2015 Gabi Melman. // Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#include <chrono>
#include <iostream>
#include <boost/log/core.hpp> #include <boost/log/core.hpp>
#include <boost/log/trivial.hpp>
#include <boost/log/expressions.hpp> #include <boost/log/expressions.hpp>
#include <boost/log/sinks/text_file_backend.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/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 logging = boost::log;
namespace src = boost::log::sources; namespace src = boost::log::sources;
@@ -18,30 +22,35 @@ namespace keywords = boost::log::keywords;
void init() void init()
{ {
logging::add_file_log logging::add_file_log(keywords::file_name = "logs/boost-bench_%N.log", /*< file name pattern >*/
( keywords::auto_flush = false, keywords::format = "[%TimeStamp%]: %Message%");
keywords::file_name = "logs/boost-sample_%N.log", /*< file name pattern >*/
keywords::auto_flush = false,
keywords::format = "[%TimeStamp%]: %Message%"
);
logging::core::get()->set_filter logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::info);
(
logging::trivial::severity >= logging::trivial::info
);
} }
int main(int, char *[])
int main(int argc, char* [])
{ {
using namespace std::chrono;
using clock = steady_clock;
int howmany = 1000000; int howmany = 1000000;
init(); init();
logging::add_common_attributes(); logging::add_common_attributes();
using namespace logging::trivial; using namespace logging::trivial;
src::severity_logger_mt< severity_level > lg; src::severity_logger_mt<severity_level> lg;
for(int i = 0 ; i < howmany; ++i)
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"; 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; 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: * GLOBAL:
FORMAT = "[%datetime]: %msg" FORMAT = "[%datetime]: %levshort %msg"
FILENAME = ./logs/easylogging.log FILENAME = ./logs/easylogging.log
ENABLED = true ENABLED = true
TO_FILE = 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) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#include <atomic>
#include <chrono>
#include <iostream>
#include <thread> #include <thread>
#include <vector> #include <vector>
#include <atomic>
#define _ELPP_THREAD_SAFE #define ELPP_THREAD_SAFE
#include "easylogging++.cc"
#include "easylogging++.h" #include "easylogging++.h"
_INITIALIZE_EASYLOGGINGPP INITIALIZE_EASYLOGGINGPP
using namespace std; 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; int thread_count = 10;
if(argc > 1) if (argc > 1)
thread_count = atoi(argv[1]); thread_count = atoi(argv[1]);
int howmany = 1000000; int howmany = 1000000;
// Load configuration from file // Load configuration from file
el::Configurations conf("easyl.conf"); el::Configurations conf("easyl-mt.conf");
el::Loggers::reconfigureLogger("default", conf); el::Loggers::reconfigureLogger("default", conf);
std::atomic<int > msg_counter {0}; std::atomic<int> msg_counter{0};
vector<thread> threads; vector<thread> threads;
auto start = clock::now();
for (int t = 0; t < thread_count; ++t) for (int t = 0; t < thread_count; ++t)
{ {
threads.push_back(std::thread([&]() threads.push_back(std::thread([&]() {
{
while (true) while (true)
{ {
int counter = ++msg_counter; int counter = ++msg_counter;
if (counter > howmany) break; if (counter > howmany)
break;
LOG(INFO) << "easylog message #" << counter << ": This is some text for your pleasure"; LOG(INFO) << "easylog message #" << counter << ": This is some text for your pleasure";
} }
})); }));
} }
for (auto &t : threads)
for(auto &t:threads)
{ {
t.join(); 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; return 0;
} }

View File

@@ -3,20 +3,37 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#include <chrono>
#include <iostream>
#include "easylogging++.cc"
#include "easylogging++.h" #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; int howmany = 1000000;
// Load configuration from file // Load configuration from file
el::Configurations conf("easyl.conf"); el::Configurations conf("easyl.conf");
el::Loggers::reconfigureLogger("default", 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"; 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; return 0;
} }

View File

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

View File

@@ -3,19 +3,32 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#include <chrono>
#include <iostream>
#include "glog/logging.h" #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_logtostderr = 0;
FLAGS_log_dir = "logs"; FLAGS_log_dir = "logs";
google::InitGoogleLogging(argv[0]); google::InitGoogleLogging(argv[0]);
for(int i = 0 ; i < howmany; ++i) auto start = clock::now();
LOG(INFO) << "glog message # " << i << ": This is some text for your pleasure"; 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; return 0;
} }

View File

@@ -9,29 +9,26 @@ void CrusherLoop()
while (true) while (true)
{ {
LOGF(INFO, "Some text to crush you machine. thread:"); LOGF(INFO, "Some text to crush you machine. thread:");
if(++counter % 1000000 == 0) if (++counter % 1000000 == 0)
{ {
std::cout << "Wrote " << counter << " entries" << std::endl; 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 << "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; std::cout << "Are you sure you want to continue ? " << std::endl;
char c; char c;
std::cin >> c; std::cin >> c;
if (toupper( c ) != 'Y') if (toupper(c) != 'Y')
return 0; return 0;
auto worker = g3::LogWorker::createLogWorker(); 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()); g3::initializeLogging(worker.get());
CrusherLoop(); CrusherLoop();
return 0; 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 "utils.h"
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cstdio>
#include <fstream>
#include <functional>
#include <g3log/g3log.hpp> #include <g3log/g3log.hpp>
#include <g3log/logworker.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; const uint64_t g_iterations = 1000000;
std::atomic<size_t> g_counter = {0}; 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) 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; std::vector<uint64_t> all_measurements;
all_measurements.reserve(g_iterations); 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()); 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 // calc avg
auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus<uint64_t>()); auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus<uint64_t>());
auto avg = double(total)/all_measurements.size(); 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;
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 // The purpose of this test is NOT to see how fast
// each thread can possibly write. It is to see what // 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 // an atomic counter is used to give each thread what
// it is to write next. The overhead of atomic // it is to write next. The overhead of atomic
// synchronization between the threads are not counted in the worst case latency // 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) if (argc == 2)
{ {
number_of_threads = atoi(argv[1]); number_of_threads = atoi(argv[1]);
@@ -91,7 +82,6 @@ int main(int argc, char** argv)
return 1; return 1;
} }
std::vector<std::thread> threads(number_of_threads); std::vector<std::thread> threads(number_of_threads);
std::map<size_t, std::vector<uint64_t>> threads_result; 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); threads_result[idx].reserve(g_iterations);
} }
const std::string g_path = "./" ; const std::string g_path = "./";
const std::string g_prefix_log_name = "g3log-performance-"; 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_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt";
auto worker = g3::LogWorker::createLogWorker(); 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()); g3::initializeLogging(worker.get());
auto start_time_application_total = std::chrono::high_resolution_clock::now(); 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(); 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); PrintResults(threads_result, total_time_in_us);
return 0; return 0;
} }

View File

@@ -1,31 +1,26 @@
#include <thread> #include "utils.h"
#include <vector>
#include <atomic>
#include <iostream>
#include <chrono>
#include <algorithm> #include <algorithm>
#include <iostream> #include <atomic>
#include <chrono>
#include <cstdio> #include <cstdio>
#include <functional>
#include <iostream>
#include <map> #include <map>
#include <numeric> #include <numeric>
#include <functional>
#include "utils.h"
#include <thread> #include <thread>
#include <vector>
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
namespace spd = spdlog; namespace spd = spdlog;
namespace namespace {
{
const uint64_t g_iterations = 1000000; const uint64_t g_iterations = 1000000;
std::atomic<size_t> g_counter = {0}; 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"); auto logger = spd::get("file_logger");
while (true) 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; std::vector<uint64_t> all_measurements;
all_measurements.reserve(g_iterations); 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()); 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 // calc avg
auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus<uint64_t>()); auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus<uint64_t>());
auto avg = double(total)/all_measurements.size(); 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;
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 // The purpose of this test is NOT to see how fast
// each thread can possibly write. It is to see what // 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 // an atomic counter is used to give each thread what
// it is to write next. The overhead of atomic // it is to write next. The overhead of atomic
// synchronization between the threads are not counted in the worst case latency // 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) if (argc == 2)
{ {
number_of_threads = atoi(argv[1]); number_of_threads = atoi(argv[1]);
@@ -89,7 +82,6 @@ int main(int argc, char** argv)
return 1; return 1;
} }
std::vector<std::thread> threads(number_of_threads); std::vector<std::thread> threads(number_of_threads);
std::map<size_t, std::vector<uint64_t>> threads_result; 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); spdlog::set_async_mode(queue_size);
auto logger = spdlog::create<spd::sinks::simple_file_sink_mt>("file_logger", "spdlog.log", true); auto logger = spdlog::create<spd::sinks::simple_file_sink_mt>("file_logger", "spdlog.log", true);
//force flush on every call to compare with g3log // force flush on every call to compare with g3log
auto s = (spd::sinks::simple_file_sink_mt*)logger->sinks()[0].get(); auto s = (spd::sinks::simple_file_sink_mt *)logger->sinks()[0].get();
s->set_force_flush(true); s->set_force_flush(true);
auto start_time_application_total = std::chrono::high_resolution_clock::now(); 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(); 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); PrintResults(threads_result, total_time_in_us);
return 0; return 0;
} }

View File

@@ -5,15 +5,14 @@
#pragma once #pragma once
#include <sstream>
#include <iomanip> #include <iomanip>
#include <locale> #include <locale>
#include <sstream>
namespace utils namespace utils {
{
template<typename T> template<typename T>
inline std::string format(const T& value) inline std::string format(const T &value)
{ {
static std::locale loc(""); static std::locale loc("");
std::stringstream ss; std::stringstream ss;
@@ -23,7 +22,7 @@ inline std::string format(const T& value)
} }
template<> template<>
inline std::string format(const double & value) inline std::string format(const double &value)
{ {
static std::locale loc(""); static std::locale loc("");
std::stringstream ss; std::stringstream ss;
@@ -32,4 +31,4 @@ inline std::string format(const double & value)
return ss.str(); 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) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#include <thread>
#include <vector>
#include <atomic> #include <atomic>
#include <iostream>
#include <chrono> #include <chrono>
#include <cstdlib> #include <cstdlib>
#include <iostream>
#include <thread>
#include <vector>
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
using namespace std; using namespace std;
int main(int argc, char* argv[]) int main(int argc, char *argv[])
{ {
using namespace std::chrono; using namespace std::chrono;
using clock=steady_clock; using clock = steady_clock;
namespace spd = spdlog;
int thread_count = 10; int thread_count = 10;
if(argc > 1) if (argc > 1)
thread_count = ::atoi(argv[1]); thread_count = std::atoi(argv[1]);
int howmany = 1000000; int howmany = 1000000;
spd::set_async_mode(1048576); spdlog::set_async_mode(1000000);
auto logger = spdlog::create<spd::sinks::simple_file_sink_mt>("file_logger", "logs/spd-bench-async.txt", false); auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("file_logger", "logs/spdlog-bench-async.log", true);
logger->set_pattern("[%Y-%b-%d %T.%e]: %v"); 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}; while (run)
vector<thread> threads;
auto start = clock::now();
for (int t = 0; t < thread_count; ++t)
{ {
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) threads.push_back(std::thread([&]() {
{ while (true)
int counter = ++msg_counter; {
if (counter > howmany) break; int counter = ++msg_counter;
logger->info("spdlog message #{}: This is some text for your pleasure", counter); 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(); t.join();
}; }
duration<float> delta = clock::now() - start; duration<float> delta = clock::now() - start;
float deltaf = delta.count(); float deltaf = delta.count();
auto rate = howmany/deltaf; auto rate = howmany / deltaf;
cout << "Total: " << howmany << std::endl; std::cout << "Total: " << howmany << std::endl;
cout << "Threads: " << thread_count << std::endl; std::cout << "Threads: " << thread_count << std::endl;
std::cout << "Delta = " << deltaf << " seconds" << std::endl; std::cout << "Delta = " << std::fixed << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << rate << "/sec" << 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) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#include <atomic>
#include <chrono>
#include <cstdlib>
#include <iostream>
#include <thread> #include <thread>
#include <vector> #include <vector>
#include <atomic>
#include <cstdlib>
#include "spdlog/spdlog.h"
#include "spdlog/spdlog.h"
using namespace std; 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; int thread_count = 10;
if(argc > 1) if (argc > 1)
thread_count = std::atoi(argv[1]); thread_count = std::atoi(argv[1]);
int howmany = 1000000; 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); std::atomic<int> msg_counter{0};
logger->set_pattern("[%Y-%b-%d %T.%e]: %v");
std::atomic<int > msg_counter {0};
std::vector<thread> threads; std::vector<thread> threads;
auto start = clock::now();
for (int t = 0; t < thread_count; ++t) for (int t = 0; t < thread_count; ++t)
{ {
threads.push_back(std::thread([&]() threads.push_back(std::thread([&]() {
{
while (true) while (true)
{ {
int counter = ++msg_counter; int counter = ++msg_counter;
if (counter > howmany) break; if (counter > howmany)
break;
logger->info("spdlog message #{}: This is some text for your pleasure", counter); logger->info("spdlog message #{}: This is some text for your pleasure", counter);
} }
})); }));
} }
for (auto &t : threads)
for(auto &t:threads)
{ {
t.join(); 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; return 0;
} }

View File

@@ -3,18 +3,32 @@
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#include <chrono>
#include <iostream>
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
int main(int, char *[])
int main(int, char* [])
{ {
int howmany = 1000000; using namespace std::chrono;
namespace spd = spdlog; using clock = steady_clock;
///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);
logger->set_pattern("[%Y-%b-%d %T.%e]: %v"); int howmany = 1000000;
for(int i = 0 ; i < howmany; ++i)
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); 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; return 0;
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -6,18 +6,17 @@
// //
// bench.cpp : spdlog benchmarks // 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 <atomic>
#include <cstdlib> // EXIT_FAILURE #include <cstdlib> // EXIT_FAILURE
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <string> #include <string>
#include <thread> #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;
using namespace std::chrono; using namespace std::chrono;
@@ -25,11 +24,10 @@ using namespace spdlog;
using namespace spdlog::sinks; using namespace spdlog::sinks;
using namespace utils; using namespace utils;
void bench(int howmany, std::shared_ptr<spdlog::logger> log); void bench(int howmany, std::shared_ptr<spdlog::logger> log);
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count); void bench_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; int queue_size = 1048576;
@@ -41,33 +39,31 @@ int main(int argc, char* argv[])
try try
{ {
if(argc > 1) if (argc > 1)
howmany = atoi(argv[1]); howmany = atoi(argv[1]);
if (argc > 2) if (argc > 2)
threads = atoi(argv[2]); threads = atoi(argv[2]);
if (argc > 3) if (argc > 3)
queue_size = atoi(argv[3]); queue_size = atoi(argv[3]);
cout << "*******************************************************************************\n"; cout << "*******************************************************************************\n";
cout << "Single thread, " << format(howmany) << " iterations" << endl; cout << "Single thread, " << format(howmany) << " iterations" << endl;
cout << "*******************************************************************************\n"; 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); 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, daily_st);
bench(howmany, spdlog::create<null_sink_st>("null_st")); bench(howmany, spdlog::create<null_sink_st>("null_st"));
cout << "\n*******************************************************************************\n"; 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"; 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); bench_mt(howmany, rotating_mt, threads);
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt");
bench_mt(howmany, daily_mt, threads); bench_mt(howmany, daily_mt, threads);
bench(howmany, spdlog::create<null_sink_st>("null_mt")); 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 << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations " << endl;
cout << "*******************************************************************************\n"; cout << "*******************************************************************************\n";
spdlog::set_async_mode(queue_size); 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); bench_mt(howmany, as, threads);
spdlog::drop("as"); spdlog::drop("as");
} }
@@ -94,7 +89,6 @@ int main(int argc, char* argv[])
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
void bench(int howmany, std::shared_ptr<spdlog::logger> log) void bench(int howmany, std::shared_ptr<spdlog::logger> log)
{ {
cout << log->name() << "...\t\t" << flush; 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); log->info("Hello logger: msg number {}", i);
} }
auto delta = system_clock::now() - start; auto delta = system_clock::now() - start;
auto delta_d = duration_cast<duration<double>> (delta).count(); auto delta_d = duration_cast<duration<double>>(delta).count();
cout << format(int(howmany / delta_d)) << "/sec" << endl; 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) void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
{ {
cout << log->name() << "...\t\t" << flush; cout << log->name() << "...\t\t" << flush;
std::atomic<int > msg_counter {0}; std::atomic<int> msg_counter{0};
vector<thread> threads; vector<thread> threads;
auto start = system_clock::now(); auto start = system_clock::now();
for (int t = 0; t < thread_count; ++t) for (int t = 0; t < thread_count; ++t)
{ {
threads.push_back(std::thread([&]() threads.push_back(std::thread([&]() {
{ for (;;)
for(;;)
{ {
int counter = ++msg_counter; int counter = ++msg_counter;
if (counter > howmany) break; if (counter > howmany)
break;
log->info("Hello logger: msg number {}", counter); log->info("Hello logger: msg number {}", counter);
} }
})); }));
} }
for (auto &t : threads)
for(auto &t:threads)
{ {
t.join(); t.join();
}; };
auto delta = system_clock::now() - start; auto delta = system_clock::now() - start;
auto delta_d = duration_cast<duration<double>> (delta).count(); auto delta_d = duration_cast<duration<double>>(delta).count();
cout << format(int(howmany / delta_d)) << "/sec" << endl; 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(); void err_handler_example();
namespace spd = spdlog; namespace spd = spdlog;
int main(int, char*[]) int main(int, char *[])
{ {
try try
{ {
@@ -31,7 +31,6 @@ int main(int, char*[])
console->info("Welcome to spdlog!"); console->info("Welcome to spdlog!");
console->error("Some error message with arg{}..", 1); console->error("Some error message with arg{}..", 1);
// Formatting examples // Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12); console->warn("Easy padding in numbers like {:08d}", 12);
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
@@ -41,7 +40,6 @@ int main(int, char*[])
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function"); spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
// Create basic file logger (not rotated) // Create basic file logger (not rotated)
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt"); auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt");
my_logger->info("Some log message"); my_logger->info("Some log message");
@@ -49,7 +47,9 @@ int main(int, char*[])
// Create a file rotating logger with 5mb size max and 3 rotated files // Create a file rotating logger with 5mb size max and 3 rotated files
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3); auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; ++i)
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i); {
rotating_logger->info("{} * {} equals {:>10}", i, i, i * i);
}
// Create a daily logger - a new file is created every day on 2:30am // Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30); auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
@@ -58,22 +58,21 @@ int main(int, char*[])
daily_logger->info(123.44); daily_logger->info(123.44);
// Customize msg format for all messages // Customize msg format for all messages
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***"); spd::set_pattern("[%^+++%$] [%H:%M:%S %z] [thread %t] %v");
rotating_logger->info("This is another message with custom format"); console->info("This an info message with custom format");
console->error("This an error message with custom format");
// Runtime log levels // Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info spd::set_level(spd::level::info); // Set global log level to info
console->debug("This message shold not be displayed!"); console->debug("This message should not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message shold be displayed.."); console->debug("This message should be displayed..");
// Compile time log levels // Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON // define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23); SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23); SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
// Asynchronous logging is very fast.. // Asynchronous logging is very fast..
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous.. // Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
async_example(); async_example();
@@ -91,16 +90,13 @@ int main(int, char*[])
err_handler_example(); err_handler_example();
// Apply a function on all registered loggers // Apply a function on all registered loggers
spd::apply_all([&](std::shared_ptr<spdlog::logger> l) spd::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); });
{
l->info("End of example.");
});
// Release and close all loggers // Release and close all loggers
spdlog::drop_all(); spdlog::drop_all();
} }
// Exceptions will only be thrown upon failed logger or sink construction (not during logging) // Exceptions will only be thrown upon failed logger or sink construction (not during logging)
catch (const spd::spdlog_ex& ex) catch (const spd::spdlog_ex &ex)
{ {
std::cout << "Log init failed: " << ex.what() << std::endl; std::cout << "Log init failed: " << ex.what() << std::endl;
return 1; return 1;
@@ -109,14 +105,16 @@ int main(int, char*[])
void async_example() void async_example()
{ {
size_t q_size = 4096; //queue size must be power of 2 size_t q_size = 4096;
spdlog::set_async_mode(q_size); spdlog::set_async_mode(q_size);
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt"); auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
for (int i = 0; i < 100; ++i) for (int i = 0; i < 100; ++i)
{
async_file->info("Async message #{}", i); async_file->info("Async message #{}", i);
}
} }
//syslog example (linux/osx/freebsd) // syslog example (linux/osx/freebsd)
void syslog_example() void syslog_example()
{ {
#ifdef SPDLOG_ENABLE_SYSLOG #ifdef SPDLOG_ENABLE_SYSLOG
@@ -141,27 +139,24 @@ struct my_type
{ {
int i; int i;
template<typename OStream> 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 #include "spdlog/fmt/ostr.h" // must be included
void user_defined_example() void user_defined_example()
{ {
spd::get("console")->info("user defined type: {}", my_type { 14 }); spd::get("console")->info("user defined type: {}", my_type{14});
} }
// //
//custom error handler // custom error handler
// //
void err_handler_example() void err_handler_example()
{ {
//can be set globaly or per logger(logger->set_error_handler(..)) // can be set globaly or per logger(logger->set_error_handler(..))
spdlog::set_error_handler([](const std::string& msg) spdlog::set_error_handler([](const std::string &msg) { std::cerr << "my err handler: " << msg << std::endl; });
{
std::cerr << "my err handler: " << msg << std::endl;
});
spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3); 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 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14 # Visual Studio 15
VisualStudioVersion = 14.0.25420.1 VisualStudioVersion = 15.0.27428.2037
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example", "example.vcxproj", "{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example", "example.vcxproj", "{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}"
EndProject 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 Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32 Debug|Win32 = Debug|Win32
@@ -23,4 +96,15 @@ Global
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection 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 EndGlobal

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?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"> <ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32"> <ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration> <Configuration>Debug</Configuration>
@@ -13,56 +13,23 @@
<ItemGroup> <ItemGroup>
<ClCompile Include="example.cpp" /> <ClCompile Include="example.cpp" />
</ItemGroup> </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"> <PropertyGroup Label="Globals">
<ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid> <ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid>
<Keyword>Win32Proj</Keyword> <Keyword>Win32Proj</Keyword>
<RootNamespace>.</RootNamespace> <RootNamespace>.</RootNamespace>
<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> <WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup> </PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries> <UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset> <PlatformToolset>v141</PlatformToolset>
<CharacterSet>Unicode</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries> <UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v120</PlatformToolset> <PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization> <WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet> <CharacterSet>Unicode</CharacterSet>
</PropertyGroup> </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> #include <memory>
namespace spd = spdlog; namespace spd = spdlog;
int main(int, char*[]) int main(int, char *[])
{ {
bool enable_debug = true; bool enable_debug = true;
try try
@@ -13,24 +13,24 @@ int main(int, char*[])
// This means that the same log_msg is forwarded to multiple sinks; // 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. // Each sink can have it's own log level and a message will be logged.
std::vector<spdlog::sink_ptr> sinks; 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::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_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::simple_file_sink_mt>("./log_debug_file.txt"));
spdlog::logger console_multisink("multisink", sinks.begin(), sinks.end() ); spdlog::logger console_multisink("multisink", sinks.begin(), sinks.end());
console_multisink.set_level( spdlog::level::warn); console_multisink.set_level(spdlog::level::warn);
sinks[0]->set_level( spdlog::level::trace); // console. Allow everything. Default value 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[1]->set_level(spdlog::level::trace); // regular file. Allow everything. Default value
sinks[2]->set_level( spdlog::level::off); // regular file. Ignore everything. sinks[2]->set_level(spdlog::level::off); // regular file. Ignore everything.
console_multisink.warn("warn: will print only on console and regular file"); 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 console_multisink.set_level(spdlog::level::debug); // level of the logger
sinks[1]->set_level( spdlog::level::debug); // regular file sinks[1]->set_level(spdlog::level::debug); // regular file
sinks[2]->set_level( spdlog::level::debug); // debug file sinks[2]->set_level(spdlog::level::debug); // debug file
} }
console_multisink.debug("Debug: you should see this on console and both files"); console_multisink.debug("Debug: you should see this on console and both files");
@@ -38,10 +38,9 @@ int main(int, char*[])
spdlog::drop_all(); spdlog::drop_all();
} }
// Exceptions will only be thrown upon failed logger or sink construction (not during logging) // Exceptions will only be thrown upon failed logger or sink construction (not during logging)
catch (const spd::spdlog_ex& ex) catch (const spd::spdlog_ex &ex)
{ {
std::cout << "Log init failed: " << ex.what() << std::endl; std::cout << "Log init failed: " << ex.what() << std::endl;
return 1; return 1;
} }
} }

View File

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

@@ -20,63 +20,53 @@
#include <chrono> #include <chrono>
#include <functional> #include <functional>
#include <string>
#include <memory> #include <memory>
#include <string>
namespace spdlog namespace spdlog {
{
namespace details namespace details {
{
class async_log_helper; class async_log_helper;
} }
class async_logger SPDLOG_FINAL :public logger class async_logger SPDLOG_FINAL : public logger
{ {
public: public:
template<class It> template<class It>
async_logger(const std::string& name, async_logger(const std::string &logger_name, const It &begin, const It &end, size_t queue_size,
const It& begin, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const It& end, const std::function<void()> &worker_warmup_cb = nullptr,
size_t queue_size, const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(),
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()> &worker_teardown_cb = nullptr);
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, async_logger(const std::string &logger_name, sinks_init_list sinks, size_t queue_size,
sinks_init_list sinks, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
size_t queue_size, const std::function<void()> &worker_warmup_cb = nullptr,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()>& worker_warmup_cb = nullptr, const std::function<void()> &worker_teardown_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, async_logger(const std::string &logger_name, sink_ptr single_sink, size_t queue_size,
sink_ptr single_sink, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
size_t queue_size, const std::function<void()> &worker_warmup_cb = nullptr,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()>& worker_warmup_cb = nullptr, const std::function<void()> &worker_teardown_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 // Wait for the queue to be empty, and flush synchronously
//Warning: this can potentially last forever as we wait it to complete // Warning: this can potentially last forever as we wait it to complete
void flush() override; void flush() override;
// Error handler // Error handler
virtual void set_error_handler(log_err_handler) override; void set_error_handler(log_err_handler) override;
virtual log_err_handler error_handler() override; log_err_handler error_handler() override;
protected: protected:
void _sink_it(details::log_msg& msg) override; void _sink_it(details::log_msg &msg) override;
void _set_formatter(spdlog::formatter_ptr msg_formatter) override; void _set_formatter(spdlog::formatter_ptr msg_formatter) override;
void _set_pattern(const std::string& pattern, pattern_time_type pattern_time) override; void _set_pattern(const std::string &pattern, pattern_time_type pattern_time) override;
private: private:
std::unique_ptr<details::async_log_helper> _async_log_helper; std::unique_ptr<details::async_log_helper> _async_log_helper;
}; };
} } // namespace spdlog
#include "details/async_logger_impl.h" #include "details/async_logger_impl.h"

View File

@@ -5,13 +5,18 @@
#pragma once #pragma once
#include <string> #define SPDLOG_VERSION "0.17.0"
#include <initializer_list>
#include <chrono> #include "tweakme.h"
#include <memory>
#include <atomic> #include <atomic>
#include <chrono>
#include <exception> #include <exception>
#include<functional> #include <functional>
#include <initializer_list>
#include <memory>
#include <string>
#include <unordered_map>
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
#include <codecvt> #include <codecvt>
@@ -20,7 +25,7 @@
#include "details/null_mutex.h" #include "details/null_mutex.h"
//visual studio upto 2013 does not support noexcept nor constexpr // visual studio upto 2013 does not support noexcept nor constexpr
#if defined(_MSC_VER) && (_MSC_VER < 1900) #if defined(_MSC_VER) && (_MSC_VER < 1900)
#define SPDLOG_NOEXCEPT throw() #define SPDLOG_NOEXCEPT throw()
#define SPDLOG_CONSTEXPR #define SPDLOG_CONSTEXPR
@@ -36,7 +41,7 @@
#define SPDLOG_FINAL final #define SPDLOG_FINAL final
#endif #endif
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#define SPDLOG_DEPRECATED __attribute__((deprecated)) #define SPDLOG_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
#define SPDLOG_DEPRECATED __declspec(deprecated) #define SPDLOG_DEPRECATED __declspec(deprecated)
@@ -46,19 +51,17 @@
#include "fmt/fmt.h" #include "fmt/fmt.h"
namespace spdlog namespace spdlog {
{
class formatter; class formatter;
namespace sinks namespace sinks {
{
class sink; class sink;
} }
using log_clock = std::chrono::system_clock; using log_clock = std::chrono::system_clock;
using sink_ptr = std::shared_ptr < sinks::sink >; using sink_ptr = std::shared_ptr<sinks::sink>;
using sinks_init_list = std::initializer_list < sink_ptr >; using sinks_init_list = std::initializer_list<sink_ptr>;
using formatter_ptr = std::shared_ptr<spdlog::formatter>; using formatter_ptr = std::shared_ptr<spdlog::formatter>;
#if defined(SPDLOG_NO_ATOMIC_LEVELS) #if defined(SPDLOG_NO_ATOMIC_LEVELS)
using level_t = details::null_atomic_int; using level_t = details::null_atomic_int;
@@ -68,10 +71,9 @@ using level_t = std::atomic<int>;
using log_err_handler = std::function<void(const std::string &err_msg)>; using log_err_handler = std::function<void(const std::string &err_msg)>;
//Log level enum // Log level enum
namespace level namespace level {
{ enum level_enum
typedef enum
{ {
trace = 0, trace = 0,
debug = 1, debug = 1,
@@ -80,33 +82,51 @@ typedef enum
err = 4, err = 4,
critical = 5, critical = 5,
off = 6 off = 6
} level_enum; };
#if !defined(SPDLOG_LEVEL_NAMES) #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 #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]; 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]; 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. // Async overflow policy - block by default.
// //
enum class async_overflow_policy 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 discard_log_msg // Discard the message it enqueue fails
}; };
@@ -123,29 +143,31 @@ enum class pattern_time_type
// //
// Log exception // Log exception
// //
namespace details namespace details {
{ namespace os {
namespace os
{
std::string errno_str(int err_num); std::string errno_str(int err_num);
} }
} } // namespace details
class spdlog_ex: public std::exception class spdlog_ex : public std::exception
{ {
public: public:
spdlog_ex(const std::string& msg):_msg(msg) explicit spdlog_ex(std::string msg)
{} : _msg(std::move(msg))
spdlog_ex(const std::string& msg, int last_errno) {
}
spdlog_ex(const std::string &msg, int last_errno)
{ {
_msg = msg + ": " + details::os::errno_str(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(); return _msg.c_str();
} }
private: private:
std::string _msg; std::string _msg;
}; };
// //
@@ -157,5 +179,4 @@ using filename_t = std::wstring;
using filename_t = std::string; using filename_t = std::string;
#endif #endif
} // namespace spdlog
} //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

@@ -13,25 +13,25 @@
#pragma once #pragma once
#include "../common.h" #include "../common.h"
#include "../sinks/sink.h"
#include "../details/mpmc_bounded_q.h"
#include "../details/log_msg.h" #include "../details/log_msg.h"
#include "../details/mpmc_blocking_q.h"
#include "../details/os.h" #include "../details/os.h"
#include "../formatter.h" #include "../formatter.h"
#include "../sinks/sink.h"
#include <chrono> #include <chrono>
#include <condition_variable>
#include <exception> #include <exception>
#include <functional> #include <functional>
#include <iostream>
#include <memory> #include <memory>
#include <string> #include <string>
#include <thread> #include <thread>
#include <utility> #include <utility>
#include <vector> #include <vector>
namespace spdlog namespace spdlog {
{ namespace details {
namespace details
{
class async_log_helper class async_log_helper
{ {
@@ -43,9 +43,9 @@ class async_log_helper
flush, flush,
terminate terminate
}; };
struct async_msg struct async_msg
{ {
std::string logger_name;
level::level_enum level; level::level_enum level;
log_clock::time_point time; log_clock::time_point time;
size_t thread_id; size_t thread_id;
@@ -56,96 +56,77 @@ class async_log_helper
async_msg() = default; async_msg() = default;
~async_msg() = default; ~async_msg() = default;
explicit async_msg(async_msg_type m_type)
async_msg(async_msg&& other) SPDLOG_NOEXCEPT: : level(level::info)
logger_name(std::move(other.logger_name)), , thread_id(0)
level(std::move(other.level)), , msg_type(m_type)
time(std::move(other.time)), , msg_id(0)
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
{ {
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.. // never copy or assign. should only be moved..
async_msg(const async_msg&) = delete; async_msg(const async_msg &) = delete;
async_msg& operator=(const async_msg& other) = delete; async_msg &operator=(const async_msg &other) = delete;
// construct from log_msg // construct from log_msg
async_msg(const details::log_msg& m): explicit async_msg(const details::log_msg &m)
level(m.level), : level(m.level)
time(m.time), , time(m.time)
thread_id(m.thread_id), , thread_id(m.thread_id)
txt(m.raw.data(), m.raw.size()), , txt(m.raw.data(), m.raw.size())
msg_type(async_msg_type::log), , msg_type(async_msg_type::log)
msg_id(m.msg_id) , msg_id(m.msg_id)
{ {
#ifndef SPDLOG_NO_NAME
logger_name = *m.logger_name;
#endif
} }
// copy into log_msg // 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.level = level;
msg.time = time; msg.time = time;
msg.thread_id = thread_id; msg.thread_id = thread_id;
msg.raw.clear();
msg.raw << txt; msg.raw << txt;
msg.msg_id = msg_id; msg.msg_id = msg_id;
} }
}; };
public: public:
using item_type = async_msg; using item_type = async_msg;
using q_type = details::mpmc_bounded_queue<item_type>; using q_type = details::mpmc_bounded_queue<item_type>;
using clock = std::chrono::steady_clock; using clock = std::chrono::steady_clock;
async_log_helper(std::string logger_name,
async_log_helper(formatter_ptr formatter, formatter_ptr formatter,
const std::vector<sink_ptr>& sinks, std::vector<sink_ptr> sinks,
size_t queue_size, size_t queue_size,
const log_err_handler err_handler, const log_err_handler err_handler,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr, std::function<void()> worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()>& worker_teardown_cb = nullptr); 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 // stop logging and join the back thread
~async_log_helper(); ~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); void set_error_handler(spdlog::log_err_handler err_handler);
private: private:
std::string _logger_name;
formatter_ptr _formatter; formatter_ptr _formatter;
std::vector<std::shared_ptr<sinks::sink>> _sinks; std::vector<std::shared_ptr<sinks::sink>> _sinks;
@@ -154,10 +135,7 @@ private:
log_err_handler _err_handler; log_err_handler _err_handler;
bool _flush_requested; std::chrono::time_point<log_clock> _last_flush;
bool _terminate_requested;
// overflow policy // overflow policy
const async_overflow_policy _overflow_policy; const async_overflow_policy _overflow_policy;
@@ -171,62 +149,62 @@ private:
// worker thread teardown callback // worker thread teardown callback
const std::function<void()> _worker_teardown_cb; 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 // worker thread
std::thread _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 // worker thread main loop
void worker_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 // 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); void handle_flush_interval();
// 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 flush_sinks();
}; };
} } // namespace details
} } // namespace spdlog
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// async_sink class implementation // async_sink class implementation
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
inline spdlog::details::async_log_helper::async_log_helper( inline spdlog::details::async_log_helper::async_log_helper(std::string logger_name,
formatter_ptr formatter, formatter_ptr formatter,
const std::vector<sink_ptr>& sinks, std::vector<sink_ptr> sinks,
size_t queue_size, size_t queue_size,
log_err_handler err_handler, log_err_handler err_handler,
const async_overflow_policy overflow_policy, const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb, std::function<void()> worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms, const std::chrono::milliseconds &flush_interval_ms,
const std::function<void()>& worker_teardown_cb): std::function<void()> worker_teardown_cb)
_formatter(formatter), : _logger_name(std::move(logger_name))
_sinks(sinks), , _formatter(std::move(formatter))
_q(queue_size), , _sinks(std::move(sinks))
_err_handler(err_handler), , _q(queue_size)
_flush_requested(false), , _err_handler(std::move(err_handler))
_terminate_requested(false), , _last_flush(os::now())
_overflow_policy(overflow_policy), , _overflow_policy(overflow_policy)
_worker_warmup_cb(worker_warmup_cb), , _worker_warmup_cb(std::move(worker_warmup_cb))
_flush_interval_ms(flush_interval_ms), , _flush_interval_ms(flush_interval_ms)
_worker_teardown_cb(worker_teardown_cb), , _worker_teardown_cb(std::move(worker_teardown_cb))
_worker_thread(&async_log_helper::worker_loop, this) {
{} _worker_thread = std::thread(&async_log_helper::worker_loop, this);
}
// Send to the worker thread termination message(level=off) // send to the worker thread terminate message, and join it.
// and wait for it to finish gracefully
inline spdlog::details::async_log_helper::~async_log_helper() inline spdlog::details::async_log_helper::~async_log_helper()
{ {
try 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(); _worker_thread.join();
} }
catch (...) // don't crash in destructor catch (...) // don't crash in destructor
@@ -234,166 +212,131 @@ 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)
//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)
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(); _q.enqueue(std::move(new_msg));
auto now = last_op_time; }
do else
{ {
now = details::os::now(); _q.enqueue_nowait(std::move(new_msg));
sleep_or_yield(now, last_op_time);
}
while (!_q.enqueue(std::move(new_msg)));
} }
} }
// optionally wait for the queue be empty and request flush from the sinks // 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)); enqueue_msg(async_msg(async_msg_type::flush), _overflow_policy);
if (wait_for_q)
wait_empty_q(); //return when queue is empty
} }
inline void spdlog::details::async_log_helper::worker_loop() inline void spdlog::details::async_log_helper::worker_loop()
{ {
if (_worker_warmup_cb) _worker_warmup_cb(); if (_worker_warmup_cb)
auto last_pop = details::os::now(); {
auto last_flush = last_pop; _worker_warmup_cb();
}
auto active = true; auto active = true;
while (active) while (active)
{ {
try try
{ {
active = process_next_msg(last_pop, last_flush); active = process_next_msg();
} }
catch (const std::exception &ex) catch (const std::exception &ex)
{ {
_err_handler(ex.what()); _err_handler(ex.what());
} }
catch(...) catch (...)
{ {
_err_handler("Unknown exeption in async logger worker loop."); _err_handler("Unknown 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 // process next message in the queue
// return true if this thread should still be active (while no terminate msg was received) // 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; async_msg incoming_async_msg;
bool dequeued = _q.dequeue_for(incoming_async_msg, std::chrono::seconds(2));
if (_q.dequeue(incoming_async_msg)) if (!dequeued)
{ {
last_pop = details::os::now(); handle_flush_interval();
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);
}
}
}
return true; return true;
} }
// Handle empty queue.. switch (incoming_async_msg.msg_type)
// This is the only place where the queue can terminate or flush to avoid losing messages already in the queue
else
{ {
auto now = details::os::now(); case async_msg_type::flush:
handle_flush_interval(now, last_flush); flush_sinks();
sleep_or_yield(now, last_pop); return true;
return !_terminate_requested;
}
}
// flush all sinks if _flush_interval_ms has expired case async_msg_type::terminate:
inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush) flush_sinks();
{ return false;
auto should_flush = _flush_requested || (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms);
if (should_flush) 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) for (auto &s : _sinks)
s->flush(); {
now = last_flush = details::os::now(); if (s->should_log(incoming_log_msg.level))
_flush_requested = false; {
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) inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter)
{ {
_formatter = msg_formatter; _formatter = std::move(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 500 ms
return sleep_for(milliseconds(500));
}
// wait for the queue to be empty
inline void spdlog::details::async_log_helper::wait_empty_q()
{
auto last_op = details::os::now();
while (!_q.is_empty())
{
sleep_or_yield(details::os::now(), last_op);
}
} }
inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler) 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 // Async Logger implementation
// Use an async_sink (queue per logger) to perform the logging in a worker thread // Use an async_sink (queue per logger) to perform the logging in a worker thread
#include "../details/async_log_helper.h"
#include "../async_logger.h" #include "../async_logger.h"
#include "../details/async_log_helper.h"
#include <string>
#include <functional>
#include <chrono> #include <chrono>
#include <functional>
#include <memory> #include <memory>
#include <string>
template<class It> template<class It>
inline spdlog::async_logger::async_logger(const std::string& logger_name, inline spdlog::async_logger::async_logger(const std::string &logger_name, const It &begin, const It &end, size_t queue_size,
const It& begin, const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb,
const It& end, const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb)
size_t queue_size, : logger(logger_name, begin, end)
const async_overflow_policy overflow_policy, , _async_log_helper(new details::async_log_helper(
const std::function<void()>& worker_warmup_cb, logger_name, _formatter, _sinks, queue_size, _err_handler, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_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, inline spdlog::async_logger::async_logger(const std::string &logger_name, sinks_init_list sinks_list, size_t queue_size,
sinks_init_list sinks_list, const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb,
size_t queue_size, const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb)
const async_overflow_policy overflow_policy, : async_logger(logger_name, sinks_list.begin(), sinks_list.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms,
const std::function<void()>& worker_warmup_cb, worker_teardown_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,
{ {
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() inline void spdlog::async_logger::flush()
{ {
_async_log_helper->flush(true); _async_log_helper->flush();
} }
// Error handler // Error handler
@@ -62,28 +52,25 @@ inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_
{ {
_err_handler = err_handler; _err_handler = err_handler;
_async_log_helper->set_error_handler(err_handler); _async_log_helper->set_error_handler(err_handler);
} }
inline spdlog::log_err_handler spdlog::async_logger::error_handler() inline spdlog::log_err_handler spdlog::async_logger::error_handler()
{ {
return _err_handler; return _err_handler;
} }
inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter) inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)
{ {
_formatter = msg_formatter; _formatter = msg_formatter;
_async_log_helper->set_formatter(_formatter); _async_log_helper->set_formatter(_formatter);
} }
inline void spdlog::async_logger::_set_pattern(const std::string& pattern, 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); _formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
_async_log_helper->set_formatter(_formatter); _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 try
{ {
@@ -92,16 +79,17 @@ inline void spdlog::async_logger::_sink_it(details::log_msg& msg)
#endif #endif
_async_log_helper->log(msg); _async_log_helper->log(msg);
if (_should_flush_on(msg)) if (_should_flush_on(msg))
_async_log_helper->flush(false); // do async flush {
_async_log_helper->flush(); // do async flush
}
} }
catch (const std::exception &ex) catch (const std::exception &ex)
{ {
_err_handler(ex.what()); _err_handler(ex.what());
} }
catch(...) catch (...)
{ {
_err_handler("Unknown exception in logger " + _name); _err_handler("Unknown exception in logger " + _name);
throw; throw;
} }
} }

View File

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

View File

@@ -8,21 +8,17 @@
#include "../common.h" #include "../common.h"
#include "../details/os.h" #include "../details/os.h"
#include <string> #include <string>
#include <utility> #include <utility>
namespace spdlog namespace spdlog {
{ namespace details {
namespace details
{
struct log_msg struct log_msg
{ {
log_msg() = default; log_msg() = default;
log_msg(const std::string *loggers_name, level::level_enum lvl) : log_msg(const std::string *loggers_name, level::level_enum lvl)
logger_name(loggers_name), : logger_name(loggers_name)
level(lvl), , level(lvl)
msg_id(0)
{ {
#ifndef SPDLOG_NO_DATETIME #ifndef SPDLOG_NO_DATETIME
time = os::now(); time = os::now();
@@ -33,18 +29,20 @@ struct log_msg
#endif #endif
} }
log_msg(const log_msg& other) = delete; log_msg(const log_msg &other) = delete;
log_msg& operator=(log_msg&& other) = delete; log_msg &operator=(log_msg &&other) = delete;
log_msg(log_msg&& other) = delete; log_msg(log_msg &&other) = delete;
const std::string *logger_name{nullptr};
const std::string *logger_name;
level::level_enum level; level::level_enum level;
log_clock::time_point time; log_clock::time_point time;
size_t thread_id; size_t thread_id;
fmt::MemoryWriter raw; fmt::MemoryWriter raw;
fmt::MemoryWriter formatted; 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

@@ -6,7 +6,6 @@
#pragma once #pragma once
#include "../logger.h" #include "../logger.h"
#include "../sinks/stdout_sinks.h"
#include <memory> #include <memory>
#include <string> #include <string>
@@ -14,53 +13,49 @@
// create logger with given name, sinks and the default pattern formatter // create logger with given name, sinks and the default pattern formatter
// all other ctors will call this one // all other ctors will call this one
template<class It> template<class It>
inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end): inline spdlog::logger::logger(std::string logger_name, const It &begin, const It &end)
_name(logger_name), : _name(std::move(logger_name))
_sinks(begin, end), , _sinks(begin, end)
_formatter(std::make_shared<pattern_formatter>("%+")), , _formatter(std::make_shared<pattern_formatter>("%+"))
_level(level::info), , _level(level::info)
_flush_level(level::off), , _flush_level(level::off)
_last_err_time(0), , _last_err_time(0)
_msg_counter(1) // message counter will start from 1. 0-message id will be reserved for controll messages , _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) _err_handler = [this](const std::string &msg) { this->_default_err_handler(msg); };
{
this->_default_err_handler(msg);
};
} }
// ctor with sinks as init list // ctor with sinks as init list
inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list): inline spdlog::logger::logger(const std::string &logger_name, sinks_init_list sinks_list)
logger(logger_name, sinks_list.begin(), sinks_list.end()) : logger(logger_name, sinks_list.begin(), sinks_list.end())
{} {
}
// ctor with single sink // ctor with single sink
inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink): inline spdlog::logger::logger(const std::string &logger_name, spdlog::sink_ptr single_sink)
logger(logger_name, : logger(logger_name, {std::move(single_sink)})
{ {
single_sink }
})
{}
inline spdlog::logger::~logger() = default; inline spdlog::logger::~logger() = default;
inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter) 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); _set_pattern(pattern, pattern_time);
} }
template <typename... Args> template<typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args) inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args)
{ {
if (!should_log(lvl)) return; if (!should_log(lvl))
{
return;
}
try try
{ {
@@ -77,17 +72,20 @@ inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Ar
{ {
_err_handler(ex.what()); _err_handler(ex.what());
} }
catch(...) catch (...)
{ {
_err_handler("Unknown exception in logger " + _name); _err_handler("Unknown exception in logger " + _name);
throw; throw;
} }
} }
template <typename... Args> template<typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char* msg) inline void spdlog::logger::log(level::level_enum lvl, const char *msg)
{ {
if (!should_log(lvl)) return; if (!should_log(lvl))
{
return;
}
try try
{ {
details::log_msg log_msg(&_name, lvl); details::log_msg log_msg(&_name, lvl);
@@ -98,12 +96,20 @@ inline void spdlog::logger::log(level::level_enum lvl, const char* msg)
{ {
_err_handler(ex.what()); _err_handler(ex.what());
} }
catch (...)
{
_err_handler("Unknown exception in logger " + _name);
throw;
}
} }
template<typename T> 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 try
{ {
details::log_msg log_msg(&_name, lvl); details::log_msg log_msg(&_name, lvl);
@@ -114,100 +120,99 @@ inline void spdlog::logger::log(level::level_enum lvl, const T& msg)
{ {
_err_handler(ex.what()); _err_handler(ex.what());
} }
catch (...)
{
_err_handler("Unknown exception in logger " + _name);
throw;
}
} }
template<typename Arg1, typename... Args>
template <typename Arg1, typename... Args> inline void spdlog::logger::trace(const char *fmt, const Arg1 &arg1, const Args &... args)
inline void spdlog::logger::trace(const char* fmt, const Arg1 &arg1, const Args&... args)
{ {
log(level::trace, fmt, arg1, args...); log(level::trace, fmt, arg1, args...);
} }
template <typename Arg1, typename... Args> template<typename Arg1, typename... Args>
inline void spdlog::logger::debug(const char* fmt, const Arg1 &arg1, const Args&... args) inline void spdlog::logger::debug(const char *fmt, const Arg1 &arg1, const Args &... args)
{ {
log(level::debug, fmt, arg1, args...); log(level::debug, fmt, arg1, args...);
} }
template <typename Arg1, typename... Args> template<typename Arg1, typename... Args>
inline void spdlog::logger::info(const char* fmt, const Arg1 &arg1, const Args&... args) inline void spdlog::logger::info(const char *fmt, const Arg1 &arg1, const Args &... args)
{ {
log(level::info, fmt, arg1, args...); log(level::info, fmt, arg1, args...);
} }
template <typename Arg1, typename... Args> template<typename Arg1, typename... Args>
inline void spdlog::logger::warn(const char* fmt, const Arg1 &arg1, const Args&... args) inline void spdlog::logger::warn(const char *fmt, const Arg1 &arg1, const Args &... args)
{ {
log(level::warn, fmt, arg1, args...); log(level::warn, fmt, arg1, args...);
} }
template <typename Arg1, typename... Args> template<typename Arg1, typename... Args>
inline void spdlog::logger::error(const char* fmt, const Arg1 &arg1, const Args&... args) inline void spdlog::logger::error(const char *fmt, const Arg1 &arg1, const Args &... args)
{ {
log(level::err, fmt, arg1, args...); log(level::err, fmt, arg1, args...);
} }
template <typename Arg1, typename... Args> template<typename Arg1, typename... Args>
inline void spdlog::logger::critical(const char* fmt, const Arg1 &arg1, const Args&... args) inline void spdlog::logger::critical(const char *fmt, const Arg1 &arg1, const Args &... args)
{ {
log(level::critical, fmt, arg1, args...); log(level::critical, fmt, arg1, args...);
} }
template<typename T> template<typename T>
inline void spdlog::logger::trace(const T& msg) inline void spdlog::logger::trace(const T &msg)
{ {
log(level::trace, msg); log(level::trace, msg);
} }
template<typename T> template<typename T>
inline void spdlog::logger::debug(const T& msg) inline void spdlog::logger::debug(const T &msg)
{ {
log(level::debug, msg); log(level::debug, msg);
} }
template<typename T> template<typename T>
inline void spdlog::logger::info(const T& msg) inline void spdlog::logger::info(const T &msg)
{ {
log(level::info, msg); log(level::info, msg);
} }
template<typename T> template<typename T>
inline void spdlog::logger::warn(const T& msg) inline void spdlog::logger::warn(const T &msg)
{ {
log(level::warn, msg); log(level::warn, msg);
} }
template<typename T> template<typename T>
inline void spdlog::logger::error(const T& msg) inline void spdlog::logger::error(const T &msg)
{ {
log(level::err, msg); log(level::err, msg);
} }
template<typename T> template<typename T>
inline void spdlog::logger::critical(const T& msg) inline void spdlog::logger::critical(const T &msg)
{ {
log(level::critical, msg); log(level::critical, msg);
} }
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
#include <codecvt> #include <codecvt>
#include <locale> #include <locale>
template <typename... Args> template<typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* msg) 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)); log(lvl, conv.to_bytes(msg));
} }
template <typename... Args> template<typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* fmt, const Args&... args) inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args)
{ {
fmt::WMemoryWriter wWriter; fmt::WMemoryWriter wWriter;
@@ -215,51 +220,48 @@ inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* fmt, const
log(lvl, wWriter.c_str()); log(lvl, wWriter.c_str());
} }
template <typename... Args> template<typename... Args>
inline void spdlog::logger::trace(const wchar_t* fmt, const Args&... args) inline void spdlog::logger::trace(const wchar_t *fmt, const Args &... args)
{ {
log(level::trace, fmt, args...); log(level::trace, fmt, args...);
} }
template <typename... Args> template<typename... Args>
inline void spdlog::logger::debug(const wchar_t* fmt, const Args&... args) inline void spdlog::logger::debug(const wchar_t *fmt, const Args &... args)
{ {
log(level::debug, fmt, args...); log(level::debug, fmt, args...);
} }
template <typename... Args> template<typename... Args>
inline void spdlog::logger::info(const wchar_t* fmt, const Args&... args) inline void spdlog::logger::info(const wchar_t *fmt, const Args &... args)
{ {
log(level::info, fmt, args...); log(level::info, fmt, args...);
} }
template<typename... Args>
template <typename... Args> inline void spdlog::logger::warn(const wchar_t *fmt, const Args &... args)
inline void spdlog::logger::warn(const wchar_t* fmt, const Args&... args)
{ {
log(level::warn, fmt, args...); log(level::warn, fmt, args...);
} }
template <typename... Args> template<typename... Args>
inline void spdlog::logger::error(const wchar_t* fmt, const Args&... args) inline void spdlog::logger::error(const wchar_t *fmt, const Args &... args)
{ {
log(level::err, fmt, args...); log(level::err, fmt, args...);
} }
template <typename... Args> template<typename... Args>
inline void spdlog::logger::critical(const wchar_t* fmt, const Args&... args) inline void spdlog::logger::critical(const wchar_t *fmt, const Args &... args)
{ {
log(level::critical, fmt, args...); log(level::critical, fmt, args...);
} }
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT #endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
// //
// name and level // name and level
// //
inline const std::string& spdlog::logger::name() const inline const std::string &spdlog::logger::name() const
{ {
return _name; return _name;
} }
@@ -271,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) 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() inline spdlog::log_err_handler spdlog::logger::error_handler()
@@ -279,7 +281,6 @@ inline spdlog::log_err_handler spdlog::logger::error_handler()
return _err_handler; return _err_handler;
} }
inline void spdlog::logger::flush_on(level::level_enum log_level) inline void spdlog::logger::flush_on(level::level_enum log_level)
{ {
_flush_level.store(log_level); _flush_level.store(log_level);
@@ -298,7 +299,7 @@ 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 // 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) #if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
_incr_msg_counter(msg); _incr_msg_counter(msg);
@@ -306,43 +307,48 @@ inline void spdlog::logger::_sink_it(details::log_msg& msg)
_formatter->format(msg); _formatter->format(msg);
for (auto &sink : _sinks) for (auto &sink : _sinks)
{ {
if( sink->should_log( msg.level)) if (sink->should_log(msg.level))
{ {
sink->log(msg); sink->log(msg);
} }
} }
if(_should_flush_on(msg)) if (_should_flush_on(msg))
{
flush(); 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); _formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
} }
inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter) inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter)
{ {
_formatter = msg_formatter; _formatter = std::move(msg_formatter);
} }
inline void spdlog::logger::flush() inline void spdlog::logger::flush()
{ {
for (auto& sink : _sinks) for (auto &sink : _sinks)
{
sink->flush(); sink->flush();
}
} }
inline void spdlog::logger::_default_err_handler(const std::string &msg) inline void spdlog::logger::_default_err_handler(const std::string &msg)
{ {
auto now = time(nullptr); auto now = time(nullptr);
if (now - _last_err_time < 60) if (now - _last_err_time < 60)
{
return; return;
}
_last_err_time = now;
auto tm_time = details::os::localtime(now); auto tm_time = details::os::localtime(now);
char date_buf[100]; char date_buf[100];
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time); std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
details::log_msg err_msg; fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), 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;
} }
inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg) inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg)
@@ -356,8 +362,7 @@ inline void spdlog::logger::_incr_msg_counter(details::log_msg &msg)
msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed); msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed);
} }
inline const std::vector<spdlog::sink_ptr>& spdlog::logger::sinks() const inline const std::vector<spdlog::sink_ptr> &spdlog::logger::sinks() const
{ {
return _sinks; return _sinks;
} }

View File

@@ -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,176 +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 "../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 = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos);
if (dif == 0)
{
if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
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 = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos + 1);
if (dif == 0)
{
if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
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;
}
bool is_empty()
{
size_t front, front1, back;
// try to take a consistent snapshot of front/tail.
do
{
front = enqueue_pos_.load(std::memory_order_acquire);
back = dequeue_pos_.load(std::memory_order_acquire);
front1 = enqueue_pos_.load(std::memory_order_relaxed);
}
while (front != front1);
return back == front;
}
private:
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> #include <atomic>
// null, no cost dummy "mutex" and dummy "atomic" int // null, no cost dummy "mutex" and dummy "atomic" int
namespace spdlog namespace spdlog {
{ namespace details {
namespace details
{
struct null_mutex struct null_mutex
{ {
void lock() {} void lock() {}
@@ -27,8 +25,10 @@ struct null_atomic_int
int value; int value;
null_atomic_int() = default; 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 int load(std::memory_order) const
{ {
@@ -41,5 +41,5 @@ struct null_atomic_int
} }
}; };
} } // namespace details
} } // namespace spdlog

View File

@@ -6,30 +6,30 @@
#include "../common.h" #include "../common.h"
#include <algorithm>
#include <chrono>
#include <cstdio> #include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime> #include <ctime>
#include <functional> #include <functional>
#include <string> #include <string>
#include <chrono>
#include <thread>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <thread>
#ifdef _WIN32 #ifdef _WIN32
#ifndef NOMINMAX #ifndef NOMINMAX
#define NOMINMAX //prevent windows redefining min/max #define NOMINMAX // prevent windows redefining min/max
#endif #endif
#ifndef WIN32_LEAN_AND_MEAN #ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#endif #endif
#include <windows.h> #include <io.h> // _get_osfhandle and _isatty support
#include <process.h> // _get_pid support #include <process.h> // _get_pid support
#include <io.h> // _get_osfhandle and _isatty support #include <windows.h>
#ifdef __MINGW32__ #ifdef __MINGW32__
#include <share.h> #include <share.h>
@@ -37,8 +37,8 @@
#else // unix #else // unix
#include <unistd.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h>
#ifdef __linux__ #ifdef __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id #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 #include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
#endif #endif
#endif //unix #endif // unix
#ifndef __has_feature // Clang - feature checking macros. #ifndef __has_feature // Clang - feature checking macros.
#define __has_feature(x) 0 // Compatibility with non-clang compilers. #define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif #endif
namespace spdlog {
namespace spdlog namespace details {
{ namespace os {
namespace details
{
namespace os
{
inline spdlog::log_clock::time_point now() inline spdlog::log_clock::time_point now()
{ {
@@ -68,14 +64,11 @@ inline spdlog::log_clock::time_point now()
timespec ts; timespec ts;
::clock_gettime(CLOCK_REALTIME_COARSE, &ts); ::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
return std::chrono::time_point<log_clock, typename log_clock::duration>( return std::chrono::time_point<log_clock, typename log_clock::duration>(
std::chrono::duration_cast<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::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
#else #else
return log_clock::now(); return log_clock::now();
#endif #endif
} }
inline std::tm localtime(const std::time_t &time_tt) inline std::tm localtime(const std::time_t &time_tt)
{ {
@@ -96,7 +89,6 @@ inline std::tm localtime()
return localtime(now_t); return localtime(now_t);
} }
inline std::tm gmtime(const std::time_t &time_tt) 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); std::time_t now_t = time(nullptr);
return gmtime(now_t); 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 && 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_min == tm2.tm_min && tm1.tm_mon == tm2.tm_mon && tm1.tm_year == tm2.tm_year && tm1.tm_isdst == tm2.tm_isdst);
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); return !(tm1 == tm2);
} }
// eol definition // eol definition
#if !defined (SPDLOG_EOL) #if !defined(SPDLOG_EOL)
#ifdef _WIN32 #ifdef _WIN32
#define SPDLOG_EOL "\r\n" #define SPDLOG_EOL "\r\n"
#else #else
@@ -140,10 +127,7 @@ inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
#endif #endif
#endif #endif
SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL; SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1;
// folder separator // folder separator
#ifdef _WIN32 #ifdef _WIN32
@@ -152,23 +136,26 @@ SPDLOG_CONSTEXPR static const char folder_sep = '\\';
SPDLOG_CONSTEXPR static const char folder_sep = '/'; SPDLOG_CONSTEXPR static const char folder_sep = '/';
#endif #endif
inline void prevent_child_fd(FILE *f) inline void prevent_child_fd(FILE *f)
{ {
#ifdef _WIN32 #ifdef _WIN32
#if !defined(__cplusplus_winrt)
auto file_handle = (HANDLE)_get_osfhandle(_fileno(f)); auto file_handle = (HANDLE)_get_osfhandle(_fileno(f));
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
throw spdlog_ex("SetHandleInformation failed", errno); throw spdlog_ex("SetHandleInformation failed", errno);
#endif
#else #else
auto fd = fileno(f); auto fd = fileno(f);
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
{
throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno); throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno);
}
#endif #endif
} }
// fopen_s on non windows for writing
//fopen_s on non windows for writing inline bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode)
{ {
#ifdef _WIN32 #ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES #ifdef SPDLOG_WCHAR_FILENAMES
@@ -176,18 +163,19 @@ inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode
#else #else
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
#endif #endif
#else //unix #else // unix
*fp = fopen((filename.c_str()), mode.c_str()); *fp = fopen((filename.c_str()), mode.c_str());
#endif #endif
#ifdef SPDLOG_PREVENT_CHILD_FD #ifdef SPDLOG_PREVENT_CHILD_FD
if (*fp != nullptr) if (*fp != nullptr)
{
prevent_child_fd(*fp); prevent_child_fd(*fp);
}
#endif #endif
return *fp == nullptr; return *fp == nullptr;
} }
inline int remove(const filename_t &filename) inline int remove(const filename_t &filename)
{ {
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
@@ -197,7 +185,7 @@ inline int remove(const filename_t &filename)
#endif #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) #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
return _wrename(filename1.c_str(), filename2.c_str()); return _wrename(filename1.c_str(), filename2.c_str());
@@ -206,9 +194,8 @@ inline int rename(const filename_t& filename1, const filename_t& filename2)
#endif #endif
} }
// Return if file exists
//Return if file exists inline bool file_exists(const filename_t &filename)
inline bool file_exists(const filename_t& filename)
{ {
#ifdef _WIN32 #ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES #ifdef SPDLOG_WCHAR_FILENAMES
@@ -217,54 +204,59 @@ inline bool file_exists(const filename_t& filename)
auto attribs = GetFileAttributesA(filename.c_str()); auto attribs = GetFileAttributesA(filename.c_str());
#endif #endif
return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY)); return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
#else //common linux/unix all have the stat system call #else // common linux/unix all have the stat system call
struct stat buffer; struct stat buffer;
return (stat(filename.c_str(), &buffer) == 0); return (stat(filename.c_str(), &buffer) == 0);
#endif #endif
} }
// Return file size according to open FILE* object
//Return file size according to open FILE* object
inline size_t filesize(FILE *f) inline size_t filesize(FILE *f)
{ {
if (f == nullptr) if (f == nullptr)
{
throw spdlog_ex("Failed getting file size. fd is null"); throw spdlog_ex("Failed getting file size. fd is null");
#if defined ( _WIN32) && !defined(__CYGWIN__) }
#if defined(_WIN32) && !defined(__CYGWIN__)
int fd = _fileno(f); int fd = _fileno(f);
#if _WIN64 //64 bits #if _WIN64 // 64 bits
struct _stat64 st; struct _stat64 st;
if (_fstat64(fd, &st) == 0) if (_fstat64(fd, &st) == 0)
{
return st.st_size; return st.st_size;
}
#else //windows 32 bits #else // windows 32 bits
long ret = _filelength(fd); long ret = _filelength(fd);
if (ret >= 0) if (ret >= 0)
{
return static_cast<size_t>(ret); return static_cast<size_t>(ret);
}
#endif #endif
#else // unix #else // unix
int fd = fileno(f); int fd = fileno(f);
//64 bits(but not in osx or cygwin, where fstat64 is deprecated) // 64 bits(but not in osx or cygwin, where fstat64 is deprecated)
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__) #if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__)
struct stat64 st; struct stat64 st;
if (fstat64(fd, &st) == 0) if (fstat64(fd, &st) == 0)
{
return static_cast<size_t>(st.st_size); return static_cast<size_t>(st.st_size);
}
#else // unix 32 bits or cygwin #else // unix 32 bits or cygwin
struct stat st; struct stat st;
if (fstat(fd, &st) == 0) if (fstat(fd, &st) == 0)
{
return static_cast<size_t>(st.st_size); return static_cast<size_t>(st.st_size);
}
#endif #endif
#endif #endif
throw spdlog_ex("Failed getting file size from fd", errno); 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 #ifdef _WIN32
@@ -280,9 +272,13 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
int offset = -tzinfo.Bias; int offset = -tzinfo.Bias;
if (tm.tm_isdst) if (tm.tm_isdst)
{
offset -= tzinfo.DaylightBias; offset -= tzinfo.DaylightBias;
}
else else
{
offset -= tzinfo.StandardBias; offset -= tzinfo.StandardBias;
}
return offset; return offset;
#else #else
@@ -290,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 // 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
struct helper 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 local_year = localtm.tm_year + (1900 - 1);
int gmt_year = gmtm.tm_year + (1900 - 1); int gmt_year = gmtm.tm_year + (1900 - 1);
long int days = ( long int days = (
// difference in day of year // difference in day of year
localtm.tm_yday - gmtm.tm_yday localtm.tm_yday -
gmtm.tm_yday
// + intervening leap days // + intervening leap days
+ ((local_year >> 2) - (gmt_year >> 2)) + ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
- (local_year / 100 - gmt_year / 100) ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
+ ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
// + difference in years * 365 */ // + difference in years * 365 */
+ (long int)(local_year - gmt_year) * 365 + (long int)(local_year - gmt_year) * 365);
);
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour); long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min); long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
@@ -316,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 #else
long int offset_seconds = tm.tm_gmtoff; auto offset_seconds = tm.tm_gmtoff;
#endif #endif
return static_cast<int>(offset_seconds / 60); return static_cast<int>(offset_seconds / 60);
#endif #endif
} }
//Return current thread id as size_t // Return current thread id as size_t
//It exists because the std::this_thread::get_id() is much slower(especially under VS 2013) // It exists because the std::this_thread::get_id() is much slower(especially under VS 2013)
inline size_t _thread_id() inline size_t _thread_id()
{ {
#ifdef _WIN32 #ifdef _WIN32
return static_cast<size_t>(::GetCurrentThreadId()); return static_cast<size_t>(::GetCurrentThreadId());
#elif __linux__ #elif __linux__
# if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21) #if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
# define SYS_gettid __NR_gettid #define SYS_gettid __NR_gettid
# endif #endif
return static_cast<size_t>(syscall(SYS_gettid)); return static_cast<size_t>(syscall(SYS_gettid));
#elif __FreeBSD__ #elif __FreeBSD__
long tid; long tid;
thr_self(&tid); thr_self(&tid);
@@ -344,42 +339,51 @@ inline size_t _thread_id()
uint64_t tid; uint64_t tid;
pthread_threadid_np(nullptr, &tid); pthread_threadid_np(nullptr, &tid);
return static_cast<size_t>(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())); return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
#endif #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() inline size_t thread_id()
{ {
#if defined(SPDLOG_DISABLE_TID_CACHING) || (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(); return _thread_id();
#else // cache thread id in tls #else // cache thread id in tls
static thread_local const size_t tid = _thread_id(); static thread_local const size_t tid = _thread_id();
return tid; return tid;
#endif #endif
} }
// 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) // wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
#define SPDLOG_FILENAME_T(s) L ## s #define SPDLOG_FILENAME_T(s) L##s
inline std::string filename_to_str(const filename_t& filename) inline std::string filename_to_str(const filename_t &filename)
{ {
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c; std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c;
return c.to_bytes(filename); return c.to_bytes(filename);
} }
#else #else
#define SPDLOG_FILENAME_T(s) s #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; return filename;
} }
#endif #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); return std::string(res);
} }
@@ -390,10 +394,7 @@ inline std::string errno_to_string(char buf[256], int res)
{ {
return std::string(buf); return std::string(buf);
} }
else return "Unknown error";
{
return "Unknown error";
}
} }
// Return errno string (thread safe) // Return errno string (thread safe)
@@ -404,21 +405,29 @@ inline std::string errno_str(int err_num)
#ifdef _WIN32 #ifdef _WIN32
if (strerror_s(buf, buf_size, err_num) == 0) if (strerror_s(buf, buf_size, err_num) == 0)
{
return std::string(buf); return std::string(buf);
}
else else
{
return "Unknown error"; return "Unknown error";
}
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \ #elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \
((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version ((_POSIX_C_SOURCE >= 200112L) && !defined(_GNU_SOURCE)) // posix version
if (strerror_r(err_num, buf, buf_size) == 0) if (strerror_r(err_num, buf, buf_size) == 0)
{
return std::string(buf); return std::string(buf);
}
else else
{
return "Unknown error"; return "Unknown error";
}
#else // gnu version (might not use the given buf, so its retval pointer must be used) #else // gnu version (might not use the given buf, so its retval pointer must be used)
auto err = strerror_r(err_num, buf, buf_size); // let compiler choose type 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 #endif
} }
@@ -426,14 +435,12 @@ inline int pid()
{ {
#ifdef _WIN32 #ifdef _WIN32
return ::_getpid(); return static_cast<int>(::GetCurrentProcessId());
#else #else
return static_cast<int>(::getpid()); return static_cast<int>(::getpid());
#endif #endif
} }
// Determine if the terminal supports colors // Determine if the terminal supports colors
// Source: https://github.com/agauniyal/rang/ // Source: https://github.com/agauniyal/rang/
inline bool is_color_terminal() inline bool is_color_terminal()
@@ -441,11 +448,8 @@ inline bool is_color_terminal()
#ifdef _WIN32 #ifdef _WIN32
return true; return true;
#else #else
static constexpr const char* Terms[] = static constexpr const char *Terms[] = {
{ "ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"};
"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm",
"linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"
};
const char *env_p = std::getenv("TERM"); const char *env_p = std::getenv("TERM");
if (env_p == nullptr) if (env_p == nullptr)
@@ -453,27 +457,23 @@ inline bool is_color_terminal()
return false; return false;
} }
static const bool result = std::any_of( static const bool result =
std::begin(Terms), std::end(Terms), [&](const char* term) std::any_of(std::begin(Terms), std::end(Terms), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
{
return std::strstr(env_p, term) != nullptr;
});
return result; return result;
#endif #endif
} }
// Detrmine if the terminal attached // Detrmine if the terminal attached
// Source: https://github.com/agauniyal/rang/ // Source: https://github.com/agauniyal/rang/
inline bool in_terminal(FILE* file) inline bool in_terminal(FILE *file)
{ {
#ifdef _WIN32 #ifdef _WIN32
return _isatty(_fileno(file)) ? true : false; return _isatty(_fileno(file)) != 0;
#else #else
return isatty(fileno(file)) ? true : false; return isatty(fileno(file)) != 0;
#endif #endif
} }
} //os } // namespace os
} //details } // namespace details
} //spdlog } // namespace spdlog

View File

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

View File

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

View File

@@ -8,21 +8,20 @@
// //
// Global registry functions // Global registry functions
// //
#include "../spdlog.h"
#include "../details/registry.h" #include "../details/registry.h"
#include "../sinks/file_sinks.h" #include "../sinks/file_sinks.h"
#include "../sinks/stdout_sinks.h" #include "../sinks/stdout_sinks.h"
#include "../spdlog.h"
#ifdef SPDLOG_ENABLE_SYSLOG #ifdef SPDLOG_ENABLE_SYSLOG
#include "../sinks/syslog_sink.h" #include "../sinks/syslog_sink.h"
#endif #endif
#ifdef _WIN32 #if defined _WIN32 && !defined(__cplusplus_winrt)
#include "../sinks/wincolor_sink.h" #include "../sinks/wincolor_sink.h"
#else #else
#include "../sinks/ansicolor_sink.h" #include "../sinks/ansicolor_sink.h"
#endif #endif
#ifdef __ANDROID__ #ifdef __ANDROID__
#include "../sinks/android_sink.h" #include "../sinks/android_sink.h"
#endif #endif
@@ -34,10 +33,10 @@
inline void spdlog::register_logger(std::shared_ptr<logger> logger) 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); 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 // 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); 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); return create<spdlog::sinks::simple_file_sink_st>(logger_name, filename, truncate);
} }
// Create multi/single threaded rotating file logger // Create multi/single threaded rotating file logger
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files) inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
{ {
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files); return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
} }
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files) inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
{ {
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files); return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
} }
// Create file logger which creates new file at midnight): // Create file logger which creates new file at midnight):
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute) inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(
const std::string &logger_name, const filename_t &filename, int hour, int minute)
{ {
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, hour, minute); return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, hour, minute);
} }
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour, int minute) inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(
const std::string &logger_name, const filename_t &filename, int hour, int minute)
{ {
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, hour, minute); return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, hour, minute);
} }
// //
// stdout/stderr loggers // 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()); 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()); 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()); 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()); 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 // stdout/stderr color loggers
// //
#ifdef _WIN32 #if defined _WIN32 && !defined(__cplusplus_winrt)
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::wincolor_stdout_sink_mt>(); auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink); return spdlog::details::registry::instance().create(logger_name, sink);
} }
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name) inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string &logger_name)
{ {
auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_st>(); auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink); return spdlog::details::registry::instance().create(logger_name, sink);
} }
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name) inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string &logger_name)
{ {
auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_mt>(); auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink); return spdlog::details::registry::instance().create(logger_name, sink);
} }
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string &logger_name)
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
{ {
auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_st>(); auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink); 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>(); auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink); return spdlog::details::registry::instance().create(logger_name, sink);
} }
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name) inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string &logger_name)
{ {
auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_st>(); auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink); return spdlog::details::registry::instance().create(logger_name, sink);
} }
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name) inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string &logger_name)
{ {
auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_mt>(); auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink); return spdlog::details::registry::instance().create(logger_name, sink);
} }
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name) inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string &logger_name)
{ {
auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_st>(); auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink); return spdlog::details::registry::instance().create(logger_name, sink);
@@ -162,71 +164,78 @@ inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string
#ifdef SPDLOG_ENABLE_SYSLOG #ifdef SPDLOG_ENABLE_SYSLOG
// Create syslog logger // Create syslog logger
inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option, int syslog_facility) inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(
const std::string &logger_name, const std::string &syslog_ident, int syslog_option, int syslog_facility)
{ {
return create<spdlog::sinks::syslog_sink>(logger_name, syslog_ident, syslog_option, syslog_facility); return create<spdlog::sinks::syslog_sink>(logger_name, syslog_ident, syslog_option, syslog_facility);
} }
#endif #endif
#ifdef __ANDROID__ #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); return create<spdlog::sinks::android_sink>(logger_name, tag);
} }
#endif #endif
// Create and register a logger a single sink // 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); return details::registry::instance().create(logger_name, sink);
} }
//Create logger with multiple sinks // Create logger with multiple sinks
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string &logger_name, spdlog::sinks_init_list 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); return details::registry::instance().create(logger_name, sinks);
} }
template<typename Sink, typename... Args>
template <typename Sink, typename... Args> inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string &logger_name, Args... args)
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, Args... args)
{ {
sink_ptr sink = std::make_shared<Sink>(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> 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); return details::registry::instance().create(logger_name, sinks_begin, sinks_end);
} }
// Create and register an async logger with a single sink // 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 // 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> 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) 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); 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); 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); 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) 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() inline void spdlog::drop_all()

View File

@@ -72,9 +72,11 @@
// Dummy implementations of strerror_r and strerror_s called if corresponding // Dummy implementations of strerror_r and strerror_s called if corresponding
// system functions are not available. // system functions are not available.
FMT_MAYBE_UNUSED
static inline fmt::internal::Null<> strerror_r(int, char *, ...) { static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
return fmt::internal::Null<>(); return fmt::internal::Null<>();
} }
FMT_MAYBE_UNUSED
static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) { static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
return fmt::internal::Null<>(); return fmt::internal::Null<>();
} }
@@ -121,7 +123,7 @@ typedef void (*FormatFunc)(Writer &, int, StringRef);
// Buffer should be at least of size 1. // Buffer should be at least of size 1.
int safe_strerror( int safe_strerror(
int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT { int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer"); FMT_ASSERT(buffer != FMT_NULL && buffer_size != 0, "invalid buffer");
class StrError { class StrError {
private: private:
@@ -159,6 +161,11 @@ int safe_strerror(
ERANGE : result; 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. // Fallback to strerror if strerror_r and strerror_s are not available.
int fallback(internal::Null<>) { int fallback(internal::Null<>) {
errno = 0; errno = 0;
@@ -166,13 +173,15 @@ int safe_strerror(
return errno; return errno;
} }
#ifdef __c2__
# pragma clang diagnostic pop
#endif
public: public:
StrError(int err_code, char *&buf, std::size_t buf_size) StrError(int err_code, char *&buf, std::size_t buf_size)
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {} : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
int run() { int run() {
// Suppress a warning about unused strerror_r.
strerror_r(0, FMT_NULL, "");
return handle(strerror_r(error_code_, buffer_, buffer_size_)); 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. 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> template <typename Char>
void internal::FixedBuffer<Char>::grow(std::size_t) { void internal::FixedBuffer<Char>::grow(std::size_t) {
FMT_THROW(std::runtime_error("buffer overflow")); FMT_THROW(std::runtime_error("buffer overflow"));
@@ -502,8 +466,6 @@ template struct internal::BasicData<void>;
template void internal::FixedBuffer<char>::grow(std::size_t); template void internal::FixedBuffer<char>::grow(std::size_t);
template void internal::ArgMap<char>::init(const ArgList &args);
template FMT_API int internal::CharTraits<char>::format_float( template FMT_API int internal::CharTraits<char>::format_float(
char *buffer, std::size_t size, const char *format, char *buffer, std::size_t size, const char *format,
unsigned width, int precision, double value); unsigned width, int precision, double value);
@@ -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::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( template FMT_API int internal::CharTraits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format, wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, double value); unsigned width, int precision, double value);

File diff suppressed because it is too large Load Diff

View File

@@ -13,13 +13,11 @@
#include "format.h" #include "format.h"
#include <ostream> #include <ostream>
namespace fmt namespace fmt {
{
namespace internal namespace internal {
{
template <class Char> template<class Char>
class FormatBuf : public std::basic_streambuf<Char> class FormatBuf : public std::basic_streambuf<Char>
{ {
private: private:
@@ -29,7 +27,10 @@ private:
Buffer<Char> &buffer_; Buffer<Char> &buffer_;
public: public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer) {} FormatBuf(Buffer<Char> &buffer)
: buffer_(buffer)
{
}
protected: protected:
// The put-area is actually always empty. This makes the implementation // The put-area is actually always empty. This makes the implementation
@@ -57,9 +58,11 @@ Yes &convert(std::ostream &);
struct DummyStream : std::ostream struct DummyStream : std::ostream
{ {
DummyStream(); // Suppress a bogus warning in MSVC. DummyStream(); // Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream. // Hide all operator<< overloads from std::ostream.
void operator<<(Null<>); template<typename T>
typename EnableIf<sizeof(T) == 0>::type operator<<(const T &);
}; };
No &operator<<(std::ostream &, int); No &operator<<(std::ostream &, int);
@@ -76,21 +79,21 @@ struct ConvertToIntImpl<T, true>
// Write the content of w to os. // Write the content of w to os.
FMT_API void write(std::ostream &os, Writer &w); FMT_API void write(std::ostream &os, Writer &w);
} // namespace internal } // namespace internal
// Formats a value. // Formats a value.
template <typename Char, typename ArgFormatter_, typename T> template<typename Char, typename ArgFormatter_, typename T>
void format_arg(BasicFormatter<Char, ArgFormatter_> &f, void format_arg(BasicFormatter<Char, ArgFormatter_> &f, const Char *&format_str, const T &value)
const Char *&format_str, const T &value)
{ {
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer; internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
internal::FormatBuf<Char> format_buf(buffer); internal::FormatBuf<Char> format_buf(buffer);
std::basic_ostream<Char> output(&format_buf); std::basic_ostream<Char> output(&format_buf);
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output << value; output << value;
BasicStringRef<Char> str(&buffer[0], buffer.size()); BasicStringRef<Char> str(&buffer[0], buffer.size());
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg; typedef internal::MakeArg<BasicFormatter<Char>> MakeArg;
format_str = f.format(format_str, MakeArg(str)); format_str = f.format(format_str, MakeArg(str));
} }
@@ -105,10 +108,10 @@ void format_arg(BasicFormatter<Char, ArgFormatter_> &f,
*/ */
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args); FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(void, print, std::ostream &, CStringRef) FMT_VARIADIC(void, print, std::ostream &, CStringRef)
} // namespace fmt } // namespace fmt
#ifdef FMT_HEADER_ONLY #ifdef FMT_HEADER_ONLY
# include "ostream.cc" #include "ostream.cc"
#endif #endif
#endif // FMT_OSTREAM_H_ #endif // FMT_OSTREAM_H_

View File

@@ -12,60 +12,60 @@
#if defined(__MINGW32__) || defined(__CYGWIN__) #if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/. // Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__ #undef __STRICT_ANSI__
#endif #endif
#include <errno.h> #include <errno.h>
#include <fcntl.h> // for O_RDONLY #include <fcntl.h> // for O_RDONLY
#include <locale.h> // for locale_t #include <locale.h> // for locale_t
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> // for strtod_l #include <stdlib.h> // for strtod_l
#include <cstddef> #include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__) #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 #endif
#include "format.h" #include "format.h"
#ifndef FMT_POSIX #ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__) #if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols. // Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call #define FMT_POSIX(call) _##call
# else #else
# define FMT_POSIX(call) call #define FMT_POSIX(call) call
# endif #endif
#endif #endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability. // Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM #ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call) #define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else #else
# define FMT_SYSTEM(call) call #define FMT_SYSTEM(call) call
# ifdef _WIN32 #ifdef _WIN32
// Fix warnings about deprecated symbols. // Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call #define FMT_POSIX_CALL(call) ::_##call
# else #else
# define FMT_POSIX_CALL(call) ::call #define FMT_POSIX_CALL(call) ::call
# endif #endif
#endif #endif
// Retries the expression while it evaluates to error_result and errno // Retries the expression while it evaluates to error_result and errno
// equals to EINTR. // equals to EINTR.
#ifndef _WIN32 #ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \ #define FMT_RETRY_VAL(result, expression, error_result) \
do { \ do \
result = (expression); \ { \
} while (result == error_result && errno == EINTR) result = (expression); \
} while (result == error_result && errno == EINTR)
#else #else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression) #define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif #endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
namespace fmt namespace fmt {
{
// An error code. // An error code.
class ErrorCode class ErrorCode
@@ -74,8 +74,7 @@ private:
int value_; int value_;
public: public:
explicit ErrorCode(int value = 0) FMT_NOEXCEPT : explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {}
value_(value) {}
int get() const FMT_NOEXCEPT int get() const FMT_NOEXCEPT
{ {
@@ -91,12 +90,14 @@ private:
friend class File; friend class File;
explicit BufferedFile(FILE *f) : file_(f) {} explicit BufferedFile(FILE *f)
: file_(f)
{
}
public: public:
// Constructs a BufferedFile object which doesn't represent any file. // Constructs a BufferedFile object which doesn't represent any file.
BufferedFile() FMT_NOEXCEPT : BufferedFile() FMT_NOEXCEPT : file_(FMT_NULL) {}
file_(FMT_NULL) {}
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~BufferedFile() FMT_NOEXCEPT; FMT_API ~BufferedFile() FMT_NOEXCEPT;
@@ -115,12 +116,10 @@ private:
public: public:
// A "move constructor" for moving from a temporary. // A "move constructor" for moving from a temporary.
BufferedFile(Proxy p) FMT_NOEXCEPT : BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {}
file_(p.file) {}
// A "move constructor" for moving from an lvalue. // A "move constructor" for moving from an lvalue.
BufferedFile(BufferedFile &f) FMT_NOEXCEPT : BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_)
file_(f.file_)
{ {
f.file_ = FMT_NULL; f.file_ = FMT_NULL;
} }
@@ -156,13 +155,12 @@ private:
FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile); FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
public: public:
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_)
file_(other.file_)
{ {
other.file_ = FMT_NULL; other.file_ = FMT_NULL;
} }
BufferedFile& operator=(BufferedFile &&other) BufferedFile &operator=(BufferedFile &&other)
{ {
close(); close();
file_ = other.file_; file_ = other.file_;
@@ -185,7 +183,7 @@ BufferedFile(BufferedFile &&other) FMT_NOEXCEPT :
// We place parentheses around fileno to workaround a bug in some versions // We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro. // of MinGW that define fileno as a macro.
FMT_API int (fileno)() const; FMT_API int(fileno)() const;
void print(CStringRef format_str, const ArgList &args) void print(CStringRef format_str, const ArgList &args)
{ {
@@ -203,10 +201,13 @@ BufferedFile(BufferedFile &&other) FMT_NOEXCEPT :
class File class File
{ {
private: private:
int fd_; // File descriptor. int fd_; // File descriptor.
// Constructs a File object with a given descriptor. // Constructs a File object with a given descriptor.
explicit File(int fd) : fd_(fd) {} explicit File(int fd)
: fd_(fd)
{
}
public: public:
// Possible values for the oflag argument to the constructor. // Possible values for the oflag argument to the constructor.
@@ -214,12 +215,11 @@ public:
{ {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing. RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
}; };
// Constructs a File object which doesn't represent any file. // Constructs a File object which doesn't represent any file.
File() FMT_NOEXCEPT : File() FMT_NOEXCEPT : fd_(-1) {}
fd_(-1) {}
// Opens a file and constructs a File object representing this file. // Opens a file and constructs a File object representing this file.
FMT_API File(CStringRef path, int oflag); FMT_API File(CStringRef path, int oflag);
@@ -238,12 +238,10 @@ private:
public: public:
// A "move constructor" for moving from a temporary. // A "move constructor" for moving from a temporary.
File(Proxy p) FMT_NOEXCEPT : File(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {}
fd_(p.fd) {}
// A "move constructor" for moving from an lvalue. // A "move constructor" for moving from an lvalue.
File(File &other) FMT_NOEXCEPT : File(File &other) FMT_NOEXCEPT : fd_(other.fd_)
fd_(other.fd_)
{ {
other.fd_ = -1; other.fd_ = -1;
} }
@@ -279,13 +277,12 @@ private:
FMT_DISALLOW_COPY_AND_ASSIGN(File); FMT_DISALLOW_COPY_AND_ASSIGN(File);
public: public:
File(File &&other) FMT_NOEXCEPT : File(File &&other) FMT_NOEXCEPT : fd_(other.fd_)
fd_(other.fd_)
{ {
other.fd_ = -1; other.fd_ = -1;
} }
File& operator=(File &&other) File &operator=(File &&other)
{ {
close(); close();
fd_ = other.fd_; fd_ = other.fd_;
@@ -340,9 +337,8 @@ File(File &&other) FMT_NOEXCEPT :
// Returns the memory page size. // Returns the memory page size.
long getpagesize(); long getpagesize();
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \ #if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && !defined(__ANDROID__) && !defined(__CYGWIN__)
!defined(__ANDROID__) && !defined(__CYGWIN__) #define FMT_LOCALE
# define FMT_LOCALE
#endif #endif
#ifdef FMT_LOCALE #ifdef FMT_LOCALE
@@ -350,10 +346,13 @@ long getpagesize();
class Locale class Locale
{ {
private: private:
# ifdef _MSC_VER #ifdef _MSC_VER
typedef _locale_t locale_t; 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) static locale_t newlocale(int category_mask, const char *locale, locale_t)
{ {
@@ -369,7 +368,7 @@ private:
{ {
return _strtod_l(nptr, endptr, locale); return _strtod_l(nptr, endptr, locale);
} }
# endif #endif
locale_t locale_; locale_t locale_;
@@ -378,7 +377,8 @@ private:
public: public:
typedef locale_t Type; typedef locale_t Type;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) Locale()
: locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL))
{ {
if (!locale_) if (!locale_)
FMT_THROW(fmt::SystemError(errno, "cannot create locale")); FMT_THROW(fmt::SystemError(errno, "cannot create locale"));
@@ -403,12 +403,11 @@ public:
return result; return result;
} }
}; };
#endif // FMT_LOCALE #endif // FMT_LOCALE
} // namespace fmt } // namespace fmt
#if !FMT_USE_RVALUE_REFERENCES #if !FMT_USE_RVALUE_REFERENCES
namespace std namespace std {
{
// For compatibility with C++98. // For compatibility with C++98.
inline fmt::BufferedFile &move(fmt::BufferedFile &f) inline fmt::BufferedFile &move(fmt::BufferedFile &f)
{ {
@@ -418,7 +417,7 @@ inline fmt::File &move(fmt::File &f)
{ {
return f; return f;
} }
} } // namespace std
#endif #endif
#endif // FMT_POSIX_H_ #endif // FMT_POSIX_H_

View File

@@ -10,22 +10,20 @@
#ifndef FMT_PRINTF_H_ #ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_ #define FMT_PRINTF_H_
#include <algorithm> // std::fill_n #include <algorithm> // std::fill_n
#include <limits> // std::numeric_limits #include <limits> // std::numeric_limits
#include "ostream.h" #include "ostream.h"
namespace fmt namespace fmt {
{ namespace internal {
namespace internal
{
// Checks if a value fits in int - used to avoid warnings about comparing // Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers. // signed and unsigned integers.
template <bool IsSigned> template<bool IsSigned>
struct IntChecker struct IntChecker
{ {
template <typename T> template<typename T>
static bool fits_in_int(T value) static bool fits_in_int(T value)
{ {
unsigned max = std::numeric_limits<int>::max(); unsigned max = std::numeric_limits<int>::max();
@@ -37,14 +35,13 @@ struct IntChecker
} }
}; };
template <> template<>
struct IntChecker<true> struct IntChecker<true>
{ {
template <typename T> template<typename T>
static bool fits_in_int(T value) static bool fits_in_int(T value)
{ {
return value >= std::numeric_limits<int>::min() && return value >= std::numeric_limits<int>::min() && value <= std::numeric_limits<int>::max();
value <= std::numeric_limits<int>::max();
} }
static bool fits_in_int(int) static bool fits_in_int(int)
{ {
@@ -60,7 +57,7 @@ public:
FMT_THROW(FormatError("precision is not integer")); FMT_THROW(FormatError("precision is not integer"));
} }
template <typename T> template<typename T>
int visit_any_int(T value) int visit_any_int(T value)
{ {
if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value)) if (!IntChecker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
@@ -73,7 +70,7 @@ public:
class IsZeroInt : public ArgVisitor<IsZeroInt, bool> class IsZeroInt : public ArgVisitor<IsZeroInt, bool>
{ {
public: public:
template <typename T> template<typename T>
bool visit_any_int(T value) bool visit_any_int(T value)
{ {
return value == 0; return value == 0;
@@ -99,13 +96,13 @@ public:
return 'p'; return 'p';
} }
template <typename T> template<typename T>
char visit_any_int(T) char visit_any_int(T)
{ {
return 'd'; return 'd';
} }
template <typename T> template<typename T>
char visit_any_double(T) char visit_any_double(T)
{ {
return 'g'; return 'g';
@@ -117,23 +114,29 @@ public:
} }
}; };
template <typename T, typename U> template<typename T, typename U>
struct is_same struct is_same
{ {
enum { value = 0 }; enum
{
value = 0
};
}; };
template <typename T> template<typename T>
struct is_same<T, T> struct is_same<T, T>
{ {
enum { value = 1 }; enum
{
value = 1
};
}; };
// An argument visitor that converts an integer argument to T for printf, // An argument visitor that converts an integer argument to T for printf,
// if T is an integral type. If T is void, the argument is converted to // 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: // corresponding signed or unsigned type depending on the type specifier:
// 'd' and 'i' - signed, other - unsigned) // 'd' and 'i' - signed, other - unsigned)
template <typename T = void> template<typename T = void>
class ArgConverter : public ArgVisitor<ArgConverter<T>, void> class ArgConverter : public ArgVisitor<ArgConverter<T>, void>
{ {
private: private:
@@ -144,7 +147,10 @@ private:
public: public:
ArgConverter(internal::Arg &arg, wchar_t type) ArgConverter(internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type) {} : arg_(arg)
, type_(type)
{
}
void visit_bool(bool value) void visit_bool(bool value)
{ {
@@ -152,13 +158,13 @@ public:
visit_any_int(value); visit_any_int(value);
} }
void visit_char(char value) void visit_char(int value)
{ {
if (type_ != 's') if (type_ != 's')
visit_any_int(value); visit_any_int(value);
} }
template <typename U> template<typename U>
void visit_any_int(U value) void visit_any_int(U value)
{ {
bool is_signed = type_ == 'd' || type_ == 'i'; bool is_signed = type_ == 'd' || type_ == 'i';
@@ -168,9 +174,8 @@ public:
} }
using internal::Arg; using internal::Arg;
typedef typename internal::Conditional< typedef typename internal::Conditional<is_same<T, void>::value, U, T>::type TargetType;
is_same<T, void>::value, U, T>::type TargetType; if (const_check(sizeof(TargetType) <= sizeof(int)))
if (sizeof(TargetType) <= sizeof(int))
{ {
// Extra casts are used to silence warnings. // Extra casts are used to silence warnings.
if (is_signed) if (is_signed)
@@ -198,8 +203,7 @@ public:
else else
{ {
arg_.type = Arg::ULONG_LONG; arg_.type = Arg::ULONG_LONG;
arg_.ulong_long_value = arg_.ulong_long_value = static_cast<typename internal::MakeUnsigned<U>::Type>(value);
static_cast<typename internal::MakeUnsigned<U>::Type>(value);
} }
} }
} }
@@ -214,9 +218,12 @@ private:
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter); FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
public: public:
explicit CharConverter(internal::Arg &arg) : arg_(arg) {} explicit CharConverter(internal::Arg &arg)
: arg_(arg)
{
}
template <typename T> template<typename T>
void visit_any_int(T value) void visit_any_int(T value)
{ {
arg_.type = internal::Arg::CHAR; arg_.type = internal::Arg::CHAR;
@@ -234,14 +241,17 @@ private:
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler); FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
public: public:
explicit WidthHandler(FormatSpec &spec) : spec_(spec) {} explicit WidthHandler(FormatSpec &spec)
: spec_(spec)
{
}
void report_unhandled_arg() void report_unhandled_arg()
{ {
FMT_THROW(FormatError("width is not integer")); FMT_THROW(FormatError("width is not integer"));
} }
template <typename T> template<typename T>
unsigned visit_any_int(T value) unsigned visit_any_int(T value)
{ {
typedef typename internal::IntTraits<T>::MainType UnsignedType; typedef typename internal::IntTraits<T>::MainType UnsignedType;
@@ -257,7 +267,7 @@ public:
return static_cast<unsigned>(width); return static_cast<unsigned>(width);
} }
}; };
} // namespace internal } // namespace internal
/** /**
\rst \rst
@@ -276,9 +286,8 @@ public:
superclass will be called. superclass will be called.
\endrst \endrst
*/ */
template <typename Impl, typename Char, typename Spec> template<typename Impl, typename Char, typename Spec>
class BasicPrintfArgFormatter : class BasicPrintfArgFormatter : public internal::ArgFormatterBase<Impl, Char, Spec>
public internal::ArgFormatterBase<Impl, Char, Spec>
{ {
private: private:
void write_null_pointer() void write_null_pointer()
@@ -298,7 +307,9 @@ public:
\endrst \endrst
*/ */
BasicPrintfArgFormatter(BasicWriter<Char> &w, Spec &s) BasicPrintfArgFormatter(BasicWriter<Char> &w, Spec &s)
: internal::ArgFormatterBase<Impl, Char, Spec>(w, s) {} : internal::ArgFormatterBase<Impl, Char, Spec>(w, s)
{
}
/** Formats an argument of type ``bool``. */ /** Formats an argument of type ``bool``. */
void visit_bool(bool value) void visit_bool(bool value)
@@ -371,18 +382,19 @@ public:
}; };
/** The default printf argument formatter. */ /** The default printf argument formatter. */
template <typename Char> template<typename Char>
class PrintfArgFormatter : class PrintfArgFormatter : public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>
public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>
{ {
public: public:
/** Constructs an argument formatter object. */ /** Constructs an argument formatter object. */
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s) PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>(w, s) {} : BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>(w, s)
{
}
}; };
/** This template formats data and writes the output to a writer. */ /** This template formats data and writes the output to a writer. */
template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> > template<typename Char, typename ArgFormatter = PrintfArgFormatter<Char>>
class PrintfFormatter : private internal::FormatterBase class PrintfFormatter : private internal::FormatterBase
{ {
private: private:
@@ -392,9 +404,7 @@ private:
// Returns the argument with specified index or, if arg_index is equal // Returns the argument with specified index or, if arg_index is equal
// to the maximum unsigned value, the next argument. // to the maximum unsigned value, the next argument.
internal::Arg get_arg( internal::Arg get_arg(const Char *s, unsigned arg_index = (std::numeric_limits<unsigned>::max)());
const Char *s,
unsigned arg_index = (std::numeric_limits<unsigned>::max)());
// Parses argument index, flags and width and returns the argument index. // Parses argument index, flags and width and returns the argument index.
unsigned parse_header(const Char *&s, FormatSpec &spec); unsigned parse_header(const Char *&s, FormatSpec &spec);
@@ -408,13 +418,16 @@ public:
\endrst \endrst
*/ */
explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w) explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w)
: FormatterBase(al), writer_(w) {} : FormatterBase(al)
, writer_(w)
{
}
/** Formats stored arguments and writes the output to the writer. */ /** Formats stored arguments and writes the output to the writer. */
void format(BasicCStringRef<Char> format_str); void format(BasicCStringRef<Char> format_str);
}; };
template <typename Char, typename AF> template<typename Char, typename AF>
void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s) void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s)
{ {
for (;;) for (;;)
@@ -443,22 +456,19 @@ void PrintfFormatter<Char, AF>::parse_flags(FormatSpec &spec, const Char *&s)
} }
} }
template <typename Char, typename AF> template<typename Char, typename AF>
internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s, internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s, unsigned arg_index)
unsigned arg_index)
{ {
(void)s; (void)s;
const char *error = FMT_NULL; const char *error = FMT_NULL;
internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ? internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ? next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
if (error) if (error)
FMT_THROW(FormatError(!*s ? "invalid format string" : error)); FMT_THROW(FormatError(!*s ? "invalid format string" : error));
return arg; return arg;
} }
template <typename Char, typename AF> template<typename Char, typename AF>
unsigned PrintfFormatter<Char, AF>::parse_header( unsigned PrintfFormatter<Char, AF>::parse_header(const Char *&s, FormatSpec &spec)
const Char *&s, FormatSpec &spec)
{ {
unsigned arg_index = std::numeric_limits<unsigned>::max(); unsigned arg_index = std::numeric_limits<unsigned>::max();
Char c = *s; Char c = *s;
@@ -467,7 +477,7 @@ unsigned PrintfFormatter<Char, AF>::parse_header(
// Parse an argument index (if followed by '$') or a width possibly // Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s). // preceded with '0' flag(s).
unsigned value = internal::parse_nonnegative_int(s); unsigned value = internal::parse_nonnegative_int(s);
if (*s == '$') // value is an argument index if (*s == '$') // value is an argument index
{ {
++s; ++s;
arg_index = value; arg_index = value;
@@ -499,7 +509,7 @@ unsigned PrintfFormatter<Char, AF>::parse_header(
return arg_index; return arg_index;
} }
template <typename Char, typename AF> template<typename Char, typename AF>
void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str) void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
{ {
const Char *start = format_str.c_str(); const Char *start = format_str.c_str();
@@ -507,7 +517,8 @@ void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
while (*s) while (*s)
{ {
Char c = *s++; Char c = *s++;
if (c != '%') continue; if (c != '%')
continue;
if (*s == c) if (*s == c)
{ {
write(writer_, start, s); write(writer_, start, s);
@@ -550,7 +561,7 @@ void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
if (arg.type <= Arg::LAST_NUMERIC_TYPE) if (arg.type <= Arg::LAST_NUMERIC_TYPE)
spec.align_ = ALIGN_NUMERIC; spec.align_ = ALIGN_NUMERIC;
else else
spec.fill_ = ' '; // Ignore '0' flag for non-numeric types. spec.fill_ = ' '; // Ignore '0' flag for non-numeric types.
} }
// Parse length and convert the argument to the required type. // Parse length and convert the argument to the required type.
@@ -703,10 +714,10 @@ inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args)
return static_cast<int>(w.size()); return static_cast<int>(w.size());
} }
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef) FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt } // namespace fmt
#ifdef FMT_HEADER_ONLY #ifdef FMT_HEADER_ONLY
# include "printf.cc" #include "printf.cc"
#endif #endif
#endif // FMT_PRINTF_H_ #endif // FMT_PRINTF_H_

View File

@@ -14,16 +14,14 @@
#include <ctime> #include <ctime>
#ifdef _MSC_VER #ifdef _MSC_VER
# pragma warning(push) #pragma warning(push)
# pragma warning(disable: 4702) // unreachable code #pragma warning(disable : 4702) // unreachable code
# pragma warning(disable: 4996) // "deprecated" functions #pragma warning(disable : 4996) // "deprecated" functions
#endif #endif
namespace fmt namespace fmt {
{ template<typename ArgFormatter>
template <typename ArgFormatter> void format_arg(BasicFormatter<char, ArgFormatter> &f, const char *&format_str, const std::tm &tm)
void format_arg(BasicFormatter<char, ArgFormatter> &f,
const char *&format_str, const std::tm &tm)
{ {
if (*format_str == ':') if (*format_str == ':')
++format_str; ++format_str;
@@ -60,8 +58,7 @@ void format_arg(BasicFormatter<char, ArgFormatter> &f,
format_str = end + 1; format_str = end + 1;
} }
namespace internal namespace internal {
{
inline Null<> localtime_r(...) inline Null<> localtime_r(...)
{ {
return Null<>(); return Null<>();
@@ -78,7 +75,7 @@ inline Null<> gmtime_s(...)
{ {
return Null<>(); return Null<>();
} }
} } // namespace internal
// Thread-safe replacement for std::localtime // Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) inline std::tm localtime(std::time_t time)
@@ -88,7 +85,10 @@ inline std::tm localtime(std::time_t time)
std::time_t time_; std::time_t time_;
std::tm tm_; std::tm tm_;
LocalTime(std::time_t t): time_(t) {} LocalTime(std::time_t t)
: time_(t)
{
}
bool run() bool run()
{ {
@@ -116,7 +116,8 @@ inline std::tm localtime(std::time_t time)
{ {
using namespace fmt::internal; using namespace fmt::internal;
std::tm *tm = std::localtime(&time_); std::tm *tm = std::localtime(&time_);
if (tm) tm_ = *tm; if (tm)
tm_ = *tm;
return tm != FMT_NULL; return tm != FMT_NULL;
} }
}; };
@@ -136,7 +137,10 @@ inline std::tm gmtime(std::time_t time)
std::time_t time_; std::time_t time_;
std::tm tm_; std::tm tm_;
GMTime(std::time_t t): time_(t) {} GMTime(std::time_t t)
: time_(t)
{
}
bool run() bool run()
{ {
@@ -163,7 +167,8 @@ inline std::tm gmtime(std::time_t time)
bool fallback(internal::Null<>) bool fallback(internal::Null<>)
{ {
std::tm *tm = std::gmtime(&time_); std::tm *tm = std::gmtime(&time_);
if (tm != FMT_NULL) tm_ = *tm; if (tm != FMT_NULL)
tm_ = *tm;
return tm != FMT_NULL; return tm != FMT_NULL;
} }
}; };
@@ -174,10 +179,10 @@ inline std::tm gmtime(std::time_t time)
FMT_THROW(fmt::FormatError("time_t value out of range")); FMT_THROW(fmt::FormatError("time_t value out of range"));
return std::tm(); return std::tm();
} }
} //namespace fmt } // namespace fmt
#ifdef _MSC_VER #ifdef _MSC_VER
# pragma warning(pop) #pragma warning(pop)
#endif #endif
#endif // FMT_TIME_H_ #endif // FMT_TIME_H_

View File

@@ -23,7 +23,7 @@
#include "bundled/printf.h" #include "bundled/printf.h"
#endif #endif
#else //external fmtlib #else // external fmtlib
#include <fmt/format.h> #include <fmt/format.h>
#if defined(SPDLOG_FMT_PRINTF) #if defined(SPDLOG_FMT_PRINTF)
@@ -31,4 +31,3 @@
#endif #endif
#endif #endif

View File

@@ -4,14 +4,15 @@
// //
#pragma once #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) #if !defined(SPDLOG_FMT_EXTERNAL)
#include "fmt.h" #ifndef FMT_HEADER_ONLY
#define FMT_HEADER_ONLY
#endif
#include "bundled/ostream.h" #include "bundled/ostream.h"
#include "fmt.h"
#else #else
#include <fmt/ostream.h> #include <fmt/ostream.h>
#endif #endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,33 +11,33 @@
#include "sink.h" #include "sink.h"
#include <algorithm> #include <algorithm>
#include <mutex>
#include <memory> #include <memory>
#include <mutex>
#include <vector> #include <vector>
// Distribution sink (mux). Stores a vector of sinks which get called when log is called // Distribution sink (mux). Stores a vector of sinks which get called when log is called
namespace spdlog namespace spdlog {
{ namespace sinks {
namespace sinks
{
template<class Mutex> template<class Mutex>
class dist_sink: public base_sink<Mutex> class dist_sink : public base_sink<Mutex>
{ {
public: public:
explicit dist_sink() :_sinks() {} explicit dist_sink()
dist_sink(const dist_sink&) = delete; : _sinks()
dist_sink& operator=(const dist_sink&) = delete; {
virtual ~dist_sink() = default; }
dist_sink(const dist_sink &) = delete;
dist_sink &operator=(const dist_sink &) = delete;
protected: protected:
std::vector<std::shared_ptr<sink>> _sinks; 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) for (auto &sink : _sinks)
{ {
if( sink->should_log( msg.level)) if (sink->should_log(msg.level))
{ {
sink->log(msg); sink->log(msg);
} }
@@ -51,8 +51,6 @@ protected:
} }
public: public:
void add_sink(std::shared_ptr<sink> sink) void add_sink(std::shared_ptr<sink> sink)
{ {
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex); std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
@@ -66,7 +64,8 @@ public:
} }
}; };
typedef dist_sink<std::mutex> dist_sink_mt; using dist_sink_mt = dist_sink<std::mutex>;
typedef dist_sink<details::null_mutex> dist_sink_st; using dist_sink_st = dist_sink<details::null_mutex>;
}
} } // namespace sinks
} // namespace spdlog

View File

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

View File

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

View File

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

@@ -8,24 +8,25 @@
#include "../details/null_mutex.h" #include "../details/null_mutex.h"
#include "base_sink.h" #include "base_sink.h"
#include <ostream>
#include <mutex> #include <mutex>
#include <ostream>
namespace spdlog namespace spdlog {
{ namespace sinks {
namespace sinks
{
template<class Mutex> template<class Mutex>
class ostream_sink: public base_sink<Mutex> class ostream_sink : public base_sink<Mutex>
{ {
public: public:
explicit ostream_sink(std::ostream& os, bool force_flush=false) :_ostream(os), _force_flush(force_flush) {} explicit ostream_sink(std::ostream &os, bool force_flush = false)
ostream_sink(const ostream_sink&) = delete; : _ostream(os)
ostream_sink& operator=(const ostream_sink&) = delete; , _force_flush(force_flush)
virtual ~ostream_sink() = default; {
}
ostream_sink(const ostream_sink &) = delete;
ostream_sink &operator=(const ostream_sink &) = delete;
protected: 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()); _ostream.write(msg.formatted.data(), msg.formatted.size());
if (_force_flush) if (_force_flush)
@@ -37,11 +38,12 @@ protected:
_ostream.flush(); _ostream.flush();
} }
std::ostream& _ostream; std::ostream &_ostream;
bool _force_flush; bool _force_flush;
}; };
typedef ostream_sink<std::mutex> ostream_sink_mt; using ostream_sink_mt = ostream_sink<std::mutex>;
typedef ostream_sink<details::null_mutex> ostream_sink_st; 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) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#pragma once #pragma once
#include "../details/log_msg.h" #include "../details/log_msg.h"
namespace spdlog namespace spdlog {
{ namespace sinks {
namespace sinks
{
class sink class sink
{ {
public: public:
sink() virtual ~sink() = default;
{
_level = level::trace;
}
virtual ~sink() {} virtual void log(const details::log_msg &msg) = 0;
virtual void log(const details::log_msg& msg) = 0;
virtual void flush() = 0; virtual void flush() = 0;
bool should_log(level::level_enum msg_level) const; bool should_log(level::level_enum msg_level) const;
@@ -29,8 +22,7 @@ public:
level::level_enum level() const; level::level_enum level() const;
private: private:
level_t _level; level_t _level{level::trace};
}; };
inline bool sink::should_log(level::level_enum msg_level) const 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)); return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed));
} }
} } // namespace sinks
} } // namespace spdlog

View File

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

View File

@@ -9,18 +9,15 @@
#ifdef SPDLOG_ENABLE_SYSLOG #ifdef SPDLOG_ENABLE_SYSLOG
#include "sink.h"
#include "../details/log_msg.h" #include "../details/log_msg.h"
#include "sink.h"
#include <array> #include <array>
#include <string> #include <string>
#include <syslog.h> #include <syslog.h>
namespace spdlog {
namespace spdlog namespace sinks {
{
namespace sinks
{
/** /**
* Sink that write to syslog using the `syscall()` library call. * Sink that write to syslog using the `syscall()` library call.
* *
@@ -30,41 +27,39 @@ class syslog_sink : public sink
{ {
public: public:
// //
syslog_sink(const std::string& ident = "", int syslog_option=0, int syslog_facility=LOG_USER): syslog_sink(const std::string &ident = "", int syslog_option = 0, int syslog_facility = LOG_USER)
_ident(ident) : _ident(ident)
{ {
_priorities[static_cast<int>(level::trace)] = LOG_DEBUG; _priorities[static_cast<size_t>(level::trace)] = LOG_DEBUG;
_priorities[static_cast<int>(level::debug)] = LOG_DEBUG; _priorities[static_cast<size_t>(level::debug)] = LOG_DEBUG;
_priorities[static_cast<int>(level::info)] = LOG_INFO; _priorities[static_cast<size_t>(level::info)] = LOG_INFO;
_priorities[static_cast<int>(level::warn)] = LOG_WARNING; _priorities[static_cast<size_t>(level::warn)] = LOG_WARNING;
_priorities[static_cast<int>(level::err)] = LOG_ERR; _priorities[static_cast<size_t>(level::err)] = LOG_ERR;
_priorities[static_cast<int>(level::critical)] = LOG_CRIT; _priorities[static_cast<size_t>(level::critical)] = LOG_CRIT;
_priorities[static_cast<int>(level::off)] = LOG_INFO; _priorities[static_cast<size_t>(level::off)] = LOG_INFO;
//set ident to be program name if empty // set ident to be program name if empty
::openlog(_ident.empty()? nullptr:_ident.c_str(), syslog_option, syslog_facility); ::openlog(_ident.empty() ? nullptr : _ident.c_str(), syslog_option, syslog_facility);
} }
~syslog_sink()
~syslog_sink() override
{ {
::closelog(); ::closelog();
} }
syslog_sink(const syslog_sink&) = delete; syslog_sink(const syslog_sink &) = delete;
syslog_sink& operator=(const syslog_sink&) = delete; syslog_sink &operator=(const syslog_sink &) = delete;
void log(const details::log_msg &msg) override void log(const details::log_msg &msg) override
{ {
::syslog(syslog_prio_from_level(msg), "%s", msg.raw.str().c_str()); ::syslog(syslog_prio_from_level(msg), "%s", msg.raw.str().c_str());
} }
void flush() override void flush() override {}
{
}
private: private:
std::array<int, 7> _priorities; 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; const std::string _ident;
// //
@@ -72,10 +67,10 @@ private:
// //
int syslog_prio_from_level(const details::log_msg &msg) const 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 #endif

View File

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

View File

@@ -9,21 +9,19 @@
#include "msvc_sink.h" #include "msvc_sink.h"
namespace spdlog namespace spdlog {
{ namespace sinks {
namespace sinks
{
/* /*
* Windows debug sink (logging using OutputDebugStringA, synonym for msvc_sink) * Windows debug sink (logging using OutputDebugStringA, synonym for msvc_sink)
*/ */
template<class Mutex> template<class Mutex>
using windebug_sink = msvc_sink<Mutex>; using windebug_sink = msvc_sink<Mutex>;
typedef msvc_sink_mt windebug_sink_mt; using windebug_sink_mt = msvc_sink_mt;
typedef msvc_sink_st windebug_sink_st; using windebug_sink_st = msvc_sink_st;
} } // namespace sinks
} } // namespace spdlog
#endif #endif

View File

@@ -7,43 +7,43 @@
#pragma once #pragma once
#define SPDLOG_VERSION "0.16.1"
#include "tweakme.h"
#include "common.h" #include "common.h"
#include "logger.h" #include "logger.h"
#include <memory>
#include <functional>
#include <chrono> #include <chrono>
#include <functional>
#include <memory>
#include <string> #include <string>
namespace spdlog namespace spdlog {
{
// //
// Return an existing logger or nullptr if a logger with such name doesn't exist. // Return an existing logger or nullptr if a logger with such name doesn't exist.
// example: spdlog::get("my_logger")->info("hello {}", "world"); // 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 // Set global formatting
// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v"); // 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); void set_formatter(formatter_ptr f);
// //
// Set global logging level for // Set global logging level
// //
void set_level(level::level_enum log_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 // 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. // 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 (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::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): // worker_warmup_cb (optional):
// callback function that will be called in worker thread upon start (can be used to init stuff like thread affinity) // 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): // worker_teardown_cb (optional):
// callback function that will be called in worker thread upon exit // 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 // Turn off async mode
void set_sync_mode(); void set_sync_mode();
// //
// Create and register multi/single threaded basic file logger. // Create and register multi/single threaded basic file logger.
// Basic logger simply writes to given file without any limitations or rotations. // Basic logger simply writes to given file without any limitations or rotations.
// //
std::shared_ptr<logger> basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate = false); std::shared_ptr<logger> basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false);
std::shared_ptr<logger> basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate = false); std::shared_ptr<logger> basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false);
// //
// Create and register multi/single threaded rotating file logger // 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_mt(
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); 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_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_st(const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0);
// //
// Create and register stdout/stderr loggers // Create and register stdout/stderr loggers
// //
std::shared_ptr<logger> stdout_logger_mt(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> 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_mt(const std::string &logger_name);
std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name); std::shared_ptr<logger> stderr_logger_st(const std::string &logger_name);
// //
// Create and register colored stdout/stderr loggers // 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_mt(const std::string &logger_name);
std::shared_ptr<logger> stdout_color_st(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_mt(const std::string &logger_name);
std::shared_ptr<logger> stderr_color_st(const std::string& logger_name); std::shared_ptr<logger> stderr_color_st(const std::string &logger_name);
// //
// Create and register a syslog logger // Create and register a syslog logger
// //
#ifdef SPDLOG_ENABLE_SYSLOG #ifdef SPDLOG_ENABLE_SYSLOG
std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0, int syslog_facilty = (1<<3)); std::shared_ptr<logger> syslog_logger(
const std::string &logger_name, const std::string &ident = "", int syslog_option = 0, int syslog_facilty = (1 << 3));
#endif #endif
#if defined(__ANDROID__) #if defined(__ANDROID__)
std::shared_ptr<logger> android_logger(const std::string& logger_name, const std::string& tag = "spdlog"); std::shared_ptr<logger> android_logger(const std::string &logger_name, const std::string &tag = "spdlog");
#endif #endif
// Create and register a logger with a single sink // Create and register a logger with a single sink
std::shared_ptr<logger> create(const std::string& logger_name, const sink_ptr& sink); std::shared_ptr<logger> create(const std::string &logger_name, const sink_ptr &sink);
// Create and register a logger with multiple sinks // Create and register a logger with multiple sinks
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);
template<class It>
std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end);
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 // Create and register a logger with templated sink type
// Example: // Example:
// spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename"); // spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename");
template <typename Sink, typename... Args> template<typename Sink, typename... Args>
std::shared_ptr<spdlog::logger> create(const std::string& logger_name, Args...); std::shared_ptr<spdlog::logger> create(const std::string &logger_name, Args... args);
// Create and register an async logger with a single sink // 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 // 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> 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 // Register the given logger with the given name
void register_logger(std::shared_ptr<logger> logger); void register_logger(std::shared_ptr<logger> logger);
@@ -150,7 +168,6 @@ void drop(const std::string &name);
// Drop all references from the registry // Drop all references from the registry
void drop_all(); void drop_all();
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// //
// Trace & Debug can be switched on/off at compile time for zero cost debug statements. // Trace & Debug can be switched on/off at compile time for zero cost debug statements.
@@ -173,16 +190,15 @@ void drop_all();
#define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ] " __VA_ARGS__) #define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ] " __VA_ARGS__)
#endif #endif
#else #else
#define SPDLOG_TRACE(logger, ...) #define SPDLOG_TRACE(logger, ...) (void)0
#endif #endif
#ifdef SPDLOG_DEBUG_ON #ifdef SPDLOG_DEBUG_ON
#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__) #define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__)
#else #else
#define SPDLOG_DEBUG(logger, ...) #define SPDLOG_DEBUG(logger, ...) (void)0
#endif #endif
} } // namespace spdlog
#include "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. // 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. // This clock is less accurate - can be off by dozens of millis - depending on the kernel HZ.
@@ -20,7 +19,6 @@
// #define SPDLOG_CLOCK_COARSE // #define SPDLOG_CLOCK_COARSE
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment if date/time logging is not needed and never appear in the log pattern. // Uncomment if date/time logging is not needed and never appear in the log pattern.
// This will prevent spdlog from querying the clock on each log call. // This will prevent spdlog from querying the clock on each log call.
@@ -31,7 +29,6 @@
// #define SPDLOG_NO_DATETIME // #define SPDLOG_NO_DATETIME
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment if thread id logging is not needed (i.e. no %t in the log pattern). // Uncomment if thread id logging is not needed (i.e. no %t in the log pattern).
// This will prevent spdlog from querying the thread id on each log call. // This will prevent spdlog from querying the thread id on each log call.
@@ -41,7 +38,6 @@
// #define SPDLOG_NO_THREAD_ID // #define SPDLOG_NO_THREAD_ID
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment to prevent spdlog from caching thread ids in thread local storage. // 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. // By default spdlog saves thread ids in tls to gain a few micros for each call.
@@ -51,10 +47,9 @@
// #define SPDLOG_DISABLE_TID_CACHING // #define SPDLOG_DISABLE_TID_CACHING
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment if logger name logging is not needed. // Uncomment if logger name logging is not needed.
// This will prevent spdlog from copying the logger name on each log call. // This will prevent spdlog from copying the logger name on each log call.
// //
// #define SPDLOG_NO_NAME // #define SPDLOG_NO_NAME
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@@ -66,7 +61,6 @@
// #define SPDLOG_TRACE_ON // #define SPDLOG_TRACE_ON
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment to avoid locking in the registry operations (spdlog::get(), spdlog::drop() spdlog::register()). // Uncomment to avoid locking in the registry operations (spdlog::get(), spdlog::drop() spdlog::register()).
// Use only if your code never modifies concurrently the registry. // Use only if your code never modifies concurrently the registry.
@@ -75,7 +69,6 @@
// #define SPDLOG_NO_REGISTRY_MUTEX // #define SPDLOG_NO_REGISTRY_MUTEX
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment to avoid spdlog's usage of atomic log levels // 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. // Use only if your code never modifies a logger's log levels concurrently by different threads.
@@ -83,21 +76,18 @@
// #define SPDLOG_NO_ATOMIC_LEVELS // #define SPDLOG_NO_ATOMIC_LEVELS
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable usage of wchar_t for file names on Windows. // Uncomment to enable usage of wchar_t for file names on Windows.
// //
// #define SPDLOG_WCHAR_FILENAMES // #define SPDLOG_WCHAR_FILENAMES
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment to override default eol ("\n" or "\r\n" under Linux/Windows) // Uncomment to override default eol ("\n" or "\r\n" under Linux/Windows)
// //
// #define SPDLOG_EOL ";-)\n" // #define SPDLOG_EOL ";-)\n"
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment to use your own copy of the fmt library instead of spdlog's copy. // 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. // In this case spdlog will try to include <fmt/format.h> so set your -I flag accordingly.
@@ -105,7 +95,6 @@
// #define SPDLOG_FMT_EXTERNAL // #define SPDLOG_FMT_EXTERNAL
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment to use printf-style messages in your logs instead of the usual // Uncomment to use printf-style messages in your logs instead of the usual
// format-style used by default. // format-style used by default.
@@ -113,28 +102,24 @@
// #define SPDLOG_FMT_PRINTF // #define SPDLOG_FMT_PRINTF
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable syslog (disabled by default) // Uncomment to enable syslog (disabled by default)
// //
// #define SPDLOG_ENABLE_SYSLOG // #define SPDLOG_ENABLE_SYSLOG
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable wchar_t support (convert to utf8) // Uncomment to enable wchar_t support (convert to utf8)
// //
// #define SPDLOG_WCHAR_TO_UTF8_SUPPORT // #define SPDLOG_WCHAR_TO_UTF8_SUPPORT
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment to prevent child processes from inheriting log file descriptors // Uncomment to prevent child processes from inheriting log file descriptors
// //
// #define SPDLOG_PREVENT_CHILD_FD // #define SPDLOG_PREVENT_CHILD_FD
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment if your compiler doesn't support the "final" keyword. // Uncomment if your compiler doesn't support the "final" keyword.
// The final keyword allows more optimizations in release // The final keyword allows more optimizations in release
@@ -144,7 +129,6 @@
// #define SPDLOG_NO_FINAL // #define SPDLOG_NO_FINAL
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable message counting feature. // Uncomment to enable message counting feature.
// Use the %i in the logger pattern to display log message sequence id. // Use the %i in the logger pattern to display log message sequence id.
@@ -152,9 +136,8 @@
// #define SPDLOG_ENABLE_MESSAGE_COUNTER // #define SPDLOG_ENABLE_MESSAGE_COUNTER
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment to customize level names (e.g. "MT TRACE") // Uncomment to customize level names (e.g. "MT TRACE")
// //
// #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING", "MY ERROR", "MY CRITICAL", "OFF" } // #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING", "MY ERROR", "MY CRITICAL", "OFF" }
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////

View File

@@ -1,19 +1,24 @@
# project(spdlog-utests CXX)
# Tests enable_testing()
# find_package(Threads REQUIRED)
enable_testing() set(SPDLOG_UTESTS_SOURCES
errors.cpp
find_package(Threads) file_helper.cpp
file_log.cpp
# Build Catch unit tests test_misc.cpp
add_library(catch INTERFACE) test_pattern_formatter.cpp
target_include_directories(catch INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) test_async.cpp
includes.h
file(GLOB catch_tests LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp *.h *.hpp) registry.cpp
test_macros.cpp
add_executable(catch_tests ${catch_tests}) utils.cpp
target_link_libraries(catch_tests spdlog ${CMAKE_THREAD_LIBS_INIT}) utils.h
add_test(NAME catch_tests COMMAND catch_tests) main.cpp)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
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,10 +1,10 @@
CXX ?= g++ CXX ?= g++
ifeq ($(STYLE),printf) ifeq ($(STYLE),printf)
$(info *** PRINTF STYLE ***) $(info *** PRINTF STYLE ***)
CXXFLAGS = -DSPDLOG_FMT_PRINTF -Wall -pedantic -std=c++11 -pthread -O2 -I../include CXXFLAGS = -DSPDLOG_FMT_PRINTF -Wall -pedantic -std=c++11 -pthread -O3 -I../include
else else
$(info *** FORMAT STYLE ***) $(info *** FORMAT STYLE ***)
CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -O2 -I../include CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -O3 -I../include
endif endif
LDPFALGS = -pthread LDPFALGS = -pthread

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 ) for( std::vector<Ptr<Pattern> >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it )
if( !(*it)->matches( testCase ) ) if( !(*it)->matches( testCase ) )
return false; 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.

View File

@@ -1,22 +1,18 @@
/* /*
* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE * This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
*/ */
#include "includes.h" #include "includes.h"
#include<iostream> #include <iostream>
class failing_sink : public spdlog::sinks::sink
class failing_sink: public spdlog::sinks::sink
{ {
void log(const spdlog::details::log_msg& msg) override void log(const spdlog::details::log_msg &msg) override
{ {
throw std::runtime_error("some error happened during log"); throw std::runtime_error("some error happened during log");
} }
void flush() override void flush() override {}
{}
}; };
TEST_CASE("default_error_handler", "[errors]]") TEST_CASE("default_error_handler", "[errors]]")
@@ -39,21 +35,16 @@ TEST_CASE("default_error_handler", "[errors]]")
REQUIRE(count_lines(filename) == 1); REQUIRE(count_lines(filename) == 1);
} }
struct custom_ex struct custom_ex
{}; {
};
TEST_CASE("custom_error_handler", "[errors]]") TEST_CASE("custom_error_handler", "[errors]]")
{ {
prepare_logdir(); prepare_logdir();
std::string filename = "logs/simple_log.txt"; std::string filename = "logs/simple_log.txt";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true); auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
logger->flush_on(spdlog::level::info); logger->flush_on(spdlog::level::info);
logger->set_error_handler([=](const std::string& msg) logger->set_error_handler([=](const std::string &msg) { throw custom_ex(); });
{
throw custom_ex();
});
logger->info("Good message #1"); logger->info("Good message #1");
#if !defined(SPDLOG_FMT_PRINTF) #if !defined(SPDLOG_FMT_PRINTF)
REQUIRE_THROWS_AS(logger->info("Bad format msg {} {}", "xxx"), custom_ex); REQUIRE_THROWS_AS(logger->info("Bad format msg {} {}", "xxx"), custom_ex);
@@ -68,10 +59,7 @@ TEST_CASE("default_error_handler2", "[errors]]")
{ {
auto logger = spdlog::create<failing_sink>("failed_logger"); auto logger = spdlog::create<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string& msg) logger->set_error_handler([=](const std::string &msg) { throw custom_ex(); });
{
throw custom_ex();
});
REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex); REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex);
} }
@@ -83,10 +71,10 @@ TEST_CASE("async_error_handler", "[errors]]")
std::string filename = "logs/simple_async_log.txt"; std::string filename = "logs/simple_async_log.txt";
{ {
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true); auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
logger->set_error_handler([=](const std::string& msg) logger->set_error_handler([=](const std::string &msg) {
{
std::ofstream ofs("logs/custom_err.txt"); std::ofstream ofs("logs/custom_err.txt");
if (!ofs) throw std::runtime_error("Failed open logs/custom_err.txt"); if (!ofs)
throw std::runtime_error("Failed open logs/custom_err.txt");
ofs << err_msg; ofs << err_msg;
}); });
logger->info("Good message #1"); logger->info("Good message #1");
@@ -96,7 +84,7 @@ TEST_CASE("async_error_handler", "[errors]]")
logger->info("Bad format msg %s %s", "xxx"); logger->info("Bad format msg %s %s", "xxx");
#endif #endif
logger->info("Good message #2"); logger->info("Good message #2");
spdlog::drop("logger"); //force logger to drain the queue and shutdown spdlog::drop("logger"); // force logger to drain the queue and shutdown
spdlog::set_sync_mode(); spdlog::set_sync_mode();
} }
REQUIRE(count_lines(filename) == 2); REQUIRE(count_lines(filename) == 2);
@@ -111,14 +99,14 @@ TEST_CASE("async_error_handler2", "[errors]]")
spdlog::set_async_mode(128); spdlog::set_async_mode(128);
{ {
auto logger = spdlog::create<failing_sink>("failed_logger"); auto logger = spdlog::create<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string& msg) logger->set_error_handler([=](const std::string &msg) {
{
std::ofstream ofs("logs/custom_err2.txt"); std::ofstream ofs("logs/custom_err2.txt");
if (!ofs) throw std::runtime_error("Failed open logs/custom_err2.txt"); if (!ofs)
throw std::runtime_error("Failed open logs/custom_err2.txt");
ofs << err_msg; ofs << err_msg;
}); });
logger->info("Hello failure"); logger->info("Hello failure");
spdlog::drop("failed_logger"); //force logger to drain the queue and shutdown spdlog::drop("failed_logger"); // force logger to drain the queue and shutdown
spdlog::set_sync_mode(); spdlog::set_sync_mode();
} }

View File

@@ -1,9 +1,10 @@
/* /*
* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE * This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
*/ */
#include "includes.h" #include "includes.h"
using namespace spdlog::details; using spdlog::details::file_helper;
using spdlog::details::log_msg;
static const std::string target_filename = "logs/file_helper_test.txt"; static const std::string target_filename = "logs/file_helper_test.txt";
@@ -15,7 +16,6 @@ static void write_with_helper(file_helper &helper, size_t howmany)
helper.flush(); helper.flush();
} }
TEST_CASE("file_helper_filename", "[file_helper::filename()]]") TEST_CASE("file_helper_filename", "[file_helper::filename()]]")
{ {
prepare_logdir(); prepare_logdir();
@@ -25,8 +25,6 @@ TEST_CASE("file_helper_filename", "[file_helper::filename()]]")
REQUIRE(helper.filename() == target_filename); REQUIRE(helper.filename() == target_filename);
} }
TEST_CASE("file_helper_size", "[file_helper::size()]]") TEST_CASE("file_helper_size", "[file_helper::size()]]")
{ {
prepare_logdir(); prepare_logdir();
@@ -40,7 +38,6 @@ TEST_CASE("file_helper_size", "[file_helper::size()]]")
REQUIRE(get_filesize(target_filename) == expected_size); REQUIRE(get_filesize(target_filename) == expected_size);
} }
TEST_CASE("file_helper_exists", "[file_helper::file_exists()]]") TEST_CASE("file_helper_exists", "[file_helper::file_exists()]]")
{ {
prepare_logdir(); prepare_logdir();
@@ -73,77 +70,40 @@ TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
REQUIRE(helper.size() == expected_size); REQUIRE(helper.size() == expected_size);
} }
static void test_split_ext(const char *fname, const char *expect_base, const char *expect_ext)
{
spdlog::filename_t filename(fname);
spdlog::filename_t expected_base(expect_base);
spdlog::filename_t expected_ext(expect_ext);
#ifdef _WIN32 // replace folder sep
std::replace(filename.begin(), filename.end(), '/', '\\');
std::replace(expected_base.begin(), expected_base.end(), '/', '\\');
#endif
spdlog::filename_t basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion(filename);
REQUIRE(basename == expected_base);
REQUIRE(ext == expected_ext);
}
TEST_CASE("file_helper_split_by_extenstion", "[file_helper::split_by_extenstion()]]") TEST_CASE("file_helper_split_by_extenstion", "[file_helper::split_by_extenstion()]]")
{ {
std::string basename, ext; test_split_ext("mylog.txt", "mylog", ".txt");
std::tie(basename, ext) = file_helper::split_by_extenstion("mylog.txt"); test_split_ext(".mylog.txt", ".mylog", ".txt");
REQUIRE(basename == "mylog"); test_split_ext(".mylog", ".mylog", "");
REQUIRE(ext == ".txt"); test_split_ext("/aaa/bb.d/mylog", "/aaa/bb.d/mylog", "");
test_split_ext("/aaa/bb.d/mylog.txt", "/aaa/bb.d/mylog", ".txt");
test_split_ext("aaa/bbb/ccc/mylog.txt", "aaa/bbb/ccc/mylog", ".txt");
test_split_ext("aaa/bbb/ccc/mylog.", "aaa/bbb/ccc/mylog.", "");
test_split_ext("aaa/bbb/ccc/.mylog.txt", "aaa/bbb/ccc/.mylog", ".txt");
test_split_ext("/aaa/bbb/ccc/mylog.txt", "/aaa/bbb/ccc/mylog", ".txt");
test_split_ext("/aaa/bbb/ccc/.mylog", "/aaa/bbb/ccc/.mylog", "");
test_split_ext("../mylog.txt", "../mylog", ".txt");
test_split_ext(".././mylog.txt", ".././mylog", ".txt");
test_split_ext(".././mylog.txt/xxx", ".././mylog.txt/xxx", "");
test_split_ext("/mylog.txt", "/mylog", ".txt");
test_split_ext("//mylog.txt", "//mylog", ".txt");
test_split_ext("", "", "");
test_split_ext(".", ".", "");
test_split_ext("..txt", ".", ".txt");
} }
TEST_CASE("file_helper_split_by_extenstion2", "[file_helper::split_by_extenstion()]]")
{
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion("mylog");
REQUIRE(basename == "mylog");
REQUIRE(ext == "");
}
TEST_CASE("file_helper_split_by_extenstion3", "[file_helper::split_by_extenstion()]]")
{
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion("mylog.xyz.txt");
REQUIRE(basename == "mylog.xyz");
REQUIRE(ext == ".txt");
}
TEST_CASE("file_helper_split_by_extenstion4", "[file_helper::split_by_extenstion()]]")
{
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion("mylog.xyz....txt");
REQUIRE(basename == "mylog.xyz...");
REQUIRE(ext == ".txt");
}
TEST_CASE("file_helper_split_by_extenstion5", "[file_helper::split_by_extenstion(hidden_file)]]")
{
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion(".mylog");
REQUIRE(basename == ".mylog");
REQUIRE(ext == "");
}
TEST_CASE("file_helper_split_by_extenstion6", "[file_helper::split_by_extenstion(hidden_file)]]")
{
#ifdef _WIN32
auto filename = "folder\\.mylog";
auto expected_basename = "folder\\.mylog";
#else
auto filename = "folder/.mylog";
auto expected_basename = "folder/.mylog";
#endif
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion(filename);
REQUIRE(basename == expected_basename);
REQUIRE(ext == "");
}
TEST_CASE("file_helper_split_by_extenstion7", "[file_helper::split_by_extenstion(hidden_file)]]")
{
#ifdef _WIN32
auto filename = "folder\\.mylog.txt";
auto expected_basename = "folder\\.mylog";
#else
auto filename = "folder/.mylog.txt";
auto expected_basename = "folder/.mylog";
#endif
std::string basename, ext;
std::tie(basename, ext) = file_helper::split_by_extenstion(filename);
REQUIRE(basename == expected_basename);
REQUIRE(ext == ".txt");
}

View File

@@ -3,7 +3,6 @@
*/ */
#include "includes.h" #include "includes.h"
TEST_CASE("simple_file_logger", "[simple_logger]]") TEST_CASE("simple_file_logger", "[simple_logger]]")
{ {
prepare_logdir(); prepare_logdir();
@@ -24,7 +23,6 @@ TEST_CASE("simple_file_logger", "[simple_logger]]")
REQUIRE(count_lines(filename) == 2); REQUIRE(count_lines(filename) == 2);
} }
TEST_CASE("flush_on", "[flush_on]]") TEST_CASE("flush_on", "[flush_on]]")
{ {
prepare_logdir(); prepare_logdir();
@@ -69,7 +67,6 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
REQUIRE(count_lines(filename) == 10); REQUIRE(count_lines(filename) == 10);
} }
TEST_CASE("rotating_file_logger2", "[rotating_logger]]") TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
{ {
prepare_logdir(); prepare_logdir();
@@ -96,11 +93,10 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
REQUIRE(get_filesize(filename1) <= 1024); REQUIRE(get_filesize(filename1) <= 1024);
} }
TEST_CASE("daily_logger", "[daily_logger]]") TEST_CASE("daily_logger", "[daily_logger]]")
{ {
prepare_logdir(); prepare_logdir();
//calculate filename (time based) // calculate filename (time based)
std::string basename = "logs/daily_log"; std::string basename = "logs/daily_log";
std::tm tm = spdlog::details::os::localtime(); std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w; fmt::MemoryWriter w;
@@ -121,15 +117,12 @@ TEST_CASE("daily_logger", "[daily_logger]]")
REQUIRE(count_lines(filename) == 10); REQUIRE(count_lines(filename) == 10);
} }
TEST_CASE("daily_logger with dateonly calculator", "[daily_logger_dateonly]]") TEST_CASE("daily_logger with dateonly calculator", "[daily_logger_dateonly]]")
{ {
using sink_type = spdlog::sinks::daily_file_sink< using sink_type = spdlog::sinks::daily_file_sink<std::mutex, spdlog::sinks::dateonly_daily_file_name_calculator>;
std::mutex,
spdlog::sinks::dateonly_daily_file_name_calculator>;
prepare_logdir(); prepare_logdir();
//calculate filename (time based) // calculate filename (time based)
std::string basename = "logs/daily_dateonly"; std::string basename = "logs/daily_dateonly";
std::tm tm = spdlog::details::os::localtime(); std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w; fmt::MemoryWriter w;
@@ -151,7 +144,7 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger_dateonly]]")
struct custom_daily_file_name_calculator struct custom_daily_file_name_calculator
{ {
static spdlog::filename_t calc_filename(const spdlog::filename_t& basename) static spdlog::filename_t calc_filename(const spdlog::filename_t &basename)
{ {
std::tm tm = spdlog::details::os::localtime(); std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w; fmt::MemoryWriter w;
@@ -162,12 +155,10 @@ struct custom_daily_file_name_calculator
TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]") TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]")
{ {
using sink_type = spdlog::sinks::daily_file_sink< using sink_type = spdlog::sinks::daily_file_sink<std::mutex, custom_daily_file_name_calculator>;
std::mutex,
custom_daily_file_name_calculator>;
prepare_logdir(); prepare_logdir();
//calculate filename (time based) // calculate filename (time based)
std::string basename = "logs/daily_dateonly"; std::string basename = "logs/daily_dateonly";
std::tm tm = spdlog::details::os::localtime(); std::tm tm = spdlog::details::os::localtime();
fmt::MemoryWriter w; fmt::MemoryWriter w;
@@ -188,7 +179,6 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]")
REQUIRE(count_lines(filename) == 10); REQUIRE(count_lines(filename) == 10);
} }
/* /*
* File name calculations * File name calculations
*/ */
@@ -211,12 +201,8 @@ TEST_CASE("rotating_file_sink::calc_filename3", "[rotating_file_sink]]")
REQUIRE(filename == "rotated.txt"); REQUIRE(filename == "rotated.txt");
} }
// regex supported only from gcc 4.9 and above // regex supported only from gcc 4.9 and above
#if defined (_MSC_VER) || !(__GNUC__ <= 4 && __GNUC_MINOR__ < 9) #if defined(_MSC_VER) || !(__GNUC__ <= 4 && __GNUC_MINOR__ < 9)
#include <regex> #include <regex>
TEST_CASE("daily_file_sink::default_daily_file_name_calculator1", "[daily_file_sink]]") TEST_CASE("daily_file_sink::default_daily_file_name_calculator1", "[daily_file_sink]]")
{ {

View File

@@ -1,56 +0,0 @@
#include "includes.h"
template<class T>
std::string log_info(const T& what, spdlog::level::level_enum logger_level = spdlog::level::info)
{
std::ostringstream oss;
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
spdlog::logger oss_logger("oss", oss_sink);
oss_logger.set_level(logger_level);
oss_logger.set_pattern("%v");
oss_logger.info(what);
return oss.str().substr(0, oss.str().length() - spdlog::details::os::eol_size);
}
TEST_CASE("basic_logging ", "[basic_logging]")
{
//const char
REQUIRE(log_info("Hello") == "Hello");
REQUIRE(log_info("") == "");
//std::string
REQUIRE(log_info(std::string("Hello")) == "Hello");
REQUIRE(log_info(std::string()) == std::string());
//Numbers
REQUIRE(log_info(5) == "5");
REQUIRE(log_info(5.6) == "5.6");
//User defined class
//REQUIRE(log_info(some_logged_class("some_val")) == "some_val");
}
TEST_CASE("log_levels", "[log_levels]")
{
REQUIRE(log_info("Hello", spdlog::level::err) == "");
REQUIRE(log_info("Hello", spdlog::level::critical) == "");
REQUIRE(log_info("Hello", spdlog::level::info) == "Hello");
REQUIRE(log_info("Hello", spdlog::level::debug) == "Hello");
REQUIRE(log_info("Hello", spdlog::level::trace) == "Hello");
}

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