Compare commits

..

152 Commits

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

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

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

_filelength() just works for both WinXP SDK and later Win7+ 32bit targets.
2016-09-20 14:13:15 +04:30
gabime
1f1f6a5f3b Support bench under OSX 2016-09-18 02:28:41 +03:00
gabime
214c67788f add async with null sink bench 2016-09-18 01:51:53 +03:00
gabime
e97621d61d Removed force_flush arg from everywhere. Use flush_on(level) instead 2016-09-18 00:43:42 +03:00
gabime
b0f8230783 Fixed bug in basic_logger creating (passed truncate to force_flush arg) 2016-09-18 00:14:56 +03:00
Gabi Melman
9c04504881 Update README.md 2016-09-16 02:58:02 +03:00
gabime
5653e5c9d7 astyle 2016-09-15 00:38:21 +03:00
gabime
6312748cc7 updated bundled fmt to version 1fb0586b065c4202e976528a6bdc6384dc56dc04 2016-09-15 00:35:51 +03:00
Gabi Melman
87d79eb95d Merge pull request #277 from hghwng/master
Fix compilation on Android.
2016-09-14 17:55:43 +03:00
Hugh Wang
96267654e6 Add Android examples. 2016-09-14 17:39:15 +08:00
Hugh Wang
72a6fd65da Support direct creation of android logger. 2016-09-14 17:37:06 +08:00
Hugh Wang
bf02f57475 Fix Android sink.
1. Remove lock.
2. Improve error detection.
3. Remove unsupported log levels.
2016-09-14 17:37:06 +08:00
Hugh Wang
f2c9692438 Fix compilation on Android. 2016-09-14 17:37:06 +08:00
Gabi Melman
59bf37f769 Merge pull request #279 from facontidavide/loglevel_per_sink
Loglevel per sink. Thanks @facontidavide
2016-09-13 00:04:59 +03:00
davide
d79af47a28 added example 2016-09-12 22:28:37 +02:00
davide
b67076fc85 added level_t to sink. Improves the flexibility of loggers with multiple sinks 2016-09-12 22:26:38 +02:00
Gabi Melman
c69df8ae44 Update spdlog.h 2016-09-03 14:15:09 +03:00
Gabi Melman
b18d235b63 Update spdlog.h 2016-09-03 14:08:55 +03:00
Gabi Melman
01ef3d3114 Update async_logger_impl.h 2016-09-03 01:35:10 +03:00
Gabi Melman
d01e288afc fix flush async flush 2016-09-03 01:33:07 +03:00
gabime
3ee1bab316 added std:: to vector decl in bench 2016-09-02 17:24:40 +03:00
gabime
c3757c99d5 remoed zf_log from bench 2016-09-02 17:23:40 +03:00
gabime
2d64a1de1c fixed bench compilation 2016-09-02 17:16:02 +03:00
gabime
0d35df584c removed zf_log from bench 2016-09-02 17:14:48 +03:00
gabime
3afabcd17e astyle 2016-09-02 17:06:00 +03:00
gabime
d2a367fa7c Fixed tests after remove of force_flush arg 2016-09-02 16:59:55 +03:00
gabime
e562e001cf Removed force_flush arg from API (use flush_on(level) instead) 2016-09-02 16:59:15 +03:00
gabime
1df30a0733 support flush_on(..) in async loggers too 2016-09-02 16:19:29 +03:00
gabime
3a12f3c560 fix typo in example 2016-09-02 15:00:51 +03:00
Gabi Melman
f310cc460f Update README.md 2016-08-26 15:39:00 +03:00
Gabi Melman
2fa29987ca Update README.md 2016-08-26 14:16:36 +03:00
gabime
8cf39857ab style 2016-08-26 00:38:08 +03:00
gabime
cb75569541 Fixed issue #266 (Improperly-formatted ISO8601 UTC offset for negative-offset timezones) 2016-08-26 00:37:41 +03:00
Gabi Melman
bf327be7f5 Update README.md 2016-08-24 03:04:55 +03:00
Gabi Melman
6dfdaa3532 Update README.md 2016-08-24 03:03:58 +03:00
Gabi Melman
6e1bd45d66 Update README.md 2016-08-24 03:02:01 +03:00
Gabi Melman
cfcd44ca36 Update README.md 2016-08-24 03:00:55 +03:00
Gabi Melman
42dfa1975a Merge pull request #265 from sbrkopac/master
Added the ability to truncate the basic file logger.
2016-08-23 00:00:49 +03:00
Sam Brkopac
0be736c7fc Added the ability to truncate the basic file logger.
Added the ability to truncate the basic file logger.
2016-08-22 13:31:43 -07:00
gabime
332b7c0d7f astyle 2016-08-22 22:09:23 +03:00
gabime
4f52cc4dec async flush now waits for queue to be empty before returning 2016-08-22 22:07:29 +03:00
gabime
dfa2c7a950 async flush now waits for queue to be empty before returning 2016-08-22 21:48:57 +03:00
Gabi Melman
73a3a32325 Update README.md 2016-08-22 21:15:54 +03:00
Gabi Melman
742922f029 Update README.md 2016-08-22 21:13:15 +03:00
gabime
b13735dc22 astyle 2016-08-22 20:54:18 +03:00
gabime
20cb73e9da disable syslog by default (enable by #define SPDLOG_ENABLE_SYSLOG ) 2016-08-22 20:52:16 +03:00
Gabi Melman
c68a0de2b6 Merge pull request #263 from jan-dx/master
Add support for Solaris operating system
2016-08-22 20:20:40 +03:00
Nazım Can Bedir
097ba5a359 Add basic support for Solaris. 2016-08-22 17:26:12 +03:00
Nazım Can Bedir
e277f9b05c Make syslog backend tweakable. 2016-08-22 16:39:46 +03:00
Nazım Can Bedir
2678c37b56 Move syslog support tests to one place. 2016-08-22 16:39:13 +03:00
gabime
e556daebc3 better support for thread id in FreeBSD 2016-08-21 01:36:27 +03:00
gabime
2705e35a8c updated example 2016-08-21 01:21:18 +03:00
gabime
86de264da9 Added support for syslog in FreeBSD 2016-08-21 01:16:04 +03:00
gabime
5b2bd79b7e Added support for syslog in FreeBSD 2016-08-21 00:57:53 +03:00
gabime
aa0f62292b fixed dist_sink in gcc 2016-08-20 15:01:08 +03:00
gabime
916a686f8f astyle 2016-08-20 14:55:50 +03:00
gabime
2c32f826ab Merge branch 'master' of https://github.com/gabime/spdlog 2016-08-20 14:54:33 +03:00
gabime
c4298a989e dist sink cleanup 2016-08-20 14:52:26 +03:00
Gabi Melman
c16f89d044 Update README.md 2016-08-20 14:11:56 +03:00
Gabi Melman
f09e0bd047 Update README.md 2016-08-20 14:11:28 +03:00
Gabi Melman
23060c6c9d Update README.md 2016-08-20 14:10:38 +03:00
Gabi Melman
efc8de6d64 Update README.md 2016-08-20 14:01:40 +03:00
gabime
4efbd950d6 atyle 2016-08-20 13:55:50 +03:00
gabime
d8f01c3a72 added apply_al()l test to tests 2016-08-20 13:51:07 +03:00
gabime
3af247fbd3 Added a way to iterate all registered loggers (issues #238 and #259). 2016-08-20 13:37:36 +03:00
gabime
1c4da3eef3 fixed example async error 2016-08-20 13:26:13 +03:00
Gabi Melman
4fcde3b850 Update README.md 2016-08-05 14:27:58 +03:00
gabime
e7debaacd7 astyle 2016-08-05 04:43:20 +03:00
Gabi Melman
bdbe908693 Update README.md 2016-08-05 04:23:34 +03:00
gabime
39cdd08a54 no exceptions while logging 2016-08-05 03:56:40 +03:00
Gabi Melman
2fc332abdc Update README.md 2016-08-04 03:11:56 +03:00
Gabi Melman
f05451650d Merge pull request #255 from Cleroth/patch-1
m
2016-08-03 12:20:13 +03:00
Cleroth
9a888cde56 m 2016-08-03 10:43:33 +02:00
Gabi Melman
d9304f17f5 Merge pull request #254 from tony/freebsd-compat
Add FreeBSD compatibility defs
2016-08-01 12:40:58 +03:00
Tony Narlock
98af71c585 Add FreeBSD compatibility defs
fix some linux-isms for spdlog on freebsd systems

- FreeBSD uses plain-old ``stat`` (not ``stat64``)
- ``errno_str``'s else requires GNU extensions, not available by default
  on FreeBSD. The one used by apple systems is most correct
2016-08-01 00:19:35 -05:00
Gabi Melman
cee155c1dd typo in comment 2016-08-01 00:38:59 +03:00
60 changed files with 3188 additions and 1708 deletions

4
.gitignore vendored
View File

@@ -1,4 +1,4 @@
# Auto generated files
# Auto generated files
*.slo
*.lo
*.o
@@ -45,6 +45,8 @@ example/*
!example/example.sln
!example/example.vcxproj
!example/CMakeLists.txt
!example/multisink.cpp
!example/jni
# generated files
generated

View File

@@ -5,14 +5,19 @@
cmake_minimum_required(VERSION 3.1)
project(spdlog VERSION 1.0.0)
include(CTest)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
set(CMAKE_CXX_FLAGS "-Wall ${CMAKE_CXX_FLAGS}")
endif()
add_library(spdlog INTERFACE)
option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF)
option(SPDLOG_BUILD_TESTS "Build tests" OFF)
option(SPDLOG_BUILD_TESTING "Build spdlog tests" ON)
target_include_directories(
spdlog
@@ -23,12 +28,11 @@ target_include_directories(
set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include")
include(CTest)
if(SPDLOG_BUILD_EXAMPLES)
add_subdirectory(example)
endif()
if(SPDLOG_BUILD_TESTS)
if(SPDLOG_BUILD_TESTING)
add_subdirectory(tests)
endif()
@@ -78,3 +82,6 @@ install(
NAMESPACE "${namespace}"
DESTINATION "${config_install_dir}"
)
file(GLOB_RECURSE spdlog_include_SRCS "${HEADER_BASE}/*.h")
add_custom_target(spdlog_headers_for_ide SOURCES ${spdlog_include_SRCS})

183
README.md
View File

@@ -4,17 +4,29 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
## Install
Just copy the source [folder](https://github.com/gabime/spdlog/tree/master/include/spdlog) to your build tree and use a C++11 compiler
#### Just copy or clone the headers:
* Copy the source [folder](https://github.com/gabime/spdlog/tree/master/include/spdlog) to your build tree and use a C++11 compiler.
#### Or use your favourite package manager:
* Ubuntu: `apt-get install libspdlog-dev`
* Homebrew: `brew install spdlog`
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
* Fedora: `yum install spdlog`
* Arch Linux: `pacman -S spdlog-git`
* vcpkg: `vcpkg install spdlog`
## Platforms
* Linux (gcc 4.8.1+, clang 3.5+)
* Windows (visual studio 2013+, cygwin/mingw with g++ 4.9.1+)
* Linux, FreeBSD, Solaris
* Windows (vc 2013+, cygwin/mingw)
* Mac OSX (clang 3.5+)
* Android
##Features
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
* Headers only.
* No dependencies - 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.
* Extremely fast asynchronous mode (optional) - using lockfree queues and other tricks to reach millions of calls/sec.
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
@@ -23,7 +35,8 @@ Just copy the source [folder](https://github.com/gabime/spdlog/tree/master/inclu
* Rotating log files.
* Daily log files.
* Console logging (colors supported).
* Linux syslog.
* syslog.
* Windows debugger (```OutputDebugString(..)```)
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
* Severity based filtering - threshold levels can be modified in runtime as well as in compile time.
@@ -57,95 +70,147 @@ Time needed to log 1,000,000 lines in asynchronous mode, i.e. the time it takes
## Usage Example
```c++
#include <iostream>
#include "spdlog/spdlog.h"
int main(int, char* [])
#include <iostream>
#include <memory>
void async_example();
void syslog_example();
void user_defined_example();
void err_handler_example();
namespace spd = spdlog;
int main(int, char*[])
{
namespace spd = spdlog;
try
{
// console logger (multithreaded and with color)
auto console = spd::stdout_logger_mt("console", true);
console->info("Welcome to spdlog!") ;
console->info("An info message example {}..", 1);
// 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->info("Easy padding in numbers like {:08d}", 12);
console->info("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
// Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12);
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
console->info("Support for floats {:03.2f}", 1.23456);
console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");
console->info("{:>30}", "right aligned");
console->info("{:^30}", "centered");
//
// Runtime log levels
//
spd::set_level(spd::level::info); //Set global log level to info
console->debug("This message shold not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("Now it should..");
//
// Create a basic multithreaded file logger (or "basic_logger_st" for single threaded logger)
//
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.txt");
my_logger->info("Some log message");
//
// Create a file rotating logger with 5mb size max and 3 rotated files
//
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3);
for(int i = 0; i < 10; ++i)
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
for (int i = 0; i < 10; ++i)
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
//
// Create a daily logger - a new file is created every day on 2:30am
//
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
// trigger flush if the log severity is error or higher
daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44);
//
// Customize msg format for all messages
//
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
rotating_logger->info("This is another message with custom format");
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
//
// Compile time debug or trace macros.
// Enabled #ifdef SPDLOG_DEBUG_ON or #ifdef SPDLOG_TRACE_ON
//
// Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info
console->debug("This message shold not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message shold be displayed..");
// Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
//
// Asynchronous logging is very fast..
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
//
size_t q_size = 8192; //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");
async_file->info("This is async log..Should be very fast!");
async_example();
//
// syslog example. linux only..
//
#ifdef __linux__
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. This is Linux only!");
#endif
// 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 failed: " << ex.what() << std::endl;
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
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
}
// 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()
{
spdlog::set_error_handler([](const std::string& msg) {
std::cerr << "my err handler: " << msg << std::endl;
});
// (or logger->set_error_handler(..) to set for specific logger)
}
```

View File

@@ -1,9 +1,9 @@
CXX ?= g++
CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -I../include
CXX_RELEASE_FLAGS = -O3 -flto -DNDEBUG
binaries=spdlog-bench spdlog-bench-mt spdlog-async zf_log-bench zf_log-bench-mt boost-bench boost-bench-mt glog-bench glog-bench-mt g2log-async easylogging-bench easylogging-bench-mt
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
all: $(binaries)
@@ -17,14 +17,11 @@ spdlog-async: spdlog-async.cpp
$(CXX) spdlog-async.cpp -o spdlog-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
ZF_LOG_FLAGS = -I../../zf_log.git/zf_log/
zf_log-bench: zf_log-bench.cpp
$(CXX) zf_log-bench.cpp -o zf_log-bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS) $(ZF_LOG_FLAGS)
spdlog-null-async: spdlog-null-async.cpp
$(CXX) spdlog-null-async.cpp -o spdlog-null-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
zf_log-bench-mt: zf_log-bench-mt.cpp
$(CXX) zf_log-bench-mt.cpp -o zf_log-bench-mt $(CXXFLAGS) $(CXX_RELEASE_FLAGS) $(ZF_LOG_FLAGS)
BOOST_FLAGS = -DBOOST_LOG_DYN_LINK -I/usr/include -lboost_log -lboost_log_setup -lboost_filesystem -lboost_system -lboost_thread -lboost_regex -lboost_date_time -lboost_chrono
boost-bench: boost-bench.cpp

View File

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

32
bench/latency/Makefile Normal file
View File

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

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

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

View File

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

View File

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

View File

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

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

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

View File

@@ -28,7 +28,7 @@ int main(int argc, char* argv[])
logger->set_pattern("[%Y-%b-%d %T.%e]: %v");
std::atomic<int > msg_counter {0};
vector<thread> threads;
std::vector<thread> threads;
for (int t = 0; t < thread_count; ++t)
{

112
bench/spdlog-null-async.cpp Normal file
View File

@@ -0,0 +1,112 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
//
// bench.cpp : spdlog benchmarks
//
#include <atomic>
#include <cstdlib> // EXIT_FAILURE
#include <iostream>
#include <memory>
#include <string>
#include <thread>
#include "spdlog/spdlog.h"
#include "spdlog/async_logger.h"
#include "spdlog/sinks/null_sink.h"
#include "utils.h"
using namespace std;
using namespace std::chrono;
using namespace spdlog;
using namespace spdlog::sinks;
using namespace utils;
size_t bench_as(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
int main(int argc, char* argv[])
{
int queue_size = 1048576;
int howmany = 1000000;
int threads = 10;
int iters = 10;
try
{
if(argc > 1)
howmany = atoi(argv[1]);
if (argc > 2)
threads = atoi(argv[2]);
if (argc > 3)
queue_size = atoi(argv[3]);
cout << "\n*******************************************************************************\n";
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " messages " << endl;
cout << "*******************************************************************************\n";
spdlog::set_async_mode(queue_size);
size_t total_rate = 0;
for(int i = 0; i < iters; ++i)
{
//auto as = spdlog::daily_logger_st("as", "logs/daily_async");
auto as = spdlog::create<null_sink_st>("async(null-sink)");
total_rate+= bench_as(howmany, as, threads);
spdlog::drop("async(null-sink)");
}
std::cout << endl;
std::cout << "Avg rate: " << format(total_rate/iters) << "/sec" <<std::endl;
}
catch (std::exception &ex)
{
std::cerr << "Error: " << ex.what() << std::endl;
perror("Last error");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
//return rate/sec
size_t bench_as(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
{
cout << log->name() << "...\t\t" << flush;
std::atomic<int > msg_counter {0};
vector<thread> threads;
auto start = system_clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
for(;;)
{
int counter = ++msg_counter;
if (counter > howmany) break;
log->info("Hello logger: msg number {}", counter);
}
}));
}
for(auto &t:threads)
{
t.join();
};
auto delta = system_clock::now() - start;
auto delta_d = duration_cast<duration<double>> (delta).count();
auto per_sec = size_t(howmany / delta_d);
cout << format(per_sec) << "/sec" << endl;
return per_sec;
}

35
bench/utils.h Normal file
View File

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

View File

@@ -1,56 +0,0 @@
#include <thread>
#include <vector>
#include <atomic>
#include <cstdlib>
#include <unistd.h>
#include <fcntl.h>
#include <zf_log.c>
const char g_path[] = "logs/zf_log.txt";
int g_fd;
static void output_callback(zf_log_message *msg)
{
*msg->p = '\n';
write(g_fd, msg->buf, msg->p - msg->buf + 1);
}
using namespace std;
int main(int argc, char* argv[])
{
g_fd = open(g_path, O_APPEND|O_CREAT|O_WRONLY);
if (0 > g_fd)
{
ZF_LOGE_AUX(ZF_LOG_STDERR, "Failed to open log file: %s", g_path);
return -1;
}
zf_log_set_output_callback(ZF_LOG_PUT_STD, output_callback);
int thread_count = 10;
if(argc > 1)
thread_count = std::atoi(argv[1]);
int howmany = 1000000;
std::atomic<int > msg_counter {0};
vector<thread> threads;
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]()
{
while (true)
{
int counter = ++msg_counter;
if (counter > howmany) break;
ZF_LOGI("zf_log message #%i: This is some text for your pleasure", counter);
}
}));
}
for (auto &t:threads)
{
t.join();
};
close(g_fd);
return 0;
}

View File

@@ -1,28 +0,0 @@
#include <stdio.h>
#include <zf_log.c>
const char g_path[] = "logs/zf_log.txt";
static FILE *g_f;
static void output_callback(zf_log_message *msg)
{
*msg->p = '\n';
fwrite(msg->buf, msg->p - msg->buf + 1, 1, g_f);
}
int main(int, char* [])
{
g_f = fopen(g_path, "wb");
if (!g_f)
{
ZF_LOGE_AUX(ZF_LOG_STDERR, "Failed to open log file: %s", g_path);
return -1;
}
zf_log_set_output_callback(ZF_LOG_PUT_STD, output_callback);
const int howmany = 1000000;
for(int i = 0 ; i < howmany; ++i)
ZF_LOGI("zf_log message #%i: This is some text for your pleasure", i);
fclose(g_f);
return 0;
}

View File

@@ -40,6 +40,9 @@ target_link_libraries(example spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT})
add_executable(benchmark bench.cpp)
target_link_libraries(benchmark spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT})
add_executable(multisink multisink.cpp)
target_link_libraries(multisink spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT})
enable_testing()
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
add_test(NAME RunExample COMMAND example)

View File

@@ -35,7 +35,6 @@ int main(int argc, char* argv[])
int queue_size = 1048576;
int howmany = 1000000;
int threads = 10;
bool auto_flush = false;
int file_size = 30 * 1024 * 1024;
int rotating_files = 5;
@@ -51,29 +50,29 @@ int main(int argc, char* argv[])
cout << "*******************************************************************************\n";
cout << "Single thread, " << format(howmany) << " iterations, auto flush=" << auto_flush << endl;
cout << "Single thread, " << format(howmany) << " iterations" << endl;
cout << "*******************************************************************************\n";
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st", file_size, rotating_files, auto_flush);
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st", file_size, rotating_files);
bench(howmany, rotating_st);
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st", auto_flush);
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st");
bench(howmany, daily_st);
bench(howmany, spdlog::create<null_sink_st>("null_st"));
cout << "\n*******************************************************************************\n";
cout << threads << " threads sharing same logger, " << format(howmany) << " iterations, auto_flush=" << auto_flush << endl;
cout << threads << " threads sharing same logger, " << format(howmany) << " iterations" << endl;
cout << "*******************************************************************************\n";
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt", file_size, rotating_files, auto_flush);
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt", file_size, rotating_files);
bench_mt(howmany, rotating_mt, threads);
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt", auto_flush);
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt");
bench_mt(howmany, daily_mt, threads);
bench(howmany, spdlog::create<null_sink_st>("null_mt"));
cout << "\n*******************************************************************************\n";
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations, auto_flush=" << auto_flush << endl;
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations " << endl;
cout << "*******************************************************************************\n";
@@ -81,7 +80,7 @@ int main(int argc, char* argv[])
for(int i = 0; i < 3; ++i)
{
auto as = spdlog::daily_logger_st("as", "logs/daily_async", auto_flush);
auto as = spdlog::daily_logger_st("as", "logs/daily_async");
bench_mt(howmany, as, threads);
spdlog::drop("as");
}

View File

@@ -5,49 +5,43 @@
//
// spdlog usage example
//
//
#include "spdlog/spdlog.h"
#include <cstdlib> // EXIT_FAILURE
#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
{
// Multithreaded color console
auto console = spd::stdout_logger_mt("console", true);
// Console logger with color
auto console = spd::stdout_color_mt("console");
console->info("Welcome to spdlog!");
console->error("An info message example {}..", 1);
console->error("Some error message with arg{}..", 1);
// Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12);
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
console->info("Support for floats {:03.2f}", 1.23456);
console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");
console->info("{:>30}", "right aligned");
console->info("{:^30}", "centered");
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
// Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info
console->debug("This message shold not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message shold be displayed..");
// Create basic file logger (not rotated)
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt");
my_logger->info("Some log message");
// Create a file rotating logger with 5mb size max and 3 rotated files
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3);
for (int i = 0; i < 10; ++i)
@@ -55,6 +49,8 @@ int main(int, char*[])
// Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
// 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
@@ -62,8 +58,14 @@ int main(int, char*[])
rotating_logger->info("This is another message with custom format");
// Compile time debug or trace macros.
// Enabled #ifdef SPDLOG_DEBUG_ON or #ifdef SPDLOG_TRACE_ON
// Runtime log levels
spd::set_level(spd::level::info); //Set global log level to info
console->debug("This message shold not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message shold be displayed..");
// Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
@@ -71,42 +73,62 @@ int main(int, char*[])
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
async_example();
// syslog example. linux/osx only..
// syslog example. linux/osx only
syslog_example();
// log user-defined types example..
// android example. compile with NDK
android_example();
// Log user-defined types example
user_defined_example();
// Change default log error handler
err_handler_example();
// Apply a function on all registered loggers
spd::apply_all([&](std::shared_ptr<spdlog::logger> l)
{
l->info("End of example.");
});
// 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 failed: " << ex.what() << std::endl;
return EXIT_FAILURE;
std::cout << "Log init failed: " << ex.what() << std::endl;
return 1;
}
return EXIT_SUCCESS;
}
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 only)
//syslog example (linux/osx/freebsd)
void syslog_example()
{
#if defined (__linux__) || defined(__APPLE__)
#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. This is Linux only!");
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
}
@@ -127,3 +149,15 @@ 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

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

15
example/jni/Android.mk Normal file
View File

@@ -0,0 +1,15 @@
# Setup a project
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := example
LOCAL_SRC_FILES := example.cpp
LOCAL_CPPFLAGS += -Wall -Wshadow -Wextra -pedantic -std=c++11 -fPIE -pie
LOCAL_LDFLAGS += -fPIE -pie
# Add exception support and set path for spdlog's headers
LOCAL_CPPFLAGS += -fexceptions -I../include
# Use android's log library
LOCAL_LDFLAGS += -llog
include $(BUILD_EXECUTABLE)

View File

@@ -0,0 +1,2 @@
# Exceptions are used in spdlog. Link to an exception-ready C++ runtime.
APP_STL = gnustl_static

1
example/jni/example.cpp Symbolic link
View File

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

47
example/multisink.cpp Normal file
View File

@@ -0,0 +1,47 @@
#include "spdlog/spdlog.h"
#include <iostream>
#include <memory>
namespace spd = spdlog;
int main(int, char*[])
{
bool enable_debug = true;
try
{
// This other example use a single logger with 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.
std::vector<spdlog::sink_ptr> sinks;
sinks.push_back( std::make_shared<spdlog::sinks::stdout_sink_mt>() );
sinks.push_back( std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_regular_file.txt") );
sinks.push_back( std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_debug_file.txt") );
spdlog::logger console_multisink("multisink", sinks.begin(), sinks.end() );
console_multisink.set_level( spdlog::level::warn);
sinks[0]->set_level( spdlog::level::trace); // console. Allow everything. Default value
sinks[1]->set_level( spdlog::level::trace); // regular file. Allow everything. Default value
sinks[2]->set_level( spdlog::level::off); // regular file. Ignore everything.
console_multisink.warn("warn: will print only on console and regular file");
if( enable_debug )
{
console_multisink.set_level( spdlog::level::debug); // level of the logger
sinks[1]->set_level( spdlog::level::debug); // regular file
sinks[2]->set_level( spdlog::level::debug); // debug file
}
console_multisink.debug("Debug: you should see this on console and both files");
// 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;
}
}

View File

@@ -60,7 +60,8 @@ public:
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
const std::function<void()>& worker_teardown_cb = nullptr);
//Wait for the queue to be empty, and flush synchronously
//Warning: this can potentialy last forever as we wait it to complete
void flush() override;
protected:
void _sink_it(details::log_msg& msg) override;

View File

@@ -11,6 +11,8 @@
#include <memory>
#include <atomic>
#include <exception>
#include<functional>
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
#include <codecvt>
#include <locale>
@@ -28,11 +30,11 @@
#endif
#if defined(__GNUC__) || defined(__clang__)
#define DEPRECATED __attribute__((deprecated))
#define SPDLOG_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
#define DEPRECATED __declspec(deprecated)
#define SPDLOG_DEPRECATED __declspec(deprecated)
#else
#define DEPRECATED
#define SPDLOG_DEPRECATED
#endif
@@ -55,9 +57,10 @@ using formatter_ptr = std::shared_ptr<spdlog::formatter>;
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
using level_t = details::null_atomic_int;
#else
using level_t = std::atomic_int;
using level_t = std::atomic<int>;
#endif
using log_err_handler = std::function<void(const std::string &err_msg)>;
//Log level enum
namespace level

View File

@@ -67,7 +67,7 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
{}
async_msg(async_msg_type m_type) :msg_type(m_type)
{};
{}
async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT
{
@@ -82,7 +82,7 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
// never copy or assign. should only be moved..
async_msg(const async_msg&) = delete;
async_msg& operator=(async_msg& other) = delete;
async_msg& operator=(const async_msg& other) = delete;
// construct from log_msg
async_msg(const details::log_msg& m) :
@@ -120,6 +120,7 @@ public:
async_log_helper(formatter_ptr formatter,
const std::vector<sink_ptr>& sinks,
size_t queue_size,
const log_err_handler err_handler,
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
const std::function<void()>& worker_warmup_cb = nullptr,
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
@@ -132,7 +133,7 @@ public:
void set_formatter(formatter_ptr);
void flush();
void flush(bool wait_for_q);
private:
@@ -142,14 +143,13 @@ private:
// queue of messages to log
q_type _q;
log_err_handler _err_handler;
bool _flush_requested;
bool _terminate_requested;
// last exception thrown from the worker thread
std::shared_ptr<spdlog_ex> _last_workerthread_ex;
// overflow policy
const async_overflow_policy _overflow_policy;
@@ -167,9 +167,6 @@ private:
void push_msg(async_msg&& new_msg);
// throw last worker thread exception or if worker thread is not active
void throw_if_bad_worker();
// worker thread main loop
void worker_loop();
@@ -182,6 +179,9 @@ private:
// sleep,yield or return immediatly 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();
};
}
}
@@ -193,6 +193,7 @@ inline spdlog::details::async_log_helper::async_log_helper(
formatter_ptr formatter,
const std::vector<sink_ptr>& sinks,
size_t queue_size,
log_err_handler err_handler,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms,
@@ -200,6 +201,7 @@ inline spdlog::details::async_log_helper::async_log_helper(
_formatter(formatter),
_sinks(sinks),
_q(queue_size),
_err_handler(err_handler),
_flush_requested(false),
_terminate_requested(false),
_overflow_policy(overflow_policy),
@@ -227,13 +229,10 @@ inline spdlog::details::async_log_helper::~async_log_helper()
inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
{
push_msg(async_msg(msg));
}
inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg)
{
throw_if_bad_worker();
if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg)
{
auto last_op_time = details::os::now();
@@ -245,12 +244,14 @@ inline void spdlog::details::async_log_helper::push_msg(details::async_log_helpe
}
while (!_q.enqueue(std::move(new_msg)));
}
}
inline void spdlog::details::async_log_helper::flush()
// optionally wait for the queue be empty and request flush from the sinks
inline void spdlog::details::async_log_helper::flush(bool wait_for_q)
{
push_msg(async_msg(async_msg_type::flush));
if(wait_for_q)
wait_empty_q(); //return only make after the above flush message was processed
}
inline void spdlog::details::async_log_helper::worker_loop()
@@ -263,13 +264,13 @@ inline void spdlog::details::async_log_helper::worker_loop()
while(process_next_msg(last_pop, last_flush));
if (_worker_teardown_cb) _worker_teardown_cb();
}
catch (const std::exception& ex)
catch (const std::exception &ex)
{
_last_workerthread_ex = std::make_shared<spdlog_ex>(std::string("async_logger worker thread exception: ") + ex.what());
_err_handler(ex.what());
}
catch (...)
{
_last_workerthread_ex = std::make_shared<spdlog_ex>("async_logger worker thread exception");
_err_handler("Unknown exception");
}
}
@@ -277,10 +278,8 @@ inline void spdlog::details::async_log_helper::worker_loop()
// return true if this thread should still be active (while no terminate msg was received)
inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush)
{
async_msg incoming_async_msg;
if (_q.dequeue(incoming_async_msg))
{
last_pop = details::os::now();
@@ -300,7 +299,12 @@ inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_
incoming_async_msg.fill_log_msg(incoming_log_msg);
_formatter->format(incoming_log_msg);
for (auto &s : _sinks)
s->log(incoming_log_msg);
{
if(s->should_log( incoming_log_msg.level))
{
s->log(incoming_log_msg);
}
}
}
return true;
}
@@ -313,7 +317,6 @@ inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_
handle_flush_interval(now, last_flush);
sleep_or_yield(now, last_pop);
return !_terminate_requested;
}
}
@@ -351,8 +354,7 @@ inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_
// yield upto 150 micros
if (time_since_op <= microseconds(100))
return yield();
return std::this_thread::yield();
// sleep for 20 ms upto 200 ms
if (time_since_op <= milliseconds(200))
@@ -362,19 +364,15 @@ inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_
return sleep_for(milliseconds(200));
}
// throw if the worker thread threw an exception or not active
inline void spdlog::details::async_log_helper::throw_if_bad_worker()
// wait for the queue to be empty
inline void spdlog::details::async_log_helper::wait_empty_q()
{
if (_last_workerthread_ex)
auto last_op = details::os::now();
while (_q.approx_size() > 0)
{
auto ex = std::move(_last_workerthread_ex);
throw *ex;
sleep_or_yield(details::os::now(), last_op);
}
}

View File

@@ -26,18 +26,18 @@ inline spdlog::async_logger::async_logger(const std::string& logger_name,
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, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb))
_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,
sinks_init_list sinks,
sinks_init_list sinks_list,
size_t queue_size,
const async_overflow_policy overflow_policy,
const std::function<void()>& worker_warmup_cb,
const std::chrono::milliseconds& flush_interval_ms,
const std::function<void()>& worker_teardown_cb) :
async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
async_logger(logger_name, sinks_list.begin(), sinks_list.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
inline spdlog::async_logger::async_logger(const std::string& logger_name,
sink_ptr single_sink,
@@ -54,8 +54,7 @@ inline spdlog::async_logger::async_logger(const std::string& logger_name,
inline void spdlog::async_logger::flush()
{
_async_log_helper->flush();
_async_log_helper->flush(true);
}
inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)
@@ -73,5 +72,18 @@ inline void spdlog::async_logger::_set_pattern(const std::string& pattern)
inline void spdlog::async_logger::_sink_it(details::log_msg& msg)
{
_async_log_helper->log(msg);
try
{
_async_log_helper->log(msg);
if (_should_flush_on(msg))
_async_log_helper->flush(false); // do async flush
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
}

View File

@@ -31,9 +31,8 @@ public:
const int open_tries = 5;
const int open_interval = 10;
explicit file_helper(bool force_flush) :
_fd(nullptr),
_force_flush(force_flush)
explicit file_helper() :
_fd(nullptr)
{}
file_helper(const file_helper&) = delete;
@@ -91,16 +90,13 @@ public:
auto data = msg.formatted.data();
if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
if (_force_flush)
std::fflush(_fd);
}
size_t size()
{
if (!_fd)
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
@@ -117,7 +113,6 @@ public:
private:
FILE* _fd;
filename_t _filename;
bool _force_flush;
};
}
}

View File

@@ -6,35 +6,42 @@
#pragma once
#include <spdlog/logger.h>
#include <spdlog/sinks/stdout_sinks.h>
#include <memory>
#include <string>
// create logger with given name, sinks and the default pattern formatter
// all other ctors will call this one
template<class It>
inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end) :
inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end):
_name(logger_name),
_sinks(begin, end),
_formatter(std::make_shared<pattern_formatter>("%+"))
{
// no support under vs2013 for member initialization for std::atomic
_level = level::info;
_flush_level = level::off;
_last_err_time = 0;
_err_handler = [this](const std::string &msg)
{
this->_default_err_handler(msg);
};
}
// ctor with sinks as init list
inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list) :
logger(logger_name, sinks_list.begin(), sinks_list.end()) {}
inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list):
logger(logger_name, sinks_list.begin(), sinks_list.end())
{}
// ctor with single sink
inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink) :
inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink):
logger(logger_name,
{
single_sink
}) {}
})
{}
inline spdlog::logger::~logger() = default;
@@ -56,28 +63,40 @@ inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Ar
{
if (!should_log(lvl)) return;
details::log_msg log_msg(&_name, lvl);
try
{
details::log_msg log_msg(&_name, lvl);
log_msg.raw.write(fmt, args...);
_sink_it(log_msg);
}
catch (fmt::FormatError &ex)
catch (const std::exception &ex)
{
throw spdlog::spdlog_ex(std::string("format error in \"") + fmt + "\": " + ex.what());
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
_sink_it(log_msg);
}
template <typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char* msg)
{
if (!should_log(lvl)) return;
details::log_msg log_msg(&_name, lvl);
log_msg.raw << msg;
_sink_it(log_msg);
try
{
details::log_msg log_msg(&_name, lvl);
log_msg.raw << msg;
_sink_it(log_msg);
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
}
@@ -85,11 +104,20 @@ template<typename T>
inline void spdlog::logger::log(level::level_enum lvl, const T& msg)
{
if (!should_log(lvl)) return;
details::log_msg log_msg(&_name, lvl);
log_msg.raw << msg;
_sink_it(log_msg);
try
{
details::log_msg log_msg(&_name, lvl);
log_msg.raw << msg;
_sink_it(log_msg);
}
catch (const std::exception &ex)
{
_err_handler(ex.what());
}
catch (...)
{
_err_handler("Unknown exception");
}
}
@@ -185,6 +213,17 @@ inline void spdlog::logger::set_level(spdlog::level::level_enum log_level)
_level.store(log_level);
}
inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler)
{
_err_handler = err_handler;
}
inline spdlog::log_err_handler spdlog::logger::error_handler()
{
return _err_handler;
}
inline void spdlog::logger::flush_on(level::level_enum log_level)
{
_flush_level.store(log_level);
@@ -207,10 +246,14 @@ inline void spdlog::logger::_sink_it(details::log_msg& msg)
{
_formatter->format(msg);
for (auto &sink : _sinks)
sink->log(msg);
{
if( sink->should_log( msg.level))
{
sink->log(msg);
}
}
const auto flush_level = _flush_level.load(std::memory_order_relaxed);
if (msg.level >= flush_level)
if(_should_flush_on(msg))
flush();
}
@@ -228,3 +271,28 @@ inline void spdlog::logger::flush()
for (auto& sink : _sinks)
sink->flush();
}
inline void spdlog::logger::_default_err_handler(const std::string &msg)
{
auto now = time(nullptr);
if (now - _last_err_time < 60)
return;
auto tm_time = details::os::localtime(now);
char date_buf[100];
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
details::log_msg err_msg;
err_msg.formatted.write("[*** LOG ERROR ***] [{}] [{}] [{}]{}", name(), msg, date_buf, details::os::eol);
sinks::stderr_sink_mt::instance()->log(err_msg);
_last_err_time = now;
}
inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg)
{
const auto flush_level = _flush_level.load(std::memory_order_relaxed);
return (msg.level >= flush_level) && (msg.level != level::off);
}
inline const std::vector<spdlog::sink_ptr>& spdlog::logger::sinks() const
{
return _sinks;
}

View File

@@ -60,8 +60,9 @@ public:
using item_type = T;
mpmc_bounded_queue(size_t buffer_size)
: buffer_(new cell_t [buffer_size]),
buffer_mask_(buffer_size - 1)
: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)))
@@ -132,6 +133,16 @@ public:
return true;
}
size_t approx_size()
{
size_t first_pos = dequeue_pos_.load(std::memory_order_relaxed);
size_t last_pos = enqueue_pos_.load(std::memory_order_relaxed);
if (last_pos <= first_pos)
return 0;
auto size = last_pos - first_pos;
return size < max_size_ ? size : max_size_;
}
private:
struct cell_t
{
@@ -139,6 +150,8 @@ private:
T data_;
};
size_t const max_size_;
static size_t const cacheline_size = 64;
typedef char cacheline_pad_t [cacheline_size];
@@ -151,8 +164,8 @@ private:
std::atomic<size_t> dequeue_pos_;
cacheline_pad_t pad3_;
mpmc_bounded_queue(mpmc_bounded_queue const&);
void operator = (mpmc_bounded_queue const&);
mpmc_bounded_queue(mpmc_bounded_queue const&) = delete;
void operator= (mpmc_bounded_queue const&) = delete;
};
} // ns details

View File

@@ -10,10 +10,12 @@
#include <ctime>
#include <functional>
#include <string>
#include <chrono>
#include <thread>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#ifdef _WIN32
@@ -25,25 +27,32 @@
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <process.h> // _get_pid support
#include <io.h> // _get_osfhandle support
#ifdef __MINGW32__
#include <share.h>
#endif
#include <sys/types.h>
#else // unix
#elif __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
#include <unistd.h>
#include <chrono>
#include <fcntl.h>
#else
#include <thread>
#ifdef __linux__
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
#elif __FreeBSD__
#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
#endif
#endif //unix
#ifndef __has_feature // Clang - feature checking macros.
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif
namespace spdlog
{
namespace details
@@ -133,6 +142,18 @@ inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL;
SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1;
inline void prevent_child_fd(FILE *f)
{
#ifdef _WIN32
auto file_handle = (HANDLE)_get_osfhandle(_fileno(f));
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
throw spdlog_ex("SetHandleInformation failed", errno);
#else
auto fd = fileno(f);
if(fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno);
#endif
}
//fopen_s on non windows for writing
@@ -144,13 +165,18 @@ inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode
#else
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
#endif
return *fp == nullptr;
#else
#else //unix
*fp = fopen((filename.c_str()), mode.c_str());
return *fp == nullptr;
#endif
#ifdef SPDLOG_PREVENT_CHILD_FD
if(*fp != nullptr)
prevent_child_fd(*fp);
#endif
return *fp == nullptr;
}
inline int remove(const filename_t &filename)
{
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
@@ -192,35 +218,35 @@ inline bool file_exists(const filename_t& filename)
//Return file size according to open FILE* object
inline size_t filesize(FILE *f)
{
if (f == nullptr)
throw spdlog_ex("Failed getting file size. fd is null");
if (f == nullptr)
throw spdlog_ex("Failed getting file size. fd is null");
#ifdef _WIN32
int fd = _fileno(f);
#if _WIN64 //64 bits
struct _stat64 st;
if (_fstat64(fd, &st) == 0)
return st.st_size;
#else //windows 32 bits
struct _stat st;
if (_fstat(fd, &st) == 0)
return st.st_size;
int fd = _fileno(f);
#if _WIN64 //64 bits
struct _stat64 st;
if (_fstat64(fd, &st) == 0)
return st.st_size;
#else //windows 32 bits
long ret = _filelength(fd);
if (ret >= 0)
return static_cast<size_t>(ret);
#endif
#else // unix
int fd = fileno(f);
//64 bits(but not in osx, where fstat64 is deprecated)
#if !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__))
struct stat64 st;
if (fstat64(fd, &st) == 0)
return st.st_size;
#else // unix 32 bits or osx
struct stat st;
if (fstat(fd, &st) == 0)
return st.st_size;
int fd = fileno(f);
//64 bits(but not in osx, where fstat64 is deprecated)
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__))
struct stat64 st;
if (fstat64(fd, &st) == 0)
return static_cast<size_t>(st.st_size);
#else // unix 32 bits or osx
struct stat st;
if (fstat(fd, &st) == 0)
return static_cast<size_t>(st.st_size);
#endif
#endif
throw spdlog_ex("Failed getting file size from fd", errno);
throw spdlog_ex("Failed getting file size from fd", errno);
}
@@ -248,13 +274,49 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
offset -= tzinfo.StandardBias;
return offset;
#else
return static_cast<int>(tm.tm_gmtoff / 60);
#if defined(sun) || defined(__sun)
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
struct helper
{
static long int calculate_gmt_offset(const std::tm & localtm = details::os::localtime(), const std::tm & gmtm = details::os::gmtime())
{
int local_year = localtm.tm_year + (1900 - 1);
int gmt_year = gmtm.tm_year + (1900 - 1);
long int days = (
// difference in day of year
localtm.tm_yday - gmtm.tm_yday
// + intervening leap days
+ ((local_year >> 2) - (gmt_year >> 2))
- (local_year / 100 - gmt_year / 100)
+ ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
// + difference in years * 365 */
+ (long int)(local_year - gmt_year) * 365
);
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
return secs;
}
};
long int offset_seconds = helper::calculate_gmt_offset(tm);
#else
long int offset_seconds = tm.tm_gmtoff;
#endif
return static_cast<int>(offset_seconds / 60);
#endif
}
//Return current thread id as size_t
//It exists because the std::this_thread::get_id() is much slower(espcially under VS 2013)
inline size_t thread_id()
inline size_t _thread_id()
{
#ifdef _WIN32
return static_cast<size_t>(::GetCurrentThreadId());
@@ -263,12 +325,28 @@ inline size_t thread_id()
# define SYS_gettid __NR_gettid
# endif
return static_cast<size_t>(syscall(SYS_gettid));
#elif __FreeBSD__
long tid;
thr_self(&tid);
return static_cast<size_t>(tid);
#else //Default to standard C++11 (OSX and other Unix)
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
#endif
}
//Return current thread id as size_t (from thread local storage)
inline size_t thread_id()
{
#if defined(_MSC_VER) && (_MSC_VER < 1900) || defined(__clang__) && !__has_feature(cxx_thread_local)
return _thread_id();
#else
static thread_local const size_t tid = _thread_id();
return tid;
#endif
}
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
@@ -299,7 +377,9 @@ inline std::string errno_str(int err_num)
else
return "Unkown error";
#elif defined(__APPLE__) || ((_POSIX_C_SOURCE >= 200112L) && ! _GNU_SOURCE) // posix version
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \
((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version
if (strerror_r(err_num, buf, buf_size) == 0)
return std::string(buf);
else
@@ -310,8 +390,17 @@ inline std::string errno_str(int err_num)
#endif
}
inline int pid()
{
#ifdef _WIN32
return ::_getpid();
#else
return static_cast<int>(::getpid());
#endif
}
} //os
} //details
} //spdlog

