Compare commits

...

114 Commits

Author SHA1 Message Date
gabime
c83c9a3193 Version 1.2.0 2018-10-07 22:31:36 +03:00
Gabi Melman
410c46f1ab Update README.md 2018-10-06 15:38:26 +03:00
gabime
1b8bf35acc Micro optimize: don't init log_msg.msg_id on construction. It will be set anyway later 2018-10-06 01:49:34 +03:00
gabime
aa47ac85c9 add constexpr to hex chars 2018-10-05 23:23:09 +03:00
gabime
3eadda9466 code formating 2018-10-05 15:21:05 +03:00
gabime
dea6a7c217 Tests: Fixed some clang-tidy warnings 2018-10-05 15:20:14 +03:00
gabime
887a104dd0 Fixed clang-tidy warning 2018-10-05 15:19:03 +03:00
gabime
1808e3c4c8 Fixed bug in async_bench 2018-10-05 14:52:38 +03:00
gabime
1f4cae4bf7 Changed forward<log_msg> to std::move(log_msg) in thread pool 2018-10-05 14:50:30 +03:00
gabime
3b009f5aa6 Changed forward<worker_ptr> to std::move in async logger 2018-10-05 14:33:53 +03:00
gabime
36112371c0 Reverted const qualifier to log_msg& args, fixed issue #849, and added counter tests 2018-10-05 14:23:37 +03:00
gabime
2fa538779f Fixed static_assert 2018-10-04 02:20:47 +03:00
gabime
b7a6659451 code formatting 2018-10-04 02:10:46 +03:00
gabime
102c31a04c Accecpt iterators by value in logger ctor insted of ref 2018-10-04 02:06:39 +03:00
gabime
10000c383a Better error message if WideCharToMultiByte fails 2018-10-03 00:26:58 +03:00
gabime
8b42b7d269 Fix support for wchar to utf8 under windows (fix issue #851 and #764) 2018-10-02 18:27:49 +03:00
gabime
17702969fa Upgrded to fmt version 5.2.1 2018-10-02 16:14:39 +03:00
Gabi Melman
cc3613e012 Update README.md 2018-10-02 15:24:43 +03:00
Gabi Melman
0258418a99 Merge pull request #853 from blackball/v1.x
return nothing if it's a void function.
2018-10-02 15:22:58 +03:00
Hui
397c2a934f Merge pull request #1 from blackball/blackball-patch-1
Fix return type in README file.
2018-10-02 19:22:02 +08:00
Hui
796986f38c Fix return type in README file.
void function should not return 1.
2018-10-02 19:21:41 +08:00
Gabi Melman
c5011181bb Update log_msg.h 2018-10-01 14:27:43 +03:00
gabime
dace099348 Added fmt_helper tests 2018-09-28 01:30:29 +03:00
gabime
0876e39c4f pad3 small optimization 2018-09-28 01:27:37 +03:00
gabime
0b516733db user buffer.clear() insread of resize(0) 2018-09-27 17:08:21 +03:00
Gabi Melman
e15deead32 Update README.md 2018-09-27 03:59:43 +03:00
gabime
18df6138a7 clang-format 2018-09-27 02:03:12 +03:00
gabime
8c125ed009 Added clang-tidy script 2018-09-27 02:02:13 +03:00
gabime
4720b703f4 Fixed clang-tidy warnings 2018-09-27 01:58:39 +03:00
gabime
cd8e15dcd1 Fixed clang-tidy warnings 2018-09-27 01:39:26 +03:00
gabime
a06d32ae19 Fixed clang-tidy warnings 2018-09-27 01:14:35 +03:00
gabime
7af3f014af Fixed clang-tidy warning 2018-09-27 00:59:28 +03:00
gabime
8e80081f99 Fixed clang-tidy warning 2018-09-27 00:56:19 +03:00
gabime
14c0417f3e Fixed clang-tidy warning 2018-09-27 00:47:09 +03:00
gabime
0879dea444 Fixed clang-tidy warning 2018-09-27 00:39:17 +03:00
gabime
a8c4aef6bd fix typo 2018-09-26 23:50:47 +03:00
gabime
669a66f18a format 2018-09-26 23:48:59 +03:00
gabime
e8dae26176 Moved spdlog::make_unique to spdlog::details::make_unique and prevent T to be array 2018-09-26 23:48:36 +03:00
gabime
e3a66473b2 Small cleanup in set_formatter 2018-09-26 15:53:54 +03:00
gabime
7704e41336 Fixed issue #833 2018-09-26 15:43:23 +03:00
gabime
a74bbe7381 base_sink default ctor 2018-09-26 15:41:57 +03:00
gabime
729ec21629 Added virtual protected functions to base_sink 2018-09-26 15:29:19 +03:00
gabime
b393715bee Added virtual protected functions to base_sink 2018-09-26 15:27:48 +03:00
gabime
5ec4e60424 Replaced SPDLOG_FINAL macro with final 2018-09-26 15:26:34 +03:00
gabime
5cd24f3033 Replaced SPDLOG_FINAL macro with final 2018-09-26 15:26:27 +03:00
gabime
27a03c5cec Replaced SPDLOG_FINAL macro with final 2018-09-26 15:24:22 +03:00
gabime
5d34d21f40 Fixed issue #842 in tests 2018-09-26 14:45:57 +03:00
gabime
ca8accbaa8 Replaced SPDLOG_FINAL macro with final 2018-09-26 14:45:38 +03:00
gabime
65defd3806 Replaced SPDLOG_FINAL macro with final 2018-09-26 14:33:37 +03:00
Gabi Melman
be7e7237e9 Update README.md 2018-09-25 01:51:03 +03:00
gabime
234cb2dfba Updated becnhmark info 2018-09-25 01:43:05 +03:00
Gabi Melman
b922ae0fb8 Update README.md 2018-09-25 01:26:17 +03:00
gabime
8649fb5118 Added binary logging example 2018-09-25 01:25:10 +03:00
Gabi Melman
a4bae6aba9 Update bench.cpp 2018-09-25 01:22:28 +03:00
gabime
808bc1f4ed clang format 2018-09-25 01:11:36 +03:00
gabime
41d879e292 Added support for binary logging using to_hex(..) 2018-09-25 01:03:57 +03:00
Gabi Melman
2e7b3cae2a Update bench.cpp 2018-09-21 11:22:57 +03:00
gabime
a0ae62a733 format.sh 2018-09-18 00:19:16 +03:00
gabime
06eb69b93a format.sh 2018-09-18 00:18:22 +03:00
gabime
7025ff4280 format.sh don't format bundled src 2018-09-18 00:16:13 +03:00
gabime
2fa7410c0e Improve rotating sink error handling 2018-09-18 00:09:58 +03:00
gabime
3771d12992 Upgraded to fmt ver 5.2.0 2018-09-17 14:40:52 +03:00
Gabi Melman
f4ac67ae1c Merge pull request #837 from myrgy/missing_include_ansicolor_sing
ansicolor_sink.h - add missing sink include
2018-09-17 12:50:17 +03:00
Alexander Dalshov
d48d6939c2 ansicolor_sink.h - add missing sink include 2018-09-17 11:26:40 +03:00
Gabi Melman
188cff7d65 Merge pull request #831 from taketwo/fix-830
Change the default value for SPDLOG_BUILD_xxx
2018-09-12 23:33:36 +03:00
Sergey Alexandrov
75925762e8 Change the default value for SPDLOG_BUILD_xxx
The value based on whether spdlog is used as a third-party dependency or
as a standalone project. If spdlog is included through add_subdirectory,
the tests/examples/benchmarsk are disabled by default, and if spdlog is
configured standalone, then they are enabled by default.
2018-09-12 16:11:45 -04:00
Gabi Melman
f2ac7d730c Update README.md 2018-09-07 02:12:28 +03:00
Gabi Melman
c5c1c5458b Update README.md 2018-09-07 02:09:13 +03:00
Gabi Melman
1649597eae Update README.md 2018-09-07 02:07:25 +03:00
Gabi Melman
1cb49bfe72 Merge pull request #824 from gabime/revert-822-patch-1
Revert "Add `conda` to package managers in README"
2018-09-06 13:31:09 +03:00
Gabi Melman
1aa4b657d6 Revert "Add conda to package managers in README" 2018-09-06 13:30:25 +03:00
Gabi Melman
d803e7003f Merge pull request #822 from epruesse/patch-1
Add `conda` to package managers in README
2018-09-06 06:02:45 +03:00
Elmar Pruesse
18efcd62ff Add conda to package managers in README 2018-09-05 19:25:11 -06:00
Gabi Melman
9fda1cb421 Merge pull request #813 from jwnimmer-tri/dist_sink_reset_v1.x
Add set_sinks method to dist_sink
2018-09-04 23:56:03 +03:00
Gabi Melman
bcc6db4a06 Update README.md 2018-09-04 19:34:59 +03:00
Gabi Melman
37cd707294 Fix issue #819 2018-09-03 19:08:57 +03:00
gabime
3698c1d2f1 added spdlog namespace when calling make_unique 2018-09-02 01:27:28 +03:00
Gabi Melman
00acb8ba41 Update registry.h 2018-09-01 22:41:56 +03:00
Gabi Melman
a6ee1cf590 fix vc compliation error about make_unique 2018-09-01 21:56:19 +03:00
gabime
ebce97947d use make_unique 2018-09-01 21:30:05 +03:00
gabime
f4bbe8b2b3 Fixed some extra warnings from clang 2018-08-31 14:17:11 +03:00
Gabi Melman
e52e258f15 Update README.md 2018-08-31 04:14:23 +03:00
Jeremy Nimmer
679fcd787f Add set_sinks method to dist_sink
This allows users to set exactly the sinks they want, even if other
unknown application code has added bespoke sinks in the meantime.
2018-08-29 12:29:32 -04:00
Gabi Melman
f3798159e7 Merge pull request #814 from maciekgajewski/no-pessimizing-move
Pessimizing move removed
2018-08-29 19:21:56 +03:00
Maciej Gajewski
c4de214cea Pessimizing move removed 2018-08-29 17:21:38 +02:00
gabime
6c5bbca0c1 Added clone tests 2018-08-25 18:10:42 +03:00
gabime
447a6a15d9 Fixed readme 2018-08-25 17:57:41 +03:00
gabime
5d7845c138 Added "clone()" support to loggers 2018-08-25 17:55:31 +03:00
gabime
91d8869f36 Added "clone()" support to loggers 2018-08-25 17:35:20 +03:00
Gabi Melman
1f8b2cbb8b Update file_helper.h 2018-08-24 13:27:49 +03:00
Gabi Melman
d0cfca0820 Update file_helper.h 2018-08-24 13:27:10 +03:00
Gabi Melman
f6049cd333 Update file_helper.h 2018-08-24 13:26:45 +03:00
Gabi Melman
a25fd62349 Merge pull request #806 from emruiz81/v1.x
WIN32: Don't deny access to file sinks from other processes
2018-08-23 19:15:28 +03:00
eruiz
39492436ec WIN32: Don't deny access to file sinks from other processes 2018-08-23 12:20:26 -03:00
Gabi Melman
df962e5c53 Merge pull request #804 from DanielChabrowski/globals-include
Add missing include in console_globals.h
2018-08-23 00:12:17 +03:00
Daniel Chabrowski
2990126054 Change console_globals include to windows.h only 2018-08-22 22:47:16 +02:00
Daniel Chabrowski
3edc7f1d18 Add missing include in console_globals.h 2018-08-22 21:46:46 +02:00
Gabi Melman
2870afdeae Update stdout_sinks.h 2018-08-21 03:37:29 +03:00
gabime
d3c1ad29a0 Optimize logging for const char* messages 2018-08-20 12:43:31 +03:00
Gabi Melman
23db7a213d Merge pull request #801 from yhchen/v1.x
remove invalid files from project
2018-08-19 13:02:35 +03:00
yhchen
3151081ff3 remove invalid files from project 2018-08-19 17:21:15 +08:00
gabime
0758b39061 Stop compilation on first error 2018-08-18 02:19:52 +03:00
gabime
45d3c8341c Fixed mpmc_q test for osx in travis 2018-08-17 17:45:48 +03:00
gabime
8418131ae3 Fixed mpmc_q test for osx in travis 2018-08-17 17:30:33 +03:00
gabime
9ad9cfb898 Fixed mpmc_q test for osx in travis 2018-08-17 17:17:16 +03:00
gabime
a281d21fbf Removed valgrind from travis 2018-08-17 16:26:52 +03:00
gabime
af5962450e Try adding osx to travis 2018-08-17 16:15:02 +03:00
gabime
3b3af1ab1e Try adding osx to travis 2018-08-17 16:00:14 +03:00
gabime
acd7a88bf2 Try adding osx to travis 2018-08-17 15:59:52 +03:00
Gabi Melman
176cab4fee Update file_helper.h 2018-08-17 14:07:49 +03:00
Gabi Melman
28435dc736 Normalized spdlog includes in file_helper 2018-08-17 14:06:48 +03:00
gabime
a58d7594cb Fixed issue #798 and added -Wconversion compiler flag to build 2018-08-17 00:32:13 +03:00
Gabi Melman
06181720fb Merge pull request #800 from DanielChabrowski/fix-registry-test
Fix registry test
2018-08-16 19:25:34 +03:00
Daniel Chabrowski
b51c8cfd0f Fix registry test 2018-08-16 17:53:55 +02:00
60 changed files with 6647 additions and 7201 deletions

28
.clang-tidy Normal file
View File

@@ -0,0 +1,28 @@
Checks: 'modernize-*,modernize-use-override,google-*,-google-runtime-references,misc-*,clang-analyzer-*'
WarningsAsErrors: ''
HeaderFilterRegex: 'async.h|async_logger.h|common.h|details|formatter.h|logger.h|sinks|spdlog.h|tweakme.h|version.h'
AnalyzeTemporaryDtors: false
FormatStyle: none
CheckOptions:
- key: google-readability-braces-around-statements.ShortStatementLines
value: '1'
- key: google-readability-function-size.StatementThreshold
value: '800'
- key: google-readability-namespace-comments.ShortNamespaceLines
value: '10'
- key: google-readability-namespace-comments.SpacesBeforeComments
value: '2'
- key: modernize-loop-convert.MaxCopySize
value: '16'
- key: modernize-loop-convert.MinConfidence
value: reasonable
- key: modernize-loop-convert.NamingStyle
value: CamelCase
- key: modernize-pass-by-value.IncludeStyle
value: llvm
- key: modernize-replace-auto-ptr.IncludeStyle
value: llvm
- key: modernize-use-nullptr.NullMacros
value: 'NULL'

View File

@@ -9,7 +9,6 @@ addons: &gcc48
apt: apt:
packages: packages:
- g++-4.8 - g++-4.8
- valgrind
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
@@ -17,7 +16,6 @@ addons: &gcc7
apt: apt:
packages: packages:
- g++-7 - g++-7
- valgrind
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
@@ -25,7 +23,6 @@ addons: &clang35
apt: apt:
packages: packages:
- clang-3.5 - clang-3.5
- valgrind
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
- llvm-toolchain-precise-3.5 - llvm-toolchain-precise-3.5
@@ -34,11 +31,11 @@ addons: &clang6
apt: apt:
packages: packages:
- clang-6.0 - clang-6.0
- valgrind
sources: sources:
- ubuntu-toolchain-r-test - ubuntu-toolchain-r-test
- llvm-toolchain-trusty-6.0 - llvm-toolchain-trusty-6.0
matrix: matrix:
include: include:
# Test gcc-4.8: C++11, Build=Debug/Release # Test gcc-4.8: C++11, Build=Debug/Release
@@ -81,16 +78,21 @@ matrix:
os: linux os: linux
addons: *clang6 addons: *clang6
# osx
- env: BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=Off
os: osx
before_script: before_script:
- if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi - if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi
- if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi - if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export CXX="clang++" CC="clang"; fi
- which $CXX - which $CXX
- which $CC - which $CC
- which valgrind
- $CXX --version - $CXX --version
- cmake --version - cmake --version
- valgrind --version
script: script:
- cd ${TRAVIS_BUILD_DIR} - cd ${TRAVIS_BUILD_DIR}

View File

@@ -4,7 +4,7 @@
# #
cmake_minimum_required(VERSION 3.1) cmake_minimum_required(VERSION 3.1)
project(spdlog VERSION 1.1.0 LANGUAGES CXX) project(spdlog VERSION 1.2.0 LANGUAGES CXX)
include(CTest) include(CTest)
include(CMakeDependentOption) include(CMakeDependentOption)
include(GNUInstallDirs) include(GNUInstallDirs)
@@ -28,7 +28,10 @@ set(CMAKE_CXX_EXTENSIONS OFF)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
add_compile_options("-Wall") add_compile_options("-Wall")
add_compile_options("-Wextra") add_compile_options("-Wextra")
add_compile_options("-Wconversion")
add_compile_options("-pedantic") add_compile_options("-pedantic")
add_compile_options("-Wfatal-errors")
endif() endif()
#--------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------
@@ -42,11 +45,17 @@ include(cmake/sanitizers.cmake)
add_library(spdlog INTERFACE) add_library(spdlog INTERFACE)
add_library(spdlog::spdlog ALIAS spdlog) add_library(spdlog::spdlog ALIAS spdlog)
option(SPDLOG_BUILD_EXAMPLES "Build examples" ON) # Check if spdlog is being used directly or via add_subdirectory
option(SPDLOG_BUILD_BENCH "Build benchmarks" ON) set(SPDLOG_MASTER_PROJECT OFF)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(SPDLOG_MASTER_PROJECT ON)
endif()
option(SPDLOG_BUILD_EXAMPLES "Build examples" ${SPDLOG_MASTER_PROJECT})
option(SPDLOG_BUILD_BENCH "Build benchmarks" ${SPDLOG_MASTER_PROJECT})
cmake_dependent_option(SPDLOG_BUILD_TESTING cmake_dependent_option(SPDLOG_BUILD_TESTING
"Build spdlog tests" ON "Build spdlog tests" ${SPDLOG_MASTER_PROJECT}
"BUILD_TESTING" OFF "BUILD_TESTING" OFF
) )

View File

@@ -3,6 +3,7 @@
Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=master)](https://travis-ci.org/gabime/spdlog)&nbsp; [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true)](https://ci.appveyor.com/project/gabime/spdlog) Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=master)](https://travis-ci.org/gabime/spdlog)&nbsp; [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true)](https://ci.appveyor.com/project/gabime/spdlog)
## Install ## Install
#### Just copy the headers: #### Just copy the headers:
@@ -20,18 +21,17 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
## Platforms ## Platforms
* Linux, FreeBSD, Solaris, AIX * Linux, FreeBSD, OpenBSD, Solaris, AIX
* Windows (vc 2013+, cygwin) * Windows (msvc 2013+, cygwin)
* Mac OSX (clang 3.5+) * macOS (clang 3.5+)
* Android * Android
## Features ## Features
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below). * Very fast (see [benchmarks](#benchmarks) below).
* Headers only, just copy and use. * Headers only, just copy and use.
* Feature rich using the excellent [fmt](https://github.com/fmtlib/fmt) library. * Feature rich formatting, using the excellent [fmt](https://github.com/fmtlib/fmt) library.
* Fast asynchronous mode (optional) * Fast asynchronous mode (optional)
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting. * [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
* Conditional Logging
* Multi/Single threaded loggers. * Multi/Single threaded loggers.
* Various log targets: * Various log targets:
* Rotating log files. * Rotating log files.
@@ -41,7 +41,7 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
* Windows debugger (```OutputDebugString(..)```) * Windows debugger (```OutputDebugString(..)```)
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface). * 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. * Severity based filtering - threshold levels can be modified in runtime as well as in compile time.
* Binary data logging.
## Benchmarks ## Benchmarks
@@ -53,28 +53,26 @@ Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/ben
******************************************************************************* *******************************************************************************
Single thread, 1,000,000 iterations Single thread, 1,000,000 iterations
******************************************************************************* *******************************************************************************
basic_st... Elapsed: 0.226664 4,411,806/sec basic_st... Elapsed: 0.181652 5,505,042/sec
rotating_st... Elapsed: 0.214339 4,665,499/sec rotating_st... Elapsed: 0.181781 5,501,117/sec
daily_st... Elapsed: 0.211292 4,732,797/sec daily_st... Elapsed: 0.187595 5,330,630/sec
null_st... Elapsed: 0.102815 9,726,227/sec null_st... Elapsed: 0.0504704 19,813,602/sec
******************************************************************************* *******************************************************************************
10 threads sharing same logger, 1,000,000 iterations 10 threads sharing same logger, 1,000,000 iterations
******************************************************************************* *******************************************************************************
basic_mt... Elapsed: 0.882268 1,133,441/sec basic_mt... Elapsed: 0.616035 1,623,284/sec
rotating_mt... Elapsed: 0.875515 1,142,184/sec rotating_mt... Elapsed: 0.620344 1,612,008/sec
daily_mt... Elapsed: 0.879573 1,136,915/sec daily_mt... Elapsed: 0.648353 1,542,369/sec
null_mt... Elapsed: 0.220114 4,543,105/sec null_mt... Elapsed: 0.151972 6,580,166/sec
``` ```
#### Asynchronous mode #### Asynchronous mode
``` ```
******************************************************************************* *******************************************************************************
10 threads sharing same logger, 1,000,000 iterations 10 threads sharing same logger, 1,000,000 iterations
******************************************************************************* *******************************************************************************
async... Elapsed: 0.429088 2,330,524/sec async... Elapsed: 0.350066 2,856,606/sec
async... Elapsed: 0.411501 2,430,126/sec async... Elapsed: 0.314865 3,175,960/sec
async... Elapsed: 0.428979 2,331,116/sec async... Elapsed: 0.349851 2,858,358/sec
``` ```
## Usage samples ## Usage samples
@@ -130,7 +128,6 @@ void basic_logfile_example()
catch (const spdlog::spdlog_ex &ex) catch (const spdlog::spdlog_ex &ex)
{ {
std::cout << "Log init failed: " << ex.what() << std::endl; std::cout << "Log init failed: " << ex.what() << std::endl;
return 1;
} }
} }
``` ```
@@ -158,6 +155,18 @@ void daily_example()
``` ```
---
#### Cloning loggers
```c++
// clone a logger and give it new name.
// Useful for creating subsystem loggers from some "root" logger
void clone_example()
{
auto network_logger = spdlog::get("root")->clone("network");
network_logger->info("Logging network stuff..");
}
```
--- ---
#### Periodic flush #### Periodic flush
```c++ ```c++
@@ -167,6 +176,33 @@ spdlog::flush_every(std::chrono::seconds(3));
``` ```
---
#### Binary logging
```c++
// log binary data as hex.
// many types of std::container<char> types can be used.
// ranges are supported too.
// format flags:
// {:X} - print in uppercase.
// {:s} - don't separate each byte with space.
// {:p} - don't print the position on each line start.
// {:n} - don't split the output to lines.
#include "spdlog/fmt/bin_to_hex.h"
void binary_example()
{
auto console = spdlog::get("console");
std::array<char, 80> buf;
console->info("Binary example: {}", spdlog::to_hex(buf));
console->info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10));
// more examples:
// logger->info("uppercase: {:X}", spdlog::to_hex(buf));
// logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf));
// logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf));
}
```
--- ---
#### Logger with multi sinks - each with different format and log level #### Logger with multi sinks - each with different format and log level

View File

@@ -35,6 +35,7 @@ int count_lines(const char *filename)
if ('\n' == ch) if ('\n' == ch)
counter++; counter++;
} }
fclose(infile);
return counter; return counter;
} }

