Compare commits

...

79 Commits

Author SHA1 Message Date
Gabi Melman
dea6bb1085 Merge pull request #2359 from tiolan/master
Revert "Merge pull request #2352 from tiolan/topic/android_buffer"
2022-05-12 23:24:55 +03:00
Timo Lange
b960d5a062 Revert "Merge pull request #2352 from tiolan/topic/android_buffer"
This reverts commit c6d0487474, reversing
changes made to a51b485637.
2022-05-06 11:54:01 +02:00
Gabi Melman
c6d0487474 Merge pull request #2352 from tiolan/topic/android_buffer
Allow modifying the used Android buffer
2022-05-03 01:44:58 +03:00
Timo Lange
7d22b689b6 use __android_log_write or __android_log_buf_write based on template parameter 2022-04-29 21:12:19 +02:00
Gabi Melman
a51b485637 Merge pull request #1210 from jimmyorourke/patch-1
Windows color file sink: Use WriteFile rather than WriteConsole
2019-09-06 07:50:15 +03:00
jimmyorourke
3a13e76bb1 Use WriteFile rather than WriteConsole to be able to use file handles or pipes 2019-09-03 14:16:39 -04:00
Gabi Melman
139c0d135f Merge pull request #1112 from thomaslewis1096/master
Added !defined(__HAIKU__) to #if for stat64 struct.
2019-06-15 22:21:00 +03:00
Thomas Lewis
cee913d76e Added __HAIKU__ to #if for stat64 struct. 2019-06-15 12:10:12 -06:00
Gabi Melman
7088644d3f Merge pull request #821 from rkanavath/compile_fix
fix build on openbsd
2018-09-04 15:32:02 +03:00
Rashad Kanavath
9ca4e3d8bc fix build on openbsd 2018-09-04 11:41:06 +02:00
Gabi Melman
f1a60eb6b1 Update INSTALL 2018-07-08 14:34:33 +03:00
Gabi Melman
96961e14ad Update INSTALL 2018-07-08 14:34:18 +03:00
Gabi Melman
c639483db0 Merge pull request #735 from jwnimmer-tri/dist_sink_reset
add remove_all_sinks method to dist_sink
2018-06-21 17:22:18 +03:00
Jeremy Nimmer
b3881b9c15 add remove_all_sinks method to dist_sink
This allows users to set exactly the sinks they want, even if other
unknown application code has added bespoke sinks in the meantime.
2018-06-13 07:42:20 -04:00
gabime
032035e72f format 2018-06-10 23:22:31 +03:00
gabime
66b5772b61 Fixed issue #720 2018-06-10 23:22:19 +03:00
gabime
faea32c9f9 simplfiy a little the use of fmt:safe_strerror 2018-06-01 16:58:07 +03:00
Gabi Melman
d78ddcee24 Merge pull request #719 from JamesReynolds/master
Closes #717
2018-06-01 16:42:45 +03:00
JamesReynolds
7bc598c9b5 Move errno code into common
errno code is no longer OS specific. Moving to common.
2018-06-01 14:32:33 +01:00
JamesReynolds
aa9012fa07 Fixes #717
This fix uses the fmt::safe_strerror instead of the underlying
strerror_r functionality. I think this is better - but I'm not
sure if there is a reason for avoiding the use of this function.
2018-06-01 12:24:06 +01:00
JamesReynolds
3fbac8e2b7 Closes #717
Failure to compile to systems that don't have a valid definition
of strerror_r.
2018-06-01 11:53:46 +01:00
Gabi Melman
d033ccc0c7 Update README.md 2018-05-30 18:26:52 +03:00
Gabi Melman
1fcc896486 Merge pull request #715 from JamesReynolds/master
Closes gabime/spdlog#74
2018-05-30 15:25:40 +03:00
Gabi Melman
4640c5cb50 Merge pull request #713 from Puasonych/master
Update step logger
2018-05-30 13:30:13 +03:00
JamesReynolds
4131347079 This closes #74 in gabime/spdlog
Support for AIX. Tested on AIX 6.1 with GCC 6.3.
2018-05-30 10:44:41 +01:00
puasonych
98b9cd38ec Update step logger 2018-05-30 13:07:43 +05:00
puasonych
877a618400 Update step logger
Fixed bugs with multithreading and added protection against data lost with insufficiently accurate setting of the maximum value of the file size
2018-05-30 12:48:56 +05:00
puasonych
eb2118c181 Update step_file_sink 2018-05-30 09:40:09 +05:00
puasonych
5c1951acec Update step_file_sink
Fixed adding a file header and adding ignoring of empty files
2018-05-25 22:20:14 +05:00
gabime
67a6eaf23e Merge branch 'master' of https://github.com/gabime/spdlog 2018-05-24 00:09:54 +03:00
gabime
f4db1c510c catch potential flush exceptions 2018-05-24 00:09:38 +03:00
Gabi Melman
2aa25109ef Merge pull request #706 from Puasonych/master
Added file header support for step_logger
2018-05-22 09:08:10 +03:00
Puasonych
bd3f8a3b92 Added file header support 2018-05-22 08:27:34 +03:00
Gabi Melman
d6b700eaf2 Update async_log_helper.h 2018-05-21 21:16:34 +03:00
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
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
28 changed files with 864 additions and 528 deletions