View File

@@ -18,6 +18,7 @@
#include <thread>
#include <utility>
#include <vector>
#include <array>
namespace spdlog
{
@@ -26,7 +27,8 @@ namespace details
class flag_formatter
{
public:
virtual ~flag_formatter() {}
virtual ~flag_formatter()
{}
virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0;
};
@@ -35,7 +37,7 @@ public:
///////////////////////////////////////////////////////////////////////
namespace
{
class name_formatter :public flag_formatter
class name_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
@@ -45,7 +47,7 @@ class name_formatter :public flag_formatter
}
// 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
{
@@ -54,7 +56,7 @@ class level_formatter :public flag_formatter
};
// 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
{
@@ -77,42 +79,60 @@ static int to12h(const tm& t)
}
//Abbreviated weekday name
static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
class a_formatter :public flag_formatter
using days_array = std::array<std::string, 7>;
static const days_array& days()
{
static const days_array arr{ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
return arr;
}
class a_formatter:public flag_formatter
{
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
static const std::string full_days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
class A_formatter :public flag_formatter
static const days_array& full_days()
{
static const days_array arr{ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
return arr;
}
class A_formatter:public flag_formatter
{
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
static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" };
class b_formatter :public flag_formatter
using months_array = std::array<std::string, 12>;
static const months_array& months()
{
static const months_array arr{ "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" };
return arr;
}
class b_formatter:public flag_formatter
{
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
static const std::string full_months[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
class B_formatter :public flag_formatter
static const months_array& full_months()
{
static const months_array arr{ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
return arr;
}
class B_formatter:public flag_formatter
{
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];
}
};
@@ -133,18 +153,18 @@ static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v
//Date and time representation (Thu Aug 23 15:35:46 2014)
class c_formatter :public flag_formatter
class c_formatter:public flag_formatter
{
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;
}
};
// year - 2 digit
class C_formatter :public flag_formatter
class C_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -155,7 +175,7 @@ class C_formatter :public flag_formatter
// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
class D_formatter :public flag_formatter
class D_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -165,7 +185,7 @@ class D_formatter :public flag_formatter
// year - 4 digit
class Y_formatter :public flag_formatter
class Y_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -174,7 +194,7 @@ class Y_formatter :public flag_formatter
};
// month 1-12
class m_formatter :public flag_formatter
class m_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -183,7 +203,7 @@ class m_formatter :public flag_formatter
};
// day of month 1-31
class d_formatter :public flag_formatter
class d_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -192,7 +212,7 @@ class d_formatter :public flag_formatter
};
// hours in 24 format 0-23
class H_formatter :public flag_formatter
class H_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -201,7 +221,7 @@ class H_formatter :public flag_formatter
};
// hours in 12 format 1-12
class I_formatter :public flag_formatter
class I_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -210,7 +230,7 @@ class I_formatter :public flag_formatter
};
// minutes 0-59
class M_formatter :public flag_formatter
class M_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -219,7 +239,7 @@ class M_formatter :public flag_formatter
};
// seconds 0-59
class S_formatter :public flag_formatter
class S_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -228,7 +248,7 @@ class S_formatter :public flag_formatter
};
// milliseconds
class e_formatter :public flag_formatter
class e_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
@@ -239,7 +259,7 @@ class e_formatter :public flag_formatter
};
// microseconds
class f_formatter :public flag_formatter
class f_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
@@ -250,7 +270,7 @@ class f_formatter :public flag_formatter
};
// nanoseconds
class F_formatter :public flag_formatter
class F_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
@@ -261,7 +281,7 @@ class F_formatter :public flag_formatter
};
// AM/PM
class p_formatter :public flag_formatter
class p_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -271,7 +291,7 @@ class p_formatter :public flag_formatter
// 12 hour clock 02:55:02 pm
class r_formatter :public flag_formatter
class r_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -280,7 +300,7 @@ class r_formatter :public flag_formatter
};
// 24-hour HH:MM time, equivalent to %H:%M
class R_formatter :public flag_formatter
class R_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -289,7 +309,7 @@ class R_formatter :public flag_formatter
};
// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
class T_formatter :public flag_formatter
class T_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -299,12 +319,13 @@ class T_formatter :public flag_formatter
// ISO 8601 offset from UTC in timezone (+-HH:MM)
class z_formatter :public flag_formatter
class z_formatter:public flag_formatter
{
public:
const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
z_formatter() :_last_update(std::chrono::seconds(0)) {}
z_formatter():_last_update(std::chrono::seconds(0))
{}
z_formatter(const z_formatter&) = delete;
z_formatter& operator=(const z_formatter&) = delete;
@@ -317,13 +338,21 @@ public:
// it is very fast (already stored in tm.tm_gmtoff)
int total_minutes = os::utc_minutes_offset(tm_time);
#endif
bool is_negative = total_minutes < 0;
char sign;
if (is_negative)
{
total_minutes = -total_minutes;
sign = '-';
}
else
{
sign = '+';
}
int h = total_minutes / 60;
int m = total_minutes % 60;
if (h >= 0) //minus sign will be printed anyway if negative
{
msg.formatted << '+';
}
msg.formatted << sign;
pad_n_join(msg.formatted, h, m, ':');
}
private:
@@ -346,8 +375,8 @@ private:
//Thread id
class t_formatter :public flag_formatter
// Thread id
class t_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
@@ -355,8 +384,17 @@ class t_formatter :public flag_formatter
}
};
// Current pid
class pid_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
msg.formatted << details::os::pid();
}
};
class v_formatter :public flag_formatter
class v_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm&) override
{
@@ -364,10 +402,10 @@ class v_formatter :public flag_formatter
}
};
class ch_formatter :public flag_formatter
class ch_formatter:public flag_formatter
{
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
{
@@ -379,7 +417,7 @@ private:
//aggregate user chars to display as is
class aggregate_formatter :public flag_formatter
class aggregate_formatter:public flag_formatter
{
public:
aggregate_formatter()
@@ -398,7 +436,7 @@ private:
// Full info formatter
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
class full_formatter :public flag_formatter
class full_formatter:public flag_formatter
{
void format(details::log_msg& msg, const std::tm& tm_time) override
{
@@ -429,7 +467,7 @@ class full_formatter :public flag_formatter
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_sec), 2, '0') << '.'
<< fmt::pad(static_cast<unsigned int>(millis), 3, '0') << "] ";
//no datetime needed
//no datetime needed
#else
(void)tm_time;
#endif
@@ -443,6 +481,8 @@ class full_formatter :public flag_formatter
}
};
}
}
///////////////////////////////////////////////////////////////////////////////
@@ -499,101 +539,101 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::short_level_formatter()));
break;
case('t') :
case('t'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::t_formatter()));
break;
case('v') :
case('v'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::v_formatter()));
break;
case('a') :
case('a'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::a_formatter()));
break;
case('A') :
case('A'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::A_formatter()));
break;
case('b') :
case('h') :
case('b'):
case('h'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::b_formatter()));
break;
case('B') :
case('B'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::B_formatter()));
break;
case('c') :
case('c'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::c_formatter()));
break;
case('C') :
case('C'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::C_formatter()));
break;
case('Y') :
case('Y'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::Y_formatter()));
break;
case('D') :
case('x') :
case('D'):
case('x'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::D_formatter()));
break;
case('m') :
case('m'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::m_formatter()));
break;
case('d') :
case('d'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::d_formatter()));
break;
case('H') :
case('H'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::H_formatter()));
break;
case('I') :
case('I'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::I_formatter()));
break;
case('M') :
case('M'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::M_formatter()));
break;
case('S') :
case('S'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::S_formatter()));
break;
case('e') :
case('e'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::e_formatter()));
break;
case('f') :
case('f'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::f_formatter()));
break;
case('F') :
case('F'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::F_formatter()));
break;
case('p') :
case('p'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::p_formatter()));
break;
case('r') :
case('r'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::r_formatter()));
break;
case('R') :
case('R'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::R_formatter()));
break;
case('T') :
case('X') :
case('T'):
case('X'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::T_formatter()));
break;
case('z') :
case('z'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::z_formatter()));
break;
@@ -601,6 +641,10 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::full_formatter()));
break;
case ('P'):
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::pid_formatter()));
break;
default: //Unkown flag appears as is
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter('%')));
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter(flag)));
@@ -611,22 +655,16 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
inline void spdlog::pattern_formatter::format(details::log_msg& msg)
{
try
{
#ifndef SPDLOG_NO_DATETIME
auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time));
auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time));
#else
std::tm tm_time;
std::tm tm_time;
#endif
for (auto &f : _formatters)
{
f->format(msg, tm_time);
}
//write eol
msg.formatted.write(details::os::eol, details::os::eol_size);
}
catch(const fmt::FormatError& e)
for (auto &f : _formatters)
{
throw spdlog_ex(fmt::format("formatting error while processing format string: {}", e.what()));
f->format(msg, tm_time);
}
//write eol
msg.formatted.write(details::os::eol, details::os::eol_size);
}