View File

@@ -35,8 +35,8 @@ int main(int argc, char *argv[])
int howmany = 1000000; int howmany = 1000000;
int queue_size = howmany + 2; int queue_size = howmany + 2;
int threads = 10; int threads = 10;
int file_size = 30 * 1024 * 1024; size_t file_size = 30 * 1024 * 1024;
int rotating_files = 5; size_t rotating_files = 5;
try try
{ {
@@ -54,7 +54,7 @@ int main(int argc, char *argv[])
cout << "******************************************************************" cout << "******************************************************************"
"*************\n"; "*************\n";
auto basic_st = spdlog::basic_logger_mt("basic_st", "logs/basic_st.log", true); auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true);
bench(howmany, basic_st); bench(howmany, basic_st);
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files); auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
@@ -89,7 +89,7 @@ int main(int argc, char *argv[])
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
{ {
spdlog::init_thread_pool(queue_size, 1); spdlog::init_thread_pool(static_cast<size_t>(queue_size), 1);
auto as = spdlog::basic_logger_mt<spdlog::async_factory>("async", "logs/basic_async.log", true); auto as = spdlog::basic_logger_mt<spdlog::async_factory>("async", "logs/basic_async.log", true);
bench_mt(howmany, as, threads); bench_mt(howmany, as, threads);
spdlog::drop("async"); spdlog::drop("async");

View File

@@ -35,8 +35,8 @@ int main(int, char *[])
int howmany = 1000000; int howmany = 1000000;
int queue_size = howmany + 2; int queue_size = howmany + 2;
int threads = 10; int threads = 10;
int file_size = 30 * 1024 * 1024; size_t file_size = 30 * 1024 * 1024;
int rotating_files = 5; size_t rotating_files = 5;
try try
{ {
@@ -82,7 +82,7 @@ int main(int, char *[])
for (int i = 0; i < 3; ++i) for (int i = 0; i < 3; ++i)
{ {
spdlog::init_thread_pool(queue_size, 1); spdlog::init_thread_pool(static_cast<size_t>(queue_size), 1);
auto as = spdlog::basic_logger_mt<spdlog::async_factory>("async", "logs/basic_async.log", true); auto as = spdlog::basic_logger_mt<spdlog::async_factory>("async", "logs/basic_async.log", true);
bench_mt(howmany, as, threads); bench_mt(howmany, as, threads);
spdlog::drop("async"); spdlog::drop("async");

2
clang_tidy.sh Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
clang-tidy example/example.cpp -- -I ./include

View File

@@ -1,5 +1,5 @@
CXX ?= g++ CXX ?= g++
CXX_FLAGS = -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1 CXX_FLAGS = -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1 -Wconversion
CXX_RELEASE_FLAGS = -O3 -march=native CXX_RELEASE_FLAGS = -O3 -march=native
CXX_DEBUG_FLAGS= -g CXX_DEBUG_FLAGS= -g

22
example/Makefile-all-warn Normal file
View File

@@ -0,0 +1,22 @@
#-Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded
CXX ?= g++
CXX_FLAGS = -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1 -Wconversion -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-weak-vtables -Wno-global-constructors
CXX_RELEASE_FLAGS = -O3 -march=native
CXX_DEBUG_FLAGS= -g
all: example
debug: example-debug
example: example.cpp
$(CXX) example.cpp -o example $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS)
example-debug: example.cpp
$(CXX) example.cpp -o example-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS)
clean:
rm -f *.o logs/*.txt example example-debug
rebuild: clean all
rebuild-debug: clean debug

View File

@@ -14,10 +14,12 @@ void basic_example();
void rotating_example(); void rotating_example();
void daily_example(); void daily_example();
void async_example(); void async_example();
void binary_example();
void multi_sink_example(); void multi_sink_example();
void user_defined_example(); void user_defined_example();
void err_handler_example(); void err_handler_example();
void syslog_example(); void syslog_example();
void clone_example();
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
@@ -34,9 +36,14 @@ int main(int, char *[])
rotating_example(); rotating_example();
daily_example(); daily_example();
clone_example();
// async logging using a backing thread pool // async logging using a backing thread pool
async_example(); async_example();
// log binary data
binary_example();
// a logger can have multiple targets with different formats // a logger can have multiple targets with different formats
multi_sink_example(); multi_sink_example();
@@ -94,6 +101,7 @@ void stdout_example()
// Customize msg format for all loggers // Customize msg format for all loggers
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v"); spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
console->info("This an info message with custom format"); console->info("This an info message with custom format");
spdlog::set_pattern("%+"); // back to default format
// Compile time log levels // Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON // define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
@@ -122,6 +130,14 @@ void daily_example()
auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30); auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
} }
// clone a logger and give it new name.
// Useful for creating component/subsystem loggers from some "root" logger
void clone_example()
{
auto network_logger = spdlog::get("console")->clone("network");
network_logger->info("Logging network stuff..");
}
#include "spdlog/async.h" #include "spdlog/async.h"
void async_example() void async_example()
{ {
@@ -137,6 +153,29 @@ void async_example()
} }
} }
// log binary data as hex.
// many types of std::container<char> types can be used.
// ranges are supported too.
// format flags:
// {:X} - print in uppercase.
// {:s} - don't separate each byte with space.
// {:p} - don't print the position on each line start.
// {:n} - don't split the output to lines.
#include "spdlog/fmt/bin_to_hex.h"
void binary_example()
{
auto console = spdlog::get("console");
std::array<char, 80> buf;
console->info("Binary example: {}", spdlog::to_hex(buf));
console->info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10));
// more examples:
// logger->info("uppercase: {:X}", spdlog::to_hex(buf));
// logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf));
// logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf));
}
// create logger with 2 targets with different log levels and formats // create logger with 2 targets with different log levels and formats
// the console will show only warnings or errors, while the file will log all // the console will show only warnings or errors, while the file will log all

View File

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

View File

@@ -40,13 +40,13 @@ namespace details {
class thread_pool; class thread_pool;
} }
class async_logger SPDLOG_FINAL : public std::enable_shared_from_this<async_logger>, public logger class async_logger final : public std::enable_shared_from_this<async_logger>, public logger
{ {
friend class details::thread_pool; friend class details::thread_pool;
public: public:
template<typename It> template<typename It>
async_logger(std::string logger_name, const It &begin, const It &end, std::weak_ptr<details::thread_pool> tp, async_logger(std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp,
async_overflow_policy overflow_policy = async_overflow_policy::block); async_overflow_policy overflow_policy = async_overflow_policy::block);
async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp,
@@ -55,11 +55,13 @@ public:
async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp,
async_overflow_policy overflow_policy = async_overflow_policy::block); async_overflow_policy overflow_policy = async_overflow_policy::block);
std::shared_ptr<logger> clone(std::string new_name) override;
protected: protected:
void sink_it_(details::log_msg &msg) override; void sink_it_(details::log_msg &msg) override;
void flush_() override; void flush_() override;
void backend_log_(details::log_msg &incoming_log_msg); void backend_log_(const details::log_msg &incoming_log_msg);
void backend_flush_(); void backend_flush_();
private: private:

View File

@@ -14,6 +14,7 @@
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
#include <string> #include <string>
#include <type_traits>
#include <unordered_map> #include <unordered_map>
#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) #if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
@@ -32,13 +33,6 @@
#define SPDLOG_CONSTEXPR constexpr #define SPDLOG_CONSTEXPR constexpr
#endif #endif
// final keyword support. On by default. See tweakme.h
#if defined(SPDLOG_NO_FINAL)
#define SPDLOG_FINAL
#else
#define SPDLOG_FINAL final
#endif
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#define SPDLOG_DEPRECATED __attribute__((deprecated)) #define SPDLOG_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER) #elif defined(_MSC_VER)
@@ -135,8 +129,8 @@ enum class pattern_time_type
class spdlog_ex : public std::exception class spdlog_ex : public std::exception
{ {
public: public:
explicit spdlog_ex(const std::string &msg) explicit spdlog_ex(std::string msg)
: msg_(msg) : msg_(std::move(msg))
{ {
} }
@@ -174,4 +168,19 @@ using filename_t = std::string;
{ \ { \
err_handler_("Unknown exeption in logger"); \ err_handler_("Unknown exeption in logger"); \
} }
namespace details {
// make_unique support for pre c++14
#if __cplusplus >= 201402L // C++14 and beyond
using std::make_unique;
#else
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args &&... args)
{
static_assert(!std::is_array<T>::value, "arrays not supported");
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
#endif
} // namespace details
} // namespace spdlog } // namespace spdlog

View File

@@ -16,7 +16,7 @@
template<typename It> template<typename It>
inline spdlog::async_logger::async_logger( inline spdlog::async_logger::async_logger(
std::string logger_name, const It &begin, const It &end, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy) std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
: logger(std::move(logger_name), begin, end) : logger(std::move(logger_name), begin, end)
, thread_pool_(tp) , thread_pool_(tp)
, overflow_policy_(overflow_policy) , overflow_policy_(overflow_policy)
@@ -67,7 +67,7 @@ inline void spdlog::async_logger::flush_()
// //
// backend functions - called from the thread pool to do the actual job // backend functions - called from the thread pool to do the actual job
// //
inline void spdlog::async_logger::backend_log_(details::log_msg &incoming_log_msg) inline void spdlog::async_logger::backend_log_(const details::log_msg &incoming_log_msg)
{ {
try try
{ {
@@ -98,3 +98,13 @@ inline void spdlog::async_logger::backend_flush_()
} }
SPDLOG_CATCH_AND_HANDLE SPDLOG_CATCH_AND_HANDLE
} }
inline std::shared_ptr<spdlog::logger> spdlog::async_logger::clone(std::string new_name)
{
auto cloned = std::make_shared<spdlog::async_logger>(std::move(new_name), sinks_.begin(), sinks_.end(), thread_pool_, overflow_policy_);
cloned->set_level(this->level());
cloned->flush_on(this->flush_level());
cloned->set_error_handler(this->error_handler());
return std::move(cloned);
}

View File

@@ -8,6 +8,19 @@
#include <cstdio> #include <cstdio>
#include <mutex> #include <mutex>
#ifdef _WIN32
#ifndef NOMINMAX
#define NOMINMAX // prevent windows redefining min/max
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#endif
namespace spdlog { namespace spdlog {
namespace details { namespace details {
struct console_stdout struct console_stdout

View File

@@ -5,13 +5,12 @@
#pragma once #pragma once
// Helper class for file sink // Helper class for file sinks.
// When failing to open a file, retry several times(5) with small delay between // When failing to open a file, retry several times(5) with a delay interval(10 ms).
// the tries(10 ms) // Throw spdlog_ex exception on errors.
// Throw spdlog_ex exception on errors
#include "../details/log_msg.h" #include "spdlog/details/log_msg.h"
#include "../details/os.h" #include "spdlog/details/os.h"
#include <cerrno> #include <cerrno>
#include <chrono> #include <chrono>
@@ -146,7 +145,7 @@ public:
} }
private: private:
FILE *fd_{nullptr}; std::FILE *fd_{nullptr};
filename_t _filename; filename_t _filename;
}; };
} // namespace details } // namespace details

View File

@@ -22,12 +22,8 @@ inline void append_str(const std::string &str, fmt::basic_memory_buffer<char, Bu
template<size_t Buffer_Size> template<size_t Buffer_Size>
inline void append_c_str(const char *c_str, fmt::basic_memory_buffer<char, Buffer_Size> &dest) inline void append_c_str(const char *c_str, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{ {
char ch; auto len = std::char_traits<char>::length(c_str);
while ((ch = *c_str) != '\0') dest.append(c_str, c_str + len);
{
dest.push_back(ch);
++c_str;
}
} }
template<size_t Buffer_Size1, size_t Buffer_Size2> template<size_t Buffer_Size1, size_t Buffer_Size2>
@@ -54,14 +50,14 @@ inline void pad2(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
} }
if (n > 9) // 10-99 if (n > 9) // 10-99
{ {
dest.push_back('0' + static_cast<char>(n / 10)); dest.push_back(static_cast<char>('0' + n / 10));
dest.push_back('0' + static_cast<char>(n % 10)); dest.push_back(static_cast<char>('0' + n % 10));
return; return;
} }
if (n >= 0) // 0-9 if (n >= 0) // 0-9
{ {
dest.push_back('0'); dest.push_back('0');
dest.push_back('0' + static_cast<char>(n)); dest.push_back(static_cast<char>('0' + n));
return; return;
} }
// negatives (unlikely, but just in case, let fmt deal with it) // negatives (unlikely, but just in case, let fmt deal with it)
@@ -79,22 +75,22 @@ inline void pad3(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
if (n > 99) // 100-999 if (n > 99) // 100-999
{ {
append_int(n / 100, dest); dest.push_back(static_cast<char>('0' + n / 100));
pad2(n % 100, dest); pad2(n % 100, dest);
return; return;
} }
if (n > 9) // 10-99 if (n > 9) // 10-99
{ {
dest.push_back('0'); dest.push_back('0');
dest.push_back('0' + static_cast<char>(n / 10)); dest.push_back(static_cast<char>('0' + n / 10));
dest.push_back('0' + static_cast<char>(n % 10)); dest.push_back(static_cast<char>('0' + n % 10));
return; return;
} }
if (n >= 0) if (n >= 0)
{ {
dest.push_back('0'); dest.push_back('0');
dest.push_back('0'); dest.push_back('0');
dest.push_back('0' + static_cast<char>(n)); dest.push_back(static_cast<char>('0' + n));
return; return;
} }
// negatives (unlikely, but just in case let fmt deal with it) // negatives (unlikely, but just in case let fmt deal with it)
@@ -119,7 +115,8 @@ inline void pad6(size_t n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
template<typename ToDuration> template<typename ToDuration>
inline ToDuration time_fraction(const log_clock::time_point &tp) inline ToDuration time_fraction(const log_clock::time_point &tp)
{ {
using namespace std::chrono; using std::chrono::duration_cast;
using std::chrono::seconds;
auto duration = tp.time_since_epoch(); auto duration = tp.time_since_epoch();
auto secs = duration_cast<seconds>(duration); auto secs = duration_cast<seconds>(duration);
return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs); return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);

View File

@@ -39,8 +39,9 @@ struct log_msg
log_clock::time_point time; log_clock::time_point time;
size_t thread_id; size_t thread_id;
fmt::memory_buffer raw; fmt::memory_buffer raw;
size_t msg_id{0}; size_t msg_id;
// info about wrapping the formatted text with color
// info about wrapping the formatted text with color (updated by pattern_formatter).
mutable size_t color_range_start{0}; mutable size_t color_range_start{0};
mutable size_t color_range_end{0}; mutable size_t color_range_end{0};
}; };

View File

@@ -5,13 +5,15 @@
#pragma once #pragma once
#include "spdlog/details/fmt_helper.h"
#include <memory> #include <memory>
#include <string> #include <string>
// create logger with given name, sinks and the default pattern formatter // create logger with given name, sinks and the default pattern formatter
// all other ctors will call this one // all other ctors will call this one
template<typename It> template<typename It>
inline spdlog::logger::logger(std::string logger_name, const It &begin, const It &end) inline spdlog::logger::logger(std::string logger_name, It begin, It end)
: name_(std::move(logger_name)) : name_(std::move(logger_name))
, sinks_(begin, end) , sinks_(begin, end)
, level_(level::info) , level_(level::info)
@@ -47,7 +49,8 @@ inline void spdlog::logger::set_formatter(std::unique_ptr<spdlog::formatter> f)
inline void spdlog::logger::set_pattern(std::string pattern, pattern_time_type time_type) inline void spdlog::logger::set_pattern(std::string pattern, pattern_time_type time_type)
{ {
set_formatter(std::unique_ptr<spdlog::formatter>(new pattern_formatter(std::move(pattern), time_type))); auto new_formatter = details::make_unique<spdlog::pattern_formatter>(std::move(pattern), time_type);
set_formatter(std::move(new_formatter));
} }
template<typename... Args> template<typename... Args>
@@ -77,7 +80,7 @@ inline void spdlog::logger::log(level::level_enum lvl, const char *msg)
try try
{ {
details::log_msg log_msg(&name_, lvl); details::log_msg log_msg(&name_, lvl);
fmt::format_to(log_msg.raw, "{}", msg); details::fmt_helper::append_c_str(msg, log_msg.raw);
sink_it_(log_msg); sink_it_(log_msg);
} }
SPDLOG_CATCH_AND_HANDLE SPDLOG_CATCH_AND_HANDLE
@@ -172,6 +175,28 @@ inline void spdlog::logger::critical(const T &msg)
} }
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
inline void wbuf_to_utf8buf(const fmt::wmemory_buffer &wbuf, fmt::memory_buffer &target)
{
int wbuf_size = static_cast<int>(wbuf.size());
if (wbuf_size == 0)
{
return;
}
auto result_size = ::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, NULL, 0, NULL, NULL);
if (result_size > 0)
{
target.resize(result_size);
::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, &target.data()[0], result_size, NULL, NULL);
}
else
{
throw spdlog::spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
}
}
template<typename... Args> template<typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args) inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args)
{ {
@@ -180,15 +205,14 @@ inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const
return; return;
} }
decltype(wstring_converter_)::byte_string utf8_string;
try try
{ {
{ // format to wmemory_buffer and convert to utf8
std::lock_guard<std::mutex> lock(wstring_converter_mutex_); details::log_msg log_msg(&name_, lvl);
utf8_string = wstring_converter_.to_bytes(fmt); fmt::wmemory_buffer wbuf;
} fmt::format_to(wbuf, fmt, args...);
log(lvl, utf8_string.c_str(), args...); wbuf_to_utf8buf(wbuf, log_msg.raw);
sink_it_(log_msg);
} }
SPDLOG_CATCH_AND_HANDLE SPDLOG_CATCH_AND_HANDLE
} }
@@ -268,6 +292,11 @@ inline void spdlog::logger::flush_on(level::level_enum log_level)
flush_level_.store(log_level); flush_level_.store(log_level);
} }
inline spdlog::level::level_enum spdlog::logger::flush_level() const
{
return static_cast<spdlog::level::level_enum>(flush_level_.load(std::memory_order_relaxed));
}
inline bool spdlog::logger::should_flush_(const details::log_msg &msg) inline bool spdlog::logger::should_flush_(const details::log_msg &msg)
{ {
auto flush_level = flush_level_.load(std::memory_order_relaxed); auto flush_level = flush_level_.load(std::memory_order_relaxed);
@@ -343,3 +372,12 @@ inline std::vector<spdlog::sink_ptr> &spdlog::logger::sinks()
{ {
return sinks_; return sinks_;
} }
inline std::shared_ptr<spdlog::logger> spdlog::logger::clone(std::string logger_name)
{
auto cloned = std::make_shared<spdlog::logger>(std::move(logger_name), sinks_.begin(), sinks_.end());
cloned->set_level(this->level());
cloned->flush_on(this->flush_level());
cloned->set_error_handler(this->error_handler());
return cloned;
}

View File

@@ -159,9 +159,9 @@ inline bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mod
{ {
#ifdef _WIN32 #ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES #ifdef SPDLOG_WCHAR_FILENAMES
*fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); *fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
#else #else
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO);
#endif #endif
#else // unix #else // unix
*fp = fopen((filename.c_str()), mode.c_str()); *fp = fopen((filename.c_str()), mode.c_str());

View File

@@ -116,7 +116,7 @@ class B_formatter : public flag_formatter
}; };
// Date and time representation (Thu Aug 23 15:35:46 2014) // Date and time representation (Thu Aug 23 15:35:46 2014)
class c_formatter SPDLOG_FINAL : public flag_formatter class c_formatter final : public flag_formatter
{ {
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
@@ -142,7 +142,7 @@ class c_formatter SPDLOG_FINAL : public flag_formatter
}; };
// year - 2 digit // year - 2 digit
class C_formatter SPDLOG_FINAL : public flag_formatter class C_formatter final : public flag_formatter
{ {
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
@@ -151,7 +151,7 @@ class C_formatter SPDLOG_FINAL : public flag_formatter
}; };
// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01 // Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
class D_formatter SPDLOG_FINAL : public flag_formatter class D_formatter final : public flag_formatter
{ {
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
@@ -164,7 +164,7 @@ class D_formatter SPDLOG_FINAL : public flag_formatter
}; };
// year - 4 digit // year - 4 digit
class Y_formatter SPDLOG_FINAL : public flag_formatter class Y_formatter final : public flag_formatter
{ {
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
@@ -173,7 +173,7 @@ class Y_formatter SPDLOG_FINAL : public flag_formatter
}; };
// month 1-12 // month 1-12
class m_formatter SPDLOG_FINAL : public flag_formatter class m_formatter final : public flag_formatter
{ {
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
@@ -182,7 +182,7 @@ class m_formatter SPDLOG_FINAL : public flag_formatter
}; };
// day of month 1-31 // day of month 1-31
class d_formatter SPDLOG_FINAL : public flag_formatter class d_formatter final : public flag_formatter
{ {
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
@@ -191,7 +191,7 @@ class d_formatter SPDLOG_FINAL : public flag_formatter
}; };
// hours in 24 format 0-23 // hours in 24 format 0-23
class H_formatter SPDLOG_FINAL : public flag_formatter class H_formatter final : public flag_formatter
{ {
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
@@ -200,7 +200,7 @@ class H_formatter SPDLOG_FINAL : public flag_formatter
}; };
// hours in 12 format 1-12 // hours in 12 format 1-12
class I_formatter SPDLOG_FINAL : public flag_formatter class I_formatter final : public flag_formatter
{ {
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
@@ -209,7 +209,7 @@ class I_formatter SPDLOG_FINAL : public flag_formatter
}; };
// minutes 0-59 // minutes 0-59
class M_formatter SPDLOG_FINAL : public flag_formatter class M_formatter final : public flag_formatter
{ {
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
@@ -218,7 +218,7 @@ class M_formatter SPDLOG_FINAL : public flag_formatter
}; };
// seconds 0-59 // seconds 0-59
class S_formatter SPDLOG_FINAL : public flag_formatter class S_formatter final : public flag_formatter
{ {
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
@@ -227,7 +227,7 @@ class S_formatter SPDLOG_FINAL : public flag_formatter
}; };
// milliseconds // milliseconds
class e_formatter SPDLOG_FINAL : public flag_formatter class e_formatter final : public flag_formatter
{ {
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
@@ -237,7 +237,7 @@ class e_formatter SPDLOG_FINAL : public flag_formatter
}; };
// microseconds // microseconds
class f_formatter SPDLOG_FINAL : public flag_formatter class f_formatter final : public flag_formatter
{ {
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
@@ -247,7 +247,7 @@ class f_formatter SPDLOG_FINAL : public flag_formatter
}; };
// nanoseconds // nanoseconds
class F_formatter SPDLOG_FINAL : public flag_formatter class F_formatter final : public flag_formatter
{ {
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
@@ -257,7 +257,7 @@ class F_formatter SPDLOG_FINAL : public flag_formatter
}; };
// seconds since epoch // seconds since epoch
class E_formatter SPDLOG_FINAL : public flag_formatter class E_formatter final : public flag_formatter
{ {
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
@@ -268,7 +268,7 @@ class E_formatter SPDLOG_FINAL : public flag_formatter
}; };
// AM/PM // AM/PM
class p_formatter SPDLOG_FINAL : public flag_formatter class p_formatter final : public flag_formatter
{ {
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
@@ -277,7 +277,7 @@ class p_formatter SPDLOG_FINAL : public flag_formatter
}; };
// 12 hour clock 02:55:02 pm // 12 hour clock 02:55:02 pm
class r_formatter SPDLOG_FINAL : public flag_formatter class r_formatter final : public flag_formatter
{ {
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
@@ -292,7 +292,7 @@ class r_formatter SPDLOG_FINAL : public flag_formatter
}; };
// 24-hour HH:MM time, equivalent to %H:%M // 24-hour HH:MM time, equivalent to %H:%M
class R_formatter SPDLOG_FINAL : public flag_formatter class R_formatter final : public flag_formatter
{ {
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
@@ -303,7 +303,7 @@ class R_formatter SPDLOG_FINAL : public flag_formatter
}; };
// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S // ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
class T_formatter SPDLOG_FINAL : public flag_formatter class T_formatter final : public flag_formatter
{ {
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
@@ -318,7 +318,7 @@ class T_formatter SPDLOG_FINAL : public flag_formatter
}; };
// ISO 8601 offset from UTC in timezone (+-HH:MM) // ISO 8601 offset from UTC in timezone (+-HH:MM)
class z_formatter SPDLOG_FINAL : public flag_formatter class z_formatter final : public flag_formatter
{ {
public: public:
const std::chrono::seconds cache_refresh = std::chrono::seconds(5); const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
@@ -371,7 +371,7 @@ private:
}; };
// Thread id // Thread id
class t_formatter SPDLOG_FINAL : public flag_formatter class t_formatter final : public flag_formatter
{ {
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
@@ -380,7 +380,7 @@ class t_formatter SPDLOG_FINAL : public flag_formatter
}; };
// Current pid // Current pid
class pid_formatter SPDLOG_FINAL : public flag_formatter class pid_formatter final : public flag_formatter
{ {
void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override
{ {
@@ -389,7 +389,7 @@ class pid_formatter SPDLOG_FINAL : public flag_formatter
}; };
// message counter formatter // message counter formatter
class i_formatter SPDLOG_FINAL : public flag_formatter class i_formatter final : public flag_formatter
{ {
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
@@ -397,7 +397,7 @@ class i_formatter SPDLOG_FINAL : public flag_formatter
} }
}; };
class v_formatter SPDLOG_FINAL : public flag_formatter class v_formatter final : public flag_formatter
{ {
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
@@ -405,7 +405,7 @@ class v_formatter SPDLOG_FINAL : public flag_formatter
} }
}; };
class ch_formatter SPDLOG_FINAL : public flag_formatter class ch_formatter final : public flag_formatter
{ {
public: public:
explicit ch_formatter(char ch) explicit ch_formatter(char ch)
@@ -422,7 +422,7 @@ private:
}; };
// aggregate user chars to display as is // aggregate user chars to display as is
class aggregate_formatter SPDLOG_FINAL : public flag_formatter class aggregate_formatter final : public flag_formatter
{ {
public: public:
aggregate_formatter() = default; aggregate_formatter() = default;
@@ -441,14 +441,14 @@ private:
}; };
// mark the color range. expect it to be in the form of "%^colored text%$" // mark the color range. expect it to be in the form of "%^colored text%$"
class color_start_formatter SPDLOG_FINAL : public flag_formatter class color_start_formatter final : public flag_formatter
{ {
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
msg.color_range_start = dest.size(); msg.color_range_start = dest.size();
} }
}; };
class color_stop_formatter SPDLOG_FINAL : public flag_formatter class color_stop_formatter final : public flag_formatter
{ {
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{ {
@@ -458,11 +458,14 @@ class color_stop_formatter SPDLOG_FINAL : public flag_formatter
// Full info formatter // Full info formatter
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v // pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
class full_formatter SPDLOG_FINAL : public flag_formatter class full_formatter final : public flag_formatter
{ {
void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override
{ {
using namespace std::chrono; using std::chrono::duration_cast;
using std::chrono::milliseconds;
using std::chrono::seconds;
#ifndef SPDLOG_NO_DATETIME #ifndef SPDLOG_NO_DATETIME
// cache the date/time part for the next second. // cache the date/time part for the next second.
@@ -471,7 +474,7 @@ class full_formatter SPDLOG_FINAL : public flag_formatter
if (cache_timestamp_ != secs || cached_datetime_.size() == 0) if (cache_timestamp_ != secs || cached_datetime_.size() == 0)
{ {
cached_datetime_.resize(0); cached_datetime_.clear();
cached_datetime_.push_back('['); cached_datetime_.push_back('[');
fmt_helper::append_int(tm_time.tm_year + 1900, cached_datetime_); fmt_helper::append_int(tm_time.tm_year + 1900, cached_datetime_);
cached_datetime_.push_back('-'); cached_datetime_.push_back('-');
@@ -528,7 +531,7 @@ private:
} // namespace details } // namespace details
class pattern_formatter SPDLOG_FINAL : public formatter class pattern_formatter final : public formatter
{ {
public: public:
explicit pattern_formatter( explicit pattern_formatter(
@@ -547,7 +550,7 @@ public:
std::unique_ptr<formatter> clone() const override std::unique_ptr<formatter> clone() const override
{ {
return std::unique_ptr<formatter>(new pattern_formatter(pattern_, pattern_time_type_, eol_)); return details::make_unique<pattern_formatter>(pattern_, pattern_time_type_, eol_);
} }
void format(const details::log_msg &msg, fmt::memory_buffer &dest) override void format(const details::log_msg &msg, fmt::memory_buffer &dest) override
@@ -588,146 +591,145 @@ private:
void handle_flag_(char flag) void handle_flag_(char flag)
{ {
using flag_formatter_ptr = std::unique_ptr<details::flag_formatter>;
switch (flag) switch (flag)
{ {
// logger name // logger name
case 'n': case 'n':
formatters_.push_back(flag_formatter_ptr(new details::name_formatter())); formatters_.push_back(details::make_unique<details::name_formatter>());
break; break;
case 'l': case 'l':
formatters_.push_back(flag_formatter_ptr(new details::level_formatter())); formatters_.push_back(details::make_unique<details::level_formatter>());
break; break;
case 'L': case 'L':
formatters_.push_back(flag_formatter_ptr(new details::short_level_formatter())); formatters_.push_back(details::make_unique<details::short_level_formatter>());
break; break;
case ('t'): case ('t'):
formatters_.push_back(flag_formatter_ptr(new details::t_formatter())); formatters_.push_back(details::make_unique<details::t_formatter>());
break; break;
case ('v'): case ('v'):
formatters_.push_back(flag_formatter_ptr(new details::v_formatter())); formatters_.push_back(details::make_unique<details::v_formatter>());
break; break;
case ('a'): case ('a'):
formatters_.push_back(flag_formatter_ptr(new details::a_formatter())); formatters_.push_back(details::make_unique<details::a_formatter>());
break; break;
case ('A'): case ('A'):
formatters_.push_back(flag_formatter_ptr(new details::A_formatter())); formatters_.push_back(details::make_unique<details::A_formatter>());
break; break;
case ('b'): case ('b'):
case ('h'): case ('h'):
formatters_.push_back(flag_formatter_ptr(new details::b_formatter())); formatters_.push_back(details::make_unique<details::b_formatter>());
break; break;
case ('B'): case ('B'):
formatters_.push_back(flag_formatter_ptr(new details::B_formatter())); formatters_.push_back(details::make_unique<details::B_formatter>());
break; break;
case ('c'): case ('c'):
formatters_.push_back(flag_formatter_ptr(new details::c_formatter())); formatters_.push_back(details::make_unique<details::c_formatter>());
break; break;
case ('C'): case ('C'):
formatters_.push_back(flag_formatter_ptr(new details::C_formatter())); formatters_.push_back(details::make_unique<details::C_formatter>());
break; break;
case ('Y'): case ('Y'):
formatters_.push_back(flag_formatter_ptr(new details::Y_formatter())); formatters_.push_back(details::make_unique<details::Y_formatter>());
break; break;
case ('D'): case ('D'):
case ('x'): case ('x'):
formatters_.push_back(flag_formatter_ptr(new details::D_formatter())); formatters_.push_back(details::make_unique<details::D_formatter>());
break; break;
case ('m'): case ('m'):
formatters_.push_back(flag_formatter_ptr(new details::m_formatter())); formatters_.push_back(details::make_unique<details::m_formatter>());
break; break;
case ('d'): case ('d'):
formatters_.push_back(flag_formatter_ptr(new details::d_formatter())); formatters_.push_back(details::make_unique<details::d_formatter>());
break; break;
case ('H'): case ('H'):
formatters_.push_back(flag_formatter_ptr(new details::H_formatter())); formatters_.push_back(details::make_unique<details::H_formatter>());
break; break;
case ('I'): case ('I'):
formatters_.push_back(flag_formatter_ptr(new details::I_formatter())); formatters_.push_back(details::make_unique<details::I_formatter>());
break; break;
case ('M'): case ('M'):
formatters_.push_back(flag_formatter_ptr(new details::M_formatter())); formatters_.push_back(details::make_unique<details::M_formatter>());
break; break;
case ('S'): case ('S'):
formatters_.push_back(flag_formatter_ptr(new details::S_formatter())); formatters_.push_back(details::make_unique<details::S_formatter>());
break; break;
case ('e'): case ('e'):
formatters_.push_back(flag_formatter_ptr(new details::e_formatter())); formatters_.push_back(details::make_unique<details::e_formatter>());
break; break;
case ('f'): case ('f'):
formatters_.push_back(flag_formatter_ptr(new details::f_formatter())); formatters_.push_back(details::make_unique<details::f_formatter>());
break; break;
case ('F'): case ('F'):
formatters_.push_back(flag_formatter_ptr(new details::F_formatter())); formatters_.push_back(details::make_unique<details::F_formatter>());
break; break;
case ('E'): case ('E'):
formatters_.push_back(flag_formatter_ptr(new details::E_formatter())); formatters_.push_back(details::make_unique<details::E_formatter>());
break; break;
case ('p'): case ('p'):
formatters_.push_back(flag_formatter_ptr(new details::p_formatter())); formatters_.push_back(details::make_unique<details::p_formatter>());
break; break;
case ('r'): case ('r'):
formatters_.push_back(flag_formatter_ptr(new details::r_formatter())); formatters_.push_back(details::make_unique<details::r_formatter>());
break; break;
case ('R'): case ('R'):
formatters_.push_back(flag_formatter_ptr(new details::R_formatter())); formatters_.push_back(details::make_unique<details::R_formatter>());
break; break;
case ('T'): case ('T'):
case ('X'): case ('X'):
formatters_.push_back(flag_formatter_ptr(new details::T_formatter())); formatters_.push_back(details::make_unique<details::T_formatter>());
break; break;
case ('z'): case ('z'):
formatters_.push_back(flag_formatter_ptr(new details::z_formatter())); formatters_.push_back(details::make_unique<details::z_formatter>());
break; break;
case ('+'): case ('+'):
formatters_.push_back(flag_formatter_ptr(new details::full_formatter())); formatters_.push_back(details::make_unique<details::full_formatter>());
break; break;
case ('P'): case ('P'):
formatters_.push_back(flag_formatter_ptr(new details::pid_formatter())); formatters_.push_back(details::make_unique<details::pid_formatter>());
break; break;
#ifdef SPDLOG_ENABLE_MESSAGE_COUNTER
case ('i'): case ('i'):
formatters_.push_back(flag_formatter_ptr(new details::i_formatter())); formatters_.push_back(details::make_unique<details::i_formatter>());
break; break;
#endif
case ('^'): case ('^'):
formatters_.push_back(flag_formatter_ptr(new details::color_start_formatter())); formatters_.push_back(details::make_unique<details::color_start_formatter>());
break; break;
case ('$'): case ('$'):
formatters_.push_back(flag_formatter_ptr(new details::color_stop_formatter())); formatters_.push_back(details::make_unique<details::color_stop_formatter>());
break; break;
default: // Unknown flag appears as is default: // Unknown flag appears as is
formatters_.push_back(flag_formatter_ptr(new details::ch_formatter('%'))); formatters_.push_back(details::make_unique<details::ch_formatter>('%'));
formatters_.push_back(flag_formatter_ptr(new details::ch_formatter(flag))); formatters_.push_back(details::make_unique<details::ch_formatter>(flag));
break; break;
} }
} }
@@ -758,7 +760,7 @@ private:
{ {
if (!user_chars) if (!user_chars)
{ {
user_chars = std::unique_ptr<details::aggregate_formatter>(new details::aggregate_formatter()); user_chars = details::make_unique<details::aggregate_formatter>();
} }
user_chars->add_ch(*it); user_chars->add_ch(*it);
} }

View File

@@ -113,7 +113,7 @@ public:
{ {
std::lock_guard<std::mutex> lock(flusher_mutex_); std::lock_guard<std::mutex> lock(flusher_mutex_);
std::function<void()> clbk = std::bind(&registry::flush_all, this); std::function<void()> clbk = std::bind(&registry::flush_all, this);
periodic_flusher_.reset(new periodic_worker(clbk, interval)); periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval);
} }
void set_error_handler(log_err_handler handler) void set_error_handler(log_err_handler handler)

View File

@@ -64,8 +64,8 @@ struct async_msg
return *this; return *this;
} }
#else // (_MSC_VER) && _MSC_VER <= 1800 #else // (_MSC_VER) && _MSC_VER <= 1800
async_msg(async_msg &&other) = default; async_msg(async_msg &&) = default;
async_msg &operator=(async_msg &&other) = default; async_msg &operator=(async_msg &&) = default;
#endif #endif
// construct from log_msg with given type // construct from log_msg with given type
@@ -75,13 +75,13 @@ struct async_msg
, time(m.time) , time(m.time)
, thread_id(m.thread_id) , thread_id(m.thread_id)
, msg_id(m.msg_id) , msg_id(m.msg_id)
, worker_ptr(std::forward<async_logger_ptr>(worker)) , worker_ptr(std::move(worker))
{ {
fmt_helper::append_buf(m.raw, raw); fmt_helper::append_buf(m.raw, raw);
} }
async_msg(async_logger_ptr &&worker, async_msg_type the_type) async_msg(async_logger_ptr &&worker, async_msg_type the_type)
: async_msg(std::forward<async_logger_ptr>(worker), the_type, details::log_msg()) : async_msg(std::move(worker), the_type, details::log_msg())
{ {
} }
@@ -146,9 +146,12 @@ public:
} }
} }
thread_pool(const thread_pool &) = delete;
thread_pool &operator=(thread_pool &&) = delete;
void post_log(async_logger_ptr &&worker_ptr, details::log_msg &&msg, async_overflow_policy overflow_policy) void post_log(async_logger_ptr &&worker_ptr, details::log_msg &&msg, async_overflow_policy overflow_policy)
{ {
async_msg async_m(std::forward<async_logger_ptr>(worker_ptr), async_msg_type::log, std::forward<log_msg>(msg)); async_msg async_m(std::move(worker_ptr), async_msg_type::log, std::move(msg));
post_async_msg_(std::move(async_m), overflow_policy); post_async_msg_(std::move(async_m), overflow_policy);
} }
@@ -198,6 +201,13 @@ private:
switch (incoming_async_msg.msg_type) switch (incoming_async_msg.msg_type)
{ {
case async_msg_type::log:
{
log_msg msg;
incoming_async_msg.to_log_msg(msg);
incoming_async_msg.worker_ptr->backend_log_(msg);
return true;
}
case async_msg_type::flush: case async_msg_type::flush:
{ {
incoming_async_msg.worker_ptr->backend_flush_(); incoming_async_msg.worker_ptr->backend_flush_();
@@ -208,16 +218,9 @@ private:
{ {
return false; return false;
} }
default:
{
log_msg msg;
incoming_async_msg.to_log_msg(msg);
incoming_async_msg.worker_ptr->backend_log_(msg);
return true;
} }
} assert(false && "Unexpected async_msg_type");
return true; // should not be reached return true;
} }
}; };

View File

@@ -0,0 +1,172 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
//
// Support for logging binary data as hex
// format flags:
// {:X} - print in uppercase.
// {:s} - don't separate each byte with space.
// {:p} - don't print the position on each line start.
// {:n} - don't split the output to lines.
//
// Examples:
//
// std::vector<char> v(200, 0x0b);
// logger->info("Some buffer {}", spdlog::to_hex(v));
// char buf[128];
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));
namespace spdlog {
namespace details {
template<typename It>
class bytes_range
{
public:
bytes_range(It range_begin, It range_end)
: begin_(range_begin)
, end_(range_end)
{
}
It begin() const
{
return begin_;
}
It end() const
{
return end_;
}
private:
It begin_, end_;
};
} // namespace details
// create a bytes_range that wraps the given container
template<typename Container>
inline details::bytes_range<typename Container::const_iterator> to_hex(const Container &container)
{
static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
using Iter = typename Container::const_iterator;
return details::bytes_range<Iter>(std::begin(container), std::end(container));
}
// create bytes_range from ranges
template<typename It>
inline details::bytes_range<It> to_hex(const It range_begin, const It range_end)
{
return details::bytes_range<It>(range_begin, range_end);
}
} // namespace spdlog
namespace fmt {
template<typename T>
struct formatter<spdlog::details::bytes_range<T>>
{
const std::size_t line_size = 100;
const char delimiter = ' ';
bool put_newlines = true;
bool put_delimiters = true;
bool use_uppercase = false;
bool put_positions = true; // position on start of each line
// parse the format string flags
template<typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin())
{
auto it = ctx.begin();
while (*it && *it != '}')
{
switch (*it)
{
case 'X':
use_uppercase = true;
break;
case 's':
put_delimiters = false;
break;
case 'p':
put_positions = false;
break;
case 'n':
put_newlines = false;
break;
}
++it;
}
return it;
}
// format the given bytes range as hex
template<typename FormatContext, typename Container>
auto format(const spdlog::details::bytes_range<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
{
constexpr const char *hex_upper = "0123456789ABCDEF";
constexpr const char *hex_lower = "0123456789abcdef";
const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
std::size_t pos = 0;
std::size_t column = line_size;
auto inserter = ctx.begin();
for (auto &item : the_range)
{
auto ch = static_cast<unsigned char>(item);
pos++;
if (put_newlines && column >= line_size)
{
column = put_newline(inserter, pos);
// put first byte without delimiter in front of it
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
*inserter++ = hex_chars[ch & 0x0f];
column += 2;
continue;
}
if (put_delimiters)
{
*inserter++ = delimiter;
++column;
}
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
*inserter++ = hex_chars[ch & 0x0f];
column += 2;
}
return inserter;
}
// put newline(and position header)
// return the next column
template<typename It>
std::size_t put_newline(It inserter, std::size_t pos)
{
#ifdef _WIN32
*inserter++ = '\r';
#endif
*inserter++ = '\n';
if (put_positions)
{
fmt::format_to(inserter, "{:<04X}: ", pos - 1);
return 7;
}
else
{
return 1;
}
}
};
} // namespace fmt

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,27 +0,0 @@
// Formatting library for C++ - locale support
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "format.h"
#include <locale>
namespace fmt {
class locale
{
private:
std::locale locale_;
public:
explicit locale(std::locale loc = std::locale())
: locale_(loc)
{
}
std::locale get()
{
return locale_;
}
};
} // namespace fmt

View File

@@ -14,139 +14,123 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { namespace internal {
template<class Char> template <class Char>
class formatbuf : public std::basic_streambuf<Char> class formatbuf : public std::basic_streambuf<Char> {
{ private:
private: typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::int_type int_type; typedef typename std::basic_streambuf<Char>::traits_type traits_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
basic_buffer<Char> &buffer_; basic_buffer<Char> &buffer_;
public: public:
formatbuf(basic_buffer<Char> &buffer) formatbuf(basic_buffer<Char> &buffer) : buffer_(buffer) {}
: buffer_(buffer)
{
}
protected: protected:
// The put-area is actually always empty. This makes the implementation // The put-area is actually always empty. This makes the implementation
// simpler and has the advantage that the streambuf and the buffer are always // simpler and has the advantage that the streambuf and the buffer are always
// in sync and sputc never writes into uninitialized memory. The obvious // in sync and sputc never writes into uninitialized memory. The obvious
// disadvantage is that each call to sputc always results in a (virtual) call // disadvantage is that each call to sputc always results in a (virtual) call
// to overflow. There is no disadvantage here for sputn since this always // to overflow. There is no disadvantage here for sputn since this always
// results in a call to xsputn. // results in a call to xsputn.
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
{ if (!traits_type::eq_int_type(ch, traits_type::eof()))
if (!traits_type::eq_int_type(ch, traits_type::eof())) buffer_.push_back(static_cast<Char>(ch));
buffer_.push_back(static_cast<Char>(ch)); return ch;
return ch; }
}
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE {
{ buffer_.append(s, s + count);
buffer_.append(s, s + count); return count;
return count; }
}
}; };
template<typename Char> template <typename Char>
struct test_stream : std::basic_ostream<Char> struct test_stream : std::basic_ostream<Char> {
{ private:
private: struct null;
struct null; // Hide all operator<< from std::basic_ostream<Char>.
// Hide all operator<< from std::basic_ostream<Char>. void operator<<(null);
void operator<<(null);
}; };
// Checks if T has a user-defined operator<< (e.g. not a member of // Checks if T has a user-defined operator<< (e.g. not a member of std::ostream).
// std::ostream). template <typename T, typename Char>
template<typename T, typename Char> class is_streamable {
class is_streamable private:
{ template <typename U>
private: static decltype(
template<typename U> internal::declval<test_stream<Char>&>()
static decltype(internal::declval<test_stream<Char> &>() << internal::declval<U>(), std::true_type()) test(int); << internal::declval<U>(), std::true_type()) test(int);
template<typename> template <typename>
static std::false_type test(...); static std::false_type test(...);
typedef decltype(test<T>(0)) result; typedef decltype(test<T>(0)) result;
public: public:
// std::string operator<< is not considered user-defined because we handle static const bool value = result::value;
// strings
// specially.
static const bool value = result::value && !std::is_same<T, std::string>::value;
};
// Disable conversion to int if T has an overloaded operator<< which is a free
// function (not a member of std::ostream).
template<typename T, typename Char>
class convert_to_int<T, Char, true>
{
public:
static const bool value = convert_to_int<T, Char, false>::value && !is_streamable<T, Char>::value;
}; };
// Write the content of buf to os. // Write the content of buf to os.
template<typename Char> template <typename Char>
void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf) void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf) {
{ const Char *data = buf.data();
const Char *data = buf.data(); typedef std::make_unsigned<std::streamsize>::type UnsignedStreamSize;
typedef std::make_unsigned<std::streamsize>::type UnsignedStreamSize; UnsignedStreamSize size = buf.size();
UnsignedStreamSize size = buf.size(); UnsignedStreamSize max_size =
UnsignedStreamSize max_size = internal::to_unsigned((std::numeric_limits<std::streamsize>::max)()); internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
do do {
{ UnsignedStreamSize n = size <= max_size ? size : max_size;
UnsignedStreamSize n = size <= max_size ? size : max_size; os.write(data, static_cast<std::streamsize>(n));
os.write(data, static_cast<std::streamsize>(n)); data += n;
data += n; size -= n;
size -= n; } while (size != 0);
} while (size != 0);
} }
template<typename Char, typename T> template <typename Char, typename T>
void format_value(basic_buffer<Char> &buffer, const T &value) void format_value(basic_buffer<Char> &buffer, const T &value) {
{ internal::formatbuf<Char> format_buf(buffer);
internal::formatbuf<Char> format_buf(buffer); std::basic_ostream<Char> output(&format_buf);
std::basic_ostream<Char> output(&format_buf); output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
output.exceptions(std::ios_base::failbit | std::ios_base::badbit); output << value;
output << value; buffer.resize(buffer.size());
buffer.resize(buffer.size());
} }
} // namespace internal
// Disable builtin formatting of enums and use operator<< instead. // Disable conversion to int if T has an overloaded operator<< which is a free
template<typename T> // function (not a member of std::ostream).
struct format_enum<T, typename std::enable_if<std::is_enum<T>::value>::type> : std::false_type template <typename T, typename Char>
{ struct convert_to_int<T, Char, void> {
static const bool value =
convert_to_int<T, Char, int>::value &&
!internal::is_streamable<T, Char>::value;
}; };
} // namespace internal
// Formats an object of type T that has an overloaded ostream operator<<. // Formats an object of type T that has an overloaded ostream operator<<.
template<typename T, typename Char> template <typename T, typename Char>
struct formatter<T, Char, typename std::enable_if<internal::is_streamable<T, Char>::value>::type> : formatter<basic_string_view<Char>, Char> struct formatter<T, Char,
{ typename std::enable_if<
internal::is_streamable<T, Char>::value &&
!internal::format_type<
typename buffer_context<Char>::type, T>::value>::type>
: formatter<basic_string_view<Char>, Char> {
template<typename Context> template <typename Context>
auto format(const T &value, Context &ctx) -> decltype(ctx.out()) auto format(const T &value, Context &ctx) -> decltype(ctx.out()) {
{ basic_memory_buffer<Char> buffer;
basic_memory_buffer<Char> buffer; internal::format_value(buffer, value);
internal::format_value(buffer, value); basic_string_view<Char> str(buffer.data(), buffer.size());
basic_string_view<Char> str(buffer.data(), buffer.size()); return formatter<basic_string_view<Char>, Char>::format(str, ctx);
formatter<basic_string_view<Char>, Char>::format(str, ctx); }
return ctx.out();
}
}; };
template<typename Char> template <typename Char>
inline void vprint( inline void vprint(std::basic_ostream<Char> &os,
std::basic_ostream<Char> &os, basic_string_view<Char> format_str, basic_format_args<typename buffer_context<Char>::type> args) basic_string_view<Char> format_str,
{ basic_format_args<typename buffer_context<Char>::type> args) {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
vformat_to(buffer, format_str, args); vformat_to(buffer, format_str, args);
internal::write(os, buffer); internal::write(os, buffer);
} }
/** /**
\rst \rst
@@ -157,17 +141,17 @@ inline void vprint(
fmt::print(cerr, "Don't {}!", "panic"); fmt::print(cerr, "Don't {}!", "panic");
\endrst \endrst
*/ */
template<typename... Args> template <typename... Args>
inline void print(std::ostream &os, string_view format_str, const Args &... args) inline void print(std::ostream &os, string_view format_str,
{ const Args & ... args) {
vprint<char>(os, format_str, make_format_args<format_context>(args...)); vprint<char>(os, format_str, make_format_args<format_context>(args...));
} }
template<typename... Args> template <typename... Args>
inline void print(std::wostream &os, wstring_view format_str, const Args &... args) inline void print(std::wostream &os, wstring_view format_str,
{ const Args & ... args) {
vprint<wchar_t>(os, format_str, make_format_args<wformat_context>(args...)); vprint<wchar_t>(os, format_str, make_format_args<wformat_context>(args...));
} }
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_OSTREAM_H_ #endif // FMT_OSTREAM_H_

View File

@@ -10,55 +10,54 @@
#if defined(__MINGW32__) || defined(__CYGWIN__) #if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/. // Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
#undef __STRICT_ANSI__ # undef __STRICT_ANSI__
#endif #endif
#include <errno.h> #include <errno.h>
#include <fcntl.h> // for O_RDONLY #include <fcntl.h> // for O_RDONLY
#include <locale.h> // for locale_t #include <locale.h> // for locale_t
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> // for strtod_l #include <stdlib.h> // for strtod_l
#include <cstddef> #include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__) #if defined __APPLE__ || defined(__FreeBSD__)
#include <xlocale.h> // for LC_NUMERIC_MASK on OS X # include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif #endif
#include "format.h" #include "format.h"
#ifndef FMT_POSIX #ifndef FMT_POSIX
#if defined(_WIN32) && !defined(__MINGW32__) # if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols. // Fix warnings about deprecated symbols.
#define FMT_POSIX(call) _##call # define FMT_POSIX(call) _##call
#else # else
#define FMT_POSIX(call) call # define FMT_POSIX(call) call
#endif # endif
#endif #endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability. // Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM #ifdef FMT_SYSTEM
#define FMT_POSIX_CALL(call) FMT_SYSTEM(call) # define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else #else
#define FMT_SYSTEM(call) call # define FMT_SYSTEM(call) call
#ifdef _WIN32 # ifdef _WIN32
// Fix warnings about deprecated symbols. // Fix warnings about deprecated symbols.
#define FMT_POSIX_CALL(call) ::_##call # define FMT_POSIX_CALL(call) ::_##call
#else # else
#define FMT_POSIX_CALL(call) ::call # define FMT_POSIX_CALL(call) ::call
#endif # endif
#endif #endif
// Retries the expression while it evaluates to error_result and errno // Retries the expression while it evaluates to error_result and errno
// equals to EINTR. // equals to EINTR.
#ifndef _WIN32 #ifndef _WIN32
#define FMT_RETRY_VAL(result, expression, error_result) \ # define FMT_RETRY_VAL(result, expression, error_result) \
do \ do { \
{ \ result = (expression); \
result = (expression); \ } while (result == error_result && errno == EINTR)
} while (result == error_result && errno == EINTR)
#else #else
#define FMT_RETRY_VAL(result, expression, error_result) result = (expression) # define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif #endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
@@ -90,167 +89,94 @@ FMT_BEGIN_NAMESPACE
format(std::string("{}"), 42); format(std::string("{}"), 42);
\endrst \endrst
*/ */
template<typename Char> template <typename Char>
class basic_cstring_view class basic_cstring_view {
{ private:
private: const Char *data_;
const Char *data_;
public: public:
/** Constructs a string reference object from a C string. */ /** Constructs a string reference object from a C string. */
basic_cstring_view(const Char *s) basic_cstring_view(const Char *s) : data_(s) {}
: data_(s)
{
}
/** /**
\rst \rst
Constructs a string reference from an ``std::string`` object. Constructs a string reference from an ``std::string`` object.
\endrst \endrst
*/ */
basic_cstring_view(const std::basic_string<Char> &s) basic_cstring_view(const std::basic_string<Char> &s) : data_(s.c_str()) {}
: data_(s.c_str())
{
}
/** Returns the pointer to a C string. */ /** Returns the pointer to a C string. */
const Char *c_str() const const Char *c_str() const { return data_; }
{
return data_;
}
}; };
typedef basic_cstring_view<char> cstring_view; typedef basic_cstring_view<char> cstring_view;
typedef basic_cstring_view<wchar_t> wcstring_view; typedef basic_cstring_view<wchar_t> wcstring_view;
// An error code. // An error code.
class error_code class error_code {
{ private:
private: int value_;
int value_;
public: public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {} explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT int get() const FMT_NOEXCEPT { return value_; }
{
return value_;
}
}; };
// A buffered file. // A buffered file.
class buffered_file class buffered_file {
{ private:
private: FILE *file_;
FILE *file_;
friend class file; friend class file;
explicit buffered_file(FILE *f) explicit buffered_file(FILE *f) : file_(f) {}
: file_(f)
{
}
public: public:
// Constructs a buffered_file object which doesn't represent any file. // Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {} buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {}
// Destroys the object closing the file it represents if any. // Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_DTOR_NOEXCEPT; FMT_API ~buffered_file() FMT_DTOR_NOEXCEPT;
#if !FMT_USE_RVALUE_REFERENCES private:
// Emulate a move constructor and a move assignment operator if rvalue buffered_file(const buffered_file &) = delete;
// references are not supported. void operator=(const buffered_file &) = delete;
private:
// A proxy object to emulate a move constructor.
// It is private to make it impossible call operator Proxy directly.
struct Proxy
{
FILE *file;
};
public: public:
// A "move constructor" for moving from a temporary. buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_) {
buffered_file(Proxy p) FMT_NOEXCEPT : file_(p.file) {} other.file_ = FMT_NULL;
}
// A "move constructor" for moving from an lvalue. buffered_file& operator=(buffered_file &&other) {
buffered_file(buffered_file &f) FMT_NOEXCEPT : file_(f.file_) close();
{ file_ = other.file_;
f.file_ = FMT_NULL; other.file_ = FMT_NULL;
} return *this;
}
// A "move assignment operator" for moving from a temporary. // Opens a file.
buffered_file &operator=(Proxy p) FMT_API buffered_file(cstring_view filename, cstring_view mode);
{
close();
file_ = p.file;
return *this;
}
// A "move assignment operator" for moving from an lvalue. // Closes the file.
buffered_file &operator=(buffered_file &other) FMT_API void close();
{
close();
file_ = other.file_;
other.file_ = FMT_NULL;
return *this;
}
// Returns a proxy object for moving from a temporary: // Returns the pointer to a FILE object representing this file.
// buffered_file file = buffered_file(...); FILE *get() const FMT_NOEXCEPT { return file_; }
operator Proxy() FMT_NOEXCEPT
{
Proxy p = {file_};
file_ = FMT_NULL;
return p;
}
#else // We place parentheses around fileno to workaround a bug in some versions
private: // of MinGW that define fileno as a macro.
FMT_DISALLOW_COPY_AND_ASSIGN(buffered_file); FMT_API int (fileno)() const;
public: void vprint(string_view format_str, format_args args) {
buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_) fmt::vprint(file_, format_str, args);
{ }
other.file_ = FMT_NULL;
}
buffered_file &operator=(buffered_file &&other) template <typename... Args>
{ inline void print(string_view format_str, const Args & ... args) {
close(); vprint(format_str, make_format_args(args...));
file_ = other.file_; }
other.file_ = FMT_NULL;
return *this;
}
#endif
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE *get() const FMT_NOEXCEPT
{
return file_;
}
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args)
{
fmt::vprint(file_, format_str, args);
}
template<typename... Args>
inline void print(string_view format_str, const Args &... args)
{
vprint(format_str, make_format_args(args...));
}
}; };
// A file. Closed file is represented by a file object with descriptor -1. // A file. Closed file is represented by a file object with descriptor -1.
@@ -259,226 +185,140 @@ public:
// closing the file multiple times will cause a crash on Windows rather // closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the // than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler. // invalid parameter handler with _set_invalid_parameter_handler.
class file class file {
{ private:
private: int fd_; // File descriptor.
int fd_; // File descriptor.
// Constructs a file object with a given descriptor. // Constructs a file object with a given descriptor.
explicit file(int fd) explicit file(int fd) : fd_(fd) {}
: fd_(fd)
{
}
public: public:
// Possible values for the oflag argument to the constructor. // Possible values for the oflag argument to the constructor.
enum enum {
{ RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only. WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing. };
};
// Constructs a file object which doesn't represent any file. // Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {} file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a file object representing this file. // Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag); FMT_API file(cstring_view path, int oflag);
#if !FMT_USE_RVALUE_REFERENCES private:
// Emulate a move constructor and a move assignment operator if rvalue file(const file &) = delete;
// references are not supported. void operator=(const file &) = delete;
private: public:
// A proxy object to emulate a move constructor. file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) {
// It is private to make it impossible call operator Proxy directly. other.fd_ = -1;
struct Proxy }
{
int fd;
};
public: file& operator=(file &&other) {
// A "move constructor" for moving from a temporary. close();
file(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {} fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// A "move constructor" for moving from an lvalue. // Destroys the object closing the file it represents if any.
file(file &other) FMT_NOEXCEPT : fd_(other.fd_) FMT_API ~file() FMT_DTOR_NOEXCEPT;
{
other.fd_ = -1;
}
// A "move assignment operator" for moving from a temporary. // Returns the file descriptor.
file &operator=(Proxy p) int descriptor() const FMT_NOEXCEPT { return fd_; }
{
close();
fd_ = p.fd;
return *this;
}
// A "move assignment operator" for moving from an lvalue. // Closes the file.
file &operator=(file &other) FMT_API void close();
{
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Returns a proxy object for moving from a temporary: // Returns the file size. The size has signed type for consistency with
// file f = file(...); // stat::st_size.
operator Proxy() FMT_NOEXCEPT FMT_API long long size() const;
{
Proxy p = {fd_};
fd_ = -1;
return p;
}
#else // Attempts to read count bytes from the file into the specified buffer.
private: FMT_API std::size_t read(void *buffer, std::size_t count);
FMT_DISALLOW_COPY_AND_ASSIGN(file);
public: // Attempts to write count bytes from the specified buffer to the file.
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) FMT_API std::size_t write(const void *buffer, std::size_t count);
{
other.fd_ = -1;
}
file &operator=(file &&other) // Duplicates a file descriptor with the dup function and returns
{ // the duplicate as a file object.
close(); FMT_API static file dup(int fd);
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
#endif
// Destroys the object closing the file it represents if any. // Makes fd be the copy of this file descriptor, closing fd first if
FMT_API ~file() FMT_DTOR_NOEXCEPT; // necessary.
FMT_API void dup2(int fd);
// Returns the file descriptor. // Makes fd be the copy of this file descriptor, closing fd first if
int descriptor() const FMT_NOEXCEPT // necessary.
{ FMT_API void dup2(int fd, error_code &ec) FMT_NOEXCEPT;
return fd_;
}
// Closes the file. // Creates a pipe setting up read_end and write_end file objects for reading
FMT_API void close(); // and writing respectively.
FMT_API static void pipe(file &read_end, file &write_end);
// Returns the file size. The size has signed type for consistency with // Creates a buffered_file object associated with this file and detaches
// stat::st_size. // this file object from the file.
FMT_API long long size() const; FMT_API buffered_file fdopen(const char *mode);
// Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void *buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API std::size_t write(const void *buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
FMT_API static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd, error_code &ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
FMT_API static void pipe(file &read_end, file &write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
FMT_API buffered_file fdopen(const char *mode);
}; };
// Returns the memory page size. // Returns the memory page size.
long getpagesize(); long getpagesize();
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && !defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__) #if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
#define FMT_LOCALE !defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__) && \
!defined(__NEWLIB_H__)
# define FMT_LOCALE
#endif #endif
#ifdef FMT_LOCALE #ifdef FMT_LOCALE
// A "C" numeric locale. // A "C" numeric locale.
class Locale class Locale {
{ private:
private: # ifdef _MSC_VER
#ifdef _MSC_VER typedef _locale_t locale_t;
typedef _locale_t locale_t;
enum enum { LC_NUMERIC_MASK = LC_NUMERIC };
{
LC_NUMERIC_MASK = LC_NUMERIC
};
static locale_t newlocale(int category_mask, const char *locale, locale_t) static locale_t newlocale(int category_mask, const char *locale, locale_t) {
{ return _create_locale(category_mask, locale);
return _create_locale(category_mask, locale); }
}
static void freelocale(locale_t locale) static void freelocale(locale_t locale) {
{ _free_locale(locale);
_free_locale(locale); }
}
static double strtod_l(const char *nptr, char **endptr, _locale_t locale) static double strtod_l(const char *nptr, char **endptr, _locale_t locale) {
{ return _strtod_l(nptr, endptr, locale);
return _strtod_l(nptr, endptr, locale); }
} # endif
#endif
locale_t locale_; locale_t locale_;
FMT_DISALLOW_COPY_AND_ASSIGN(Locale); Locale(const Locale &) = delete;
void operator=(const Locale &) = delete;
public: public:
typedef locale_t Type; typedef locale_t Type;
Locale() Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) {
: locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) if (!locale_)
{ FMT_THROW(system_error(errno, "cannot create locale"));
if (!locale_) }
FMT_THROW(system_error(errno, "cannot create locale")); ~Locale() { freelocale(locale_); }
}
~Locale()
{
freelocale(locale_);
}
Type get() const Type get() const { return locale_; }
{
return locale_;
}
// Converts string to floating-point number and advances str past the end // Converts string to floating-point number and advances str past the end
// of the parsed input. // of the parsed input.
double strtod(const char *&str) const double strtod(const char *&str) const {
{ char *end = FMT_NULL;
char *end = FMT_NULL; double result = strtod_l(str, &end, locale_);
double result = strtod_l(str, &end, locale_); str = end;
str = end; return result;
return result; }
}
}; };
#endif // FMT_LOCALE #endif // FMT_LOCALE
FMT_END_NAMESPACE FMT_END_NAMESPACE
#if !FMT_USE_RVALUE_REFERENCES #endif // FMT_POSIX_H_
namespace std {
// For compatibility with C++98.
inline fmt::buffered_file &move(fmt::buffered_file &f)
{
return f;
}
inline fmt::file &move(fmt::file &f)
{
return f;
}
} // namespace std
#endif
#endif // FMT_POSIX_H_