3
.gitignore vendored
View File

@@ -66,3 +66,6 @@ install_manifest.txt
# 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.3 LANGUAGES CXX) project(spdlog VERSION 0.17.0 LANGUAGES CXX)
include(CTest) include(CTest)
include(CMakeDependentOption) include(CMakeDependentOption)
include(GNUInstallDirs) include(GNUInstallDirs)

View File

@@ -2,9 +2,9 @@ spdlog is header only library.
Just copy the files to your build tree and use a C++11 compiler Just copy the files to your build tree and use a C++11 compiler
Tested on: Tested on:
gcc 4.8.1 and above gcc 4.8.1 or above
clang 3.5 clang 3.5 or above
Visual Studio 2013 Visual Studio 2013 or above
gcc 4.8 flags: --std==c++11 -pthread -O3 -flto -Wl,--no-as-needed gcc 4.8 flags: --std==c++11 -pthread -O3 -flto -Wl,--no-as-needed
gcc 4.9 flags: --std=c++11 -pthread -O3 -flto gcc 4.9 flags: --std=c++11 -pthread -O3 -flto

View File

@@ -20,7 +20,7 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
## Platforms ## Platforms
* Linux, FreeBSD, Solaris * Linux, FreeBSD, Solaris, AIX
* Windows (vc 2013+, cygwin) * 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|
@@ -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

@@ -25,8 +25,8 @@ int main(int argc, char *argv[])
int howmany = 1000000; int howmany = 1000000;
spdlog::set_async_mode(1048576); spdlog::set_async_mode(1000000);
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("file_logger", "logs/spdlog-bench-async.log", false); auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("file_logger", "logs/spdlog-bench-async.log", true);
logger->set_pattern("[%Y-%m-%d %T.%F]: %L %t %v"); logger->set_pattern("[%Y-%m-%d %T.%F]: %L %t %v");
std::cout << "To stop, press <Enter>" << std::endl; std::cout << "To stop, press <Enter>" << std::endl;

View File

@@ -100,7 +100,7 @@ void bench(int howmany, std::shared_ptr<spdlog::logger> log)
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)
@@ -130,5 +130,5 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count
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

@@ -105,7 +105,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;
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)

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

@@ -5,7 +5,7 @@
#pragma once #pragma once
#define SPDLOG_VERSION "0.16.4-rc" #define SPDLOG_VERSION "0.17.0"
#include "tweakme.h" #include "tweakme.h"
@@ -143,11 +143,6 @@ enum class pattern_time_type
// //
// Log exception // Log exception
// //
namespace details {
namespace os {
std::string errno_str(int err_num);
}
} // namespace details
class spdlog_ex : public std::exception class spdlog_ex : public std::exception
{ {
public: public:
@@ -158,7 +153,9 @@ public:
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); fmt::MemoryWriter writer;
fmt::format_system_error(writer, last_errno, msg);
_msg = writer.str();
} }
const char *what() const SPDLOG_NOEXCEPT override const char *what() const SPDLOG_NOEXCEPT override
@@ -179,4 +176,13 @@ using filename_t = std::wstring;
using filename_t = std::string; using filename_t = std::string;
#endif #endif
#define SPDLOG_CATCH_AND_HANDLE \
catch (const std::exception &ex) \
{ \
_err_handler(ex.what()); \
} \
catch (...) \
{ \
_err_handler("Unknown exeption in logger"); \
}
} // namespace spdlog } // namespace spdlog

View File

