mirror of
https://github.com/gabime/spdlog.git
synced 2024-12-26 10:31:34 +08:00
Merge remote-tracking branch 'origin/v1.x' into pwm1234/rotate_on_open
This commit is contained in:
commit
4fe98bf6e6
@ -88,7 +88,7 @@ PenaltyExcessCharacter: 1000000
|
|||||||
PenaltyReturnTypeOnItsOwnLine: 60
|
PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
PointerAlignment: Right
|
PointerAlignment: Right
|
||||||
ReflowComments: true
|
ReflowComments: true
|
||||||
SortIncludes: true
|
SortIncludes: false
|
||||||
SortUsingDeclarations: true
|
SortUsingDeclarations: true
|
||||||
SpaceAfterCStyleCast: false
|
SpaceAfterCStyleCast: false
|
||||||
SpaceAfterTemplateKeyword: false
|
SpaceAfterTemplateKeyword: false
|
||||||
|
28
.clang-tidy
Normal file
28
.clang-tidy
Normal 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'
|
||||||
|
|
@ -104,6 +104,7 @@ script:
|
|||||||
-DCMAKE_CXX_STANDARD=$CPP \
|
-DCMAKE_CXX_STANDARD=$CPP \
|
||||||
-DSPDLOG_BUILD_EXAMPLES=ON \
|
-DSPDLOG_BUILD_EXAMPLES=ON \
|
||||||
-DSPDLOG_BUILD_BENCH=OFF \
|
-DSPDLOG_BUILD_BENCH=OFF \
|
||||||
|
-DSPDLOG_BUILD_TESTS=ON \
|
||||||
-DSPDLOG_SANITIZE_ADDRESS=$ASAN \
|
-DSPDLOG_SANITIZE_ADDRESS=$ASAN \
|
||||||
-DSPDLOG_SANITIZE_THREAD=$TSAN
|
-DSPDLOG_SANITIZE_THREAD=$TSAN
|
||||||
- make VERBOSE=1 -j2
|
- make VERBOSE=1 -j2
|
||||||
|
@ -4,8 +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.3.0 LANGUAGES CXX)
|
||||||
include(CTest)
|
|
||||||
include(CMakeDependentOption)
|
include(CMakeDependentOption)
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
@ -45,13 +44,20 @@ 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()
|
||||||
|
|
||||||
cmake_dependent_option(SPDLOG_BUILD_TESTING
|
option(SPDLOG_BUILD_EXAMPLES "Build examples" ${SPDLOG_MASTER_PROJECT})
|
||||||
"Build spdlog tests" ON
|
option(SPDLOG_BUILD_BENCH "Build benchmarks" ${SPDLOG_MASTER_PROJECT})
|
||||||
"BUILD_TESTING" OFF
|
option(SPDLOG_BUILD_TESTS "Build tests" ${SPDLOG_MASTER_PROJECT})
|
||||||
)
|
option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF)
|
||||||
|
|
||||||
|
if(SPDLOG_FMT_EXTERNAL)
|
||||||
|
find_package(fmt REQUIRED CONFIG)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_include_directories(
|
target_include_directories(
|
||||||
spdlog
|
spdlog
|
||||||
@ -60,13 +66,19 @@ target_include_directories(
|
|||||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if(SPDLOG_FMT_EXTERNAL)
|
||||||
|
target_compile_definitions(spdlog INTERFACE SPDLOG_FMT_EXTERNAL)
|
||||||
|
target_link_libraries(spdlog INTERFACE fmt::fmt)
|
||||||
|
endif()
|
||||||
|
|
||||||
set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||||
|
|
||||||
if(SPDLOG_BUILD_EXAMPLES)
|
if(SPDLOG_BUILD_EXAMPLES)
|
||||||
add_subdirectory(example)
|
add_subdirectory(example)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(SPDLOG_BUILD_TESTING)
|
if(SPDLOG_BUILD_TESTS)
|
||||||
|
include(CTest)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
@ -82,7 +94,8 @@ set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
|||||||
set(include_install_dir "${CMAKE_INSTALL_INCLUDEDIR}")
|
set(include_install_dir "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||||
set(pkgconfig_install_dir "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
set(pkgconfig_install_dir "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||||
set(version_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
|
set(version_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
|
||||||
set(project_config "${PROJECT_NAME}Config.cmake")
|
set(project_config "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake")
|
||||||
|
set(targets_config "${PROJECT_NAME}Targets.cmake")
|
||||||
set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc")
|
set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc")
|
||||||
set(targets_export_name "${PROJECT_NAME}Targets")
|
set(targets_export_name "${PROJECT_NAME}Targets")
|
||||||
set(namespace "${PROJECT_NAME}::")
|
set(namespace "${PROJECT_NAME}::")
|
||||||
@ -95,6 +108,8 @@ write_basic_package_version_file(
|
|||||||
|
|
||||||
# configure pkg config file
|
# configure pkg config file
|
||||||
configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY)
|
configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY)
|
||||||
|
# configure spdlogConfig.cmake file
|
||||||
|
configure_file("cmake/Config.cmake.in" "${project_config}" @ONLY)
|
||||||
|
|
||||||
# install targets
|
# install targets
|
||||||
install(
|
install(
|
||||||
@ -108,9 +123,9 @@ install(
|
|||||||
DESTINATION "${include_install_dir}"
|
DESTINATION "${include_install_dir}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# install project version file
|
# install project config and version file
|
||||||
install(
|
install(
|
||||||
FILES "${version_config}"
|
FILES "${project_config}" "${version_config}"
|
||||||
DESTINATION "${config_install_dir}"
|
DESTINATION "${config_install_dir}"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -120,19 +135,19 @@ install(
|
|||||||
DESTINATION "${pkgconfig_install_dir}"
|
DESTINATION "${pkgconfig_install_dir}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# install project config file
|
# install targets config file
|
||||||
install(
|
install(
|
||||||
EXPORT "${targets_export_name}"
|
EXPORT "${targets_export_name}"
|
||||||
NAMESPACE "${namespace}"
|
NAMESPACE "${namespace}"
|
||||||
DESTINATION "${config_install_dir}"
|
DESTINATION "${config_install_dir}"
|
||||||
FILE ${project_config}
|
FILE ${targets_config}
|
||||||
)
|
)
|
||||||
|
|
||||||
# export build directory config file
|
# export build directory targets file
|
||||||
export(
|
export(
|
||||||
EXPORT ${targets_export_name}
|
EXPORT ${targets_export_name}
|
||||||
NAMESPACE "${namespace}"
|
NAMESPACE "${namespace}"
|
||||||
FILE ${project_config}
|
FILE ${targets_config}
|
||||||
)
|
)
|
||||||
|
|
||||||
# register project in CMake user registry
|
# register project in CMake user registry
|
||||||
|
122
README.md
122
README.md
@ -21,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.
|
||||||
@ -42,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
|
||||||
@ -54,32 +53,58 @@ 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
|
||||||
|
|
||||||
|
#### Basic usage
|
||||||
|
```c++
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
spdlog::info("Welcome to spdlog!");
|
||||||
|
spdlog::error("Some error message with arg: {}", 1);
|
||||||
|
|
||||||
|
spdlog::warn("Easy padding in numbers like {:08d}", 12);
|
||||||
|
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||||
|
spdlog::info("Support for floats {:03.2f}", 1.23456);
|
||||||
|
spdlog::info("Positional args are {1} {0}..", "too", "supported");
|
||||||
|
spdlog::info("{:<30}", "left aligned");
|
||||||
|
|
||||||
|
spdlog::set_level(spdlog::level::debug/ Set global log level to debug
|
||||||
|
spdlog::debug("This message should be displayed..");
|
||||||
|
|
||||||
|
// change log pattern
|
||||||
|
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
|
||||||
|
|
||||||
|
// Compile time log levels
|
||||||
|
// define SPDLOG_ACTIVE_LEVEL to desired level
|
||||||
|
SPDLOG_TRACE("Some trace message with param {}", {});
|
||||||
|
SPDLOG_DEBUG("Some debug message");
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
#### create stdout/stderr logger object
|
||||||
```c++
|
```c++
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
@ -87,35 +112,8 @@ void stdout_example()
|
|||||||
{
|
{
|
||||||
// create color multi threaded logger
|
// create color multi threaded logger
|
||||||
auto console = spdlog::stdout_color_mt("console");
|
auto console = spdlog::stdout_color_mt("console");
|
||||||
console->info("Welcome to spdlog!");
|
|
||||||
console->error("Some error message with arg: {}", 1);
|
|
||||||
|
|
||||||
auto err_logger = spdlog::stderr_color_mt("stderr");
|
auto err_logger = spdlog::stderr_color_mt("stderr");
|
||||||
err_logger->error("Some error message");
|
|
||||||
|
|
||||||
// Formatting examples
|
|
||||||
console->warn("Easy padding in numbers like {:08d}", 12);
|
|
||||||
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
|
||||||
console->info("Support for floats {:03.2f}", 1.23456);
|
|
||||||
console->info("Positional args are {1} {0}..", "too", "supported");
|
|
||||||
console->info("{:<30}", "left aligned");
|
|
||||||
|
|
||||||
spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
|
spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
|
||||||
|
|
||||||
// Runtime log levels
|
|
||||||
spdlog::set_level(spdlog::level::info); // Set global log level to info
|
|
||||||
console->debug("This message should not be displayed!");
|
|
||||||
console->set_level(spdlog::level::trace); // Set specific logger's log level
|
|
||||||
console->debug("This message should be displayed..");
|
|
||||||
|
|
||||||
// Customize msg format for all loggers
|
|
||||||
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
|
|
||||||
console->info("This an info message with custom format");
|
|
||||||
|
|
||||||
// Compile time log levels
|
|
||||||
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
|
|
||||||
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
|
|
||||||
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
---
|
---
|
||||||
@ -131,7 +129,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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -180,6 +177,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
|
||||||
@ -282,7 +306,7 @@ void syslog_example()
|
|||||||
---
|
---
|
||||||
#### Android example
|
#### Android example
|
||||||
```c++
|
```c++
|
||||||
#incude "spdlog/sinks/android_sink.h"
|
#include "spdlog/sinks/android_sink.h"
|
||||||
void android_example()
|
void android_example()
|
||||||
{
|
{
|
||||||
std::string tag = "spdlog-android";
|
std::string tag = "spdlog-android";
|
||||||
|
@ -26,7 +26,9 @@ build_script:
|
|||||||
|
|
||||||
set PATH=C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin;%PATH%
|
set PATH=C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin;%PATH%
|
||||||
|
|
||||||
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE%
|
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DSPDLOG_BUILD_BENCH=OFF
|
||||||
|
|
||||||
cmake --build . --config %BUILD_TYPE%
|
cmake --build . --config %BUILD_TYPE%
|
||||||
test: off
|
|
||||||
|
test_script:
|
||||||
|
- ctest -VV -C "%BUILD_TYPE%"
|
||||||
|
@ -38,6 +38,12 @@ add_executable(async_bench async_bench.cpp)
|
|||||||
target_link_libraries(async_bench spdlog::spdlog Threads::Threads)
|
target_link_libraries(async_bench spdlog::spdlog Threads::Threads)
|
||||||
|
|
||||||
add_executable(latency latency.cpp)
|
add_executable(latency latency.cpp)
|
||||||
|
set(CMAKE_CXX_STANDARD_LIBRARIES -lbenchmark)
|
||||||
target_link_libraries(latency spdlog::spdlog Threads::Threads)
|
target_link_libraries(latency spdlog::spdlog Threads::Threads)
|
||||||
|
|
||||||
|
|
||||||
|
add_executable(formatter-bench formatter-bench.cpp)
|
||||||
|
set(CMAKE_CXX_STANDARD_LIBRARIES -lbenchmark)
|
||||||
|
target_link_libraries(formatter-bench spdlog::spdlog Threads::Threads)
|
||||||
|
|
||||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
|
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
CXX ?= g++
|
CXX ?= g++
|
||||||
CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1
|
CXXFLAGS = -march=native -Wall -Wextra -pedantic -Wconversion -std=c++11 -pthread -I../include -fmax-errors=1
|
||||||
CXX_RELEASE_FLAGS = -Ofast -flto -Wl,--no-as-needed
|
CXX_RELEASE_FLAGS = -O3 -flto -Wl,--no-as-needed
|
||||||
|
|
||||||
|
|
||||||
binaries=bench latency async_bench
|
binaries=bench async_bench latency formatter-bench
|
||||||
|
|
||||||
all: $(binaries)
|
all: $(binaries)
|
||||||
|
|
||||||
@ -16,12 +16,16 @@ async_bench: async_bench.cpp
|
|||||||
|
|
||||||
|
|
||||||
latency: latency.cpp
|
latency: latency.cpp
|
||||||
$(CXX) latency.cpp -o latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
$(CXX) latency.cpp -o latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -lbenchmark
|
||||||
|
|
||||||
|
|
||||||
|
formatter-bench: formatter-bench.cpp
|
||||||
|
$(CXX) formatter-bench.cpp -o formatter-bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -lbenchmark
|
||||||
|
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f *.o logs/* $(binaries)
|
rm -f *.o logs/* latecy_logs $(binaries)
|
||||||
|
|
||||||
rebuild: clean all
|
rebuild: clean all
|
||||||
|
@ -6,10 +6,11 @@
|
|||||||
//
|
//
|
||||||
// bench.cpp : spdlog benchmarks
|
// bench.cpp : spdlog benchmarks
|
||||||
//
|
//
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
#include "spdlog/async.h"
|
#include "spdlog/async.h"
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
@ -35,6 +36,7 @@ int count_lines(const char *filename)
|
|||||||
if ('\n' == ch)
|
if ('\n' == ch)
|
||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
|
fclose(infile);
|
||||||
|
|
||||||
return counter;
|
return counter;
|
||||||
}
|
}
|
||||||
@ -48,11 +50,10 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto console = spdlog::stdout_color_mt("console");
|
|
||||||
if (argc == 1)
|
if (argc == 1)
|
||||||
{
|
{
|
||||||
console->set_pattern("%v");
|
spdlog::set_pattern("%v");
|
||||||
console->info("Usage: {} <message_count> <threads> <q_size> <iterations>", argv[0]);
|
spdlog::info("Usage: {} <message_count> <threads> <q_size> <iterations>", argv[0]);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,12 +67,12 @@ int main(int argc, char *argv[])
|
|||||||
if (argc > 4)
|
if (argc > 4)
|
||||||
iters = atoi(argv[4]);
|
iters = atoi(argv[4]);
|
||||||
|
|
||||||
console->info("-------------------------------------------------");
|
spdlog::info("-------------------------------------------------");
|
||||||
console->info("Messages: {:14n}", howmany);
|
spdlog::info("Messages: {:14n}", howmany);
|
||||||
console->info("Threads : {:14n}", threads);
|
spdlog::info("Threads : {:14n}", threads);
|
||||||
console->info("Queue : {:14n}", queue_size);
|
spdlog::info("Queue : {:14n}", queue_size);
|
||||||
console->info("Iters : {:>14n}", iters);
|
spdlog::info("Iters : {:>14n}", iters);
|
||||||
console->info("-------------------------------------------------");
|
spdlog::info("-------------------------------------------------");
|
||||||
|
|
||||||
const char *filename = "logs/basic_async.log";
|
const char *filename = "logs/basic_async.log";
|
||||||
|
|
||||||
@ -85,14 +86,15 @@ int main(int argc, char *argv[])
|
|||||||
|
|
||||||
if (count != howmany)
|
if (count != howmany)
|
||||||
{
|
{
|
||||||
console->error("Test failed. {} has {:n} lines instead of {:n}", filename, count, howmany);
|
spdlog::error("Test failed. {} has {:n} lines instead of {:n}", filename, count, howmany);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
console->info("Line count OK ({:n})\n", count);
|
spdlog::info("Line count OK ({:n})\n", count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
spdlog::shutdown();
|
||||||
}
|
}
|
||||||
catch (std::exception &ex)
|
catch (std::exception &ex)
|
||||||
{
|
{
|
||||||
@ -100,6 +102,7 @@ int main(int argc, char *argv[])
|
|||||||
perror("Last error");
|
perror("Last error");
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,5 +137,5 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> logger, int thread_co
|
|||||||
|
|
||||||
auto delta = high_resolution_clock::now() - start;
|
auto delta = high_resolution_clock::now() - start;
|
||||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
spdlog::get("console")->info("Elapsed: {} secs\t {:n}/sec", delta_d, int(howmany / delta_d));
|
spdlog::info("Elapsed: {} secs\t {:n}/sec", delta_d, int(howmany / delta_d));
|
||||||
}
|
}
|
||||||
|
115
bench/bench.cpp
115
bench/bench.cpp
@ -6,16 +6,16 @@
|
|||||||
//
|
//
|
||||||
// bench.cpp : spdlog benchmarks
|
// bench.cpp : spdlog benchmarks
|
||||||
//
|
//
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
#include "spdlog/async.h"
|
#include "spdlog/async.h"
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
#include "spdlog/sinks/daily_file_sink.h"
|
#include "spdlog/sinks/daily_file_sink.h"
|
||||||
#include "spdlog/sinks/null_sink.h"
|
#include "spdlog/sinks/null_sink.h"
|
||||||
#include "spdlog/sinks/rotating_file_sink.h"
|
#include "spdlog/sinks/rotating_file_sink.h"
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstdlib> // EXIT_FAILURE
|
#include <cstdlib> // EXIT_FAILURE
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@ -28,10 +28,13 @@ using namespace utils;
|
|||||||
|
|
||||||
void bench(int howmany, std::shared_ptr<spdlog::logger> log);
|
void bench(int howmany, std::shared_ptr<spdlog::logger> log);
|
||||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
||||||
|
void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log);
|
||||||
|
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log);
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
|
||||||
|
spdlog::default_logger()->set_pattern("[%^%l%$] %v");
|
||||||
int howmany = 1000000;
|
int howmany = 1000000;
|
||||||
int queue_size = howmany + 2;
|
int queue_size = howmany + 2;
|
||||||
int threads = 10;
|
int threads = 10;
|
||||||
@ -48,57 +51,65 @@ int main(int argc, char *argv[])
|
|||||||
if (argc > 3)
|
if (argc > 3)
|
||||||
queue_size = atoi(argv[3]);
|
queue_size = atoi(argv[3]);
|
||||||
|
|
||||||
cout << "******************************************************************"
|
spdlog::info("**************************************************************");
|
||||||
"*************\n";
|
spdlog::info("Single thread, {:n} iterations", howmany);
|
||||||
cout << "Single thread, " << format(howmany) << " iterations" << endl;
|
spdlog::info("**************************************************************");
|
||||||
cout << "******************************************************************"
|
|
||||||
"*************\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, std::move(basic_st));
|
||||||
|
|
||||||
|
basic_st.reset();
|
||||||
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);
|
||||||
bench(howmany, rotating_st);
|
bench(howmany, std::move(rotating_st));
|
||||||
|
|
||||||
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
|
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
|
||||||
bench(howmany, daily_st);
|
bench(howmany, std::move(daily_st));
|
||||||
|
|
||||||
bench(howmany, spdlog::create<null_sink_st>("null_st"));
|
bench(howmany, spdlog::create<null_sink_st>("null_st"));
|
||||||
|
|
||||||
cout << "\n****************************************************************"
|
spdlog::info("**************************************************************");
|
||||||
"***************\n";
|
spdlog::info("C-string (400 bytes). Single thread, {:n} iterations", howmany);
|
||||||
cout << threads << " threads sharing same logger, " << format(howmany) << " iterations" << endl;
|
spdlog::info("**************************************************************");
|
||||||
cout << "******************************************************************"
|
|
||||||
"*************\n";
|
basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_cs.log", true);
|
||||||
|
bench_c_string(howmany, std::move(basic_st));
|
||||||
|
|
||||||
|
rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_cs.log", file_size, rotating_files);
|
||||||
|
bench_c_string(howmany, std::move(rotating_st));
|
||||||
|
|
||||||
|
daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_cs.log");
|
||||||
|
bench_c_string(howmany, std::move(daily_st));
|
||||||
|
|
||||||
|
bench_c_string(howmany, spdlog::create<null_sink_st>("null_st"));
|
||||||
|
|
||||||
|
spdlog::info("**************************************************************");
|
||||||
|
spdlog::info("{:n} threads sharing same logger, {:n} iterations", threads, howmany);
|
||||||
|
spdlog::info("**************************************************************");
|
||||||
|
|
||||||
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
|
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
|
||||||
bench_mt(howmany, basic_mt, threads);
|
bench_mt(howmany, std::move(basic_mt), threads);
|
||||||
|
|
||||||
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files);
|
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files);
|
||||||
bench_mt(howmany, rotating_mt, threads);
|
bench_mt(howmany, std::move(rotating_mt), threads);
|
||||||
|
|
||||||
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
|
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
|
||||||
bench_mt(howmany, daily_mt, threads);
|
bench_mt(howmany, std::move(daily_mt), threads);
|
||||||
bench_mt(howmany, spdlog::create<null_sink_mt>("null_mt"), threads);
|
bench_mt(howmany, spdlog::create<null_sink_mt>("null_mt"), threads);
|
||||||
|
|
||||||
cout << "\n****************************************************************"
|
spdlog::info("**************************************************************");
|
||||||
"***************\n";
|
spdlog::info("Asyncronous.. {:n} threads sharing same logger, {:n} iterations", threads, howmany);
|
||||||
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations " << endl;
|
spdlog::info("**************************************************************");
|
||||||
cout << "******************************************************************"
|
|
||||||
"*************\n";
|
|
||||||
|
|
||||||
for (int i = 0; i < 3; ++i)
|
for (int i = 0; i < 3; ++i)
|
||||||
{
|
{
|
||||||
spdlog::init_thread_pool(static_cast<size_t>(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, std::move(as), threads);
|
||||||
spdlog::drop("async");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (std::exception &ex)
|
catch (std::exception &ex)
|
||||||
{
|
{
|
||||||
std::cerr << "Error: " << ex.what() << std::endl;
|
spdlog::error(ex.what());
|
||||||
perror("Last error");
|
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
@ -107,7 +118,6 @@ int main(int argc, char *argv[])
|
|||||||
void bench(int howmany, std::shared_ptr<spdlog::logger> log)
|
void bench(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||||
{
|
{
|
||||||
using std::chrono::high_resolution_clock;
|
using std::chrono::high_resolution_clock;
|
||||||
cout << log->name() << "...\t\t" << flush;
|
|
||||||
auto start = high_resolution_clock::now();
|
auto start = high_resolution_clock::now();
|
||||||
for (auto i = 0; i < howmany; ++i)
|
for (auto i = 0; i < howmany; ++i)
|
||||||
{
|
{
|
||||||
@ -117,14 +127,13 @@ void bench(int howmany, std::shared_ptr<spdlog::logger> log)
|
|||||||
auto delta = high_resolution_clock::now() - start;
|
auto delta = high_resolution_clock::now() - start;
|
||||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
|
|
||||||
cout << "Elapsed: " << delta_d << "\t" << format(int(howmany / delta_d)) << "/sec" << endl;
|
spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||||
spdlog::drop(log->name());
|
spdlog::drop(log->name());
|
||||||
}
|
}
|
||||||
|
|
||||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
|
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
|
||||||
{
|
{
|
||||||
using std::chrono::high_resolution_clock;
|
using std::chrono::high_resolution_clock;
|
||||||
cout << log->name() << "...\t\t" << flush;
|
|
||||||
vector<thread> threads;
|
vector<thread> threads;
|
||||||
auto start = high_resolution_clock::now();
|
auto start = high_resolution_clock::now();
|
||||||
for (int t = 0; t < thread_count; ++t)
|
for (int t = 0; t < thread_count; ++t)
|
||||||
@ -144,5 +153,47 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count
|
|||||||
|
|
||||||
auto delta = high_resolution_clock::now() - start;
|
auto delta = high_resolution_clock::now() - start;
|
||||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
cout << "Elapsed: " << delta_d << "\t" << format(int(howmany / delta_d)) << "/sec" << endl;
|
spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||||
|
spdlog::drop(log->name());
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||||
|
{
|
||||||
|
using std::chrono::high_resolution_clock;
|
||||||
|
auto orig_default = spdlog::default_logger();
|
||||||
|
spdlog::set_default_logger(log);
|
||||||
|
auto start = high_resolution_clock::now();
|
||||||
|
for (auto i = 0; i < howmany; ++i)
|
||||||
|
{
|
||||||
|
spdlog::info("Hello logger: msg number {}", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto delta = high_resolution_clock::now() - start;
|
||||||
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
|
spdlog::drop(log->name());
|
||||||
|
spdlog::set_default_logger(std::move(orig_default));
|
||||||
|
spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||||
|
{
|
||||||
|
const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus "
|
||||||
|
"lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus volutpat mi, eu consequat sem "
|
||||||
|
"libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam non dapibus eros. Donec fringilla dui sed "
|
||||||
|
"augue pretium, nec scelerisque est maximus. Nullam convallis, sem nec blandit maximus, nisi turpis ornare "
|
||||||
|
"nisl, sit amet volutpat neque massa eu odio. Maecenas malesuada quam ex, posuere congue nibh turpis duis.";
|
||||||
|
using std::chrono::high_resolution_clock;
|
||||||
|
auto orig_default = spdlog::default_logger();
|
||||||
|
spdlog::set_default_logger(log);
|
||||||
|
auto start = high_resolution_clock::now();
|
||||||
|
for (auto i = 0; i < howmany; ++i)
|
||||||
|
{
|
||||||
|
spdlog::log(level::info, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto delta = high_resolution_clock::now() - start;
|
||||||
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
|
spdlog::drop(log->name());
|
||||||
|
spdlog::set_default_logger(std::move(orig_default));
|
||||||
|
spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
|
||||||
}
|
}
|
||||||
|
92
bench/formatter-bench.cpp
Normal file
92
bench/formatter-bench.cpp
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2018 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
#include "spdlog/details/pattern_formatter.h"
|
||||||
|
|
||||||
|
void bench_scoped_pad(benchmark::State &state, size_t wrapped_size, spdlog::details::padding_info padinfo)
|
||||||
|
{
|
||||||
|
fmt::memory_buffer dest;
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
spdlog::details::scoped_pad p(wrapped_size, padinfo, dest);
|
||||||
|
benchmark::DoNotOptimize(p);
|
||||||
|
dest.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_formatter(benchmark::State &state, std::string pattern)
|
||||||
|
{
|
||||||
|
auto formatter = spdlog::details::make_unique<spdlog::pattern_formatter>(pattern);
|
||||||
|
fmt::memory_buffer dest;
|
||||||
|
std::string logger_name = "logger-name";
|
||||||
|
const char *text = "Hello. This is some message with length of 80 ";
|
||||||
|
|
||||||
|
spdlog::details::log_msg msg(&logger_name, spdlog::level::info, text);
|
||||||
|
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
dest.clear();
|
||||||
|
formatter->format(msg, dest);
|
||||||
|
benchmark::DoNotOptimize(dest);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_formatters()
|
||||||
|
{
|
||||||
|
// basic patterns(single flag)
|
||||||
|
std::string all_flags = "+vtPnlLaAbBcCYDmdHIMSefFprRTXzEi%";
|
||||||
|
std::vector<std::string> basic_patterns;
|
||||||
|
for (auto &flag : all_flags)
|
||||||
|
{
|
||||||
|
auto pattern = std::string("%") + flag;
|
||||||
|
benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
||||||
|
|
||||||
|
// pattern = std::string("%16") + flag;
|
||||||
|
// benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
||||||
|
//
|
||||||
|
// // bench center padding
|
||||||
|
// pattern = std::string("%=16") + flag;
|
||||||
|
// benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
// complex patterns
|
||||||
|
std::vector<std::string> patterns = {
|
||||||
|
"[%D %X] [%l] [%n] %v",
|
||||||
|
"[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] %v",
|
||||||
|
"[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] [%t] %v",
|
||||||
|
};
|
||||||
|
for (auto &pattern : patterns)
|
||||||
|
{
|
||||||
|
benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern)->Iterations(2500000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
|
||||||
|
spdlog::set_pattern("[%^%l%$] %v");
|
||||||
|
if (argc != 2)
|
||||||
|
{
|
||||||
|
spdlog::error("Usage: {} <pattern> (or \"all\" to bench all)", argv[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string pattern = argv[1];
|
||||||
|
if (pattern == "all")
|
||||||
|
{
|
||||||
|
bench_formatters();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
||||||
|
}
|
||||||
|
benchmark::Initialize(&argc, argv);
|
||||||
|
benchmark::RunSpecifiedBenchmarks();
|
||||||
|
}
|
@ -6,146 +6,138 @@
|
|||||||
//
|
//
|
||||||
// latency.cpp : spdlog latency benchmarks
|
// latency.cpp : spdlog latency benchmarks
|
||||||
//
|
//
|
||||||
|
|
||||||
|
#include "benchmark/benchmark.h"
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
#include "spdlog/async.h"
|
#include "spdlog/async.h"
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
#include "spdlog/sinks/daily_file_sink.h"
|
#include "spdlog/sinks/daily_file_sink.h"
|
||||||
#include "spdlog/sinks/null_sink.h"
|
#include "spdlog/sinks/null_sink.h"
|
||||||
#include "spdlog/sinks/rotating_file_sink.h"
|
#include "spdlog/sinks/rotating_file_sink.h"
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#include "utils.h"
|
|
||||||
#include <atomic>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
using namespace std;
|
void prepare_logdir()
|
||||||
using namespace std::chrono;
|
|
||||||
using namespace spdlog;
|
|
||||||
using namespace spdlog::sinks;
|
|
||||||
using namespace utils;
|
|
||||||
|
|
||||||
void bench(int howmany, std::shared_ptr<spdlog::logger> log);
|
|
||||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
|
||||||
|
|
||||||
int main(int, char *[])
|
|
||||||
{
|
{
|
||||||
std::srand(static_cast<unsigned>(std::time(nullptr))); // use current time as seed for random generator
|
spdlog::info("Preparing latency_logs directory..");
|
||||||
int howmany = 1000000;
|
#ifdef _WIN32
|
||||||
int queue_size = howmany + 2;
|
system("if not exist logs mkdir latency_logs");
|
||||||
int threads = 10;
|
system("del /F /Q logs\\*");
|
||||||
|
#else
|
||||||
|
auto rv = system("mkdir -p latency_logs");
|
||||||
|
if (rv != 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to mkdir -p latency_logs");
|
||||||
|
}
|
||||||
|
rv = system("rm -f latency_logs/*");
|
||||||
|
if (rv != 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("Failed to rm -f latency_logs/*");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_c_string(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
|
||||||
|
{
|
||||||
|
const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus "
|
||||||
|
"lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus volutpat mi, eu consequat sem "
|
||||||
|
"libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam non dapibus eros. Donec fringilla dui sed "
|
||||||
|
"augue pretium, nec scelerisque est maximus. Nullam convallis, sem nec blandit maximus, nisi turpis ornare "
|
||||||
|
"nisl, sit amet volutpat neque massa eu odio. Maecenas malesuada quam ex, posuere congue nibh turpis duis.";
|
||||||
|
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
logger->info(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_logger(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
logger->info("Hello logger: msg number {}...............", ++i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_disabled_macro(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
benchmark::DoNotOptimize(i); // prevent unused warnings
|
||||||
|
benchmark::DoNotOptimize(logger); // prevent unused warnings
|
||||||
|
for (auto _ : state)
|
||||||
|
{
|
||||||
|
SPDLOG_LOGGER_DEBUG(logger, "Hello logger: msg number {}...............", i++);
|
||||||
|
SPDLOG_DEBUG("Hello logger: msg number {}...............", i++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
|
||||||
|
using spdlog::sinks::basic_file_sink_mt;
|
||||||
|
using spdlog::sinks::basic_file_sink_st;
|
||||||
|
using spdlog::sinks::null_sink_mt;
|
||||||
|
using spdlog::sinks::null_sink_st;
|
||||||
|
|
||||||
size_t file_size = 30 * 1024 * 1024;
|
size_t file_size = 30 * 1024 * 1024;
|
||||||
size_t rotating_files = 5;
|
size_t rotating_files = 5;
|
||||||
|
int n_threads = benchmark::CPUInfo::Get().num_cpus;
|
||||||
|
|
||||||
try
|
prepare_logdir();
|
||||||
{
|
|
||||||
|
|
||||||
cout << "******************************************************************"
|
// disabled loggers
|
||||||
"*************\n";
|
auto disabled_logger = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>());
|
||||||
cout << "Single thread\n";
|
disabled_logger->set_level(spdlog::level::off);
|
||||||
cout << "******************************************************************"
|
benchmark::RegisterBenchmark("disabled-at-compile-time", bench_disabled_macro, disabled_logger);
|
||||||
"*************\n";
|
benchmark::RegisterBenchmark("disabled-at-runtime", bench_logger, disabled_logger);
|
||||||
|
|
||||||
auto basic_st = spdlog::basic_logger_mt("basic_st", "logs/basic_st.log", true);
|
auto null_logger_st = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_st>());
|
||||||
bench(howmany, basic_st);
|
benchmark::RegisterBenchmark("null_sink_st (500_bytes c_str)", bench_c_string, std::move(null_logger_st));
|
||||||
|
benchmark::RegisterBenchmark("null_sink_st", bench_logger, null_logger_st);
|
||||||
|
|
||||||
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
|
// basic_st
|
||||||
bench(howmany, rotating_st);
|
auto basic_st = spdlog::basic_logger_st("basic_st", "latency_logs/basic_st.log", true);
|
||||||
|
benchmark::RegisterBenchmark("basic_st", bench_logger, std::move(basic_st))->UseRealTime();
|
||||||
|
spdlog::drop("basic_st");
|
||||||
|
|
||||||
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
|
// rotating st
|
||||||
bench(howmany, daily_st);
|
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "latency_logs/rotating_st.log", file_size, rotating_files);
|
||||||
|
benchmark::RegisterBenchmark("rotating_st", bench_logger, std::move(rotating_st))->UseRealTime();
|
||||||
|
spdlog::drop("rotating_st");
|
||||||
|
|
||||||
bench(howmany, spdlog::create<null_sink_st>("null_st"));
|
// daily st
|
||||||
|
auto daily_st = spdlog::daily_logger_mt("daily_st", "latency_logs/daily_st.log");
|
||||||
|
benchmark::RegisterBenchmark("daily_st", bench_logger, std::move(daily_st))->UseRealTime();
|
||||||
|
spdlog::drop("daily_st");
|
||||||
|
|
||||||
cout << "\n****************************************************************"
|
// //
|
||||||
"***************\n";
|
// // Multi threaded bench, 10 loggers using same logger concurrently
|
||||||
cout << threads << " threads sharing same logger\n";
|
// //
|
||||||
cout << "******************************************************************"
|
auto null_logger_mt = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>());
|
||||||
"*************\n";
|
benchmark::RegisterBenchmark("null_sink_mt", bench_logger, null_logger_mt)->Threads(n_threads)->UseRealTime();
|
||||||
|
|
||||||
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
|
// basic_mt
|
||||||
bench_mt(howmany, basic_mt, threads);
|
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "latency_logs/basic_mt.log", true);
|
||||||
|
benchmark::RegisterBenchmark("basic_mt", bench_logger, std::move(basic_mt))->Threads(n_threads)->UseRealTime();
|
||||||
|
spdlog::drop("basic_mt");
|
||||||
|
|
||||||
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files);
|
// rotating mt
|
||||||
bench_mt(howmany, rotating_mt, threads);
|
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "latency_logs/rotating_mt.log", file_size, rotating_files);
|
||||||
|
benchmark::RegisterBenchmark("rotating_mt", bench_logger, std::move(rotating_mt))->Threads(n_threads)->UseRealTime();
|
||||||
|
spdlog::drop("rotating_mt");
|
||||||
|
|
||||||
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
|
// daily mt
|
||||||
bench_mt(howmany, daily_mt, threads);
|
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "latency_logs/daily_mt.log");
|
||||||
bench(howmany, spdlog::create<null_sink_st>("null_mt"));
|
benchmark::RegisterBenchmark("daily_mt", bench_logger, std::move(daily_mt))->Threads(n_threads)->UseRealTime();
|
||||||
|
spdlog::drop("daily_mt");
|
||||||
|
|
||||||
cout << "\n****************************************************************"
|
// async
|
||||||
"***************\n";
|
auto queue_size = 1024 * 1024 * 3;
|
||||||
cout << "async logging.. " << threads << " threads sharing same logger\n";
|
auto tp = std::make_shared<spdlog::details::thread_pool>(queue_size, 1);
|
||||||
cout << "******************************************************************"
|
auto async_logger = std::make_shared<spdlog::async_logger>(
|
||||||
"*************\n";
|
"async_logger", std::make_shared<null_sink_mt>(), std::move(tp), spdlog::async_overflow_policy::overrun_oldest);
|
||||||
|
benchmark::RegisterBenchmark("async_logger", bench_logger, async_logger)->Threads(n_threads)->UseRealTime();
|
||||||
|
|
||||||
for (int i = 0; i < 3; ++i)
|
benchmark::Initialize(&argc, argv);
|
||||||
{
|
benchmark::RunSpecifiedBenchmarks();
|
||||||
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);
|
|
||||||
bench_mt(howmany, as, threads);
|
|
||||||
spdlog::drop("async");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (std::exception &ex)
|
|
||||||
{
|
|
||||||
std::cerr << "Error: " << ex.what() << std::endl;
|
|
||||||
perror("Last error");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bench(int howmany, std::shared_ptr<spdlog::logger> log)
|
|
||||||
{
|
|
||||||
using namespace std::chrono;
|
|
||||||
using chrono::high_resolution_clock;
|
|
||||||
using chrono::milliseconds;
|
|
||||||
using chrono::nanoseconds;
|
|
||||||
|
|
||||||
cout << log->name() << "...\t\t" << flush;
|
|
||||||
nanoseconds total_nanos = nanoseconds::zero();
|
|
||||||
for (auto i = 0; i < howmany; ++i)
|
|
||||||
{
|
|
||||||
auto start = high_resolution_clock::now();
|
|
||||||
log->info("Hello logger: msg number {}", i);
|
|
||||||
auto delta_nanos = chrono::duration_cast<nanoseconds>(high_resolution_clock::now() - start);
|
|
||||||
total_nanos += delta_nanos;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto avg = total_nanos.count() / howmany;
|
|
||||||
cout << format(avg) << " ns/call" << endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
|
|
||||||
{
|
|
||||||
using namespace std::chrono;
|
|
||||||
using chrono::high_resolution_clock;
|
|
||||||
using chrono::milliseconds;
|
|
||||||
using chrono::nanoseconds;
|
|
||||||
|
|
||||||
cout << log->name() << "...\t\t" << flush;
|
|
||||||
vector<thread> threads;
|
|
||||||
std::atomic<nanoseconds::rep> total_nanos{0};
|
|
||||||
for (int t = 0; t < thread_count; ++t)
|
|
||||||
{
|
|
||||||
threads.push_back(std::thread([&]() {
|
|
||||||
for (int j = 0; j < howmany / thread_count; j++)
|
|
||||||
{
|
|
||||||
auto start = high_resolution_clock::now();
|
|
||||||
log->info("Hello logger: msg number {}", j);
|
|
||||||
auto delta_nanos = chrono::duration_cast<nanoseconds>(high_resolution_clock::now() - start);
|
|
||||||
total_nanos += delta_nanos.count();
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &t : threads)
|
|
||||||
{
|
|
||||||
t.join();
|
|
||||||
};
|
|
||||||
|
|
||||||
auto avg = total_nanos / howmany;
|
|
||||||
cout << format(avg) << " ns/call" << endl;
|
|
||||||
}
|
}
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <iostream>
|
|
||||||
#include <thread>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "spdlog/async.h"
|
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
using namespace std::chrono;
|
|
||||||
using clock = steady_clock;
|
|
||||||
|
|
||||||
int thread_count = 10;
|
|
||||||
if (argc > 1)
|
|
||||||
thread_count = std::atoi(argv[1]);
|
|
||||||
|
|
||||||
int howmany = 1000000;
|
|
||||||
spdlog::init_thread_pool(howmany, 1);
|
|
||||||
|
|
||||||
auto logger = spdlog::create_async_logger<spdlog::sinks::basic_file_sink_mt>("file_logger", "logs/spdlog-bench-async.log", false);
|
|
||||||
logger->set_pattern("[%Y-%m-%d %T.%F]: %L %t %v");
|
|
||||||
|
|
||||||
std::cout << "To stop, press <Enter>" << std::endl;
|
|
||||||
std::atomic<bool> run{true};
|
|
||||||
std::thread stoper(std::thread([&run]() {
|
|
||||||
std::cin.get();
|
|
||||||
run = false;
|
|
||||||
}));
|
|
||||||
|
|
||||||
while (run)
|
|
||||||
{
|
|
||||||
std::atomic<int> msg_counter{0};
|
|
||||||
std::vector<std::thread> threads;
|
|
||||||
|
|
||||||
auto start = clock::now();
|
|
||||||
for (int t = 0; t < thread_count; ++t)
|
|
||||||
{
|
|
||||||
threads.push_back(std::thread([&]() {
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
int counter = ++msg_counter;
|
|
||||||
if (counter > howmany)
|
|
||||||
break;
|
|
||||||
logger->info("spdlog message #{}: This is some text for your pleasure", counter);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &t : threads)
|
|
||||||
{
|
|
||||||
t.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
duration<float> delta = clock::now() - start;
|
|
||||||
float deltaf = delta.count();
|
|
||||||
auto rate = howmany / deltaf;
|
|
||||||
|
|
||||||
std::cout << "Total: " << howmany << std::endl;
|
|
||||||
std::cout << "Threads: " << thread_count << std::endl;
|
|
||||||
std::cout << "Delta = " << std::fixed << deltaf << " seconds" << std::endl;
|
|
||||||
std::cout << "Rate = " << std::fixed << rate << "/sec" << std::endl;
|
|
||||||
} // while
|
|
||||||
|
|
||||||
stoper.join();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
2
clang_tidy.sh
Executable file
2
clang_tidy.sh
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
clang-tidy example/example.cpp -- -I ./include
|
@ -21,4 +21,11 @@
|
|||||||
# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
# *************************************************************************/
|
# *************************************************************************/
|
||||||
|
|
||||||
|
set(SPDLOG_FMT_EXTERNAL @SPDLOG_FMT_EXTERNAL@)
|
||||||
|
|
||||||
include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
|
include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
|
||||||
|
|
||||||
|
if(SPDLOG_FMT_EXTERNAL)
|
||||||
|
include(CMakeFindDependencyMacro)
|
||||||
|
find_dependency(fmt CONFIG)
|
||||||
|
endif()
|
||||||
|
@ -32,7 +32,13 @@ endif()
|
|||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
add_executable(example example.cpp)
|
add_executable(example example.cpp)
|
||||||
|
if(CMAKE_SYSTEM_NAME STREQUAL "Android")
|
||||||
|
find_library(log-lib log)
|
||||||
|
target_link_libraries(example spdlog::spdlog Threads::Threads log)
|
||||||
|
else()
|
||||||
target_link_libraries(example spdlog::spdlog Threads::Threads)
|
target_link_libraries(example spdlog::spdlog Threads::Threads)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
add_executable(multisink multisink.cpp)
|
add_executable(multisink multisink.cpp)
|
||||||
target_link_libraries(multisink spdlog::spdlog Threads::Threads)
|
target_link_libraries(multisink spdlog::spdlog Threads::Threads)
|
||||||
|
@ -7,13 +7,15 @@
|
|||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <iostream>
|
#include <cstdio>
|
||||||
|
|
||||||
void stdout_example();
|
void stdout_logger_example();
|
||||||
void basic_example();
|
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 trace_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();
|
||||||
@ -24,119 +26,101 @@ void clone_example();
|
|||||||
|
|
||||||
int main(int, char *[])
|
int main(int, char *[])
|
||||||
{
|
{
|
||||||
|
spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH);
|
||||||
|
spdlog::warn("Easy padding in numbers like {:08d}", 12);
|
||||||
|
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||||
|
spdlog::info("Support for floats {:03.2f}", 1.23456);
|
||||||
|
spdlog::info("Positional args are {1} {0}..", "too", "supported");
|
||||||
|
spdlog::info("{:>8} aligned, {:<8} aligned", "right", "left");
|
||||||
|
|
||||||
|
// Runtime log levels
|
||||||
|
spdlog::set_level(spdlog::level::info); // Set global log level to info
|
||||||
|
spdlog::debug("This message should not be displayed!");
|
||||||
|
spdlog::set_level(spdlog::level::trace); // Set specific logger's log level
|
||||||
|
spdlog::debug("This message should be displayed..");
|
||||||
|
|
||||||
|
// Customize msg format for all loggers
|
||||||
|
spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [thread %t] %v");
|
||||||
|
spdlog::info("This an info message with custom format");
|
||||||
|
spdlog::set_pattern("%+"); // back to default format
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// console logging example
|
stdout_logger_example();
|
||||||
stdout_example();
|
|
||||||
|
|
||||||
// various file loggers
|
|
||||||
basic_example();
|
basic_example();
|
||||||
rotating_example();
|
rotating_example();
|
||||||
daily_example();
|
daily_example();
|
||||||
|
|
||||||
clone_example();
|
clone_example();
|
||||||
|
|
||||||
// async logging using a backing thread pool
|
|
||||||
async_example();
|
async_example();
|
||||||
|
binary_example();
|
||||||
// a logger can have multiple targets with different formats
|
|
||||||
multi_sink_example();
|
multi_sink_example();
|
||||||
|
|
||||||
// user defined types logging by implementing operator<<
|
|
||||||
user_defined_example();
|
user_defined_example();
|
||||||
|
|
||||||
// custom error handler
|
|
||||||
err_handler_example();
|
err_handler_example();
|
||||||
|
trace_example();
|
||||||
|
|
||||||
// flush all *registered* loggers using a worker thread every 3 seconds.
|
// Flush all *registered* loggers using a worker thread every 3 seconds.
|
||||||
// note: registered loggers *must* be thread safe for this to work correctly!
|
// note: registered loggers *must* be thread safe for this to work correctly!
|
||||||
spdlog::flush_every(std::chrono::seconds(3));
|
spdlog::flush_every(std::chrono::seconds(3));
|
||||||
|
|
||||||
// apply some function on all registered loggers
|
// Apply some function on all registered loggers
|
||||||
spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); });
|
spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); });
|
||||||
|
|
||||||
// release any threads created by spdlog, and drop all loggers in the registry.
|
// Release all spdlog resources, and drop all loggers in the registry.
|
||||||
|
// This is optional (only mandatory if using windows + async log).
|
||||||
spdlog::shutdown();
|
spdlog::shutdown();
|
||||||
}
|
}
|
||||||
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
|
|
||||||
|
// Exceptions will only be thrown upon failed logger or sink construction (not during logging).
|
||||||
catch (const spdlog::spdlog_ex &ex)
|
catch (const spdlog::spdlog_ex &ex)
|
||||||
{
|
{
|
||||||
std::cout << "Log init failed: " << ex.what() << std::endl;
|
std::printf("Log initialization failed: %s\n", ex.what());
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
// or #include "spdlog/sinks/stdout_sinks.h" if no colors needed
|
// or #include "spdlog/sinks/stdout_sinks.h" if no colors needed.
|
||||||
void stdout_example()
|
void stdout_logger_example()
|
||||||
{
|
{
|
||||||
// create color multi threaded logger
|
// Create color multi threaded logger.
|
||||||
auto console = spdlog::stdout_color_mt("console");
|
auto console = spdlog::stdout_color_mt("console");
|
||||||
console->info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH);
|
// or for stderr:
|
||||||
console->error("Some error message with arg: {}", 1);
|
// auto console = spdlog::stderr_color_mt("error-logger");
|
||||||
|
|
||||||
auto err_logger = spdlog::stderr_color_mt("stderr");
|
|
||||||
err_logger->error("Some error message");
|
|
||||||
|
|
||||||
// Formatting examples
|
|
||||||
console->warn("Easy padding in numbers like {:08d}", 12);
|
|
||||||
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
|
||||||
console->info("Support for floats {:03.2f}", 1.23456);
|
|
||||||
console->info("Positional args are {1} {0}..", "too", "supported");
|
|
||||||
console->info("{:<30}", "left aligned");
|
|
||||||
|
|
||||||
spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
|
|
||||||
|
|
||||||
// Runtime log levels
|
|
||||||
spdlog::set_level(spdlog::level::info); // Set global log level to info
|
|
||||||
console->debug("This message should not be displayed!");
|
|
||||||
console->set_level(spdlog::level::trace); // Set specific logger's log level
|
|
||||||
console->debug("This message should be displayed..");
|
|
||||||
|
|
||||||
// Customize msg format for all loggers
|
|
||||||
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
|
|
||||||
console->info("This an info message with custom format");
|
|
||||||
|
|
||||||
// Compile time log levels
|
|
||||||
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
|
|
||||||
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
|
|
||||||
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
void basic_example()
|
void basic_example()
|
||||||
{
|
{
|
||||||
// Create basic file logger (not rotated)
|
// Create basic file logger (not rotated).
|
||||||
auto my_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
|
auto my_logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "spdlog/sinks/rotating_file_sink.h"
|
#include "spdlog/sinks/rotating_file_sink.h"
|
||||||
void rotating_example()
|
void rotating_example()
|
||||||
{
|
{
|
||||||
// Create a file rotating logger with 5mb size max and 3 rotated files
|
// Create a file rotating logger with 5mb size max and 3 rotated files.
|
||||||
auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
|
auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "spdlog/sinks/daily_file_sink.h"
|
#include "spdlog/sinks/daily_file_sink.h"
|
||||||
void daily_example()
|
void daily_example()
|
||||||
{
|
{
|
||||||
// Create a daily logger - a new file is created every day on 2:30am
|
// Create a daily logger - a new file is created every day on 2:30am.
|
||||||
auto daily_logger = 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.
|
// Clone a logger and give it new name.
|
||||||
// Useful for creating component/subsystem loggers from some "root" logger
|
// Useful for creating component/subsystem loggers from some "root" logger.
|
||||||
void clone_example()
|
void clone_example()
|
||||||
{
|
{
|
||||||
auto network_logger = spdlog::get("console")->clone("network");
|
auto network_logger = spdlog::default_logger()->clone("network");
|
||||||
network_logger->info("Logging network stuff..");
|
network_logger->info("Logging network stuff..");
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "spdlog/async.h"
|
#include "spdlog/async.h"
|
||||||
void async_example()
|
void async_example()
|
||||||
{
|
{
|
||||||
// default thread pool settings can be modified *before* creating the async logger:
|
// Default thread pool settings can be modified *before* creating the async logger:
|
||||||
// spdlog::init_thread_pool(32768, 1); // queue with max 32k items 1 backing thread.
|
// spdlog::init_thread_pool(32768, 1); // queue with max 32k items 1 backing thread.
|
||||||
auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
|
auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
|
||||||
// alternatively:
|
// alternatively:
|
||||||
@ -148,9 +132,46 @@ void async_example()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// create logger with 2 targets with different log levels and formats
|
// Log binary data as hex.
|
||||||
// the console will show only warnings or errors, while the file will log all
|
// Many types of std::container<char> types can be used.
|
||||||
|
// Iterator 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()
|
||||||
|
{
|
||||||
|
std::vector<char> buf;
|
||||||
|
for (int i = 0; i < 80; i++)
|
||||||
|
{
|
||||||
|
buf.push_back(static_cast<char>(i & 0xff));
|
||||||
|
}
|
||||||
|
spdlog::info("Binary example: {}", spdlog::to_hex(buf));
|
||||||
|
spdlog::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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compile time log levels.
|
||||||
|
// define SPDLOG_ACTIVE_LEVEL to required level (e.g. SPDLOG_LEVEL_TRACE)
|
||||||
|
void trace_example()
|
||||||
|
{
|
||||||
|
// trace from default logger
|
||||||
|
SPDLOG_TRACE("Some trace message.. {} ,{}", 1, 3.23);
|
||||||
|
// debug from default logger
|
||||||
|
SPDLOG_DEBUG("Some debug message.. {} ,{}", 1, 3.23);
|
||||||
|
|
||||||
|
// trace from logger object
|
||||||
|
auto logger = spdlog::get("file_logger");
|
||||||
|
SPDLOG_LOGGER_TRACE(logger, "another trace message");
|
||||||
|
}
|
||||||
|
|
||||||
|
// A logger with multiple sinks (stdout and file) - each with a different format and log level.
|
||||||
void multi_sink_example()
|
void multi_sink_example()
|
||||||
{
|
{
|
||||||
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
||||||
@ -165,7 +186,8 @@ void multi_sink_example()
|
|||||||
logger.warn("this should appear in both console and file");
|
logger.warn("this should appear in both console and file");
|
||||||
logger.info("this message should not appear in the console, only in the file");
|
logger.info("this message should not appear in the console, only in the file");
|
||||||
}
|
}
|
||||||
// user defined types logging by implementing operator<<
|
|
||||||
|
// User defined types logging by implementing operator<<
|
||||||
#include "spdlog/fmt/ostr.h" // must be included
|
#include "spdlog/fmt/ostr.h" // must be included
|
||||||
struct my_type
|
struct my_type
|
||||||
{
|
{
|
||||||
@ -179,17 +201,14 @@ struct my_type
|
|||||||
|
|
||||||
void user_defined_example()
|
void user_defined_example()
|
||||||
{
|
{
|
||||||
spdlog::get("console")->info("user defined type: {}", my_type{14});
|
spdlog::info("user defined type: {}", my_type{14});
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
// Custom error handler. Will be triggered on log failure.
|
||||||
// custom error handler
|
|
||||||
//
|
|
||||||
void err_handler_example()
|
void err_handler_example()
|
||||||
{
|
{
|
||||||
// can be set globally or per logger(logger->set_error_handler(..))
|
// can be set globally or per logger(logger->set_error_handler(..))
|
||||||
spdlog::set_error_handler([](const std::string &msg) { spdlog::get("console")->error("*** ERROR HANDLER EXAMPLE ***: {}", msg); });
|
spdlog::set_error_handler([](const std::string &msg) { printf("*** Custom log error handler: %s ***\n", msg.c_str()); });
|
||||||
spdlog::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// syslog example (linux/osx/freebsd)
|
// syslog example (linux/osx/freebsd)
|
||||||
@ -203,7 +222,7 @@ void syslog_example()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Android example
|
// Android example.
|
||||||
#if defined(__ANDROID__)
|
#if defined(__ANDROID__)
|
||||||
#include "spdlog/sinks/android_sink.h"
|
#include "spdlog/sinks/android_sink.h"
|
||||||
void android_example()
|
void android_example()
|
||||||
|
@ -25,7 +25,6 @@
|
|||||||
<ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid>
|
<ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid>
|
||||||
<Keyword>Win32Proj</Keyword>
|
<Keyword>Win32Proj</Keyword>
|
||||||
<RootNamespace>.</RootNamespace>
|
<RootNamespace>.</RootNamespace>
|
||||||
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
|
#include "spdlog/spdlog.h"
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
#include "spdlog/sinks/stdout_sinks.h"
|
#include "spdlog/sinks/stdout_sinks.h"
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
|
|||||||
struct async_factory_impl
|
struct async_factory_impl
|
||||||
{
|
{
|
||||||
template<typename Sink, typename... SinkArgs>
|
template<typename Sink, typename... SinkArgs>
|
||||||
static std::shared_ptr<async_logger> create(const std::string &logger_name, SinkArgs &&... args)
|
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&... args)
|
||||||
{
|
{
|
||||||
auto ®istry_inst = details::registry::instance();
|
auto ®istry_inst = details::registry::instance();
|
||||||
|
|
||||||
@ -51,8 +51,8 @@ struct async_factory_impl
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
||||||
auto new_logger = std::make_shared<async_logger>(logger_name, std::move(sink), std::move(tp), OverflowPolicy);
|
auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy);
|
||||||
registry_inst.register_and_init(new_logger);
|
registry_inst.initialize_logger(new_logger);
|
||||||
return new_logger;
|
return new_logger;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -61,15 +61,15 @@ using async_factory = async_factory_impl<async_overflow_policy::block>;
|
|||||||
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
|
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
|
||||||
|
|
||||||
template<typename Sink, typename... SinkArgs>
|
template<typename Sink, typename... SinkArgs>
|
||||||
inline std::shared_ptr<spdlog::logger> create_async(const std::string &logger_name, SinkArgs &&... sink_args)
|
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&... sink_args)
|
||||||
{
|
{
|
||||||
return async_factory::create<Sink>(logger_name, std::forward<SinkArgs>(sink_args)...);
|
return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename Sink, typename... SinkArgs>
|
template<typename Sink, typename... SinkArgs>
|
||||||
inline std::shared_ptr<spdlog::logger> create_async_nb(const std::string &logger_name, SinkArgs &&... sink_args)
|
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&... sink_args)
|
||||||
{
|
{
|
||||||
return async_factory_nonblock::create<Sink>(logger_name, std::forward<SinkArgs>(sink_args)...);
|
return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
// set global thread pool.
|
// set global thread pool.
|
||||||
|
@ -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,
|
||||||
@ -61,7 +61,7 @@ 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:
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
#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)
|
||||||
@ -23,6 +25,8 @@
|
|||||||
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
#include "spdlog/details/null_mutex.h"
|
||||||
|
|
||||||
|
#include "spdlog/fmt/fmt.h"
|
||||||
|
|
||||||
// visual studio upto 2013 does not support noexcept nor constexpr
|
// visual studio upto 2013 does not support noexcept nor constexpr
|
||||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||||
#define SPDLOG_NOEXCEPT throw()
|
#define SPDLOG_NOEXCEPT throw()
|
||||||
@ -32,13 +36,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)
|
||||||
@ -47,7 +44,29 @@
|
|||||||
#define SPDLOG_DEPRECATED
|
#define SPDLOG_DEPRECATED
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "spdlog/fmt/fmt.h"
|
// disable thread local on msvc 2013
|
||||||
|
#ifndef SPDLOG_NO_TLS
|
||||||
|
#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
|
||||||
|
#define SPDLOG_NO_TLS 1
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Get the basename of __FILE__ (at compile time if possible)
|
||||||
|
#if FMT_HAS_FEATURE(__builtin_strrchr)
|
||||||
|
#define SPDLOG_STRRCHR(str, sep) __builtin_strrchr(str, sep)
|
||||||
|
#else
|
||||||
|
#define SPDLOG_STRRCHR(str, sep) strrchr(str, sep)
|
||||||
|
#endif //__builtin_strrchr not defined
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#define SPDLOG_FILE_BASENAME(file) SPDLOG_STRRCHR("\\" file, '\\') + 1
|
||||||
|
#else
|
||||||
|
#define SPDLOG_FILE_BASENAME(file) SPDLOG_STRRCHR("/" file, '/') + 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SPDLOG_FUNCTION
|
||||||
|
#define SPDLOG_FUNCTION __FUNCTION__
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
|
|
||||||
@ -62,23 +81,42 @@ using sink_ptr = std::shared_ptr<sinks::sink>;
|
|||||||
using sinks_init_list = std::initializer_list<sink_ptr>;
|
using sinks_init_list = std::initializer_list<sink_ptr>;
|
||||||
using log_err_handler = std::function<void(const std::string &err_msg)>;
|
using log_err_handler = std::function<void(const std::string &err_msg)>;
|
||||||
|
|
||||||
|
// string_view type - either std::string_view or fmt::string_view (pre c++17)
|
||||||
|
#if defined(FMT_USE_STD_STRING_VIEW)
|
||||||
|
using string_view_t = std::string_view;
|
||||||
|
#else
|
||||||
|
using string_view_t = fmt::string_view;
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
|
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
|
||||||
using level_t = details::null_atomic_int;
|
using level_t = details::null_atomic_int;
|
||||||
#else
|
#else
|
||||||
using level_t = std::atomic<int>;
|
using level_t = std::atomic<int>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define SPDLOG_LEVEL_TRACE 0
|
||||||
|
#define SPDLOG_LEVEL_DEBUG 1
|
||||||
|
#define SPDLOG_LEVEL_INFO 2
|
||||||
|
#define SPDLOG_LEVEL_WARN 3
|
||||||
|
#define SPDLOG_LEVEL_ERROR 4
|
||||||
|
#define SPDLOG_LEVEL_CRITICAL 5
|
||||||
|
#define SPDLOG_LEVEL_OFF 6
|
||||||
|
|
||||||
|
#if !defined(SPDLOG_ACTIVE_LEVEL)
|
||||||
|
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
|
||||||
|
#endif
|
||||||
|
|
||||||
// Log level enum
|
// Log level enum
|
||||||
namespace level {
|
namespace level {
|
||||||
enum level_enum
|
enum level_enum
|
||||||
{
|
{
|
||||||
trace = 0,
|
trace = SPDLOG_LEVEL_TRACE,
|
||||||
debug = 1,
|
debug = SPDLOG_LEVEL_DEBUG,
|
||||||
info = 2,
|
info = SPDLOG_LEVEL_INFO,
|
||||||
warn = 3,
|
warn = SPDLOG_LEVEL_WARN,
|
||||||
err = 4,
|
err = SPDLOG_LEVEL_ERROR,
|
||||||
critical = 5,
|
critical = SPDLOG_LEVEL_CRITICAL,
|
||||||
off = 6
|
off = SPDLOG_LEVEL_OFF,
|
||||||
};
|
};
|
||||||
|
|
||||||
#if !defined(SPDLOG_LEVEL_NAMES)
|
#if !defined(SPDLOG_LEVEL_NAMES)
|
||||||
@ -87,33 +125,32 @@ enum level_enum
|
|||||||
"trace", "debug", "info", "warning", "error", "critical", "off" \
|
"trace", "debug", "info", "warning", "error", "critical", "off" \
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
static const char *level_names[] SPDLOG_LEVEL_NAMES;
|
|
||||||
|
|
||||||
|
static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
|
||||||
static const char *short_level_names[]{"T", "D", "I", "W", "E", "C", "O"};
|
static const char *short_level_names[]{"T", "D", "I", "W", "E", "C", "O"};
|
||||||
|
|
||||||
inline const char *to_c_str(spdlog::level::level_enum l)
|
inline string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
return level_names[l];
|
return level_string_views[l];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const char *to_short_c_str(spdlog::level::level_enum l)
|
inline const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
return short_level_names[l];
|
return short_level_names[l];
|
||||||
}
|
}
|
||||||
|
|
||||||
inline spdlog::level::level_enum from_str(const std::string &name)
|
inline spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
static std::unordered_map<std::string, level_enum> name_to_level = // map string->level
|
int level = 0;
|
||||||
{{level_names[0], level::trace}, // trace
|
for (const auto &level_str : level_string_views)
|
||||||
{level_names[1], level::debug}, // debug
|
{
|
||||||
{level_names[2], level::info}, // info
|
if (level_str == name)
|
||||||
{level_names[3], level::warn}, // warn
|
{
|
||||||
{level_names[4], level::err}, // err
|
return static_cast<level::level_enum>(level);
|
||||||
{level_names[5], level::critical}, // critical
|
}
|
||||||
{level_names[6], level::off}}; // off
|
level++;
|
||||||
|
}
|
||||||
auto lvl_it = name_to_level.find(name);
|
return level::off;
|
||||||
return lvl_it != name_to_level.end() ? lvl_it->second : level::off;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using level_hasher = std::hash<int>;
|
using level_hasher = std::hash<int>;
|
||||||
@ -135,8 +172,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))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,26 +202,42 @@ using filename_t = std::wstring;
|
|||||||
using filename_t = std::string;
|
using filename_t = std::string;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define SPDLOG_CATCH_AND_HANDLE \
|
struct source_loc
|
||||||
catch (const std::exception &ex) \
|
{
|
||||||
{ \
|
SPDLOG_CONSTEXPR source_loc()
|
||||||
err_handler_(ex.what()); \
|
: filename{""}
|
||||||
} \
|
, line{0}
|
||||||
catch (...) \
|
, funcname{""}
|
||||||
{ \
|
{
|
||||||
err_handler_("Unknown exeption in logger"); \
|
}
|
||||||
|
SPDLOG_CONSTEXPR source_loc(const char *filename, int line, const char *funcname)
|
||||||
|
: filename{filename}
|
||||||
|
, line{static_cast<uint32_t>(line)}
|
||||||
|
, funcname{funcname}
|
||||||
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT
|
||||||
// make_unique support
|
{
|
||||||
//
|
return line == 0;
|
||||||
|
}
|
||||||
|
const char *filename;
|
||||||
|
uint32_t line;
|
||||||
|
const char *funcname;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
// make_unique support for pre c++14
|
||||||
|
|
||||||
#if __cplusplus >= 201402L // C++14 and beyond
|
#if __cplusplus >= 201402L // C++14 and beyond
|
||||||
using std::make_unique;
|
using std::make_unique;
|
||||||
#else
|
#else
|
||||||
template<typename T, typename... Args>
|
template<typename T, typename... Args>
|
||||||
std::unique_ptr<T> make_unique(Args &&... 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)...));
|
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
} // namespace details
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
@ -16,22 +16,22 @@
|
|||||||
|
|
||||||
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_(std::move(tp))
|
||||||
, overflow_policy_(overflow_policy)
|
, overflow_policy_(overflow_policy)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
inline spdlog::async_logger::async_logger(
|
inline spdlog::async_logger::async_logger(
|
||||||
std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
||||||
: async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), tp, overflow_policy)
|
: async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), std::move(tp), overflow_policy)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
inline spdlog::async_logger::async_logger(
|
inline spdlog::async_logger::async_logger(
|
||||||
std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
||||||
: async_logger(std::move(logger_name), {single_sink}, tp, overflow_policy)
|
: async_logger(std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ inline void spdlog::async_logger::sink_it_(details::log_msg &msg)
|
|||||||
#endif
|
#endif
|
||||||
if (auto pool_ptr = thread_pool_.lock())
|
if (auto pool_ptr = thread_pool_.lock())
|
||||||
{
|
{
|
||||||
pool_ptr->post_log(shared_from_this(), std::move(msg), overflow_policy_);
|
pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -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
|
||||||
{
|
{
|
||||||
|
@ -122,7 +122,7 @@ public:
|
|||||||
// ".mylog" => (".mylog". "")
|
// ".mylog" => (".mylog". "")
|
||||||
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
||||||
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
||||||
static std::tuple<filename_t, filename_t> split_by_extenstion(const spdlog::filename_t &fname)
|
static std::tuple<filename_t, filename_t> split_by_extension(const spdlog::filename_t &fname)
|
||||||
{
|
{
|
||||||
auto ext_index = fname.rfind('.');
|
auto ext_index = fname.rfind('.');
|
||||||
|
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "chrono"
|
#include <chrono>
|
||||||
|
#include <type_traits>
|
||||||
#include "spdlog/fmt/fmt.h"
|
#include "spdlog/fmt/fmt.h"
|
||||||
|
|
||||||
// Some fmt helpers to efficiently format and pad ints and strings
|
// Some fmt helpers to efficiently format and pad ints and strings
|
||||||
@ -13,17 +14,9 @@ namespace details {
|
|||||||
namespace fmt_helper {
|
namespace fmt_helper {
|
||||||
|
|
||||||
template<size_t Buffer_Size>
|
template<size_t Buffer_Size>
|
||||||
inline void append_str(const std::string &str, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
inline spdlog::string_view_t to_string_view(const fmt::basic_memory_buffer<char, Buffer_Size> &buf) SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
auto *str_ptr = str.data();
|
return spdlog::string_view_t(buf.data(), buf.size());
|
||||||
dest.append(str_ptr, str_ptr + str.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
template<size_t Buffer_Size>
|
|
||||||
inline void append_c_str(const char *c_str, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
|
||||||
{
|
|
||||||
auto len = std::char_traits<char>::length(c_str);
|
|
||||||
dest.append(c_str, c_str + len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t Buffer_Size1, size_t Buffer_Size2>
|
template<size_t Buffer_Size1, size_t Buffer_Size2>
|
||||||
@ -33,6 +26,16 @@ inline void append_buf(const fmt::basic_memory_buffer<char, Buffer_Size1> &buf,
|
|||||||
dest.append(buf_ptr, buf_ptr + buf.size());
|
dest.append(buf_ptr, buf_ptr + buf.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<size_t Buffer_Size>
|
||||||
|
inline void append_string_view(spdlog::string_view_t view, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
||||||
|
{
|
||||||
|
auto *buf_ptr = view.data();
|
||||||
|
if (buf_ptr != nullptr)
|
||||||
|
{
|
||||||
|
dest.append(buf_ptr, buf_ptr + view.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T, size_t Buffer_Size>
|
template<typename T, size_t Buffer_Size>
|
||||||
inline void append_int(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
inline void append_int(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
||||||
{
|
{
|
||||||
@ -40,73 +43,65 @@ inline void append_int(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
|||||||
dest.append(i.data(), i.data() + i.size());
|
dest.append(i.data(), i.data() + i.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline unsigned count_digits(T n)
|
||||||
|
{
|
||||||
|
using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
|
||||||
|
return static_cast<unsigned>(fmt::internal::count_digits(static_cast<count_type>(n)));
|
||||||
|
}
|
||||||
|
|
||||||
template<size_t Buffer_Size>
|
template<size_t Buffer_Size>
|
||||||
inline void pad2(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
inline void pad2(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
||||||
{
|
{
|
||||||
if (n > 99)
|
if (n > 99)
|
||||||
{
|
{
|
||||||
append_int(n, dest);
|
append_int(n, dest);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (n > 9) // 10-99
|
else if (n > 9) // 10-99
|
||||||
{
|
{
|
||||||
dest.push_back(static_cast<char>('0' + n / 10));
|
dest.push_back(static_cast<char>('0' + n / 10));
|
||||||
dest.push_back(static_cast<char>('0' + n % 10));
|
dest.push_back(static_cast<char>('0' + n % 10));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
if (n >= 0) // 0-9
|
else if (n >= 0) // 0-9
|
||||||
{
|
{
|
||||||
dest.push_back('0');
|
dest.push_back('0');
|
||||||
dest.push_back(static_cast<char>('0' + n));
|
dest.push_back(static_cast<char>('0' + n));
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// negatives (unlikely, but just in case, let fmt deal with it)
|
else // negatives (unlikely, but just in case, let fmt deal with it)
|
||||||
|
{
|
||||||
fmt::format_to(dest, "{:02}", n);
|
fmt::format_to(dest, "{:02}", n);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<size_t Buffer_Size>
|
template<typename T, size_t Buffer_Size>
|
||||||
inline void pad3(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
inline void pad_uint(T n, unsigned int width, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
||||||
{
|
{
|
||||||
if (n > 999)
|
static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
|
||||||
|
auto digits = count_digits(n);
|
||||||
|
if (width > digits)
|
||||||
{
|
{
|
||||||
|
const char *zeroes = "0000000000000000000";
|
||||||
|
dest.append(zeroes, zeroes + width - digits);
|
||||||
|
}
|
||||||
append_int(n, dest);
|
append_int(n, dest);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n > 99) // 100-999
|
template<typename T, size_t Buffer_Size>
|
||||||
|
inline void pad3(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
||||||
{
|
{
|
||||||
append_int(n / 100, dest);
|
pad_uint(n, 3, dest);
|
||||||
pad2(n % 100, dest);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (n > 9) // 10-99
|
|
||||||
{
|
|
||||||
dest.push_back('0');
|
|
||||||
dest.push_back(static_cast<char>('0' + n / 10));
|
|
||||||
dest.push_back(static_cast<char>('0' + n % 10));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (n >= 0)
|
|
||||||
{
|
|
||||||
dest.push_back('0');
|
|
||||||
dest.push_back('0');
|
|
||||||
dest.push_back(static_cast<char>('0' + n));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// negatives (unlikely, but just in case let fmt deal with it)
|
|
||||||
fmt::format_to(dest, "{:03}", n);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template<size_t Buffer_Size>
|
template<typename T, size_t Buffer_Size>
|
||||||
inline void pad6(size_t n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
inline void pad6(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
||||||
{
|
{
|
||||||
if (n > 99999)
|
pad_uint(n, 6, dest);
|
||||||
{
|
|
||||||
append_int(n, dest);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
pad3(static_cast<int>(n / 1000), dest);
|
|
||||||
pad3(static_cast<int>(n % 1000), dest);
|
template<typename T, size_t Buffer_Size>
|
||||||
|
inline void pad9(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
|
||||||
|
{
|
||||||
|
pad_uint(n, 9, dest);
|
||||||
}
|
}
|
||||||
|
|
||||||
// return fraction of a second of the given time_point.
|
// return fraction of a second of the given time_point.
|
||||||
@ -115,7 +110,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);
|
||||||
|
@ -15,9 +15,8 @@ namespace spdlog {
|
|||||||
namespace details {
|
namespace details {
|
||||||
struct log_msg
|
struct log_msg
|
||||||
{
|
{
|
||||||
log_msg() = default;
|
|
||||||
|
|
||||||
log_msg(const std::string *loggers_name, level::level_enum lvl)
|
log_msg(source_loc loc, const std::string *loggers_name, level::level_enum lvl, string_view_t view)
|
||||||
: logger_name(loggers_name)
|
: logger_name(loggers_name)
|
||||||
, level(lvl)
|
, level(lvl)
|
||||||
#ifndef SPDLOG_NO_DATETIME
|
#ifndef SPDLOG_NO_DATETIME
|
||||||
@ -26,23 +25,31 @@ struct log_msg
|
|||||||
|
|
||||||
#ifndef SPDLOG_NO_THREAD_ID
|
#ifndef SPDLOG_NO_THREAD_ID
|
||||||
, thread_id(os::thread_id())
|
, thread_id(os::thread_id())
|
||||||
|
, source(loc)
|
||||||
|
, payload(view)
|
||||||
#endif
|
#endif
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
log_msg(const log_msg &other) = delete;
|
log_msg(const std::string *loggers_name, level::level_enum lvl, string_view_t view)
|
||||||
log_msg(log_msg &&other) = delete;
|
: log_msg(source_loc{}, loggers_name, lvl, view)
|
||||||
log_msg &operator=(log_msg &&other) = delete;
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
log_msg(const log_msg &other) = default;
|
||||||
|
|
||||||
const std::string *logger_name{nullptr};
|
const std::string *logger_name{nullptr};
|
||||||
level::level_enum level;
|
level::level_enum level{level::off};
|
||||||
log_clock::time_point time;
|
log_clock::time_point time;
|
||||||
size_t thread_id;
|
size_t thread_id{0};
|
||||||
fmt::memory_buffer raw;
|
|
||||||
size_t msg_id{0};
|
size_t msg_id{0};
|
||||||
// info about wrapping the formatted text with color
|
|
||||||
|
// 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};
|
||||||
|
|
||||||
|
source_loc source;
|
||||||
|
const string_view_t payload;
|
||||||
};
|
};
|
||||||
} // namespace details
|
} // namespace details
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
@ -10,19 +10,23 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#define SPDLOG_CATCH_AND_HANDLE \
|
||||||
|
catch (const std::exception &ex) \
|
||||||
|
{ \
|
||||||
|
err_handler_(ex.what()); \
|
||||||
|
} \
|
||||||
|
catch (...) \
|
||||||
|
{ \
|
||||||
|
err_handler_("Unknown exception in logger"); \
|
||||||
|
}
|
||||||
|
|
||||||
// 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)
|
|
||||||
, flush_level_(level::off)
|
|
||||||
, last_err_time_(0)
|
|
||||||
, msg_counter_(1) // message counter will start from 1. 0-message id will be
|
|
||||||
// reserved for controll messages
|
|
||||||
{
|
{
|
||||||
err_handler_ = [this](const std::string &msg) { this->default_err_handler_(msg); };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ctor with sinks as init list
|
// ctor with sinks as init list
|
||||||
@ -49,11 +53,12 @@ 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>
|
||||||
inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args)
|
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const char *fmt, const Args &... args)
|
||||||
{
|
{
|
||||||
if (!should_log(lvl))
|
if (!should_log(lvl))
|
||||||
{
|
{
|
||||||
@ -62,15 +67,43 @@ inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Ar
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
details::log_msg log_msg(&name_, lvl);
|
using details::fmt_helper::to_string_view;
|
||||||
fmt::format_to(log_msg.raw, fmt, args...);
|
fmt::memory_buffer buf;
|
||||||
|
fmt::format_to(buf, fmt, args...);
|
||||||
|
details::log_msg log_msg(source, &name_, lvl, to_string_view(buf));
|
||||||
sink_it_(log_msg);
|
sink_it_(log_msg);
|
||||||
}
|
}
|
||||||
SPDLOG_CATCH_AND_HANDLE
|
SPDLOG_CATCH_AND_HANDLE
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(source_loc{}, lvl, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const char *msg)
|
||||||
|
{
|
||||||
|
if (!should_log(lvl))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
details::log_msg log_msg(source, &name_, lvl, spdlog::string_view_t(msg));
|
||||||
|
sink_it_(log_msg);
|
||||||
|
}
|
||||||
|
SPDLOG_CATCH_AND_HANDLE
|
||||||
|
}
|
||||||
|
|
||||||
inline void spdlog::logger::log(level::level_enum lvl, const char *msg)
|
inline void spdlog::logger::log(level::level_enum lvl, const char *msg)
|
||||||
|
{
|
||||||
|
log(source_loc{}, lvl, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
|
||||||
|
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const T &msg)
|
||||||
{
|
{
|
||||||
if (!should_log(lvl))
|
if (!should_log(lvl))
|
||||||
{
|
{
|
||||||
@ -78,15 +111,20 @@ 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(source, &name_, lvl, 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
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
|
||||||
inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
|
inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
|
||||||
|
{
|
||||||
|
log(source_loc{}, lvl, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
|
||||||
|
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const T &msg)
|
||||||
{
|
{
|
||||||
if (!should_log(lvl))
|
if (!should_log(lvl))
|
||||||
{
|
{
|
||||||
@ -94,13 +132,21 @@ inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
details::log_msg log_msg(&name_, lvl);
|
using details::fmt_helper::to_string_view;
|
||||||
fmt::format_to(log_msg.raw, "{}", msg);
|
fmt::memory_buffer buf;
|
||||||
|
fmt::format_to(buf, "{}", msg);
|
||||||
|
details::log_msg log_msg(source, &name_, lvl, to_string_view(buf));
|
||||||
sink_it_(log_msg);
|
sink_it_(log_msg);
|
||||||
}
|
}
|
||||||
SPDLOG_CATCH_AND_HANDLE
|
SPDLOG_CATCH_AND_HANDLE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
|
||||||
|
inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
|
||||||
|
{
|
||||||
|
log(source_loc{}, lvl, msg);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
inline void spdlog::logger::trace(const char *fmt, const Args &... args)
|
inline void spdlog::logger::trace(const char *fmt, const Args &... args)
|
||||||
{
|
{
|
||||||
@ -174,27 +220,56 @@ 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(source_loc source, level::level_enum lvl, const wchar_t *fmt, const Args &... args)
|
||||||
{
|
{
|
||||||
if (!should_log(lvl))
|
if (!should_log(lvl))
|
||||||
{
|
{
|
||||||
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_);
|
using details::fmt_helper::to_string_view;
|
||||||
utf8_string = wstring_converter_.to_bytes(fmt);
|
fmt::wmemory_buffer wbuf;
|
||||||
}
|
fmt::format_to(wbuf, fmt, args...);
|
||||||
log(lvl, utf8_string.c_str(), args...);
|
fmt::memory_buffer buf;
|
||||||
|
wbuf_to_utf8buf(wbuf, buf);
|
||||||
|
details::log_msg log_msg(source, &name_, lvl, to_string_view(buf));
|
||||||
|
sink_it_(log_msg);
|
||||||
}
|
}
|
||||||
SPDLOG_CATCH_AND_HANDLE
|
SPDLOG_CATCH_AND_HANDLE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(source_loc{}, lvl, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
inline void spdlog::logger::trace(const wchar_t *fmt, const Args &... args)
|
inline void spdlog::logger::trace(const wchar_t *fmt, const Args &... args)
|
||||||
{
|
{
|
||||||
@ -251,7 +326,7 @@ inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handle
|
|||||||
err_handler_ = std::move(err_handler);
|
err_handler_ = std::move(err_handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline spdlog::log_err_handler spdlog::logger::error_handler()
|
inline spdlog::log_err_handler spdlog::logger::error_handler() const
|
||||||
{
|
{
|
||||||
return err_handler_;
|
return err_handler_;
|
||||||
}
|
}
|
||||||
@ -281,6 +356,11 @@ inline bool spdlog::logger::should_flush_(const details::log_msg &msg)
|
|||||||
return (msg.level >= flush_level) && (msg.level != level::off);
|
return (msg.level >= flush_level) && (msg.level != level::off);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline spdlog::level::level_enum spdlog::logger::default_level()
|
||||||
|
{
|
||||||
|
return static_cast<spdlog::level::level_enum>(SPDLOG_ACTIVE_LEVEL);
|
||||||
|
}
|
||||||
|
|
||||||
inline spdlog::level::level_enum spdlog::logger::level() const
|
inline spdlog::level::level_enum spdlog::logger::level() const
|
||||||
{
|
{
|
||||||
return static_cast<spdlog::level::level_enum>(level_.load(std::memory_order_relaxed));
|
return static_cast<spdlog::level::level_enum>(level_.load(std::memory_order_relaxed));
|
||||||
@ -310,7 +390,7 @@ inline void spdlog::logger::sink_it_(details::log_msg &msg)
|
|||||||
|
|
||||||
if (should_flush_(msg))
|
if (should_flush_(msg))
|
||||||
{
|
{
|
||||||
flush();
|
flush_();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +57,7 @@ namespace spdlog {
|
|||||||
namespace details {
|
namespace details {
|
||||||
namespace os {
|
namespace os {
|
||||||
|
|
||||||
inline spdlog::log_clock::time_point now()
|
inline spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
|
|
||||||
#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
|
#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
|
||||||
@ -70,7 +70,7 @@ inline spdlog::log_clock::time_point now()
|
|||||||
return log_clock::now();
|
return log_clock::now();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
inline std::tm localtime(const std::time_t &time_tt)
|
inline std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -83,13 +83,13 @@ inline std::tm localtime(const std::time_t &time_tt)
|
|||||||
return tm;
|
return tm;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::tm localtime()
|
inline std::tm localtime() SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
std::time_t now_t = time(nullptr);
|
std::time_t now_t = time(nullptr);
|
||||||
return localtime(now_t);
|
return localtime(now_t);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::tm gmtime(const std::time_t &time_tt)
|
inline std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@ -102,21 +102,11 @@ inline std::tm gmtime(const std::time_t &time_tt)
|
|||||||
return tm;
|
return tm;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::tm gmtime()
|
inline std::tm gmtime() SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
std::time_t now_t = time(nullptr);
|
std::time_t now_t = time(nullptr);
|
||||||
return gmtime(now_t);
|
return gmtime(now_t);
|
||||||
}
|
}
|
||||||
inline bool operator==(const std::tm &tm1, const std::tm &tm2)
|
|
||||||
{
|
|
||||||
return (tm1.tm_sec == tm2.tm_sec && tm1.tm_min == tm2.tm_min && tm1.tm_hour == tm2.tm_hour && tm1.tm_mday == tm2.tm_mday &&
|
|
||||||
tm1.tm_mon == tm2.tm_mon && tm1.tm_year == tm2.tm_year && tm1.tm_isdst == tm2.tm_isdst);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline bool operator!=(const std::tm &tm1, const std::tm &tm2)
|
|
||||||
{
|
|
||||||
return !(tm1 == tm2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// eol definition
|
// eol definition
|
||||||
#if !defined(SPDLOG_EOL)
|
#if !defined(SPDLOG_EOL)
|
||||||
@ -176,7 +166,7 @@ inline bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mod
|
|||||||
return *fp == nullptr;
|
return *fp == nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int remove(const filename_t &filename)
|
inline int remove(const filename_t &filename) SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
return _wremove(filename.c_str());
|
return _wremove(filename.c_str());
|
||||||
@ -185,7 +175,7 @@ inline int remove(const filename_t &filename)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int rename(const filename_t &filename1, const filename_t &filename2)
|
inline int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
return _wrename(filename1.c_str(), filename2.c_str());
|
return _wrename(filename1.c_str(), filename2.c_str());
|
||||||
@ -195,7 +185,7 @@ inline int rename(const filename_t &filename1, const filename_t &filename2)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return if file exists
|
// Return if file exists
|
||||||
inline bool file_exists(const filename_t &filename)
|
inline bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#ifdef SPDLOG_WCHAR_FILENAMES
|
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||||
@ -220,10 +210,10 @@ inline size_t filesize(FILE *f)
|
|||||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||||
int fd = _fileno(f);
|
int fd = _fileno(f);
|
||||||
#if _WIN64 // 64 bits
|
#if _WIN64 // 64 bits
|
||||||
struct _stat64 st;
|
__int64 ret = _filelengthi64(fd);
|
||||||
if (_fstat64(fd, &st) == 0)
|
if (ret >= 0)
|
||||||
{
|
{
|
||||||
return st.st_size;
|
return static_cast<size_t>(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else // windows 32 bits
|
#else // windows 32 bits
|
||||||
@ -323,7 +313,7 @@ inline int utc_minutes_offset(const std::tm &tm = details::os::localtime())
|
|||||||
// Return current thread id as size_t
|
// Return current thread id as size_t
|
||||||
// It exists because the std::this_thread::get_id() is much slower(especially
|
// It exists because the std::this_thread::get_id() is much slower(especially
|
||||||
// under VS 2013)
|
// under VS 2013)
|
||||||
inline size_t _thread_id()
|
inline size_t _thread_id() SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
return static_cast<size_t>(::GetCurrentThreadId());
|
return static_cast<size_t>(::GetCurrentThreadId());
|
||||||
@ -346,10 +336,9 @@ inline size_t _thread_id()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Return current thread id as size_t (from thread local storage)
|
// Return current thread id as size_t (from thread local storage)
|
||||||
inline size_t thread_id()
|
inline size_t thread_id() SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
#if defined(SPDLOG_DISABLE_TID_CACHING) || (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt) || \
|
#if defined(SPDLOG_NO_TLS)
|
||||||
(defined(__clang__) && !__has_feature(cxx_thread_local))
|
|
||||||
return _thread_id();
|
return _thread_id();
|
||||||
#else // cache thread id in tls
|
#else // cache thread id in tls
|
||||||
static thread_local const size_t tid = _thread_id();
|
static thread_local const size_t tid = _thread_id();
|
||||||
@ -359,7 +348,7 @@ inline size_t thread_id()
|
|||||||
|
|
||||||
// This is avoid msvc issue in sleep_for that happens if the clock changes.
|
// This is avoid msvc issue in sleep_for that happens if the clock changes.
|
||||||
// See https://github.com/gabime/spdlog/issues/609
|
// See https://github.com/gabime/spdlog/issues/609
|
||||||
inline void sleep_for_millis(int milliseconds)
|
inline void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
::Sleep(milliseconds);
|
::Sleep(milliseconds);
|
||||||
@ -396,7 +385,7 @@ inline int pid()
|
|||||||
|
|
||||||
// Determine if the terminal supports colors
|
// Determine if the terminal supports colors
|
||||||
// Source: https://github.com/agauniyal/rang/
|
// Source: https://github.com/agauniyal/rang/
|
||||||
inline bool is_color_terminal()
|
inline bool is_color_terminal() SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
return true;
|
return true;
|
||||||
@ -418,7 +407,7 @@ inline bool is_color_terminal()
|
|||||||
|
|
||||||
// Detrmine if the terminal attached
|
// Detrmine if the terminal attached
|
||||||
// Source: https://github.com/agauniyal/rang/
|
// Source: https://github.com/agauniyal/rang/
|
||||||
inline bool in_terminal(FILE *file)
|
inline bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,7 @@
|
|||||||
//
|
//
|
||||||
// RAII over the owned thread:
|
// RAII over the owned thread:
|
||||||
// creates the thread on construction.
|
// creates the thread on construction.
|
||||||
// stops and joins the thread on destruction.
|
// stops and joins the thread on destruction (if the thread is executing a callback, wait for it to finish first).
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
|
@ -14,6 +14,15 @@
|
|||||||
#include "spdlog/details/periodic_worker.h"
|
#include "spdlog/details/periodic_worker.h"
|
||||||
#include "spdlog/logger.h"
|
#include "spdlog/logger.h"
|
||||||
|
|
||||||
|
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||||
|
// support for the default stdout color logger
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include "spdlog/sinks/wincolor_sink.h"
|
||||||
|
#else
|
||||||
|
#include "spdlog/sinks/ansicolor_sink.h"
|
||||||
|
#endif
|
||||||
|
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -33,18 +42,12 @@ public:
|
|||||||
void register_logger(std::shared_ptr<logger> new_logger)
|
void register_logger(std::shared_ptr<logger> new_logger)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
auto logger_name = new_logger->name();
|
register_logger_(std::move(new_logger));
|
||||||
throw_if_exists_(logger_name);
|
|
||||||
loggers_[logger_name] = std::move(new_logger);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void register_and_init(std::shared_ptr<logger> new_logger)
|
void initialize_logger(std::shared_ptr<logger> new_logger)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
auto logger_name = new_logger->name();
|
|
||||||
throw_if_exists_(logger_name);
|
|
||||||
|
|
||||||
// set the global formatter pattern
|
|
||||||
new_logger->set_formatter(formatter_->clone());
|
new_logger->set_formatter(formatter_->clone());
|
||||||
|
|
||||||
if (err_handler_)
|
if (err_handler_)
|
||||||
@ -55,8 +58,10 @@ public:
|
|||||||
new_logger->set_level(level_);
|
new_logger->set_level(level_);
|
||||||
new_logger->flush_on(flush_level_);
|
new_logger->flush_on(flush_level_);
|
||||||
|
|
||||||
// add to registry
|
if (automatic_registration_)
|
||||||
loggers_[logger_name] = std::move(new_logger);
|
{
|
||||||
|
register_logger_(std::move(new_logger));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<logger> get(const std::string &logger_name)
|
std::shared_ptr<logger> get(const std::string &logger_name)
|
||||||
@ -66,6 +71,38 @@ public:
|
|||||||
return found == loggers_.end() ? nullptr : found->second;
|
return found == loggers_.end() ? nullptr : found->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<logger> default_logger()
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
return default_logger_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return raw ptr to the default logger.
|
||||||
|
// To be used directly by the spdlog default api (e.g. spdlog::info)
|
||||||
|
// This make the default API faster, but cannot be used concurrently with set_default_logger().
|
||||||
|
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
|
||||||
|
logger *get_default_raw()
|
||||||
|
{
|
||||||
|
return default_logger_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
// set default logger.
|
||||||
|
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
|
||||||
|
void set_default_logger(std::shared_ptr<logger> new_default_logger)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
// remove previous default logger from the map
|
||||||
|
if (default_logger_ != nullptr)
|
||||||
|
{
|
||||||
|
loggers_.erase(default_logger_->name());
|
||||||
|
}
|
||||||
|
if (new_default_logger != nullptr)
|
||||||
|
{
|
||||||
|
loggers_[new_default_logger->name()] = new_default_logger;
|
||||||
|
}
|
||||||
|
default_logger_ = std::move(new_default_logger);
|
||||||
|
}
|
||||||
|
|
||||||
void set_tp(std::shared_ptr<thread_pool> tp)
|
void set_tp(std::shared_ptr<thread_pool> tp)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
|
||||||
@ -113,7 +150,7 @@ public:
|
|||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
std::lock_guard<std::mutex> lock(flusher_mutex_);
|
||||||
std::function<void()> clbk = std::bind(®istry::flush_all, this);
|
std::function<void()> clbk = std::bind(®istry::flush_all, this);
|
||||||
periodic_flusher_ = spdlog::make_unique<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)
|
||||||
@ -148,15 +185,20 @@ public:
|
|||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
loggers_.erase(logger_name);
|
loggers_.erase(logger_name);
|
||||||
|
if (default_logger_ && default_logger_->name() == logger_name)
|
||||||
|
{
|
||||||
|
default_logger_.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void drop_all()
|
void drop_all()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
loggers_.clear();
|
loggers_.clear();
|
||||||
|
default_logger_.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
// clean all reasources and threads started by the registry
|
// clean all resources and threads started by the registry
|
||||||
void shutdown()
|
void shutdown()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@ -177,6 +219,12 @@ public:
|
|||||||
return tp_mutex_;
|
return tp_mutex_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_automatic_registration(bool automatic_regsistration)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock(logger_map_mutex_);
|
||||||
|
automatic_registration_ = automatic_regsistration;
|
||||||
|
}
|
||||||
|
|
||||||
static registry &instance()
|
static registry &instance()
|
||||||
{
|
{
|
||||||
static registry s_instance;
|
static registry s_instance;
|
||||||
@ -185,8 +233,22 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
registry()
|
registry()
|
||||||
: formatter_(new pattern_formatter("%+"))
|
: formatter_(new pattern_formatter())
|
||||||
{
|
{
|
||||||
|
|
||||||
|
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||||
|
// create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows).
|
||||||
|
#ifdef _WIN32
|
||||||
|
auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>();
|
||||||
|
#else
|
||||||
|
auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const char *default_logger_name = "";
|
||||||
|
default_logger_ = std::make_shared<spdlog::logger>(default_logger_name, std::move(color_sink));
|
||||||
|
loggers_[default_logger_name] = default_logger_;
|
||||||
|
|
||||||
|
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||||
}
|
}
|
||||||
|
|
||||||
~registry() = default;
|
~registry() = default;
|
||||||
@ -199,15 +261,24 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void register_logger_(std::shared_ptr<logger> new_logger)
|
||||||
|
{
|
||||||
|
auto logger_name = new_logger->name();
|
||||||
|
throw_if_exists_(logger_name);
|
||||||
|
loggers_[logger_name] = std::move(new_logger);
|
||||||
|
}
|
||||||
|
|
||||||
std::mutex logger_map_mutex_, flusher_mutex_;
|
std::mutex logger_map_mutex_, flusher_mutex_;
|
||||||
std::recursive_mutex tp_mutex_;
|
std::recursive_mutex tp_mutex_;
|
||||||
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
|
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
|
||||||
std::unique_ptr<formatter> formatter_;
|
std::unique_ptr<formatter> formatter_;
|
||||||
level::level_enum level_ = level::info;
|
level::level_enum level_ = spdlog::logger::default_level();
|
||||||
level::level_enum flush_level_ = level::off;
|
level::level_enum flush_level_ = level::off;
|
||||||
log_err_handler err_handler_;
|
log_err_handler err_handler_;
|
||||||
std::shared_ptr<thread_pool> tp_;
|
std::shared_ptr<thread_pool> tp_;
|
||||||
std::unique_ptr<periodic_worker> periodic_flusher_;
|
std::unique_ptr<periodic_worker> periodic_flusher_;
|
||||||
|
std::shared_ptr<logger> default_logger_;
|
||||||
|
bool automatic_registration_ = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace details
|
} // namespace details
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "spdlog/details/fmt_helper.h"
|
||||||
#include "spdlog/details/log_msg.h"
|
#include "spdlog/details/log_msg.h"
|
||||||
#include "spdlog/details/mpmc_blocking_q.h"
|
#include "spdlog/details/mpmc_blocking_q.h"
|
||||||
#include "spdlog/details/os.h"
|
#include "spdlog/details/os.h"
|
||||||
@ -32,6 +33,7 @@ struct async_msg
|
|||||||
fmt::basic_memory_buffer<char, 176> raw;
|
fmt::basic_memory_buffer<char, 176> raw;
|
||||||
|
|
||||||
size_t msg_id;
|
size_t msg_id;
|
||||||
|
source_loc source;
|
||||||
async_logger_ptr worker_ptr;
|
async_logger_ptr worker_ptr;
|
||||||
|
|
||||||
async_msg() = default;
|
async_msg() = default;
|
||||||
@ -48,6 +50,7 @@ struct async_msg
|
|||||||
thread_id(other.thread_id),
|
thread_id(other.thread_id),
|
||||||
raw(move(other.raw)),
|
raw(move(other.raw)),
|
||||||
msg_id(other.msg_id),
|
msg_id(other.msg_id),
|
||||||
|
source(other.source),
|
||||||
worker_ptr(std::move(other.worker_ptr))
|
worker_ptr(std::move(other.worker_ptr))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -60,6 +63,7 @@ struct async_msg
|
|||||||
thread_id = other.thread_id;
|
thread_id = other.thread_id;
|
||||||
raw = std::move(other.raw);
|
raw = std::move(other.raw);
|
||||||
msg_id = other.msg_id;
|
msg_id = other.msg_id;
|
||||||
|
source = other.source;
|
||||||
worker_ptr = std::move(other.worker_ptr);
|
worker_ptr = std::move(other.worker_ptr);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
@ -69,38 +73,45 @@ struct async_msg
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// construct from log_msg with given type
|
// construct from log_msg with given type
|
||||||
async_msg(async_logger_ptr &&worker, async_msg_type the_type, details::log_msg &&m)
|
async_msg(async_logger_ptr &&worker, async_msg_type the_type, details::log_msg &m)
|
||||||
: msg_type(the_type)
|
: msg_type(the_type)
|
||||||
, level(m.level)
|
, level(m.level)
|
||||||
, 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))
|
, source(m.source)
|
||||||
|
, worker_ptr(std::move(worker))
|
||||||
{
|
{
|
||||||
fmt_helper::append_buf(m.raw, raw);
|
fmt_helper::append_string_view(m.payload, 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())
|
: msg_type(the_type)
|
||||||
|
, level(level::off)
|
||||||
|
, time()
|
||||||
|
, thread_id(0)
|
||||||
|
, msg_id(0)
|
||||||
|
, source()
|
||||||
|
, worker_ptr(std::move(worker))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
explicit async_msg(async_msg_type the_type)
|
explicit async_msg(async_msg_type the_type)
|
||||||
: async_msg(nullptr, the_type, details::log_msg())
|
: async_msg(nullptr, the_type)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy into log_msg
|
// copy into log_msg
|
||||||
void to_log_msg(log_msg &msg)
|
log_msg to_log_msg()
|
||||||
{
|
{
|
||||||
msg.logger_name = &worker_ptr->name();
|
log_msg msg(&worker_ptr->name(), level, string_view_t(raw.data(), raw.size()));
|
||||||
msg.level = level;
|
|
||||||
msg.time = time;
|
msg.time = time;
|
||||||
msg.thread_id = thread_id;
|
msg.thread_id = thread_id;
|
||||||
fmt_helper::append_buf(raw, msg.raw);
|
|
||||||
msg.msg_id = msg_id;
|
msg.msg_id = msg_id;
|
||||||
|
msg.source = source;
|
||||||
msg.color_range_start = 0;
|
msg.color_range_start = 0;
|
||||||
msg.color_range_end = 0;
|
msg.color_range_end = 0;
|
||||||
|
return msg;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -146,9 +157,12 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void post_log(async_logger_ptr &&worker_ptr, details::log_msg &&msg, async_overflow_policy overflow_policy)
|
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)
|
||||||
{
|
{
|
||||||
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, msg);
|
||||||
post_async_msg_(std::move(async_m), overflow_policy);
|
post_async_msg_(std::move(async_m), overflow_policy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,8 +214,7 @@ private:
|
|||||||
{
|
{
|
||||||
case async_msg_type::log:
|
case async_msg_type::log:
|
||||||
{
|
{
|
||||||
log_msg msg;
|
auto msg = incoming_async_msg.to_log_msg();
|
||||||
incoming_async_msg.to_log_msg(msg);
|
|
||||||
incoming_async_msg.worker_ptr->backend_log_(msg);
|
incoming_async_msg.worker_ptr->backend_log_(msg);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
172
include/spdlog/fmt/bin_to_hex.h
Normal file
172
include/spdlog/fmt/bin_to_hex.h
Normal 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())
|
||||||
|
{
|
||||||
|
SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
|
||||||
|
SPDLOG_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
|
452
include/spdlog/fmt/bundled/chrono.h
Normal file
452
include/spdlog/fmt/bundled/chrono.h
Normal file
@ -0,0 +1,452 @@
|
|||||||
|
// Formatting library for C++ - chrono support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_CHRONO_H_
|
||||||
|
#define FMT_CHRONO_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
#include "locale.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <ctime>
|
||||||
|
#include <locale>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
namespace internal{
|
||||||
|
|
||||||
|
enum class numeric_system {
|
||||||
|
standard,
|
||||||
|
// Alternative numeric system, e.g. 十二 instead of 12 in ja_JP locale.
|
||||||
|
alternative
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parses a put_time-like format string and invokes handler actions.
|
||||||
|
template <typename Char, typename Handler>
|
||||||
|
FMT_CONSTEXPR const Char *parse_chrono_format(
|
||||||
|
const Char *begin, const Char *end, Handler &&handler) {
|
||||||
|
auto ptr = begin;
|
||||||
|
while (ptr != end) {
|
||||||
|
auto c = *ptr;
|
||||||
|
if (c == '}') break;
|
||||||
|
if (c != '%') {
|
||||||
|
++ptr;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (begin != ptr)
|
||||||
|
handler.on_text(begin, ptr);
|
||||||
|
++ptr; // consume '%'
|
||||||
|
if (ptr == end)
|
||||||
|
throw format_error("invalid format");
|
||||||
|
c = *ptr++;
|
||||||
|
switch (c) {
|
||||||
|
case '%':
|
||||||
|
handler.on_text(ptr - 1, ptr);
|
||||||
|
break;
|
||||||
|
case 'n': {
|
||||||
|
const char newline[] = "\n";
|
||||||
|
handler.on_text(newline, newline + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 't': {
|
||||||
|
const char tab[] = "\t";
|
||||||
|
handler.on_text(tab, tab + 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Day of the week:
|
||||||
|
case 'a':
|
||||||
|
handler.on_abbr_weekday();
|
||||||
|
break;
|
||||||
|
case 'A':
|
||||||
|
handler.on_full_weekday();
|
||||||
|
break;
|
||||||
|
case 'w':
|
||||||
|
handler.on_dec0_weekday(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
handler.on_dec1_weekday(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
// Month:
|
||||||
|
case 'b':
|
||||||
|
handler.on_abbr_month();
|
||||||
|
break;
|
||||||
|
case 'B':
|
||||||
|
handler.on_full_month();
|
||||||
|
break;
|
||||||
|
// Hour, minute, second:
|
||||||
|
case 'H':
|
||||||
|
handler.on_24_hour(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
handler.on_12_hour(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
handler.on_minute(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
handler.on_second(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
// Other:
|
||||||
|
case 'c':
|
||||||
|
handler.on_datetime(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
handler.on_loc_date(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
handler.on_loc_time(numeric_system::standard);
|
||||||
|
break;
|
||||||
|
case 'D':
|
||||||
|
handler.on_us_date();
|
||||||
|
break;
|
||||||
|
case 'F':
|
||||||
|
handler.on_iso_date();
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
handler.on_12_hour_time();
|
||||||
|
break;
|
||||||
|
case 'R':
|
||||||
|
handler.on_24_hour_time();
|
||||||
|
break;
|
||||||
|
case 'T':
|
||||||
|
handler.on_iso_time();
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
handler.on_am_pm();
|
||||||
|
break;
|
||||||
|
case 'z':
|
||||||
|
handler.on_utc_offset();
|
||||||
|
break;
|
||||||
|
case 'Z':
|
||||||
|
handler.on_tz_name();
|
||||||
|
break;
|
||||||
|
// Alternative representation:
|
||||||
|
case 'E': {
|
||||||
|
if (ptr == end)
|
||||||
|
throw format_error("invalid format");
|
||||||
|
c = *ptr++;
|
||||||
|
switch (c) {
|
||||||
|
case 'c':
|
||||||
|
handler.on_datetime(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'x':
|
||||||
|
handler.on_loc_date(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'X':
|
||||||
|
handler.on_loc_time(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw format_error("invalid format");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'O':
|
||||||
|
if (ptr == end)
|
||||||
|
throw format_error("invalid format");
|
||||||
|
c = *ptr++;
|
||||||
|
switch (c) {
|
||||||
|
case 'w':
|
||||||
|
handler.on_dec0_weekday(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'u':
|
||||||
|
handler.on_dec1_weekday(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'H':
|
||||||
|
handler.on_24_hour(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'I':
|
||||||
|
handler.on_12_hour(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'M':
|
||||||
|
handler.on_minute(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
handler.on_second(numeric_system::alternative);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw format_error("invalid format");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw format_error("invalid format");
|
||||||
|
}
|
||||||
|
begin = ptr;
|
||||||
|
}
|
||||||
|
if (begin != ptr)
|
||||||
|
handler.on_text(begin, ptr);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct chrono_format_checker {
|
||||||
|
void report_no_date() { throw format_error("no date"); }
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
void on_text(const Char *, const Char *) {}
|
||||||
|
void on_abbr_weekday() { report_no_date(); }
|
||||||
|
void on_full_weekday() { report_no_date(); }
|
||||||
|
void on_dec0_weekday(numeric_system) { report_no_date(); }
|
||||||
|
void on_dec1_weekday(numeric_system) { report_no_date(); }
|
||||||
|
void on_abbr_month() { report_no_date(); }
|
||||||
|
void on_full_month() { report_no_date(); }
|
||||||
|
void on_24_hour(numeric_system) {}
|
||||||
|
void on_12_hour(numeric_system) {}
|
||||||
|
void on_minute(numeric_system) {}
|
||||||
|
void on_second(numeric_system) {}
|
||||||
|
void on_datetime(numeric_system) { report_no_date(); }
|
||||||
|
void on_loc_date(numeric_system) { report_no_date(); }
|
||||||
|
void on_loc_time(numeric_system) { report_no_date(); }
|
||||||
|
void on_us_date() { report_no_date(); }
|
||||||
|
void on_iso_date() { report_no_date(); }
|
||||||
|
void on_12_hour_time() {}
|
||||||
|
void on_24_hour_time() {}
|
||||||
|
void on_iso_time() {}
|
||||||
|
void on_am_pm() {}
|
||||||
|
void on_utc_offset() { report_no_date(); }
|
||||||
|
void on_tz_name() { report_no_date(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Int>
|
||||||
|
inline int to_int(Int value) {
|
||||||
|
FMT_ASSERT(value >= (std::numeric_limits<int>::min)() &&
|
||||||
|
value <= (std::numeric_limits<int>::max)(), "invalid value");
|
||||||
|
return static_cast<int>(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext, typename OutputIt>
|
||||||
|
struct chrono_formatter {
|
||||||
|
FormatContext &context;
|
||||||
|
OutputIt out;
|
||||||
|
std::chrono::seconds s;
|
||||||
|
std::chrono::milliseconds ms;
|
||||||
|
|
||||||
|
typedef typename FormatContext::char_type char_type;
|
||||||
|
|
||||||
|
explicit chrono_formatter(FormatContext &ctx, OutputIt o)
|
||||||
|
: context(ctx), out(o) {}
|
||||||
|
|
||||||
|
int hour() const { return to_int((s.count() / 3600) % 24); }
|
||||||
|
|
||||||
|
int hour12() const {
|
||||||
|
auto hour = to_int((s.count() / 3600) % 12);
|
||||||
|
return hour > 0 ? hour : 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
int minute() const { return to_int((s.count() / 60) % 60); }
|
||||||
|
int second() const { return to_int(s.count() % 60); }
|
||||||
|
|
||||||
|
std::tm time() const {
|
||||||
|
auto time = std::tm();
|
||||||
|
time.tm_hour = hour();
|
||||||
|
time.tm_min = minute();
|
||||||
|
time.tm_sec = second();
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(int value, int width) {
|
||||||
|
typedef typename int_traits<int>::main_type main_type;
|
||||||
|
main_type n = to_unsigned(value);
|
||||||
|
int num_digits = internal::count_digits(n);
|
||||||
|
if (width > num_digits)
|
||||||
|
out = std::fill_n(out, width - num_digits, '0');
|
||||||
|
out = format_decimal<char_type>(out, n, num_digits);
|
||||||
|
}
|
||||||
|
|
||||||
|
void format_localized(const tm &time, const char *format) {
|
||||||
|
auto locale = context.locale().template get<std::locale>();
|
||||||
|
auto &facet = std::use_facet<std::time_put<char_type>>(locale);
|
||||||
|
std::basic_ostringstream<char_type> os;
|
||||||
|
os.imbue(locale);
|
||||||
|
facet.put(os, os, ' ', &time, format, format + std::strlen(format));
|
||||||
|
auto str = os.str();
|
||||||
|
std::copy(str.begin(), str.end(), out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_text(const char_type *begin, const char_type *end) {
|
||||||
|
std::copy(begin, end, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are not implemented because durations don't have date information.
|
||||||
|
void on_abbr_weekday() {}
|
||||||
|
void on_full_weekday() {}
|
||||||
|
void on_dec0_weekday(numeric_system) {}
|
||||||
|
void on_dec1_weekday(numeric_system) {}
|
||||||
|
void on_abbr_month() {}
|
||||||
|
void on_full_month() {}
|
||||||
|
void on_datetime(numeric_system) {}
|
||||||
|
void on_loc_date(numeric_system) {}
|
||||||
|
void on_loc_time(numeric_system) {}
|
||||||
|
void on_us_date() {}
|
||||||
|
void on_iso_date() {}
|
||||||
|
void on_utc_offset() {}
|
||||||
|
void on_tz_name() {}
|
||||||
|
|
||||||
|
void on_24_hour(numeric_system ns) {
|
||||||
|
if (ns == numeric_system::standard)
|
||||||
|
return write(hour(), 2);
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_hour = hour();
|
||||||
|
format_localized(time, "%OH");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_12_hour(numeric_system ns) {
|
||||||
|
if (ns == numeric_system::standard)
|
||||||
|
return write(hour12(), 2);
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_hour = hour();
|
||||||
|
format_localized(time, "%OI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_minute(numeric_system ns) {
|
||||||
|
if (ns == numeric_system::standard)
|
||||||
|
return write(minute(), 2);
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_min = minute();
|
||||||
|
format_localized(time, "%OM");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_second(numeric_system ns) {
|
||||||
|
if (ns == numeric_system::standard) {
|
||||||
|
write(second(), 2);
|
||||||
|
if (ms != std::chrono::milliseconds(0)) {
|
||||||
|
*out++ = '.';
|
||||||
|
write(to_int(ms.count()), 3);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto time = tm();
|
||||||
|
time.tm_sec = second();
|
||||||
|
format_localized(time, "%OS");
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_12_hour_time() { format_localized(time(), "%r"); }
|
||||||
|
|
||||||
|
void on_24_hour_time() {
|
||||||
|
write(hour(), 2);
|
||||||
|
*out++ = ':';
|
||||||
|
write(minute(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_iso_time() {
|
||||||
|
on_24_hour_time();
|
||||||
|
*out++ = ':';
|
||||||
|
write(second(), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_am_pm() { format_localized(time(), "%p"); }
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <typename Period> FMT_CONSTEXPR const char *get_units() {
|
||||||
|
return FMT_NULL;
|
||||||
|
}
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::atto>() { return "as"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::femto>() { return "fs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::pico>() { return "ps"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::nano>() { return "ns"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::micro>() { return "µs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::milli>() { return "ms"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::centi>() { return "cs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::deci>() { return "ds"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::ratio<1>>() { return "s"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::deca>() { return "das"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::hecto>() { return "hs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::kilo>() { return "ks"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::mega>() { return "Ms"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::giga>() { return "Gs"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::tera>() { return "Ts"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::peta>() { return "Ps"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::exa>() { return "Es"; }
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::ratio<60>>() {
|
||||||
|
return "m";
|
||||||
|
}
|
||||||
|
template <> FMT_CONSTEXPR const char *get_units<std::ratio<3600>>() {
|
||||||
|
return "h";
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Rep, typename Period, typename Char>
|
||||||
|
struct formatter<std::chrono::duration<Rep, Period>, Char> {
|
||||||
|
private:
|
||||||
|
align_spec spec;
|
||||||
|
internal::arg_ref<Char> width_ref;
|
||||||
|
mutable basic_string_view<Char> format_str;
|
||||||
|
typedef std::chrono::duration<Rep, Period> duration;
|
||||||
|
|
||||||
|
struct spec_handler {
|
||||||
|
formatter &f;
|
||||||
|
basic_parse_context<Char> &context;
|
||||||
|
|
||||||
|
typedef internal::arg_ref<Char> arg_ref_type;
|
||||||
|
|
||||||
|
template <typename Id>
|
||||||
|
FMT_CONSTEXPR arg_ref_type make_arg_ref(Id arg_id) {
|
||||||
|
context.check_arg_id(arg_id);
|
||||||
|
return arg_ref_type(arg_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR arg_ref_type make_arg_ref(internal::auto_id) {
|
||||||
|
return arg_ref_type(context.next_arg_id());
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_error(const char *msg) { throw format_error(msg); }
|
||||||
|
void on_fill(Char fill) { f.spec.fill_ = fill; }
|
||||||
|
void on_align(alignment align) { f.spec.align_ = align; }
|
||||||
|
void on_width(unsigned width) { f.spec.width_ = width; }
|
||||||
|
|
||||||
|
template <typename Id>
|
||||||
|
void on_dynamic_width(Id arg_id) {
|
||||||
|
f.width_ref = make_arg_ref(arg_id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
formatter() : spec() {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR auto parse(basic_parse_context<Char> &ctx)
|
||||||
|
-> decltype(ctx.begin()) {
|
||||||
|
auto begin = ctx.begin(), end = ctx.end();
|
||||||
|
if (begin == end) return begin;
|
||||||
|
spec_handler handler{*this, ctx};
|
||||||
|
begin = internal::parse_align(begin, end, handler);
|
||||||
|
if (begin == end) return begin;
|
||||||
|
begin = internal::parse_width(begin, end, handler);
|
||||||
|
end = parse_chrono_format(begin, end, internal::chrono_format_checker());
|
||||||
|
format_str = basic_string_view<Char>(&*begin, internal::to_unsigned(end - begin));
|
||||||
|
return end;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const duration &d, FormatContext &ctx)
|
||||||
|
-> decltype(ctx.out()) {
|
||||||
|
auto begin = format_str.begin(), end = format_str.end();
|
||||||
|
memory_buffer buf;
|
||||||
|
typedef output_range<decltype(ctx.out()), Char> range;
|
||||||
|
basic_writer<range> w(range(ctx.out()));
|
||||||
|
if (begin == end || *begin == '}') {
|
||||||
|
if (const char *unit = get_units<Period>())
|
||||||
|
format_to(buf, "{}{}", d.count(), unit);
|
||||||
|
else if (Period::den == 1)
|
||||||
|
format_to(buf, "{}[{}]s", d.count(), Period::num);
|
||||||
|
else
|
||||||
|
format_to(buf, "{}[{}/{}]s", d.count(), Period::num, Period::den);
|
||||||
|
internal::handle_dynamic_spec<internal::width_checker>(
|
||||||
|
spec.width_, width_ref, ctx);
|
||||||
|
} else {
|
||||||
|
auto out = std::back_inserter(buf);
|
||||||
|
internal::chrono_formatter<FormatContext, decltype(out)> f(ctx, out);
|
||||||
|
f.s = std::chrono::duration_cast<std::chrono::seconds>(d);
|
||||||
|
f.ms = std::chrono::duration_cast<std::chrono::milliseconds>(d - f.s);
|
||||||
|
parse_chrono_format(begin, end, f);
|
||||||
|
}
|
||||||
|
w.write(buf.data(), buf.size(), spec);
|
||||||
|
return w.out();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_CHRONO_H_
|
577
include/spdlog/fmt/bundled/color.h
Normal file
577
include/spdlog/fmt/bundled/color.h
Normal file
@ -0,0 +1,577 @@
|
|||||||
|
// Formatting library for C++ - color support
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
|
||||||
|
// All rights reserved.
|
||||||
|
//
|
||||||
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_COLOR_H_
|
||||||
|
#define FMT_COLOR_H_
|
||||||
|
|
||||||
|
#include "format.h"
|
||||||
|
|
||||||
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
#ifdef FMT_DEPRECATED_COLORS
|
||||||
|
|
||||||
|
// color and (v)print_colored are deprecated.
|
||||||
|
enum color { black, red, green, yellow, blue, magenta, cyan, white };
|
||||||
|
FMT_API void vprint_colored(color c, string_view format, format_args args);
|
||||||
|
FMT_API void vprint_colored(color c, wstring_view format, wformat_args args);
|
||||||
|
template <typename... Args>
|
||||||
|
inline void print_colored(color c, string_view format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
vprint_colored(c, format_str, make_format_args(args...));
|
||||||
|
}
|
||||||
|
template <typename... Args>
|
||||||
|
inline void print_colored(color c, wstring_view format_str,
|
||||||
|
const Args & ... args) {
|
||||||
|
vprint_colored(c, format_str, make_format_args<wformat_context>(args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void vprint_colored(color c, string_view format, format_args args) {
|
||||||
|
char escape[] = "\x1b[30m";
|
||||||
|
escape[3] = static_cast<char>('0' + c);
|
||||||
|
std::fputs(escape, stdout);
|
||||||
|
vprint(format, args);
|
||||||
|
std::fputs(internal::data::RESET_COLOR, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void vprint_colored(color c, wstring_view format, wformat_args args) {
|
||||||
|
wchar_t escape[] = L"\x1b[30m";
|
||||||
|
escape[3] = static_cast<wchar_t>('0' + c);
|
||||||
|
std::fputws(escape, stdout);
|
||||||
|
vprint(format, args);
|
||||||
|
std::fputws(internal::data::WRESET_COLOR, stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
enum class color : uint32_t {
|
||||||
|
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||||
|
antique_white = 0xFAEBD7, // rgb(250,235,215)
|
||||||
|
aqua = 0x00FFFF, // rgb(0,255,255)
|
||||||
|
aquamarine = 0x7FFFD4, // rgb(127,255,212)
|
||||||
|
azure = 0xF0FFFF, // rgb(240,255,255)
|
||||||
|
beige = 0xF5F5DC, // rgb(245,245,220)
|
||||||
|
bisque = 0xFFE4C4, // rgb(255,228,196)
|
||||||
|
black = 0x000000, // rgb(0,0,0)
|
||||||
|
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
|
||||||
|
blue = 0x0000FF, // rgb(0,0,255)
|
||||||
|
blue_violet = 0x8A2BE2, // rgb(138,43,226)
|
||||||
|
brown = 0xA52A2A, // rgb(165,42,42)
|
||||||
|
burly_wood = 0xDEB887, // rgb(222,184,135)
|
||||||
|
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
|
||||||
|
chartreuse = 0x7FFF00, // rgb(127,255,0)
|
||||||
|
chocolate = 0xD2691E, // rgb(210,105,30)
|
||||||
|
coral = 0xFF7F50, // rgb(255,127,80)
|
||||||
|
cornflower_blue = 0x6495ED, // rgb(100,149,237)
|
||||||
|
cornsilk = 0xFFF8DC, // rgb(255,248,220)
|
||||||
|
crimson = 0xDC143C, // rgb(220,20,60)
|
||||||
|
cyan = 0x00FFFF, // rgb(0,255,255)
|
||||||
|
dark_blue = 0x00008B, // rgb(0,0,139)
|
||||||
|
dark_cyan = 0x008B8B, // rgb(0,139,139)
|
||||||
|
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
|
||||||
|
dark_gray = 0xA9A9A9, // rgb(169,169,169)
|
||||||
|
dark_green = 0x006400, // rgb(0,100,0)
|
||||||
|
dark_khaki = 0xBDB76B, // rgb(189,183,107)
|
||||||
|
dark_magenta = 0x8B008B, // rgb(139,0,139)
|
||||||
|
dark_olive_green = 0x556B2F, // rgb(85,107,47)
|
||||||
|
dark_orange = 0xFF8C00, // rgb(255,140,0)
|
||||||
|
dark_orchid = 0x9932CC, // rgb(153,50,204)
|
||||||
|
dark_red = 0x8B0000, // rgb(139,0,0)
|
||||||
|
dark_salmon = 0xE9967A, // rgb(233,150,122)
|
||||||
|
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
|
||||||
|
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
|
||||||
|
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
|
||||||
|
dark_turquoise = 0x00CED1, // rgb(0,206,209)
|
||||||
|
dark_violet = 0x9400D3, // rgb(148,0,211)
|
||||||
|
deep_pink = 0xFF1493, // rgb(255,20,147)
|
||||||
|
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
|
||||||
|
dim_gray = 0x696969, // rgb(105,105,105)
|
||||||
|
dodger_blue = 0x1E90FF, // rgb(30,144,255)
|
||||||
|
fire_brick = 0xB22222, // rgb(178,34,34)
|
||||||
|
floral_white = 0xFFFAF0, // rgb(255,250,240)
|
||||||
|
forest_green = 0x228B22, // rgb(34,139,34)
|
||||||
|
fuchsia = 0xFF00FF, // rgb(255,0,255)
|
||||||
|
gainsboro = 0xDCDCDC, // rgb(220,220,220)
|
||||||
|
ghost_white = 0xF8F8FF, // rgb(248,248,255)
|
||||||
|
gold = 0xFFD700, // rgb(255,215,0)
|
||||||
|
golden_rod = 0xDAA520, // rgb(218,165,32)
|
||||||
|
gray = 0x808080, // rgb(128,128,128)
|
||||||
|
green = 0x008000, // rgb(0,128,0)
|
||||||
|
green_yellow = 0xADFF2F, // rgb(173,255,47)
|
||||||
|
honey_dew = 0xF0FFF0, // rgb(240,255,240)
|
||||||
|
hot_pink = 0xFF69B4, // rgb(255,105,180)
|
||||||
|
indian_red = 0xCD5C5C, // rgb(205,92,92)
|
||||||
|
indigo = 0x4B0082, // rgb(75,0,130)
|
||||||
|
ivory = 0xFFFFF0, // rgb(255,255,240)
|
||||||
|
khaki = 0xF0E68C, // rgb(240,230,140)
|
||||||
|
lavender = 0xE6E6FA, // rgb(230,230,250)
|
||||||
|
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
|
||||||
|
lawn_green = 0x7CFC00, // rgb(124,252,0)
|
||||||
|
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
|
||||||
|
light_blue = 0xADD8E6, // rgb(173,216,230)
|
||||||
|
light_coral = 0xF08080, // rgb(240,128,128)
|
||||||
|
light_cyan = 0xE0FFFF, // rgb(224,255,255)
|
||||||
|
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
|
||||||
|
light_gray = 0xD3D3D3, // rgb(211,211,211)
|
||||||
|
light_green = 0x90EE90, // rgb(144,238,144)
|
||||||
|
light_pink = 0xFFB6C1, // rgb(255,182,193)
|
||||||
|
light_salmon = 0xFFA07A, // rgb(255,160,122)
|
||||||
|
light_sea_green = 0x20B2AA, // rgb(32,178,170)
|
||||||
|
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
|
||||||
|
light_slate_gray = 0x778899, // rgb(119,136,153)
|
||||||
|
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
|
||||||
|
light_yellow = 0xFFFFE0, // rgb(255,255,224)
|
||||||
|
lime = 0x00FF00, // rgb(0,255,0)
|
||||||
|
lime_green = 0x32CD32, // rgb(50,205,50)
|
||||||
|
linen = 0xFAF0E6, // rgb(250,240,230)
|
||||||
|
magenta = 0xFF00FF, // rgb(255,0,255)
|
||||||
|
maroon = 0x800000, // rgb(128,0,0)
|
||||||
|
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
|
||||||
|
medium_blue = 0x0000CD, // rgb(0,0,205)
|
||||||
|
medium_orchid = 0xBA55D3, // rgb(186,85,211)
|
||||||
|
medium_purple = 0x9370DB, // rgb(147,112,219)
|
||||||
|
medium_sea_green = 0x3CB371, // rgb(60,179,113)
|
||||||
|
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
|
||||||
|
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
|
||||||
|
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
|
||||||
|
medium_violet_red = 0xC71585, // rgb(199,21,133)
|
||||||
|
midnight_blue = 0x191970, // rgb(25,25,112)
|
||||||
|
mint_cream = 0xF5FFFA, // rgb(245,255,250)
|
||||||
|
misty_rose = 0xFFE4E1, // rgb(255,228,225)
|
||||||
|
moccasin = 0xFFE4B5, // rgb(255,228,181)
|
||||||
|
navajo_white = 0xFFDEAD, // rgb(255,222,173)
|
||||||
|
navy = 0x000080, // rgb(0,0,128)
|
||||||
|
old_lace = 0xFDF5E6, // rgb(253,245,230)
|
||||||
|
olive = 0x808000, // rgb(128,128,0)
|
||||||
|
olive_drab = 0x6B8E23, // rgb(107,142,35)
|
||||||
|
orange = 0xFFA500, // rgb(255,165,0)
|
||||||
|
orange_red = 0xFF4500, // rgb(255,69,0)
|
||||||
|
orchid = 0xDA70D6, // rgb(218,112,214)
|
||||||
|
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
|
||||||
|
pale_green = 0x98FB98, // rgb(152,251,152)
|
||||||
|
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
|
||||||
|
pale_violet_red = 0xDB7093, // rgb(219,112,147)
|
||||||
|
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
|
||||||
|
peach_puff = 0xFFDAB9, // rgb(255,218,185)
|
||||||
|
peru = 0xCD853F, // rgb(205,133,63)
|
||||||
|
pink = 0xFFC0CB, // rgb(255,192,203)
|
||||||
|
plum = 0xDDA0DD, // rgb(221,160,221)
|
||||||
|
powder_blue = 0xB0E0E6, // rgb(176,224,230)
|
||||||
|
purple = 0x800080, // rgb(128,0,128)
|
||||||
|
rebecca_purple = 0x663399, // rgb(102,51,153)
|
||||||
|
red = 0xFF0000, // rgb(255,0,0)
|
||||||
|
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
|
||||||
|
royal_blue = 0x4169E1, // rgb(65,105,225)
|
||||||
|
saddle_brown = 0x8B4513, // rgb(139,69,19)
|
||||||
|
salmon = 0xFA8072, // rgb(250,128,114)
|
||||||
|
sandy_brown = 0xF4A460, // rgb(244,164,96)
|
||||||
|
sea_green = 0x2E8B57, // rgb(46,139,87)
|
||||||
|
sea_shell = 0xFFF5EE, // rgb(255,245,238)
|
||||||
|
sienna = 0xA0522D, // rgb(160,82,45)
|
||||||
|
silver = 0xC0C0C0, // rgb(192,192,192)
|
||||||
|
sky_blue = 0x87CEEB, // rgb(135,206,235)
|
||||||
|
slate_blue = 0x6A5ACD, // rgb(106,90,205)
|
||||||
|
slate_gray = 0x708090, // rgb(112,128,144)
|
||||||
|
snow = 0xFFFAFA, // rgb(255,250,250)
|
||||||
|
spring_green = 0x00FF7F, // rgb(0,255,127)
|
||||||
|
steel_blue = 0x4682B4, // rgb(70,130,180)
|
||||||
|
tan = 0xD2B48C, // rgb(210,180,140)
|
||||||
|
teal = 0x008080, // rgb(0,128,128)
|
||||||
|
thistle = 0xD8BFD8, // rgb(216,191,216)
|
||||||
|
tomato = 0xFF6347, // rgb(255,99,71)
|
||||||
|
turquoise = 0x40E0D0, // rgb(64,224,208)
|
||||||
|
violet = 0xEE82EE, // rgb(238,130,238)
|
||||||
|
wheat = 0xF5DEB3, // rgb(245,222,179)
|
||||||
|
white = 0xFFFFFF, // rgb(255,255,255)
|
||||||
|
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
||||||
|
yellow = 0xFFFF00, // rgb(255,255,0)
|
||||||
|
yellow_green = 0x9ACD32 // rgb(154,205,50)
|
||||||
|
}; // enum class color
|
||||||
|
|
||||||
|
enum class terminal_color : uint8_t {
|
||||||
|
black = 30,
|
||||||
|
red,
|
||||||
|
green,
|
||||||
|
yellow,
|
||||||
|
blue,
|
||||||
|
magenta,
|
||||||
|
cyan,
|
||||||
|
white,
|
||||||
|
bright_black = 90,
|
||||||
|
bright_red,
|
||||||
|
bright_green,
|
||||||
|
bright_yellow,
|
||||||
|
bright_blue,
|
||||||
|
bright_magenta,
|
||||||
|
bright_cyan,
|
||||||
|
bright_white
|
||||||
|
}; // enum class terminal_color
|
||||||
|
|
||||||
|
enum class emphasis : uint8_t {
|
||||||
|
bold = 1,
|
||||||
|
italic = 1 << 1,
|
||||||
|
underline = 1 << 2,
|
||||||
|
strikethrough = 1 << 3
|
||||||
|
}; // enum class emphasis
|
||||||
|
|
||||||
|
// rgb is a struct for red, green and blue colors.
|
||||||
|
// We use rgb as name because some editors will show it as color direct in the
|
||||||
|
// editor.
|
||||||
|
struct rgb {
|
||||||
|
FMT_CONSTEXPR_DECL rgb() : r(0), g(0), b(0) {}
|
||||||
|
FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_)
|
||||||
|
: r(r_), g(g_), b(b_) {}
|
||||||
|
FMT_CONSTEXPR_DECL rgb(uint32_t hex)
|
||||||
|
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b((hex) & 0xFF) {}
|
||||||
|
FMT_CONSTEXPR_DECL rgb(color hex)
|
||||||
|
: r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF),
|
||||||
|
b(uint32_t(hex) & 0xFF) {}
|
||||||
|
uint8_t r;
|
||||||
|
uint8_t g;
|
||||||
|
uint8_t b;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
// color is a struct of either a rgb color or a terminal color.
|
||||||
|
struct color_type {
|
||||||
|
FMT_CONSTEXPR color_type() FMT_NOEXCEPT
|
||||||
|
: is_rgb(), value{} {}
|
||||||
|
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT
|
||||||
|
: is_rgb(true), value{} {
|
||||||
|
value.rgb_color = static_cast<uint32_t>(rgb_color);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT
|
||||||
|
: is_rgb(true), value{} {
|
||||||
|
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16)
|
||||||
|
| (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT
|
||||||
|
: is_rgb(), value{} {
|
||||||
|
value.term_color = static_cast<uint8_t>(term_color);
|
||||||
|
}
|
||||||
|
bool is_rgb;
|
||||||
|
union color_union {
|
||||||
|
uint8_t term_color;
|
||||||
|
uint32_t rgb_color;
|
||||||
|
} value;
|
||||||
|
};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
// Experimental text formatting support.
|
||||||
|
class text_style {
|
||||||
|
public:
|
||||||
|
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
|
||||||
|
: set_foreground_color(), set_background_color(), ems(em) {}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style &operator|=(const text_style &rhs) {
|
||||||
|
if (!set_foreground_color) {
|
||||||
|
set_foreground_color = rhs.set_foreground_color;
|
||||||
|
foreground_color = rhs.foreground_color;
|
||||||
|
} else if (rhs.set_foreground_color) {
|
||||||
|
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||||
|
throw format_error("can't OR a terminal color");
|
||||||
|
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_background_color) {
|
||||||
|
set_background_color = rhs.set_background_color;
|
||||||
|
background_color = rhs.background_color;
|
||||||
|
} else if (rhs.set_background_color) {
|
||||||
|
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||||
|
throw format_error("can't OR a terminal color");
|
||||||
|
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
|
||||||
|
static_cast<uint8_t>(rhs.ems));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR
|
||||||
|
text_style operator|(text_style lhs, const text_style &rhs) {
|
||||||
|
return lhs |= rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style &operator&=(const text_style &rhs) {
|
||||||
|
if (!set_foreground_color) {
|
||||||
|
set_foreground_color = rhs.set_foreground_color;
|
||||||
|
foreground_color = rhs.foreground_color;
|
||||||
|
} else if (rhs.set_foreground_color) {
|
||||||
|
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||||
|
throw format_error("can't AND a terminal color");
|
||||||
|
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_background_color) {
|
||||||
|
set_background_color = rhs.set_background_color;
|
||||||
|
background_color = rhs.background_color;
|
||||||
|
} else if (rhs.set_background_color) {
|
||||||
|
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||||
|
throw format_error("can't AND a terminal color");
|
||||||
|
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
|
||||||
|
static_cast<uint8_t>(rhs.ems));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR
|
||||||
|
text_style operator&(text_style lhs, const text_style &rhs) {
|
||||||
|
return lhs &= rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
|
||||||
|
return set_foreground_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR bool has_background() const FMT_NOEXCEPT {
|
||||||
|
return set_background_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
|
||||||
|
return static_cast<uint8_t>(ems) != 0;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
|
||||||
|
assert(has_foreground() && "no foreground specified for this style");
|
||||||
|
return foreground_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
|
||||||
|
assert(has_background() && "no background specified for this style");
|
||||||
|
return background_color;
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
|
||||||
|
assert(has_emphasis() && "no emphasis specified for this style");
|
||||||
|
return ems;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||||
|
internal::color_type text_color) FMT_NOEXCEPT
|
||||||
|
: set_foreground_color(),
|
||||||
|
set_background_color(),
|
||||||
|
ems() {
|
||||||
|
if (is_foreground) {
|
||||||
|
foreground_color = text_color;
|
||||||
|
set_foreground_color = true;
|
||||||
|
} else {
|
||||||
|
background_color = text_color;
|
||||||
|
set_background_color = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
internal::color_type foreground_color;
|
||||||
|
internal::color_type background_color;
|
||||||
|
bool set_foreground_color;
|
||||||
|
bool set_background_color;
|
||||||
|
emphasis ems;
|
||||||
|
};
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
|
||||||
|
return text_style(/*is_foreground=*/true, foreground);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
|
||||||
|
return text_style(/*is_foreground=*/false, background);
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
|
||||||
|
return text_style(lhs) | rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct ansi_color_escape {
|
||||||
|
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
|
||||||
|
const char * esc) FMT_NOEXCEPT {
|
||||||
|
// If we have a terminal color, we need to output another escape code
|
||||||
|
// sequence.
|
||||||
|
if (!text_color.is_rgb) {
|
||||||
|
bool is_background = esc == internal::data::BACKGROUND_COLOR;
|
||||||
|
uint32_t value = text_color.value.term_color;
|
||||||
|
// Background ASCII codes are the same as the foreground ones but with
|
||||||
|
// 10 more.
|
||||||
|
if (is_background)
|
||||||
|
value += 10u;
|
||||||
|
|
||||||
|
std::size_t index = 0;
|
||||||
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
|
buffer[index++] = static_cast<Char>('[');
|
||||||
|
|
||||||
|
if (value >= 100u) {
|
||||||
|
buffer[index++] = static_cast<Char>('1');
|
||||||
|
value %= 100u;
|
||||||
|
}
|
||||||
|
buffer[index++] = static_cast<Char>('0' + value / 10u);
|
||||||
|
buffer[index++] = static_cast<Char>('0' + value % 10u);
|
||||||
|
|
||||||
|
buffer[index++] = static_cast<Char>('m');
|
||||||
|
buffer[index++] = static_cast<Char>('\0');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 7; i++) {
|
||||||
|
buffer[i] = static_cast<Char>(esc[i]);
|
||||||
|
}
|
||||||
|
rgb color(text_color.value.rgb_color);
|
||||||
|
to_esc(color.r, buffer + 7, ';');
|
||||||
|
to_esc(color.g, buffer + 11, ';');
|
||||||
|
to_esc(color.b, buffer + 15, 'm');
|
||||||
|
buffer[19] = static_cast<Char>(0);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
|
||||||
|
uint8_t em_codes[4] = {};
|
||||||
|
uint8_t em_bits = static_cast<uint8_t>(em);
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::bold))
|
||||||
|
em_codes[0] = 1;
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::italic))
|
||||||
|
em_codes[1] = 3;
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::underline))
|
||||||
|
em_codes[2] = 4;
|
||||||
|
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
|
||||||
|
em_codes[3] = 9;
|
||||||
|
|
||||||
|
std::size_t index = 0;
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
if (!em_codes[i])
|
||||||
|
continue;
|
||||||
|
buffer[index++] = static_cast<Char>('\x1b');
|
||||||
|
buffer[index++] = static_cast<Char>('[');
|
||||||
|
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
|
||||||
|
buffer[index++] = static_cast<Char>('m');
|
||||||
|
}
|
||||||
|
buffer[index++] = static_cast<Char>(0);
|
||||||
|
}
|
||||||
|
FMT_CONSTEXPR operator const Char *() const FMT_NOEXCEPT { return buffer; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Char buffer[7u + 3u * 4u + 1u];
|
||||||
|
|
||||||
|
static FMT_CONSTEXPR void to_esc(uint8_t c, Char *out,
|
||||||
|
char delimiter) FMT_NOEXCEPT {
|
||||||
|
out[0] = static_cast<Char>('0' + c / 100);
|
||||||
|
out[1] = static_cast<Char>('0' + c / 10 % 10);
|
||||||
|
out[2] = static_cast<Char>('0' + c % 10);
|
||||||
|
out[3] = static_cast<Char>(delimiter);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char>
|
||||||
|
make_foreground_color(internal::color_type foreground) FMT_NOEXCEPT {
|
||||||
|
return ansi_color_escape<Char>(foreground, internal::data::FOREGROUND_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char>
|
||||||
|
make_background_color(internal::color_type background) FMT_NOEXCEPT {
|
||||||
|
return ansi_color_escape<Char>(background, internal::data::BACKGROUND_COLOR);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
FMT_CONSTEXPR ansi_color_escape<Char>
|
||||||
|
make_emphasis(emphasis em) FMT_NOEXCEPT {
|
||||||
|
return ansi_color_escape<Char>(em);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline void fputs(const Char *chars, FILE *stream) FMT_NOEXCEPT {
|
||||||
|
std::fputs(chars, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline void fputs<wchar_t>(const wchar_t *chars, FILE *stream) FMT_NOEXCEPT {
|
||||||
|
std::fputws(chars, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
inline void reset_color(FILE *stream) FMT_NOEXCEPT {
|
||||||
|
fputs(internal::data::RESET_COLOR, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
inline void reset_color<wchar_t>(FILE *stream) FMT_NOEXCEPT {
|
||||||
|
fputs(internal::data::WRESET_COLOR, stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The following specialiazation disables using std::FILE as a character type,
|
||||||
|
// which is needed because or else
|
||||||
|
// fmt::print(stderr, fmt::emphasis::bold, "");
|
||||||
|
// would take stderr (a std::FILE *) as the format string.
|
||||||
|
template <>
|
||||||
|
struct is_string<std::FILE *> : std::false_type {};
|
||||||
|
template <>
|
||||||
|
struct is_string<const std::FILE *> : std::false_type {};
|
||||||
|
} // namespace internal
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename S, typename Char = typename internal::char_t<S>::type>
|
||||||
|
void vprint(std::FILE *f, const text_style &ts, const S &format,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
bool has_style = false;
|
||||||
|
if (ts.has_emphasis()) {
|
||||||
|
has_style = true;
|
||||||
|
internal::fputs<Char>(
|
||||||
|
internal::make_emphasis<Char>(ts.get_emphasis()), f);
|
||||||
|
}
|
||||||
|
if (ts.has_foreground()) {
|
||||||
|
has_style = true;
|
||||||
|
internal::fputs<Char>(
|
||||||
|
internal::make_foreground_color<Char>(ts.get_foreground()), f);
|
||||||
|
}
|
||||||
|
if (ts.has_background()) {
|
||||||
|
has_style = true;
|
||||||
|
internal::fputs<Char>(
|
||||||
|
internal::make_background_color<Char>(ts.get_background()), f);
|
||||||
|
}
|
||||||
|
vprint(f, format, args);
|
||||||
|
if (has_style) {
|
||||||
|
internal::reset_color<Char>(f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats a string and prints it to the specified file stream using ANSI
|
||||||
|
escape sequences to specify text formatting.
|
||||||
|
Example:
|
||||||
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
*/
|
||||||
|
template <typename String, typename... Args>
|
||||||
|
typename std::enable_if<internal::is_string<String>::value>::type print(
|
||||||
|
std::FILE *f, const text_style &ts, const String &format_str,
|
||||||
|
const Args &... args) {
|
||||||
|
internal::check_format_string<Args...>(format_str);
|
||||||
|
typedef typename internal::char_t<String>::type char_t;
|
||||||
|
typedef typename buffer_context<char_t>::type context_t;
|
||||||
|
format_arg_store<context_t, Args...> as{args...};
|
||||||
|
vprint(f, ts, format_str, basic_format_args<context_t>(as));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||||
|
specify text formatting.
|
||||||
|
Example:
|
||||||
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
*/
|
||||||
|
template <typename String, typename... Args>
|
||||||
|
typename std::enable_if<internal::is_string<String>::value>::type print(
|
||||||
|
const text_style &ts, const String &format_str,
|
||||||
|
const Args &... args) {
|
||||||
|
return print(stdout, ts, format_str, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_COLOR_H_
|
@ -1,257 +0,0 @@
|
|||||||
// Formatting library for C++ - the core API
|
|
||||||
//
|
|
||||||
// Copyright (c) 2012 - present, Victor Zverovich
|
|
||||||
// All rights reserved.
|
|
||||||
//
|
|
||||||
// For the license information refer to format.h.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
|
|
||||||
// All Rights Reserved
|
|
||||||
// {fmt} support for rgb color output.
|
|
||||||
|
|
||||||
#ifndef FMT_COLORS_H_
|
|
||||||
#define FMT_COLORS_H_
|
|
||||||
|
|
||||||
#include "format.h"
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
|
||||||
|
|
||||||
// rgb is a struct for red, green and blue colors.
|
|
||||||
// We use rgb as name because some editors will show it as color direct in the
|
|
||||||
// editor.
|
|
||||||
struct rgb
|
|
||||||
{
|
|
||||||
FMT_CONSTEXPR_DECL rgb()
|
|
||||||
: r(0)
|
|
||||||
, g(0)
|
|
||||||
, b(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_)
|
|
||||||
: r(r_)
|
|
||||||
, g(g_)
|
|
||||||
, b(b_)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
FMT_CONSTEXPR_DECL rgb(uint32_t hex)
|
|
||||||
: r((hex >> 16) & 0xFF)
|
|
||||||
, g((hex >> 8) & 0xFF)
|
|
||||||
, b((hex)&0xFF)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
uint8_t r;
|
|
||||||
uint8_t g;
|
|
||||||
uint8_t b;
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace internal {
|
|
||||||
|
|
||||||
FMT_CONSTEXPR inline void to_esc(uint8_t c, char out[], int offset)
|
|
||||||
{
|
|
||||||
out[offset + 0] = static_cast<char>('0' + c / 100);
|
|
||||||
out[offset + 1] = static_cast<char>('0' + c / 10 % 10);
|
|
||||||
out[offset + 2] = static_cast<char>('0' + c % 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace internal
|
|
||||||
|
|
||||||
FMT_FUNC void vprint_rgb(rgb fd, string_view format, format_args args)
|
|
||||||
{
|
|
||||||
char escape_fd[] = "\x1b[38;2;000;000;000m";
|
|
||||||
static FMT_CONSTEXPR_DECL const char RESET_COLOR[] = "\x1b[0m";
|
|
||||||
internal::to_esc(fd.r, escape_fd, 7);
|
|
||||||
internal::to_esc(fd.g, escape_fd, 11);
|
|
||||||
internal::to_esc(fd.b, escape_fd, 15);
|
|
||||||
|
|
||||||
std::fputs(escape_fd, stdout);
|
|
||||||
vprint(format, args);
|
|
||||||
std::fputs(RESET_COLOR, stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
FMT_FUNC void vprint_rgb(rgb fd, rgb bg, string_view format, format_args args)
|
|
||||||
{
|
|
||||||
char escape_fd[] = "\x1b[38;2;000;000;000m"; // foreground color
|
|
||||||
char escape_bg[] = "\x1b[48;2;000;000;000m"; // background color
|
|
||||||
static FMT_CONSTEXPR_DECL const char RESET_COLOR[] = "\x1b[0m";
|
|
||||||
internal::to_esc(fd.r, escape_fd, 7);
|
|
||||||
internal::to_esc(fd.g, escape_fd, 11);
|
|
||||||
internal::to_esc(fd.b, escape_fd, 15);
|
|
||||||
|
|
||||||
internal::to_esc(bg.r, escape_bg, 7);
|
|
||||||
internal::to_esc(bg.g, escape_bg, 11);
|
|
||||||
internal::to_esc(bg.b, escape_bg, 15);
|
|
||||||
|
|
||||||
std::fputs(escape_fd, stdout);
|
|
||||||
std::fputs(escape_bg, stdout);
|
|
||||||
vprint(format, args);
|
|
||||||
std::fputs(RESET_COLOR, stdout);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename... Args>
|
|
||||||
inline void print_rgb(rgb fd, string_view format_str, const Args &... args)
|
|
||||||
{
|
|
||||||
vprint_rgb(fd, format_str, make_format_args(args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
// rgb foreground color
|
|
||||||
template<typename... Args>
|
|
||||||
inline void print(rgb fd, string_view format_str, const Args &... args)
|
|
||||||
{
|
|
||||||
vprint_rgb(fd, format_str, make_format_args(args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
// rgb foreground color and background color
|
|
||||||
template<typename... Args>
|
|
||||||
inline void print(rgb fd, rgb bg, string_view format_str, const Args &... args)
|
|
||||||
{
|
|
||||||
vprint_rgb(fd, bg, format_str, make_format_args(args...));
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class color : uint32_t
|
|
||||||
{
|
|
||||||
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
|
||||||
antique_white = 0xFAEBD7, // rgb(250,235,215)
|
|
||||||
aqua = 0x00FFFF, // rgb(0,255,255)
|
|
||||||
aquamarine = 0x7FFFD4, // rgb(127,255,212)
|
|
||||||
azure = 0xF0FFFF, // rgb(240,255,255)
|
|
||||||
beige = 0xF5F5DC, // rgb(245,245,220)
|
|
||||||
bisque = 0xFFE4C4, // rgb(255,228,196)
|
|
||||||
black = 0x000000, // rgb(0,0,0)
|
|
||||||
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
|
|
||||||
blue = 0x0000FF, // rgb(0,0,255)
|
|
||||||
blue_violet = 0x8A2BE2, // rgb(138,43,226)
|
|
||||||
brown = 0xA52A2A, // rgb(165,42,42)
|
|
||||||
burly_wood = 0xDEB887, // rgb(222,184,135)
|
|
||||||
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
|
|
||||||
chartreuse = 0x7FFF00, // rgb(127,255,0)
|
|
||||||
chocolate = 0xD2691E, // rgb(210,105,30)
|
|
||||||
coral = 0xFF7F50, // rgb(255,127,80)
|
|
||||||
cornflower_blue = 0x6495ED, // rgb(100,149,237)
|
|
||||||
cornsilk = 0xFFF8DC, // rgb(255,248,220)
|
|
||||||
crimson = 0xDC143C, // rgb(220,20,60)
|
|
||||||
cyan = 0x00FFFF, // rgb(0,255,255)
|
|
||||||
dark_blue = 0x00008B, // rgb(0,0,139)
|
|
||||||
dark_cyan = 0x008B8B, // rgb(0,139,139)
|
|
||||||
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
|
|
||||||
dark_gray = 0xA9A9A9, // rgb(169,169,169)
|
|
||||||
dark_green = 0x006400, // rgb(0,100,0)
|
|
||||||
dark_khaki = 0xBDB76B, // rgb(189,183,107)
|
|
||||||
dark_magenta = 0x8B008B, // rgb(139,0,139)
|
|
||||||
dark_olive_green = 0x556B2F, // rgb(85,107,47)
|
|
||||||
dark_orange = 0xFF8C00, // rgb(255,140,0)
|
|
||||||
dark_orchid = 0x9932CC, // rgb(153,50,204)
|
|
||||||
dark_red = 0x8B0000, // rgb(139,0,0)
|
|
||||||
dark_salmon = 0xE9967A, // rgb(233,150,122)
|
|
||||||
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
|
|
||||||
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
|
|
||||||
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
|
|
||||||
dark_turquoise = 0x00CED1, // rgb(0,206,209)
|
|
||||||
dark_violet = 0x9400D3, // rgb(148,0,211)
|
|
||||||
deep_pink = 0xFF1493, // rgb(255,20,147)
|
|
||||||
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
|
|
||||||
dim_gray = 0x696969, // rgb(105,105,105)
|
|
||||||
dodger_blue = 0x1E90FF, // rgb(30,144,255)
|
|
||||||
fire_brick = 0xB22222, // rgb(178,34,34)
|
|
||||||
floral_white = 0xFFFAF0, // rgb(255,250,240)
|
|
||||||
forest_green = 0x228B22, // rgb(34,139,34)
|
|
||||||
fuchsia = 0xFF00FF, // rgb(255,0,255)
|
|
||||||
gainsboro = 0xDCDCDC, // rgb(220,220,220)
|
|
||||||
ghost_white = 0xF8F8FF, // rgb(248,248,255)
|
|
||||||
gold = 0xFFD700, // rgb(255,215,0)
|
|
||||||
golden_rod = 0xDAA520, // rgb(218,165,32)
|
|
||||||
gray = 0x808080, // rgb(128,128,128)
|
|
||||||
green = 0x008000, // rgb(0,128,0)
|
|
||||||
green_yellow = 0xADFF2F, // rgb(173,255,47)
|
|
||||||
honey_dew = 0xF0FFF0, // rgb(240,255,240)
|
|
||||||
hot_pink = 0xFF69B4, // rgb(255,105,180)
|
|
||||||
indian_red = 0xCD5C5C, // rgb(205,92,92)
|
|
||||||
indigo = 0x4B0082, // rgb(75,0,130)
|
|
||||||
ivory = 0xFFFFF0, // rgb(255,255,240)
|
|
||||||
khaki = 0xF0E68C, // rgb(240,230,140)
|
|
||||||
lavender = 0xE6E6FA, // rgb(230,230,250)
|
|
||||||
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
|
|
||||||
lawn_green = 0x7CFC00, // rgb(124,252,0)
|
|
||||||
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
|
|
||||||
light_blue = 0xADD8E6, // rgb(173,216,230)
|
|
||||||
light_coral = 0xF08080, // rgb(240,128,128)
|
|
||||||
light_cyan = 0xE0FFFF, // rgb(224,255,255)
|
|
||||||
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
|
|
||||||
light_gray = 0xD3D3D3, // rgb(211,211,211)
|
|
||||||
light_green = 0x90EE90, // rgb(144,238,144)
|
|
||||||
light_pink = 0xFFB6C1, // rgb(255,182,193)
|
|
||||||
light_salmon = 0xFFA07A, // rgb(255,160,122)
|
|
||||||
light_sea_green = 0x20B2AA, // rgb(32,178,170)
|
|
||||||
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
|
|
||||||
light_slate_gray = 0x778899, // rgb(119,136,153)
|
|
||||||
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
|
|
||||||
light_yellow = 0xFFFFE0, // rgb(255,255,224)
|
|
||||||
lime = 0x00FF00, // rgb(0,255,0)
|
|
||||||
lime_green = 0x32CD32, // rgb(50,205,50)
|
|
||||||
linen = 0xFAF0E6, // rgb(250,240,230)
|
|
||||||
magenta = 0xFF00FF, // rgb(255,0,255)
|
|
||||||
maroon = 0x800000, // rgb(128,0,0)
|
|
||||||
medium_aqua_marine = 0x66CDAA, // rgb(102,205,170)
|
|
||||||
medium_blue = 0x0000CD, // rgb(0,0,205)
|
|
||||||
medium_orchid = 0xBA55D3, // rgb(186,85,211)
|
|
||||||
medium_purple = 0x9370DB, // rgb(147,112,219)
|
|
||||||
medium_sea_green = 0x3CB371, // rgb(60,179,113)
|
|
||||||
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
|
|
||||||
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
|
|
||||||
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
|
|
||||||
medium_violet_red = 0xC71585, // rgb(199,21,133)
|
|
||||||
midnight_blue = 0x191970, // rgb(25,25,112)
|
|
||||||
mint_cream = 0xF5FFFA, // rgb(245,255,250)
|
|
||||||
misty_rose = 0xFFE4E1, // rgb(255,228,225)
|
|
||||||
moccasin = 0xFFE4B5, // rgb(255,228,181)
|
|
||||||
navajo_white = 0xFFDEAD, // rgb(255,222,173)
|
|
||||||
navy = 0x000080, // rgb(0,0,128)
|
|
||||||
old_lace = 0xFDF5E6, // rgb(253,245,230)
|
|
||||||
olive = 0x808000, // rgb(128,128,0)
|
|
||||||
olive_drab = 0x6B8E23, // rgb(107,142,35)
|
|
||||||
orange = 0xFFA500, // rgb(255,165,0)
|
|
||||||
orange_red = 0xFF4500, // rgb(255,69,0)
|
|
||||||
orchid = 0xDA70D6, // rgb(218,112,214)
|
|
||||||
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
|
|
||||||
pale_green = 0x98FB98, // rgb(152,251,152)
|
|
||||||
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
|
|
||||||
pale_violet_red = 0xDB7093, // rgb(219,112,147)
|
|
||||||
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
|
|
||||||
peach_puff = 0xFFDAB9, // rgb(255,218,185)
|
|
||||||
peru = 0xCD853F, // rgb(205,133,63)
|
|
||||||
pink = 0xFFC0CB, // rgb(255,192,203)
|
|
||||||
plum = 0xDDA0DD, // rgb(221,160,221)
|
|
||||||
powder_blue = 0xB0E0E6, // rgb(176,224,230)
|
|
||||||
purple = 0x800080, // rgb(128,0,128)
|
|
||||||
rebecca_purple = 0x663399, // rgb(102,51,153)
|
|
||||||
red = 0xFF0000, // rgb(255,0,0)
|
|
||||||
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
|
|
||||||
royal_blue = 0x4169E1, // rgb(65,105,225)
|
|
||||||
saddle_brown = 0x8B4513, // rgb(139,69,19)
|
|
||||||
salmon = 0xFA8072, // rgb(250,128,114)
|
|
||||||
sandy_brown = 0xF4A460, // rgb(244,164,96)
|
|
||||||
sea_green = 0x2E8B57, // rgb(46,139,87)
|
|
||||||
sea_shell = 0xFFF5EE, // rgb(255,245,238)
|
|
||||||
sienna = 0xA0522D, // rgb(160,82,45)
|
|
||||||
silver = 0xC0C0C0, // rgb(192,192,192)
|
|
||||||
sky_blue = 0x87CEEB, // rgb(135,206,235)
|
|
||||||
slate_blue = 0x6A5ACD, // rgb(106,90,205)
|
|
||||||
slate_gray = 0x708090, // rgb(112,128,144)
|
|
||||||
snow = 0xFFFAFA, // rgb(255,250,250)
|
|
||||||
spring_green = 0x00FF7F, // rgb(0,255,127)
|
|
||||||
steel_blue = 0x4682B4, // rgb(70,130,180)
|
|
||||||
tan = 0xD2B48C, // rgb(210,180,140)
|
|
||||||
teal = 0x008080, // rgb(0,128,128)
|
|
||||||
thistle = 0xD8BFD8, // rgb(216,191,216)
|
|
||||||
tomato = 0xFF6347, // rgb(255,99,71)
|
|
||||||
turquoise = 0x40E0D0, // rgb(64,224,208)
|
|
||||||
violet = 0xEE82EE, // rgb(238,130,238)
|
|
||||||
wheat = 0xF5DEB3, // rgb(245,222,179)
|
|
||||||
white = 0xFFFFFF, // rgb(255,255,255)
|
|
||||||
white_smoke = 0xF5F5F5, // rgb(245,245,245)
|
|
||||||
yellow = 0xFFFF00, // rgb(255,255,0)
|
|
||||||
yellow_green = 0x9ACD32, // rgb(154,205,50)
|
|
||||||
}; // enum class colors
|
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
|
||||||
|
|
||||||
#endif // FMT_COLORS_H_
|
|
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
@ -1,27 +1,77 @@
|
|||||||
// Formatting library for C++ - locale support
|
// Formatting library for C++ - std::locale support
|
||||||
//
|
//
|
||||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// For the license information refer to format.h.
|
// For the license information refer to format.h.
|
||||||
|
|
||||||
|
#ifndef FMT_LOCALE_H_
|
||||||
|
#define FMT_LOCALE_H_
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
#include <locale>
|
#include <locale>
|
||||||
|
|
||||||
namespace fmt {
|
FMT_BEGIN_NAMESPACE
|
||||||
class locale
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
std::locale locale_;
|
|
||||||
|
|
||||||
public:
|
namespace internal {
|
||||||
explicit locale(std::locale loc = std::locale())
|
template <typename Char>
|
||||||
: locale_(loc)
|
typename buffer_context<Char>::type::iterator vformat_to(
|
||||||
{
|
const std::locale &loc, basic_buffer<Char> &buf,
|
||||||
|
basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
typedef back_insert_range<basic_buffer<Char> > range;
|
||||||
|
return vformat_to<arg_formatter<range>>(
|
||||||
|
buf, to_string_view(format_str), args, internal::locale_ref(loc));
|
||||||
}
|
}
|
||||||
std::locale get()
|
|
||||||
{
|
template <typename Char>
|
||||||
return locale_;
|
std::basic_string<Char> vformat(
|
||||||
|
const std::locale &loc, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
basic_memory_buffer<Char> buffer;
|
||||||
|
internal::vformat_to(loc, buffer, format_str, args);
|
||||||
|
return fmt::to_string(buffer);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
} // namespace fmt
|
|
||||||
|
template <typename S, typename Char = FMT_CHAR(S)>
|
||||||
|
inline std::basic_string<Char> vformat(
|
||||||
|
const std::locale &loc, const S &format_str,
|
||||||
|
basic_format_args<typename buffer_context<Char>::type> args) {
|
||||||
|
return internal::vformat(loc, to_string_view(format_str), args);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename S, typename... Args>
|
||||||
|
inline std::basic_string<FMT_CHAR(S)> format(
|
||||||
|
const std::locale &loc, const S &format_str, const Args &... args) {
|
||||||
|
return internal::vformat(
|
||||||
|
loc, to_string_view(format_str),
|
||||||
|
*internal::checked_args<S, Args...>(format_str, args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename String, typename OutputIt, typename... Args>
|
||||||
|
inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value,
|
||||||
|
OutputIt>::type
|
||||||
|
vformat_to(OutputIt out, const std::locale &loc, const String &format_str,
|
||||||
|
typename format_args_t<OutputIt, FMT_CHAR(String)>::type args) {
|
||||||
|
typedef output_range<OutputIt, FMT_CHAR(String)> range;
|
||||||
|
return vformat_to<arg_formatter<range>>(
|
||||||
|
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename S, typename... Args>
|
||||||
|
inline typename std::enable_if<
|
||||||
|
internal::is_string<S>::value &&
|
||||||
|
internal::is_output_iterator<OutputIt>::value, OutputIt>::type
|
||||||
|
format_to(OutputIt out, const std::locale &loc, const S &format_str,
|
||||||
|
const Args &... args) {
|
||||||
|
internal::check_format_string<Args...>(format_str);
|
||||||
|
typedef typename format_context_t<OutputIt, FMT_CHAR(S)>::type context;
|
||||||
|
format_arg_store<context, Args...> as{args...};
|
||||||
|
return vformat_to(out, loc, to_string_view(format_str),
|
||||||
|
basic_format_args<context>(as));
|
||||||
|
}
|
||||||
|
|
||||||
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // FMT_LOCALE_H_
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Formatting library for C++ - std::ostream support
|
// Formatting library for C++ - std::ostream support
|
||||||
//
|
//
|
||||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// For the license information refer to format.h.
|
// For the license information refer to format.h.
|
||||||
@ -15,8 +15,7 @@ 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;
|
||||||
@ -24,10 +23,7 @@ private:
|
|||||||
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
|
||||||
@ -37,37 +33,34 @@ protected:
|
|||||||
// 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:
|
private:
|
||||||
template <typename U>
|
template <typename U>
|
||||||
static decltype(internal::declval<test_stream<Char> &>() << internal::declval<U>(), std::true_type()) test(int);
|
static decltype(
|
||||||
|
internal::declval<test_stream<Char>&>()
|
||||||
|
<< internal::declval<U>(), std::true_type()) test(int);
|
||||||
|
|
||||||
template <typename>
|
template <typename>
|
||||||
static std::false_type test(...);
|
static std::false_type test(...);
|
||||||
@ -75,31 +68,18 @@ private:
|
|||||||
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 = internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
|
UnsignedStreamSize max_size =
|
||||||
do
|
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
|
||||||
{
|
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;
|
||||||
@ -108,44 +88,48 @@ void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable builtin formatting of enums and use operator<< instead.
|
|
||||||
template<typename T>
|
|
||||||
struct format_enum<T, typename std::enable_if<std::is_enum<T>::value>::type> : std::false_type
|
|
||||||
{
|
|
||||||
};
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
|
// 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>
|
||||||
|
struct convert_to_int<T, Char, void> {
|
||||||
|
static const bool value =
|
||||||
|
convert_to_int<T, Char, int>::value &&
|
||||||
|
!internal::is_streamable<T, Char>::value;
|
||||||
|
};
|
||||||
|
|
||||||
// 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());
|
||||||
formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
return 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);
|
internal::vformat_to(buffer, format_str, args);
|
||||||
internal::write(os, buffer);
|
internal::write(os, buffer);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -157,16 +141,12 @@ inline void vprint(
|
|||||||
fmt::print(cerr, "Don't {}!", "panic");
|
fmt::print(cerr, "Don't {}!", "panic");
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
template<typename... Args>
|
template <typename S, typename... Args>
|
||||||
inline void print(std::ostream &os, string_view format_str, const Args &... args)
|
inline typename std::enable_if<internal::is_string<S>::value>::type
|
||||||
{
|
print(std::basic_ostream<FMT_CHAR(S)> &os, const S &format_str,
|
||||||
vprint<char>(os, format_str, make_format_args<format_context>(args...));
|
const Args & ... args) {
|
||||||
}
|
internal::checked_args<S, Args...> ca(format_str, args...);
|
||||||
|
vprint(os, to_string_view(format_str), *ca);
|
||||||
template<typename... 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...));
|
|
||||||
}
|
}
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
@ -53,8 +53,7 @@
|
|||||||
// 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
|
||||||
@ -91,140 +90,72 @@ FMT_BEGIN_NAMESPACE
|
|||||||
\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_NOEXCEPT;
|
||||||
|
|
||||||
#if !FMT_USE_RVALUE_REFERENCES
|
|
||||||
// Emulate a move constructor and a move assignment operator if rvalue
|
|
||||||
// references are not supported.
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// A proxy object to emulate a move constructor.
|
buffered_file(const buffered_file &) = delete;
|
||||||
// It is private to make it impossible call operator Proxy directly.
|
void operator=(const buffered_file &) = delete;
|
||||||
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(buffered_file &f) FMT_NOEXCEPT : file_(f.file_)
|
|
||||||
{
|
|
||||||
f.file_ = FMT_NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// A "move assignment operator" for moving from a temporary.
|
buffered_file& operator=(buffered_file &&other) {
|
||||||
buffered_file &operator=(Proxy p)
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
file_ = p.file;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A "move assignment operator" for moving from an lvalue.
|
|
||||||
buffered_file &operator=(buffered_file &other)
|
|
||||||
{
|
|
||||||
close();
|
close();
|
||||||
file_ = other.file_;
|
file_ = other.file_;
|
||||||
other.file_ = FMT_NULL;
|
other.file_ = FMT_NULL;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a proxy object for moving from a temporary:
|
|
||||||
// buffered_file file = buffered_file(...);
|
|
||||||
operator Proxy() FMT_NOEXCEPT
|
|
||||||
{
|
|
||||||
Proxy p = {file_};
|
|
||||||
file_ = FMT_NULL;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
private:
|
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(buffered_file);
|
|
||||||
|
|
||||||
public:
|
|
||||||
buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_)
|
|
||||||
{
|
|
||||||
other.file_ = FMT_NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffered_file &operator=(buffered_file &&other)
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
file_ = other.file_;
|
|
||||||
other.file_ = FMT_NULL;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Opens a file.
|
// Opens a file.
|
||||||
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
FMT_API buffered_file(cstring_view filename, cstring_view mode);
|
||||||
|
|
||||||
@ -232,23 +163,18 @@ public:
|
|||||||
FMT_API void close();
|
FMT_API void close();
|
||||||
|
|
||||||
// Returns the pointer to a FILE object representing this file.
|
// Returns the pointer to a FILE object representing this file.
|
||||||
FILE *get() const FMT_NOEXCEPT
|
FILE *get() const FMT_NOEXCEPT { return file_; }
|
||||||
{
|
|
||||||
return file_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We place parentheses around fileno to workaround a bug in some versions
|
// We place parentheses around fileno to workaround a bug in some versions
|
||||||
// of MinGW that define fileno as a macro.
|
// of MinGW that define fileno as a macro.
|
||||||
FMT_API int (fileno)() const;
|
FMT_API int (fileno)() const;
|
||||||
|
|
||||||
void vprint(string_view format_str, format_args args)
|
void vprint(string_view format_str, format_args args) {
|
||||||
{
|
|
||||||
fmt::vprint(file_, format_str, args);
|
fmt::vprint(file_, format_str, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
inline void print(string_view format_str, const Args &... args)
|
inline void print(string_view format_str, const Args & ... args) {
|
||||||
{
|
|
||||||
vprint(format_str, make_format_args(args...));
|
vprint(format_str, make_format_args(args...));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -259,21 +185,16 @@ 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.
|
||||||
@ -285,81 +206,27 @@ public:
|
|||||||
// 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
|
|
||||||
// Emulate a move constructor and a move assignment operator if rvalue
|
|
||||||
// references are not supported.
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// A proxy object to emulate a move constructor.
|
file(const file &) = delete;
|
||||||
// It is private to make it impossible call operator Proxy directly.
|
void operator=(const file &) = delete;
|
||||||
struct Proxy
|
|
||||||
{
|
|
||||||
int fd;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// A "move constructor" for moving from a temporary.
|
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) {
|
||||||
file(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {}
|
|
||||||
|
|
||||||
// A "move constructor" for moving from an lvalue.
|
|
||||||
file(file &other) FMT_NOEXCEPT : fd_(other.fd_)
|
|
||||||
{
|
|
||||||
other.fd_ = -1;
|
other.fd_ = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// A "move assignment operator" for moving from a temporary.
|
file& operator=(file &&other) {
|
||||||
file &operator=(Proxy p)
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
fd_ = p.fd;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A "move assignment operator" for moving from an lvalue.
|
|
||||||
file &operator=(file &other)
|
|
||||||
{
|
|
||||||
close();
|
close();
|
||||||
fd_ = other.fd_;
|
fd_ = other.fd_;
|
||||||
other.fd_ = -1;
|
other.fd_ = -1;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a proxy object for moving from a temporary:
|
|
||||||
// file f = file(...);
|
|
||||||
operator Proxy() FMT_NOEXCEPT
|
|
||||||
{
|
|
||||||
Proxy p = {fd_};
|
|
||||||
fd_ = -1;
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
#else
|
|
||||||
private:
|
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(file);
|
|
||||||
|
|
||||||
public:
|
|
||||||
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_)
|
|
||||||
{
|
|
||||||
other.fd_ = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
file &operator=(file &&other)
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
fd_ = other.fd_;
|
|
||||||
other.fd_ = -1;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Destroys the object closing the file it represents if any.
|
// Destroys the object closing the file it represents if any.
|
||||||
FMT_API ~file() FMT_DTOR_NOEXCEPT;
|
FMT_API ~file() FMT_NOEXCEPT;
|
||||||
|
|
||||||
// Returns the file descriptor.
|
// Returns the file descriptor.
|
||||||
int descriptor() const FMT_NOEXCEPT
|
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
||||||
{
|
|
||||||
return fd_;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Closes the file.
|
// Closes the file.
|
||||||
FMT_API void close();
|
FMT_API void close();
|
||||||
@ -398,66 +265,53 @@ public:
|
|||||||
// 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)) && \
|
||||||
|
!defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__) && \
|
||||||
|
!defined(__NEWLIB_H__)
|
||||||
# define FMT_LOCALE
|
# 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_)
|
if (!locale_)
|
||||||
FMT_THROW(system_error(errno, "cannot create locale"));
|
FMT_THROW(system_error(errno, "cannot create locale"));
|
||||||
}
|
}
|
||||||
~Locale()
|
~Locale() { freelocale(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;
|
||||||
@ -467,18 +321,4 @@ public:
|
|||||||
#endif // FMT_LOCALE
|
#endif // FMT_LOCALE
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#if !FMT_USE_RVALUE_REFERENCES
|
|
||||||
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_
|
#endif // FMT_POSIX_H_
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -23,45 +23,31 @@
|
|||||||
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()
|
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
|
||||||
: prefix('{')
|
|
||||||
, delimiter(',')
|
|
||||||
, postfix('}')
|
|
||||||
{
|
|
||||||
}
|
|
||||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
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()
|
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
|
||||||
: prefix('(')
|
|
||||||
, delimiter(',')
|
|
||||||
, postfix(')')
|
|
||||||
{
|
|
||||||
}
|
|
||||||
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
|
||||||
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
|
||||||
};
|
};
|
||||||
@ -69,71 +55,69 @@ struct formatting_tuple : formatting_base<Char>
|
|||||||
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) -> decltype(p->find('a'), p->length(), p->data(), int());
|
static auto check(U *p) ->
|
||||||
|
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 Char>
|
||||||
|
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {};
|
||||||
|
|
||||||
template <typename... Ts>
|
template <typename... Ts>
|
||||||
struct conditional_helper
|
struct conditional_helper {};
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T, typename _ = void>
|
template <typename T, typename _ = void>
|
||||||
struct is_range_ : std::false_type
|
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) -> decltype(std::tuple_size<U>::value, internal::declval<typename std::tuple_element<0, U>::type>(), int());
|
static auto check(U *p) ->
|
||||||
|
decltype(std::tuple_size<U>::value,
|
||||||
|
internal::declval<typename std::tuple_element<0, U>::type>(), 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;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check for integer_sequence
|
// Check for integer_sequence
|
||||||
@ -146,12 +130,10 @@ 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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -160,21 +142,16 @@ 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>
|
template <typename T, T... Ns>
|
||||||
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<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)...};
|
||||||
@ -182,78 +159,70 @@ void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 (i > 0)
|
if (formatting.add_prepostfix_space) {
|
||||||
{
|
|
||||||
if (formatting.add_prepostfix_space)
|
|
||||||
{
|
|
||||||
*out++ = ' ';
|
*out++ = ' ';
|
||||||
}
|
}
|
||||||
internal::copy(formatting.delimiter, out);
|
internal::copy(formatting.delimiter, out);
|
||||||
}
|
}
|
||||||
format_to(out, internal::format_str_quoted((formatting.add_delimiter_spaces && i > 0), v), v);
|
format_to(out,
|
||||||
|
internal::format_str_quoted(
|
||||||
|
(formatting.add_delimiter_spaces && i > 0), v),
|
||||||
|
v);
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,21 +235,18 @@ 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>
|
template <typename FormatContext = format_context>
|
||||||
auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out())
|
auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out()) {
|
||||||
{
|
|
||||||
auto out = ctx.out();
|
auto out = ctx.out();
|
||||||
std::size_t i = 0;
|
std::size_t i = 0;
|
||||||
internal::copy(formatting.prefix, out);
|
internal::copy(formatting.prefix, out);
|
||||||
|
|
||||||
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
|
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
|
||||||
if (formatting.add_prepostfix_space)
|
if (formatting.add_prepostfix_space) {
|
||||||
{
|
|
||||||
*out++ = ' ';
|
*out++ = ' ';
|
||||||
}
|
}
|
||||||
internal::copy(formatting.postfix, out);
|
internal::copy(formatting.postfix, out);
|
||||||
@ -290,48 +256,45 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
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) {
|
||||||
{
|
|
||||||
if (formatting.add_prepostfix_space)
|
|
||||||
{
|
|
||||||
*out++ = ' ';
|
*out++ = ' ';
|
||||||
}
|
}
|
||||||
internal::copy(formatting.delimiter, out);
|
internal::copy(formatting.delimiter, out);
|
||||||
}
|
}
|
||||||
format_to(out, internal::format_str_quoted((formatting.add_delimiter_spaces && i > 0), *it), *it);
|
format_to(out,
|
||||||
if (++i > formatting.range_length_limit)
|
internal::format_str_quoted(
|
||||||
{
|
(formatting.add_delimiter_spaces && i > 0), *it),
|
||||||
|
*it);
|
||||||
|
if (++i > formatting.range_length_limit) {
|
||||||
format_to(out, " ... <other elements>");
|
format_to(out, " ... <other elements>");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (formatting.add_prepostfix_space)
|
if (formatting.add_prepostfix_space) {
|
||||||
{
|
|
||||||
*out++ = ' ';
|
*out++ = ' ';
|
||||||
}
|
}
|
||||||
internal::copy(formatting.postfix, out);
|
internal::copy(formatting.postfix, out);
|
||||||
@ -342,3 +305,4 @@ struct formatter<RangeT, Char, typename std::enable_if<fmt::is_range<RangeT>::va
|
|||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // FMT_RANGES_H_
|
#endif // FMT_RANGES_H_
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Formatting library for C++ - time formatting
|
// Formatting library for C++ - time formatting
|
||||||
//
|
//
|
||||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
// Copyright (c) 2012 - present, Victor Zverovich
|
||||||
// All rights reserved.
|
// All rights reserved.
|
||||||
//
|
//
|
||||||
// For the license information refer to format.h.
|
// For the license information refer to format.h.
|
||||||
@ -10,176 +10,137 @@
|
|||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
// Prevents expansion of a preceding token as a function-style macro.
|
||||||
|
// Usage: f FMT_NOMACRO()
|
||||||
|
#define FMT_NOMACRO
|
||||||
|
|
||||||
namespace internal{
|
namespace internal{
|
||||||
inline null<> localtime_r(...)
|
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
|
||||||
{
|
inline null<> localtime_s(...) { return null<>(); }
|
||||||
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
|
} // 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::time_t time_;
|
||||||
std::tm tm_;
|
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<>)
|
#if !FMT_MSC_VER
|
||||||
{
|
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)
|
if (tm) tm_ = *tm;
|
||||||
tm_ = *tm;
|
|
||||||
return tm != FMT_NULL;
|
return tm != FMT_NULL;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
dispatcher lt(time);
|
dispatcher lt(time);
|
||||||
if (lt.run())
|
|
||||||
return lt.tm_;
|
|
||||||
// Too big time values may be unsupported.
|
// Too big time values may be unsupported.
|
||||||
|
if (!lt.run())
|
||||||
FMT_THROW(format_error("time_t value out of range"));
|
FMT_THROW(format_error("time_t value out of range"));
|
||||||
|
return lt.tm_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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::time_t time_;
|
||||||
std::tm tm_;
|
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<>)
|
#if !FMT_MSC_VER
|
||||||
{
|
bool fallback(internal::null<>) {
|
||||||
std::tm *tm = std::gmtime(&time_);
|
std::tm *tm = std::gmtime(&time_);
|
||||||
if (tm)
|
if (tm) tm_ = *tm;
|
||||||
tm_ = *tm;
|
|
||||||
return tm != FMT_NULL;
|
return tm != FMT_NULL;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
dispatcher gt(time);
|
dispatcher gt(time);
|
||||||
if (gt.run())
|
|
||||||
return gt.tm_;
|
|
||||||
// Too big time values may be unsupported.
|
// Too big time values may be unsupported.
|
||||||
|
if (!gt.run())
|
||||||
FMT_THROW(format_error("time_t value out of range"));
|
FMT_THROW(format_error("time_t value out of range"));
|
||||||
|
return gt.tm_;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = ctx.begin();
|
||||||
auto it = internal::null_terminating_iterator<Char>(ctx);
|
if (it != ctx.end() && *it == ':')
|
||||||
if (*it == ':')
|
|
||||||
++it;
|
++it;
|
||||||
auto end = it;
|
auto end = it;
|
||||||
while (*end && *end != '}')
|
while (end != ctx.end() && *end != '}')
|
||||||
++end;
|
++end;
|
||||||
tm_format.reserve(end - it + 1);
|
tm_format.reserve(internal::to_unsigned(end - it + 1));
|
||||||
using internal::pointer_from;
|
tm_format.append(it, end);
|
||||||
tm_format.append(pointer_from(it), pointer_from(end));
|
|
||||||
tm_format.push_back('\0');
|
tm_format.push_back('\0');
|
||||||
return pointer_from(end);
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out())
|
auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) {
|
||||||
{
|
basic_memory_buffer<Char> buf;
|
||||||
internal::basic_buffer<Char> &buf = internal::get_container(ctx.out());
|
|
||||||
std::size_t start = buf.size();
|
std::size_t start = buf.size();
|
||||||
for (;;)
|
for (;;) {
|
||||||
{
|
|
||||||
std::size_t size = buf.capacity() - start;
|
std::size_t size = buf.capacity() - start;
|
||||||
std::size_t count = internal::strftime(&buf[start], size, &tm_format[0], &tm);
|
std::size_t count =
|
||||||
if (count != 0)
|
internal::strftime(&buf[start], size, &tm_format[0], &tm);
|
||||||
{
|
if (count != 0) {
|
||||||
buf.resize(start + count);
|
buf.resize(start + count);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (size >= tm_format.size() * 256)
|
if (size >= tm_format.size() * 256) {
|
||||||
{
|
|
||||||
// If the buffer is 256 times larger than the format string, assume
|
// 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
|
// that `strftime` gives an empty result. There doesn't seem to be a
|
||||||
// better way to distinguish the two cases:
|
// better way to distinguish the two cases:
|
||||||
@ -189,7 +150,7 @@ struct formatter<std::tm, Char>
|
|||||||
const std::size_t MIN_GROWTH = 10;
|
const std::size_t MIN_GROWTH = 10;
|
||||||
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
||||||
}
|
}
|
||||||
return ctx.out();
|
return std::copy(buf.begin(), buf.end(), ctx.out());
|
||||||
}
|
}
|
||||||
|
|
||||||
basic_memory_buffer<Char> tm_format;
|
basic_memory_buffer<Char> tm_format;
|
||||||
|
@ -35,7 +35,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();
|
||||||
|
|
||||||
@ -46,8 +46,12 @@ public:
|
|||||||
void log(level::level_enum lvl, const char *fmt, const Args &... args);
|
void log(level::level_enum lvl, const char *fmt, const Args &... args);
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
|
void log(source_loc loc, level::level_enum lvl, const char *fmt, const Args &... args);
|
||||||
|
|
||||||
void log(level::level_enum lvl, const char *msg);
|
void log(level::level_enum lvl, const char *msg);
|
||||||
|
|
||||||
|
void log(source_loc loc, level::level_enum lvl, const char *msg);
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void trace(const char *fmt, const Args &... args);
|
void trace(const char *fmt, const Args &... args);
|
||||||
|
|
||||||
@ -67,9 +71,15 @@ 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);
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
void log(source_loc source, level::level_enum lvl, const wchar_t *fmt, const Args &... args);
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void trace(const wchar_t *fmt, const Args &... args);
|
void trace(const wchar_t *fmt, const Args &... args);
|
||||||
|
|
||||||
@ -87,11 +97,25 @@ 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>
|
// T can be statically converted to string_view
|
||||||
|
template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
|
||||||
void log(level::level_enum lvl, const T &);
|
void log(level::level_enum lvl, const T &);
|
||||||
|
|
||||||
|
// T can be statically converted to string_view
|
||||||
|
template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
|
||||||
|
void log(source_loc loc, level::level_enum lvl, const T &);
|
||||||
|
|
||||||
|
// T cannot be statically converted to string_view
|
||||||
|
template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
|
||||||
|
void log(level::level_enum lvl, const T &);
|
||||||
|
|
||||||
|
// T cannot be statically converted to string_view
|
||||||
|
template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
|
||||||
|
void log(source_loc loc, level::level_enum lvl, const T &);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void trace(const T &msg);
|
void trace(const T &msg);
|
||||||
|
|
||||||
@ -112,6 +136,8 @@ public:
|
|||||||
|
|
||||||
bool should_log(level::level_enum msg_level) const;
|
bool should_log(level::level_enum msg_level) const;
|
||||||
void set_level(level::level_enum log_level);
|
void set_level(level::level_enum log_level);
|
||||||
|
|
||||||
|
static level::level_enum default_level();
|
||||||
level::level_enum level() const;
|
level::level_enum level() const;
|
||||||
const std::string &name() const;
|
const std::string &name() const;
|
||||||
|
|
||||||
@ -131,7 +157,7 @@ public:
|
|||||||
|
|
||||||
// error handler
|
// 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() const;
|
||||||
|
|
||||||
// create new logger with same sinks and configuration.
|
// create new logger with same sinks and configuration.
|
||||||
virtual std::shared_ptr<logger> clone(std::string logger_name);
|
virtual std::shared_ptr<logger> clone(std::string logger_name);
|
||||||
@ -142,26 +168,20 @@ protected:
|
|||||||
|
|
||||||
bool should_flush_(const details::log_msg &msg);
|
bool should_flush_(const details::log_msg &msg);
|
||||||
|
|
||||||
// default error handler: print the error to stderr with the max rate of 1
|
// default error handler.
|
||||||
// message/minute
|
// print the error to stderr with the max rate of 1 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_;
|
||||||
std::vector<sink_ptr> sinks_;
|
std::vector<sink_ptr> sinks_;
|
||||||
spdlog::level_t level_;
|
spdlog::level_t level_{spdlog::logger::default_level()};
|
||||||
spdlog::level_t flush_level_;
|
spdlog::level_t flush_level_{level::off};
|
||||||
log_err_handler err_handler_;
|
log_err_handler err_handler_{[this](const std::string &msg) { this->default_err_handler_(msg); }};
|
||||||
std::atomic<time_t> last_err_time_;
|
std::atomic<time_t> last_err_time_{0};
|
||||||
std::atomic<size_t> msg_counter_;
|
std::atomic<size_t> msg_counter_{1};
|
||||||
|
|
||||||
#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
|
||||||
|
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_H
|
||||||
|
#error "spdlog.h must be included before this file."
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "spdlog/details/fmt_helper.h"
|
#include "spdlog/details/fmt_helper.h"
|
||||||
#include "spdlog/details/null_mutex.h"
|
#include "spdlog/details/null_mutex.h"
|
||||||
#include "spdlog/details/os.h"
|
#include "spdlog/details/os.h"
|
||||||
@ -27,7 +31,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)
|
||||||
@ -43,7 +47,7 @@ protected:
|
|||||||
fmt::memory_buffer formatted;
|
fmt::memory_buffer formatted;
|
||||||
if (use_raw_msg_)
|
if (use_raw_msg_)
|
||||||
{
|
{
|
||||||
details::fmt_helper::append_buf(msg.raw, formatted);
|
details::fmt_helper::append_string_view(msg.payload, formatted);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -5,9 +5,14 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_H
|
||||||
|
#error "spdlog.h must be included before this file."
|
||||||
|
#endif
|
||||||
|
|
||||||
#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 +29,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 +121,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));
|
||||||
|
@ -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
|
||||||
|
@ -4,10 +4,14 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_H
|
||||||
|
#error "spdlog.h must be included before this file."
|
||||||
|
#endif
|
||||||
|
|
||||||
#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/sinks/base_sink.h"
|
#include "spdlog/sinks/base_sink.h"
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -18,7 +22,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)
|
||||||
|
@ -4,11 +4,15 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_H
|
||||||
|
#error "spdlog.h must be included before this file."
|
||||||
|
#endif
|
||||||
|
|
||||||
#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 <chrono>
|
#include <chrono>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@ -28,7 +32,7 @@ struct daily_filename_calculator
|
|||||||
static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
|
static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
|
||||||
{
|
{
|
||||||
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_extension(filename);
|
||||||
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w;
|
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w;
|
||||||
fmt::format_to(
|
fmt::format_to(
|
||||||
w, SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, ext);
|
w, SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, ext);
|
||||||
@ -40,7 +44,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
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_H
|
||||||
|
#error "spdlog.h must be included before this file."
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "base_sink.h"
|
#include "base_sink.h"
|
||||||
#include "spdlog/details/log_msg.h"
|
#include "spdlog/details/log_msg.h"
|
||||||
#include "spdlog/details/null_mutex.h"
|
#include "spdlog/details/null_mutex.h"
|
||||||
@ -62,8 +66,24 @@ 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_(details::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_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_H
|
||||||
|
#error "spdlog.h must be included before this file."
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
#include "spdlog/details/null_mutex.h"
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_H
|
||||||
|
#error "spdlog.h must be included before this file."
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
#include "spdlog/details/null_mutex.h"
|
||||||
#include "spdlog/sinks/base_sink.h"
|
#include "spdlog/sinks/base_sink.h"
|
||||||
|
|
||||||
@ -25,4 +29,21 @@ using null_sink_mt = null_sink<std::mutex>;
|
|||||||
using null_sink_st = null_sink<details::null_mutex>;
|
using null_sink_st = null_sink<details::null_mutex>;
|
||||||
|
|
||||||
} // namespace sinks
|
} // namespace sinks
|
||||||
|
|
||||||
|
template<typename Factory = default_factory>
|
||||||
|
inline std::shared_ptr<logger> null_logger_mt(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
auto null_logger = Factory::template create<sinks::null_sink_mt>(logger_name);
|
||||||
|
null_logger->set_level(level::off);
|
||||||
|
return null_logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Factory = default_factory>
|
||||||
|
inline std::shared_ptr<logger> null_logger_st(const std::string &logger_name)
|
||||||
|
{
|
||||||
|
auto null_logger = Factory::template create<sinks::null_sink_st>(logger_name);
|
||||||
|
null_logger->set_level(level::off);
|
||||||
|
return null_logger;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_H
|
||||||
|
#error "spdlog.h must be included before this file."
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
#include "spdlog/details/null_mutex.h"
|
||||||
#include "spdlog/sinks/base_sink.h"
|
#include "spdlog/sinks/base_sink.h"
|
||||||
|
|
||||||
@ -14,7 +18,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)
|
||||||
@ -32,8 +36,10 @@ protected:
|
|||||||
sink::formatter_->format(msg, formatted);
|
sink::formatter_->format(msg, formatted);
|
||||||
ostream_.write(formatted.data(), static_cast<std::streamsize>(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
|
||||||
{
|
{
|
||||||
|
@ -4,11 +4,15 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_H
|
||||||
|
#error "spdlog.h must be included before this file."
|
||||||
|
#endif
|
||||||
|
|
||||||
#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 <cerrno>
|
#include <cerrno>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
@ -24,7 +28,7 @@ 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, bool rotate_on_open=false)
|
rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open=false)
|
||||||
@ -46,7 +50,7 @@ public:
|
|||||||
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_extension(filename);
|
||||||
fmt::format_to(w, SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
|
fmt::format_to(w, SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -88,24 +92,22 @@ private:
|
|||||||
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);
|
||||||
|
if (!details::file_helper::file_exists(src))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
filename_t target = calc_filename(base_filename_, i);
|
filename_t target = calc_filename(base_filename_, i);
|
||||||
|
|
||||||
if (details::file_helper::file_exists(target))
|
if (!rename_file(src, target))
|
||||||
{
|
{
|
||||||
if (details::os::remove(target) != 0)
|
// if failed try again after a small delay.
|
||||||
{
|
|
||||||
throw spdlog_ex("rotating_file_sink: failed removing " + filename_to_str(target), errno);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (details::file_helper::file_exists(src) && details::os::rename(src, target) != 0)
|
|
||||||
{
|
|
||||||
// if failed try again after small delay.
|
|
||||||
// this is a workaround to a windows issue, where very high rotation
|
// this is a workaround to a windows issue, where very high rotation
|
||||||
// rates sometimes fail (because of antivirus?).
|
// rates can cause the rename to fail with permission denied (because of antivirus?).
|
||||||
details::os::sleep_for_millis(20);
|
details::os::sleep_for_millis(100);
|
||||||
details::os::remove(target);
|
if (!rename_file(src, target))
|
||||||
if (details::os::rename(src, target) != 0)
|
|
||||||
{
|
{
|
||||||
|
file_helper_.reopen(true); // truncate the log file anyway to prevent it to grow beyond its limit!
|
||||||
|
current_size_ = 0;
|
||||||
throw spdlog_ex(
|
throw spdlog_ex(
|
||||||
"rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
|
"rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
|
||||||
}
|
}
|
||||||
@ -114,6 +116,15 @@ private:
|
|||||||
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)
|
||||||
|
{
|
||||||
|
// try to delete the target file in case it already exists.
|
||||||
|
(void)details::os::remove(target_filename);
|
||||||
|
return details::os::rename(src_filename, target_filename) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
filename_t base_filename_;
|
filename_t base_filename_;
|
||||||
std::size_t max_size_;
|
std::size_t max_size_;
|
||||||
std::size_t max_files_;
|
std::size_t max_files_;
|
||||||
|
@ -16,7 +16,7 @@ class sink
|
|||||||
public:
|
public:
|
||||||
sink()
|
sink()
|
||||||
: level_(level::trace)
|
: level_(level::trace)
|
||||||
, formatter_(new pattern_formatter("%+"))
|
, formatter_(new pattern_formatter())
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "spdlog/spdlog.h"
|
#ifndef SPDLOG_H
|
||||||
|
#error "spdlog.h must be included before this file."
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#include "spdlog/sinks/wincolor_sink.h"
|
#include "spdlog/sinks/wincolor_sink.h"
|
||||||
#else
|
#else
|
||||||
|
@ -5,9 +5,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_H
|
||||||
|
#error "spdlog.h must be included before this file."
|
||||||
|
#endif
|
||||||
|
|
||||||
#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/spdlog.h"
|
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -18,7 +21,7 @@ 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;
|
||||||
|
@ -5,8 +5,11 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_H
|
||||||
|
#error "spdlog.h must be included before this file."
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "spdlog/sinks/base_sink.h"
|
#include "spdlog/sinks/base_sink.h"
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -50,7 +53,7 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
void sink_it_(const details::log_msg &msg) override
|
void sink_it_(const details::log_msg &msg) override
|
||||||
{
|
{
|
||||||
::syslog(syslog_prio_from_level(msg), "%s", fmt::to_string(msg.raw).c_str());
|
::syslog(syslog_prio_from_level(msg), "%s", fmt::to_string(msg.payload).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void flush_() override {}
|
void flush_() override {}
|
||||||
|
@ -5,6 +5,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SPDLOG_H
|
||||||
|
#error "spdlog.h must be included before this file."
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "spdlog/common.h"
|
#include "spdlog/common.h"
|
||||||
#include "spdlog/details/console_globals.h"
|
#include "spdlog/details/console_globals.h"
|
||||||
#include "spdlog/details/null_mutex.h"
|
#include "spdlog/details/null_mutex.h"
|
||||||
@ -61,7 +65,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 +89,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);
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
// spdlog main header file.
|
// spdlog main header file.
|
||||||
// see example.cpp for usage example
|
// see example.cpp for usage example
|
||||||
|
|
||||||
|
#ifndef SPDLOG_H
|
||||||
|
#define SPDLOG_H
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "spdlog/common.h"
|
#include "spdlog/common.h"
|
||||||
@ -23,12 +25,11 @@ namespace spdlog {
|
|||||||
struct synchronous_factory
|
struct synchronous_factory
|
||||||
{
|
{
|
||||||
template<typename Sink, typename... SinkArgs>
|
template<typename Sink, typename... SinkArgs>
|
||||||
|
|
||||||
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... args)
|
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&... args)
|
||||||
{
|
{
|
||||||
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
||||||
auto new_logger = std::make_shared<logger>(std::move(logger_name), std::move(sink));
|
auto new_logger = std::make_shared<logger>(std::move(logger_name), std::move(sink));
|
||||||
details::registry::instance().register_and_init(new_logger);
|
details::registry::instance().initialize_logger(new_logger);
|
||||||
return new_logger;
|
return new_logger;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -124,38 +125,242 @@ inline void shutdown()
|
|||||||
details::registry::instance().shutdown();
|
details::registry::instance().shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
// Automatic registration of loggers when using spdlog::create() or spdlog::create_async
|
||||||
//
|
inline void set_automatic_registration(bool automatic_registation)
|
||||||
// Trace & Debug can be switched on/off at compile time for zero cost debug
|
{
|
||||||
// statements.
|
details::registry::instance().set_automatic_registration(automatic_registation);
|
||||||
// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in tweakme.h to enable.
|
}
|
||||||
// SPDLOG_TRACE(..) will also print current file and line.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
// spdlog::set_level(spdlog::level::trace);
|
|
||||||
// SPDLOG_TRACE(my_logger, "some trace message");
|
|
||||||
// SPDLOG_TRACE(my_logger, "another trace message {} {}", 1, 2);
|
|
||||||
// SPDLOG_DEBUG(my_logger, "some debug message {} {}", 3, 4);
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
#ifdef SPDLOG_TRACE_ON
|
// API for using default logger (stdout_color_mt),
|
||||||
#define SPDLOG_STR_H(x) #x
|
// e.g: spdlog::info("Message {}", 1);
|
||||||
#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x)
|
//
|
||||||
#ifdef _MSC_VER
|
// The default logger object can be accessed using the spdlog::default_logger():
|
||||||
#define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__)
|
// For example, to add another sink to it:
|
||||||
#else
|
// spdlog::default_logger()->sinks()->push_back(some_sink);
|
||||||
#define SPDLOG_TRACE(logger, ...) \
|
//
|
||||||
logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ]" \
|
// The default logger can replaced using spdlog::set_default_logger(new_logger).
|
||||||
" " __VA_ARGS__)
|
// For example, to replace it with a file logger.
|
||||||
#endif
|
//
|
||||||
#else
|
// IMPORTANT:
|
||||||
#define SPDLOG_TRACE(logger, ...) (void)0
|
// The default API is thread safe (for _mt loggers), but:
|
||||||
#endif
|
// set_default_logger() *should not* be used concurrently with the default API.
|
||||||
|
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
|
||||||
|
|
||||||
#ifdef SPDLOG_DEBUG_ON
|
inline std::shared_ptr<spdlog::logger> default_logger()
|
||||||
#define SPDLOG_DEBUG(logger, ...) logger->debug(__VA_ARGS__)
|
{
|
||||||
#else
|
return details::registry::instance().default_logger();
|
||||||
#define SPDLOG_DEBUG(logger, ...) (void)0
|
}
|
||||||
#endif
|
|
||||||
|
inline spdlog::logger *default_logger_raw()
|
||||||
|
{
|
||||||
|
return details::registry::instance().get_default_raw();
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void set_default_logger(std::shared_ptr<spdlog::logger> default_logger)
|
||||||
|
{
|
||||||
|
details::registry::instance().set_default_logger(std::move(default_logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void log(source_loc source, level::level_enum lvl, const char *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->log(source, lvl, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void log(level::level_enum lvl, const char *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->log(source_loc{}, lvl, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void trace(const char *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->trace(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void debug(const char *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->debug(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void info(const char *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->info(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void warn(const char *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->warn(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void error(const char *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->error(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void critical(const char *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->critical(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void log(level::level_enum lvl, const T &msg)
|
||||||
|
{
|
||||||
|
default_logger_raw()->log(lvl, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void trace(const T &msg)
|
||||||
|
{
|
||||||
|
default_logger_raw()->trace(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void debug(const T &msg)
|
||||||
|
{
|
||||||
|
default_logger_raw()->debug(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void info(const T &msg)
|
||||||
|
{
|
||||||
|
default_logger_raw()->info(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void warn(const T &msg)
|
||||||
|
{
|
||||||
|
default_logger_raw()->warn(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void error(const T &msg)
|
||||||
|
{
|
||||||
|
default_logger_raw()->error(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void critical(const T &msg)
|
||||||
|
{
|
||||||
|
default_logger_raw()->critical(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
|
template<typename... Args>
|
||||||
|
inline void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->log(lvl, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void trace(const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->trace(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void debug(const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->debug(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void info(const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->info(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void warn(const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->warn(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void error(const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->error(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void critical(const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
default_logger_raw()->critical(fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
|
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
|
//
|
||||||
|
// enable/disable log calls at compile time according to global level.
|
||||||
|
//
|
||||||
|
// define SPDLOG_ACTIVE_LEVEL to one of those (before including spdlog.h):
|
||||||
|
// SPDLOG_LEVEL_TRACE,
|
||||||
|
// SPDLOG_LEVEL_DEBUG,
|
||||||
|
// SPDLOG_LEVEL_INFO,
|
||||||
|
// SPDLOG_LEVEL_WARN,
|
||||||
|
// SPDLOG_LEVEL_ERROR,
|
||||||
|
// SPDLOG_LEVEL_CRITICAL,
|
||||||
|
// SPDLOG_LEVEL_OFF
|
||||||
|
//
|
||||||
|
|
||||||
|
#define SPDLOG_LOGGER_CALL(logger, level, ...) \
|
||||||
|
if (logger->should_log(level)) \
|
||||||
|
logger->log(spdlog::source_loc{SPDLOG_FILE_BASENAME(__FILE__), __LINE__, SPDLOG_FUNCTION}, level, __VA_ARGS__)
|
||||||
|
|
||||||
|
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_TRACE
|
||||||
|
#define SPDLOG_LOGGER_TRACE(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::trace, __VA_ARGS__)
|
||||||
|
#define SPDLOG_TRACE(...) SPDLOG_LOGGER_TRACE(spdlog::default_logger_raw(), __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define SPDLOG_LOGGER_TRACE(logger, ...) (void)0
|
||||||
|
#define SPDLOG_TRACE(...) (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_DEBUG
|
||||||
|
#define SPDLOG_LOGGER_DEBUG(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::debug, __VA_ARGS__)
|
||||||
|
#define SPDLOG_DEBUG(...) SPDLOG_LOGGER_DEBUG(spdlog::default_logger_raw(), __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define SPDLOG_LOGGER_DEBUG(logger, ...) (void)0
|
||||||
|
#define SPDLOG_DEBUG(...) (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_INFO
|
||||||
|
#define SPDLOG_LOGGER_INFO(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::info, __VA_ARGS__)
|
||||||
|
#define SPDLOG_INFO(...) SPDLOG_LOGGER_INFO(spdlog::default_logger_raw(), __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define SPDLOG_LOGGER_INFO(logger, ...) (void)0
|
||||||
|
#define SPDLOG_INFO(...) (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_WARN
|
||||||
|
#define SPDLOG_LOGGER_WARN(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::warn, __VA_ARGS__)
|
||||||
|
#define SPDLOG_WARN(...) SPDLOG_LOGGER_WARN(spdlog::default_logger_raw(), __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define SPDLOG_LOGGER_WARN(logger, ...) (void)0
|
||||||
|
#define SPDLOG_WARN(...) (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_ERROR
|
||||||
|
#define SPDLOG_LOGGER_ERROR(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::err, __VA_ARGS__)
|
||||||
|
#define SPDLOG_ERROR(...) SPDLOG_LOGGER_ERROR(spdlog::default_logger_raw(), __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define SPDLOG_LOGGER_ERROR(logger, ...) (void)0
|
||||||
|
#define SPDLOG_ERROR(...) (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if SPDLOG_ACTIVE_LEVEL <= SPDLOG_LEVEL_CRITICAL
|
||||||
|
#define SPDLOG_LOGGER_CRITICAL(logger, ...) SPDLOG_LOGGER_CALL(logger, spdlog::level::critical, __VA_ARGS__)
|
||||||
|
#define SPDLOG_CRITICAL(...) SPDLOG_LOGGER_CRITICAL(spdlog::default_logger_raw(), __VA_ARGS__)
|
||||||
|
#else
|
||||||
|
#define SPDLOG_LOGGER_CRITICAL(logger, ...) (void)0
|
||||||
|
#define SPDLOG_CRITICAL(...) (void)0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // SPDLOG_H
|
||||||
|
@ -45,13 +45,12 @@
|
|||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Uncomment to prevent spdlog from caching thread ids in thread local storage.
|
// Uncomment to prevent spdlog from using thread local storage.
|
||||||
// By default spdlog saves thread ids in tls to gain a few micros for each call.
|
|
||||||
//
|
//
|
||||||
// WARNING: if your program forks, UNCOMMENT this flag to prevent undefined
|
// WARNING: if your program forks, UNCOMMENT this flag to prevent undefined
|
||||||
// thread ids in the children logs.
|
// thread ids in the children logs.
|
||||||
//
|
//
|
||||||
// #define SPDLOG_DISABLE_TID_CACHING
|
// #define SPDLOG_NO_TLS
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
@ -108,15 +107,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.
|
||||||
@ -130,3 +120,26 @@
|
|||||||
// #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING",
|
// #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING",
|
||||||
// "MY ERROR", "MY CRITICAL", "OFF" }
|
// "MY ERROR", "MY CRITICAL", "OFF" }
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Uncomment to disable default logger creation.
|
||||||
|
// This might save some (very) small initialization time if no default logger is needed.
|
||||||
|
//
|
||||||
|
// #define SPDLOG_DISABLE_DEFAULT_LOGGER
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Uncomment and set to compile time level with zero cost (default is INFO).
|
||||||
|
// Macros like SPDLOG_DEBUG(..), SPDLOG_INFO(..) will expand to empty statements if not enabled
|
||||||
|
//
|
||||||
|
// #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Uncomment (and change if desired) macro to use for function names.
|
||||||
|
// This is compiler dependent.
|
||||||
|
// __PRETTY_FUNCTION__ might be nicer in clang/gcc, and __FUNCTION__ in msvc.
|
||||||
|
// Defaults to __FUNCTION__ (should work on all compilers) if not defined.
|
||||||
|
//
|
||||||
|
// #define SPDLOG_FUNCTION __PRETTY_FUNCTION__
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
@ -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 3
|
||||||
#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)
|
||||||
|
@ -3,21 +3,21 @@ project(spdlog-utests CXX)
|
|||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
|
|
||||||
set(SPDLOG_UTESTS_SOURCES
|
set(SPDLOG_UTESTS_SOURCES
|
||||||
errors.cpp
|
test_errors.cpp
|
||||||
file_helper.cpp
|
test_file_helper.cpp
|
||||||
file_log.cpp
|
test_file_logging.cpp
|
||||||
test_misc.cpp
|
test_misc.cpp
|
||||||
test_pattern_formatter.cpp
|
test_pattern_formatter.cpp
|
||||||
test_async.cpp
|
test_async.cpp
|
||||||
includes.h
|
includes.h
|
||||||
registry.cpp
|
test_registry.cpp
|
||||||
test_macros.cpp
|
test_macros.cpp
|
||||||
utils.cpp
|
utils.cpp
|
||||||
utils.h
|
utils.h
|
||||||
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)
|
||||||
|
@ -10,9 +10,10 @@
|
|||||||
#include <ostream>
|
#include <ostream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#define SPDLOG_TRACE_ON
|
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
|
||||||
#define SPDLOG_DEBUG_ON
|
#define SPDLOG_ENABLE_MESSAGE_COUNTER
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
#include "spdlog/async.h"
|
#include "spdlog/async.h"
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
#include "spdlog/sinks/daily_file_sink.h"
|
#include "spdlog/sinks/daily_file_sink.h"
|
||||||
@ -20,4 +21,3 @@
|
|||||||
#include "spdlog/sinks/ostream_sink.h"
|
#include "spdlog/sinks/ostream_sink.h"
|
||||||
#include "spdlog/sinks/rotating_file_sink.h"
|
#include "spdlog/sinks/rotating_file_sink.h"
|
||||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
|
@ -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");
|
@ -10,7 +10,6 @@ static const std::string target_filename = "logs/file_helper_test.txt";
|
|||||||
|
|
||||||
static void write_with_helper(file_helper &helper, size_t howmany)
|
static void write_with_helper(file_helper &helper, size_t howmany)
|
||||||
{
|
{
|
||||||
log_msg msg;
|
|
||||||
fmt::memory_buffer formatted;
|
fmt::memory_buffer formatted;
|
||||||
fmt::format_to(formatted, "{}", std::string(howmany, '1'));
|
fmt::format_to(formatted, "{}", std::string(howmany, '1'));
|
||||||
helper.write(formatted);
|
helper.write(formatted);
|
||||||
@ -82,12 +81,12 @@ static void test_split_ext(const char *fname, const char *expect_base, const cha
|
|||||||
std::replace(expected_base.begin(), expected_base.end(), '/', '\\');
|
std::replace(expected_base.begin(), expected_base.end(), '/', '\\');
|
||||||
#endif
|
#endif
|
||||||
spdlog::filename_t basename, ext;
|
spdlog::filename_t basename, ext;
|
||||||
std::tie(basename, ext) = file_helper::split_by_extenstion(filename);
|
std::tie(basename, ext) = file_helper::split_by_extension(filename);
|
||||||
REQUIRE(basename == expected_base);
|
REQUIRE(basename == expected_base);
|
||||||
REQUIRE(ext == expected_ext);
|
REQUIRE(ext == expected_ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("file_helper_split_by_extenstion", "[file_helper::split_by_extenstion()]]")
|
TEST_CASE("file_helper_split_by_extension", "[file_helper::split_by_extension()]]")
|
||||||
{
|
{
|
||||||
test_split_ext("mylog.txt", "mylog", ".txt");
|
test_split_ext("mylog.txt", "mylog", ".txt");
|
||||||
test_split_ext(".mylog.txt", ".mylog", ".txt");
|
test_split_ext(".mylog.txt", ".mylog", ".txt");
|
@ -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, true);
|
auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 1, true);
|
||||||
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;
|
75
tests/test_fmt_helper.cpp
Normal file
75
tests/test_fmt_helper.cpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
|
||||||
|
#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(uint32_t 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_pad9(std::size_t n, const char *expected)
|
||||||
|
{
|
||||||
|
fmt::memory_buffer buf;
|
||||||
|
spdlog::details::fmt_helper::pad9(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_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");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("pad9", "[fmt_helper]")
|
||||||
|
{
|
||||||
|
test_pad9(0, "000000000");
|
||||||
|
test_pad9(3, "000000003");
|
||||||
|
test_pad9(23, "000000023");
|
||||||
|
test_pad9(123, "000000123");
|
||||||
|
test_pad9(1234, "000001234");
|
||||||
|
test_pad9(12345, "000012345");
|
||||||
|
test_pad9(123456, "000123456");
|
||||||
|
test_pad9(1234567, "001234567");
|
||||||
|
test_pad9(12345678, "012345678");
|
||||||
|
test_pad9(123456789, "123456789");
|
||||||
|
test_pad9(1234567891, "1234567891");
|
||||||
|
}
|
@ -4,8 +4,13 @@
|
|||||||
|
|
||||||
#include "includes.h"
|
#include "includes.h"
|
||||||
|
|
||||||
|
#if SPDLOG_ACTIVE_LEVEL != SPDLOG_LEVEL_DEBUG
|
||||||
|
#error "Invalid SPDLOG_ACTIVE_LEVEL in test. Should be SPDLOG_LEVEL_DEBUG"
|
||||||
|
#endif
|
||||||
|
|
||||||
TEST_CASE("debug and trace w/o format string", "[macros]]")
|
TEST_CASE("debug and trace w/o format string", "[macros]]")
|
||||||
{
|
{
|
||||||
|
|
||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
std::string filename = "logs/simple_log";
|
std::string filename = "logs/simple_log";
|
||||||
|
|
||||||
@ -13,28 +18,24 @@ TEST_CASE("debug and trace w/o format string", "[macros]]")
|
|||||||
logger->set_pattern("%v");
|
logger->set_pattern("%v");
|
||||||
logger->set_level(spdlog::level::trace);
|
logger->set_level(spdlog::level::trace);
|
||||||
|
|
||||||
SPDLOG_TRACE(logger, "Test message 1");
|
SPDLOG_LOGGER_TRACE(logger, "Test message 1");
|
||||||
SPDLOG_DEBUG(logger, "Test message 2");
|
SPDLOG_LOGGER_DEBUG(logger, "Test message 2");
|
||||||
logger->flush();
|
logger->flush();
|
||||||
|
|
||||||
REQUIRE(ends_with(file_contents(filename), "Test message 2\n"));
|
REQUIRE(ends_with(file_contents(filename), "Test message 2\n"));
|
||||||
REQUIRE(count_lines(filename) == 2);
|
REQUIRE(count_lines(filename) == 1);
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("debug and trace with format strings", "[macros]]")
|
spdlog::set_default_logger(logger);
|
||||||
{
|
|
||||||
prepare_logdir();
|
|
||||||
std::string filename = "logs/simple_log";
|
|
||||||
|
|
||||||
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename);
|
SPDLOG_TRACE("Test message 3");
|
||||||
logger->set_pattern("%v");
|
SPDLOG_DEBUG("Test message {}", 4);
|
||||||
logger->set_level(spdlog::level::trace);
|
|
||||||
|
|
||||||
SPDLOG_TRACE(logger, "Test message {}", 1);
|
|
||||||
// SPDLOG_DEBUG(logger, "Test message 2");
|
|
||||||
SPDLOG_DEBUG(logger, "Test message {}", 222);
|
|
||||||
logger->flush();
|
logger->flush();
|
||||||
|
|
||||||
REQUIRE(ends_with(file_contents(filename), "Test message 222\n"));
|
REQUIRE(ends_with(file_contents(filename), "Test message 4\n"));
|
||||||
REQUIRE(count_lines(filename) == 2);
|
REQUIRE(count_lines(filename) == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("disable param evaluation", "[macros]")
|
||||||
|
{
|
||||||
|
SPDLOG_TRACE("Test message {}", throw std::runtime_error("Should not be evaluated"));
|
||||||
|
}
|
||||||
|
@ -43,15 +43,15 @@ TEST_CASE("log_levels", "[log_levels]")
|
|||||||
REQUIRE(log_info("Hello", spdlog::level::trace) == "Hello");
|
REQUIRE(log_info("Hello", spdlog::level::trace) == "Hello");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("to_c_str", "[convert_to_c_str]")
|
TEST_CASE("level_to_string_view", "[convert_to_string_view")
|
||||||
{
|
{
|
||||||
REQUIRE(std::string(spdlog::level::to_c_str(spdlog::level::trace)) == "trace");
|
REQUIRE(spdlog::level::to_string_view(spdlog::level::trace) == "trace");
|
||||||
REQUIRE(std::string(spdlog::level::to_c_str(spdlog::level::debug)) == "debug");
|
REQUIRE(spdlog::level::to_string_view(spdlog::level::debug) == "debug");
|
||||||
REQUIRE(std::string(spdlog::level::to_c_str(spdlog::level::info)) == "info");
|
REQUIRE(spdlog::level::to_string_view(spdlog::level::info) == "info");
|
||||||
REQUIRE(std::string(spdlog::level::to_c_str(spdlog::level::warn)) == "warning");
|
REQUIRE(spdlog::level::to_string_view(spdlog::level::warn) == "warning");
|
||||||
REQUIRE(std::string(spdlog::level::to_c_str(spdlog::level::err)) == "error");
|
REQUIRE(spdlog::level::to_string_view(spdlog::level::err) == "error");
|
||||||
REQUIRE(std::string(spdlog::level::to_c_str(spdlog::level::critical)) == "critical");
|
REQUIRE(spdlog::level::to_string_view(spdlog::level::critical) == "critical");
|
||||||
REQUIRE(std::string(spdlog::level::to_c_str(spdlog::level::off)) == "off");
|
REQUIRE(spdlog::level::to_string_view(spdlog::level::off) == "off");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("to_short_c_str", "[convert_to_short_c_str]")
|
TEST_CASE("to_short_c_str", "[convert_to_short_c_str]")
|
||||||
@ -86,13 +86,13 @@ TEST_CASE("periodic flush", "[periodic_flush]")
|
|||||||
auto test_sink = std::static_pointer_cast<sinks::test_sink_mt>(logger->sinks()[0]);
|
auto test_sink = std::static_pointer_cast<sinks::test_sink_mt>(logger->sinks()[0]);
|
||||||
|
|
||||||
spdlog::flush_every(std::chrono::seconds(1));
|
spdlog::flush_every(std::chrono::seconds(1));
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1100));
|
std::this_thread::sleep_for(std::chrono::milliseconds(1250));
|
||||||
REQUIRE(test_sink->flush_counter() == 1);
|
REQUIRE(test_sink->flush_counter() == 1);
|
||||||
spdlog::flush_every(std::chrono::seconds(0));
|
spdlog::flush_every(std::chrono::seconds(0));
|
||||||
spdlog::drop_all();
|
spdlog::drop_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("clone", "[clone]")
|
TEST_CASE("clone-logger", "[clone]")
|
||||||
{
|
{
|
||||||
using namespace spdlog;
|
using namespace spdlog;
|
||||||
|
|
||||||
@ -134,3 +134,100 @@ TEST_CASE("clone async", "[clone]")
|
|||||||
|
|
||||||
spdlog::drop_all();
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("default logger API", "[default logger]")
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
|
||||||
|
|
||||||
|
spdlog::set_default_logger(std::make_shared<spdlog::logger>("oss", oss_sink));
|
||||||
|
spdlog::set_pattern("*** %v");
|
||||||
|
|
||||||
|
spdlog::default_logger()->set_level(spdlog::level::trace);
|
||||||
|
spdlog::trace("hello trace");
|
||||||
|
REQUIRE(oss.str() == "*** hello trace" + std::string(spdlog::details::os::default_eol));
|
||||||
|
|
||||||
|
oss.str("");
|
||||||
|
spdlog::debug("hello debug");
|
||||||
|
REQUIRE(oss.str() == "*** hello debug" + std::string(spdlog::details::os::default_eol));
|
||||||
|
|
||||||
|
oss.str("");
|
||||||
|
spdlog::info("Hello");
|
||||||
|
REQUIRE(oss.str() == "*** Hello" + std::string(spdlog::details::os::default_eol));
|
||||||
|
|
||||||
|
oss.str("");
|
||||||
|
spdlog::warn("Hello again {}", 2);
|
||||||
|
REQUIRE(oss.str() == "*** Hello again 2" + std::string(spdlog::details::os::default_eol));
|
||||||
|
|
||||||
|
oss.str("");
|
||||||
|
spdlog::error(123);
|
||||||
|
REQUIRE(oss.str() == "*** 123" + std::string(spdlog::details::os::default_eol));
|
||||||
|
|
||||||
|
oss.str("");
|
||||||
|
spdlog::critical(std::string("some string"));
|
||||||
|
REQUIRE(oss.str() == "*** some string" + std::string(spdlog::details::os::default_eol));
|
||||||
|
|
||||||
|
oss.str("");
|
||||||
|
spdlog::set_level(spdlog::level::info);
|
||||||
|
spdlog::debug("should not be logged");
|
||||||
|
REQUIRE(oss.str().empty());
|
||||||
|
spdlog::drop_all();
|
||||||
|
spdlog::set_pattern("%v");
|
||||||
|
}
|
||||||
|
@ -29,7 +29,7 @@ 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(100);
|
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;
|
||||||
@ -40,7 +40,7 @@ TEST_CASE("dequeue-empty-wait", "[mpmc_blocking_q]")
|
|||||||
REQUIRE(rv == false);
|
REQUIRE(rv == false);
|
||||||
|
|
||||||
INFO("Delta " << delta_ms.count() << " millis");
|
INFO("Delta " << delta_ms.count() << " millis");
|
||||||
REQUIRE(delta_ms >= wait_ms);
|
REQUIRE(delta_ms >= wait_ms - tolerance_wait);
|
||||||
REQUIRE(delta_ms <= wait_ms + tolerance_wait);
|
REQUIRE(delta_ms <= wait_ms + tolerance_wait);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,9 +60,12 @@ TEST_CASE("date MM/DD/YY ", "[pattern_formatter]")
|
|||||||
TEST_CASE("color range test1", "[pattern_formatter]")
|
TEST_CASE("color range test1", "[pattern_formatter]")
|
||||||
{
|
{
|
||||||
auto formatter = std::make_shared<spdlog::pattern_formatter>("%^%v%$", spdlog::pattern_time_type::local, "\n");
|
auto formatter = std::make_shared<spdlog::pattern_formatter>("%^%v%$", spdlog::pattern_time_type::local, "\n");
|
||||||
spdlog::details::log_msg msg;
|
|
||||||
fmt::format_to(msg.raw, "Hello");
|
fmt::memory_buffer buf;
|
||||||
|
fmt::format_to(buf, "Hello");
|
||||||
fmt::memory_buffer formatted;
|
fmt::memory_buffer formatted;
|
||||||
|
std::string logger_name = "test";
|
||||||
|
spdlog::details::log_msg msg(&logger_name, spdlog::level::info, spdlog::string_view_t(buf.data(), buf.size()));
|
||||||
formatter->format(msg, formatted);
|
formatter->format(msg, formatted);
|
||||||
REQUIRE(msg.color_range_start == 0);
|
REQUIRE(msg.color_range_start == 0);
|
||||||
REQUIRE(msg.color_range_end == 5);
|
REQUIRE(msg.color_range_end == 5);
|
||||||
@ -75,7 +75,8 @@ TEST_CASE("color range test1", "[pattern_formatter]")
|
|||||||
TEST_CASE("color range test2", "[pattern_formatter]")
|
TEST_CASE("color range test2", "[pattern_formatter]")
|
||||||
{
|
{
|
||||||
auto formatter = std::make_shared<spdlog::pattern_formatter>("%^%$", spdlog::pattern_time_type::local, "\n");
|
auto formatter = std::make_shared<spdlog::pattern_formatter>("%^%$", spdlog::pattern_time_type::local, "\n");
|
||||||
spdlog::details::log_msg msg;
|
std::string logger_name = "test";
|
||||||
|
spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "");
|
||||||
fmt::memory_buffer formatted;
|
fmt::memory_buffer formatted;
|
||||||
formatter->format(msg, formatted);
|
formatter->format(msg, formatted);
|
||||||
REQUIRE(msg.color_range_start == 0);
|
REQUIRE(msg.color_range_start == 0);
|
||||||
@ -86,7 +87,8 @@ TEST_CASE("color range test2", "[pattern_formatter]")
|
|||||||
TEST_CASE("color range test3", "[pattern_formatter]")
|
TEST_CASE("color range test3", "[pattern_formatter]")
|
||||||
{
|
{
|
||||||
auto formatter = std::make_shared<spdlog::pattern_formatter>("%^***%$");
|
auto formatter = std::make_shared<spdlog::pattern_formatter>("%^***%$");
|
||||||
spdlog::details::log_msg msg;
|
std::string logger_name = "test";
|
||||||
|
spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "ignored");
|
||||||
fmt::memory_buffer formatted;
|
fmt::memory_buffer formatted;
|
||||||
formatter->format(msg, formatted);
|
formatter->format(msg, formatted);
|
||||||
REQUIRE(msg.color_range_start == 0);
|
REQUIRE(msg.color_range_start == 0);
|
||||||
@ -96,8 +98,9 @@ TEST_CASE("color range test3", "[pattern_formatter]")
|
|||||||
TEST_CASE("color range test4", "[pattern_formatter]")
|
TEST_CASE("color range test4", "[pattern_formatter]")
|
||||||
{
|
{
|
||||||
auto formatter = std::make_shared<spdlog::pattern_formatter>("XX%^YYY%$", spdlog::pattern_time_type::local, "\n");
|
auto formatter = std::make_shared<spdlog::pattern_formatter>("XX%^YYY%$", spdlog::pattern_time_type::local, "\n");
|
||||||
spdlog::details::log_msg msg;
|
std::string logger_name = "test";
|
||||||
fmt::format_to(msg.raw, "ignored");
|
spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "ignored");
|
||||||
|
|
||||||
fmt::memory_buffer formatted;
|
fmt::memory_buffer formatted;
|
||||||
formatter->format(msg, formatted);
|
formatter->format(msg, formatted);
|
||||||
REQUIRE(msg.color_range_start == 2);
|
REQUIRE(msg.color_range_start == 2);
|
||||||
@ -108,7 +111,8 @@ TEST_CASE("color range test4", "[pattern_formatter]")
|
|||||||
TEST_CASE("color range test5", "[pattern_formatter]")
|
TEST_CASE("color range test5", "[pattern_formatter]")
|
||||||
{
|
{
|
||||||
auto formatter = std::make_shared<spdlog::pattern_formatter>("**%^");
|
auto formatter = std::make_shared<spdlog::pattern_formatter>("**%^");
|
||||||
spdlog::details::log_msg msg;
|
std::string logger_name = "test";
|
||||||
|
spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "ignored");
|
||||||
fmt::memory_buffer formatted;
|
fmt::memory_buffer formatted;
|
||||||
formatter->format(msg, formatted);
|
formatter->format(msg, formatted);
|
||||||
REQUIRE(msg.color_range_start == 2);
|
REQUIRE(msg.color_range_start == 2);
|
||||||
@ -118,9 +122,134 @@ TEST_CASE("color range test5", "[pattern_formatter]")
|
|||||||
TEST_CASE("color range test6", "[pattern_formatter]")
|
TEST_CASE("color range test6", "[pattern_formatter]")
|
||||||
{
|
{
|
||||||
auto formatter = std::make_shared<spdlog::pattern_formatter>("**%$");
|
auto formatter = std::make_shared<spdlog::pattern_formatter>("**%$");
|
||||||
spdlog::details::log_msg msg;
|
std::string logger_name = "test";
|
||||||
|
spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "ignored");
|
||||||
fmt::memory_buffer formatted;
|
fmt::memory_buffer formatted;
|
||||||
formatter->format(msg, formatted);
|
formatter->format(msg, formatted);
|
||||||
REQUIRE(msg.color_range_start == 0);
|
REQUIRE(msg.color_range_start == 0);
|
||||||
REQUIRE(msg.color_range_end == 2);
|
REQUIRE(msg.color_range_end == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test padding
|
||||||
|
//
|
||||||
|
|
||||||
|
TEST_CASE("level_left_padded", "[pattern_formatter]")
|
||||||
|
{
|
||||||
|
REQUIRE(log_to_str("Some message", "[%8l] %v", spdlog::pattern_time_type::local, "\n") == "[ info] Some message\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("level_right_padded", "[pattern_formatter]")
|
||||||
|
{
|
||||||
|
REQUIRE(log_to_str("Some message", "[%-8l] %v", spdlog::pattern_time_type::local, "\n") == "[info ] Some message\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("level_center_padded", "[pattern_formatter]")
|
||||||
|
{
|
||||||
|
REQUIRE(log_to_str("Some message", "[%=8l] %v", spdlog::pattern_time_type::local, "\n") == "[ info ] Some message\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("short level_left_padded", "[pattern_formatter]")
|
||||||
|
{
|
||||||
|
REQUIRE(log_to_str("Some message", "[%3L] %v", spdlog::pattern_time_type::local, "\n") == "[ I] Some message\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("short level_right_padded", "[pattern_formatter]")
|
||||||
|
{
|
||||||
|
REQUIRE(log_to_str("Some message", "[%-3L] %v", spdlog::pattern_time_type::local, "\n") == "[I ] Some message\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("short level_center_padded", "[pattern_formatter]")
|
||||||
|
{
|
||||||
|
REQUIRE(log_to_str("Some message", "[%=3L] %v", spdlog::pattern_time_type::local, "\n") == "[ I ] Some message\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("left_padded_short", "[pattern_formatter]")
|
||||||
|
{
|
||||||
|
REQUIRE(log_to_str("Some message", "[%3n] %v", spdlog::pattern_time_type::local, "\n") == "[pattern_tester] Some message\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("right_padded_short", "[pattern_formatter]")
|
||||||
|
{
|
||||||
|
REQUIRE(log_to_str("Some message", "[%-3n] %v", spdlog::pattern_time_type::local, "\n") == "[pattern_tester] Some message\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("center_padded_short", "[pattern_formatter]")
|
||||||
|
{
|
||||||
|
REQUIRE(log_to_str("Some message", "[%=3n] %v", spdlog::pattern_time_type::local, "\n") == "[pattern_tester] Some message\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("left_padded_huge", "[pattern_formatter]")
|
||||||
|
{
|
||||||
|
REQUIRE(
|
||||||
|
log_to_str("Some message", "[%-300n] %v", spdlog::pattern_time_type::local, "\n") ==
|
||||||
|
"[pattern_tester ]"
|
||||||
|
" Some message\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("left_padded_max", "[pattern_formatter]")
|
||||||
|
{
|
||||||
|
REQUIRE(
|
||||||
|
log_to_str("Some message", "[%-128n] %v", spdlog::pattern_time_type::local, "\n") ==
|
||||||
|
"[pattern_tester ]"
|
||||||
|
" Some message\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("clone-default-formatter", "[pattern_formatter]")
|
||||||
|
{
|
||||||
|
auto formatter_1 = std::make_shared<spdlog::pattern_formatter>();
|
||||||
|
auto formatter_2 = formatter_1->clone();
|
||||||
|
std::string logger_name = "test";
|
||||||
|
spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "some message");
|
||||||
|
|
||||||
|
fmt::memory_buffer formatted_1;
|
||||||
|
fmt::memory_buffer formatted_2;
|
||||||
|
formatter_1->format(msg, formatted_1);
|
||||||
|
formatter_2->format(msg, formatted_2);
|
||||||
|
|
||||||
|
REQUIRE(fmt::to_string(formatted_1) == fmt::to_string(formatted_2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("clone-default-formatter2", "[pattern_formatter]")
|
||||||
|
{
|
||||||
|
auto formatter_1 = std::make_shared<spdlog::pattern_formatter>("%+");
|
||||||
|
auto formatter_2 = formatter_1->clone();
|
||||||
|
std::string logger_name = "test";
|
||||||
|
spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "some message");
|
||||||
|
|
||||||
|
fmt::memory_buffer formatted_1;
|
||||||
|
fmt::memory_buffer formatted_2;
|
||||||
|
formatter_1->format(msg, formatted_1);
|
||||||
|
formatter_2->format(msg, formatted_2);
|
||||||
|
|
||||||
|
REQUIRE(fmt::to_string(formatted_1) == fmt::to_string(formatted_2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("clone-formatter", "[pattern_formatter]")
|
||||||
|
{
|
||||||
|
auto formatter_1 = std::make_shared<spdlog::pattern_formatter>("%D %X [%] [%n] %v");
|
||||||
|
auto formatter_2 = formatter_1->clone();
|
||||||
|
std::string logger_name = "test";
|
||||||
|
spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "some message");
|
||||||
|
|
||||||
|
fmt::memory_buffer formatted_1;
|
||||||
|
fmt::memory_buffer formatted_2;
|
||||||
|
formatter_1->format(msg, formatted_1);
|
||||||
|
formatter_2->format(msg, formatted_2);
|
||||||
|
REQUIRE(fmt::to_string(formatted_1) == fmt::to_string(formatted_2));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("clone-formatter-2", "[pattern_formatter]")
|
||||||
|
{
|
||||||
|
using spdlog::pattern_time_type;
|
||||||
|
auto formatter_1 = std::make_shared<spdlog::pattern_formatter>("%D %X [%] [%n] %v", pattern_time_type::utc, "xxxxxx\n");
|
||||||
|
auto formatter_2 = formatter_1->clone();
|
||||||
|
std::string logger_name = "test2";
|
||||||
|
spdlog::details::log_msg msg(&logger_name, spdlog::level::info, "some message");
|
||||||
|
|
||||||
|
fmt::memory_buffer formatted_1;
|
||||||
|
fmt::memory_buffer formatted_2;
|
||||||
|
formatter_1->format(msg, formatted_1);
|
||||||
|
formatter_2->format(msg, formatted_2);
|
||||||
|
REQUIRE(fmt::to_string(formatted_1) == fmt::to_string(formatted_2));
|
||||||
|
}
|
||||||
|
@ -12,8 +12,7 @@ TEST_CASE("register_drop", "[registry]")
|
|||||||
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", "[registry]")
|
||||||
"[registry]")
|
|
||||||
{
|
{
|
||||||
spdlog::drop_all();
|
spdlog::drop_all();
|
||||||
auto logger = std::make_shared<spdlog::logger>(tested_logger_name, std::make_shared<spdlog::sinks::null_sink_st>());
|
auto logger = std::make_shared<spdlog::logger>(tested_logger_name, std::make_shared<spdlog::sinks::null_sink_st>());
|
||||||
@ -23,8 +22,7 @@ TEST_CASE("explicit register"
|
|||||||
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", "[registry]")
|
||||||
"[registry]")
|
|
||||||
{
|
{
|
||||||
spdlog::drop_all();
|
spdlog::drop_all();
|
||||||
auto logger = std::make_shared<spdlog::logger>(tested_logger_name, std::make_shared<spdlog::sinks::null_sink_st>());
|
auto logger = std::make_shared<spdlog::logger>(tested_logger_name, std::make_shared<spdlog::sinks::null_sink_st>());
|
||||||
@ -45,8 +43,7 @@ TEST_CASE("apply_all"
|
|||||||
REQUIRE(counter == 1);
|
REQUIRE(counter == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("drop"
|
TEST_CASE("drop", "[registry]")
|
||||||
"[registry]")
|
|
||||||
{
|
{
|
||||||
spdlog::drop_all();
|
spdlog::drop_all();
|
||||||
spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name);
|
spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name);
|
||||||
@ -54,8 +51,15 @@ TEST_CASE("drop"
|
|||||||
REQUIRE_FALSE(spdlog::get(tested_logger_name));
|
REQUIRE_FALSE(spdlog::get(tested_logger_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("drop_all"
|
TEST_CASE("drop-default", "[registry]")
|
||||||
"[registry]")
|
{
|
||||||
|
spdlog::set_default_logger(spdlog::null_logger_st(tested_logger_name));
|
||||||
|
spdlog::drop(tested_logger_name);
|
||||||
|
REQUIRE_FALSE(spdlog::default_logger());
|
||||||
|
REQUIRE_FALSE(spdlog::get(tested_logger_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("drop_all", "[registry]")
|
||||||
{
|
{
|
||||||
spdlog::drop_all();
|
spdlog::drop_all();
|
||||||
spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name);
|
spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name);
|
||||||
@ -63,10 +67,10 @@ TEST_CASE("drop_all"
|
|||||||
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_name2));
|
REQUIRE_FALSE(spdlog::get(tested_logger_name2));
|
||||||
|
REQUIRE_FALSE(spdlog::default_logger());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("drop non existing"
|
TEST_CASE("drop non existing", "[registry]")
|
||||||
"[registry]")
|
|
||||||
{
|
{
|
||||||
spdlog::drop_all();
|
spdlog::drop_all();
|
||||||
spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name);
|
spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name);
|
||||||
@ -75,3 +79,36 @@ TEST_CASE("drop non existing"
|
|||||||
REQUIRE(spdlog::get(tested_logger_name));
|
REQUIRE(spdlog::get(tested_logger_name));
|
||||||
spdlog::drop_all();
|
spdlog::drop_all();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("default logger", "[registry]")
|
||||||
|
{
|
||||||
|
spdlog::drop_all();
|
||||||
|
spdlog::set_default_logger(spdlog::null_logger_st(tested_logger_name));
|
||||||
|
REQUIRE(spdlog::get(tested_logger_name) == spdlog::default_logger());
|
||||||
|
spdlog::drop_all();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("set_default_logger(nullptr)", "[registry]")
|
||||||
|
{
|
||||||
|
spdlog::set_default_logger(nullptr);
|
||||||
|
REQUIRE_FALSE(spdlog::default_logger());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("disable automatic registration", "[registry]")
|
||||||
|
{
|
||||||
|
// set some global parameters
|
||||||
|
spdlog::level::level_enum log_level = spdlog::level::level_enum::warn;
|
||||||
|
spdlog::set_level(log_level);
|
||||||
|
// but disable automatic registration
|
||||||
|
spdlog::set_automatic_registration(false);
|
||||||
|
auto logger1 = spdlog::create<spdlog::sinks::daily_file_sink_st>(tested_logger_name, "filename", 11, 59);
|
||||||
|
auto logger2 = spdlog::create_async<spdlog::sinks::stdout_color_sink_mt>(tested_logger_name2);
|
||||||
|
// loggers should not be part of the registry
|
||||||
|
REQUIRE_FALSE(spdlog::get(tested_logger_name));
|
||||||
|
REQUIRE_FALSE(spdlog::get(tested_logger_name2));
|
||||||
|
// but make sure they are still initialized according to global defaults
|
||||||
|
REQUIRE(logger1->level() == log_level);
|
||||||
|
REQUIRE(logger2->level() == log_level);
|
||||||
|
spdlog::set_level(spdlog::level::info);
|
||||||
|
spdlog::set_automatic_registration(true);
|
||||||
|
}
|
@ -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());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user