View File

@@ -60,12 +60,24 @@ public:
if (_formatter)
new_logger->set_formatter(_formatter);
if (_err_handler)
new_logger->set_error_handler(_err_handler);
new_logger->set_level(_level);
//Add to registry
_loggers[logger_name] = new_logger;
return new_logger;
}
void apply_all(std::function<void(std::shared_ptr<logger>)> fun)
{
std::lock_guard<Mutex> lock(_mutex);
for (auto &l : _loggers)
fun(l.second);
}
void drop(const std::string& logger_name)
{
std::lock_guard<Mutex> lock(_mutex);
@@ -112,6 +124,13 @@ public:
_level = log_level;
}
void set_error_handler(log_err_handler handler)
{
for (auto& l : _loggers)
l.second->set_error_handler(handler);
_err_handler = handler;
}
void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
{
std::lock_guard<Mutex> lock(_mutex);
@@ -149,6 +168,7 @@ private:
std::unordered_map <std::string, std::shared_ptr<logger>> _loggers;
formatter_ptr _formatter;
level::level_enum _level = level::info;
log_err_handler _err_handler;
bool _async_mode = false;
size_t _async_q_size = 0;
async_overflow_policy _overflow_policy = async_overflow_policy::block_retry;

View File

@@ -12,8 +12,20 @@
#include <spdlog/details/registry.h>
#include <spdlog/sinks/file_sinks.h>
#include <spdlog/sinks/stdout_sinks.h>
#ifdef SPDLOG_ENABLE_SYSLOG
#include <spdlog/sinks/syslog_sink.h>
#endif
#ifdef _WIN32
#include <spdlog/sinks/wincolor_sink.h>
#else
#include <spdlog/sinks/ansicolor_sink.h>
#endif
#ifdef __ANDROID__
#include <spdlog/sinks/android_sink.h>
#endif
#include <chrono>
#include <functional>
@@ -36,67 +48,119 @@ inline void spdlog::drop(const std::string &name)
}
// Create multi/single threaded simple file logger
inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool force_flush)
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, force_flush);
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 force_flush)
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, force_flush);
return create<spdlog::sinks::simple_file_sink_st>(logger_name, filename, truncate);
}
// Create multi/single threaded rotating file logger
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files, bool force_flush)
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
{
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files, force_flush);
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, SPDLOG_FILENAME_T("txt"), 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, bool force_flush)
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
{
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files, force_flush);
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files);
}
// Create file logger which creates new file at midnight):
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute, bool force_flush)
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute)
{
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute, force_flush);
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, SPDLOG_FILENAME_T("txt"), 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, bool force_flush)
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour, int minute)
{
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute, force_flush);
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute);
}
// Create stdout/stderr loggers (with optinal color support)
inline std::shared_ptr<spdlog::logger> create_console_logger(const std::string& logger_name, spdlog::sink_ptr sink, bool color)
//
// stdout/stderr loggers
//
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name)
{
if (color) //use color wrapper sink
sink = std::make_shared<spdlog::sinks::ansicolor_sink>(sink);
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name)
{
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name)
{
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance());
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name)
{
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance());
}
//
// stdout/stderr color loggers
//
#ifdef _WIN32
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name, bool color)
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
{
return create_console_logger(logger_name, sinks::stdout_sink_mt::instance(), color);
auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name, bool color)
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
{
return create_console_logger(logger_name, sinks::stdout_sink_st::instance(), color);
auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_mt>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name, bool color)
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
{
return create_console_logger(logger_name, sinks::stderr_sink_mt::instance(), color);
auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_st>();
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name, bool color)
#else //ansi terminal colors
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
{
return create_console_logger(logger_name, sinks::stderr_sink_st::instance(), color);
auto sink = std::make_shared<spdlog::sinks::ansicolor_sink>(spdlog::sinks::stdout_sink_mt::instance());
return spdlog::details::registry::instance().create(logger_name, sink);
}
#if defined(__linux__) || defined(__APPLE__)
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::ansicolor_sink>(spdlog::sinks::stdout_sink_st::instance());
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::ansicolor_sink>(spdlog::sinks::stderr_sink_mt::instance());
return spdlog::details::registry::instance().create(logger_name, sink);
}
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
{
auto sink = std::make_shared<spdlog::sinks::ansicolor_sink>(spdlog::sinks::stderr_sink_st::instance());
return spdlog::details::registry::instance().create(logger_name, sink);
}
#endif
#ifdef SPDLOG_ENABLE_SYSLOG
// Create syslog logger
inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option)
{
@@ -104,6 +168,13 @@ inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string&
}
#endif
#ifdef __ANDROID__
inline std::shared_ptr<spdlog::logger> spdlog::android_logger(const std::string& logger_name, const std::string& tag)
{
return create<spdlog::sinks::android_sink>(logger_name, tag);
}
#endif
// Create and register a logger a single sink
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const spdlog::sink_ptr& sink)
{
@@ -147,6 +218,11 @@ inline void spdlog::set_level(level::level_enum log_level)
return details::registry::instance().set_level(log_level);
}
inline void spdlog::set_error_handler(log_err_handler handler)
{
return details::registry::instance().set_error_handler(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)
{
@@ -158,6 +234,11 @@ inline void spdlog::set_sync_mode()
details::registry::instance().set_sync_mode();
}
inline void spdlog::apply_all(std::function<void(std::shared_ptr<logger>)> fun)
{
details::registry::instance().apply_all(fun);
}
inline void spdlog::drop_all()
{
details::registry::instance().drop_all();

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -1,17 +1,17 @@
/*
Formatting library for C++ - std::ostream support
Formatting library for C++ - std::ostream support
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
For the license information refer to format.h.
*/
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
// Commented out by spdlog to use header only
// #include "fmt/format.h"
// commented out by spdlog
// #include "format.h"
#include <ostream>
namespace fmt
@@ -21,7 +21,7 @@ namespace internal
{
template <class Char>
class FormatBuf : public std::basic_streambuf<Char>
class FormatBuf: public std::basic_streambuf<Char>
{
private:
typedef typename std::basic_streambuf<Char>::int_type int_type;
@@ -31,7 +31,7 @@ private:
Char *start_;
public:
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0])
FormatBuf(Buffer<Char> &buffer): buffer_(buffer), start_(&buffer[0])
{
this->setp(start_, start_ + buffer_.capacity());
}
@@ -46,7 +46,7 @@ public:
start_ = &buffer_[0];
start_[buf_size] = traits_type::to_char_type(ch);
this->setp(start_+ buf_size + 1, start_ + buf_size * 2);
this->setp(start_ + buf_size + 1, start_ + buf_size * 2);
}
return ch;
}
@@ -59,7 +59,7 @@ public:
Yes &convert(std::ostream &);
struct DummyStream : std::ostream
struct DummyStream: std::ostream
{
DummyStream(); // Suppress a bogus warning in MSVC.
// Hide all operator<< overloads from std::ostream.
@@ -77,12 +77,15 @@ struct ConvertToIntImpl<T, true>
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
};
};
// Write the content of w to os.
void write(std::ostream &os, Writer &w);
} // namespace internal
// Formats a value.
template <typename Char, typename ArgFormatter, typename T>
void format(BasicFormatter<Char, ArgFormatter> &f,
const Char *&format_str, const T &value)
void format_arg(BasicFormatter<Char, ArgFormatter> &f,
const Char *&format_str, const T &value)
{
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
@@ -96,28 +99,16 @@ void format(BasicFormatter<Char, ArgFormatter> &f,
}
/**
\rst
Prints formatted data to the stream *os*.
\rst
Prints formatted data to the stream *os*.
**Example**::
**Example**::
print(cerr, "Don't {}!", "panic");
\endrst
*/
print(cerr, "Don't {}!", "panic");
\endrst
*/
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(void, print, std::ostream &, CStringRef)
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
FMT_API int fprintf(std::ostream &os, CStringRef format_str, ArgList args);
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY

View File

@@ -1,11 +1,11 @@
/*
Formatting library for C++
Formatting library for C++
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
Copyright (c) 2012 - 2016, Victor Zverovich
All rights reserved.
For the license information refer to format.h.
*/
For the license information refer to format.h.
*/
#ifndef FMT_PRINTF_H_
#define FMT_PRINTF_H_
@@ -13,7 +13,7 @@
#include <algorithm> // std::fill_n
#include <limits> // std::numeric_limits
#include "fmt/format.h"
#include "ostream.h"
namespace fmt
{
@@ -52,7 +52,7 @@ struct IntChecker<true>
}
};
class PrecisionHandler : public ArgVisitor<PrecisionHandler, int>
class PrecisionHandler: public ArgVisitor<PrecisionHandler, int>
{
public:
void report_unhandled_arg()
@@ -70,7 +70,7 @@ public:
};
// IsZeroInt::visit(arg) returns true iff arg is a zero integer.
class IsZeroInt : public ArgVisitor<IsZeroInt, bool>
class IsZeroInt: public ArgVisitor<IsZeroInt, bool>
{
public:
template <typename T>
@@ -83,13 +83,19 @@ public:
template <typename T, typename U>
struct is_same
{
enum { value = 0 };
enum
{
value = 0
};
};
template <typename T>
struct is_same<T, T>
{
enum { value = 1 };
enum
{
value = 1
};
};
// An argument visitor that converts an integer argument to T for printf,
@@ -97,7 +103,7 @@ struct is_same<T, T>
// corresponding signed or unsigned type depending on the type specifier:
// 'd' and 'i' - signed, other - unsigned)
template <typename T = void>
class ArgConverter : public ArgVisitor<ArgConverter<T>, void>
class ArgConverter: public ArgVisitor<ArgConverter<T>, void>
{
private:
internal::Arg &arg_;
@@ -107,7 +113,8 @@ private:
public:
ArgConverter(internal::Arg &arg, wchar_t type)
: arg_(arg), type_(type) {}
: arg_(arg), type_(type)
{}
void visit_bool(bool value)
{
@@ -158,7 +165,7 @@ public:
};
// Converts an integer argument to char for printf.
class CharConverter : public ArgVisitor<CharConverter, void>
class CharConverter: public ArgVisitor<CharConverter, void>
{
private:
internal::Arg &arg_;
@@ -166,7 +173,8 @@ private:
FMT_DISALLOW_COPY_AND_ASSIGN(CharConverter);
public:
explicit CharConverter(internal::Arg &arg) : arg_(arg) {}
explicit CharConverter(internal::Arg &arg): arg_(arg)
{}
template <typename T>
void visit_any_int(T value)
@@ -178,7 +186,7 @@ public:
// Checks if an argument is a valid printf width specifier and sets
// left alignment if it is negative.
class WidthHandler : public ArgVisitor<WidthHandler, unsigned>
class WidthHandler: public ArgVisitor<WidthHandler, unsigned>
{
private:
FormatSpec &spec_;
@@ -186,7 +194,8 @@ private:
FMT_DISALLOW_COPY_AND_ASSIGN(WidthHandler);
public:
explicit WidthHandler(FormatSpec &spec) : spec_(spec) {}
explicit WidthHandler(FormatSpec &spec): spec_(spec)
{}
void report_unhandled_arg()
{
@@ -212,24 +221,24 @@ public:
} // namespace internal
/**
\rst
A ``printf`` argument formatter based on the `curiously recurring template
pattern <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_.
\rst
A ``printf`` argument formatter based on the `curiously recurring template
pattern <http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern>`_.
To use `~fmt::BasicPrintfArgFormatter` define a subclass that implements some
or all of the visit methods with the same signatures as the methods in
`~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`.
Pass the subclass as the *Impl* template parameter. When a formatting
function processes an argument, it will dispatch to a visit method
specific to the argument type. For example, if the argument type is
``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass
will be called. If the subclass doesn't contain a method with this signature,
then a corresponding method of `~fmt::BasicPrintfArgFormatter` or its
superclass will be called.
\endrst
*/
To use `~fmt::BasicPrintfArgFormatter` define a subclass that implements some
or all of the visit methods with the same signatures as the methods in
`~fmt::ArgVisitor`, for example, `~fmt::ArgVisitor::visit_int()`.
Pass the subclass as the *Impl* template parameter. When a formatting
function processes an argument, it will dispatch to a visit method
specific to the argument type. For example, if the argument type is
``double`` then the `~fmt::ArgVisitor::visit_double()` method of a subclass
will be called. If the subclass doesn't contain a method with this signature,
then a corresponding method of `~fmt::BasicPrintfArgFormatter` or its
superclass will be called.
\endrst
*/
template <typename Impl, typename Char>
class BasicPrintfArgFormatter : public internal::ArgFormatterBase<Impl, Char>
class BasicPrintfArgFormatter: public internal::ArgFormatterBase<Impl, Char>
{
private:
void write_null_pointer()
@@ -242,14 +251,15 @@ private:
public:
/**
\rst
Constructs an argument formatter object.
*writer* is a reference to the output writer and *spec* contains format
specifier information for standard argument types.
\endrst
*/
BasicPrintfArgFormatter(BasicWriter<Char> &writer, FormatSpec &spec)
: internal::ArgFormatterBase<Impl, Char>(writer, spec) {}
\rst
Constructs an argument formatter object.
*writer* is a reference to the output writer and *spec* contains format
specifier information for standard argument types.
\endrst
*/
BasicPrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: internal::ArgFormatterBase<Impl, Char>(w, s)
{}
/** Formats an argument of type ``bool``. */
void visit_bool(bool value)
@@ -315,7 +325,7 @@ public:
void visit_custom(internal::Arg::CustomValue c)
{
BasicFormatter<Char> formatter(ArgList(), this->writer());
const Char format_str[] = {'}', 0};
const Char format_str[] = { '}', 0 };
const Char *format = format_str;
c.format(&formatter, c.value, &format);
}
@@ -329,12 +339,13 @@ class PrintfArgFormatter
public:
/** Constructs an argument formatter object. */
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char>(w, s) {}
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char>(w, s)
{}
};
/** This template formats data and writes the output to a writer. */
template <typename Char, typename ArgFormatter = PrintfArgFormatter<Char> >
class PrintfFormatter : private internal::FormatterBase
class PrintfFormatter: private internal::FormatterBase
{
private:
BasicWriter<Char> &writer_;
@@ -352,14 +363,15 @@ private:
public:
/**
\rst
Constructs a ``PrintfFormatter`` object. References to the arguments and
the writer are stored in the formatter object so make sure they have
appropriate lifetimes.
\endrst
*/
explicit PrintfFormatter(const ArgList &args, BasicWriter<Char> &w)
: FormatterBase(args), writer_(w) {}
\rst
Constructs a ``PrintfFormatter`` object. References to the arguments and
the writer are stored in the formatter object so make sure they have
appropriate lifetimes.
\endrst
*/
explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w)
: FormatterBase(al), writer_(w)
{}
/** Formats stored arguments and writes the output to the writer. */
FMT_API void format(BasicCStringRef<Char> format_str);
@@ -399,7 +411,7 @@ internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s,
unsigned arg_index)
{
(void)s;
const char *error = 0;
const char *error = FMT_NULL;
internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ?
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
if (error)
@@ -569,13 +581,13 @@ void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args)
}
/**
\rst
Formats arguments and returns the result as a string.
\rst
Formats arguments and returns the result as a string.
**Example**::
**Example**::
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
std::string message = fmt::sprintf("The answer is %d", 42);
\endrst
*/
inline std::string sprintf(CStringRef format, ArgList args)
{
@@ -594,31 +606,53 @@ inline std::wstring sprintf(WCStringRef format, ArgList args)
FMT_VARIADIC_W(std::wstring, sprintf, WCStringRef)
/**
\rst
Prints formatted data to the file *f*.
\rst
Prints formatted data to the file *f*.
**Example**::
**Example**::
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst
*/
FMT_API int fprintf(std::FILE *f, CStringRef format, ArgList args);
FMT_VARIADIC(int, fprintf, std::FILE *, CStringRef)
/**
\rst
Prints formatted data to ``stdout``.
\rst
Prints formatted data to ``stdout``.
**Example**::
**Example**::
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst
*/
inline int printf(CStringRef format, ArgList args)
{
return fprintf(stdout, format, args);
}
FMT_VARIADIC(int, printf, CStringRef)
/**
\rst
Prints formatted data to the stream *os*.
**Example**::
fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args)
{
MemoryWriter w;
printf(w, format_str, args);
internal::write(os, w);
return static_cast<int>(w.size());
}
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
} // namespace fmt
#ifdef FMT_HEADER_ONLY
# include "printf.cc"
#endif
#endif // FMT_PRINTF_H_

View File

@@ -5,7 +5,7 @@
#pragma once
// Thread safe logger
// Thread safe logger (except for set_pattern(..), set_formatter(..) and set_error_handler())
// Has name, log level, vector of std::shared sink pointers and formatter
// Upon each log write the logger:
// 1. Checks if its log level is enough to log the message
@@ -19,7 +19,6 @@
#include <memory>
#include <string>
namespace spdlog
{
@@ -53,7 +52,6 @@ public:
template <typename T> void error(const T&);
template <typename T> void critical(const T&);
bool should_log(level::level_enum) const;
void set_level(level::level_enum);
level::level_enum level() const;
@@ -61,23 +59,36 @@ public:
void set_pattern(const std::string&);
void set_formatter(formatter_ptr);
// error handler
void set_error_handler(log_err_handler);
log_err_handler error_handler();
// automatically call flush() if message level >= log_level
void flush_on(level::level_enum log_level);
virtual void flush();
const std::vector<sink_ptr>& sinks() const;
protected:
virtual void _sink_it(details::log_msg&);
virtual void _set_pattern(const std::string&);
virtual void _set_formatter(formatter_ptr);
// default error handler: print the error to stderr with the max rate of 1 message/minute
virtual void _default_err_handler(const std::string &msg);
// return true if the given message level should trigger a flush
bool _should_flush_on(const details::log_msg&);
const std::string _name;
std::vector<sink_ptr> _sinks;
formatter_ptr _formatter;
spdlog::level_t _level;
spdlog::level_t _flush_level;
log_err_handler _err_handler;
std::atomic<time_t> _last_err_time;
};
}
#include <spdlog/details/logger_impl.h>

View File

@@ -7,51 +7,43 @@
#if defined(__ANDROID__)
#include <spdlog/sinks/base_sink.h>
#include <spdlog/details/null_mutex.h>
#include <android/log.h>
#include <spdlog/sinks/sink.h>
#include <mutex>
#include <string>
#include <android/log.h>
namespace spdlog
{
namespace sinks
{
/*
* Android sink (logging using __android_log_write)
* __android_log_write is thread-safe. No lock is needed.
*/
template<class Mutex>
class base_android_sink : public base_sink < Mutex >
class android_sink : public sink
{
public:
explicit base_android_sink(std::string tag="spdlog"): _tag(tag)
explicit android_sink(const std::string& tag = "spdlog"): _tag(tag) {}
void log(const details::log_msg& msg) override
{
const android_LogPriority priority = convert_to_android(msg.level);
// See system/core/liblog/logger_write.c for explanation of return value
const int ret = __android_log_write(
priority, _tag.c_str(), msg.formatted.c_str()
);
if (ret < 0)
{
throw spdlog_ex("__android_log_write() failed", ret);
}
}
void flush() override
{
}
protected:
void _sink_it(const details::log_msg& msg) override
{
const android_LogPriority priority = convert_to_android(msg.level);
const int expected_size = msg.formatted.size();
const int size = __android_log_write(
priority, _tag.c_str(), msg.formatted.c_str()
);
if (size > expected_size)
{
// Will write a little bit more than original message
}
else
{
throw spdlog_ex("Send to Android logcat failed");
}
}
private:
static android_LogPriority convert_to_android(spdlog::level::level_enum level)
{
@@ -63,29 +55,20 @@ private:
return ANDROID_LOG_DEBUG;
case spdlog::level::info:
return ANDROID_LOG_INFO;
case spdlog::level::notice:
return ANDROID_LOG_INFO;
case spdlog::level::warn:
return ANDROID_LOG_WARN;
case spdlog::level::err:
return ANDROID_LOG_ERROR;
case spdlog::level::critical:
return ANDROID_LOG_FATAL;
case spdlog::level::alert:
return ANDROID_LOG_FATAL;
case spdlog::level::emerg:
return ANDROID_LOG_FATAL;
default:
throw spdlog_ex("Incorrect level value");
return ANDROID_LOG_DEFAULT;
}
}
std::string _tag;
};
typedef base_android_sink<std::mutex> android_sink_mt;
typedef base_android_sink<details::null_mutex> android_sink_st;
}
}