@@ -0,0 +1,216 @@
#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(), bool delete_empty_files = true, const
// filename_t &file_header = ""); 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,
// bool delete_empty_files, const filename_t &file_header)
// {
// return create<spdlog::sinks::step_file_sink_mt>(logger_name, filename_fmt, seconds, tmp_ext, max_file_size, delete_empty_files,
// file_header);
// }
// 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,
// bool delete_empty_files, const filename_t &file_header)
// {
// return create<spdlog::sinks::step_file_sink_st>(logger_name, filename_fmt, seconds, tmp_ext, max_file_size, delete_empty_files,
// file_header);
// }
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, bool delete_empty_files,
filename_t file_header)
: _base_filename(std::move(base_filename))
, _tmp_ext(std::move(tmp_ext))
, _step_seconds(step_seconds)
, _max_size(max_size)
, _delete_empty_files(delete_empty_files)
{
if (step_seconds == 0)
{
throw spdlog_ex("step_file_sink: Invalid time step in ctor");
}
if (!file_header.empty())
{
pattern_formatter formatter_for_file_header("%v");
_file_header.raw << file_header;
formatter_for_file_header.format(_file_header);
}
if (max_size <= _file_header.formatted.size())
{
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
if (!_current_size)
{
_current_size += _file_header.formatted.size();
if (_current_size)
_file_helper.write(_file_header);
}
}
~step_file_sink()
{
try
{
close_current_file();
}
catch (...)
{
}
}
protected:
void _sink_it(const details::log_msg &msg) override
{
auto msg_size = msg.formatted.size();
if (std::chrono::system_clock::now() >= _tp || _current_size + msg_size > _max_size)
{
filename_t new_filename;
std::tie(new_filename, std::ignore) = FileNameCalc::calc_filename(_base_filename, _tmp_ext);
bool change_occured = !details::file_helper::file_exists(new_filename);
if (change_occured)
{
close_current_file();
_current_filename = std::move(new_filename);
_file_helper.open(_current_filename);
}
_tp = _next_tp();
if (change_occured)
{
_current_size = _file_header.formatted.size();
if (_current_size)
_file_helper.write(_file_header);
}
}
_current_size += msg_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;
// Delete empty files, if required
if (_delete_empty_files && _current_size <= _file_header.formatted.size())
{
if (details::os::remove(_current_filename) != 0)
{
throw spdlog_ex("step_file_sink: not remove " + filename_to_str(_current_filename), errno);
}
return;
}
filename_t target;
std::tie(target, std::ignore) = details::file_helper::split_by_extenstion(_current_filename);
target += _ext;
if (details::file_helper::file_exists(_current_filename) && details::os::rename(_current_filename, target) != 0)
{
throw spdlog_ex(
"step_file_sink: failed renaming " + filename_to_str(_current_filename) + " 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;
const bool _delete_empty_files;
std::chrono::system_clock::time_point _tp;
filename_t _current_filename;
filename_t _ext;
unsigned _current_size;
details::file_helper _file_helper;
details::log_msg _file_header;
};
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

@@ -14,12 +14,13 @@
#include "../common.h" #include "../common.h"
#include "../details/log_msg.h" #include "../details/log_msg.h"
#include "../details/mpmc_bounded_q.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 "../sinks/sink.h"
#include <chrono> #include <chrono>
#include <condition_variable>
#include <exception> #include <exception>
#include <functional> #include <functional>
#include <memory> #include <memory>
@@ -44,7 +45,6 @@ class async_log_helper
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;
@@ -63,27 +63,8 @@ class async_log_helper
{ {
} }
async_msg(async_msg &&other) SPDLOG_NOEXCEPT : logger_name(std::move(other.logger_name)), async_msg(async_msg &&other) = default;
level(std::move(other.level)), async_msg &operator=(async_msg &&other) = default;
time(std::move(other.time)),
thread_id(other.thread_id),
txt(std::move(other.txt)),
msg_type(std::move(other.msg_type)),
msg_id(other.msg_id)
{
}
async_msg &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;
}
// 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;
@@ -98,18 +79,16 @@ class async_log_helper
, 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;
} }
@@ -121,8 +100,9 @@ public:
using clock = std::chrono::steady_clock; using clock = std::chrono::steady_clock;
async_log_helper(formatter_ptr formatter, std::vector<sink_ptr> sinks, size_t queue_size, const log_err_handler err_handler, async_log_helper(std::string logger_name, formatter_ptr formatter, std::vector<sink_ptr> sinks, size_t queue_size,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, std::function<void()> worker_warmup_cb = nullptr, const log_err_handler err_handler, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
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(),
std::function<void()> worker_teardown_cb = nullptr); std::function<void()> worker_teardown_cb = nullptr);
@@ -136,11 +116,12 @@ public:
void set_formatter(formatter_ptr msg_formatter); void set_formatter(formatter_ptr msg_formatter);
void flush(bool wait_for_q); 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;
@@ -149,9 +130,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;
@@ -165,25 +144,26 @@ 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 void flush_sinks();
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();
}; };
} // namespace details } // namespace details
} // namespace spdlog } // namespace spdlog
@@ -191,15 +171,15 @@ private:
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// async_sink class implementation // async_sink class implementation
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
inline spdlog::details::async_log_helper::async_log_helper(formatter_ptr formatter, std::vector<sink_ptr> sinks, size_t queue_size, inline spdlog::details::async_log_helper::async_log_helper(std::string logger_name, formatter_ptr formatter, std::vector<sink_ptr> sinks,
log_err_handler err_handler, const async_overflow_policy overflow_policy, std::function<void()> worker_warmup_cb, size_t queue_size, log_err_handler err_handler, const async_overflow_policy overflow_policy, std::function<void()> worker_warmup_cb,
const std::chrono::milliseconds &flush_interval_ms, std::function<void()> worker_teardown_cb) const std::chrono::milliseconds &flush_interval_ms, std::function<void()> worker_teardown_cb)
: _formatter(std::move(formatter)) : _logger_name(std::move(logger_name))
, _formatter(std::move(formatter))
, _sinks(std::move(sinks)) , _sinks(std::move(sinks))
, _q(queue_size) , _q(queue_size)
, _err_handler(std::move(err_handler)) , _err_handler(std::move(err_handler))
, _flush_requested(false) , _last_flush(os::now())
, _terminate_requested(false)
, _overflow_policy(overflow_policy) , _overflow_policy(overflow_policy)
, _worker_warmup_cb(std::move(worker_warmup_cb)) , _worker_warmup_cb(std::move(worker_warmup_cb))
, _flush_interval_ms(flush_interval_ms) , _flush_interval_ms(flush_interval_ms)
@@ -208,13 +188,12 @@ inline spdlog::details::async_log_helper::async_log_helper(formatter_ptr formatt
_worker_thread = std::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
@@ -222,34 +201,30 @@ 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()
@@ -258,23 +233,14 @@ inline void spdlog::details::async_log_helper::worker_loop()
{ {
_worker_warmup_cb(); _worker_warmup_cb();
} }
auto last_pop = details::os::now();
auto last_flush = last_pop;
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)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exeption in async logger worker loop.");
} }
SPDLOG_CATCH_AND_HANDLE
} }
if (_worker_teardown_cb) if (_worker_teardown_cb)
{ {
@@ -284,61 +250,46 @@ inline void spdlog::details::async_log_helper::worker_loop()
// 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
auto now = details::os::now();
handle_flush_interval(now, last_flush);
sleep_or_yield(now, last_pop);
return !_terminate_requested;
}
// flush all sinks if _flush_interval_ms has expired
inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point &now, log_clock::time_point &last_flush)
{
auto should_flush =
_flush_requested || (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms);
if (should_flush)
{ {
case async_msg_type::flush:
flush_sinks();
return true;
case async_msg_type::terminate:
flush_sinks();
return false;
default:
log_msg incoming_log_msg;
incoming_async_msg.fill_log_msg(incoming_log_msg, &_logger_name);
_formatter->format(incoming_log_msg);
for (auto &s : _sinks) for (auto &s : _sinks)
{ {
s->flush(); if (s->should_log(incoming_log_msg.level))
{
try
{
s->log(incoming_log_msg);
}
SPDLOG_CATCH_AND_HANDLE
}
} }
now = last_flush = details::os::now(); handle_flush_interval();
_flush_requested = false; 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)
@@ -346,48 +297,37 @@ inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_f
_formatter = std::move(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 std::chrono::microseconds;
using std::chrono::milliseconds;
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 details::os::sleep_for_millis(20);
}
// sleep for 500 ms
return details::os::sleep_for_millis(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 = std::move(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)
{
try
{
s->flush();
}
SPDLOG_CATCH_AND_HANDLE
}
_last_flush = os::now();
}

View File

@@ -21,8 +21,8 @@ inline spdlog::async_logger::async_logger(const std::string &logger_name, const
const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb, 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 std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb)
: logger(logger_name, begin, end) : logger(logger_name, begin, end)
, _async_log_helper(new details::async_log_helper( , _async_log_helper(new details::async_log_helper(logger_name, _formatter, _sinks, queue_size, _err_handler, overflow_policy,
_formatter, _sinks, queue_size, _err_handler, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb)) worker_warmup_cb, flush_interval_ms, worker_teardown_cb))
{ {
} }
@@ -44,7 +44,7 @@ inline spdlog::async_logger::async_logger(const std::string &logger_name, sink_p
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
@@ -80,7 +80,7 @@ inline void spdlog::async_logger::_sink_it(details::log_msg &msg)
_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)

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>
@@ -69,15 +68,7 @@ inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Ar
#endif #endif
_sink_it(log_msg); _sink_it(log_msg);
} }
catch (const std::exception &ex) SPDLOG_CATCH_AND_HANDLE
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception in logger " + _name);
throw;
}
} }
template<typename... Args> template<typename... Args>
@@ -93,15 +84,7 @@ inline void spdlog::logger::log(level::level_enum lvl, const char *msg)
log_msg.raw << msg; log_msg.raw << msg;
_sink_it(log_msg); _sink_it(log_msg);
} }
catch (const std::exception &ex) SPDLOG_CATCH_AND_HANDLE
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception in logger " + _name);
throw;
}
} }
template<typename T> template<typename T>
@@ -117,15 +100,7 @@ inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
log_msg.raw << msg; log_msg.raw << msg;
_sink_it(log_msg); _sink_it(log_msg);
} }
catch (const std::exception &ex) SPDLOG_CATCH_AND_HANDLE
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception in logger " + _name);
throw;
}
} }
template<typename Arg1, typename... Args> template<typename Arg1, typename... Args>
@@ -332,10 +307,14 @@ inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter)
inline void spdlog::logger::flush() inline void spdlog::logger::flush()
{ {
for (auto &sink : _sinks) try
{ {
sink->flush(); for (auto &sink : _sinks)
{
sink->flush();
}
} }
SPDLOG_CATCH_AND_HANDLE
} }
inline void spdlog::logger::_default_err_handler(const std::string &msg) inline void spdlog::logger::_default_err_handler(const std::string &msg)
@@ -345,13 +324,11 @@ inline void spdlog::logger::_default_err_handler(const std::string &msg)
{ {
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::default_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)

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,183 +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;
explicit 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_;
}
mpmc_bounded_queue(mpmc_bounded_queue const &) = delete;
void operator=(mpmc_bounded_queue const &) = delete;
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;
using cacheline_pad_t = char[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_;
};
} // namespace details
} // namespace spdlog