File diff suppressed because it is too large Load Diff

View File

@@ -17,328 +17,292 @@
// output only up to N items from the range. // output only up to N items from the range.
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT #ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
#define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256 # define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
#endif #endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template<typename Char> template <typename Char>
struct formatting_base struct formatting_base {
{ template <typename ParseContext>
template<typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) return ctx.begin();
{ }
return ctx.begin();
}
}; };
template<typename Char, typename Enable = void> template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> struct formatting_range : formatting_base<Char> {
{ static FMT_CONSTEXPR_DECL const std::size_t range_length_limit =
static FMT_CONSTEXPR_DECL const std::size_t range_length_limit = FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the range.
// range. Char prefix;
Char prefix; Char delimiter;
Char delimiter; Char postfix;
Char postfix; formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
formatting_range() static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
: prefix('{') static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
, delimiter(',')
, postfix('}')
{
}
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
}; };
template<typename Char, typename Enable = void> template <typename Char, typename Enable = void>
struct formatting_tuple : formatting_base<Char> struct formatting_tuple : formatting_base<Char> {
{ Char prefix;
Char prefix; Char delimiter;
Char delimiter; Char postfix;
Char postfix; formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
formatting_tuple() static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
: prefix('(') static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
, delimiter(',')
, postfix(')')
{
}
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
}; };
namespace internal { namespace internal {
template<typename RangeT, typename OutputIterator> template <typename RangeT, typename OutputIterator>
void copy(const RangeT &range, OutputIterator out) void copy(const RangeT &range, OutputIterator out) {
{ for (auto it = range.begin(), end = range.end(); it != end; ++it)
for (auto it = range.begin(), end = range.end(); it != end; ++it) *out++ = *it;
*out++ = *it;
} }
template<typename OutputIterator> template <typename OutputIterator>
void copy(const char *str, OutputIterator out) void copy(const char *str, OutputIterator out) {
{ const char *p_curr = str;
const char *p_curr = str; while (*p_curr) {
while (*p_curr) *out++ = *p_curr++;
{ }
*out++ = *p_curr++;
}
} }
template<typename OutputIterator> template <typename OutputIterator>
void copy(char ch, OutputIterator out) void copy(char ch, OutputIterator out) {
{ *out++ = ch;
*out++ = ch;
} }
/// Return true value if T has std::string interface, like std::string_view. /// Return true value if T has std::string interface, like std::string_view.
template<typename T> template <typename T>
class is_like_std_string class is_like_std_string {
{ template <typename U>
template<typename U> static auto check(U *p) ->
static auto check(U *p) -> decltype(p->find('a'), p->length(), p->data(), int()); decltype(p->find('a'), p->length(), p->data(), int());
template<typename> template <typename>
static void check(...); static void check(...);
public: public:
static FMT_CONSTEXPR_DECL const bool value = !std::is_void<decltype(check<T>(FMT_NULL))>::value; static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(FMT_NULL))>::value;
}; };
template<typename... Ts> template <typename Char>
struct conditional_helper struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
{
};
template<typename T, typename _ = void> template <typename... Ts>
struct is_range_ : std::false_type struct conditional_helper {};
{
}; template <typename T, typename _ = void>
struct is_range_ : std::false_type {};
#if !FMT_MSC_VER || FMT_MSC_VER > 1800 #if !FMT_MSC_VER || FMT_MSC_VER > 1800
template<typename T> template <typename T>
struct is_range_<T, typename std::conditional<false, struct is_range_<T, typename std::conditional<
conditional_helper<decltype(internal::declval<T>().begin()), decltype(internal::declval<T>().end())>, void>::type> false,
: std::true_type conditional_helper<decltype(internal::declval<T>().begin()),
{ decltype(internal::declval<T>().end())>,
}; void>::type> : std::true_type {};
#endif #endif
/// tuple_size and tuple_element check. /// tuple_size and tuple_element check.
template<typename T> template <typename T>
class is_tuple_like_ class is_tuple_like_ {
{ template <typename U>
template<typename U> static auto check(U *p) ->
static auto check(U *p) -> decltype(std::tuple_size<U>::value, internal::declval<typename std::tuple_element<0, U>::type>(), int()); decltype(std::tuple_size<U>::value,
template<typename> internal::declval<typename std::tuple_element<0, U>::type>(), int());
static void check(...); template <typename>
static void check(...);
public: public:
static FMT_CONSTEXPR_DECL const bool value = !std::is_void<decltype(check<T>(FMT_NULL))>::value; static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(FMT_NULL))>::value;
}; };
// Check for integer_sequence // Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900 #if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
template<typename T, T... N> template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>; using integer_sequence = std::integer_sequence<T, N...>;
template<std::size_t... N> template <std::size_t... N>
using index_sequence = std::index_sequence<N...>; using index_sequence = std::index_sequence<N...>;
template<std::size_t N> template <std::size_t N>
using make_index_sequence = std::make_index_sequence<N>; using make_index_sequence = std::make_index_sequence<N>;
#else #else
template<typename T, T... N> template <typename T, T... N>
struct integer_sequence struct integer_sequence {
{ typedef T value_type;
typedef T value_type;
static FMT_CONSTEXPR std::size_t size() static FMT_CONSTEXPR std::size_t size() {
{ return sizeof...(N);
return sizeof...(N); }
}
}; };
template<std::size_t... N> template <std::size_t... N>
using index_sequence = integer_sequence<std::size_t, N...>; using index_sequence = integer_sequence<std::size_t, N...>;
template<typename T, std::size_t N, T... Ns> template <typename T, std::size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
{ template <typename T, T... Ns>
}; struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
template<typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...>
{
};
template<std::size_t N> template <std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>; using make_index_sequence = make_integer_sequence<std::size_t, N>;
#endif #endif
template<class Tuple, class F, size_t... Is> template <class Tuple, class F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT {
{ using std::get;
using std::get; // using free function get<I>(T) now.
// using free function get<I>(T) now. const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...}; (void)_; // blocks warnings
(void)_; // blocks warnings
} }
template<class T> template <class T>
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(T const &) FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value>
{ get_indexes(T const &) { return {}; }
return {};
}
template<class Tuple, class F> template <class Tuple, class F>
void for_each(Tuple &&tup, F &&f) void for_each(Tuple &&tup, F &&f) {
{ const auto indexes = get_indexes(tup);
const auto indexes = get_indexes(tup); for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
} }
template<typename Arg> template<typename Arg>
FMT_CONSTEXPR const char *format_str_quoted( FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&,
bool add_space, const Arg &, typename std::enable_if<!is_like_std_string<typename std::decay<Arg>::type>::value>::type * = nullptr) typename std::enable_if<
{ !is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
return add_space ? " {}" : "{}"; return add_space ? " {}" : "{}";
} }
template<typename Arg> template<typename Arg>
FMT_CONSTEXPR const char *format_str_quoted( FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&,
bool add_space, const Arg &, typename std::enable_if<is_like_std_string<typename std::decay<Arg>::type>::value>::type * = nullptr) typename std::enable_if<
{ is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
return add_space ? " \"{}\"" : "\"{}\""; return add_space ? " \"{}\"" : "\"{}\"";
} }
FMT_CONSTEXPR const char *format_str_quoted(bool add_space, const char *) FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
{ return add_space ? " \"{}\"" : "\"{}\"";
return add_space ? " \"{}\"" : "\"{}\"";
} }
FMT_CONSTEXPR const wchar_t *format_str_quoted(bool add_space, const wchar_t *) FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
{
return add_space ? L" \"{}\"" : L"\"{}\""; return add_space ? L" \"{}\"" : L"\"{}\"";
} }
FMT_CONSTEXPR const char *format_str_quoted(bool add_space, const char) FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
{
return add_space ? " '{}'" : "'{}'"; return add_space ? " '{}'" : "'{}'";
} }
FMT_CONSTEXPR const wchar_t *format_str_quoted(bool add_space, const wchar_t) FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
{
return add_space ? L" '{}'" : L"'{}'"; return add_space ? L" '{}'" : L"'{}'";
} }
} // namespace internal } // namespace internal
template<typename T> template <typename T>
struct is_tuple_like struct is_tuple_like {
{ static FMT_CONSTEXPR_DECL const bool value =
static FMT_CONSTEXPR_DECL const bool value = internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value; internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
}; };
template<typename TupleT, typename Char> template <typename TupleT, typename Char>
struct formatter<TupleT, Char, typename std::enable_if<fmt::is_tuple_like<TupleT>::value>::type> struct formatter<TupleT, Char,
{ typename std::enable_if<fmt::is_tuple_like<TupleT>::value>::type> {
private: private:
// C++11 generic lambda for format() // C++11 generic lambda for format()
template<typename FormatContext> template <typename FormatContext>
struct format_each struct format_each {
{ template <typename T>
template<typename T> void operator()(const T& v) {
void operator()(const T &v) if (i > 0) {
{ if (formatting.add_prepostfix_space) {
if (i > 0) *out++ = ' ';
{
if (formatting.add_prepostfix_space)
{
*out++ = ' ';
}
internal::copy(formatting.delimiter, out);
}
format_to(out, internal::format_str_quoted((formatting.add_delimiter_spaces && i > 0), v), v);
++i;
} }
internal::copy(formatting.delimiter, out);
}
format_to(out,
internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v),
v);
++i;
}
formatting_tuple<Char> &formatting; formatting_tuple<Char>& formatting;
std::size_t &i; std::size_t& i;
typename std::add_lvalue_reference<decltype(std::declval<FormatContext>().out())>::type out; typename std::add_lvalue_reference<decltype(std::declval<FormatContext>().out())>::type out;
}; };
public: public:
formatting_tuple<Char> formatting; formatting_tuple<Char> formatting;
template<typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
{ return formatting.parse(ctx);
return formatting.parse(ctx); }
template <typename FormatContext = format_context>
auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
std::size_t i = 0;
internal::copy(formatting.prefix, out);
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
if (formatting.add_prepostfix_space) {
*out++ = ' ';
} }
internal::copy(formatting.postfix, out);
template<typename FormatContext = format_context> return ctx.out();
auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out()) }
{
auto out = ctx.out();
std::size_t i = 0;
internal::copy(formatting.prefix, out);
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
if (formatting.add_prepostfix_space)
{
*out++ = ' ';
}
internal::copy(formatting.postfix, out);
return ctx.out();
}
}; };
template<typename T> template <typename T>
struct is_range struct is_range {
{ static FMT_CONSTEXPR_DECL const bool value =
static FMT_CONSTEXPR_DECL const bool value = internal::is_range_<T>::value && !internal::is_like_std_string<T>::value; internal::is_range_<T>::value && !internal::is_like_std_string<T>::value;
}; };
template<typename RangeT, typename Char> template <typename RangeT, typename Char>
struct formatter<RangeT, Char, typename std::enable_if<fmt::is_range<RangeT>::value>::type> struct formatter<RangeT, Char,
{ typename std::enable_if<fmt::is_range<RangeT>::value>::type> {
formatting_range<Char> formatting; formatting_range<Char> formatting;
template<typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
{ return formatting.parse(ctx);
return formatting.parse(ctx); }
}
template<typename FormatContext> template <typename FormatContext>
typename FormatContext::iterator format(const RangeT &values, FormatContext &ctx) typename FormatContext::iterator format(
{ const RangeT &values, FormatContext &ctx) {
auto out = ctx.out(); auto out = ctx.out();
internal::copy(formatting.prefix, out); internal::copy(formatting.prefix, out);
std::size_t i = 0; std::size_t i = 0;
for (auto it = values.begin(), end = values.end(); it != end; ++it) for (auto it = values.begin(), end = values.end(); it != end; ++it) {
{ if (i > 0) {
if (i > 0) if (formatting.add_prepostfix_space) {
{ *out++ = ' ';
if (formatting.add_prepostfix_space)
{
*out++ = ' ';
}
internal::copy(formatting.delimiter, out);
}
format_to(out, internal::format_str_quoted((formatting.add_delimiter_spaces && i > 0), *it), *it);
if (++i > formatting.range_length_limit)
{
format_to(out, " ... <other elements>");
break;
}
} }
if (formatting.add_prepostfix_space) internal::copy(formatting.delimiter, out);
{ }
*out++ = ' '; format_to(out,
} internal::format_str_quoted(
internal::copy(formatting.postfix, out); (formatting.add_delimiter_spaces && i > 0), *it),
return ctx.out(); *it);
if (++i > formatting.range_length_limit) {
format_to(out, " ... <other elements>");
break;
}
} }
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
internal::copy(formatting.postfix, out);
return ctx.out();
}
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_RANGES_H_ #endif // FMT_RANGES_H_

View File

@@ -13,187 +13,144 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace internal { // Prevents expansion of a preceding token as a function-style macro.
inline null<> localtime_r(...) // Usage: f FMT_NOMACRO()
{ #define FMT_NOMACRO
return null<>();
namespace internal{
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); }
} }
inline null<> localtime_s(...)
{
return null<>();
}
inline null<> gmtime_r(...)
{
return null<>();
}
inline null<> gmtime_s(...)
{
return null<>();
}
} // namespace internal
// Thread-safe replacement for std::localtime // Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) inline std::tm localtime(std::time_t time) {
{ struct dispatcher {
struct dispatcher std::time_t time_;
{ std::tm tm_;
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t) dispatcher(std::time_t t): time_(t) {}
: time_(t)
{
}
bool run() bool run() {
{ using namespace fmt::internal;
using namespace fmt::internal; return handle(localtime_r(&time_, &tm_));
return handle(localtime_r(&time_, &tm_)); }
}
bool handle(std::tm *tm) bool handle(std::tm *tm) { return tm != FMT_NULL; }
{
return tm != FMT_NULL;
}
bool handle(internal::null<>) bool handle(internal::null<>) {
{ using namespace fmt::internal;
using namespace fmt::internal; return fallback(localtime_s(&tm_, &time_));
return fallback(localtime_s(&tm_, &time_)); }
}
bool fallback(int res) bool fallback(int res) { return res == 0; }
{
return res == 0;
}
bool fallback(internal::null<>) bool fallback(internal::null<>) {
{ using namespace fmt::internal;
using namespace fmt::internal; std::tm *tm = std::localtime(&time_);
std::tm *tm = std::localtime(&time_); if (tm) tm_ = *tm;
if (tm) return tm != FMT_NULL;
tm_ = *tm; }
return tm != FMT_NULL; };
} dispatcher lt(time);
}; if (lt.run())
dispatcher lt(time); return lt.tm_;
if (lt.run()) // Too big time values may be unsupported.
return lt.tm_; FMT_THROW(format_error("time_t value out of range"));
// Too big time values may be unsupported.
FMT_THROW(format_error("time_t value out of range"));
} }
// Thread-safe replacement for std::gmtime // Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time) inline std::tm gmtime(std::time_t time) {
{ struct dispatcher {
struct dispatcher std::time_t time_;
{ std::tm tm_;
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t) dispatcher(std::time_t t): time_(t) {}
: time_(t)
{
}
bool run() bool run() {
{ using namespace fmt::internal;
using namespace fmt::internal; return handle(gmtime_r(&time_, &tm_));
return handle(gmtime_r(&time_, &tm_)); }
}
bool handle(std::tm *tm) bool handle(std::tm *tm) { return tm != FMT_NULL; }
{
return tm != FMT_NULL;
}
bool handle(internal::null<>) bool handle(internal::null<>) {
{ using namespace fmt::internal;
using namespace fmt::internal; return fallback(gmtime_s(&tm_, &time_));
return fallback(gmtime_s(&tm_, &time_)); }
}
bool fallback(int res) bool fallback(int res) { return res == 0; }
{
return res == 0;
}
bool fallback(internal::null<>) bool fallback(internal::null<>) {
{ std::tm *tm = std::gmtime(&time_);
std::tm *tm = std::gmtime(&time_); if (tm) tm_ = *tm;
if (tm) return tm != FMT_NULL;
tm_ = *tm; }
return tm != FMT_NULL; };
} dispatcher gt(time);
}; if (gt.run())
dispatcher gt(time); return gt.tm_;
if (gt.run()) // Too big time values may be unsupported.
return gt.tm_; FMT_THROW(format_error("time_t value out of range"));
// Too big time values may be unsupported.
FMT_THROW(format_error("time_t value out of range"));
} }
namespace internal { namespace internal {
inline std::size_t strftime(char *str, std::size_t count, const char *format, const std::tm *time) inline std::size_t strftime(char *str, std::size_t count, const char *format,
{ const std::tm *time) {
return std::strftime(str, count, format, time); return std::strftime(str, count, format, time);
} }
inline std::size_t strftime(wchar_t *str, std::size_t count, const wchar_t *format, const std::tm *time) inline std::size_t strftime(wchar_t *str, std::size_t count,
{ const wchar_t *format, const std::tm *time) {
return std::wcsftime(str, count, format, time); return std::wcsftime(str, count, format, time);
}
} }
} // namespace internal
template<typename Char> template <typename Char>
struct formatter<std::tm, Char> struct formatter<std::tm, Char> {
{ template <typename ParseContext>
template<typename ParseContext> auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) auto it = internal::null_terminating_iterator<Char>(ctx);
{ if (*it == ':')
auto it = internal::null_terminating_iterator<Char>(ctx); ++it;
if (*it == ':') auto end = it;
++it; while (*end && *end != '}')
auto end = it; ++end;
while (*end && *end != '}') tm_format.reserve(end - it + 1);
++end; using internal::pointer_from;
tm_format.reserve(end - it + 1); tm_format.append(pointer_from(it), pointer_from(end));
using internal::pointer_from; tm_format.push_back('\0');
tm_format.append(pointer_from(it), pointer_from(end)); return pointer_from(end);
tm_format.push_back('\0'); }
return pointer_from(end);
template <typename FormatContext>
auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) {
internal::basic_buffer<Char> &buf = internal::get_container(ctx.out());
std::size_t start = buf.size();
for (;;) {
std::size_t size = buf.capacity() - start;
std::size_t count =
internal::strftime(&buf[start], size, &tm_format[0], &tm);
if (count != 0) {
buf.resize(start + count);
break;
}
if (size >= tm_format.size() * 256) {
// If the buffer is 256 times larger than the format string, assume
// that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases:
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
} }
return ctx.out();
}
template<typename FormatContext> basic_memory_buffer<Char> tm_format;
auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out())
{
internal::basic_buffer<Char> &buf = internal::get_container(ctx.out());
std::size_t start = buf.size();
for (;;)
{
std::size_t size = buf.capacity() - start;
std::size_t count = internal::strftime(&buf[start], size, &tm_format[0], &tm);
if (count != 0)
{
buf.resize(start + count);
break;
}
if (size >= tm_format.size() * 256)
{
// If the buffer is 256 times larger than the format string, assume
// that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases:
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
return ctx.out();
}
basic_memory_buffer<Char> tm_format;
}; };
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_TIME_H_ #endif // FMT_TIME_H_