View File

@@ -33,7 +33,7 @@ public:
virtual void log(const details::log_msg& msg) override;
virtual void flush() override;
void set_color(level::level_enum level, const std::string& color);
void set_color(level::level_enum color_level, const std::string& color);
/// Formatting codes
const std::string reset = "\033[00m";
@@ -101,9 +101,9 @@ inline void ansicolor_sink::flush()
sink_->flush();
}
inline void ansicolor_sink::set_color(level::level_enum level, const std::string& color)
inline void ansicolor_sink::set_color(level::level_enum color_level, const std::string& color)
{
colors_[level] = color;
colors_[color_level] = color;
}
inline ansicolor_sink::~ansicolor_sink()

View File

@@ -7,7 +7,7 @@
//
// base sink templated over a mutex (either dummy or realy)
// concrete implementation should only overrid the _sink_it method.
// all locking is taken care of here so no locking needed by the implementors..
// all locking is taken care of here so no locking needed by the implementers..
//
#include <spdlog/sinks/sink.h>

View File

@@ -11,10 +11,12 @@
#include <spdlog/sinks/sink.h>
#include <algorithm>
#include <memory>
#include <mutex>
#include <memory>
#include <vector>
// Distribution sink (mux). Stores a vector of sinks which get called when log is called
namespace spdlog
{
namespace sinks
@@ -29,40 +31,37 @@ public:
virtual ~dist_sink() = default;
protected:
std::vector<std::shared_ptr<sink>> _sinks;
void _sink_it(const details::log_msg& msg) override
{
for (auto iter = _sinks.begin(); iter != _sinks.end(); iter++)
(*iter)->log(msg);
for (auto &sink : _sinks)
{
if( sink->should_log( msg.level))
{
sink->log(msg);
}
}
}
std::vector<std::shared_ptr<sink>> _sinks;
public:
void flush() override
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
for (auto iter = _sinks.begin(); iter != _sinks.end(); iter++)
(*iter)->flush();
for (auto &sink : _sinks)
sink->flush();
}
void add_sink(std::shared_ptr<sink> sink)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
if (sink &&
_sinks.end() == std::find(_sinks.begin(), _sinks.end(), sink))
{
_sinks.push_back(sink);
}
_sinks.push_back(sink);
}
void remove_sink(std::shared_ptr<sink> sink)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
auto pos = std::find(_sinks.begin(), _sinks.end(), sink);
if (pos != _sinks.end())
{
_sinks.erase(pos);
}
_sinks.erase(std::remove(_sinks.begin(), _sinks.end(), sink), _sinks.end());
}
};