View File

@@ -237,7 +237,7 @@ inline size_t filesize(FILE *f)
#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(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__APPLE__) && !defined(__HAIKU__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__)
struct stat64 st; struct stat64 st;
if (fstat64(fd, &st) == 0) if (fstat64(fd, &st) == 0)
{ {
@@ -282,7 +282,7 @@ inline int utc_minutes_offset(const std::tm &tm = details::os::localtime())
return offset; return offset;
#else #else
#if defined(sun) || defined(__sun) #if defined(sun) || defined(__sun) || defined(_AIX)
// '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
{ {
@@ -383,54 +383,6 @@ inline std::string filename_to_str(const filename_t &filename)
} }
#endif #endif
inline std::string errno_to_string(char[256], char *res)
{
return std::string(res);
}
inline std::string errno_to_string(char buf[256], int res)
{
if (res == 0)
{
return std::string(buf);
}
return "Unknown error";
}
// Return errno string (thread safe)
inline std::string errno_str(int err_num)
{
char buf[256];
SPDLOG_CONSTEXPR auto buf_size = sizeof(buf);
#ifdef _WIN32
if (strerror_s(buf, buf_size, err_num) == 0)
{
return std::string(buf);
}
else
{
return "Unknown error";
}
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \
((_POSIX_C_SOURCE >= 200112L) && !defined(_GNU_SOURCE)) // posix version
if (strerror_r(err_num, buf, buf_size) == 0)
{
return std::string(buf);
}
else
{
return "Unknown error";
}
#else // gnu version (might not use the given buf, so its retval pointer must be used)
auto err = strerror_r(err_num, buf, buf_size); // let compiler choose type
return errno_to_string(buf, err); // use overloading to select correct stringify function
#endif
}
inline int pid() inline int pid()
{ {

View File

@@ -62,6 +62,12 @@ public:
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex); std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
_sinks.erase(std::remove(_sinks.begin(), _sinks.end(), sink), _sinks.end()); _sinks.erase(std::remove(_sinks.begin(), _sinks.end(), sink), _sinks.end());
} }
void remove_all_sinks()
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
_sinks.clear();
}
}; };
using dist_sink_mt = dist_sink<std::mutex>; using dist_sink_mt = dist_sink<std::mutex>;