View File

@@ -22,6 +22,7 @@
#include "spdlog/formatter.h" #include "spdlog/formatter.h"
#include "spdlog/sinks/sink.h" #include "spdlog/sinks/sink.h"
#include <locale>
#include <memory> #include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
@@ -35,7 +36,7 @@ public:
logger(std::string name, sinks_init_list sinks); logger(std::string name, sinks_init_list sinks);
template<typename It> template<typename It>
logger(std::string name, const It &begin, const It &end); logger(std::string name, It begin, It end);
virtual ~logger(); virtual ~logger();
@@ -67,6 +68,9 @@ public:
void critical(const char *fmt, const Args &... args); void critical(const char *fmt, const Args &... args);
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
#ifndef _WIN32
#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
#else
template<typename... Args> template<typename... Args>
void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args); void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args);
@@ -87,6 +91,7 @@ public:
template<typename... Args> template<typename... Args>
void critical(const wchar_t *fmt, const Args &... args); void critical(const wchar_t *fmt, const Args &... args);
#endif // _WIN32
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT #endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
template<typename T> template<typename T>
@@ -120,16 +125,22 @@ public:
void set_formatter(std::unique_ptr<formatter> formatter); void set_formatter(std::unique_ptr<formatter> formatter);
void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local); void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local);
// flush functions
void flush(); void flush();
void flush_on(level::level_enum log_level); void flush_on(level::level_enum log_level);
level::level_enum flush_level() const;
// sinks
const std::vector<sink_ptr> &sinks() const; const std::vector<sink_ptr> &sinks() const;
std::vector<sink_ptr> &sinks(); std::vector<sink_ptr> &sinks();
// error handler
void set_error_handler(log_err_handler err_handler); void set_error_handler(log_err_handler err_handler);
log_err_handler error_handler(); log_err_handler error_handler();
// create new logger with same sinks and configuration.
virtual std::shared_ptr<logger> clone(std::string logger_name);
protected: protected:
virtual void sink_it_(details::log_msg &msg); virtual void sink_it_(details::log_msg &msg);
virtual void flush_(); virtual void flush_();
@@ -140,8 +151,7 @@ protected:
// message/minute // message/minute
void default_err_handler_(const std::string &msg); void default_err_handler_(const std::string &msg);
// increment the message count (only if // increment the message count (only if defined(SPDLOG_ENABLE_MESSAGE_COUNTER))
// defined(SPDLOG_ENABLE_MESSAGE_COUNTER))
void incr_msg_counter_(details::log_msg &msg); void incr_msg_counter_(details::log_msg &msg);
const std::string name_; const std::string name_;
@@ -151,11 +161,6 @@ protected:
log_err_handler err_handler_; log_err_handler err_handler_;
std::atomic<time_t> last_err_time_; std::atomic<time_t> last_err_time_;
std::atomic<size_t> msg_counter_; std::atomic<size_t> msg_counter_;
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
std::wstring_convert<std::codecvt_utf8<wchar_t>> wstring_converter_;
std::mutex wstring_converter_mutex_;
#endif
}; };
} // namespace spdlog } // namespace spdlog