View File

@@ -23,51 +23,55 @@ namespace spdlog
namespace sinks
{
/*
* Trivial file sink with single file as target
*/
* Trivial file sink with single file as target
*/
template<class Mutex>
class simple_file_sink : public base_sink < Mutex >
{
public:
explicit simple_file_sink(const filename_t &filename,
bool force_flush = false) :
_file_helper(force_flush)
explicit simple_file_sink(const filename_t &filename, bool truncate = false):_force_flush(false)
{
_file_helper.open(filename);
_file_helper.open(filename, truncate);
}
void flush() override
{
_file_helper.flush();
}
void set_force_flush(bool force_flush)
{
_force_flush = force_flush;
}
protected:
void _sink_it(const details::log_msg& msg) override
{
_file_helper.write(msg);
if(_force_flush)
_file_helper.flush();
}
private:
details::file_helper _file_helper;
bool _force_flush;
};
typedef simple_file_sink<std::mutex> simple_file_sink_mt;
typedef simple_file_sink<details::null_mutex> simple_file_sink_st;
/*
* Rotating file sink based on size
*/
* Rotating file sink based on size
*/
template<class Mutex>
class rotating_file_sink : public base_sink < Mutex >
{
public:
rotating_file_sink(const filename_t &base_filename, const filename_t &extension,
std::size_t max_size, std::size_t max_files,
bool force_flush = false) :
std::size_t max_size, std::size_t max_files ) :
_base_filename(base_filename),
_extension(extension),
_max_size(max_size),
_max_files(max_files),
_current_size(0),
_file_helper(force_flush)
_file_helper()
{
_file_helper.open(calc_filename(_base_filename, 0, _extension));
_current_size = _file_helper.size(); //expensive. called only once
@@ -142,11 +146,11 @@ typedef rotating_file_sink<std::mutex> rotating_file_sink_mt;
typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
/*
* Default generator of daily log file names.
*/
* Default generator of daily log file names.
*/
struct default_daily_file_name_calculator
{
//Create filename for the form basename.YYYY-MM-DD_hh-mm.extension
// Create filename for the form basename.YYYY-MM-DD_hh-mm.extension
static filename_t calc_filename(const filename_t& basename, const filename_t& extension)
{
std::tm tm = spdlog::details::os::localtime();
@@ -157,11 +161,11 @@ struct default_daily_file_name_calculator
};
/*
* Generator of daily log file names in format basename.YYYY-MM-DD.extension
*/
* Generator of daily log file names in format basename.YYYY-MM-DD.extension
*/
struct dateonly_daily_file_name_calculator
{
//Create filename for the form basename.YYYY-MM-DD.extension
// Create filename for the form basename.YYYY-MM-DD.extension
static filename_t calc_filename(const filename_t& basename, const filename_t& extension)
{
std::tm tm = spdlog::details::os::localtime();
@@ -172,8 +176,8 @@ 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>
class daily_file_sink :public base_sink < Mutex >
{
@@ -183,12 +187,10 @@ public:
const filename_t& base_filename,
const filename_t& extension,
int rotation_hour,
int rotation_minute,
bool force_flush = false) : _base_filename(base_filename),
int rotation_minute) : _base_filename(base_filename),
_extension(extension),
_rotation_h(rotation_hour),
_rotation_m(rotation_minute),
_file_helper(force_flush)
_rotation_m(rotation_minute)
{
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");

View File

@@ -27,7 +27,7 @@ protected:
};
typedef null_sink<details::null_mutex> null_sink_st;
typedef null_sink<std::mutex> null_sink_mt;
typedef null_sink<details::null_mutex> null_sink_mt;
}
}

View File

@@ -15,10 +15,39 @@ namespace sinks
class sink
{
public:
sink()
{
_level = level::trace;
}
virtual ~sink() {}
virtual void log(const details::log_msg& msg) = 0;
virtual void flush() = 0;
bool should_log(level::level_enum msg_level) const;
void set_level(level::level_enum log_level);
level::level_enum level() const;
private:
level_t _level;
};
inline bool sink::should_log(level::level_enum msg_level) const
{
return msg_level >= _level.load(std::memory_order_relaxed);
}
inline void sink::set_level(level::level_enum log_level)
{
_level.store(log_level);
}
inline level::level_enum sink::level() const
{
return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed));
}
}
}