View File

@@ -104,7 +104,7 @@ private:
void _print_range(const details::log_msg &msg, size_t start, size_t end) void _print_range(const details::log_msg &msg, size_t start, size_t end)
{ {
DWORD size = static_cast<DWORD>(end - start); DWORD size = static_cast<DWORD>(end - start);
WriteConsoleA(out_handle_, msg.formatted.data() + start, size, nullptr, nullptr); WriteFile(out_handle_, msg.formatted.data() + start, size, nullptr, nullptr);
} }
}; };

View File

@@ -7,7 +7,8 @@ set(SPDLOG_UTESTS_SOURCES
file_helper.cpp file_helper.cpp
file_log.cpp file_log.cpp
test_misc.cpp test_misc.cpp
test_pattern_formatter test_pattern_formatter.cpp
test_async.cpp
includes.h includes.h
registry.cpp registry.cpp
test_macros.cpp test_macros.cpp

View File

@@ -7,14 +7,17 @@
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 &) 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
{
throw std::runtime_error("some error happened during flush");
}
}; };
using namespace std;
TEST_CASE("default_error_handler", "[errors]]") TEST_CASE("default_error_handler", "[errors]]")
{ {
prepare_logdir(); prepare_logdir();
@@ -44,7 +47,7 @@ TEST_CASE("custom_error_handler", "[errors]]")
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) { throw custom_ex(); }); logger->set_error_handler([=](const std::string &) { 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);
@@ -57,12 +60,19 @@ TEST_CASE("custom_error_handler", "[errors]]")
TEST_CASE("default_error_handler2", "[errors]]") 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) { throw custom_ex(); }); logger->set_error_handler([=](const std::string &) { throw custom_ex(); });
REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex); REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex);
} }
TEST_CASE("flush_error_handler", "[errors]]")
{
spdlog::drop_all();
auto logger = spdlog::create<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string &) { throw custom_ex(); });
REQUIRE_THROWS_AS(logger->flush(), custom_ex);
}
TEST_CASE("async_error_handler", "[errors]]") TEST_CASE("async_error_handler", "[errors]]")
{ {
prepare_logdir(); prepare_logdir();
@@ -71,7 +81,7 @@ 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 &) {
std::ofstream ofs("logs/custom_err.txt"); std::ofstream ofs("logs/custom_err.txt");
if (!ofs) if (!ofs)
throw std::runtime_error("Failed open logs/custom_err.txt"); throw std::runtime_error("Failed open logs/custom_err.txt");
@@ -99,7 +109,7 @@ 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 &) {
std::ofstream ofs("logs/custom_err2.txt"); std::ofstream ofs("logs/custom_err2.txt");
if (!ofs) if (!ofs)
throw std::runtime_error("Failed open logs/custom_err2.txt"); throw std::runtime_error("Failed open logs/custom_err2.txt");
@@ -108,6 +118,7 @@ TEST_CASE("async_error_handler2", "[errors]]")
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();
logger.reset();
} }
REQUIRE(file_contents("logs/custom_err2.txt") == err_msg); REQUIRE(file_contents("logs/custom_err2.txt") == err_msg);