View File

@@ -27,7 +27,7 @@ namespace sinks {
* Android sink (logging using __android_log_write) * Android sink (logging using __android_log_write)
*/ */
template<typename Mutex> template<typename Mutex>
class android_sink SPDLOG_FINAL : public base_sink<Mutex> class android_sink final : public base_sink<Mutex>
{ {
public: public:
explicit android_sink(std::string tag = "spdlog", bool use_raw_msg = false) explicit android_sink(std::string tag = "spdlog", bool use_raw_msg = false)

View File

@@ -8,6 +8,7 @@
#include "spdlog/details/console_globals.h" #include "spdlog/details/console_globals.h"
#include "spdlog/details/null_mutex.h" #include "spdlog/details/null_mutex.h"
#include "spdlog/details/os.h" #include "spdlog/details/os.h"
#include "spdlog/sinks/sink.h"
#include <memory> #include <memory>
#include <mutex> #include <mutex>
@@ -24,7 +25,7 @@ namespace sinks {
* If no color terminal detected, omit the escape codes. * If no color terminal detected, omit the escape codes.
*/ */
template<typename TargetStream, class ConsoleMutex> template<typename TargetStream, class ConsoleMutex>
class ansicolor_sink SPDLOG_FINAL : public sink class ansicolor_sink final : public sink
{ {
public: public:
using mutex_t = typename ConsoleMutex::mutex_t; using mutex_t = typename ConsoleMutex::mutex_t;
@@ -116,7 +117,7 @@ public:
fflush(target_file_); fflush(target_file_);
} }
void set_pattern(const std::string &pattern) SPDLOG_FINAL void set_pattern(const std::string &pattern) final
{ {
std::lock_guard<mutex_t> lock(mutex_); std::lock_guard<mutex_t> lock(mutex_);
formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern)); formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));

View File

@@ -22,41 +22,47 @@ template<typename Mutex>
class base_sink : public sink class base_sink : public sink
{ {
public: public:
base_sink() base_sink() = default;
: sink()
{
}
base_sink(const base_sink &) = delete; base_sink(const base_sink &) = delete;
base_sink &operator=(const base_sink &) = delete; base_sink &operator=(const base_sink &) = delete;
void log(const details::log_msg &msg) SPDLOG_FINAL void log(const details::log_msg &msg) final
{ {
std::lock_guard<Mutex> lock(mutex_); std::lock_guard<Mutex> lock(mutex_);
sink_it_(msg); sink_it_(msg);
} }
void flush() SPDLOG_FINAL override void flush() final
{ {
std::lock_guard<Mutex> lock(mutex_); std::lock_guard<Mutex> lock(mutex_);
flush_(); flush_();
} }
void set_pattern(const std::string &pattern) SPDLOG_FINAL override void set_pattern(const std::string &pattern) final
{ {
std::lock_guard<Mutex> lock(mutex_); std::lock_guard<Mutex> lock(mutex_);
formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern)); set_pattern_(pattern);
} }
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) SPDLOG_FINAL override void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) final
{ {
std::lock_guard<Mutex> lock(mutex_); std::lock_guard<Mutex> lock(mutex_);
formatter_ = std::move(sink_formatter); set_formatter_(std::move(sink_formatter));
} }
protected: protected:
virtual void sink_it_(const details::log_msg &msg) = 0; virtual void sink_it_(const details::log_msg &msg) = 0;
virtual void flush_() = 0; virtual void flush_() = 0;
virtual void set_pattern_(const std::string &pattern)
{
set_formatter_(details::make_unique<spdlog::pattern_formatter>(pattern));
}
virtual void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter)
{
formatter_ = std::move(sink_formatter);
}
Mutex mutex_; Mutex mutex_;
}; };
} // namespace sinks } // namespace sinks