View File

@@ -18,11 +18,12 @@ namespace sinks
{
template <class Mutex>
class stdout_sink : public base_sink<Mutex>
class stdout_sink: public base_sink<Mutex>
{
using MyType = stdout_sink<Mutex>;
public:
stdout_sink() {}
stdout_sink()
{}
static std::shared_ptr<MyType> instance()
{
static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
@@ -46,11 +47,12 @@ typedef stdout_sink<std::mutex> stdout_sink_mt;
template <class Mutex>
class stderr_sink : public base_sink<Mutex>
class stderr_sink: public base_sink<Mutex>
{
using MyType = stderr_sink<Mutex>;
public:
stderr_sink() {}
stderr_sink()
{}
static std::shared_ptr<MyType> instance()
{
static std::shared_ptr<MyType> instance = std::make_shared<MyType>();

View File

@@ -5,10 +5,11 @@
#pragma once
#if defined(__linux__) || defined(__APPLE__)
#include <spdlog/common.h>
#ifdef SPDLOG_ENABLE_SYSLOG
#include <spdlog/sinks/sink.h>
#include <spdlog/common.h>
#include <spdlog/details/log_msg.h>
#include <array>

View File

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

View File

@@ -2,12 +2,13 @@
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
// spdlog main header file.
// see example.cpp for usage example
#pragma once
#define SPDLOG_VERSION "0.12.0"
#include <spdlog/tweakme.h>
#include <spdlog/common.h>
#include <spdlog/logger.h>
@@ -20,15 +21,13 @@
namespace spdlog
{
// Return an existing logger or nullptr if a logger with such name doesn't exist.
// Examples:
//
// spdlog::get("mylog")->info("Hello");
// auto logger = spdlog::get("mylog");
// logger.info("This is another message" , x, y, z);
// logger.info() << "This is another message" << x << y << z;
// Return an existing logger or nullptr if a logger with such name doesn't exist.
// example: spdlog::get("my_logger")->info("hello {}", "world");
//
std::shared_ptr<logger> get(const std::string& name);
//
// Set global formatting
// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v");
@@ -41,6 +40,11 @@ void set_formatter(formatter_ptr f);
//
void set_level(level::level_enum log_level);
//
// Set global error handler
//
void set_error_handler(log_err_handler);
//
// Turn on async mode (off by default) and set the queue size for each async_logger.
// effective only for loggers created after this call.
@@ -64,39 +68,50 @@ void set_sync_mode();
//
// Create and register multi/single basic file logger
// Create and register multi/single threaded basic file logger.
// Basic logger simply writes to given file without any limitatons or rotations.
//
std::shared_ptr<logger> basic_logger_mt(const std::string& logger_name, const filename_t& filename,bool force_flush = false);
std::shared_ptr<logger> basic_logger_st(const std::string& logger_name, const filename_t& filename, bool force_flush = false);
std::shared_ptr<logger> basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate = false);
std::shared_ptr<logger> basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate = false);
//
// Create and register multi/single threaded rotating file logger
//
std::shared_ptr<logger> rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files, bool force_flush = false);
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, bool force_flush = false);
std::shared_ptr<logger> rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files);
std::shared_ptr<logger> rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files);
//
// Create file logger which creates new file on the given time (default in midnight):
//
std::shared_ptr<logger> daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0, bool force_flush = false);
std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0, bool force_flush = false);
std::shared_ptr<logger> daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0);
std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0);
//
// Create and register stdout/stderr loggers
//
std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name, bool color = false);
std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name, bool color = false);
std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name, bool color = false);
std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name, bool color = false);
std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name);
std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name);
std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name);
std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name);
//
// Create and register colored stdout/stderr loggers
//
std::shared_ptr<logger> stdout_color_mt(const std::string& logger_name);
std::shared_ptr<logger> stdout_color_st(const std::string& logger_name);
std::shared_ptr<logger> stderr_color_mt(const std::string& logger_name);
std::shared_ptr<logger> stderr_color_st(const std::string& logger_name);
//
// Create and register a syslog logger
//
#if defined(__linux__) || defined(__APPLE__)
#ifdef SPDLOG_ENABLE_SYSLOG
std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0);
#endif
#if defined(__ANDROID__)
std::shared_ptr<logger> android_logger(const std::string& logger_name, const std::string& tag = "spdlog");
#endif
// Create and register a logger a single sink
std::shared_ptr<logger> create(const std::string& logger_name, const sink_ptr& sink);
@@ -108,7 +123,8 @@ std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_b
// Create and register a logger with templated sink type
// Example: spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename", "txt");
// Example:
// spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename", "txt");
template <typename Sink, typename... Args>
std::shared_ptr<spdlog::logger> create(const std::string& logger_name, Args...);
@@ -116,10 +132,15 @@ std::shared_ptr<spdlog::logger> create(const std::string& logger_name, Args...);
// Register the given logger with the given name
void register_logger(std::shared_ptr<logger> logger);
// Apply a user defined function on all registered loggers
// Example:
// spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->flush();});
void apply_all(std::function<void(std::shared_ptr<logger>)> fun);
// Drop the reference to the given logger
void drop(const std::string &name);
// Drop all references
// Drop all references from the registry
void drop_all();