View File

@@ -50,7 +50,7 @@ TEST_CASE("flush_on", "[flush_on]]")
TEST_CASE("rotating_file_logger1", "[rotating_logger]]") TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
{ {
prepare_logdir(); prepare_logdir();
std::string basename = "logs/rotating_log"; std::string basename = "logs/rotating_log_A";
auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0); auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0);
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; ++i)
@@ -70,8 +70,9 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
TEST_CASE("rotating_file_logger2", "[rotating_logger]]") TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
{ {
prepare_logdir(); prepare_logdir();
std::string basename = "logs/rotating_log"; size_t max_size = 10 * 1024;
auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 1); std::string basename = "logs/rotating_log.txt";
auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 1);
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i); logger->info("Test message {}", i);
@@ -88,9 +89,9 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
} }
logger->flush(); logger->flush();
REQUIRE(get_filesize(filename) <= 1024); REQUIRE(get_filesize(filename) <= max_size);
auto filename1 = basename + ".1"; auto filename1 = "logs/rotating_log.1.txt";
REQUIRE(get_filesize(filename1) <= 1024); REQUIRE(get_filesize(filename1) <= max_size);
} }
TEST_CASE("daily_logger", "[daily_logger]]") TEST_CASE("daily_logger", "[daily_logger]]")