View File

@@ -18,7 +18,7 @@ namespace sinks {
* Trivial file sink with single file as target * Trivial file sink with single file as target
*/ */
template<typename Mutex> template<typename Mutex>
class basic_file_sink SPDLOG_FINAL : public base_sink<Mutex> class basic_file_sink final : public base_sink<Mutex>
{ {
public: public:
explicit basic_file_sink(const filename_t &filename, bool truncate = false) explicit basic_file_sink(const filename_t &filename, bool truncate = false)

View File

@@ -40,7 +40,7 @@ struct daily_filename_calculator
* Rotating file sink based on date. rotates at midnight * Rotating file sink based on date. rotates at midnight
*/ */
template<typename Mutex, typename FileNameCalc = daily_filename_calculator> template<typename Mutex, typename FileNameCalc = daily_filename_calculator>
class daily_file_sink SPDLOG_FINAL : public base_sink<Mutex> class daily_file_sink final : public base_sink<Mutex>
{ {
public: public:
// create daily file sink which rotates on given time // create daily file sink which rotates on given time

View File

@@ -40,6 +40,12 @@ public:
sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end()); sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
} }
void set_sinks(std::vector<std::shared_ptr<sink>> sinks)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
sinks_ = std::move(sinks);
}
protected: protected:
void sink_it_(const details::log_msg &msg) override void sink_it_(const details::log_msg &msg) override
{ {
@@ -56,7 +62,23 @@ protected:
void flush_() override void flush_() override
{ {
for (auto &sink : sinks_) for (auto &sink : sinks_)
{
sink->flush(); sink->flush();
}
}
void set_pattern_(const std::string &pattern) override
{
set_formatter_(spdlog::make_unique<spdlog::pattern_formatter>(pattern));
}
void set_formatter_(std::unique_ptr<spdlog::formatter> sink_formatter) override
{
base_sink<Mutex>::formatter_ = std::move(sink_formatter);
for (auto &sink : sinks_)
{
sink->set_formatter(base_sink<Mutex>::formatter_->clone());
}
} }
std::vector<std::shared_ptr<sink>> sinks_; std::vector<std::shared_ptr<sink>> sinks_;
}; };

View File

@@ -14,7 +14,7 @@
namespace spdlog { namespace spdlog {
namespace sinks { namespace sinks {
template<typename Mutex> template<typename Mutex>
class ostream_sink SPDLOG_FINAL : public base_sink<Mutex> class ostream_sink final : public base_sink<Mutex>
{ {
public: public:
explicit ostream_sink(std::ostream &os, bool force_flush = false) explicit ostream_sink(std::ostream &os, bool force_flush = false)
@@ -30,9 +30,11 @@ protected:
{ {
fmt::memory_buffer formatted; fmt::memory_buffer formatted;
sink::formatter_->format(msg, formatted); sink::formatter_->format(msg, formatted);
ostream_.write(formatted.data(), formatted.size()); ostream_.write(formatted.data(), static_cast<std::streamsize>(formatted.size()));
if (force_flush_) if (force_flush_)
{
ostream_.flush(); ostream_.flush();
}
} }
void flush_() override void flush_() override

View File

@@ -1,144 +1,150 @@
// //
// Copyright(c) 2015 Gabi Melman. // Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#pragma once #pragma once
#include "spdlog/details/file_helper.h" #include "spdlog/details/file_helper.h"
#include "spdlog/details/null_mutex.h" #include "spdlog/details/null_mutex.h"
#include "spdlog/fmt/fmt.h" #include "spdlog/fmt/fmt.h"
#include "spdlog/sinks/base_sink.h" #include "spdlog/sinks/base_sink.h"
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include <cerrno> #include <cerrno>
#include <chrono> #include <chrono>
#include <ctime> #include <ctime>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <tuple> #include <tuple>
namespace spdlog { namespace spdlog {
namespace sinks { namespace sinks {
// //
// Rotating file sink based on size // Rotating file sink based on size
// //
template<typename Mutex> template<typename Mutex>
class rotating_file_sink SPDLOG_FINAL : public base_sink<Mutex> class rotating_file_sink final : public base_sink<Mutex>
{ {
public: public:
rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files) rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files)
: base_filename_(std::move(base_filename)) : base_filename_(std::move(base_filename))
, max_size_(max_size) , max_size_(max_size)
, max_files_(max_files) , max_files_(max_files)
{ {
file_helper_.open(calc_filename(base_filename_, 0)); file_helper_.open(calc_filename(base_filename_, 0));
current_size_ = file_helper_.size(); // expensive. called only once current_size_ = file_helper_.size(); // expensive. called only once
} }
// calc filename according to index and file extension if exists. // calc filename according to index and file extension if exists.
// e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt". // e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
static filename_t calc_filename(const filename_t &filename, std::size_t index) static filename_t calc_filename(const filename_t &filename, std::size_t index)
{ {
typename std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w; typename std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w;
if (index != 0u) if (index != 0u)
{ {
filename_t basename, ext; filename_t basename, ext;
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename); std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
fmt::format_to(w, SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext); fmt::format_to(w, SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
} }
else else
{ {
fmt::format_to(w, SPDLOG_FILENAME_T("{}"), filename); fmt::format_to(w, SPDLOG_FILENAME_T("{}"), filename);
} }
return fmt::to_string(w); return fmt::to_string(w);
} }
protected: protected:
void sink_it_(const details::log_msg &msg) override void sink_it_(const details::log_msg &msg) override
{ {
fmt::memory_buffer formatted; fmt::memory_buffer formatted;
sink::formatter_->format(msg, formatted); sink::formatter_->format(msg, formatted);
current_size_ += formatted.size(); current_size_ += formatted.size();
if (current_size_ > max_size_) if (current_size_ > max_size_)
{ {
rotate_(); rotate_();
current_size_ = formatted.size(); current_size_ = formatted.size();
} }
file_helper_.write(formatted); file_helper_.write(formatted);
} }
void flush_() override void flush_() override
{ {
file_helper_.flush(); file_helper_.flush();
} }
private: private:
// Rotate files: // Rotate files:
// log.txt -> log.1.txt // log.txt -> log.1.txt
// log.1.txt -> log.2.txt // log.1.txt -> log.2.txt
// log.2.txt -> log.3.txt // log.2.txt -> log.3.txt
// log.3.txt -> delete // log.3.txt -> delete
void rotate_() void rotate_()
{ {
using details::os::filename_to_str; using details::os::filename_to_str;
file_helper_.close(); file_helper_.close();
for (auto i = max_files_; i > 0; --i) for (auto i = max_files_; i > 0; --i)
{ {
filename_t src = calc_filename(base_filename_, i - 1); filename_t src = calc_filename(base_filename_, i - 1);
filename_t target = calc_filename(base_filename_, i); if (!details::file_helper::file_exists(src))
{
if (details::file_helper::file_exists(target)) continue;
{ }
if (details::os::remove(target) != 0) filename_t target = calc_filename(base_filename_, i);
{
throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target), errno); if (!rename_file(src, target))
} {
} // if failed try again after a small delay.
if (details::file_helper::file_exists(src) && details::os::rename(src, target) != 0) // this is a workaround to a windows issue, where very high rotation
{ // rates can cause the rename to fail with permission denied (because of antivirus?).
// if failed try again after small delay. details::os::sleep_for_millis(100);
// this is a workaround to a windows issue, where very high rotation if (!rename_file(src, target))
// rates sometimes fail (because of antivirus?). {
details::os::sleep_for_millis(20); file_helper_.reopen(true); // truncate the log file anyway to prevent it to grow beyond its limit!
details::os::remove(target); throw spdlog_ex(
if (details::os::rename(src, target) != 0) "rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
{ }
throw spdlog_ex( }
"rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno); }
} file_helper_.reopen(true);
} }
}
file_helper_.reopen(true); // delete the target if exists, and rename the src file to target
} // return true on success, false otherwise.
bool rename_file(const filename_t &src_filename, const filename_t &target_filename)
filename_t base_filename_; {
std::size_t max_size_; // try to delete the target file in case it already exists.
std::size_t max_files_; (void)details::os::remove(target_filename);
std::size_t current_size_; return details::os::rename(src_filename, target_filename) == 0;
details::file_helper file_helper_; }
};
filename_t base_filename_;
using rotating_file_sink_mt = rotating_file_sink<std::mutex>; std::size_t max_size_;
using rotating_file_sink_st = rotating_file_sink<details::null_mutex>; std::size_t max_files_;
std::size_t current_size_;
} // namespace sinks details::file_helper file_helper_;
};
//
// factory functions using rotating_file_sink_mt = rotating_file_sink<std::mutex>;
// using rotating_file_sink_st = rotating_file_sink<details::null_mutex>;
template<typename Factory = default_factory> } // namespace sinks
inline 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) //
{ // factory functions
return Factory::template create<sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files); //
}
template<typename Factory = default_factory>
template<typename Factory = default_factory> inline std::shared_ptr<logger> rotating_logger_mt(
inline std::shared_ptr<logger> rotating_logger_st( const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files) {
{ return Factory::template create<sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
return Factory::template create<sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files); }
}
} // namespace spdlog template<typename Factory = default_factory>
inline 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)
{
return Factory::template create<sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
}
} // namespace spdlog

View File

@@ -22,7 +22,9 @@ public:
explicit sink(std::unique_ptr<spdlog::pattern_formatter> formatter) explicit sink(std::unique_ptr<spdlog::pattern_formatter> formatter)
: level_(level::trace) : level_(level::trace)
, formatter_(std::move(formatter)){}; , formatter_(std::move(formatter))
{
}
virtual ~sink() = default; virtual ~sink() = default;
virtual void log(const details::log_msg &msg) = 0; virtual void log(const details::log_msg &msg) = 0;

View File

@@ -12,14 +12,13 @@
#include <cstdio> #include <cstdio>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <spdlog/details/console_globals.h>
namespace spdlog { namespace spdlog {
namespace sinks { namespace sinks {
template<typename TargetStream, typename ConsoleMutex> template<typename TargetStream, typename ConsoleMutex>
class stdout_sink SPDLOG_FINAL : public sink class stdout_sink final : public sink
{ {
public: public:
using mutex_t = typename ConsoleMutex::mutex_t; using mutex_t = typename ConsoleMutex::mutex_t;

View File

@@ -61,7 +61,7 @@ public:
colors_[level] = color; colors_[level] = color;
} }
void log(const details::log_msg &msg) SPDLOG_FINAL override void log(const details::log_msg &msg) final override
{ {
std::lock_guard<mutex_t> lock(mutex_); std::lock_guard<mutex_t> lock(mutex_);
fmt::memory_buffer formatted; fmt::memory_buffer formatted;
@@ -85,18 +85,18 @@ public:
} }
} }
void flush() SPDLOG_FINAL override void flush() final override
{ {
// windows console always flushed? // windows console always flushed?
} }
void set_pattern(const std::string &pattern) override SPDLOG_FINAL void set_pattern(const std::string &pattern) override final
{ {
std::lock_guard<mutex_t> lock(mutex_); std::lock_guard<mutex_t> lock(mutex_);
formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern)); formatter_ = std::unique_ptr<spdlog::formatter>(new pattern_formatter(pattern));
} }
void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override SPDLOG_FINAL void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) override final
{ {
std::lock_guard<mutex_t> lock(mutex_); std::lock_guard<mutex_t> lock(mutex_);
formatter_ = std::move(sink_formatter); formatter_ = std::move(sink_formatter);

View File

@@ -108,15 +108,6 @@
// #define SPDLOG_PREVENT_CHILD_FD // #define SPDLOG_PREVENT_CHILD_FD
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// Uncomment if your compiler doesn't support the "final" keyword.
// The final keyword allows more optimizations in release
// mode with recent compilers. See GCC's documentation for -Wsuggest-final-types
// for instance.
//
// #define SPDLOG_NO_FINAL
///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// Uncomment to enable message counting feature. // Uncomment to enable message counting feature.
// Use the %i in the logger pattern to display log message sequence id. // Use the %i in the logger pattern to display log message sequence id.

View File

@@ -6,7 +6,7 @@
#pragma once #pragma once
#define SPDLOG_VER_MAJOR 1 #define SPDLOG_VER_MAJOR 1
#define SPDLOG_VER_MINOR 1 #define SPDLOG_VER_MINOR 2
#define SPDLOG_VER_PATCH 0 #define SPDLOG_VER_PATCH 0
#define SPDLOG_VERSION (SPDLOG_VER_MAJOR * 10000 + SPDLOG_VER_MINOR * 100 + SPDLOG_VER_PATCH) #define SPDLOG_VERSION (SPDLOG_VER_MAJOR * 10000 + SPDLOG_VER_MINOR * 100 + SPDLOG_VER_PATCH)

View File

@@ -17,7 +17,7 @@ set(SPDLOG_UTESTS_SOURCES
main.cpp main.cpp
test_mpmc_q.cpp test_mpmc_q.cpp
test_sink.h test_sink.h
) test_fmt_helper.cpp)
add_executable(${PROJECT_NAME} ${SPDLOG_UTESTS_SOURCES}) add_executable(${PROJECT_NAME} ${SPDLOG_UTESTS_SOURCES})
target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads) target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads)