View File

@@ -11,6 +11,7 @@
//
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// 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.
@@ -48,7 +49,6 @@
// #define SPDLOG_NO_NAME
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable the SPDLOG_DEBUG/SPDLOG_TRACE macros.
//
@@ -92,3 +92,17 @@
//
// #define SPDLOG_FMT_EXTERNAL
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable syslog (disabled by default)
//
// #define SPDLOG_ENABLE_SYSLOG
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment to prevent child processes from inheriting log file descriptors
//
// #define SPDLOG_PREVENT_CHILD_FD
///////////////////////////////////////////////////////////////////////////////

64
tests/errors.cpp Normal file
View File

@@ -0,0 +1,64 @@
/*
* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
*/
#include "includes.h"
#include<iostream>
TEST_CASE("default_error_handler", "[errors]]")
{
prepare_logdir();
std::string filename = "logs/simple_log.txt";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
logger->set_pattern("%v");
logger->info("Test message {} {}", 1);
logger->info("Test message {}", 2);
logger->flush();
REQUIRE(file_contents(filename) == std::string("Test message 2\n"));
REQUIRE(count_lines(filename) == 1);
}
struct custom_ex {};
TEST_CASE("custom_error_handler", "[errors]]")
{
prepare_logdir();
std::string filename = "logs/simple_log.txt";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
logger->flush_on(spdlog::level::info);
logger->set_error_handler([=](const std::string& msg)
{
throw custom_ex();
});
logger->info("Good message #1");
REQUIRE_THROWS_AS(logger->info("Bad format msg {} {}", "xxx"), custom_ex);
logger->info("Good message #2");
REQUIRE(count_lines(filename) == 2);
}
TEST_CASE("async_error_handler", "[errors]]")
{
prepare_logdir();
std::string err_msg("log failed with some msg");
spdlog::set_async_mode(128);
std::string filename = "logs/simple_async_log.txt";
{
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
logger->set_error_handler([=](const std::string& msg)
{
std::ofstream ofs("logs/custom_err.txt");
if (!ofs) throw std::runtime_error("Failed open logs/custom_err.txt");
ofs << err_msg;
});
logger->info("Good message #1");
logger->info("Bad format msg {} {}", "xxx");
logger->info("Good message #2");
spdlog::drop("logger"); //force logger to drain the queue and shutdown
spdlog::set_sync_mode();
}
REQUIRE(count_lines(filename) == 2);
REQUIRE(file_contents("logs/custom_err.txt") == err_msg);
}