134
tests/test_async.cpp Normal file
View File

@@ -0,0 +1,134 @@
#include "includes.h"
#include "test_sink.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() - strlen(spdlog::details::os::default_eol));
}
TEST_CASE("basic async test ", "[async]")
{
auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
size_t queue_size = 128;
size_t messages = 256;
auto logger = spdlog::create_async("as", test_sink, queue_size, spdlog::async_overflow_policy::block_retry);
for (size_t i = 0; i < messages; i++)
{
logger->info("Hello message #{}", i);
}
// the dtor wait for all messages in the queue to get processed
logger.reset();
spdlog::drop("as");
REQUIRE(test_sink->msg_counter() == messages);
REQUIRE(test_sink->flushed_msg_counter() == messages);
}
TEST_CASE("discard policy ", "[async]")
{
auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
size_t queue_size = 2;
size_t messages = 1024;
spdlog::drop("as");
auto logger = spdlog::create_async("as", test_sink, queue_size, spdlog::async_overflow_policy::discard_log_msg);
for (size_t i = 0; i < messages; i++)
{
logger->info("Hello message #{}", i);
}
// the dtor wait for all messages in the queue to get processed
logger.reset();
spdlog::drop("as");
REQUIRE(test_sink->msg_counter() < messages);
REQUIRE(test_sink->flushed_msg_counter() < messages);
}
TEST_CASE("flush", "[async]")
{
auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
size_t queue_size = 256;
size_t messages = 256;
spdlog::drop("as");
auto logger = std::make_shared<spdlog::async_logger>("as", test_sink, queue_size);
for (size_t i = 0; i < messages; i++)
{
logger->info("Hello message #{}", i);
}
// the dtor wait for all messages in the queue to get processed
logger->flush();
std::this_thread::sleep_for(std::chrono::milliseconds(250));
REQUIRE(test_sink->msg_counter() == messages);
REQUIRE(test_sink->flushed_msg_counter() == messages);
REQUIRE(test_sink->flushed_msg_counter() == messages);
}
TEST_CASE("multi threads", "[async]")
{
auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
size_t queue_size = 128;
size_t messages = 256;
size_t n_threads = 10;
auto logger = std::make_shared<spdlog::async_logger>("as", test_sink, queue_size);
std::vector<std::thread> threads;
for (size_t i = 0; i < n_threads; i++)
{
threads.emplace_back([logger, messages] {
for (size_t j = 0; j < messages; j++)
{
logger->info("Hello message #{}", j);
}
});
}
for (auto &t : threads)
{
t.join();
}
// the dtor wait for all messages in the queue to get processed
logger.reset();
REQUIRE(test_sink->msg_counter() == messages * n_threads);
REQUIRE(test_sink->flushed_msg_counter() == messages * n_threads);
}
TEST_CASE("to_file", "[async]")
{
prepare_logdir();
size_t queue_size = 512;
size_t messages = 512;
size_t n_threads = 4;
auto file_sink = std::make_shared<spdlog::sinks::simple_file_sink_mt>("logs/async_test.log", true);
auto logger = spdlog::create_async("as", file_sink, queue_size);
std::vector<std::thread> threads;
for (size_t i = 0; i < n_threads; i++)
{
threads.emplace_back([logger, messages] {
for (size_t j = 0; j < messages; j++)
{
logger->info("Hello message #{}", j);
}
});
}
for (auto &t : threads)
{
t.join();
}
logger.reset();
spdlog::drop("as");
REQUIRE(count_lines("logs/async_test.log") == messages * n_threads);
}

48
tests/test_sink.h Normal file
View File