View File

@@ -1,5 +1,5 @@
CXX ?= g++ CXX ?= g++
CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -O3 -I../include -fmax-errors=1 CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -Wconversion -O3 -I../include -fmax-errors=1
LDPFALGS = -pthread LDPFALGS = -pthread
CPP_FILES := $(wildcard *.cpp) CPP_FILES := $(wildcard *.cpp)

View File

@@ -83,7 +83,9 @@ TEST_CASE("async_error_handler", "[errors]]")
logger->set_error_handler([=](const std::string &) { logger->set_error_handler([=](const std::string &) {
std::ofstream ofs("logs/custom_err.txt"); std::ofstream ofs("logs/custom_err.txt");
if (!ofs) if (!ofs)
{
throw std::runtime_error("Failed open logs/custom_err.txt"); throw std::runtime_error("Failed open logs/custom_err.txt");
}
ofs << err_msg; ofs << err_msg;
}); });
logger->info("Good message #1"); logger->info("Good message #1");

View File

@@ -62,7 +62,9 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
std::string basename = "logs/rotating_log"; std::string basename = "logs/rotating_log";
auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 1); auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 1);
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; ++i)
{
logger->info("Test message {}", i); logger->info("Test message {}", i);
}
logger->flush(); logger->flush();
auto filename = basename; auto filename = basename;