View File

@@ -12,6 +12,7 @@ static void write_with_helper(file_helper &helper, size_t howmany)
log_msg msg;
msg.formatted << std::string(howmany, '1');
helper.write(msg);
helper.flush();
}
@@ -19,7 +20,7 @@ TEST_CASE("file_helper_filename", "[file_helper::filename()]]")
{
prepare_logdir();
file_helper helper(false);
file_helper helper;
helper.open(target_filename);
REQUIRE(helper.filename() == target_filename);
}
@@ -31,7 +32,7 @@ TEST_CASE("file_helper_size", "[file_helper::size()]]")
prepare_logdir();
size_t expected_size = 123;
{
file_helper helper(true);
file_helper helper;
helper.open(target_filename);
write_with_helper(helper, expected_size);
REQUIRE(static_cast<size_t>(helper.size()) == expected_size);
@@ -44,7 +45,7 @@ TEST_CASE("file_helper_exists", "[file_helper::file_exists()]]")
{
prepare_logdir();
REQUIRE(!file_helper::file_exists(target_filename));
file_helper helper(false);
file_helper helper;
helper.open(target_filename);
REQUIRE(file_helper::file_exists(target_filename));
}
@@ -52,7 +53,7 @@ TEST_CASE("file_helper_exists", "[file_helper::file_exists()]]")
TEST_CASE("file_helper_reopen", "[file_helper::reopen()]]")
{
prepare_logdir();
file_helper helper(true);
file_helper helper;
helper.open(target_filename);
write_with_helper(helper, 12);
REQUIRE(helper.size() == 12);
@@ -64,7 +65,7 @@ TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
{
prepare_logdir();
size_t expected_size = 14;
file_helper helper(true);
file_helper helper;
helper.open(target_filename);
write_with_helper(helper, expected_size);
REQUIRE(helper.size() == expected_size);

View File

@@ -18,22 +18,40 @@ TEST_CASE("simple_file_logger", "[simple_logger]]")
logger->flush();
REQUIRE(file_contents(filename) == std::string("Test message 1\nTest message 2\n"));
REQUIRE(count_lines(filename) == 2);
}
TEST_CASE("flush_on", "[flush_on]]")
{
prepare_logdir();
std::string filename = "logs/simple_log.txt";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
logger->set_level(spdlog::level::trace);
logger->flush_on(spdlog::level::info);
logger->trace("Should not be flushed");
REQUIRE(count_lines(filename) == 0);
logger->info("Test message {}", 1);
logger->info("Test message {}", 2);
logger->flush();
REQUIRE(file_contents(filename) == std::string("Should not be flushed\nTest message 1\nTest message 2\n"));
REQUIRE(count_lines(filename) == 3);
}
TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
{
prepare_logdir();
std::string basename = "logs/rotating_log";
auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0, true);
auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);
logger->flush();
auto filename = basename + ".txt";
REQUIRE(count_lines(filename) == 10);
for (int i = 0; i < 1000; i++)
logger->info("Test message {}", i);
}
@@ -41,7 +59,7 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
{
prepare_logdir();
std::string basename = "logs/rotating_log";
auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 1, false);
auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 1);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);
@@ -60,7 +78,6 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
TEST_CASE("daily_logger", "[daily_logger]]")
{
prepare_logdir();
//calculate filename (time based)
std::string basename = "logs/daily_log";
@@ -68,7 +85,8 @@ TEST_CASE("daily_logger", "[daily_logger]]")
fmt::MemoryWriter w;
w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
auto logger = spdlog::daily_logger_mt("logger", basename, 0, 0, true);
auto logger = spdlog::daily_logger_mt("logger", basename, 0, 0);
logger->flush_on(spdlog::level::info);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);
@@ -90,10 +108,10 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger_dateonly]]")
fmt::MemoryWriter w;
w.write("{}_{:04d}-{:02d}-{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0, true);
auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);
logger->flush();
auto filename = w.str();
REQUIRE(count_lines(filename) == 10);
}
@@ -122,10 +140,11 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]")
fmt::MemoryWriter w;
w.write("{}{:04d}{:02d}{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0, true);
auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0);
for (int i = 0; i < 10; ++i)
logger->info("Test message {}", i);
logger->flush();
auto filename = w.str();
REQUIRE(count_lines(filename) == 10);
}

View File

@@ -49,24 +49,7 @@ TEST_CASE("log_levels", "[log_levels]")
REQUIRE(log_info("Hello", spdlog::level::trace) == "Hello");
}
TEST_CASE("invalid_format", "[format]")
{
using namespace spdlog::sinks;
spdlog::logger null_logger("null_logger", std::make_shared<null_sink_st>());
REQUIRE_THROWS_AS(
null_logger.info("{} {}", "first"),
spdlog::spdlog_ex);
REQUIRE_THROWS_AS(
null_logger.info("{0:f}", "aads"),
spdlog::spdlog_ex);
REQUIRE_THROWS_AS(
null_logger.info("{0:kk}", 123),
spdlog::spdlog_ex);
}

View File

@@ -1,6 +1,7 @@
#include "includes.h"
static const char *tested_logger_name = "null_logger";
static const char *tested_logger_name2 = "null_logger2";
TEST_CASE("register_drop", "[registry]")
{
@@ -22,6 +23,34 @@ TEST_CASE("explicit register" "[registry]")
REQUIRE_THROWS_AS(spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name), spdlog::spdlog_ex);
}
TEST_CASE("apply_all" "[registry]")
{
spdlog::drop_all();
auto logger = std::make_shared<spdlog::logger>(tested_logger_name, std::make_shared<spdlog::sinks::null_sink_st>());
spdlog::register_logger(logger);
auto logger2 = std::make_shared<spdlog::logger>(tested_logger_name2, std::make_shared<spdlog::sinks::null_sink_st>());
spdlog::register_logger(logger2);
int counter = 0;
spdlog::apply_all([&counter](std::shared_ptr<spdlog::logger> l)
{
counter++;
});
REQUIRE(counter == 2);
counter = 0;
spdlog::drop(tested_logger_name2);
spdlog::apply_all([&counter](std::shared_ptr<spdlog::logger> l)
{
REQUIRE(l->name() == tested_logger_name);
counter++;
}
);
REQUIRE(counter == 1);
}
TEST_CASE("drop" "[registry]")
{
spdlog::drop_all();
@@ -34,10 +63,10 @@ TEST_CASE("drop_all" "[registry]")
{
spdlog::drop_all();
spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name);
spdlog::create<spdlog::sinks::null_sink_mt>("name2");
spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name2);
spdlog::drop_all();
REQUIRE_FALSE(spdlog::get(tested_logger_name));
REQUIRE_FALSE(spdlog::get("name2"));
REQUIRE_FALSE(spdlog::get(tested_logger_name));
}
@@ -51,3 +80,5 @@ TEST_CASE("drop non existing" "[registry]")
spdlog::drop_all();
}

View File

@@ -125,6 +125,7 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="errors.cpp" />
<ClCompile Include="file_helper.cpp" />
<ClCompile Include="file_log.cpp" />
<ClCompile Include="format.cpp" />

View File

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