@@ -0,0 +1,48 @@
//
// Copyright(c) 2018 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#include "spdlog/details/null_mutex.h"
#include "spdlog/sinks/base_sink.h"
#include <mutex>
namespace spdlog {
namespace sinks {
template<class Mutex>
class test_sink : public base_sink<Mutex>
{
public:
size_t msg_counter()
{
return msg_counter_;
}
size_t flushed_msg_counter()
{
return flushed_msg_counter_;
}
protected:
void _sink_it(const details::log_msg &) override
{
msg_counter_++;
}
void _flush() override
{
flushed_msg_counter_ += msg_counter_;
}
size_t msg_counter_{0};
size_t flushed_msg_counter_{0};
};
using test_sink_mt = test_sink<std::mutex>;
using test_sink_st = test_sink<details::null_mutex>;
} // namespace sinks
} // namespace spdlog

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 2015 # Visual Studio 15
VisualStudioVersion = 14.0 VisualStudioVersion = 15.0.27428.2037
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests", "tests.vcxproj", "{59A07559-5F38-4DD6-A7FA-DB4153690B42}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests", "tests.vcxproj", "{59A07559-5F38-4DD6-A7FA-DB4153690B42}"
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "spdlog", "spdlog", "{7EFD7EC9-512F-4B35-ADFE-49863B2C04A5}"
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", "{AAD3C108-4E24-4AA8-BA09-4C9A75A881B3}"
ProjectSection(SolutionItems) = preProject
..\include\spdlog\contrib\README.md = ..\include\spdlog\contrib\README.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sinks", "sinks", "{3FB7E4DF-2397-463E-BDAD-32854AB66DFD}"
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", "{97DED9BF-821E-4A7A-8D13-ED9A739E1F55}"
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", "{0B649723-CF78-47C0-B1CA-1F173DDBFED4}"
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", "{1FBA69C4-7EAA-4D60-BCF9-3D59D5A88D32}"
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", "{278CDF3C-6E6D-4FAF-AF79-C1806101B4CB}"
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
@@ -25,4 +98,15 @@ Global
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
EndGlobalSection EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{AAD3C108-4E24-4AA8-BA09-4C9A75A881B3} = {7EFD7EC9-512F-4B35-ADFE-49863B2C04A5}
{3FB7E4DF-2397-463E-BDAD-32854AB66DFD} = {AAD3C108-4E24-4AA8-BA09-4C9A75A881B3}
{97DED9BF-821E-4A7A-8D13-ED9A739E1F55} = {7EFD7EC9-512F-4B35-ADFE-49863B2C04A5}
{0B649723-CF78-47C0-B1CA-1F173DDBFED4} = {7EFD7EC9-512F-4B35-ADFE-49863B2C04A5}
{1FBA69C4-7EAA-4D60-BCF9-3D59D5A88D32} = {0B649723-CF78-47C0-B1CA-1F173DDBFED4}
{278CDF3C-6E6D-4FAF-AF79-C1806101B4CB} = {7EFD7EC9-512F-4B35-ADFE-49863B2C04A5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {25A6A4D3-9B25-4071-81B4-99DFDD066255}
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>
@@ -27,26 +27,26 @@
<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>v140</PlatformToolset> <PlatformToolset>v141</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>MultiByte</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries> <UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset> <PlatformToolset>v141</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>MultiByte</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>v140</PlatformToolset> <PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization> <WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>MultiByte</CharacterSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType> <ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries> <UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset> <PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization> <WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet> <CharacterSet>MultiByte</CharacterSet>
</PropertyGroup> </PropertyGroup>
@@ -129,6 +129,7 @@
<ClCompile Include="errors.cpp" /> <ClCompile Include="errors.cpp" />
<ClCompile Include="file_helper.cpp" /> <ClCompile Include="file_helper.cpp" />
<ClCompile Include="file_log.cpp" /> <ClCompile Include="file_log.cpp" />
<ClCompile Include="test_async.cpp" />
<ClCompile Include="test_misc.cpp" /> <ClCompile Include="test_misc.cpp" />
<ClCompile Include="main.cpp" /> <ClCompile Include="main.cpp" />
<ClCompile Include="registry.cpp" /> <ClCompile Include="registry.cpp" />

View File

@@ -30,9 +30,6 @@
<ClCompile Include="utils.cpp"> <ClCompile Include="utils.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="errors.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="test_macros.cpp"> <ClCompile Include="test_macros.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
@@ -42,6 +39,12 @@
<ClCompile Include="test_misc.cpp"> <ClCompile Include="test_misc.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="errors.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="test_async.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="includes.h"> <ClInclude Include="includes.h">

View File

@@ -8,6 +8,7 @@ void prepare_logdir()
system("del /F /Q logs\\*"); system("del /F /Q logs\\*");
#else #else
auto rv = system("mkdir -p logs"); auto rv = system("mkdir -p logs");
(void)rv;
rv = system("rm -f logs/*"); rv = system("rm -f logs/*");
(void)rv; (void)rv;
#endif #endif