View File

@@ -6,11 +6,13 @@
#include <cstdio> #include <cstdio>
#include <exception> #include <exception>
#include <fstream> #include <fstream>
#include <iostream>
#include <ostream> #include <ostream>
#include <string> #include <string>
#define SPDLOG_TRACE_ON #define SPDLOG_TRACE_ON
#define SPDLOG_DEBUG_ON #define SPDLOG_DEBUG_ON
#define SPDLOG_ENABLE_MESSAGE_COUNTER
#include "spdlog/async.h" #include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h" #include "spdlog/sinks/basic_file_sink.h"

View File

@@ -9,7 +9,7 @@ TEST_CASE("register_drop", "[registry]")
spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name); spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name);
REQUIRE(spdlog::get(tested_logger_name) != nullptr); REQUIRE(spdlog::get(tested_logger_name) != nullptr);
// Throw if registring existing name // Throw if registring existing name
REQUIRE_THROWS_AS(spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name), const spdlog::spdlog_ex&); REQUIRE_THROWS_AS(spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name), const spdlog::spdlog_ex &);
} }
TEST_CASE("explicit register" TEST_CASE("explicit register"
@@ -20,7 +20,7 @@ TEST_CASE("explicit register"
spdlog::register_logger(logger); spdlog::register_logger(logger);
REQUIRE(spdlog::get(tested_logger_name) != nullptr); REQUIRE(spdlog::get(tested_logger_name) != nullptr);
// Throw if registring existing name // Throw if registring existing name
REQUIRE_THROWS_AS(spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name), const spdlog::spdlog_ex&); REQUIRE_THROWS_AS(spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name), const spdlog::spdlog_ex &);
} }
TEST_CASE("apply_all" TEST_CASE("apply_all"
@@ -62,7 +62,7 @@ TEST_CASE("drop_all"
spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name2); spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name2);
spdlog::drop_all(); spdlog::drop_all();
REQUIRE_FALSE(spdlog::get(tested_logger_name)); REQUIRE_FALSE(spdlog::get(tested_logger_name));
REQUIRE_FALSE(spdlog::get(tested_logger_name)); REQUIRE_FALSE(spdlog::get(tested_logger_name2));
} }
TEST_CASE("drop non existing" TEST_CASE("drop non existing"

54
tests/test_fmt_helper.cpp Normal file
View File

@@ -0,0 +1,54 @@
#include "includes.h"
void test_pad2(int n, const char *expected)
{
fmt::memory_buffer buf;
spdlog::details::fmt_helper::pad2(n, buf);
REQUIRE(fmt::to_string(buf) == expected);
}
void test_pad3(int n, const char *expected)
{
fmt::memory_buffer buf;
spdlog::details::fmt_helper::pad3(n, buf);
REQUIRE(fmt::to_string(buf) == expected);
}
void test_pad6(std::size_t n, const char *expected)
{
fmt::memory_buffer buf;
spdlog::details::fmt_helper::pad6(n, buf);
REQUIRE(fmt::to_string(buf) == expected);
}
TEST_CASE("pad2", "[fmt_helper]")
{
test_pad2(0, "00");
test_pad2(3, "03");
test_pad2(23, "23");
test_pad2(123, "123");
test_pad2(1234, "1234");
test_pad2(-5, "-5");
}
TEST_CASE("pad3", "[fmt_helper]")
{
test_pad3(0, "000");
test_pad3(3, "003");
test_pad3(23, "023");
test_pad3(123, "123");
test_pad3(1234, "1234");
test_pad3(-5, "-05");
}
TEST_CASE("pad6", "[fmt_helper]")
{
test_pad6(0, "000000");
test_pad6(3, "000003");
test_pad6(23, "000023");
test_pad6(123, "000123");
test_pad6(1234, "001234");
test_pad6(12345, "012345");
test_pad6(123456, "123456");
}

View File

@@ -91,3 +91,103 @@ TEST_CASE("periodic flush", "[periodic_flush]")
spdlog::flush_every(std::chrono::seconds(0)); spdlog::flush_every(std::chrono::seconds(0));
spdlog::drop_all(); spdlog::drop_all();
} }
TEST_CASE("clone", "[clone]")
{
using namespace spdlog;
auto logger = spdlog::create<sinks::test_sink_mt>("orig");
auto cloned = logger->clone("clone");
REQUIRE(cloned->name() == "clone");
REQUIRE(logger->sinks() == cloned->sinks());
REQUIRE(logger->level() == cloned->level());
REQUIRE(logger->flush_level() == cloned->flush_level());
logger->info("Some message 1");
cloned->info("Some message 2");
auto test_sink = std::static_pointer_cast<sinks::test_sink_mt>(cloned->sinks()[0]);
REQUIRE(test_sink->msg_counter() == 2);
spdlog::drop_all();
}
TEST_CASE("clone async", "[clone]")
{
using namespace spdlog;
auto logger = spdlog::create_async<sinks::test_sink_mt>("orig");
auto cloned = logger->clone("clone");
REQUIRE(cloned->name() == "clone");
REQUIRE(logger->sinks() == cloned->sinks());
REQUIRE(logger->level() == cloned->level());
REQUIRE(logger->flush_level() == cloned->flush_level());
logger->info("Some message 1");
cloned->info("Some message 2");
spdlog::details::os::sleep_for_millis(10);
auto test_sink = std::static_pointer_cast<sinks::test_sink_mt>(cloned->sinks()[0]);
REQUIRE(test_sink->msg_counter() == 2);
spdlog::drop_all();
}
#include "spdlog/fmt/bin_to_hex.h"
TEST_CASE("to_hex", "[to_hex]")
{
std::ostringstream oss;
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
spdlog::logger oss_logger("oss", oss_sink);
std::vector<unsigned char> v{9, 0xa, 0xb, 0xc, 0xff, 0xff};
oss_logger.info("{}", spdlog::to_hex(v));
auto output = oss.str();
REQUIRE(ends_with(output, "0000: 09 0a 0b 0c ff ff" + std::string(spdlog::details::os::default_eol)));
}
TEST_CASE("to_hex_upper", "[to_hex]")
{
std::ostringstream oss;
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
spdlog::logger oss_logger("oss", oss_sink);
std::vector<unsigned char> v{9, 0xa, 0xb, 0xc, 0xff, 0xff};
oss_logger.info("{:X}", spdlog::to_hex(v));
auto output = oss.str();
REQUIRE(ends_with(output, "0000: 09 0A 0B 0C FF FF" + std::string(spdlog::details::os::default_eol)));
}
TEST_CASE("to_hex_no_delimiter", "[to_hex]")
{
std::ostringstream oss;
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
spdlog::logger oss_logger("oss", oss_sink);
std::vector<unsigned char> v{9, 0xa, 0xb, 0xc, 0xff, 0xff};
oss_logger.info("{:sX}", spdlog::to_hex(v));
auto output = oss.str();
REQUIRE(ends_with(output, "0000: 090A0B0CFFFF" + std::string(spdlog::details::os::default_eol)));
}
TEST_CASE("message_counter", "[message_counter]")
{
std::ostringstream oss;
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
spdlog::logger oss_logger("oss", oss_sink);
oss_logger.set_pattern("%i %v");
oss_logger.info("Hello");
REQUIRE(oss.str() == "000001 Hello" + std::string(spdlog::details::os::default_eol));
oss.str("");
oss_logger.info("Hello again");
REQUIRE(oss.str() == "000002 Hello again" + std::string(spdlog::details::os::default_eol));
}

View File

@@ -2,11 +2,11 @@
using namespace std::chrono; using namespace std::chrono;
using std::chrono::milliseconds; using std::chrono::milliseconds;
using std::chrono::system_clock; using test_clock = std::chrono::high_resolution_clock;
system_clock::time_point now_millis() static milliseconds millis_from(const test_clock::time_point &tp0)
{ {
return time_point_cast<milliseconds>(system_clock::now()); return std::chrono::duration_cast<milliseconds>(test_clock::now() - tp0);
} }
TEST_CASE("dequeue-empty-nowait", "[mpmc_blocking_q]") TEST_CASE("dequeue-empty-nowait", "[mpmc_blocking_q]")
{ {
@@ -15,12 +15,13 @@ TEST_CASE("dequeue-empty-nowait", "[mpmc_blocking_q]")
spdlog::details::mpmc_blocking_queue<int> q(q_size); spdlog::details::mpmc_blocking_queue<int> q(q_size);
int popped_item; int popped_item;
auto millis_0 = now_millis(); auto start = test_clock::now();
auto rv = q.dequeue_for(popped_item, milliseconds::zero()); auto rv = q.dequeue_for(popped_item, milliseconds::zero());
auto millis_1 = now_millis(); auto delta_ms = millis_from(start);
REQUIRE(rv == false); REQUIRE(rv == false);
REQUIRE((millis_1 - millis_0) <= tolerance_wait); INFO("Delta " << delta_ms.count() << " millis");
REQUIRE(delta_ms <= tolerance_wait);
} }
TEST_CASE("dequeue-empty-wait", "[mpmc_blocking_q]") TEST_CASE("dequeue-empty-wait", "[mpmc_blocking_q]")
@@ -28,17 +29,18 @@ TEST_CASE("dequeue-empty-wait", "[mpmc_blocking_q]")
size_t q_size = 100; size_t q_size = 100;
milliseconds wait_ms(250); milliseconds wait_ms(250);
milliseconds tolerance_wait(10); milliseconds tolerance_wait(50);
spdlog::details::mpmc_blocking_queue<int> q(q_size); spdlog::details::mpmc_blocking_queue<int> q(q_size);
int popped_item; int popped_item;
auto millis_0 = now_millis(); auto start = test_clock::now();
auto rv = q.dequeue_for(popped_item, wait_ms); auto rv = q.dequeue_for(popped_item, wait_ms);
auto millis_1 = now_millis(); auto delta_ms = millis_from(start);
auto delta_ms = millis_1 - millis_0;
REQUIRE(rv == false); REQUIRE(rv == false);
REQUIRE(delta_ms >= wait_ms);
INFO("Delta " << delta_ms.count() << " millis");
REQUIRE(delta_ms >= wait_ms - tolerance_wait);
REQUIRE(delta_ms <= wait_ms + tolerance_wait); REQUIRE(delta_ms <= wait_ms + tolerance_wait);
} }
@@ -52,10 +54,12 @@ TEST_CASE("enqueue_nowait", "[mpmc_blocking_q]")
q.enqueue(1); q.enqueue(1);
REQUIRE(q.overrun_counter() == 0); REQUIRE(q.overrun_counter() == 0);
auto millis_0 = now_millis(); auto start = test_clock::now();
q.enqueue_nowait(2); q.enqueue_nowait(2);
auto millis_1 = now_millis(); auto delta_ms = millis_from(start);
REQUIRE((millis_1 - millis_0) <= tolerance_wait);
INFO("Delta " << delta_ms.count() << " millis");
REQUIRE(delta_ms <= tolerance_wait);
REQUIRE(q.overrun_counter() == 1); REQUIRE(q.overrun_counter() == 1);
} }

View File

@@ -20,9 +20,6 @@ TEST_CASE("custom eol", "[pattern_formatter]")
{ {
std::string msg = "Hello custom eol test"; std::string msg = "Hello custom eol test";
std::string eol = ";)"; std::string eol = ";)";
// auto formatter = std::make_shared<spdlog::pattern_formatter>("%v", spdlog::pattern_time_type::local, ";)");
std::unique_ptr<spdlog::formatter>(new spdlog::pattern_formatter("%v", spdlog::pattern_time_type::local, ";)"));
REQUIRE(log_to_str(msg, "%v", spdlog::pattern_time_type::local, ";)") == msg + eol); REQUIRE(log_to_str(msg, "%v", spdlog::pattern_time_type::local, ";)") == msg + eol);
} }

View File

@@ -56,7 +56,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sinks", "sinks", "{093AE34A
..\include\spdlog\sinks\ansicolor_sink.h = ..\include\spdlog\sinks\ansicolor_sink.h ..\include\spdlog\sinks\ansicolor_sink.h = ..\include\spdlog\sinks\ansicolor_sink.h
..\include\spdlog\sinks\base_sink.h = ..\include\spdlog\sinks\base_sink.h ..\include\spdlog\sinks\base_sink.h = ..\include\spdlog\sinks\base_sink.h
..\include\spdlog\sinks\dist_sink.h = ..\include\spdlog\sinks\dist_sink.h ..\include\spdlog\sinks\dist_sink.h = ..\include\spdlog\sinks\dist_sink.h
..\include\spdlog\sinks\file_sinks.h = ..\include\spdlog\sinks\file_sinks.h
..\include\spdlog\sinks\msvc_sink.h = ..\include\spdlog\sinks\msvc_sink.h ..\include\spdlog\sinks\msvc_sink.h = ..\include\spdlog\sinks\msvc_sink.h
..\include\spdlog\sinks\null_sink.h = ..\include\spdlog\sinks\null_sink.h ..\include\spdlog\sinks\null_sink.h = ..\include\spdlog\sinks\null_sink.h
..\include\spdlog\sinks\ostream_sink.h = ..\include\spdlog\sinks\ostream_sink.h ..\include\spdlog\sinks\ostream_sink.h = ..\include\spdlog\sinks\ostream_sink.h
@@ -65,7 +64,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sinks", "sinks", "{093AE34A
..\include\spdlog\sinks\stdout_sinks.h = ..\include\spdlog\sinks\stdout_sinks.h ..\include\spdlog\sinks\stdout_sinks.h = ..\include\spdlog\sinks\stdout_sinks.h
..\include\spdlog\sinks\syslog_sink.h = ..\include\spdlog\sinks\syslog_sink.h ..\include\spdlog\sinks\syslog_sink.h = ..\include\spdlog\sinks\syslog_sink.h
..\include\spdlog\sinks\wincolor_sink.h = ..\include\spdlog\sinks\wincolor_sink.h ..\include\spdlog\sinks\wincolor_sink.h = ..\include\spdlog\sinks\wincolor_sink.h
..\include\spdlog\sinks\windebug_sink.h = ..\include\spdlog\sinks\windebug_sink.h
EndProjectSection EndProjectSection
EndProject EndProject
Global Global

View File

@@ -8,8 +8,15 @@ void prepare_logdir()
system("del /F /Q logs\\*"); system("del /F /Q logs\\*");
#else #else
auto rv = system("mkdir -p logs"); auto rv = system("mkdir -p logs");
if (rv != 0)
{
throw std::runtime_error("Failed to mkdir -p logs");
}
rv = system("rm -f logs/*"); rv = system("rm -f logs/*");
(void)rv; if (rv != 0)
{
throw std::runtime_error("Failed to rm -f logs/*");
}
#endif #endif
} }
@@ -17,7 +24,9 @@ std::string file_contents(const std::string &filename)
{ {
std::ifstream ifs(filename); std::ifstream ifs(filename);
if (!ifs) if (!ifs)
{
throw std::runtime_error("Failed open file "); throw std::runtime_error("Failed open file ");
}
return std::string((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>())); return std::string((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
} }
@@ -25,7 +34,9 @@ std::size_t count_lines(const std::string &filename)
{ {
std::ifstream ifs(filename); std::ifstream ifs(filename);
if (!ifs) if (!ifs)
{
throw std::runtime_error("Failed open file "); throw std::runtime_error("Failed open file ");
}
std::string line; std::string line;
size_t counter = 0; size_t counter = 0;
@@ -38,7 +49,9 @@ std::size_t get_filesize(const std::string &filename)
{ {
std::ifstream ifs(filename, std::ifstream::ate | std::ifstream::binary); std::ifstream ifs(filename, std::ifstream::ate | std::ifstream::binary);
if (!ifs) if (!ifs)
{
throw std::runtime_error("Failed open file "); throw std::runtime_error("Failed open file ");
}
return static_cast<std::size_t>(ifs.tellg()); return static_cast<std::size_t>(ifs.tellg());
} }
@@ -47,6 +60,8 @@ std::size_t get_filesize(const std::string &filename)
bool ends_with(std::string const &value, std::string const &ending) bool ends_with(std::string const &value, std::string const &ending)
{ {
if (ending.size() > value.size()) if (ending.size() > value.size())
{
return false; return false;
}
return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
} }