mirror of
https://github.com/gabime/spdlog.git
synced 2025-10-02 03:19:02 +08:00
Compare commits
119 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
012fe99ab1 | ||
![]() |
8ff5a3e096 | ||
![]() |
65317eb019 | ||
![]() |
e86f450428 | ||
![]() |
7b2776fdc7 | ||
![]() |
2a16d1d230 | ||
![]() |
53e1c9ab11 | ||
![]() |
410abc4626 | ||
![]() |
a2e28443f0 | ||
![]() |
c1af0a3f21 | ||
![]() |
bb5e1ee2f9 | ||
![]() |
3aee89c8fd | ||
![]() |
44e1f9f682 | ||
![]() |
37d76b961c | ||
![]() |
1305663d99 | ||
![]() |
8f4efe57a2 | ||
![]() |
0613dbc4a2 | ||
![]() |
0ed0d69368 | ||
![]() |
2ffbbee1f6 | ||
![]() |
b9d2f2537b | ||
![]() |
69dc173979 | ||
![]() |
ded8b5ebd4 | ||
![]() |
ed58ae9f98 | ||
![]() |
f7f790b4b3 | ||
![]() |
fe74c80992 | ||
![]() |
fa659bf7ad | ||
![]() |
9b41649601 | ||
![]() |
1b3438f5a5 | ||
![]() |
3eed64e5c4 | ||
![]() |
0fac33781d | ||
![]() |
3135b6a33d | ||
![]() |
2686ae2322 | ||
![]() |
a709e29586 | ||
![]() |
dd46579cb4 | ||
![]() |
f4b7210e7b | ||
![]() |
05a0b0d7b0 | ||
![]() |
c1f4d7506a | ||
![]() |
b6ba0be550 | ||
![]() |
23dfb4e2f9 | ||
![]() |
7a10e31982 | ||
![]() |
de89c4fd01 | ||
![]() |
5d4956d34b | ||
![]() |
42c5eb59c9 | ||
![]() |
09cc6e7754 | ||
![]() |
4a5bc41e89 | ||
![]() |
0ade18828d | ||
![]() |
91046e6ca4 | ||
![]() |
17e1ba8ae2 | ||
![]() |
c47c854f15 | ||
![]() |
e931866b35 | ||
![]() |
7828a065bf | ||
![]() |
3e689e700e | ||
![]() |
a9964afcf7 | ||
![]() |
95c19876c6 | ||
![]() |
5efccfa5e2 | ||
![]() |
89e737a258 | ||
![]() |
8fbc853b0d | ||
![]() |
2e008b319c | ||
![]() |
ff6e3c95f2 | ||
![]() |
7e9385405f | ||
![]() |
592ea36a86 | ||
![]() |
e059ebf99d | ||
![]() |
609480ed78 | ||
![]() |
4271185936 | ||
![]() |
aacae62591 | ||
![]() |
47cbf3828d | ||
![]() |
46d418164d | ||
![]() |
ede8d84884 | ||
![]() |
53d223b45f | ||
![]() |
ac35dd5a6f | ||
![]() |
9e19012cb0 | ||
![]() |
1234cda3b3 | ||
![]() |
710a0e3a45 | ||
![]() |
b7f24b2456 | ||
![]() |
fc594b551a | ||
![]() |
f39ccccc0c | ||
![]() |
f0a4ddd78b | ||
![]() |
c691769e46 | ||
![]() |
19dc30567e | ||
![]() |
a453bccff0 | ||
![]() |
aa2053a575 | ||
![]() |
3d8f71c4d2 | ||
![]() |
6aaaabbc4d | ||
![]() |
42c36f48ed | ||
![]() |
a5f4139102 | ||
![]() |
030d85a9b3 | ||
![]() |
adcfb7fb55 | ||
![]() |
cec365888a | ||
![]() |
55bfa8dd11 | ||
![]() |
e99759fe45 | ||
![]() |
17c6e6ee3f | ||
![]() |
7fff900a1a | ||
![]() |
c67974e4c8 | ||
![]() |
a36696e02e | ||
![]() |
9b80ca6c41 | ||
![]() |
22f514aabf | ||
![]() |
211478e13e | ||
![]() |
5e33a7e58b | ||
![]() |
b2e31721e8 | ||
![]() |
de0dbfa359 | ||
![]() |
f93459579f | ||
![]() |
2b81c40b90 | ||
![]() |
233e97c5e4 | ||
![]() |
fc1ce48dc7 | ||
![]() |
fd5562eebe | ||
![]() |
0695d9cb5f | ||
![]() |
456b24134d | ||
![]() |
f8ba24afee | ||
![]() |
eebb921c9f | ||
![]() |
e17ee87f38 | ||
![]() |
18e3f07f7d | ||
![]() |
9ce39a470f | ||
![]() |
23572369fc | ||
![]() |
01b350de96 | ||
![]() |
365e470a32 | ||
![]() |
a42b40656c | ||
![]() |
40160f2a57 | ||
![]() |
90b33b1552 | ||
![]() |
5567ed01e5 |
@@ -30,6 +30,11 @@ if(NOT CMAKE_CXX_STANDARD)
|
|||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# make sure __cplusplus is defined when using msvc
|
||||||
|
if(MSVC)
|
||||||
|
string(APPEND CMAKE_CXX_FLAGS " /Zc:__cplusplus")
|
||||||
|
endif()
|
||||||
|
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
|
||||||
if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN" OR CMAKE_SYSTEM_NAME MATCHES "MSYS")
|
if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN" OR CMAKE_SYSTEM_NAME MATCHES "MSYS")
|
||||||
@@ -100,6 +105,7 @@ option(
|
|||||||
SPDLOG_NO_ATOMIC_LEVELS
|
SPDLOG_NO_ATOMIC_LEVELS
|
||||||
"prevent spdlog from using of std::atomic log levels (use only if your code never modifies log levels concurrently"
|
"prevent spdlog from using of std::atomic log levels (use only if your code never modifies log levels concurrently"
|
||||||
OFF)
|
OFF)
|
||||||
|
option(SPDLOG_DISABLE_DEFAULT_LOGGER "Disable default logger creation" OFF)
|
||||||
|
|
||||||
# clang-tidy
|
# clang-tidy
|
||||||
if(${CMAKE_VERSION} VERSION_GREATER "3.5")
|
if(${CMAKE_VERSION} VERSION_GREATER "3.5")
|
||||||
@@ -123,7 +129,7 @@ if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
|||||||
list(APPEND SPDLOG_SRCS src/fmt.cpp)
|
list(APPEND SPDLOG_SRCS src/fmt.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(SPDLOG_BUILD_SHARED)
|
if(SPDLOG_BUILD_SHARED OR BUILD_SHARED_LIBS)
|
||||||
if(WIN32)
|
if(WIN32)
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY)
|
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY)
|
||||||
list(APPEND SPDLOG_SRCS ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
list(APPEND SPDLOG_SRCS ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
||||||
@@ -131,7 +137,8 @@ if(SPDLOG_BUILD_SHARED)
|
|||||||
add_library(spdlog SHARED ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
|
add_library(spdlog SHARED ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
|
||||||
target_compile_definitions(spdlog PUBLIC SPDLOG_SHARED_LIB)
|
target_compile_definitions(spdlog PUBLIC SPDLOG_SHARED_LIB)
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
target_compile_options(spdlog PUBLIC /wd4251 /wd4275)
|
target_compile_options(spdlog PUBLIC
|
||||||
|
$<$<AND:$<CXX_COMPILER_ID:MSVC>,$<NOT:$<COMPILE_LANGUAGE:CUDA>>>:/wd4251 /wd4275>)
|
||||||
endif()
|
endif()
|
||||||
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
||||||
target_compile_definitions(spdlog PRIVATE FMT_EXPORT PUBLIC FMT_SHARED)
|
target_compile_definitions(spdlog PRIVATE FMT_EXPORT PUBLIC FMT_SHARED)
|
||||||
@@ -171,7 +178,7 @@ target_link_libraries(spdlog_header_only INTERFACE Threads::Threads)
|
|||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO)
|
if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO)
|
||||||
if(NOT TARGET fmt::fmt)
|
if(NOT TARGET fmt::fmt)
|
||||||
find_package(fmt 5.3.0 REQUIRED)
|
find_package(fmt CONFIG REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
target_compile_definitions(spdlog PUBLIC SPDLOG_FMT_EXTERNAL)
|
target_compile_definitions(spdlog PUBLIC SPDLOG_FMT_EXTERNAL)
|
||||||
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FMT_EXTERNAL)
|
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FMT_EXTERNAL)
|
||||||
@@ -188,6 +195,14 @@ if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO)
|
|||||||
set(PKG_CONFIG_REQUIRES fmt) # add dependency to pkg-config
|
set(PKG_CONFIG_REQUIRES fmt) # add dependency to pkg-config
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------------------
|
||||||
|
# Add required libraries for Android CMake build
|
||||||
|
# ---------------------------------------------------------------------------------------
|
||||||
|
if (ANDROID)
|
||||||
|
target_link_libraries(spdlog PUBLIC log)
|
||||||
|
target_link_libraries(spdlog_header_only INTERFACE log)
|
||||||
|
endif ()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
# Misc definitions according to tweak options
|
# Misc definitions according to tweak options
|
||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
@@ -201,7 +216,8 @@ foreach(
|
|||||||
SPDLOG_PREVENT_CHILD_FD
|
SPDLOG_PREVENT_CHILD_FD
|
||||||
SPDLOG_NO_THREAD_ID
|
SPDLOG_NO_THREAD_ID
|
||||||
SPDLOG_NO_TLS
|
SPDLOG_NO_TLS
|
||||||
SPDLOG_NO_ATOMIC_LEVELS)
|
SPDLOG_NO_ATOMIC_LEVELS
|
||||||
|
SPDLOG_DISABLE_DEFAULT_LOGGER)
|
||||||
if(${SPDLOG_OPTION})
|
if(${SPDLOG_OPTION})
|
||||||
target_compile_definitions(spdlog PUBLIC ${SPDLOG_OPTION})
|
target_compile_definitions(spdlog PUBLIC ${SPDLOG_OPTION})
|
||||||
target_compile_definitions(spdlog_header_only INTERFACE ${SPDLOG_OPTION})
|
target_compile_definitions(spdlog_header_only INTERFACE ${SPDLOG_OPTION})
|
||||||
@@ -289,3 +305,4 @@ if(SPDLOG_INSTALL)
|
|||||||
# ---------------------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------------------
|
||||||
include(cmake/spdlogCPack.cmake)
|
include(cmake/spdlogCPack.cmake)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@@ -31,6 +31,8 @@ $ cmake .. && make -j
|
|||||||
* vcpkg: `vcpkg install spdlog`
|
* vcpkg: `vcpkg install spdlog`
|
||||||
* conan: `spdlog/[>=1.4.1]`
|
* conan: `spdlog/[>=1.4.1]`
|
||||||
* conda: `conda install -c conda-forge spdlog`
|
* conda: `conda install -c conda-forge spdlog`
|
||||||
|
* build2: ```depends: spdlog ^1.8.2```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
@@ -45,7 +47,8 @@ $ cmake .. && make -j
|
|||||||
* Daily log files.
|
* Daily log files.
|
||||||
* Console logging (colors supported).
|
* Console logging (colors supported).
|
||||||
* syslog.
|
* syslog.
|
||||||
* Windows debugger (```OutputDebugString(..)```)
|
* Windows event log.
|
||||||
|
* 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).
|
||||||
* Log filtering - log levels can be modified in runtime as well as in compile time.
|
* Log filtering - log levels can be modified in runtime as well as in compile time.
|
||||||
* Support for loading log levels from argv or from environment var.
|
* Support for loading log levels from argv or from environment var.
|
||||||
@@ -188,6 +191,7 @@ void stopwatch_example()
|
|||||||
// {:s} - don't separate each byte with space.
|
// {:s} - don't separate each byte with space.
|
||||||
// {:p} - don't print the position on each line start.
|
// {:p} - don't print the position on each line start.
|
||||||
// {:n} - don't split the output to lines.
|
// {:n} - don't split the output to lines.
|
||||||
|
// {:a} - show ASCII if :n is not set.
|
||||||
|
|
||||||
#include "spdlog/fmt/bin_to_hex.h"
|
#include "spdlog/fmt/bin_to_hex.h"
|
||||||
|
|
||||||
|
36
appveyor.yml
36
appveyor.yml
@@ -4,32 +4,52 @@ environment:
|
|||||||
matrix:
|
matrix:
|
||||||
- GENERATOR: '"Visual Studio 14 2015"'
|
- GENERATOR: '"Visual Studio 14 2015"'
|
||||||
BUILD_TYPE: Debug
|
BUILD_TYPE: Debug
|
||||||
WCHAR: 'OFF'
|
|
||||||
BUILD_SHARED: 'OFF'
|
BUILD_SHARED: 'OFF'
|
||||||
|
WCHAR: 'OFF'
|
||||||
|
WCHAR_FILES: 'OFF'
|
||||||
|
BUILD_EXAMPLE: 'ON'
|
||||||
- GENERATOR: '"Visual Studio 14 2015"'
|
- GENERATOR: '"Visual Studio 14 2015"'
|
||||||
BUILD_TYPE: Release
|
BUILD_TYPE: Release
|
||||||
WCHAR: 'ON'
|
|
||||||
BUILD_SHARED: 'OFF'
|
BUILD_SHARED: 'OFF'
|
||||||
|
WCHAR: 'ON'
|
||||||
|
WCHAR_FILES: 'OFF'
|
||||||
|
BUILD_EXAMPLE: 'ON'
|
||||||
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
||||||
BUILD_TYPE: Debug
|
BUILD_TYPE: Debug
|
||||||
WCHAR: 'ON'
|
|
||||||
BUILD_SHARED: 'OFF'
|
BUILD_SHARED: 'OFF'
|
||||||
|
WCHAR: 'ON'
|
||||||
|
WCHAR_FILES: 'OFF'
|
||||||
|
BUILD_EXAMPLE: 'ON'
|
||||||
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
||||||
BUILD_TYPE: Release
|
BUILD_TYPE: Release
|
||||||
WCHAR: 'ON'
|
|
||||||
BUILD_SHARED: 'OFF'
|
BUILD_SHARED: 'OFF'
|
||||||
|
WCHAR: 'ON'
|
||||||
|
WCHAR_FILES: 'OFF'
|
||||||
|
BUILD_EXAMPLE: 'ON'
|
||||||
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
||||||
BUILD_TYPE: Debug
|
BUILD_TYPE: Debug
|
||||||
|
BUILD_SHARED: 'OFF'
|
||||||
WCHAR: 'ON'
|
WCHAR: 'ON'
|
||||||
BUILD_SHARED: 'OFF'
|
WCHAR_FILES: 'OFF'
|
||||||
|
BUILD_EXAMPLE: 'ON'
|
||||||
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
||||||
BUILD_TYPE: Release
|
BUILD_TYPE: Release
|
||||||
WCHAR: 'OFF'
|
|
||||||
BUILD_SHARED: 'OFF'
|
BUILD_SHARED: 'OFF'
|
||||||
|
WCHAR: 'OFF'
|
||||||
|
WCHAR_FILES: 'OFF'
|
||||||
|
BUILD_EXAMPLE: 'ON'
|
||||||
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
||||||
BUILD_TYPE: Release
|
BUILD_TYPE: Release
|
||||||
WCHAR: 'OFF'
|
|
||||||
BUILD_SHARED: 'ON'
|
BUILD_SHARED: 'ON'
|
||||||
|
WCHAR: 'OFF'
|
||||||
|
WCHAR_FILES: 'OFF'
|
||||||
|
BUILD_EXAMPLE: 'ON'
|
||||||
|
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
||||||
|
BUILD_TYPE: Release
|
||||||
|
BUILD_SHARED: 'ON'
|
||||||
|
WCHAR: 'ON'
|
||||||
|
WCHAR_FILES: 'ON'
|
||||||
|
BUILD_EXAMPLE: 'OFF'
|
||||||
build_script:
|
build_script:
|
||||||
- cmd: >-
|
- cmd: >-
|
||||||
set
|
set
|
||||||
@@ -40,7 +60,7 @@ build_script:
|
|||||||
|
|
||||||
set PATH=%PATH%;C:\Program Files\Git\usr\bin
|
set PATH=%PATH%;C:\Program Files\Git\usr\bin
|
||||||
|
|
||||||
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DSPDLOG_WCHAR_SUPPORT=%WCHAR% -DSPDLOG_BUILD_SHARED=%BUILD_SHARED% -DSPDLOG_BUILD_EXAMPLE=ON -DSPDLOG_BUILD_EXAMPLE_HO=ON -DSPDLOG_BUILD_TESTS=ON -DSPDLOG_BUILD_TESTS_HO=OFF -DSPDLOG_BUILD_WARNINGS=ON
|
cmake -G %GENERATOR% -D CMAKE_BUILD_TYPE=%BUILD_TYPE% -D BUILD_SHARED_LIBS=%BUILD_SHARED% -D SPDLOG_WCHAR_SUPPORT=%WCHAR% -D SPDLOG_WCHAR_FILENAMES=%WCHAR_FILES% -D SPDLOG_BUILD_EXAMPLE=%BUILD_EXAMPLE% -D SPDLOG_BUILD_EXAMPLE_HO=%BUILD_EXAMPLE% -D SPDLOG_BUILD_TESTS=ON -D SPDLOG_BUILD_TESTS_HO=OFF -D SPDLOG_BUILD_WARNINGS=ON ..
|
||||||
|
|
||||||
cmake --build . --config %BUILD_TYPE%
|
cmake --build . --config %BUILD_TYPE%
|
||||||
|
|
||||||
|
@@ -9,7 +9,24 @@ if(NOT TARGET spdlog)
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
find_package(Threads REQUIRED)
|
||||||
find_package(benchmark CONFIG REQUIRED)
|
find_package(benchmark CONFIG)
|
||||||
|
if (NOT benchmark_FOUND)
|
||||||
|
message(STATUS "Using CMake Version ${CMAKE_VERSION}")
|
||||||
|
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.11.0")
|
||||||
|
# User can fetch googlebenchmark
|
||||||
|
message(STATUS "Downloading GoogleBenchmark")
|
||||||
|
include(FetchContent)
|
||||||
|
set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE INTERNAL "")
|
||||||
|
# Do not build and run googlebenchmark tests
|
||||||
|
FetchContent_Declare(googlebenchmark
|
||||||
|
GIT_REPOSITORY https://github.com/google/benchmark.git
|
||||||
|
GIT_TAG v1.5.2)
|
||||||
|
|
||||||
|
FetchContent_MakeAvailable(googlebenchmark)
|
||||||
|
else()
|
||||||
|
message(FATAL_ERROR "GoogleBenchmark is missing. Use CMake >= 3.11 or download it")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
add_executable(bench bench.cpp)
|
add_executable(bench bench.cpp)
|
||||||
spdlog_enable_warnings(bench)
|
spdlog_enable_warnings(bench)
|
||||||
|
@@ -9,7 +9,12 @@
|
|||||||
#include "spdlog/spdlog.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"
|
||||||
|
|
||||||
|
#ifdef SPDLOG_FMT_EXTERNAL
|
||||||
|
#include <fmt/locale.h>
|
||||||
|
#else
|
||||||
#include "spdlog/fmt/bundled/locale.h"
|
#include "spdlog/fmt/bundled/locale.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
@@ -11,7 +11,12 @@
|
|||||||
#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"
|
||||||
|
|
||||||
|
#ifdef SPDLOG_FMT_EXTERNAL
|
||||||
|
#include <fmt/locale.h>
|
||||||
|
#else
|
||||||
#include "spdlog/fmt/bundled/locale.h"
|
#include "spdlog/fmt/bundled/locale.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
|
@@ -34,14 +34,14 @@ void bench_formatters()
|
|||||||
for (auto &flag : all_flags)
|
for (auto &flag : all_flags)
|
||||||
{
|
{
|
||||||
auto pattern = std::string("%") + flag;
|
auto pattern = std::string("%") + flag;
|
||||||
benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern);
|
||||||
|
|
||||||
// pattern = std::string("%16") + flag;
|
// pattern = std::string("%16") + flag;
|
||||||
// benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
// benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern);
|
||||||
//
|
//
|
||||||
// // bench center padding
|
// // bench center padding
|
||||||
// pattern = std::string("%=16") + flag;
|
// pattern = std::string("%=16") + flag;
|
||||||
// benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
// benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
// complex patterns
|
// complex patterns
|
||||||
@@ -52,7 +52,7 @@ void bench_formatters()
|
|||||||
};
|
};
|
||||||
for (auto &pattern : patterns)
|
for (auto &pattern : patterns)
|
||||||
{
|
{
|
||||||
benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern)->Iterations(2500000);
|
benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern)->Iterations(2500000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
|
benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern);
|
||||||
}
|
}
|
||||||
benchmark::Initialize(&argc, argv);
|
benchmark::Initialize(&argc, argv);
|
||||||
benchmark::RunSpecifiedBenchmarks();
|
benchmark::RunSpecifiedBenchmarks();
|
||||||
|
@@ -18,23 +18,37 @@ set(CPACK_PACKAGE_RELOCATABLE ON CACHE BOOL "Build relocatable package")
|
|||||||
|
|
||||||
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
|
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
|
||||||
set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries")
|
set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_SECTION "libs")
|
||||||
set(CPACK_RPM_PACKAGE_URL ${CPACK_PROJECT_URL})
|
set(CPACK_RPM_PACKAGE_URL ${CPACK_PROJECT_URL})
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE ${CPACK_PROJECT_URL})
|
||||||
set(CPACK_RPM_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.")
|
set(CPACK_RPM_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.")
|
||||||
|
|
||||||
if(CPACK_PACKAGE_NAME)
|
if(CPACK_PACKAGE_NAME)
|
||||||
set(CPACK_RPM_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
|
set(CPACK_RPM_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
|
||||||
|
set(CPACK_DEBIAN_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
|
||||||
else()
|
else()
|
||||||
set(CPACK_RPM_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}")
|
set(CPACK_RPM_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}")
|
||||||
|
set(CPACK_DEBIAN_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}")
|
||||||
|
set(CPACK_RPM_PACKAGE_NAME "${PROJECT_NAME}")
|
||||||
|
set(CPACK_DEBIAN_PACKAGE_NAME "${PROJECT_NAME}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(CPACK_RPM_PACKAGE_RELEASE)
|
if(CPACK_RPM_PACKAGE_RELEASE)
|
||||||
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}-${CPACK_RPM_PACKAGE_RELEASE}")
|
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}-${CPACK_RPM_PACKAGE_RELEASE}")
|
||||||
endif()
|
endif()
|
||||||
|
if(CPACK_DEBIAN_PACKAGE_RELEASE)
|
||||||
|
set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}-${CPACK_DEBIAN_PACKAGE_RELEASE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
if(CPACK_RPM_PACKAGE_ARCHITECTURE)
|
if(CPACK_RPM_PACKAGE_ARCHITECTURE)
|
||||||
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.${CPACK_RPM_PACKAGE_ARCHITECTURE}")
|
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.${CPACK_RPM_PACKAGE_ARCHITECTURE}")
|
||||||
endif()
|
endif()
|
||||||
|
if(CPACK_DEBIAN_PACKAGE_ARCHITECTURE)
|
||||||
|
set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
|
||||||
|
endif()
|
||||||
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.rpm")
|
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.rpm")
|
||||||
|
set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.deb")
|
||||||
|
|
||||||
if(NOT CPACK_PACKAGE_RELOCATABLE)
|
if(NOT CPACK_PACKAGE_RELOCATABLE)
|
||||||
# Depend on pkgconfig rpm to create the system pkgconfig folder
|
# Depend on pkgconfig rpm to create the system pkgconfig folder
|
||||||
|
@@ -59,3 +59,4 @@ function(spdlog_enable_sanitizer target_name)
|
|||||||
target_compile_options(${target_name} PRIVATE -fno-omit-frame-pointer)
|
target_compile_options(${target_name} PRIVATE -fno-omit-frame-pointer)
|
||||||
target_link_libraries(${target_name} PRIVATE -fsanitize=address,undefined -fuse-ld=gold)
|
target_link_libraries(${target_name} PRIVATE -fsanitize=address,undefined -fuse-ld=gold)
|
||||||
endfunction()
|
endfunction()
|
||||||
|
|
||||||
|
@@ -22,7 +22,7 @@ void syslog_example();
|
|||||||
void custom_flags_example();
|
void custom_flags_example();
|
||||||
|
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
#include "spdlog/cfg/env.h" // for loading levels from the environment variable
|
#include "spdlog/cfg/env.h" // support for loading levels from the environment variable
|
||||||
|
|
||||||
int main(int, char *[])
|
int main(int, char *[])
|
||||||
{
|
{
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
#include <spdlog/details/os.h>
|
#include <spdlog/details/os.h>
|
||||||
#include <spdlog/details/registry.h>
|
#include <spdlog/details/registry.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
@@ -9,15 +9,24 @@
|
|||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace level {
|
namespace level {
|
||||||
|
|
||||||
|
#if __cplusplus >= 201703L
|
||||||
|
constexpr
|
||||||
|
#endif
|
||||||
static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
|
static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
|
||||||
|
|
||||||
static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES;
|
static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES;
|
||||||
|
|
||||||
SPDLOG_INLINE string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
SPDLOG_INLINE const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
return level_string_views[l];
|
return level_string_views[l];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void set_string_view(spdlog::level::level_enum l, const string_view_t &s) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
level_string_views[l] = s;
|
||||||
|
}
|
||||||
|
|
||||||
SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
return short_level_names[l];
|
return short_level_names[l];
|
||||||
|
@@ -88,7 +88,9 @@ class sink;
|
|||||||
|
|
||||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
using filename_t = std::wstring;
|
using filename_t = std::wstring;
|
||||||
#define SPDLOG_FILENAME_T(s) L##s
|
// allow macro expansion to occur in SPDLOG_FILENAME_T
|
||||||
|
#define SPDLOG_FILENAME_T_INNER(s) L##s
|
||||||
|
#define SPDLOG_FILENAME_T(s) SPDLOG_FILENAME_T_INNER(s)
|
||||||
#else
|
#else
|
||||||
using filename_t = std::string;
|
using filename_t = std::string;
|
||||||
#define SPDLOG_FILENAME_T(s) s
|
#define SPDLOG_FILENAME_T(s) s
|
||||||
@@ -101,6 +103,7 @@ using err_handler = std::function<void(const std::string &err_msg)>;
|
|||||||
using string_view_t = fmt::basic_string_view<char>;
|
using string_view_t = fmt::basic_string_view<char>;
|
||||||
using wstring_view_t = fmt::basic_string_view<wchar_t>;
|
using wstring_view_t = fmt::basic_string_view<wchar_t>;
|
||||||
using memory_buf_t = fmt::basic_memory_buffer<char, 250>;
|
using memory_buf_t = fmt::basic_memory_buffer<char, 250>;
|
||||||
|
using wmemory_buf_t = fmt::basic_memory_buffer<wchar_t, 250>;
|
||||||
|
|
||||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
@@ -163,7 +166,8 @@ enum level_enum
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SPDLOG_API string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
SPDLOG_API const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
||||||
|
SPDLOG_API void set_string_view(spdlog::level::level_enum l, const string_view_t &s) SPDLOG_NOEXCEPT;
|
||||||
SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
||||||
SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
|
SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
|
||||||
|
|
||||||
@@ -203,8 +207,8 @@ private:
|
|||||||
std::string msg_;
|
std::string msg_;
|
||||||
};
|
};
|
||||||
|
|
||||||
SPDLOG_API void throw_spdlog_ex(const std::string &msg, int last_errno);
|
[[noreturn]] SPDLOG_API void throw_spdlog_ex(const std::string &msg, int last_errno);
|
||||||
SPDLOG_API void throw_spdlog_ex(std::string msg);
|
[[noreturn]] SPDLOG_API void throw_spdlog_ex(std::string msg);
|
||||||
|
|
||||||
struct source_loc
|
struct source_loc
|
||||||
{
|
{
|
||||||
|
@@ -29,12 +29,27 @@ SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate)
|
|||||||
{
|
{
|
||||||
close();
|
close();
|
||||||
filename_ = fname;
|
filename_ = fname;
|
||||||
auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
|
|
||||||
|
auto *mode = SPDLOG_FILENAME_T("ab");
|
||||||
|
auto *trunc_mode = SPDLOG_FILENAME_T("wb");
|
||||||
|
|
||||||
for (int tries = 0; tries < open_tries_; ++tries)
|
for (int tries = 0; tries < open_tries_; ++tries)
|
||||||
{
|
{
|
||||||
// create containing folder if not exists already.
|
// create containing folder if not exists already.
|
||||||
os::create_dir(os::dir_name(fname));
|
os::create_dir(os::dir_name(fname));
|
||||||
|
if (truncate)
|
||||||
|
{
|
||||||
|
// Truncate by opening-and-closing a tmp file in "wb" mode, always
|
||||||
|
// opening the actual log-we-write-to in "ab" mode, since that
|
||||||
|
// interacts more politely with eternal processes that might
|
||||||
|
// rotate/truncate the file underneath us.
|
||||||
|
std::FILE *tmp;
|
||||||
|
if (os::fopen_s(&tmp, fname, trunc_mode))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
std::fclose(tmp);
|
||||||
|
}
|
||||||
if (!os::fopen_s(&fd_, fname, mode))
|
if (!os::fopen_s(&fd_, fname, mode))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@@ -118,7 +133,7 @@ SPDLOG_INLINE std::tuple<filename_t, filename_t> file_helper::split_by_extension
|
|||||||
}
|
}
|
||||||
|
|
||||||
// treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
|
// treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
|
||||||
auto folder_index = fname.rfind(details::os::folder_sep);
|
auto folder_index = fname.find_last_of(details::os::folder_seps_filename);
|
||||||
if (folder_index != filename_t::npos && folder_index >= ext_index - 1)
|
if (folder_index != filename_t::npos && folder_index >= ext_index - 1)
|
||||||
{
|
{
|
||||||
return std::make_tuple(fname, filename_t());
|
return std::make_tuple(fname, filename_t());
|
||||||
|
@@ -26,7 +26,9 @@ SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other)
|
|||||||
update_string_views();
|
update_string_views();
|
||||||
}
|
}
|
||||||
|
|
||||||
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT : log_msg{other}, buffer{std::move(other.buffer)}
|
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT
|
||||||
|
: log_msg{other}
|
||||||
|
, buffer{std::move(other.buffer)}
|
||||||
{
|
{
|
||||||
update_string_views();
|
update_string_views();
|
||||||
}
|
}
|
||||||
|
@@ -110,6 +110,12 @@ public:
|
|||||||
return q_.overrun_counter();
|
return q_.overrun_counter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t size()
|
||||||
|
{
|
||||||
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
|
return q_.size();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::mutex queue_mutex_;
|
std::mutex queue_mutex_;
|
||||||
std::condition_variable push_cv_;
|
std::condition_variable push_cv_;
|
||||||
|
@@ -397,17 +397,26 @@ SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT
|
|||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
return true;
|
return true;
|
||||||
#else
|
#else
|
||||||
static constexpr std::array<const char *, 14> terms = {
|
|
||||||
{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"}};
|
|
||||||
|
|
||||||
const char *env_p = std::getenv("TERM");
|
static const bool result = []() {
|
||||||
if (env_p == nullptr)
|
const char *env_colorterm_p = std::getenv("COLORTERM");
|
||||||
{
|
if (env_colorterm_p != nullptr)
|
||||||
return false;
|
{
|
||||||
}
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr std::array<const char *, 15> terms = {{"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux",
|
||||||
|
"msys", "putty", "rxvt", "screen", "vt100", "xterm", "alacritty"}};
|
||||||
|
|
||||||
|
const char *env_term_p = std::getenv("TERM");
|
||||||
|
if (env_term_p == nullptr)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::any_of(terms.begin(), terms.end(), [&](const char *term) { return std::strstr(env_term_p, term) != nullptr; });
|
||||||
|
}();
|
||||||
|
|
||||||
static const bool result =
|
|
||||||
std::any_of(terms.begin(), terms.end(), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
|
|
||||||
return result;
|
return result;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -427,7 +436,7 @@ SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
|
|||||||
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||||
SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
|
SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
|
||||||
{
|
{
|
||||||
if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()))
|
if (wstr.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) / 2 - 1)
|
||||||
{
|
{
|
||||||
throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
|
throw_spdlog_ex("UTF-16 string is too big to be converted to UTF-8");
|
||||||
}
|
}
|
||||||
@@ -459,6 +468,41 @@ SPDLOG_INLINE void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target)
|
|||||||
|
|
||||||
throw_spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
|
throw_spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SPDLOG_INLINE void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target)
|
||||||
|
{
|
||||||
|
if (str.size() > static_cast<size_t>((std::numeric_limits<int>::max)()) - 1)
|
||||||
|
{
|
||||||
|
throw_spdlog_ex("UTF-8 string is too big to be converted to UTF-16");
|
||||||
|
}
|
||||||
|
|
||||||
|
int str_size = static_cast<int>(str.size());
|
||||||
|
if (str_size == 0)
|
||||||
|
{
|
||||||
|
target.resize(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int result_size = static_cast<int>(target.capacity());
|
||||||
|
if (str_size + 1 > result_size)
|
||||||
|
{
|
||||||
|
result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result_size > 0)
|
||||||
|
{
|
||||||
|
target.resize(result_size);
|
||||||
|
result_size = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, str.data(), str_size, target.data(), result_size);
|
||||||
|
|
||||||
|
if (result_size > 0)
|
||||||
|
{
|
||||||
|
target.resize(result_size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw_spdlog_ex(fmt::format("MultiByteToWideChar failed. Last error: {}", ::GetLastError()));
|
||||||
|
}
|
||||||
#endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
#endif // (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||||
|
|
||||||
// return true on success
|
// return true on success
|
||||||
@@ -489,15 +533,10 @@ SPDLOG_INLINE bool create_dir(filename_t path)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
// support forward slash in windows
|
|
||||||
std::replace(path.begin(), path.end(), '/', folder_sep);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t search_offset = 0;
|
size_t search_offset = 0;
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
auto token_pos = path.find(folder_sep, search_offset);
|
auto token_pos = path.find_first_of(folder_seps_filename, search_offset);
|
||||||
// treat the entire path as a folder if no folder separator not found
|
// treat the entire path as a folder if no folder separator not found
|
||||||
if (token_pos == filename_t::npos)
|
if (token_pos == filename_t::npos)
|
||||||
{
|
{
|
||||||
@@ -523,11 +562,7 @@ SPDLOG_INLINE bool create_dir(filename_t path)
|
|||||||
// "abc///" => "abc//"
|
// "abc///" => "abc//"
|
||||||
SPDLOG_INLINE filename_t dir_name(filename_t path)
|
SPDLOG_INLINE filename_t dir_name(filename_t path)
|
||||||
{
|
{
|
||||||
#ifdef _WIN32
|
auto pos = path.find_last_of(folder_seps_filename);
|
||||||
// support forward slash in windows
|
|
||||||
std::replace(path.begin(), path.end(), '/', folder_sep);
|
|
||||||
#endif
|
|
||||||
auto pos = path.find_last_of(folder_sep);
|
|
||||||
return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
|
return pos != filename_t::npos ? path.substr(0, pos) : filename_t{};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -32,11 +32,16 @@ SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT;
|
|||||||
SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
|
SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
|
||||||
|
|
||||||
// folder separator
|
// folder separator
|
||||||
|
#if !defined(SPDLOG_FOLDER_SEPS)
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static const char folder_sep = '\\';
|
#define SPDLOG_FOLDER_SEPS "\\/"
|
||||||
#else
|
#else
|
||||||
SPDLOG_CONSTEXPR static const char folder_sep = '/';
|
#define SPDLOG_FOLDER_SEPS "/"
|
||||||
#endif
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SPDLOG_CONSTEXPR static const char folder_seps[] = SPDLOG_FOLDER_SEPS;
|
||||||
|
SPDLOG_CONSTEXPR static const filename_t::value_type folder_seps_filename[] = SPDLOG_FILENAME_T(SPDLOG_FOLDER_SEPS);
|
||||||
|
|
||||||
// fopen_s on non windows for writing
|
// fopen_s on non windows for writing
|
||||||
SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
|
SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
|
||||||
@@ -85,6 +90,8 @@ SPDLOG_API bool in_terminal(FILE *file) SPDLOG_NOEXCEPT;
|
|||||||
|
|
||||||
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
#if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32)
|
||||||
SPDLOG_API void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
|
SPDLOG_API void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
|
||||||
|
|
||||||
|
SPDLOG_API void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Return directory name from given path or empty string
|
// Return directory name from given path or empty string
|
||||||
|
@@ -99,7 +99,7 @@ private:
|
|||||||
std::unique_ptr<formatter> formatter_;
|
std::unique_ptr<formatter> formatter_;
|
||||||
spdlog::level::level_enum global_log_level_ = level::info;
|
spdlog::level::level_enum global_log_level_ = level::info;
|
||||||
level::level_enum flush_level_ = level::off;
|
level::level_enum flush_level_ = level::off;
|
||||||
void (*err_handler_)(const std::string &msg);
|
void (*err_handler_)(const std::string &msg) = nullptr;
|
||||||
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_;
|
std::shared_ptr<logger> default_logger_;
|
||||||
|
@@ -68,6 +68,11 @@ size_t SPDLOG_INLINE thread_pool::overrun_counter()
|
|||||||
return q_.overrun_counter();
|
return q_.overrun_counter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t SPDLOG_INLINE thread_pool::queue_size()
|
||||||
|
{
|
||||||
|
return q_.size();
|
||||||
|
}
|
||||||
|
|
||||||
void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
|
void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
|
||||||
{
|
{
|
||||||
if (overflow_policy == async_overflow_policy::block)
|
if (overflow_policy == async_overflow_policy::block)
|
||||||
|
@@ -97,6 +97,7 @@ public:
|
|||||||
void post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy);
|
void post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy);
|
||||||
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
|
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
|
||||||
size_t overrun_counter();
|
size_t overrun_counter();
|
||||||
|
size_t queue_size();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
q_type q_;
|
q_type q_;
|
||||||
|
@@ -72,43 +72,27 @@ FMT_CONSTEXPR To lossless_integral_conversion(const From from, int& ec) {
|
|||||||
static_assert(F::is_integer, "From must be integral");
|
static_assert(F::is_integer, "From must be integral");
|
||||||
static_assert(T::is_integer, "To must be integral");
|
static_assert(T::is_integer, "To must be integral");
|
||||||
|
|
||||||
if (F::is_signed && !T::is_signed) {
|
if (detail::const_check(F::is_signed && !T::is_signed)) {
|
||||||
// From may be negative, not allowed!
|
// From may be negative, not allowed!
|
||||||
if (fmt::detail::is_negative(from)) {
|
if (fmt::detail::is_negative(from)) {
|
||||||
ec = 1;
|
ec = 1;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// From is positive. Can it always fit in To?
|
// From is positive. Can it always fit in To?
|
||||||
if (F::digits <= T::digits) {
|
if (F::digits > T::digits &&
|
||||||
// yes, From always fits in To.
|
from > static_cast<From>(detail::max_value<To>())) {
|
||||||
} else {
|
ec = 1;
|
||||||
// from may not fit in To, we have to do a dynamic check
|
return {};
|
||||||
if (from > static_cast<From>((T::max)())) {
|
|
||||||
ec = 1;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!F::is_signed && T::is_signed) {
|
if (!F::is_signed && T::is_signed && F::digits >= T::digits &&
|
||||||
// can from be held in To?
|
from > static_cast<From>(detail::max_value<To>())) {
|
||||||
if (F::digits < T::digits) {
|
ec = 1;
|
||||||
// yes, From always fits in To.
|
return {};
|
||||||
} else {
|
|
||||||
// from may not fit in To, we have to do a dynamic check
|
|
||||||
if (from > static_cast<From>((T::max)())) {
|
|
||||||
// outside range.
|
|
||||||
ec = 1;
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return static_cast<To>(from); // Lossless conversion.
|
||||||
// reaching here means all is ok for lossless conversion.
|
}
|
||||||
return static_cast<To>(from);
|
|
||||||
|
|
||||||
} // function
|
|
||||||
|
|
||||||
template <typename To, typename From,
|
template <typename To, typename From,
|
||||||
FMT_ENABLE_IF(std::is_same<From, To>::value)>
|
FMT_ENABLE_IF(std::is_same<From, To>::value)>
|
||||||
@@ -190,11 +174,9 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
|||||||
// safe conversion to IntermediateRep
|
// safe conversion to IntermediateRep
|
||||||
IntermediateRep count =
|
IntermediateRep count =
|
||||||
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
|
lossless_integral_conversion<IntermediateRep>(from.count(), ec);
|
||||||
if (ec) {
|
if (ec) return {};
|
||||||
return {};
|
|
||||||
}
|
|
||||||
// multiply with Factor::num without overflow or underflow
|
// multiply with Factor::num without overflow or underflow
|
||||||
if (Factor::num != 1) {
|
if (detail::const_check(Factor::num != 1)) {
|
||||||
const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
|
const auto max1 = detail::max_value<IntermediateRep>() / Factor::num;
|
||||||
if (count > max1) {
|
if (count > max1) {
|
||||||
ec = 1;
|
ec = 1;
|
||||||
@@ -209,17 +191,9 @@ To safe_duration_cast(std::chrono::duration<FromRep, FromPeriod> from,
|
|||||||
count *= Factor::num;
|
count *= Factor::num;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this can't go wrong, right? den>0 is checked earlier.
|
if (detail::const_check(Factor::den != 1)) count /= Factor::den;
|
||||||
if (Factor::den != 1) {
|
auto tocount = lossless_integral_conversion<typename To::rep>(count, ec);
|
||||||
count /= Factor::den;
|
return ec ? To() : To(tocount);
|
||||||
}
|
|
||||||
// convert to the to type, safely
|
|
||||||
using ToRep = typename To::rep;
|
|
||||||
const ToRep tocount = lossless_integral_conversion<ToRep>(count, ec);
|
|
||||||
if (ec) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return To{tocount};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -351,6 +325,11 @@ inline std::tm localtime(std::time_t time) {
|
|||||||
return lt.tm_;
|
return lt.tm_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::tm localtime(
|
||||||
|
std::chrono::time_point<std::chrono::system_clock> time_point) {
|
||||||
|
return localtime(std::chrono::system_clock::to_time_t(time_point));
|
||||||
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
@@ -387,6 +366,11 @@ inline std::tm gmtime(std::time_t time) {
|
|||||||
return gt.tm_;
|
return gt.tm_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::tm gmtime(
|
||||||
|
std::chrono::time_point<std::chrono::system_clock> time_point) {
|
||||||
|
return gmtime(std::chrono::system_clock::to_time_t(time_point));
|
||||||
|
}
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
inline size_t strftime(char* str, size_t count, const char* format,
|
inline size_t strftime(char* str, size_t count, const char* format,
|
||||||
const std::tm* time) {
|
const std::tm* time) {
|
||||||
@@ -399,6 +383,17 @@ inline size_t strftime(wchar_t* str, size_t count, const wchar_t* format,
|
|||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct formatter<std::chrono::time_point<std::chrono::system_clock>, Char>
|
||||||
|
: formatter<std::tm, Char> {
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(std::chrono::time_point<std::chrono::system_clock> val,
|
||||||
|
FormatContext& ctx) -> decltype(ctx.out()) {
|
||||||
|
std::tm time = localtime(val);
|
||||||
|
return formatter<std::tm, Char>::format(time, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <typename Char> struct formatter<std::tm, Char> {
|
template <typename 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()) {
|
||||||
|
@@ -463,16 +463,16 @@ template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
inline void reset_color(basic_memory_buffer<Char>& buffer) FMT_NOEXCEPT {
|
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
|
||||||
const char* begin = data::reset_color;
|
const char* begin = data::reset_color;
|
||||||
const char* end = begin + sizeof(data::reset_color) - 1;
|
const char* end = begin + sizeof(data::reset_color) - 1;
|
||||||
buffer.append(begin, end);
|
buffer.append(begin, end);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
|
void vformat_to(buffer<Char>& buf, const text_style& ts,
|
||||||
basic_string_view<Char> format_str,
|
basic_string_view<Char> format_str,
|
||||||
basic_format_args<buffer_context<Char>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
bool has_style = false;
|
bool has_style = false;
|
||||||
if (ts.has_emphasis()) {
|
if (ts.has_emphasis()) {
|
||||||
has_style = true;
|
has_style = true;
|
||||||
@@ -496,7 +496,7 @@ void vformat_to(basic_memory_buffer<Char>& buf, const text_style& ts,
|
|||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
void vprint(std::FILE* f, const text_style& ts, const S& format,
|
||||||
basic_format_args<buffer_context<Char>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buf;
|
basic_memory_buffer<Char> buf;
|
||||||
detail::vformat_to(buf, ts, to_string_view(format), args);
|
detail::vformat_to(buf, ts, to_string_view(format), args);
|
||||||
buf.push_back(Char(0));
|
buf.push_back(Char(0));
|
||||||
@@ -504,20 +504,22 @@ void vprint(std::FILE* f, const text_style& ts, const S& format,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
\rst
|
||||||
Formats a string and prints it to the specified file stream using ANSI
|
Formats a string and prints it to the specified file stream using ANSI
|
||||||
escape sequences to specify text formatting.
|
escape sequences to specify text formatting.
|
||||||
Example:
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||||
|
\endrst
|
||||||
*/
|
*/
|
||||||
template <typename S, typename... Args,
|
template <typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||||
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
detail::check_format_string<Args...>(format_str);
|
vprint(f, ts, format_str,
|
||||||
using context = buffer_context<char_t<S>>;
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
format_arg_store<context, Args...> as{args...};
|
|
||||||
vprint(f, ts, format_str, basic_format_args<context>(as));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -558,7 +560,42 @@ template <typename S, typename... Args, typename Char = char_t<S>>
|
|||||||
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
return vformat(ts, to_string_view(format_str),
|
return vformat(ts, to_string_view(format_str),
|
||||||
detail::make_args_checked<Args...>(format_str, args...));
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Formats a string with the given text_style and writes the output to ``out``.
|
||||||
|
*/
|
||||||
|
template <typename OutputIt, typename Char,
|
||||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
|
||||||
|
OutputIt vformat_to(
|
||||||
|
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
|
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
|
||||||
|
detail::vformat_to(buf, ts, format_str, args);
|
||||||
|
return detail::get_iterator(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Formats arguments with the given text_style, writes the result to the output
|
||||||
|
iterator ``out`` and returns the iterator past the end of the output range.
|
||||||
|
|
||||||
|
**Example**::
|
||||||
|
|
||||||
|
std::vector<char> out;
|
||||||
|
fmt::format_to(std::back_inserter(out),
|
||||||
|
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
|
||||||
|
detail::is_string<S>::value>
|
||||||
|
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
|
||||||
|
Args&&... args) ->
|
||||||
|
typename std::enable_if<enable, OutputIt>::type {
|
||||||
|
return vformat_to(out, ts, to_string_view(format_str),
|
||||||
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
@@ -368,7 +368,8 @@ template <typename... Args> struct type_list {};
|
|||||||
|
|
||||||
// Returns a reference to the argument at index N from [first, rest...].
|
// Returns a reference to the argument at index N from [first, rest...].
|
||||||
template <int N, typename T, typename... Args>
|
template <int N, typename T, typename... Args>
|
||||||
constexpr const auto& get(const T& first, const Args&... rest) {
|
constexpr const auto& get([[maybe_unused]] const T& first,
|
||||||
|
[[maybe_unused]] const Args&... rest) {
|
||||||
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
|
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
|
||||||
if constexpr (N == 0)
|
if constexpr (N == 0)
|
||||||
return first;
|
return first;
|
||||||
@@ -406,6 +407,19 @@ constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
|
|||||||
return {{&s[pos], size}};
|
return {{&s[pos], size}};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Char> struct code_unit {
|
||||||
|
Char value;
|
||||||
|
using char_type = Char;
|
||||||
|
|
||||||
|
template <typename OutputIt, typename... Args>
|
||||||
|
OutputIt format(OutputIt out, const Args&...) const {
|
||||||
|
return write<Char>(out, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Char>
|
||||||
|
struct is_compiled_format<code_unit<Char>> : std::true_type {};
|
||||||
|
|
||||||
// A replacement field that refers to argument N.
|
// A replacement field that refers to argument N.
|
||||||
template <typename Char, typename T, int N> struct field {
|
template <typename Char, typename T, int N> struct field {
|
||||||
using char_type = Char;
|
using char_type = Char;
|
||||||
@@ -430,7 +444,9 @@ template <typename Char, typename T, int N> struct spec_field {
|
|||||||
OutputIt format(OutputIt out, const Args&... args) const {
|
OutputIt format(OutputIt out, const Args&... args) const {
|
||||||
// This ensures that the argument type is convertile to `const T&`.
|
// This ensures that the argument type is convertile to `const T&`.
|
||||||
const T& arg = get<N>(args...);
|
const T& arg = get<N>(args...);
|
||||||
basic_format_context<OutputIt, Char> ctx(out, {});
|
const auto& vargs =
|
||||||
|
make_format_args<basic_format_context<OutputIt, Char>>(args...);
|
||||||
|
basic_format_context<OutputIt, Char> ctx(out, vargs);
|
||||||
return fmt.format(arg, ctx);
|
return fmt.format(arg, ctx);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -489,16 +505,17 @@ constexpr auto parse_tail(T head, S format_str) {
|
|||||||
template <typename T, typename Char> struct parse_specs_result {
|
template <typename T, typename Char> struct parse_specs_result {
|
||||||
formatter<T, Char> fmt;
|
formatter<T, Char> fmt;
|
||||||
size_t end;
|
size_t end;
|
||||||
|
int next_arg_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T, typename Char>
|
template <typename T, typename Char>
|
||||||
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
||||||
size_t pos) {
|
size_t pos, int arg_id) {
|
||||||
str.remove_prefix(pos);
|
str.remove_prefix(pos);
|
||||||
auto ctx = basic_format_parse_context<Char>(str);
|
auto ctx = basic_format_parse_context<Char>(str, {}, arg_id + 1);
|
||||||
auto f = formatter<T, Char>();
|
auto f = formatter<T, Char>();
|
||||||
auto end = f.parse(ctx);
|
auto end = f.parse(ctx);
|
||||||
return {f, pos + (end - str.data()) + 1};
|
return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compiles a non-empty format string and returns the compiled representation
|
// Compiles a non-empty format string and returns the compiled representation
|
||||||
@@ -518,8 +535,8 @@ constexpr auto compile_format_string(S format_str) {
|
|||||||
format_str);
|
format_str);
|
||||||
} else if constexpr (str[POS + 1] == ':') {
|
} else if constexpr (str[POS + 1] == ':') {
|
||||||
using type = get_type<ID, Args>;
|
using type = get_type<ID, Args>;
|
||||||
constexpr auto result = parse_specs<type>(str, POS + 2);
|
constexpr auto result = parse_specs<type>(str, POS + 2, ID);
|
||||||
return parse_tail<Args, result.end, ID + 1>(
|
return parse_tail<Args, result.end, result.next_arg_id>(
|
||||||
spec_field<char_type, type, ID>{result.fmt}, format_str);
|
spec_field<char_type, type, ID>{result.fmt}, format_str);
|
||||||
} else {
|
} else {
|
||||||
return unknown_format();
|
return unknown_format();
|
||||||
@@ -530,8 +547,13 @@ constexpr auto compile_format_string(S format_str) {
|
|||||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||||
} else {
|
} else {
|
||||||
constexpr auto end = parse_text(str, POS + 1);
|
constexpr auto end = parse_text(str, POS + 1);
|
||||||
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
|
if constexpr (end - POS > 1) {
|
||||||
format_str);
|
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
|
||||||
|
format_str);
|
||||||
|
} else {
|
||||||
|
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
|
||||||
|
format_str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -587,8 +609,7 @@ template <typename CompiledFormat, typename... Args,
|
|||||||
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
|
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
|
||||||
const Args&... args) {
|
const Args&... args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
detail::buffer<Char>& base = buffer;
|
cf.format(detail::buffer_appender<Char>(buffer), args...);
|
||||||
cf.format(std::back_inserter(base), args...);
|
|
||||||
return to_string(buffer);
|
return to_string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,8 +629,7 @@ template <typename CompiledFormat, typename... Args,
|
|||||||
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
using context = buffer_context<Char>;
|
using context = buffer_context<Char>;
|
||||||
detail::buffer<Char>& base = buffer;
|
detail::cf::vformat_to<context>(detail::buffer_appender<Char>(buffer), cf,
|
||||||
detail::cf::vformat_to<context>(std::back_inserter(base), cf,
|
|
||||||
make_format_args<context>(args...));
|
make_format_args<context>(args...));
|
||||||
return to_string(buffer);
|
return to_string(buffer);
|
||||||
}
|
}
|
||||||
@@ -618,9 +638,13 @@ template <typename S, typename... Args,
|
|||||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
||||||
Args&&... args) {
|
Args&&... args) {
|
||||||
constexpr basic_string_view<typename S::char_type> str = S();
|
#ifdef __cpp_if_constexpr
|
||||||
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
|
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
||||||
return fmt::to_string(detail::first(args...));
|
constexpr basic_string_view<typename S::char_type> str = S();
|
||||||
|
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
|
||||||
|
return fmt::to_string(detail::first(args...));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
constexpr auto compiled = detail::compile<Args...>(S());
|
constexpr auto compiled = detail::compile<Args...>(S());
|
||||||
return format(compiled, std::forward<Args>(args)...);
|
return format(compiled, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
@@ -643,18 +667,30 @@ OutputIt format_to(OutputIt out, const S&, const Args&... args) {
|
|||||||
return format_to(out, compiled, args...);
|
return format_to(out, compiled, args...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <
|
template <typename OutputIt, typename CompiledFormat, typename... Args>
|
||||||
typename OutputIt, typename CompiledFormat, typename... Args,
|
auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf,
|
||||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&& std::is_base_of<
|
const Args&... args) ->
|
||||||
detail::basic_compiled_format, CompiledFormat>::value)>
|
typename std::enable_if<
|
||||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
detail::is_output_iterator<OutputIt,
|
||||||
const CompiledFormat& cf,
|
typename CompiledFormat::char_type>::value &&
|
||||||
const Args&... args) {
|
std::is_base_of<detail::basic_compiled_format,
|
||||||
|
CompiledFormat>::value,
|
||||||
|
format_to_n_result<OutputIt>>::type {
|
||||||
auto it =
|
auto it =
|
||||||
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
|
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
|
||||||
return {it.base(), it.count()};
|
return {it.base(), it.count()};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||||
|
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S&,
|
||||||
|
const Args&... args) {
|
||||||
|
constexpr auto compiled = detail::compile<Args...>(S());
|
||||||
|
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), compiled,
|
||||||
|
args...);
|
||||||
|
return {it.base(), it.count()};
|
||||||
|
}
|
||||||
|
|
||||||
template <typename CompiledFormat, typename... Args>
|
template <typename CompiledFormat, typename... Args>
|
||||||
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
||||||
return format_to(detail::counting_iterator(), cf, args...).count();
|
return format_to(detail::counting_iterator(), cf, args...).count();
|
||||||
|
@@ -18,7 +18,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
||||||
#define FMT_VERSION 70003
|
#define FMT_VERSION 70103
|
||||||
|
|
||||||
#ifdef __clang__
|
#ifdef __clang__
|
||||||
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
|
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
|
||||||
@@ -57,6 +57,7 @@
|
|||||||
# define FMT_MSC_VER 0
|
# define FMT_MSC_VER 0
|
||||||
# define FMT_SUPPRESS_MSC_WARNING(n)
|
# define FMT_SUPPRESS_MSC_WARNING(n)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __has_feature
|
#ifdef __has_feature
|
||||||
# define FMT_HAS_FEATURE(x) __has_feature(x)
|
# define FMT_HAS_FEATURE(x) __has_feature(x)
|
||||||
#else
|
#else
|
||||||
@@ -64,7 +65,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__has_include) && !defined(__INTELLISENSE__) && \
|
#if defined(__has_include) && !defined(__INTELLISENSE__) && \
|
||||||
!(FMT_ICC_VERSION && FMT_ICC_VERSION < 1600)
|
(!FMT_ICC_VERSION || FMT_ICC_VERSION >= 1600)
|
||||||
# define FMT_HAS_INCLUDE(x) __has_include(x)
|
# define FMT_HAS_INCLUDE(x) __has_include(x)
|
||||||
#else
|
#else
|
||||||
# define FMT_HAS_INCLUDE(x) 0
|
# define FMT_HAS_INCLUDE(x) 0
|
||||||
@@ -99,7 +100,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FMT_OVERRIDE
|
#ifndef FMT_OVERRIDE
|
||||||
# if FMT_HAS_FEATURE(cxx_override) || \
|
# if FMT_HAS_FEATURE(cxx_override_control) || \
|
||||||
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
|
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1900
|
||||||
# define FMT_OVERRIDE override
|
# define FMT_OVERRIDE override
|
||||||
# else
|
# else
|
||||||
@@ -152,7 +153,7 @@
|
|||||||
# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
|
# if FMT_HAS_CPP14_ATTRIBUTE(deprecated) || FMT_MSC_VER >= 1900
|
||||||
# define FMT_DEPRECATED [[deprecated]]
|
# define FMT_DEPRECATED [[deprecated]]
|
||||||
# else
|
# else
|
||||||
# if defined(__GNUC__) || defined(__clang__)
|
# if (defined(__GNUC__) && !defined(__LCC__)) || defined(__clang__)
|
||||||
# define FMT_DEPRECATED __attribute__((deprecated))
|
# define FMT_DEPRECATED __attribute__((deprecated))
|
||||||
# elif FMT_MSC_VER
|
# elif FMT_MSC_VER
|
||||||
# define FMT_DEPRECATED __declspec(deprecated)
|
# define FMT_DEPRECATED __declspec(deprecated)
|
||||||
@@ -177,9 +178,17 @@
|
|||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef FMT_BEGIN_NAMESPACE
|
#ifndef FMT_USE_INLINE_NAMESPACES
|
||||||
# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
|
# if FMT_HAS_FEATURE(cxx_inline_namespaces) || FMT_GCC_VERSION >= 404 || \
|
||||||
FMT_MSC_VER >= 1900
|
(FMT_MSC_VER >= 1900 && !_MANAGED)
|
||||||
|
# define FMT_USE_INLINE_NAMESPACES 1
|
||||||
|
# else
|
||||||
|
# define FMT_USE_INLINE_NAMESPACES 0
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef FMT_BEGIN_NAMESPACE
|
||||||
|
# if FMT_USE_INLINE_NAMESPACES
|
||||||
# define FMT_INLINE_NAMESPACE inline namespace
|
# define FMT_INLINE_NAMESPACE inline namespace
|
||||||
# define FMT_END_NAMESPACE \
|
# define FMT_END_NAMESPACE \
|
||||||
} \
|
} \
|
||||||
@@ -269,8 +278,7 @@ struct monostate {};
|
|||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
// A helper function to suppress bogus "conditional expression is constant"
|
// A helper function to suppress "conditional expression is constant" warnings.
|
||||||
// warnings.
|
|
||||||
template <typename T> constexpr T const_check(T value) { return value; }
|
template <typename T> constexpr T const_check(T value) { return value; }
|
||||||
|
|
||||||
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
|
FMT_NORETURN FMT_API void assert_fail(const char* file, int line,
|
||||||
@@ -299,7 +307,8 @@ template <typename T> struct std_string_view {};
|
|||||||
|
|
||||||
#ifdef FMT_USE_INT128
|
#ifdef FMT_USE_INT128
|
||||||
// Do nothing.
|
// Do nothing.
|
||||||
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && !(FMT_CLANG_VERSION && FMT_MSC_VER)
|
#elif defined(__SIZEOF_INT128__) && !FMT_NVCC && \
|
||||||
|
!(FMT_CLANG_VERSION && FMT_MSC_VER)
|
||||||
# define FMT_USE_INT128 1
|
# define FMT_USE_INT128 1
|
||||||
using int128_t = __int128_t;
|
using int128_t = __int128_t;
|
||||||
using uint128_t = __uint128_t;
|
using uint128_t = __uint128_t;
|
||||||
@@ -506,6 +515,18 @@ template <typename S> struct char_t_impl<S, enable_if_t<is_string<S>::value>> {
|
|||||||
using type = typename result::value_type;
|
using type = typename result::value_type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Reports a compile-time error if S is not a valid format string.
|
||||||
|
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
|
||||||
|
FMT_INLINE void check_format_string(const S&) {
|
||||||
|
#ifdef FMT_ENFORCE_COMPILE_STRING
|
||||||
|
static_assert(is_compile_string<S>::value,
|
||||||
|
"FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
|
||||||
|
"FMT_STRING.");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
|
||||||
|
void check_format_string(S);
|
||||||
|
|
||||||
struct error_handler {
|
struct error_handler {
|
||||||
constexpr error_handler() = default;
|
constexpr error_handler() = default;
|
||||||
constexpr error_handler(const error_handler&) = default;
|
constexpr error_handler(const error_handler&) = default;
|
||||||
@@ -545,8 +566,9 @@ class basic_format_parse_context : private ErrorHandler {
|
|||||||
using iterator = typename basic_string_view<Char>::iterator;
|
using iterator = typename basic_string_view<Char>::iterator;
|
||||||
|
|
||||||
explicit constexpr basic_format_parse_context(
|
explicit constexpr basic_format_parse_context(
|
||||||
basic_string_view<Char> format_str, ErrorHandler eh = {})
|
basic_string_view<Char> format_str, ErrorHandler eh = {},
|
||||||
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(0) {}
|
int next_arg_id = 0)
|
||||||
|
: ErrorHandler(eh), format_str_(format_str), next_arg_id_(next_arg_id) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Returns an iterator to the beginning of the format string range being
|
Returns an iterator to the beginning of the format string range being
|
||||||
@@ -616,8 +638,24 @@ template <typename T, typename Context>
|
|||||||
using has_formatter =
|
using has_formatter =
|
||||||
std::is_constructible<typename Context::template formatter_type<T>>;
|
std::is_constructible<typename Context::template formatter_type<T>>;
|
||||||
|
|
||||||
|
// Checks whether T is a container with contiguous storage.
|
||||||
|
template <typename T> struct is_contiguous : std::false_type {};
|
||||||
|
template <typename Char>
|
||||||
|
struct is_contiguous<std::basic_string<Char>> : std::true_type {};
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
// Extracts a reference to the container from back_insert_iterator.
|
||||||
|
template <typename Container>
|
||||||
|
inline Container& get_container(std::back_insert_iterator<Container> it) {
|
||||||
|
using bi_iterator = std::back_insert_iterator<Container>;
|
||||||
|
struct accessor : bi_iterator {
|
||||||
|
accessor(bi_iterator iter) : bi_iterator(iter) {}
|
||||||
|
using bi_iterator::container;
|
||||||
|
};
|
||||||
|
return *accessor(it).container;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
A contiguous memory buffer with an optional growing ability. It is an internal
|
A contiguous memory buffer with an optional growing ability. It is an internal
|
||||||
@@ -640,6 +678,8 @@ template <typename T> class buffer {
|
|||||||
size_(sz),
|
size_(sz),
|
||||||
capacity_(cap) {}
|
capacity_(cap) {}
|
||||||
|
|
||||||
|
~buffer() = default;
|
||||||
|
|
||||||
/** Sets the buffer data and capacity. */
|
/** Sets the buffer data and capacity. */
|
||||||
void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
|
void set(T* buf_data, size_t buf_capacity) FMT_NOEXCEPT {
|
||||||
ptr_ = buf_data;
|
ptr_ = buf_data;
|
||||||
@@ -655,7 +695,6 @@ template <typename T> class buffer {
|
|||||||
|
|
||||||
buffer(const buffer&) = delete;
|
buffer(const buffer&) = delete;
|
||||||
void operator=(const buffer&) = delete;
|
void operator=(const buffer&) = delete;
|
||||||
virtual ~buffer() = default;
|
|
||||||
|
|
||||||
T* begin() FMT_NOEXCEPT { return ptr_; }
|
T* begin() FMT_NOEXCEPT { return ptr_; }
|
||||||
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
|
T* end() FMT_NOEXCEPT { return ptr_ + size_; }
|
||||||
@@ -675,24 +714,26 @@ template <typename T> class buffer {
|
|||||||
/** Returns a pointer to the buffer data. */
|
/** Returns a pointer to the buffer data. */
|
||||||
const T* data() const FMT_NOEXCEPT { return ptr_; }
|
const T* data() const FMT_NOEXCEPT { return ptr_; }
|
||||||
|
|
||||||
/**
|
|
||||||
Resizes the buffer. If T is a POD type new elements may not be initialized.
|
|
||||||
*/
|
|
||||||
void resize(size_t new_size) {
|
|
||||||
reserve(new_size);
|
|
||||||
size_ = new_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Clears this buffer. */
|
/** Clears this buffer. */
|
||||||
void clear() { size_ = 0; }
|
void clear() { size_ = 0; }
|
||||||
|
|
||||||
/** Reserves space to store at least *capacity* elements. */
|
// Tries resizing the buffer to contain *count* elements. If T is a POD type
|
||||||
void reserve(size_t new_capacity) {
|
// the new elements may not be initialized.
|
||||||
|
void try_resize(size_t count) {
|
||||||
|
try_reserve(count);
|
||||||
|
size_ = count <= capacity_ ? count : capacity_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tries increasing the buffer capacity to *new_capacity*. It can increase the
|
||||||
|
// capacity by a smaller amount than requested but guarantees there is space
|
||||||
|
// for at least one additional element either by increasing the capacity or by
|
||||||
|
// flushing the buffer if it is full.
|
||||||
|
void try_reserve(size_t new_capacity) {
|
||||||
if (new_capacity > capacity_) grow(new_capacity);
|
if (new_capacity > capacity_) grow(new_capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void push_back(const T& value) {
|
void push_back(const T& value) {
|
||||||
reserve(size_ + 1);
|
try_reserve(size_ + 1);
|
||||||
ptr_[size_++] = value;
|
ptr_[size_++] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -705,32 +746,150 @@ template <typename T> class buffer {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// A container-backed buffer.
|
struct buffer_traits {
|
||||||
|
explicit buffer_traits(size_t) {}
|
||||||
|
size_t count() const { return 0; }
|
||||||
|
size_t limit(size_t size) { return size; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class fixed_buffer_traits {
|
||||||
|
private:
|
||||||
|
size_t count_ = 0;
|
||||||
|
size_t limit_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit fixed_buffer_traits(size_t limit) : limit_(limit) {}
|
||||||
|
size_t count() const { return count_; }
|
||||||
|
size_t limit(size_t size) {
|
||||||
|
size_t n = limit_ > count_ ? limit_ - count_ : 0;
|
||||||
|
count_ += size;
|
||||||
|
return size < n ? size : n;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// A buffer that writes to an output iterator when flushed.
|
||||||
|
template <typename OutputIt, typename T, typename Traits = buffer_traits>
|
||||||
|
class iterator_buffer final : public Traits, public buffer<T> {
|
||||||
|
private:
|
||||||
|
OutputIt out_;
|
||||||
|
enum { buffer_size = 256 };
|
||||||
|
T data_[buffer_size];
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void grow(size_t) final FMT_OVERRIDE {
|
||||||
|
if (this->size() == buffer_size) flush();
|
||||||
|
}
|
||||||
|
void flush();
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit iterator_buffer(OutputIt out, size_t n = buffer_size)
|
||||||
|
: Traits(n),
|
||||||
|
buffer<T>(data_, 0, buffer_size),
|
||||||
|
out_(out) {}
|
||||||
|
~iterator_buffer() { flush(); }
|
||||||
|
|
||||||
|
OutputIt out() {
|
||||||
|
flush();
|
||||||
|
return out_;
|
||||||
|
}
|
||||||
|
size_t count() const { return Traits::count() + this->size(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T> class iterator_buffer<T*, T> final : public buffer<T> {
|
||||||
|
protected:
|
||||||
|
void grow(size_t) final FMT_OVERRIDE {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit iterator_buffer(T* out, size_t = 0) : buffer<T>(out, 0, ~size_t()) {}
|
||||||
|
|
||||||
|
T* out() { return &*this->end(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// A buffer that writes to a container with the contiguous storage.
|
||||||
template <typename Container>
|
template <typename Container>
|
||||||
class container_buffer : public buffer<typename Container::value_type> {
|
class iterator_buffer<std::back_insert_iterator<Container>,
|
||||||
|
enable_if_t<is_contiguous<Container>::value,
|
||||||
|
typename Container::value_type>>
|
||||||
|
final : public buffer<typename Container::value_type> {
|
||||||
private:
|
private:
|
||||||
Container& container_;
|
Container& container_;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void grow(size_t capacity) FMT_OVERRIDE {
|
void grow(size_t capacity) final FMT_OVERRIDE {
|
||||||
container_.resize(capacity);
|
container_.resize(capacity);
|
||||||
this->set(&container_[0], capacity);
|
this->set(&container_[0], capacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit container_buffer(Container& c)
|
explicit iterator_buffer(Container& c)
|
||||||
: buffer<typename Container::value_type>(c.size()), container_(c) {}
|
: buffer<typename Container::value_type>(c.size()), container_(c) {}
|
||||||
|
explicit iterator_buffer(std::back_insert_iterator<Container> out, size_t = 0)
|
||||||
|
: iterator_buffer(get_container(out)) {}
|
||||||
|
std::back_insert_iterator<Container> out() {
|
||||||
|
return std::back_inserter(container_);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Extracts a reference to the container from back_insert_iterator.
|
// A buffer that counts the number of code units written discarding the output.
|
||||||
template <typename Container>
|
template <typename T = char> class counting_buffer final : public buffer<T> {
|
||||||
inline Container& get_container(std::back_insert_iterator<Container> it) {
|
private:
|
||||||
using bi_iterator = std::back_insert_iterator<Container>;
|
enum { buffer_size = 256 };
|
||||||
struct accessor : bi_iterator {
|
T data_[buffer_size];
|
||||||
accessor(bi_iterator iter) : bi_iterator(iter) {}
|
size_t count_ = 0;
|
||||||
using bi_iterator::container;
|
|
||||||
};
|
protected:
|
||||||
return *accessor(it).container;
|
void grow(size_t) final FMT_OVERRIDE {
|
||||||
|
if (this->size() != buffer_size) return;
|
||||||
|
count_ += this->size();
|
||||||
|
this->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
counting_buffer() : buffer<T>(data_, 0, buffer_size) {}
|
||||||
|
|
||||||
|
size_t count() { return count_ + this->size(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
// An output iterator that appends to the buffer.
|
||||||
|
// It is used to reduce symbol sizes for the common case.
|
||||||
|
template <typename T>
|
||||||
|
class buffer_appender : public std::back_insert_iterator<buffer<T>> {
|
||||||
|
using base = std::back_insert_iterator<buffer<T>>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit buffer_appender(buffer<T>& buf) : base(buf) {}
|
||||||
|
buffer_appender(base it) : base(it) {}
|
||||||
|
|
||||||
|
buffer_appender& operator++() {
|
||||||
|
base::operator++();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_appender operator++(int) {
|
||||||
|
buffer_appender tmp = *this;
|
||||||
|
++*this;
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Maps an output iterator into a buffer.
|
||||||
|
template <typename T, typename OutputIt>
|
||||||
|
iterator_buffer<OutputIt, T> get_buffer(OutputIt);
|
||||||
|
template <typename T> buffer<T>& get_buffer(buffer_appender<T>);
|
||||||
|
|
||||||
|
template <typename OutputIt> OutputIt get_buffer_init(OutputIt out) {
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
template <typename T> buffer<T>& get_buffer_init(buffer_appender<T> out) {
|
||||||
|
return get_container(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Buffer>
|
||||||
|
auto get_iterator(Buffer& buf) -> decltype(buf.out()) {
|
||||||
|
return buf.out();
|
||||||
|
}
|
||||||
|
template <typename T> buffer_appender<T> get_iterator(buffer<T>& buf) {
|
||||||
|
return buffer_appender<T>(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, typename Char = char, typename Enable = void>
|
template <typename T, typename Char = char, typename Enable = void>
|
||||||
@@ -759,7 +918,8 @@ template <typename Char> struct named_arg_info {
|
|||||||
template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
|
template <typename T, typename Char, size_t NUM_ARGS, size_t NUM_NAMED_ARGS>
|
||||||
struct arg_data {
|
struct arg_data {
|
||||||
// args_[0].named_args points to named_args_ to avoid bloating format_args.
|
// args_[0].named_args points to named_args_ to avoid bloating format_args.
|
||||||
T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : 1)];
|
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
|
||||||
|
T args_[1 + (NUM_ARGS != 0 ? NUM_ARGS : +1)];
|
||||||
named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
|
named_arg_info<Char> named_args_[NUM_NAMED_ARGS];
|
||||||
|
|
||||||
template <typename... U>
|
template <typename... U>
|
||||||
@@ -771,7 +931,8 @@ struct arg_data {
|
|||||||
|
|
||||||
template <typename T, typename Char, size_t NUM_ARGS>
|
template <typename T, typename Char, size_t NUM_ARGS>
|
||||||
struct arg_data<T, Char, NUM_ARGS, 0> {
|
struct arg_data<T, Char, NUM_ARGS, 0> {
|
||||||
T args_[NUM_ARGS != 0 ? NUM_ARGS : 1];
|
// +1 to workaround a bug in gcc 7.5 that causes duplicated-branches warning.
|
||||||
|
T args_[NUM_ARGS != 0 ? NUM_ARGS : +1];
|
||||||
|
|
||||||
template <typename... U>
|
template <typename... U>
|
||||||
FMT_INLINE arg_data(const U&... init) : args_{init...} {}
|
FMT_INLINE arg_data(const U&... init) : args_{init...} {}
|
||||||
@@ -959,6 +1120,8 @@ enum { long_short = sizeof(long) == sizeof(int) };
|
|||||||
using long_type = conditional_t<long_short, int, long long>;
|
using long_type = conditional_t<long_short, int, long long>;
|
||||||
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
|
using ulong_type = conditional_t<long_short, unsigned, unsigned long long>;
|
||||||
|
|
||||||
|
struct unformattable {};
|
||||||
|
|
||||||
// Maps formatting arguments to core types.
|
// Maps formatting arguments to core types.
|
||||||
template <typename Context> struct arg_mapper {
|
template <typename Context> struct arg_mapper {
|
||||||
using char_type = typename Context::char_type;
|
using char_type = typename Context::char_type;
|
||||||
@@ -1067,15 +1230,7 @@ template <typename Context> struct arg_mapper {
|
|||||||
return map(val.value);
|
return map(val.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
int map(...) {
|
unformattable map(...) { return {}; }
|
||||||
constexpr bool formattable = sizeof(Context) == 0;
|
|
||||||
static_assert(
|
|
||||||
formattable,
|
|
||||||
"Cannot format argument. To make type T formattable provide a "
|
|
||||||
"formatter<T> specialization: "
|
|
||||||
"https://fmt.dev/latest/api.html#formatting-user-defined-types");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// A type constant after applying arg_mapper<Context>.
|
// A type constant after applying arg_mapper<Context>.
|
||||||
@@ -1199,15 +1354,25 @@ FMT_CONSTEXPR_DECL FMT_INLINE auto visit_format_arg(
|
|||||||
return vis(monostate());
|
return vis(monostate());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks whether T is a container with contiguous storage.
|
template <typename T> struct formattable : std::false_type {};
|
||||||
template <typename T> struct is_contiguous : std::false_type {};
|
|
||||||
template <typename Char>
|
|
||||||
struct is_contiguous<std::basic_string<Char>> : std::true_type {};
|
|
||||||
template <typename Char>
|
|
||||||
struct is_contiguous<detail::buffer<Char>> : std::true_type {};
|
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
|
// A workaround for gcc 4.8 to make void_t work in a SFINAE context.
|
||||||
|
template <typename... Ts> struct void_t_impl { using type = void; };
|
||||||
|
template <typename... Ts>
|
||||||
|
using void_t = typename detail::void_t_impl<Ts...>::type;
|
||||||
|
|
||||||
|
template <typename It, typename T, typename Enable = void>
|
||||||
|
struct is_output_iterator : std::false_type {};
|
||||||
|
|
||||||
|
template <typename It, typename T>
|
||||||
|
struct is_output_iterator<
|
||||||
|
It, T,
|
||||||
|
void_t<typename std::iterator_traits<It>::iterator_category,
|
||||||
|
decltype(*std::declval<It>() = std::declval<T>())>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
template <typename OutputIt>
|
template <typename OutputIt>
|
||||||
struct is_back_insert_iterator : std::false_type {};
|
struct is_back_insert_iterator : std::false_type {};
|
||||||
template <typename Container>
|
template <typename Container>
|
||||||
@@ -1219,6 +1384,9 @@ struct is_contiguous_back_insert_iterator : std::false_type {};
|
|||||||
template <typename Container>
|
template <typename Container>
|
||||||
struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
|
struct is_contiguous_back_insert_iterator<std::back_insert_iterator<Container>>
|
||||||
: is_contiguous<Container> {};
|
: is_contiguous<Container> {};
|
||||||
|
template <typename Char>
|
||||||
|
struct is_contiguous_back_insert_iterator<buffer_appender<Char>>
|
||||||
|
: std::true_type {};
|
||||||
|
|
||||||
// A type-erased reference to an std::locale to avoid heavy <locale> include.
|
// A type-erased reference to an std::locale to avoid heavy <locale> include.
|
||||||
class locale_ref {
|
class locale_ref {
|
||||||
@@ -1250,13 +1418,24 @@ FMT_CONSTEXPR basic_format_arg<Context> make_arg(const T& value) {
|
|||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T> int check(unformattable) {
|
||||||
|
static_assert(
|
||||||
|
formattable<T>(),
|
||||||
|
"Cannot format an argument. To make type T formattable provide a "
|
||||||
|
"formatter<T> specialization: https://fmt.dev/latest/api.html#udt");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
template <typename T, typename U> inline const U& check(const U& val) {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
// The type template parameter is there to avoid an ODR violation when using
|
// The type template parameter is there to avoid an ODR violation when using
|
||||||
// a fallback formatter in one translation unit and an implicit conversion in
|
// a fallback formatter in one translation unit and an implicit conversion in
|
||||||
// another (not recommended).
|
// another (not recommended).
|
||||||
template <bool IS_PACKED, typename Context, type, typename T,
|
template <bool IS_PACKED, typename Context, type, typename T,
|
||||||
FMT_ENABLE_IF(IS_PACKED)>
|
FMT_ENABLE_IF(IS_PACKED)>
|
||||||
inline value<Context> make_arg(const T& val) {
|
inline value<Context> make_arg(const T& val) {
|
||||||
return arg_mapper<Context>().map(val);
|
return check<T>(arg_mapper<Context>().map(val));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool IS_PACKED, typename Context, type, typename T,
|
template <bool IS_PACKED, typename Context, type, typename T,
|
||||||
@@ -1356,13 +1535,13 @@ template <typename OutputIt, typename Char> class basic_format_context {
|
|||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
using buffer_context =
|
using buffer_context =
|
||||||
basic_format_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
|
basic_format_context<detail::buffer_appender<Char>, Char>;
|
||||||
using format_context = buffer_context<char>;
|
using format_context = buffer_context<char>;
|
||||||
using wformat_context = buffer_context<wchar_t>;
|
using wformat_context = buffer_context<wchar_t>;
|
||||||
|
|
||||||
// Workaround a bug in gcc: https://stackoverflow.com/q/62767544/471164.
|
// Workaround an alias issue: https://stackoverflow.com/q/62767544/471164.
|
||||||
#define FMT_BUFFER_CONTEXT(Char) \
|
#define FMT_BUFFER_CONTEXT(Char) \
|
||||||
basic_format_context<std::back_insert_iterator<detail::buffer<Char>>, Char>
|
basic_format_context<detail::buffer_appender<Char>, Char>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
@@ -1414,7 +1593,7 @@ class format_arg_store
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Constructs an `~fmt::format_arg_store` object that contains references to
|
Constructs a `~fmt::format_arg_store` object that contains references to
|
||||||
arguments and can be implicitly converted to `~fmt::format_args`. `Context`
|
arguments and can be implicitly converted to `~fmt::format_args`. `Context`
|
||||||
can be omitted in which case it defaults to `~fmt::context`.
|
can be omitted in which case it defaults to `~fmt::context`.
|
||||||
See `~fmt::arg` for lifetime considerations.
|
See `~fmt::arg` for lifetime considerations.
|
||||||
@@ -1426,6 +1605,27 @@ inline format_arg_store<Context, Args...> make_format_args(
|
|||||||
return {args...};
|
return {args...};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Constructs a `~fmt::format_arg_store` object that contains references
|
||||||
|
to arguments and can be implicitly converted to `~fmt::format_args`.
|
||||||
|
If ``format_str`` is a compile-time string then `make_args_checked` checks
|
||||||
|
its validity at compile time.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename... Args, typename S, typename Char = char_t<S>>
|
||||||
|
inline auto make_args_checked(const S& format_str,
|
||||||
|
const remove_reference_t<Args>&... args)
|
||||||
|
-> format_arg_store<buffer_context<Char>, remove_reference_t<Args>...> {
|
||||||
|
static_assert(
|
||||||
|
detail::count<(
|
||||||
|
std::is_base_of<detail::view, remove_reference_t<Args>>::value &&
|
||||||
|
std::is_reference<Args>::value)...>() == 0,
|
||||||
|
"passing views as lvalues is disallowed");
|
||||||
|
detail::check_format_string<Args...>(format_str);
|
||||||
|
return {args...};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Returns a named argument to be used in a formatting function. It should only
|
Returns a named argument to be used in a formatting function. It should only
|
||||||
@@ -1729,7 +1929,14 @@ template <typename Context> class basic_format_args {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** An alias to ``basic_format_args<context>``. */
|
#ifdef FMT_ARM_ABI_COMPATIBILITY
|
||||||
|
/** An alias to ``basic_format_args<format_context>``. */
|
||||||
|
// Separate types would result in shorter symbols but break ABI compatibility
|
||||||
|
// between clang and gcc on ARM (#1919).
|
||||||
|
using format_args = basic_format_args<format_context>;
|
||||||
|
using wformat_args = basic_format_args<wformat_context>;
|
||||||
|
#else
|
||||||
|
// DEPRECATED! These are kept for ABI compatibility.
|
||||||
// It is a separate type rather than an alias to make symbols readable.
|
// It is a separate type rather than an alias to make symbols readable.
|
||||||
struct format_args : basic_format_args<format_context> {
|
struct format_args : basic_format_args<format_context> {
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
@@ -1738,32 +1945,10 @@ struct format_args : basic_format_args<format_context> {
|
|||||||
struct wformat_args : basic_format_args<wformat_context> {
|
struct wformat_args : basic_format_args<wformat_context> {
|
||||||
using basic_format_args::basic_format_args;
|
using basic_format_args::basic_format_args;
|
||||||
};
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
// Reports a compile-time error if S is not a valid format string.
|
|
||||||
template <typename..., typename S, FMT_ENABLE_IF(!is_compile_string<S>::value)>
|
|
||||||
FMT_INLINE void check_format_string(const S&) {
|
|
||||||
#ifdef FMT_ENFORCE_COMPILE_STRING
|
|
||||||
static_assert(is_compile_string<S>::value,
|
|
||||||
"FMT_ENFORCE_COMPILE_STRING requires all format strings to use "
|
|
||||||
"FMT_STRING.");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
template <typename..., typename S, FMT_ENABLE_IF(is_compile_string<S>::value)>
|
|
||||||
void check_format_string(S);
|
|
||||||
|
|
||||||
template <typename... Args, typename S, typename Char = char_t<S>>
|
|
||||||
inline format_arg_store<buffer_context<Char>, remove_reference_t<Args>...>
|
|
||||||
make_args_checked(const S& format_str,
|
|
||||||
const remove_reference_t<Args>&... args) {
|
|
||||||
static_assert(count<(std::is_base_of<view, remove_reference_t<Args>>::value &&
|
|
||||||
std::is_reference<Args>::value)...>() == 0,
|
|
||||||
"passing views as lvalues is disallowed");
|
|
||||||
check_format_string<Args...>(format_str);
|
|
||||||
return {args...};
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||||
std::basic_string<Char> vformat(
|
std::basic_string<Char> vformat(
|
||||||
basic_string_view<Char> format_str,
|
basic_string_view<Char> format_str,
|
||||||
@@ -1772,9 +1957,10 @@ std::basic_string<Char> vformat(
|
|||||||
FMT_API std::string vformat(string_view format_str, format_args args);
|
FMT_API std::string vformat(string_view format_str, format_args args);
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
typename FMT_BUFFER_CONTEXT(Char)::iterator vformat_to(
|
void vformat_to(
|
||||||
buffer<Char>& buf, basic_string_view<Char> format_str,
|
buffer<Char>& buf, basic_string_view<Char> format_str,
|
||||||
basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args);
|
basic_format_args<FMT_BUFFER_CONTEXT(type_identity_t<Char>)> args,
|
||||||
|
detail::locale_ref loc = {});
|
||||||
|
|
||||||
template <typename Char, typename Args,
|
template <typename Char, typename Args,
|
||||||
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||||
@@ -1789,26 +1975,80 @@ inline void vprint_mojibake(std::FILE*, string_view, format_args) {}
|
|||||||
/** Formats a string and writes the output to ``out``. */
|
/** Formats a string and writes the output to ``out``. */
|
||||||
// GCC 8 and earlier cannot handle std::back_insert_iterator<Container> with
|
// GCC 8 and earlier cannot handle std::back_insert_iterator<Container> with
|
||||||
// vformat_to<ArgFormatter>(...) overload, so SFINAE on iterator type instead.
|
// vformat_to<ArgFormatter>(...) overload, so SFINAE on iterator type instead.
|
||||||
template <
|
template <typename OutputIt, typename S, typename Char = char_t<S>,
|
||||||
typename OutputIt, typename S, typename Char = char_t<S>,
|
bool enable = detail::is_output_iterator<OutputIt, Char>::value>
|
||||||
FMT_ENABLE_IF(detail::is_contiguous_back_insert_iterator<OutputIt>::value)>
|
auto vformat_to(OutputIt out, const S& format_str,
|
||||||
OutputIt vformat_to(
|
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||||
OutputIt out, const S& format_str,
|
-> typename std::enable_if<enable, OutputIt>::type {
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
|
||||||
auto& c = detail::get_container(out);
|
|
||||||
detail::container_buffer<remove_reference_t<decltype(c)>> buf(c);
|
|
||||||
detail::vformat_to(buf, to_string_view(format_str), args);
|
detail::vformat_to(buf, to_string_view(format_str), args);
|
||||||
return out;
|
return detail::get_iterator(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Container, typename S, typename... Args,
|
/**
|
||||||
FMT_ENABLE_IF(
|
\rst
|
||||||
is_contiguous<Container>::value&& detail::is_string<S>::value)>
|
Formats arguments, writes the result to the output iterator ``out`` and returns
|
||||||
inline std::back_insert_iterator<Container> format_to(
|
the iterator past the end of the output range.
|
||||||
std::back_insert_iterator<Container> out, const S& format_str,
|
|
||||||
Args&&... args) {
|
**Example**::
|
||||||
return vformat_to(out, to_string_view(format_str),
|
|
||||||
detail::make_args_checked<Args...>(format_str, args...));
|
std::vector<char> out;
|
||||||
|
fmt::format_to(std::back_inserter(out), "{}", 42);
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
// We cannot use FMT_ENABLE_IF because of a bug in gcc 8.3.
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
|
||||||
|
inline auto format_to(OutputIt out, const S& format_str, Args&&... args) ->
|
||||||
|
typename std::enable_if<enable, OutputIt>::type {
|
||||||
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
|
return vformat_to(out, to_string_view(format_str), vargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename OutputIt> struct format_to_n_result {
|
||||||
|
/** Iterator past the end of the output range. */
|
||||||
|
OutputIt out;
|
||||||
|
/** Total (not truncated) output size. */
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename OutputIt, typename Char, typename... Args,
|
||||||
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
|
||||||
|
inline format_to_n_result<OutputIt> vformat_to_n(
|
||||||
|
OutputIt out, size_t n, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
|
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
|
||||||
|
n);
|
||||||
|
detail::vformat_to(buf, format_str, args);
|
||||||
|
return {buf.out(), buf.count()};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
\rst
|
||||||
|
Formats arguments, writes up to ``n`` characters of the result to the output
|
||||||
|
iterator ``out`` and returns the total output size and the iterator past the
|
||||||
|
end of the output range.
|
||||||
|
\endrst
|
||||||
|
*/
|
||||||
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
|
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
|
||||||
|
inline auto format_to_n(OutputIt out, size_t n, const S& format_str,
|
||||||
|
const Args&... args) ->
|
||||||
|
typename std::enable_if<enable, format_to_n_result<OutputIt>>::type {
|
||||||
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
|
return vformat_to_n(out, n, to_string_view(format_str), vargs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Returns the number of characters in the output of
|
||||||
|
``format(format_str, args...)``.
|
||||||
|
*/
|
||||||
|
template <typename... Args>
|
||||||
|
inline size_t formatted_size(string_view format_str, Args&&... args) {
|
||||||
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
|
detail::counting_buffer<> buf;
|
||||||
|
detail::vformat_to(buf, format_str, vargs);
|
||||||
|
return buf.count();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename Char = char_t<S>>
|
template <typename S, typename Char = char_t<S>>
|
||||||
@@ -1832,7 +2072,7 @@ FMT_INLINE std::basic_string<Char> vformat(
|
|||||||
// std::basic_string<char_t<S>> to reduce the symbol size.
|
// std::basic_string<char_t<S>> to reduce the symbol size.
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
FMT_INLINE std::basic_string<Char> format(const S& format_str, Args&&... args) {
|
FMT_INLINE std::basic_string<Char> format(const S& format_str, Args&&... args) {
|
||||||
const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
return detail::vformat(to_string_view(format_str), vargs);
|
return detail::vformat(to_string_view(format_str), vargs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1852,7 +2092,7 @@ FMT_API void vprint(std::FILE*, string_view, format_args);
|
|||||||
*/
|
*/
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
inline void print(std::FILE* f, const S& format_str, Args&&... args) {
|
inline void print(std::FILE* f, const S& format_str, Args&&... args) {
|
||||||
const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
return detail::is_unicode<Char>()
|
return detail::is_unicode<Char>()
|
||||||
? vprint(f, to_string_view(format_str), vargs)
|
? vprint(f, to_string_view(format_str), vargs)
|
||||||
: detail::vprint_mojibake(f, to_string_view(format_str), vargs);
|
: detail::vprint_mojibake(f, to_string_view(format_str), vargs);
|
||||||
@@ -1871,7 +2111,7 @@ inline void print(std::FILE* f, const S& format_str, Args&&... args) {
|
|||||||
*/
|
*/
|
||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
inline void print(const S& format_str, Args&&... args) {
|
inline void print(const S& format_str, Args&&... args) {
|
||||||
const auto& vargs = detail::make_args_checked<Args...>(format_str, args...);
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
return detail::is_unicode<Char>()
|
return detail::is_unicode<Char>()
|
||||||
? vprint(to_string_view(format_str), vargs)
|
? vprint(to_string_view(format_str), vargs)
|
||||||
: detail::vprint_mojibake(stdout, to_string_view(format_str),
|
: detail::vprint_mojibake(stdout, to_string_view(format_str),
|
||||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -15,22 +15,12 @@
|
|||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
template <typename Char>
|
|
||||||
typename buffer_context<Char>::iterator vformat_to(
|
|
||||||
const std::locale& loc, buffer<Char>& buf,
|
|
||||||
basic_string_view<Char> format_str,
|
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
|
||||||
using af = arg_formatter<typename buffer_context<Char>::iterator, Char>;
|
|
||||||
return vformat_to<af>(std::back_inserter(buf), to_string_view(format_str),
|
|
||||||
args, detail::locale_ref(loc));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
std::basic_string<Char> vformat(
|
std::basic_string<Char> vformat(
|
||||||
const std::locale& loc, basic_string_view<Char> format_str,
|
const std::locale& loc, basic_string_view<Char> format_str,
|
||||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
basic_memory_buffer<Char> buffer;
|
basic_memory_buffer<Char> buffer;
|
||||||
detail::vformat_to(loc, buffer, format_str, args);
|
detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
|
||||||
return fmt::to_string(buffer);
|
return fmt::to_string(buffer);
|
||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
@@ -45,32 +35,28 @@ inline std::basic_string<Char> vformat(
|
|||||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||||
inline std::basic_string<Char> format(const std::locale& loc,
|
inline std::basic_string<Char> format(const std::locale& loc,
|
||||||
const S& format_str, Args&&... args) {
|
const S& format_str, Args&&... args) {
|
||||||
return detail::vformat(
|
return detail::vformat(loc, to_string_view(format_str),
|
||||||
loc, to_string_view(format_str),
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
detail::make_args_checked<Args...>(format_str, args...));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename OutputIt, typename... Args,
|
template <typename S, typename OutputIt, typename... Args,
|
||||||
typename Char = enable_if_t<
|
typename Char = char_t<S>,
|
||||||
detail::is_output_iterator<OutputIt>::value, char_t<S>>>
|
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
|
||||||
inline OutputIt vformat_to(
|
inline OutputIt vformat_to(
|
||||||
OutputIt out, const std::locale& loc, const S& format_str,
|
OutputIt out, const std::locale& loc, const S& format_str,
|
||||||
format_args_t<type_identity_t<OutputIt>, Char> args) {
|
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||||
using af = detail::arg_formatter<OutputIt, Char>;
|
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
|
||||||
return vformat_to<af>(out, to_string_view(format_str), args,
|
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
|
||||||
detail::locale_ref(loc));
|
return detail::get_iterator(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename OutputIt, typename S, typename... Args,
|
template <typename OutputIt, typename S, typename... Args,
|
||||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt>::value&&
|
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
|
||||||
detail::is_string<S>::value)>
|
inline auto format_to(OutputIt out, const std::locale& loc,
|
||||||
inline OutputIt format_to(OutputIt out, const std::locale& loc,
|
const S& format_str, Args&&... args) ->
|
||||||
const S& format_str, Args&&... args) {
|
typename std::enable_if<enable, OutputIt>::type {
|
||||||
detail::check_format_string<Args...>(format_str);
|
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
|
||||||
using context = format_context_t<OutputIt, char_t<S>>;
|
return vformat_to(out, loc, to_string_view(format_str), vargs);
|
||||||
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
|
FMT_END_NAMESPACE
|
||||||
|
@@ -29,7 +29,8 @@
|
|||||||
#if FMT_HAS_INCLUDE("winapifamily.h")
|
#if FMT_HAS_INCLUDE("winapifamily.h")
|
||||||
# include <winapifamily.h>
|
# include <winapifamily.h>
|
||||||
#endif
|
#endif
|
||||||
#if FMT_HAS_INCLUDE("fcntl.h") && \
|
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
||||||
|
defined(__linux__)) && \
|
||||||
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||||
# include <fcntl.h> // for O_RDONLY
|
# include <fcntl.h> // for O_RDONLY
|
||||||
# define FMT_USE_FCNTL 1
|
# define FMT_USE_FCNTL 1
|
||||||
@@ -278,7 +279,8 @@ class file {
|
|||||||
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.
|
||||||
CREATE = FMT_POSIX(O_CREAT) // Create if the file doesn't exist.
|
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
|
||||||
|
APPEND = FMT_POSIX(O_APPEND) // Open in append mode.
|
||||||
};
|
};
|
||||||
|
|
||||||
// Constructs a file object which doesn't represent any file.
|
// Constructs a file object which doesn't represent any file.
|
||||||
@@ -343,36 +345,69 @@ class file {
|
|||||||
// Returns the memory page size.
|
// Returns the memory page size.
|
||||||
long getpagesize();
|
long getpagesize();
|
||||||
|
|
||||||
class direct_buffered_file;
|
namespace detail {
|
||||||
|
|
||||||
template <typename S, typename... Args>
|
struct buffer_size {
|
||||||
void print(direct_buffered_file& f, const S& format_str,
|
size_t value = 0;
|
||||||
const Args&... args);
|
buffer_size operator=(size_t val) const {
|
||||||
|
auto bs = buffer_size();
|
||||||
|
bs.value = val;
|
||||||
|
return bs;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// A buffered file with a direct buffer access and no synchronization.
|
struct ostream_params {
|
||||||
class direct_buffered_file {
|
int oflag = file::WRONLY | file::CREATE;
|
||||||
|
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
|
||||||
|
|
||||||
|
ostream_params() {}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
ostream_params(T... params, int oflag) : ostream_params(params...) {
|
||||||
|
this->oflag = oflag;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
ostream_params(T... params, detail::buffer_size bs)
|
||||||
|
: ostream_params(params...) {
|
||||||
|
this->buffer_size = bs.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
|
static constexpr detail::buffer_size buffer_size;
|
||||||
|
|
||||||
|
// A fast output stream which is not thread-safe.
|
||||||
|
class ostream final : private detail::buffer<char> {
|
||||||
private:
|
private:
|
||||||
file file_;
|
file file_;
|
||||||
|
|
||||||
enum { buffer_size = 4096 };
|
|
||||||
char buffer_[buffer_size];
|
|
||||||
int pos_;
|
|
||||||
|
|
||||||
void flush() {
|
void flush() {
|
||||||
if (pos_ == 0) return;
|
if (size() == 0) return;
|
||||||
file_.write(buffer_, pos_);
|
file_.write(data(), size());
|
||||||
pos_ = 0;
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
int free_capacity() const { return buffer_size - pos_; }
|
FMT_API void grow(size_t) override final;
|
||||||
|
|
||||||
|
ostream(cstring_view path, const detail::ostream_params& params)
|
||||||
|
: file_(path, params.oflag) {
|
||||||
|
set(new char[params.buffer_size], params.buffer_size);
|
||||||
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
direct_buffered_file(cstring_view path, int oflag)
|
ostream(ostream&& other)
|
||||||
: file_(path, oflag), pos_(0) {}
|
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
|
||||||
|
file_(std::move(other.file_)) {
|
||||||
~direct_buffered_file() {
|
other.set(nullptr, 0);
|
||||||
flush();
|
|
||||||
}
|
}
|
||||||
|
~ostream() {
|
||||||
|
flush();
|
||||||
|
delete[] data();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
friend ostream output_file(cstring_view path, T... params);
|
||||||
|
|
||||||
void close() {
|
void close() {
|
||||||
flush();
|
flush();
|
||||||
@@ -380,25 +415,20 @@ class direct_buffered_file {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename S, typename... Args>
|
template <typename S, typename... Args>
|
||||||
friend void print(direct_buffered_file& f, const S& format_str,
|
void print(const S& format_str, const Args&... args) {
|
||||||
const Args&... args) {
|
format_to(detail::buffer_appender<char>(*this), format_str, args...);
|
||||||
// We could avoid double buffering.
|
|
||||||
auto buf = fmt::memory_buffer();
|
|
||||||
fmt::format_to(std::back_inserter(buf), format_str, args...);
|
|
||||||
auto remaining_pos = 0;
|
|
||||||
auto remaining_size = buf.size();
|
|
||||||
while (remaining_size > detail::to_unsigned(f.free_capacity())) {
|
|
||||||
auto size = f.free_capacity();
|
|
||||||
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, size);
|
|
||||||
f.pos_ += size;
|
|
||||||
f.flush();
|
|
||||||
remaining_pos += size;
|
|
||||||
remaining_size -= size;
|
|
||||||
}
|
|
||||||
memcpy(f.buffer_ + f.pos_, buf.data() + remaining_pos, remaining_size);
|
|
||||||
f.pos_ += static_cast<int>(remaining_size);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
Opens a file for writing. Supported parameters passed in `params`:
|
||||||
|
* ``<integer>``: Output flags (``file::WRONLY | file::CREATE`` by default)
|
||||||
|
* ``buffer_size=<integer>``: Output buffer size
|
||||||
|
*/
|
||||||
|
template <typename... T>
|
||||||
|
inline ostream output_file(cstring_view path, T... params) {
|
||||||
|
return {path, detail::ostream_params(params...)};
|
||||||
|
}
|
||||||
#endif // FMT_USE_FCNTL
|
#endif // FMT_USE_FCNTL
|
||||||
|
|
||||||
#ifdef FMT_LOCALE
|
#ifdef FMT_LOCALE
|
||||||
|
@@ -49,17 +49,27 @@ template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct converter {
|
||||||
|
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
|
||||||
|
};
|
||||||
|
|
||||||
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
template <typename Char> struct test_stream : std::basic_ostream<Char> {
|
||||||
private:
|
private:
|
||||||
// Hide all operator<< from std::basic_ostream<Char>.
|
void_t<> operator<<(converter);
|
||||||
void_t<> operator<<(null<>);
|
|
||||||
void_t<> operator<<(const Char*);
|
|
||||||
|
|
||||||
template <typename T, FMT_ENABLE_IF(std::is_convertible<T, int>::value &&
|
|
||||||
!std::is_enum<T>::value)>
|
|
||||||
void_t<> operator<<(T);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Hide insertion operators for built-in types.
|
||||||
|
template <typename Char, typename Traits>
|
||||||
|
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
|
||||||
|
template <typename Char, typename Traits>
|
||||||
|
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
|
||||||
|
template <typename Traits>
|
||||||
|
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
|
||||||
|
template <typename Traits>
|
||||||
|
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
|
||||||
|
template <typename Traits>
|
||||||
|
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
|
||||||
|
|
||||||
// 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> class is_streamable {
|
template <typename T, typename Char> class is_streamable {
|
||||||
@@ -103,7 +113,7 @@ void format_value(buffer<Char>& buf, const T& value,
|
|||||||
#endif
|
#endif
|
||||||
output << value;
|
output << value;
|
||||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||||
buf.resize(buf.size());
|
buf.try_resize(buf.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||||
@@ -160,7 +170,7 @@ template <typename S, typename... Args,
|
|||||||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||||
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {
|
||||||
vprint(os, to_string_view(format_str),
|
vprint(os, to_string_view(format_str),
|
||||||
detail::make_args_checked<Args...>(format_str, args...));
|
fmt::make_args_checked<Args...>(format_str, args...));
|
||||||
}
|
}
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
|
@@ -181,7 +181,7 @@ template <typename Char> class printf_width_handler {
|
|||||||
template <typename Char, typename Context>
|
template <typename Char, typename Context>
|
||||||
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||||
basic_format_args<Context> args) {
|
basic_format_args<Context> args) {
|
||||||
Context(std::back_inserter(buf), format, args).format();
|
Context(buffer_appender<Char>(buf), format, args).format();
|
||||||
}
|
}
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
|
|
||||||
@@ -598,7 +598,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
|||||||
|
|
||||||
template <typename Char>
|
template <typename Char>
|
||||||
using basic_printf_context_t =
|
using basic_printf_context_t =
|
||||||
basic_printf_context<std::back_insert_iterator<detail::buffer<Char>>, Char>;
|
basic_printf_context<detail::buffer_appender<Char>, Char>;
|
||||||
|
|
||||||
using printf_context = basic_printf_context_t<char>;
|
using printf_context = basic_printf_context_t<char>;
|
||||||
using wprintf_context = basic_printf_context_t<wchar_t>;
|
using wprintf_context = basic_printf_context_t<wchar_t>;
|
||||||
|
@@ -157,6 +157,9 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
|
|||||||
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Range>
|
||||||
|
using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>;
|
||||||
|
|
||||||
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
|
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string<
|
||||||
typename std::decay<Arg>::type>::value)>
|
typename std::decay<Arg>::type>::value)>
|
||||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||||
@@ -182,7 +185,6 @@ FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
|
|||||||
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 detail
|
} // namespace detail
|
||||||
|
|
||||||
template <typename T> struct is_tuple_like {
|
template <typename T> struct is_tuple_like {
|
||||||
@@ -246,9 +248,18 @@ template <typename T, typename Char> struct is_range {
|
|||||||
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
!std::is_constructible<detail::std_string_view<Char>, T>::value;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename RangeT, typename Char>
|
template <typename T, typename Char>
|
||||||
struct formatter<RangeT, Char,
|
struct formatter<
|
||||||
enable_if_t<fmt::is_range<RangeT, Char>::value>> {
|
T, Char,
|
||||||
|
enable_if_t<fmt::is_range<T, Char>::value
|
||||||
|
// Workaround a bug in MSVC 2017 and earlier.
|
||||||
|
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
|
||||||
|
&&
|
||||||
|
(has_formatter<detail::value_type<T>, format_context>::value ||
|
||||||
|
detail::has_fallback_formatter<detail::value_type<T>,
|
||||||
|
format_context>::value)
|
||||||
|
#endif
|
||||||
|
>> {
|
||||||
formatting_range<Char> formatting;
|
formatting_range<Char> formatting;
|
||||||
|
|
||||||
template <typename ParseContext>
|
template <typename ParseContext>
|
||||||
@@ -257,8 +268,7 @@ struct formatter<RangeT, Char,
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename FormatContext>
|
template <typename FormatContext>
|
||||||
typename FormatContext::iterator format(const RangeT& values,
|
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
|
||||||
FormatContext& ctx) {
|
|
||||||
auto out = detail::copy(formatting.prefix, ctx.out());
|
auto out = detail::copy(formatting.prefix, ctx.out());
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
auto it = values.begin();
|
auto it = values.begin();
|
||||||
|
@@ -75,58 +75,58 @@ public:
|
|||||||
|
|
||||||
// FormatString is a type derived from fmt::compile_string
|
// FormatString is a type derived from fmt::compile_string
|
||||||
template<typename FormatString, typename std::enable_if<fmt::is_compile_string<FormatString>::value, int>::type = 0, typename... Args>
|
template<typename FormatString, typename std::enable_if<fmt::is_compile_string<FormatString>::value, int>::type = 0, typename... Args>
|
||||||
void log(source_loc loc, level::level_enum lvl, const FormatString &fmt, const Args &...args)
|
void log(source_loc loc, level::level_enum lvl, const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
log_(loc, lvl, fmt, args...);
|
log_(loc, lvl, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormatString is NOT a type derived from fmt::compile_string but is a string_view_t or can be implicitly converted to one
|
// FormatString is NOT a type derived from fmt::compile_string but is a string_view_t or can be implicitly converted to one
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void log(source_loc loc, level::level_enum lvl, string_view_t fmt, const Args &...args)
|
void log(source_loc loc, level::level_enum lvl, string_view_t fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
log_(loc, lvl, fmt, args...);
|
log_(loc, lvl, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
void log(level::level_enum lvl, const FormatString &fmt, const Args &...args)
|
void log(level::level_enum lvl, const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
log(source_loc{}, lvl, fmt, args...);
|
log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
void trace(const FormatString &fmt, const Args &...args)
|
void trace(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
log(level::trace, fmt, args...);
|
log(level::trace, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
void debug(const FormatString &fmt, const Args &...args)
|
void debug(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
log(level::debug, fmt, args...);
|
log(level::debug, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
void info(const FormatString &fmt, const Args &...args)
|
void info(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
log(level::info, fmt, args...);
|
log(level::info, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
void warn(const FormatString &fmt, const Args &...args)
|
void warn(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
log(level::warn, fmt, args...);
|
log(level::warn, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
void error(const FormatString &fmt, const Args &...args)
|
void error(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
log(level::err, fmt, args...);
|
log(level::err, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
void critical(const FormatString &fmt, const Args &...args)
|
void critical(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
log(level::critical, fmt, args...);
|
log(level::critical, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
@@ -225,7 +225,7 @@ public:
|
|||||||
#else
|
#else
|
||||||
|
|
||||||
template<typename... Args>
|
template<typename... Args>
|
||||||
void log(source_loc loc, level::level_enum lvl, wstring_view_t fmt, const Args &...args)
|
void log(source_loc loc, level::level_enum lvl, wstring_view_t fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
bool log_enabled = should_log(lvl);
|
bool log_enabled = should_log(lvl);
|
||||||
bool traceback_enabled = tracer_.enabled();
|
bool traceback_enabled = tracer_.enabled();
|
||||||
@@ -237,7 +237,7 @@ public:
|
|||||||
{
|
{
|
||||||
// format to wmemory_buffer and convert to utf8
|
// format to wmemory_buffer and convert to utf8
|
||||||
fmt::wmemory_buffer wbuf;
|
fmt::wmemory_buffer wbuf;
|
||||||
fmt::format_to(wbuf, fmt, args...);
|
fmt::format_to(wbuf, fmt, std::forward<Args>(args)...);
|
||||||
|
|
||||||
memory_buf_t buf;
|
memory_buf_t buf;
|
||||||
details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf);
|
details::os::wstr_to_utf8buf(wstring_view_t(wbuf.data(), wbuf.size()), buf);
|
||||||
@@ -326,7 +326,7 @@ protected:
|
|||||||
|
|
||||||
// common implementation for after templated public api has been resolved
|
// common implementation for after templated public api has been resolved
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
void log_(source_loc loc, level::level_enum lvl, const FormatString &fmt, const Args &...args)
|
void log_(source_loc loc, level::level_enum lvl, const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
bool log_enabled = should_log(lvl);
|
bool log_enabled = should_log(lvl);
|
||||||
bool traceback_enabled = tracer_.enabled();
|
bool traceback_enabled = tracer_.enabled();
|
||||||
@@ -337,7 +337,7 @@ protected:
|
|||||||
SPDLOG_TRY
|
SPDLOG_TRY
|
||||||
{
|
{
|
||||||
memory_buf_t buf;
|
memory_buf_t buf;
|
||||||
fmt::format_to(buf, fmt, args...);
|
fmt::format_to(buf, fmt, std::forward<Args>(args)...);
|
||||||
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
details::log_msg log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()));
|
||||||
log_it_(log_msg, log_enabled, traceback_enabled);
|
log_it_(log_msg, log_enabled, traceback_enabled);
|
||||||
}
|
}
|
||||||
|
@@ -13,11 +13,13 @@
|
|||||||
#include <spdlog/fmt/fmt.h>
|
#include <spdlog/fmt/fmt.h>
|
||||||
#include <spdlog/formatter.h>
|
#include <spdlog/formatter.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <iterator>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
@@ -127,7 +129,7 @@ public:
|
|||||||
|
|
||||||
void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
|
void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
|
||||||
{
|
{
|
||||||
string_view_t &level_name = level::to_string_view(msg.level);
|
const string_view_t &level_name = level::to_string_view(msg.level);
|
||||||
ScopedPadder p(level_name.size(), padinfo_, dest);
|
ScopedPadder p(level_name.size(), padinfo_, dest);
|
||||||
fmt_helper::append_string_view(level_name, dest);
|
fmt_helper::append_string_view(level_name, dest);
|
||||||
}
|
}
|
||||||
@@ -814,11 +816,31 @@ public:
|
|||||||
: flag_formatter(padinfo)
|
: flag_formatter(padinfo)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(push)
|
||||||
|
#pragma warning(disable : 4127) // consider using 'if constexpr' instead
|
||||||
|
#endif // _MSC_VER
|
||||||
static const char *basename(const char *filename)
|
static const char *basename(const char *filename)
|
||||||
{
|
{
|
||||||
const char *rv = std::strrchr(filename, os::folder_sep);
|
// if the size is 2 (1 character + null terminator) we can use the more efficient strrchr
|
||||||
return rv != nullptr ? rv + 1 : filename;
|
// the branch will be elided by optimizations
|
||||||
|
if (sizeof(os::folder_seps) == 2)
|
||||||
|
{
|
||||||
|
const char *rv = std::strrchr(filename, os::folder_seps[0]);
|
||||||
|
return rv != nullptr ? rv + 1 : filename;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const std::reverse_iterator<const char*> begin(filename + std::strlen(filename));
|
||||||
|
const std::reverse_iterator<const char*> end(filename);
|
||||||
|
|
||||||
|
const auto it = std::find_first_of(begin, end, std::begin(os::folder_seps), std::end(os::folder_seps) - 1);
|
||||||
|
return it != end ? it.base() : filename;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma warning(pop)
|
||||||
|
#endif // _MSC_VER
|
||||||
|
|
||||||
void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
|
void format(const details::log_msg &msg, const std::tm &, memory_buf_t &dest) override
|
||||||
{
|
{
|
||||||
|
@@ -92,9 +92,9 @@ public:
|
|||||||
void format(const details::log_msg &msg, memory_buf_t &dest) override;
|
void format(const details::log_msg &msg, memory_buf_t &dest) override;
|
||||||
|
|
||||||
template<typename T, typename... Args>
|
template<typename T, typename... Args>
|
||||||
pattern_formatter &add_flag(char flag, const Args &...args)
|
pattern_formatter &add_flag(char flag, Args&&...args)
|
||||||
{
|
{
|
||||||
custom_handlers_[flag] = details::make_unique<T>(args...);
|
custom_handlers_[flag] = details::make_unique<T>(std::forward<Args>(args)...);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
void set_pattern(std::string pattern);
|
void set_pattern(std::string pattern);
|
||||||
|
@@ -106,6 +106,8 @@ SPDLOG_INLINE void ansicolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
|
|||||||
case color_mode::never:
|
case color_mode::never:
|
||||||
should_do_colors_ = false;
|
should_do_colors_ = false;
|
||||||
return;
|
return;
|
||||||
|
default:
|
||||||
|
should_do_colors_ = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -7,6 +7,7 @@
|
|||||||
#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/fmt/chrono.h>
|
||||||
#include <spdlog/sinks/base_sink.h>
|
#include <spdlog/sinks/base_sink.h>
|
||||||
#include <spdlog/details/os.h>
|
#include <spdlog/details/os.h>
|
||||||
#include <spdlog/details/circular_q.h>
|
#include <spdlog/details/circular_q.h>
|
||||||
@@ -36,6 +37,23 @@ struct daily_filename_calculator
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generator of daily log file names with strftime format.
|
||||||
|
* Usages:
|
||||||
|
* auto sink = std::make_shared<spdlog::sinks::daily_file_format_sink_mt>("myapp-%Y-%m-%d:%H:%M:%S.log", hour, minute);"
|
||||||
|
* auto logger = spdlog::daily_logger_format_mt("loggername, "myapp-%Y-%m-%d:%X.log", hour, minute)"
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
struct daily_filename_format_calculator
|
||||||
|
{
|
||||||
|
static filename_t calc_filename (const filename_t &filename, const tm &now_tm)
|
||||||
|
{
|
||||||
|
// generate fmt datetime format string, e.g. {:%Y-%m-%d}.
|
||||||
|
filename_t fmt_filename = fmt::format(SPDLOG_FILENAME_T ("{{:{}}}"), filename);
|
||||||
|
return fmt::format(fmt_filename, now_tm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Rotating file sink based on date.
|
* Rotating file sink based on date.
|
||||||
* If truncate != false , the created file will be truncated.
|
* If truncate != false , the created file will be truncated.
|
||||||
@@ -182,6 +200,8 @@ private:
|
|||||||
|
|
||||||
using daily_file_sink_mt = daily_file_sink<std::mutex>;
|
using daily_file_sink_mt = daily_file_sink<std::mutex>;
|
||||||
using daily_file_sink_st = daily_file_sink<details::null_mutex>;
|
using daily_file_sink_st = daily_file_sink<details::null_mutex>;
|
||||||
|
using daily_file_format_sink_mt = daily_file_sink<std::mutex, daily_filename_format_calculator>;
|
||||||
|
using daily_file_format_sink_st = daily_file_sink<details::null_mutex, daily_filename_format_calculator>;
|
||||||
|
|
||||||
} // namespace sinks
|
} // namespace sinks
|
||||||
|
|
||||||
@@ -195,10 +215,24 @@ inline std::shared_ptr<logger> daily_logger_mt(
|
|||||||
return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate, max_files);
|
return Factory::template create<sinks::daily_file_sink_mt>(logger_name, filename, hour, minute, truncate, max_files);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
inline std::shared_ptr<logger> daily_logger_format_mt(
|
||||||
|
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::daily_file_format_sink_mt>(logger_name, filename, hour, minute, truncate, max_files);
|
||||||
|
}
|
||||||
|
|
||||||
template<typename Factory = spdlog::synchronous_factory>
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
inline std::shared_ptr<logger> daily_logger_st(
|
inline std::shared_ptr<logger> daily_logger_st(
|
||||||
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0)
|
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0)
|
||||||
{
|
{
|
||||||
return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate, max_files);
|
return Factory::template create<sinks::daily_file_sink_st>(logger_name, filename, hour, minute, truncate, max_files);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
inline std::shared_ptr<logger> daily_logger_format_st(
|
||||||
|
const std::string &logger_name, const filename_t &filename, int hour = 0, int minute = 0, bool truncate = false, uint16_t max_files = 0)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::daily_file_format_sink_st>(logger_name, filename, hour, minute, truncate, max_files);
|
||||||
|
}
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
@@ -64,7 +64,7 @@ protected:
|
|||||||
{
|
{
|
||||||
memory_buf_t buf;
|
memory_buf_t buf;
|
||||||
fmt::format_to(buf, "Skipped {} duplicate messages..", skip_counter_);
|
fmt::format_to(buf, "Skipped {} duplicate messages..", skip_counter_);
|
||||||
details::log_msg skipped_msg{msg.logger_name, msg.level, string_view_t{buf.data(), buf.size()}};
|
details::log_msg skipped_msg{msg.logger_name, level::info, string_view_t{buf.data(), buf.size()}};
|
||||||
dist_sink<Mutex>::sink_it_(skipped_msg);
|
dist_sink<Mutex>::sink_it_(skipped_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
194
include/spdlog/sinks/hourly_file_sink.h
Normal file
194
include/spdlog/sinks/hourly_file_sink.h
Normal file
@@ -0,0 +1,194 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <spdlog/common.h>
|
||||||
|
#include <spdlog/details/file_helper.h>
|
||||||
|
#include <spdlog/details/null_mutex.h>
|
||||||
|
#include <spdlog/fmt/fmt.h>
|
||||||
|
#include <spdlog/sinks/base_sink.h>
|
||||||
|
#include <spdlog/details/os.h>
|
||||||
|
#include <spdlog/details/circular_q.h>
|
||||||
|
#include <spdlog/details/synchronous_factory.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <ctime>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generator of Hourly log file names in format basename.YYYY-MM-DD-HH.ext
|
||||||
|
*/
|
||||||
|
struct hourly_filename_calculator
|
||||||
|
{
|
||||||
|
// Create filename for the form basename.YYYY-MM-DD-H
|
||||||
|
static filename_t calc_filename(const filename_t &filename, const tm &now_tm)
|
||||||
|
{
|
||||||
|
filename_t basename, ext;
|
||||||
|
std::tie(basename, ext) = details::file_helper::split_by_extension(filename);
|
||||||
|
return fmt::format(
|
||||||
|
SPDLOG_FILENAME_T("{}_{:04d}{:02d}{:02d}_{:02d}{}"), basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday, now_tm.tm_hour, ext);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rotating file sink based on time.
|
||||||
|
* If truncate != false , the created file will be truncated.
|
||||||
|
* If max_files > 0, retain only the last max_files and delete previous.
|
||||||
|
*/
|
||||||
|
template<typename Mutex, typename FileNameCalc = hourly_filename_calculator>
|
||||||
|
class hourly_file_sink final : public base_sink<Mutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// create hourly file sink which rotates on given time
|
||||||
|
hourly_file_sink(filename_t base_filename, bool truncate = false, uint16_t max_files = 0)
|
||||||
|
: base_filename_(std::move(base_filename))
|
||||||
|
, truncate_(truncate)
|
||||||
|
, max_files_(max_files)
|
||||||
|
, filenames_q_()
|
||||||
|
{
|
||||||
|
auto now = log_clock::now();
|
||||||
|
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
|
||||||
|
file_helper_.open(filename, truncate_);
|
||||||
|
rotation_tp_ = next_rotation_tp_();
|
||||||
|
|
||||||
|
if (max_files_ > 0)
|
||||||
|
{
|
||||||
|
init_filenames_q_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filename_t filename()
|
||||||
|
{
|
||||||
|
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||||
|
return file_helper_.filename();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void sink_it_(const details::log_msg &msg) override
|
||||||
|
{
|
||||||
|
auto time = msg.time;
|
||||||
|
bool should_rotate = time >= rotation_tp_;
|
||||||
|
if (should_rotate)
|
||||||
|
{
|
||||||
|
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(time));
|
||||||
|
file_helper_.open(filename, truncate_);
|
||||||
|
rotation_tp_ = next_rotation_tp_();
|
||||||
|
}
|
||||||
|
memory_buf_t formatted;
|
||||||
|
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||||
|
file_helper_.write(formatted);
|
||||||
|
|
||||||
|
// Do the cleaning only at the end because it might throw on failure.
|
||||||
|
if (should_rotate && max_files_ > 0)
|
||||||
|
{
|
||||||
|
delete_old_();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush_() override
|
||||||
|
{
|
||||||
|
file_helper_.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void init_filenames_q_()
|
||||||
|
{
|
||||||
|
using details::os::path_exists;
|
||||||
|
|
||||||
|
filenames_q_ = details::circular_q<filename_t>(static_cast<size_t>(max_files_));
|
||||||
|
std::vector<filename_t> filenames;
|
||||||
|
auto now = log_clock::now();
|
||||||
|
while (filenames.size() < max_files_)
|
||||||
|
{
|
||||||
|
auto filename = FileNameCalc::calc_filename(base_filename_, now_tm(now));
|
||||||
|
if (!path_exists(filename))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
filenames.emplace_back(filename);
|
||||||
|
now -= std::chrono::hours(1);
|
||||||
|
}
|
||||||
|
for (auto iter = filenames.rbegin(); iter != filenames.rend(); ++iter)
|
||||||
|
{
|
||||||
|
filenames_q_.push_back(std::move(*iter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tm now_tm(log_clock::time_point tp)
|
||||||
|
{
|
||||||
|
time_t tnow = log_clock::to_time_t(tp);
|
||||||
|
return spdlog::details::os::localtime(tnow);
|
||||||
|
}
|
||||||
|
|
||||||
|
log_clock::time_point next_rotation_tp_()
|
||||||
|
{
|
||||||
|
auto now = log_clock::now();
|
||||||
|
tm date = now_tm(now);
|
||||||
|
date.tm_min = 0;
|
||||||
|
date.tm_sec = 0;
|
||||||
|
auto rotation_time = log_clock::from_time_t(std::mktime(&date));
|
||||||
|
if (rotation_time > now)
|
||||||
|
{
|
||||||
|
return rotation_time;
|
||||||
|
}
|
||||||
|
return {rotation_time + std::chrono::hours(1)};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the file N rotations ago.
|
||||||
|
// Throw spdlog_ex on failure to delete the old file.
|
||||||
|
void delete_old_()
|
||||||
|
{
|
||||||
|
using details::os::filename_to_str;
|
||||||
|
using details::os::remove_if_exists;
|
||||||
|
|
||||||
|
filename_t current_file = file_helper_.filename();
|
||||||
|
if (filenames_q_.full())
|
||||||
|
{
|
||||||
|
auto old_filename = std::move(filenames_q_.front());
|
||||||
|
filenames_q_.pop_front();
|
||||||
|
bool ok = remove_if_exists(old_filename) == 0;
|
||||||
|
if (!ok)
|
||||||
|
{
|
||||||
|
filenames_q_.push_back(std::move(current_file));
|
||||||
|
SPDLOG_THROW(spdlog_ex("Failed removing hourly file " + filename_to_str(old_filename), errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
filenames_q_.push_back(std::move(current_file));
|
||||||
|
}
|
||||||
|
|
||||||
|
filename_t base_filename_;
|
||||||
|
log_clock::time_point rotation_tp_;
|
||||||
|
details::file_helper file_helper_;
|
||||||
|
bool truncate_;
|
||||||
|
uint16_t max_files_;
|
||||||
|
details::circular_q<filename_t> filenames_q_;
|
||||||
|
};
|
||||||
|
|
||||||
|
using hourly_file_sink_mt = hourly_file_sink<std::mutex>;
|
||||||
|
using hourly_file_sink_st = hourly_file_sink<details::null_mutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
|
||||||
|
//
|
||||||
|
// factory functions
|
||||||
|
//
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
inline std::shared_ptr<logger> hourly_logger_mt(
|
||||||
|
const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::hourly_file_sink_mt>(logger_name, filename, truncate, max_files);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Factory = spdlog::synchronous_factory>
|
||||||
|
inline std::shared_ptr<logger> hourly_logger_st(
|
||||||
|
const std::string &logger_name, const filename_t &filename, bool truncate = false, uint16_t max_files = 0)
|
||||||
|
{
|
||||||
|
return Factory::template create<sinks::hourly_file_sink_st>(logger_name, filename, truncate, max_files);
|
||||||
|
}
|
||||||
|
} // namespace spdlog
|
@@ -32,8 +32,13 @@ SPDLOG_INLINE stdout_sink_base<ConsoleMutex>::stdout_sink_base(FILE *file)
|
|||||||
{
|
{
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
// get windows handle from the FILE* object
|
// get windows handle from the FILE* object
|
||||||
|
|
||||||
handle_ = (HANDLE)::_get_osfhandle(::_fileno(file_));
|
handle_ = (HANDLE)::_get_osfhandle(::_fileno(file_));
|
||||||
if (handle_ == INVALID_HANDLE_VALUE)
|
|
||||||
|
// don't throw to support cases where no console is attached,
|
||||||
|
// and let the log method to do nothing if (handle_ == INVALID_HANDLE_VALUE).
|
||||||
|
// throw only if non stdout/stderr target is requested (probably regular file and not console).
|
||||||
|
if (handle_ == INVALID_HANDLE_VALUE && file != stdout && file != stderr)
|
||||||
{
|
{
|
||||||
throw_spdlog_ex("spdlog::stdout_sink_base: _get_osfhandle() failed", errno);
|
throw_spdlog_ex("spdlog::stdout_sink_base: _get_osfhandle() failed", errno);
|
||||||
}
|
}
|
||||||
@@ -43,10 +48,14 @@ SPDLOG_INLINE stdout_sink_base<ConsoleMutex>::stdout_sink_base(FILE *file)
|
|||||||
template<typename ConsoleMutex>
|
template<typename ConsoleMutex>
|
||||||
SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &msg)
|
SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &msg)
|
||||||
{
|
{
|
||||||
|
#ifdef _WIN32
|
||||||
|
if (handle_ == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
memory_buf_t formatted;
|
memory_buf_t formatted;
|
||||||
formatter_->format(msg, formatted);
|
formatter_->format(msg, formatted);
|
||||||
#ifdef _WIN32
|
|
||||||
::fflush(file_); // flush in case there is somthing in this file_ already
|
::fflush(file_); // flush in case there is somthing in this file_ already
|
||||||
auto size = static_cast<DWORD>(formatted.size());
|
auto size = static_cast<DWORD>(formatted.size());
|
||||||
DWORD bytes_written = 0;
|
DWORD bytes_written = 0;
|
||||||
@@ -56,6 +65,9 @@ SPDLOG_INLINE void stdout_sink_base<ConsoleMutex>::log(const details::log_msg &m
|
|||||||
throw_spdlog_ex("stdout_sink_base: WriteFile() failed. GetLastError(): " + std::to_string(::GetLastError()));
|
throw_spdlog_ex("stdout_sink_base: WriteFile() failed. GetLastError(): " + std::to_string(::GetLastError()));
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
memory_buf_t formatted;
|
||||||
|
formatter_->format(msg, formatted);
|
||||||
::fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
|
::fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
|
||||||
::fflush(file_); // flush every line to terminal
|
::fflush(file_); // flush every line to terminal
|
||||||
#endif // WIN32
|
#endif // WIN32
|
||||||
|
@@ -57,7 +57,7 @@ struct win32_error : public spdlog_ex
|
|||||||
|
|
||||||
LPSTR format_message_result{};
|
LPSTR format_message_result{};
|
||||||
auto format_message_succeeded =
|
auto format_message_succeeded =
|
||||||
::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
|
::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
|
||||||
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&format_message_result, 0, nullptr);
|
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&format_message_result, 0, nullptr);
|
||||||
|
|
||||||
if (format_message_succeeded && format_message_result)
|
if (format_message_succeeded && format_message_result)
|
||||||
@@ -203,7 +203,7 @@ private:
|
|||||||
{
|
{
|
||||||
if (!hEventLog_)
|
if (!hEventLog_)
|
||||||
{
|
{
|
||||||
hEventLog_ = ::RegisterEventSource(nullptr, source_.c_str());
|
hEventLog_ = ::RegisterEventSourceA(nullptr, source_.c_str());
|
||||||
if (!hEventLog_ || hEventLog_ == (HANDLE)ERROR_ACCESS_DENIED)
|
if (!hEventLog_ || hEventLog_ == (HANDLE)ERROR_ACCESS_DENIED)
|
||||||
{
|
{
|
||||||
SPDLOG_THROW(internal::win32_error("RegisterEventSource"));
|
SPDLOG_THROW(internal::win32_error("RegisterEventSource"));
|
||||||
@@ -218,13 +218,23 @@ protected:
|
|||||||
{
|
{
|
||||||
using namespace internal;
|
using namespace internal;
|
||||||
|
|
||||||
|
bool succeeded;
|
||||||
memory_buf_t formatted;
|
memory_buf_t formatted;
|
||||||
base_sink<Mutex>::formatter_->format(msg, formatted);
|
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||||
formatted.push_back('\0');
|
formatted.push_back('\0');
|
||||||
LPCSTR lp_str = static_cast<LPCSTR>(formatted.data());
|
|
||||||
|
|
||||||
auto succeeded = ::ReportEvent(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_,
|
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
current_user_sid_.as_sid(), 1, 0, &lp_str, nullptr);
|
wmemory_buf_t buf;
|
||||||
|
details::os::utf8_to_wstrbuf(string_view_t(formatted.data(), formatted.size()), buf);
|
||||||
|
|
||||||
|
LPCWSTR lp_wstr = buf.data();
|
||||||
|
succeeded = ::ReportEventW(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_,
|
||||||
|
current_user_sid_.as_sid(), 1, 0, &lp_wstr, nullptr);
|
||||||
|
#else
|
||||||
|
LPCSTR lp_str = formatted.data();
|
||||||
|
succeeded = ::ReportEventA(event_log_handle(), eventlog::get_event_type(msg), eventlog::get_event_category(msg), event_id_,
|
||||||
|
current_user_sid_.as_sid(), 1, 0, &lp_str, nullptr);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!succeeded)
|
if (!succeeded)
|
||||||
{
|
{
|
||||||
|
@@ -7,30 +7,30 @@
|
|||||||
#include <spdlog/sinks/wincolor_sink.h>
|
#include <spdlog/sinks/wincolor_sink.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/details/windows_include.h>
|
||||||
|
#include <wincon.h>
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
#include <spdlog/common.h>
|
||||||
#include <spdlog/pattern_formatter.h>
|
#include <spdlog/pattern_formatter.h>
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace sinks {
|
namespace sinks {
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
template<typename ConsoleMutex>
|
||||||
SPDLOG_INLINE wincolor_sink<ConsoleMutex>::wincolor_sink(HANDLE out_handle, color_mode mode)
|
SPDLOG_INLINE wincolor_sink<ConsoleMutex>::wincolor_sink(void *out_handle, color_mode mode)
|
||||||
: out_handle_(out_handle)
|
: out_handle_(out_handle)
|
||||||
, mutex_(ConsoleMutex::mutex())
|
, mutex_(ConsoleMutex::mutex())
|
||||||
, formatter_(details::make_unique<spdlog::pattern_formatter>())
|
, formatter_(details::make_unique<spdlog::pattern_formatter>())
|
||||||
{
|
{
|
||||||
// check if out_handle is points to the actual console.
|
|
||||||
// ::GetConsoleMode() should return 0 if it is redirected or not valid console handle.
|
|
||||||
DWORD console_mode;
|
|
||||||
in_console_ = ::GetConsoleMode(out_handle, &console_mode) != 0;
|
|
||||||
|
|
||||||
set_color_mode(mode);
|
set_color_mode_impl(mode);
|
||||||
colors_[level::trace] = WHITE;
|
// set level colors
|
||||||
colors_[level::debug] = CYAN;
|
colors_[level::trace] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; // white
|
||||||
colors_[level::info] = GREEN;
|
colors_[level::debug] = FOREGROUND_GREEN | FOREGROUND_BLUE; // cyan
|
||||||
colors_[level::warn] = YELLOW | BOLD;
|
colors_[level::info] = FOREGROUND_GREEN; // green
|
||||||
colors_[level::err] = RED | BOLD; // red bold
|
colors_[level::warn] = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY; // intense yellow
|
||||||
colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background
|
colors_[level::err] = FOREGROUND_RED | FOREGROUND_INTENSITY; // intense red
|
||||||
|
colors_[level::critical] =
|
||||||
|
BACKGROUND_RED | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; // intense white on red background
|
||||||
colors_[level::off] = 0;
|
colors_[level::off] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,7 +42,7 @@ SPDLOG_INLINE wincolor_sink<ConsoleMutex>::~wincolor_sink()
|
|||||||
|
|
||||||
// change the color for the given level
|
// change the color for the given level
|
||||||
template<typename ConsoleMutex>
|
template<typename ConsoleMutex>
|
||||||
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color(level::level_enum level, WORD color)
|
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color(level::level_enum level, std::uint16_t color)
|
||||||
{
|
{
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
colors_[level] = color;
|
colors_[level] = color;
|
||||||
@@ -51,31 +51,30 @@ void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color(level::level_enum leve
|
|||||||
template<typename ConsoleMutex>
|
template<typename ConsoleMutex>
|
||||||
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
|
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::log(const details::log_msg &msg)
|
||||||
{
|
{
|
||||||
|
if (out_handle_ == nullptr || out_handle_ == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::lock_guard<mutex_t> lock(mutex_);
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
msg.color_range_start = 0;
|
msg.color_range_start = 0;
|
||||||
msg.color_range_end = 0;
|
msg.color_range_end = 0;
|
||||||
memory_buf_t formatted;
|
memory_buf_t formatted;
|
||||||
formatter_->format(msg, formatted);
|
formatter_->format(msg, formatted);
|
||||||
if (!in_console_)
|
|
||||||
{
|
|
||||||
write_to_file_(formatted);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
|
if (should_do_colors_ && msg.color_range_end > msg.color_range_start)
|
||||||
{
|
{
|
||||||
// before color range
|
// before color range
|
||||||
print_range_(formatted, 0, msg.color_range_start);
|
print_range_(formatted, 0, msg.color_range_start);
|
||||||
|
|
||||||
// in color range
|
// in color range
|
||||||
auto orig_attribs = set_foreground_color_(colors_[msg.level]);
|
auto orig_attribs = static_cast<WORD>(set_foreground_color_(colors_[msg.level]));
|
||||||
print_range_(formatted, msg.color_range_start, msg.color_range_end);
|
print_range_(formatted, msg.color_range_start, msg.color_range_end);
|
||||||
// reset to orig colors
|
// reset to orig colors
|
||||||
::SetConsoleTextAttribute(out_handle_, orig_attribs);
|
::SetConsoleTextAttribute(static_cast<HANDLE>(out_handle_), orig_attribs);
|
||||||
print_range_(formatted, msg.color_range_end, formatted.size());
|
print_range_(formatted, msg.color_range_end, formatted.size());
|
||||||
}
|
}
|
||||||
else // print without colors if color range is invalid (or color is disabled)
|
else // print without colors if color range is invalid (or color is disabled)
|
||||||
{
|
{
|
||||||
print_range_(formatted, 0, formatted.size());
|
write_to_file_(formatted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,56 +101,63 @@ void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_formatter(std::unique_ptr<sp
|
|||||||
template<typename ConsoleMutex>
|
template<typename ConsoleMutex>
|
||||||
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
|
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color_mode(color_mode mode)
|
||||||
{
|
{
|
||||||
switch (mode)
|
std::lock_guard<mutex_t> lock(mutex_);
|
||||||
|
set_color_mode_impl(mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename ConsoleMutex>
|
||||||
|
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_color_mode_impl(color_mode mode)
|
||||||
|
{
|
||||||
|
if (mode == color_mode::automatic)
|
||||||
{
|
{
|
||||||
case color_mode::always:
|
// should do colors only if out_handle_ points to actual console.
|
||||||
case color_mode::automatic:
|
DWORD console_mode;
|
||||||
should_do_colors_ = true;
|
bool in_console = ::GetConsoleMode(static_cast<HANDLE>(out_handle_), &console_mode) != 0;
|
||||||
break;
|
should_do_colors_ = in_console;
|
||||||
case color_mode::never:
|
}
|
||||||
should_do_colors_ = false;
|
else
|
||||||
break;
|
{
|
||||||
default:
|
should_do_colors_ = mode == color_mode::always ? true : false;
|
||||||
should_do_colors_ = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set foreground color and return the orig console attributes (for resetting later)
|
// set foreground color and return the orig console attributes (for resetting later)
|
||||||
template<typename ConsoleMutex>
|
template<typename ConsoleMutex>
|
||||||
WORD SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_foreground_color_(WORD attribs)
|
std::uint16_t SPDLOG_INLINE wincolor_sink<ConsoleMutex>::set_foreground_color_(std::uint16_t attribs)
|
||||||
{
|
{
|
||||||
CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
|
CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
|
||||||
::GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info);
|
if (!::GetConsoleScreenBufferInfo(static_cast<HANDLE>(out_handle_), &orig_buffer_info))
|
||||||
WORD back_color = orig_buffer_info.wAttributes;
|
{
|
||||||
// retrieve the current background color
|
// just return white if failed getting console info
|
||||||
back_color &= static_cast<WORD>(~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY));
|
return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||||
// keep the background color unchanged
|
}
|
||||||
::SetConsoleTextAttribute(out_handle_, attribs | back_color);
|
|
||||||
return orig_buffer_info.wAttributes; // return orig attribs
|
// change only the foreground bits (lowest 4 bits)
|
||||||
|
auto new_attribs = static_cast<WORD>(attribs) | (orig_buffer_info.wAttributes & 0xfff0);
|
||||||
|
auto ignored = ::SetConsoleTextAttribute(static_cast<HANDLE>(out_handle_), static_cast<WORD>(new_attribs));
|
||||||
|
(void)(ignored);
|
||||||
|
return static_cast<std::uint16_t>(orig_buffer_info.wAttributes); // return orig attribs
|
||||||
}
|
}
|
||||||
|
|
||||||
// print a range of formatted message to console
|
// print a range of formatted message to console
|
||||||
template<typename ConsoleMutex>
|
template<typename ConsoleMutex>
|
||||||
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::print_range_(const memory_buf_t &formatted, size_t start, size_t end)
|
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::print_range_(const memory_buf_t &formatted, size_t start, size_t end)
|
||||||
{
|
{
|
||||||
auto size = static_cast<DWORD>(end - start);
|
if (end > start)
|
||||||
::WriteConsoleA(out_handle_, formatted.data() + start, size, nullptr, nullptr);
|
{
|
||||||
|
auto size = static_cast<DWORD>(end - start);
|
||||||
|
auto ignored = ::WriteConsoleA(static_cast<HANDLE>(out_handle_), formatted.data() + start, size, nullptr, nullptr);
|
||||||
|
(void)(ignored);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
template<typename ConsoleMutex>
|
||||||
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::write_to_file_(const memory_buf_t &formatted)
|
void SPDLOG_INLINE wincolor_sink<ConsoleMutex>::write_to_file_(const memory_buf_t &formatted)
|
||||||
{
|
{
|
||||||
if (out_handle_ == nullptr) // no console and no file redirect
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
auto size = static_cast<DWORD>(formatted.size());
|
auto size = static_cast<DWORD>(formatted.size());
|
||||||
DWORD bytes_written = 0;
|
DWORD bytes_written = 0;
|
||||||
bool ok = ::WriteFile(out_handle_, formatted.data(), size, &bytes_written, nullptr) != 0;
|
auto ignored = ::WriteFile(static_cast<HANDLE>(out_handle_), formatted.data(), size, &bytes_written, nullptr);
|
||||||
if (!ok)
|
(void)(ignored);
|
||||||
{
|
|
||||||
throw_spdlog_ex("wincolor_sink: ::WriteFile() failed. GetLastError(): " + std::to_string(::GetLastError()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// wincolor_stdout_sink
|
// wincolor_stdout_sink
|
||||||
@@ -165,6 +171,5 @@ template<typename ConsoleMutex>
|
|||||||
SPDLOG_INLINE wincolor_stderr_sink<ConsoleMutex>::wincolor_stderr_sink(color_mode mode)
|
SPDLOG_INLINE wincolor_stderr_sink<ConsoleMutex>::wincolor_stderr_sink(color_mode mode)
|
||||||
: wincolor_sink<ConsoleMutex>(::GetStdHandle(STD_ERROR_HANDLE), mode)
|
: wincolor_sink<ConsoleMutex>(::GetStdHandle(STD_ERROR_HANDLE), mode)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
} // namespace sinks
|
} // namespace sinks
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
@@ -12,9 +12,7 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
#include <spdlog/details/windows_include.h>
|
|
||||||
#include <wincon.h>
|
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace sinks {
|
namespace sinks {
|
||||||
@@ -26,21 +24,14 @@ template<typename ConsoleMutex>
|
|||||||
class wincolor_sink : public sink
|
class wincolor_sink : public sink
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
const WORD BOLD = FOREGROUND_INTENSITY;
|
wincolor_sink(void *out_handle, color_mode mode);
|
||||||
const WORD RED = FOREGROUND_RED;
|
|
||||||
const WORD GREEN = FOREGROUND_GREEN;
|
|
||||||
const WORD CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE;
|
|
||||||
const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
|
||||||
const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN;
|
|
||||||
|
|
||||||
wincolor_sink(HANDLE out_handle, color_mode mode);
|
|
||||||
~wincolor_sink() override;
|
~wincolor_sink() override;
|
||||||
|
|
||||||
wincolor_sink(const wincolor_sink &other) = delete;
|
wincolor_sink(const wincolor_sink &other) = delete;
|
||||||
wincolor_sink &operator=(const wincolor_sink &other) = delete;
|
wincolor_sink &operator=(const wincolor_sink &other) = delete;
|
||||||
|
|
||||||
// change the color for the given level
|
// change the color for the given level
|
||||||
void set_color(level::level_enum level, WORD color);
|
void set_color(level::level_enum level, std::uint16_t color);
|
||||||
void log(const details::log_msg &msg) final override;
|
void log(const details::log_msg &msg) final override;
|
||||||
void flush() final override;
|
void flush() final override;
|
||||||
void set_pattern(const std::string &pattern) override final;
|
void set_pattern(const std::string &pattern) override final;
|
||||||
@@ -49,21 +40,22 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
using mutex_t = typename ConsoleMutex::mutex_t;
|
using mutex_t = typename ConsoleMutex::mutex_t;
|
||||||
HANDLE out_handle_;
|
void *out_handle_;
|
||||||
mutex_t &mutex_;
|
mutex_t &mutex_;
|
||||||
bool in_console_;
|
|
||||||
bool should_do_colors_;
|
bool should_do_colors_;
|
||||||
std::unique_ptr<spdlog::formatter> formatter_;
|
std::unique_ptr<spdlog::formatter> formatter_;
|
||||||
std::array<WORD, level::n_levels> colors_;
|
std::array<std::uint16_t, level::n_levels> colors_;
|
||||||
|
|
||||||
// set foreground color and return the orig console attributes (for resetting later)
|
// set foreground color and return the orig console attributes (for resetting later)
|
||||||
WORD set_foreground_color_(WORD attribs);
|
std::uint16_t set_foreground_color_(std::uint16_t attribs);
|
||||||
|
|
||||||
// print a range of formatted message to console
|
// print a range of formatted message to console
|
||||||
void print_range_(const memory_buf_t &formatted, size_t start, size_t end);
|
void print_range_(const memory_buf_t &formatted, size_t start, size_t end);
|
||||||
|
|
||||||
// in case we are redirected to file (not in console mode)
|
// in case we are redirected to file (not in console mode)
|
||||||
void write_to_file_(const memory_buf_t &formatted);
|
void write_to_file_(const memory_buf_t &formatted);
|
||||||
|
|
||||||
|
void set_color_mode_impl(color_mode mode);
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename ConsoleMutex>
|
template<typename ConsoleMutex>
|
||||||
@@ -85,7 +77,6 @@ using wincolor_stdout_sink_st = wincolor_stdout_sink<details::console_nullmutex>
|
|||||||
|
|
||||||
using wincolor_stderr_sink_mt = wincolor_stderr_sink<details::console_mutex>;
|
using wincolor_stderr_sink_mt = wincolor_stderr_sink<details::console_mutex>;
|
||||||
using wincolor_stderr_sink_st = wincolor_stderr_sink<details::console_nullmutex>;
|
using wincolor_stderr_sink_st = wincolor_stderr_sink<details::console_nullmutex>;
|
||||||
|
|
||||||
} // namespace sinks
|
} // namespace sinks
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
|
@@ -111,7 +111,7 @@ SPDLOG_API void set_automatic_registration(bool automatic_registration);
|
|||||||
//
|
//
|
||||||
// The default logger object can be accessed using the spdlog::default_logger():
|
// The default logger object can be accessed using the spdlog::default_logger():
|
||||||
// For example, to add another sink to it:
|
// For example, to add another sink to it:
|
||||||
// spdlog::default_logger()->sinks()->push_back(some_sink);
|
// spdlog::default_logger()->sinks().push_back(some_sink);
|
||||||
//
|
//
|
||||||
// The default logger can replaced using spdlog::set_default_logger(new_logger).
|
// The default logger can replaced using spdlog::set_default_logger(new_logger).
|
||||||
// For example, to replace it with a file logger.
|
// For example, to replace it with a file logger.
|
||||||
@@ -128,51 +128,51 @@ SPDLOG_API spdlog::logger *default_logger_raw();
|
|||||||
SPDLOG_API void set_default_logger(std::shared_ptr<spdlog::logger> default_logger);
|
SPDLOG_API void set_default_logger(std::shared_ptr<spdlog::logger> default_logger);
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
inline void log(source_loc source, level::level_enum lvl, const FormatString &fmt, const Args &...args)
|
inline void log(source_loc source, level::level_enum lvl, const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
default_logger_raw()->log(source, lvl, fmt, args...);
|
default_logger_raw()->log(source, lvl, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
inline void log(level::level_enum lvl, const FormatString &fmt, const Args &...args)
|
inline void log(level::level_enum lvl, const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
default_logger_raw()->log(source_loc{}, lvl, fmt, args...);
|
default_logger_raw()->log(source_loc{}, lvl, fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
inline void trace(const FormatString &fmt, const Args &...args)
|
inline void trace(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
default_logger_raw()->trace(fmt, args...);
|
default_logger_raw()->trace(fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
inline void debug(const FormatString &fmt, const Args &...args)
|
inline void debug(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
default_logger_raw()->debug(fmt, args...);
|
default_logger_raw()->debug(fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
inline void info(const FormatString &fmt, const Args &...args)
|
inline void info(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
default_logger_raw()->info(fmt, args...);
|
default_logger_raw()->info(fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
inline void warn(const FormatString &fmt, const Args &...args)
|
inline void warn(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
default_logger_raw()->warn(fmt, args...);
|
default_logger_raw()->warn(fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
inline void error(const FormatString &fmt, const Args &...args)
|
inline void error(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
default_logger_raw()->error(fmt, args...);
|
default_logger_raw()->error(fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename FormatString, typename... Args>
|
template<typename FormatString, typename... Args>
|
||||||
inline void critical(const FormatString &fmt, const Args &...args)
|
inline void critical(const FormatString &fmt, Args&&...args)
|
||||||
{
|
{
|
||||||
default_logger_raw()->critical(fmt, args...);
|
default_logger_raw()->critical(fmt, std::forward<Args>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
|
@@ -58,6 +58,14 @@
|
|||||||
// #define SPDLOG_EOL ";-)\n"
|
// #define SPDLOG_EOL ";-)\n"
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Uncomment to override default folder separators ("/" or "\\/" under
|
||||||
|
// Linux/Windows). Each character in the string is treated as a different
|
||||||
|
// separator.
|
||||||
|
//
|
||||||
|
// #define SPDLOG_FOLDER_SEPS "\\"
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Uncomment to use your own copy of the fmt library instead of spdlog's copy.
|
// Uncomment to use your own copy of the fmt library instead of spdlog's copy.
|
||||||
// In this case spdlog will try to include <fmt/format.h> so set your -I flag
|
// In this case spdlog will try to include <fmt/format.h> so set your -I flag
|
||||||
|
@@ -5,6 +5,6 @@
|
|||||||
|
|
||||||
#define SPDLOG_VER_MAJOR 1
|
#define SPDLOG_VER_MAJOR 1
|
||||||
#define SPDLOG_VER_MINOR 8
|
#define SPDLOG_VER_MINOR 8
|
||||||
#define SPDLOG_VER_PATCH 1
|
#define SPDLOG_VER_PATCH 4
|
||||||
|
|
||||||
#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)
|
||||||
|
80
src/fmt.cpp
80
src/fmt.cpp
@@ -9,29 +9,63 @@
|
|||||||
#if !defined(SPDLOG_FMT_EXTERNAL)
|
#if !defined(SPDLOG_FMT_EXTERNAL)
|
||||||
#include <spdlog/fmt/bundled/format-inl.h>
|
#include <spdlog/fmt/bundled/format-inl.h>
|
||||||
|
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
int format_float(char *buf, std::size_t size, const char *format, int precision, T value)
|
int format_float(char* buf, std::size_t size, const char* format, int precision,
|
||||||
{
|
T value) {
|
||||||
#ifdef FMT_FUZZ
|
#ifdef FMT_FUZZ
|
||||||
if (precision > 100000)
|
if (precision > 100000)
|
||||||
throw std::runtime_error("fuzz mode - avoid large allocation inside snprintf");
|
throw std::runtime_error(
|
||||||
|
"fuzz mode - avoid large allocation inside snprintf");
|
||||||
#endif
|
#endif
|
||||||
// Suppress the warning about nonliteral format string.
|
// Suppress the warning about nonliteral format string.
|
||||||
int (*snprintf_ptr)(char *, size_t, const char *, ...) = FMT_SNPRINTF;
|
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
|
||||||
return precision < 0 ? snprintf_ptr(buf, size, format, value) : snprintf_ptr(buf, size, format, precision, value);
|
return precision < 0 ? snprintf_ptr(buf, size, format, value)
|
||||||
|
: snprintf_ptr(buf, size, format, precision, value);
|
||||||
}
|
}
|
||||||
} // namespace detail
|
|
||||||
|
template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x)
|
||||||
|
FMT_NOEXCEPT;
|
||||||
|
|
||||||
|
// DEPRECATED! This function exists for ABI compatibility.
|
||||||
|
template <typename Char>
|
||||||
|
typename basic_format_context<std::back_insert_iterator<buffer<Char>>,
|
||||||
|
Char>::iterator
|
||||||
|
vformat_to(buffer<Char>& buf, basic_string_view<Char> format_str,
|
||||||
|
basic_format_args<basic_format_context<
|
||||||
|
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
|
||||||
|
type_identity_t<Char>>>
|
||||||
|
args) {
|
||||||
|
using iterator = std::back_insert_iterator<buffer<char>>;
|
||||||
|
using context = basic_format_context<
|
||||||
|
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
|
||||||
|
type_identity_t<Char>>;
|
||||||
|
auto out = iterator(buf);
|
||||||
|
format_handler<iterator, Char, context> h(out, format_str, args, {});
|
||||||
|
parse_format_string<false>(format_str, h);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
template basic_format_context<std::back_insert_iterator<buffer<char>>,
|
||||||
|
char>::iterator
|
||||||
|
vformat_to(buffer<char>&, string_view,
|
||||||
|
basic_format_args<basic_format_context<
|
||||||
|
std::back_insert_iterator<buffer<type_identity_t<char>>>,
|
||||||
|
type_identity_t<char>>>);
|
||||||
|
} // namespace detail
|
||||||
|
|
||||||
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
|
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
|
||||||
|
|
||||||
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
||||||
int (*instantiate_format_float)(double, int, detail::float_specs, detail::buffer<char> &) = detail::format_float;
|
int (*instantiate_format_float)(double, int, detail::float_specs,
|
||||||
|
detail::buffer<char>&) = detail::format_float;
|
||||||
|
|
||||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||||
template FMT_API detail::locale_ref::locale_ref(const std::locale &loc);
|
template FMT_API detail::locale_ref::locale_ref(const std::locale& loc);
|
||||||
template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
|
template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -41,15 +75,21 @@ template FMT_API std::string detail::grouping_impl<char>(locale_ref);
|
|||||||
template FMT_API char detail::thousands_sep_impl(locale_ref);
|
template FMT_API char detail::thousands_sep_impl(locale_ref);
|
||||||
template FMT_API char detail::decimal_point_impl(locale_ref);
|
template FMT_API char detail::decimal_point_impl(locale_ref);
|
||||||
|
|
||||||
template FMT_API void detail::buffer<char>::append(const char *, const char *);
|
template FMT_API void detail::buffer<char>::append(const char*, const char*);
|
||||||
|
|
||||||
template FMT_API FMT_BUFFER_CONTEXT(char)::iterator detail::vformat_to(
|
template FMT_API void detail::vformat_to(
|
||||||
detail::buffer<char> &, string_view, basic_format_args<FMT_BUFFER_CONTEXT(char)>);
|
detail::buffer<char>&, string_view,
|
||||||
|
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
|
||||||
|
|
||||||
template FMT_API int detail::snprintf_float(double, int, detail::float_specs, detail::buffer<char> &);
|
template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
|
||||||
template FMT_API int detail::snprintf_float(long double, int, detail::float_specs, detail::buffer<char> &);
|
detail::buffer<char>&);
|
||||||
template FMT_API int detail::format_float(double, int, detail::float_specs, detail::buffer<char> &);
|
template FMT_API int detail::snprintf_float(long double, int,
|
||||||
template FMT_API int detail::format_float(long double, int, detail::float_specs, detail::buffer<char> &);
|
detail::float_specs,
|
||||||
|
detail::buffer<char>&);
|
||||||
|
template FMT_API int detail::format_float(double, int, detail::float_specs,
|
||||||
|
detail::buffer<char>&);
|
||||||
|
template FMT_API int detail::format_float(long double, int, detail::float_specs,
|
||||||
|
detail::buffer<char>&);
|
||||||
|
|
||||||
// Explicit instantiations for wchar_t.
|
// Explicit instantiations for wchar_t.
|
||||||
|
|
||||||
@@ -57,7 +97,7 @@ template FMT_API std::string detail::grouping_impl<wchar_t>(locale_ref);
|
|||||||
template FMT_API wchar_t detail::thousands_sep_impl(locale_ref);
|
template FMT_API wchar_t detail::thousands_sep_impl(locale_ref);
|
||||||
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
|
template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
|
||||||
|
|
||||||
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t *, const wchar_t *);
|
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
|
||||||
|
const wchar_t*);
|
||||||
FMT_END_NAMESPACE
|
FMT_END_NAMESPACE
|
||||||
|
|
||||||
#endif // !SPDLOG_FMT_EXTERNAL
|
#endif // !SPDLOG_FMT_EXTERNAL
|
@@ -3,6 +3,8 @@
|
|||||||
#include "spdlog/sinks/basic_file_sink.h"
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
#include "test_sink.h"
|
#include "test_sink.h"
|
||||||
|
|
||||||
|
#define TEST_FILENAME "test_logs/async_test.log"
|
||||||
|
|
||||||
TEST_CASE("basic async test ", "[async]")
|
TEST_CASE("basic async test ", "[async]")
|
||||||
{
|
{
|
||||||
auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
|
auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
|
||||||
@@ -87,7 +89,7 @@ TEST_CASE("async periodic flush", "[async]")
|
|||||||
auto test_sink = std::static_pointer_cast<spdlog::sinks::test_sink_mt>(logger->sinks()[0]);
|
auto test_sink = std::static_pointer_cast<spdlog::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(1700));
|
||||||
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();
|
||||||
@@ -149,7 +151,7 @@ TEST_CASE("to_file", "[async]")
|
|||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
size_t messages = 1024;
|
size_t messages = 1024;
|
||||||
size_t tp_threads = 1;
|
size_t tp_threads = 1;
|
||||||
std::string filename = "test_logs/async_test.log";
|
spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME);
|
||||||
{
|
{
|
||||||
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
|
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
|
||||||
auto tp = std::make_shared<spdlog::details::thread_pool>(messages, tp_threads);
|
auto tp = std::make_shared<spdlog::details::thread_pool>(messages, tp_threads);
|
||||||
@@ -161,8 +163,8 @@ TEST_CASE("to_file", "[async]")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
require_message_count(filename, messages);
|
require_message_count(TEST_FILENAME, messages);
|
||||||
auto contents = file_contents(filename);
|
auto contents = file_contents(TEST_FILENAME);
|
||||||
using spdlog::details::os::default_eol;
|
using spdlog::details::os::default_eol;
|
||||||
REQUIRE(ends_with(contents, fmt::format("Hello message #1023{}", default_eol)));
|
REQUIRE(ends_with(contents, fmt::format("Hello message #1023{}", default_eol)));
|
||||||
}
|
}
|
||||||
@@ -172,7 +174,7 @@ TEST_CASE("to_file multi-workers", "[async]")
|
|||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
size_t messages = 1024 * 10;
|
size_t messages = 1024 * 10;
|
||||||
size_t tp_threads = 10;
|
size_t tp_threads = 10;
|
||||||
std::string filename = "test_logs/async_test.log";
|
spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME);
|
||||||
{
|
{
|
||||||
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
|
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
|
||||||
auto tp = std::make_shared<spdlog::details::thread_pool>(messages, tp_threads);
|
auto tp = std::make_shared<spdlog::details::thread_pool>(messages, tp_threads);
|
||||||
@@ -184,5 +186,5 @@ TEST_CASE("to_file multi-workers", "[async]")
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
require_message_count(filename, messages);
|
require_message_count(TEST_FILENAME, messages);
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,7 @@
|
|||||||
using spdlog::details::os::create_dir;
|
using spdlog::details::os::create_dir;
|
||||||
using spdlog::details::os::path_exists;
|
using spdlog::details::os::path_exists;
|
||||||
|
|
||||||
bool try_create_dir(const char *path, const char *normalized_path)
|
bool try_create_dir(const spdlog::filename_t &path, const spdlog::filename_t &normalized_path)
|
||||||
{
|
{
|
||||||
auto rv = create_dir(path);
|
auto rv = create_dir(path);
|
||||||
REQUIRE(rv == true);
|
REQUIRE(rv == true);
|
||||||
@@ -17,24 +17,24 @@ TEST_CASE("create_dir", "[create_dir]")
|
|||||||
{
|
{
|
||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
|
|
||||||
REQUIRE(try_create_dir("test_logs/dir1/dir1", "test_logs/dir1/dir1"));
|
REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs/dir1/dir1"), SPDLOG_FILENAME_T("test_logs/dir1/dir1")));
|
||||||
REQUIRE(try_create_dir("test_logs/dir1/dir1", "test_logs/dir1/dir1")); // test existing
|
REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs/dir1/dir1"), SPDLOG_FILENAME_T("test_logs/dir1/dir1"))); // test existing
|
||||||
REQUIRE(try_create_dir("test_logs/dir1///dir2//", "test_logs/dir1/dir2"));
|
REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs/dir1///dir2//"), SPDLOG_FILENAME_T("test_logs/dir1/dir2")));
|
||||||
REQUIRE(try_create_dir("./test_logs/dir1/dir3", "test_logs/dir1/dir3"));
|
REQUIRE(try_create_dir(SPDLOG_FILENAME_T("./test_logs/dir1/dir3"), SPDLOG_FILENAME_T("test_logs/dir1/dir3")));
|
||||||
REQUIRE(try_create_dir("test_logs/../test_logs/dir1/dir4", "test_logs/dir1/dir4"));
|
REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs/../test_logs/dir1/dir4"), SPDLOG_FILENAME_T("test_logs/dir1/dir4")));
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
// test backslash folder separator
|
// test backslash folder separator
|
||||||
REQUIRE(try_create_dir("test_logs\\dir1\\dir222", "test_logs\\dir1\\dir222"));
|
REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs\\dir1\\dir222"), SPDLOG_FILENAME_T("test_logs\\dir1\\dir222")));
|
||||||
REQUIRE(try_create_dir("test_logs\\dir1\\dir223\\", "test_logs\\dir1\\dir223\\"));
|
REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs\\dir1\\dir223\\"), SPDLOG_FILENAME_T("test_logs\\dir1\\dir223\\")));
|
||||||
REQUIRE(try_create_dir(".\\test_logs\\dir1\\dir2\\dir99\\..\\dir23", "test_logs\\dir1\\dir2\\dir23"));
|
REQUIRE(try_create_dir(SPDLOG_FILENAME_T(".\\test_logs\\dir1\\dir2\\dir99\\..\\dir23"), SPDLOG_FILENAME_T("test_logs\\dir1\\dir2\\dir23")));
|
||||||
REQUIRE(try_create_dir("test_logs\\..\\test_logs\\dir1\\dir5", "test_logs\\dir1\\dir5"));
|
REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs\\..\\test_logs\\dir1\\dir5"), SPDLOG_FILENAME_T("test_logs\\dir1\\dir5")));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("create_invalid_dir", "[create_dir]")
|
TEST_CASE("create_invalid_dir", "[create_dir]")
|
||||||
{
|
{
|
||||||
REQUIRE(create_dir("") == false);
|
REQUIRE(create_dir(SPDLOG_FILENAME_T("")) == false);
|
||||||
REQUIRE(create_dir(spdlog::filename_t{}) == false);
|
REQUIRE(create_dir(spdlog::filename_t{}) == false);
|
||||||
#ifdef __linux__
|
#ifdef __linux__
|
||||||
REQUIRE(create_dir("/proc/spdlog-utest") == false);
|
REQUIRE(create_dir("/proc/spdlog-utest") == false);
|
||||||
@@ -44,37 +44,31 @@ TEST_CASE("create_invalid_dir", "[create_dir]")
|
|||||||
TEST_CASE("dir_name", "[create_dir]")
|
TEST_CASE("dir_name", "[create_dir]")
|
||||||
{
|
{
|
||||||
using spdlog::details::os::dir_name;
|
using spdlog::details::os::dir_name;
|
||||||
REQUIRE(dir_name("").empty());
|
REQUIRE(dir_name(SPDLOG_FILENAME_T("")).empty());
|
||||||
REQUIRE(dir_name("dir").empty());
|
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir")).empty());
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
REQUIRE(dir_name(R"(dir\)") == "dir");
|
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\)")) == SPDLOG_FILENAME_T("dir"));
|
||||||
REQUIRE(dir_name(R"(dir\\\)") == R"(dir\\)");
|
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\\\)")) == SPDLOG_FILENAME_T(R"(dir\\)"));
|
||||||
REQUIRE(dir_name(R"(dir\file)") == "dir");
|
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\file)")) == SPDLOG_FILENAME_T("dir"));
|
||||||
REQUIRE(dir_name(R"(dir/file)") == "dir");
|
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir/file)")) == SPDLOG_FILENAME_T("dir"));
|
||||||
REQUIRE(dir_name(R"(dir\file.txt)") == "dir");
|
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\file.txt)")) == SPDLOG_FILENAME_T("dir"));
|
||||||
REQUIRE(dir_name(R"(dir/file)") == "dir");
|
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir/file)")) == SPDLOG_FILENAME_T("dir"));
|
||||||
REQUIRE(dir_name(R"(dir\file.txt\)") == R"(dir\file.txt)");
|
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\file.txt\)")) == SPDLOG_FILENAME_T(R"(dir\file.txt)"));
|
||||||
REQUIRE(dir_name(R"(dir/file.txt/)") == R"(dir\file.txt)");
|
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(\dir\file.txt)")) == SPDLOG_FILENAME_T(R"(\dir)"));
|
||||||
REQUIRE(dir_name(R"(\dir\file.txt)") == R"(\dir)");
|
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(\\dir\file.txt)")) == SPDLOG_FILENAME_T(R"(\\dir)"));
|
||||||
REQUIRE(dir_name(R"(/dir/file.txt)") == R"(\dir)");
|
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(..\file.txt)")) == SPDLOG_FILENAME_T(".."));
|
||||||
REQUIRE(dir_name(R"(\\dir\file.txt)") == R"(\\dir)");
|
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(.\file.txt)")) == SPDLOG_FILENAME_T("."));
|
||||||
REQUIRE(dir_name(R"(//dir/file.txt)") == R"(\\dir)");
|
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(c:\\a\b\c\d\file.txt)")) == SPDLOG_FILENAME_T(R"(c:\\a\b\c\d)"));
|
||||||
REQUIRE(dir_name(R"(..\file.txt)") == "..");
|
REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(c://a/b/c/d/file.txt)")) == SPDLOG_FILENAME_T(R"(c://a/b/c/d)"));
|
||||||
REQUIRE(dir_name(R"(../file.txt)") == "..");
|
|
||||||
REQUIRE(dir_name(R"(.\file.txt)") == ".");
|
|
||||||
REQUIRE(dir_name(R"(./file.txt)") == ".");
|
|
||||||
REQUIRE(dir_name(R"(c:\\a\b\c\d\file.txt)") == R"(c:\\a\b\c\d)");
|
|
||||||
REQUIRE(dir_name(R"(c://a/b/c/d/file.txt)") == R"(c:\\a\b\c\d)");
|
|
||||||
#else
|
|
||||||
REQUIRE(dir_name("dir/") == "dir");
|
|
||||||
REQUIRE(dir_name("dir///") == "dir//");
|
|
||||||
REQUIRE(dir_name("dir/file") == "dir");
|
|
||||||
REQUIRE(dir_name("dir/file.txt") == "dir");
|
|
||||||
REQUIRE(dir_name("dir/file.txt/") == "dir/file.txt");
|
|
||||||
REQUIRE(dir_name("/dir/file.txt") == "/dir");
|
|
||||||
REQUIRE(dir_name("//dir/file.txt") == "//dir");
|
|
||||||
REQUIRE(dir_name("../file.txt") == "..");
|
|
||||||
REQUIRE(dir_name("./file.txt") == ".");
|
|
||||||
#endif
|
#endif
|
||||||
|
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/")) == SPDLOG_FILENAME_T("dir"));
|
||||||
|
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir///")) == SPDLOG_FILENAME_T("dir//"));
|
||||||
|
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/file")) == SPDLOG_FILENAME_T("dir"));
|
||||||
|
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/file.txt")) == SPDLOG_FILENAME_T("dir"));
|
||||||
|
REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/file.txt/")) == SPDLOG_FILENAME_T("dir/file.txt"));
|
||||||
|
REQUIRE(dir_name(SPDLOG_FILENAME_T("/dir/file.txt")) == SPDLOG_FILENAME_T("/dir"));
|
||||||
|
REQUIRE(dir_name(SPDLOG_FILENAME_T("//dir/file.txt")) == SPDLOG_FILENAME_T("//dir"));
|
||||||
|
REQUIRE(dir_name(SPDLOG_FILENAME_T("../file.txt")) == SPDLOG_FILENAME_T(".."));
|
||||||
|
REQUIRE(dir_name(SPDLOG_FILENAME_T("./file.txt")) == SPDLOG_FILENAME_T("."));
|
||||||
}
|
}
|
||||||
|
@@ -3,6 +3,8 @@
|
|||||||
*/
|
*/
|
||||||
#include "includes.h"
|
#include "includes.h"
|
||||||
|
|
||||||
|
using filename_memory_buf_t = fmt::basic_memory_buffer<spdlog::filename_t::value_type, 250>;
|
||||||
|
|
||||||
TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]")
|
TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]")
|
||||||
{
|
{
|
||||||
using sink_type = spdlog::sinks::daily_file_sink<std::mutex, spdlog::sinks::daily_filename_calculator>;
|
using sink_type = spdlog::sinks::daily_file_sink<std::mutex, spdlog::sinks::daily_filename_calculator>;
|
||||||
@@ -10,10 +12,10 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]")
|
|||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
|
|
||||||
// calculate filename (time based)
|
// calculate filename (time based)
|
||||||
std::string basename = "test_logs/daily_dateonly";
|
spdlog::filename_t basename = SPDLOG_FILENAME_T("test_logs/daily_dateonly");
|
||||||
std::tm tm = spdlog::details::os::localtime();
|
std::tm tm = spdlog::details::os::localtime();
|
||||||
spdlog::memory_buf_t w;
|
filename_memory_buf_t w;
|
||||||
fmt::format_to(w, "{}_{:04d}-{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
|
fmt::format_to(w, SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
|
||||||
|
|
||||||
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
|
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
|
||||||
for (int i = 0; i < 10; ++i)
|
for (int i = 0; i < 10; ++i)
|
||||||
@@ -23,7 +25,13 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]")
|
|||||||
}
|
}
|
||||||
logger->flush();
|
logger->flush();
|
||||||
|
|
||||||
|
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||||
|
spdlog::memory_buf_t buf;
|
||||||
|
spdlog::details::os::wstr_to_utf8buf(fmt::to_string(w), buf);
|
||||||
|
auto filename = fmt::to_string(buf);
|
||||||
|
#else
|
||||||
auto filename = fmt::to_string(w);
|
auto filename = fmt::to_string(w);
|
||||||
|
#endif
|
||||||
require_message_count(filename, 10);
|
require_message_count(filename, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,8 +39,8 @@ struct custom_daily_file_name_calculator
|
|||||||
{
|
{
|
||||||
static spdlog::filename_t calc_filename(const spdlog::filename_t &basename, const tm &now_tm)
|
static spdlog::filename_t calc_filename(const spdlog::filename_t &basename, const tm &now_tm)
|
||||||
{
|
{
|
||||||
spdlog::memory_buf_t w;
|
filename_memory_buf_t w;
|
||||||
fmt::format_to(w, "{}{:04d}{:02d}{:02d}", basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, now_tm.tm_mday);
|
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);
|
||||||
return fmt::to_string(w);
|
return fmt::to_string(w);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -44,10 +52,10 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger]")
|
|||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
|
|
||||||
// calculate filename (time based)
|
// calculate filename (time based)
|
||||||
std::string basename = "test_logs/daily_dateonly";
|
spdlog::filename_t basename = SPDLOG_FILENAME_T("test_logs/daily_dateonly");
|
||||||
std::tm tm = spdlog::details::os::localtime();
|
std::tm tm = spdlog::details::os::localtime();
|
||||||
spdlog::memory_buf_t w;
|
filename_memory_buf_t w;
|
||||||
fmt::format_to(w, "{}{:04d}{:02d}{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
|
fmt::format_to(w, SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
|
||||||
|
|
||||||
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
|
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
|
||||||
for (int i = 0; i < 10; ++i)
|
for (int i = 0; i < 10; ++i)
|
||||||
@@ -57,7 +65,13 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger]")
|
|||||||
|
|
||||||
logger->flush();
|
logger->flush();
|
||||||
|
|
||||||
|
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||||
|
spdlog::memory_buf_t buf;
|
||||||
|
spdlog::details::os::wstr_to_utf8buf(fmt::to_string(w), buf);
|
||||||
|
auto filename = fmt::to_string(buf);
|
||||||
|
#else
|
||||||
auto filename = fmt::to_string(w);
|
auto filename = fmt::to_string(w);
|
||||||
|
#endif
|
||||||
require_message_count(filename, 10);
|
require_message_count(filename, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,20 +81,20 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger]")
|
|||||||
|
|
||||||
TEST_CASE("rotating_file_sink::calc_filename1", "[rotating_file_sink]]")
|
TEST_CASE("rotating_file_sink::calc_filename1", "[rotating_file_sink]]")
|
||||||
{
|
{
|
||||||
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename("rotated.txt", 3);
|
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename(SPDLOG_FILENAME_T("rotated.txt"), 3);
|
||||||
REQUIRE(filename == "rotated.3.txt");
|
REQUIRE(filename == SPDLOG_FILENAME_T("rotated.3.txt"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("rotating_file_sink::calc_filename2", "[rotating_file_sink]]")
|
TEST_CASE("rotating_file_sink::calc_filename2", "[rotating_file_sink]]")
|
||||||
{
|
{
|
||||||
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename("rotated", 3);
|
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename(SPDLOG_FILENAME_T("rotated"), 3);
|
||||||
REQUIRE(filename == "rotated.3");
|
REQUIRE(filename == SPDLOG_FILENAME_T("rotated.3"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("rotating_file_sink::calc_filename3", "[rotating_file_sink]]")
|
TEST_CASE("rotating_file_sink::calc_filename3", "[rotating_file_sink]]")
|
||||||
{
|
{
|
||||||
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename("rotated.txt", 0);
|
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename(SPDLOG_FILENAME_T("rotated.txt"), 0);
|
||||||
REQUIRE(filename == "rotated.txt");
|
REQUIRE(filename == SPDLOG_FILENAME_T("rotated.txt"));
|
||||||
}
|
}
|
||||||
|
|
||||||
// regex supported only from gcc 4.9 and above
|
// regex supported only from gcc 4.9 and above
|
||||||
@@ -91,10 +105,10 @@ TEST_CASE("rotating_file_sink::calc_filename3", "[rotating_file_sink]]")
|
|||||||
TEST_CASE("daily_file_sink::daily_filename_calculator", "[daily_file_sink]]")
|
TEST_CASE("daily_file_sink::daily_filename_calculator", "[daily_file_sink]]")
|
||||||
{
|
{
|
||||||
// daily_YYYY-MM-DD_hh-mm.txt
|
// daily_YYYY-MM-DD_hh-mm.txt
|
||||||
auto filename = spdlog::sinks::daily_filename_calculator::calc_filename("daily.txt", spdlog::details::os::localtime());
|
auto filename = spdlog::sinks::daily_filename_calculator::calc_filename(SPDLOG_FILENAME_T("daily.txt"), spdlog::details::os::localtime());
|
||||||
// date regex based on https://www.regular-expressions.info/dates.html
|
// date regex based on https://www.regular-expressions.info/dates.html
|
||||||
std::regex re(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])\.txt$)");
|
std::basic_regex<spdlog::filename_t::value_type> re(SPDLOG_FILENAME_T(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])\.txt$)"));
|
||||||
std::smatch match;
|
std::match_results<spdlog::filename_t::const_iterator> match;
|
||||||
REQUIRE(std::regex_match(filename, match, re));
|
REQUIRE(std::regex_match(filename, match, re));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -116,7 +130,7 @@ static void test_rotate(int days_to_run, uint16_t max_days, uint16_t expected_n_
|
|||||||
|
|
||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
|
|
||||||
std::string basename = "test_logs/daily_rotate.txt";
|
spdlog::filename_t basename = SPDLOG_FILENAME_T("test_logs/daily_rotate.txt");
|
||||||
daily_file_sink_st sink{basename, 2, 30, true, max_days};
|
daily_file_sink_st sink{basename, 2, 30, true, max_days};
|
||||||
|
|
||||||
// simulate messages with 24 intervals
|
// simulate messages with 24 intervals
|
||||||
|
@@ -5,6 +5,9 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#define SIMPLE_LOG "test_logs/simple_log.txt"
|
||||||
|
#define SIMPLE_ASYNC_LOG "test_logs/simple_async_log.txt"
|
||||||
|
|
||||||
class failing_sink : public spdlog::sinks::base_sink<std::mutex>
|
class failing_sink : public spdlog::sinks::base_sink<std::mutex>
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
@@ -22,7 +25,7 @@ protected:
|
|||||||
TEST_CASE("default_error_handler", "[errors]]")
|
TEST_CASE("default_error_handler", "[errors]]")
|
||||||
{
|
{
|
||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
std::string filename = "test_logs/simple_log.txt";
|
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
|
||||||
|
|
||||||
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("test-error", filename, true);
|
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("test-error", filename, true);
|
||||||
logger->set_pattern("%v");
|
logger->set_pattern("%v");
|
||||||
@@ -31,8 +34,8 @@ TEST_CASE("default_error_handler", "[errors]]")
|
|||||||
logger->flush();
|
logger->flush();
|
||||||
|
|
||||||
using spdlog::details::os::default_eol;
|
using spdlog::details::os::default_eol;
|
||||||
REQUIRE(file_contents(filename) == fmt::format("Test message 2{}", default_eol));
|
REQUIRE(file_contents(SIMPLE_LOG) == fmt::format("Test message 2{}", default_eol));
|
||||||
REQUIRE(count_lines(filename) == 1);
|
REQUIRE(count_lines(SIMPLE_LOG) == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct custom_ex
|
struct custom_ex
|
||||||
@@ -40,7 +43,7 @@ struct custom_ex
|
|||||||
TEST_CASE("custom_error_handler", "[errors]]")
|
TEST_CASE("custom_error_handler", "[errors]]")
|
||||||
{
|
{
|
||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
std::string filename = "test_logs/simple_log.txt";
|
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
|
||||||
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename, true);
|
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename, true);
|
||||||
logger->flush_on(spdlog::level::info);
|
logger->flush_on(spdlog::level::info);
|
||||||
logger->set_error_handler([=](const std::string &) { throw custom_ex(); });
|
logger->set_error_handler([=](const std::string &) { throw custom_ex(); });
|
||||||
@@ -48,7 +51,7 @@ TEST_CASE("custom_error_handler", "[errors]]")
|
|||||||
|
|
||||||
REQUIRE_THROWS_AS(logger->info("Bad format msg {} {}", "xxx"), custom_ex);
|
REQUIRE_THROWS_AS(logger->info("Bad format msg {} {}", "xxx"), custom_ex);
|
||||||
logger->info("Good message #2");
|
logger->info("Good message #2");
|
||||||
require_message_count(filename, 2);
|
require_message_count(SIMPLE_LOG, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("default_error_handler2", "[errors]]")
|
TEST_CASE("default_error_handler2", "[errors]]")
|
||||||
@@ -72,7 +75,7 @@ TEST_CASE("async_error_handler", "[errors]]")
|
|||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
std::string err_msg("log failed with some msg");
|
std::string err_msg("log failed with some msg");
|
||||||
|
|
||||||
std::string filename = "test_logs/simple_async_log.txt";
|
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_ASYNC_LOG);
|
||||||
{
|
{
|
||||||
spdlog::init_thread_pool(128, 1);
|
spdlog::init_thread_pool(128, 1);
|
||||||
auto logger = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("logger", filename, true);
|
auto logger = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("logger", filename, true);
|
||||||
@@ -90,7 +93,7 @@ TEST_CASE("async_error_handler", "[errors]]")
|
|||||||
spdlog::drop("logger"); // force logger to drain the queue and shutdown
|
spdlog::drop("logger"); // force logger to drain the queue and shutdown
|
||||||
}
|
}
|
||||||
spdlog::init_thread_pool(128, 1);
|
spdlog::init_thread_pool(128, 1);
|
||||||
require_message_count(filename, 2);
|
require_message_count(SIMPLE_ASYNC_LOG, 2);
|
||||||
REQUIRE(file_contents("test_logs/custom_err.txt") == err_msg);
|
REQUIRE(file_contents("test_logs/custom_err.txt") == err_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,7 +103,7 @@ TEST_CASE("async_error_handler2", "[errors]]")
|
|||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
std::string err_msg("This is async handler error message");
|
std::string err_msg("This is async handler error message");
|
||||||
{
|
{
|
||||||
spdlog::details::os::create_dir("test_logs");
|
spdlog::details::os::create_dir(SPDLOG_FILENAME_T("test_logs"));
|
||||||
spdlog::init_thread_pool(128, 1);
|
spdlog::init_thread_pool(128, 1);
|
||||||
auto logger = spdlog::create_async<failing_sink>("failed_logger");
|
auto logger = spdlog::create_async<failing_sink>("failed_logger");
|
||||||
logger->set_error_handler([=](const std::string &) {
|
logger->set_error_handler([=](const std::string &) {
|
||||||
|
@@ -24,20 +24,20 @@ static void test_single_print(std::function<void(std::string const &)> do_log, s
|
|||||||
REQUIRE(CloseEventLog(handle_));
|
REQUIRE(CloseEventLog(handle_));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} event_log{::OpenEventLog(nullptr, TEST_SOURCE)};
|
} event_log{::OpenEventLogA(nullptr, TEST_SOURCE)};
|
||||||
|
|
||||||
REQUIRE(event_log.handle_);
|
REQUIRE(event_log.handle_);
|
||||||
|
|
||||||
DWORD read_bytes{}, size_needed{};
|
DWORD read_bytes{}, size_needed{};
|
||||||
auto ok =
|
auto ok =
|
||||||
::ReadEventLog(event_log.handle_, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ, 0, &read_bytes, 0, &read_bytes, &size_needed);
|
::ReadEventLogA(event_log.handle_, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ, 0, &read_bytes, 0, &read_bytes, &size_needed);
|
||||||
REQUIRE(!ok);
|
REQUIRE(!ok);
|
||||||
REQUIRE(::GetLastError() == ERROR_INSUFFICIENT_BUFFER);
|
REQUIRE(::GetLastError() == ERROR_INSUFFICIENT_BUFFER);
|
||||||
|
|
||||||
std::vector<char> record_buffer(size_needed);
|
std::vector<char> record_buffer(size_needed);
|
||||||
PEVENTLOGRECORD record = (PEVENTLOGRECORD)record_buffer.data();
|
PEVENTLOGRECORD record = (PEVENTLOGRECORD)record_buffer.data();
|
||||||
|
|
||||||
ok = ::ReadEventLog(
|
ok = ::ReadEventLogA(
|
||||||
event_log.handle_, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ, 0, record, size_needed, &read_bytes, &size_needed);
|
event_log.handle_, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ, 0, record, size_needed, &read_bytes, &size_needed);
|
||||||
REQUIRE(ok);
|
REQUIRE(ok);
|
||||||
|
|
||||||
|
@@ -3,6 +3,8 @@
|
|||||||
*/
|
*/
|
||||||
#include "includes.h"
|
#include "includes.h"
|
||||||
|
|
||||||
|
#define TEST_FILENAME "test_logs/file_helper_test.txt"
|
||||||
|
|
||||||
using spdlog::details::file_helper;
|
using spdlog::details::file_helper;
|
||||||
|
|
||||||
static void write_with_helper(file_helper &helper, size_t howmany)
|
static void write_with_helper(file_helper &helper, size_t howmany)
|
||||||
@@ -18,7 +20,7 @@ TEST_CASE("file_helper_filename", "[file_helper::filename()]]")
|
|||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
|
|
||||||
file_helper helper;
|
file_helper helper;
|
||||||
std::string target_filename = "test_logs/file_helper_test.txt";
|
spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME);
|
||||||
helper.open(target_filename);
|
helper.open(target_filename);
|
||||||
REQUIRE(helper.filename() == target_filename);
|
REQUIRE(helper.filename() == target_filename);
|
||||||
}
|
}
|
||||||
@@ -26,7 +28,7 @@ TEST_CASE("file_helper_filename", "[file_helper::filename()]]")
|
|||||||
TEST_CASE("file_helper_size", "[file_helper::size()]]")
|
TEST_CASE("file_helper_size", "[file_helper::size()]]")
|
||||||
{
|
{
|
||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
std::string target_filename = "test_logs/file_helper_test.txt";
|
spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME);
|
||||||
size_t expected_size = 123;
|
size_t expected_size = 123;
|
||||||
{
|
{
|
||||||
file_helper helper;
|
file_helper helper;
|
||||||
@@ -34,13 +36,13 @@ TEST_CASE("file_helper_size", "[file_helper::size()]]")
|
|||||||
write_with_helper(helper, expected_size);
|
write_with_helper(helper, expected_size);
|
||||||
REQUIRE(static_cast<size_t>(helper.size()) == expected_size);
|
REQUIRE(static_cast<size_t>(helper.size()) == expected_size);
|
||||||
}
|
}
|
||||||
REQUIRE(get_filesize(target_filename) == expected_size);
|
REQUIRE(get_filesize(TEST_FILENAME) == expected_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("file_helper_reopen", "[file_helper::reopen()]]")
|
TEST_CASE("file_helper_reopen", "[file_helper::reopen()]]")
|
||||||
{
|
{
|
||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
std::string target_filename = "test_logs/file_helper_test.txt";
|
spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME);
|
||||||
file_helper helper;
|
file_helper helper;
|
||||||
helper.open(target_filename);
|
helper.open(target_filename);
|
||||||
write_with_helper(helper, 12);
|
write_with_helper(helper, 12);
|
||||||
@@ -52,7 +54,7 @@ TEST_CASE("file_helper_reopen", "[file_helper::reopen()]]")
|
|||||||
TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
|
TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
|
||||||
{
|
{
|
||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
std::string target_filename = "test_logs/file_helper_test.txt";
|
spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME);
|
||||||
size_t expected_size = 14;
|
size_t expected_size = 14;
|
||||||
file_helper helper;
|
file_helper helper;
|
||||||
helper.open(target_filename);
|
helper.open(target_filename);
|
||||||
@@ -62,16 +64,12 @@ TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
|
|||||||
REQUIRE(helper.size() == expected_size);
|
REQUIRE(helper.size() == expected_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void test_split_ext(const char *fname, const char *expect_base, const char *expect_ext)
|
static void test_split_ext(const spdlog::filename_t::value_type *fname, const spdlog::filename_t::value_type *expect_base, const spdlog::filename_t::value_type *expect_ext)
|
||||||
{
|
{
|
||||||
spdlog::filename_t filename(fname);
|
spdlog::filename_t filename(fname);
|
||||||
spdlog::filename_t expected_base(expect_base);
|
spdlog::filename_t expected_base(expect_base);
|
||||||
spdlog::filename_t expected_ext(expect_ext);
|
spdlog::filename_t expected_ext(expect_ext);
|
||||||
|
|
||||||
#ifdef _WIN32 // replace folder sep
|
|
||||||
std::replace(filename.begin(), filename.end(), '/', '\\');
|
|
||||||
std::replace(expected_base.begin(), expected_base.end(), '/', '\\');
|
|
||||||
#endif
|
|
||||||
spdlog::filename_t basename;
|
spdlog::filename_t basename;
|
||||||
spdlog::filename_t ext;
|
spdlog::filename_t ext;
|
||||||
std::tie(basename, ext) = file_helper::split_by_extension(filename);
|
std::tie(basename, ext) = file_helper::split_by_extension(filename);
|
||||||
@@ -81,22 +79,22 @@ static void test_split_ext(const char *fname, const char *expect_base, const cha
|
|||||||
|
|
||||||
TEST_CASE("file_helper_split_by_extension", "[file_helper::split_by_extension()]]")
|
TEST_CASE("file_helper_split_by_extension", "[file_helper::split_by_extension()]]")
|
||||||
{
|
{
|
||||||
test_split_ext("mylog.txt", "mylog", ".txt");
|
test_split_ext(SPDLOG_FILENAME_T("mylog.txt"), SPDLOG_FILENAME_T("mylog"), SPDLOG_FILENAME_T(".txt"));
|
||||||
test_split_ext(".mylog.txt", ".mylog", ".txt");
|
test_split_ext(SPDLOG_FILENAME_T(".mylog.txt"), SPDLOG_FILENAME_T(".mylog"), SPDLOG_FILENAME_T(".txt"));
|
||||||
test_split_ext(".mylog", ".mylog", "");
|
test_split_ext(SPDLOG_FILENAME_T(".mylog"), SPDLOG_FILENAME_T(".mylog"), SPDLOG_FILENAME_T(""));
|
||||||
test_split_ext("/aaa/bb.d/mylog", "/aaa/bb.d/mylog", "");
|
test_split_ext(SPDLOG_FILENAME_T("/aaa/bb.d/mylog"), SPDLOG_FILENAME_T("/aaa/bb.d/mylog"), SPDLOG_FILENAME_T(""));
|
||||||
test_split_ext("/aaa/bb.d/mylog.txt", "/aaa/bb.d/mylog", ".txt");
|
test_split_ext(SPDLOG_FILENAME_T("/aaa/bb.d/mylog.txt"), SPDLOG_FILENAME_T("/aaa/bb.d/mylog"), SPDLOG_FILENAME_T(".txt"));
|
||||||
test_split_ext("aaa/bbb/ccc/mylog.txt", "aaa/bbb/ccc/mylog", ".txt");
|
test_split_ext(SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog.txt"), SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog"), SPDLOG_FILENAME_T(".txt"));
|
||||||
test_split_ext("aaa/bbb/ccc/mylog.", "aaa/bbb/ccc/mylog.", "");
|
test_split_ext(SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog."), SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog."), SPDLOG_FILENAME_T(""));
|
||||||
test_split_ext("aaa/bbb/ccc/.mylog.txt", "aaa/bbb/ccc/.mylog", ".txt");
|
test_split_ext(SPDLOG_FILENAME_T("aaa/bbb/ccc/.mylog.txt"), SPDLOG_FILENAME_T("aaa/bbb/ccc/.mylog"), SPDLOG_FILENAME_T(".txt"));
|
||||||
test_split_ext("/aaa/bbb/ccc/mylog.txt", "/aaa/bbb/ccc/mylog", ".txt");
|
test_split_ext(SPDLOG_FILENAME_T("/aaa/bbb/ccc/mylog.txt"), SPDLOG_FILENAME_T("/aaa/bbb/ccc/mylog"), SPDLOG_FILENAME_T(".txt"));
|
||||||
test_split_ext("/aaa/bbb/ccc/.mylog", "/aaa/bbb/ccc/.mylog", "");
|
test_split_ext(SPDLOG_FILENAME_T("/aaa/bbb/ccc/.mylog"), SPDLOG_FILENAME_T("/aaa/bbb/ccc/.mylog"), SPDLOG_FILENAME_T(""));
|
||||||
test_split_ext("../mylog.txt", "../mylog", ".txt");
|
test_split_ext(SPDLOG_FILENAME_T("../mylog.txt"), SPDLOG_FILENAME_T("../mylog"), SPDLOG_FILENAME_T(".txt"));
|
||||||
test_split_ext(".././mylog.txt", ".././mylog", ".txt");
|
test_split_ext(SPDLOG_FILENAME_T(".././mylog.txt"), SPDLOG_FILENAME_T(".././mylog"), SPDLOG_FILENAME_T(".txt"));
|
||||||
test_split_ext(".././mylog.txt/xxx", ".././mylog.txt/xxx", "");
|
test_split_ext(SPDLOG_FILENAME_T(".././mylog.txt/xxx"), SPDLOG_FILENAME_T(".././mylog.txt/xxx"), SPDLOG_FILENAME_T(""));
|
||||||
test_split_ext("/mylog.txt", "/mylog", ".txt");
|
test_split_ext(SPDLOG_FILENAME_T("/mylog.txt"), SPDLOG_FILENAME_T("/mylog"), SPDLOG_FILENAME_T(".txt"));
|
||||||
test_split_ext("//mylog.txt", "//mylog", ".txt");
|
test_split_ext(SPDLOG_FILENAME_T("//mylog.txt"), SPDLOG_FILENAME_T("//mylog"), SPDLOG_FILENAME_T(".txt"));
|
||||||
test_split_ext("", "", "");
|
test_split_ext(SPDLOG_FILENAME_T(""), SPDLOG_FILENAME_T(""), SPDLOG_FILENAME_T(""));
|
||||||
test_split_ext(".", ".", "");
|
test_split_ext(SPDLOG_FILENAME_T("."), SPDLOG_FILENAME_T("."), SPDLOG_FILENAME_T(""));
|
||||||
test_split_ext("..txt", ".", ".txt");
|
test_split_ext(SPDLOG_FILENAME_T("..txt"), SPDLOG_FILENAME_T("."), SPDLOG_FILENAME_T(".txt"));
|
||||||
}
|
}
|
||||||
|
@@ -3,10 +3,13 @@
|
|||||||
*/
|
*/
|
||||||
#include "includes.h"
|
#include "includes.h"
|
||||||
|
|
||||||
|
#define SIMPLE_LOG "test_logs/simple_log"
|
||||||
|
#define ROTATING_LOG "test_logs/rotating_log"
|
||||||
|
|
||||||
TEST_CASE("simple_file_logger", "[simple_logger]]")
|
TEST_CASE("simple_file_logger", "[simple_logger]]")
|
||||||
{
|
{
|
||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
std::string filename = "test_logs/simple_log";
|
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
|
||||||
|
|
||||||
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename);
|
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename);
|
||||||
logger->set_pattern("%v");
|
logger->set_pattern("%v");
|
||||||
@@ -15,29 +18,29 @@ TEST_CASE("simple_file_logger", "[simple_logger]]")
|
|||||||
logger->info("Test message {}", 2);
|
logger->info("Test message {}", 2);
|
||||||
|
|
||||||
logger->flush();
|
logger->flush();
|
||||||
require_message_count(filename, 2);
|
require_message_count(SIMPLE_LOG, 2);
|
||||||
using spdlog::details::os::default_eol;
|
using spdlog::details::os::default_eol;
|
||||||
REQUIRE(file_contents(filename) == fmt::format("Test message 1{}Test message 2{}", default_eol, default_eol));
|
REQUIRE(file_contents(SIMPLE_LOG) == fmt::format("Test message 1{}Test message 2{}", default_eol, default_eol));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("flush_on", "[flush_on]]")
|
TEST_CASE("flush_on", "[flush_on]]")
|
||||||
{
|
{
|
||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
std::string filename = "test_logs/simple_log";
|
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
|
||||||
|
|
||||||
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename);
|
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename);
|
||||||
logger->set_pattern("%v");
|
logger->set_pattern("%v");
|
||||||
logger->set_level(spdlog::level::trace);
|
logger->set_level(spdlog::level::trace);
|
||||||
logger->flush_on(spdlog::level::info);
|
logger->flush_on(spdlog::level::info);
|
||||||
logger->trace("Should not be flushed");
|
logger->trace("Should not be flushed");
|
||||||
REQUIRE(count_lines(filename) == 0);
|
REQUIRE(count_lines(SIMPLE_LOG) == 0);
|
||||||
|
|
||||||
logger->info("Test message {}", 1);
|
logger->info("Test message {}", 1);
|
||||||
logger->info("Test message {}", 2);
|
logger->info("Test message {}", 2);
|
||||||
|
|
||||||
require_message_count(filename, 3);
|
require_message_count(SIMPLE_LOG, 3);
|
||||||
using spdlog::details::os::default_eol;
|
using spdlog::details::os::default_eol;
|
||||||
REQUIRE(file_contents(filename) ==
|
REQUIRE(file_contents(SIMPLE_LOG) ==
|
||||||
fmt::format("Should not be flushed{}Test message 1{}Test message 2{}", default_eol, default_eol, default_eol));
|
fmt::format("Should not be flushed{}Test message 1{}Test message 2{}", default_eol, default_eol, default_eol));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,7 +48,7 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
|
|||||||
{
|
{
|
||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
size_t max_size = 1024 * 10;
|
size_t max_size = 1024 * 10;
|
||||||
std::string basename = "test_logs/rotating_log";
|
spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG);
|
||||||
auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 0);
|
auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 0);
|
||||||
|
|
||||||
for (int i = 0; i < 10; ++i)
|
for (int i = 0; i < 10; ++i)
|
||||||
@@ -54,14 +57,14 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger->flush();
|
logger->flush();
|
||||||
require_message_count(basename, 10);
|
require_message_count(ROTATING_LOG, 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
|
TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
|
||||||
{
|
{
|
||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
size_t max_size = 1024 * 10;
|
size_t max_size = 1024 * 10;
|
||||||
std::string basename = "test_logs/rotating_log";
|
spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG);
|
||||||
|
|
||||||
{
|
{
|
||||||
// make an initial logger to create the first output file
|
// make an initial logger to create the first output file
|
||||||
@@ -83,7 +86,7 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
|
|||||||
|
|
||||||
logger->flush();
|
logger->flush();
|
||||||
|
|
||||||
require_message_count(basename, 10);
|
require_message_count(ROTATING_LOG, 10);
|
||||||
|
|
||||||
for (int i = 0; i < 1000; i++)
|
for (int i = 0; i < 1000; i++)
|
||||||
{
|
{
|
||||||
@@ -92,7 +95,6 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger->flush();
|
logger->flush();
|
||||||
REQUIRE(get_filesize(basename) <= max_size);
|
REQUIRE(get_filesize(ROTATING_LOG) <= max_size);
|
||||||
auto filename1 = basename + ".1";
|
REQUIRE(get_filesize(ROTATING_LOG ".1") <= max_size);
|
||||||
REQUIRE(get_filesize(filename1) <= max_size);
|
|
||||||
}
|
}
|
||||||
|
@@ -8,11 +8,13 @@
|
|||||||
#error "Invalid SPDLOG_ACTIVE_LEVEL in test. Should be SPDLOG_LEVEL_DEBUG"
|
#error "Invalid SPDLOG_ACTIVE_LEVEL in test. Should be SPDLOG_LEVEL_DEBUG"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define TEST_FILENAME "test_logs/simple_log"
|
||||||
|
|
||||||
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 = "test_logs/simple_log";
|
spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME);
|
||||||
|
|
||||||
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename);
|
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename);
|
||||||
logger->set_pattern("%v");
|
logger->set_pattern("%v");
|
||||||
@@ -23,8 +25,8 @@ TEST_CASE("debug and trace w/o format string", "[macros]]")
|
|||||||
logger->flush();
|
logger->flush();
|
||||||
|
|
||||||
using spdlog::details::os::default_eol;
|
using spdlog::details::os::default_eol;
|
||||||
REQUIRE(ends_with(file_contents(filename), fmt::format("Test message 2{}", default_eol)));
|
REQUIRE(ends_with(file_contents(TEST_FILENAME), fmt::format("Test message 2{}", default_eol)));
|
||||||
REQUIRE(count_lines(filename) == 1);
|
REQUIRE(count_lines(TEST_FILENAME) == 1);
|
||||||
|
|
||||||
spdlog::set_default_logger(logger);
|
spdlog::set_default_logger(logger);
|
||||||
|
|
||||||
@@ -32,8 +34,8 @@ TEST_CASE("debug and trace w/o format string", "[macros]]")
|
|||||||
SPDLOG_DEBUG("Test message {}", 4);
|
SPDLOG_DEBUG("Test message {}", 4);
|
||||||
logger->flush();
|
logger->flush();
|
||||||
|
|
||||||
require_message_count(filename, 2);
|
require_message_count(TEST_FILENAME, 2);
|
||||||
REQUIRE(ends_with(file_contents(filename), fmt::format("Test message 4{}", default_eol)));
|
REQUIRE(ends_with(file_contents(TEST_FILENAME), fmt::format("Test message 4{}", default_eol)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("disable param evaluation", "[macros]")
|
TEST_CASE("disable param evaluation", "[macros]")
|
||||||
|
@@ -55,6 +55,14 @@ TEST_CASE("level_to_string_view", "[convert_to_string_view")
|
|||||||
REQUIRE(spdlog::level::to_string_view(spdlog::level::off) == "off");
|
REQUIRE(spdlog::level::to_string_view(spdlog::level::off) == "off");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("set_level_to_string_view", "[set_string_view")
|
||||||
|
{
|
||||||
|
spdlog::level::set_string_view(spdlog::level::info, "INF");
|
||||||
|
REQUIRE(spdlog::level::to_string_view(spdlog::level::info) == "INF");
|
||||||
|
spdlog::level::set_string_view(spdlog::level::info, "info"); // set it back
|
||||||
|
REQUIRE(spdlog::level::to_string_view(spdlog::level::info) == "info");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("to_short_c_str", "[convert_to_short_c_str]")
|
TEST_CASE("to_short_c_str", "[convert_to_short_c_str]")
|
||||||
{
|
{
|
||||||
REQUIRE(std::string(spdlog::level::to_short_c_str(spdlog::level::trace)) == "T");
|
REQUIRE(std::string(spdlog::level::to_short_c_str(spdlog::level::trace)) == "T");
|
||||||
|
@@ -372,7 +372,7 @@ TEST_CASE("clone-custom_formatter", "[pattern_formatter]")
|
|||||||
//
|
//
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
static const char *const test_path = "\\a\\b\\myfile.cpp";
|
static const char *const test_path = "\\a\\b\\c/myfile.cpp";
|
||||||
#else
|
#else
|
||||||
static const char *const test_path = "/a/b//myfile.cpp";
|
static const char *const test_path = "/a/b//myfile.cpp";
|
||||||
#endif
|
#endif
|
||||||
|
@@ -103,7 +103,7 @@ TEST_CASE("disable automatic registration", "[registry]")
|
|||||||
spdlog::set_level(log_level);
|
spdlog::set_level(log_level);
|
||||||
// but disable automatic registration
|
// but disable automatic registration
|
||||||
spdlog::set_automatic_registration(false);
|
spdlog::set_automatic_registration(false);
|
||||||
auto logger1 = spdlog::create<spdlog::sinks::daily_file_sink_st>(tested_logger_name, "filename", 11, 59);
|
auto logger1 = spdlog::create<spdlog::sinks::daily_file_sink_st>(tested_logger_name, SPDLOG_FILENAME_T("filename"), 11, 59);
|
||||||
auto logger2 = spdlog::create_async<spdlog::sinks::stdout_color_sink_mt>(tested_logger_name2);
|
auto logger2 = spdlog::create_async<spdlog::sinks::stdout_color_sink_mt>(tested_logger_name2);
|
||||||
// loggers should not be part of the registry
|
// loggers should not be part of the registry
|
||||||
REQUIRE_FALSE(spdlog::get(tested_logger_name));
|
REQUIRE_FALSE(spdlog::get(tested_logger_name));
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
#include "includes.h"
|
#include "includes.h"
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifdef _WIN32
|
||||||
|
#include <Windows.h>
|
||||||
|
#else
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#endif
|
#endif
|
||||||
@@ -82,7 +84,7 @@ bool ends_with(std::string const &value, std::string const &ending)
|
|||||||
std::size_t count_files(const std::string &folder)
|
std::size_t count_files(const std::string &folder)
|
||||||
{
|
{
|
||||||
size_t counter = 0;
|
size_t counter = 0;
|
||||||
WIN32_FIND_DATA ffd;
|
WIN32_FIND_DATAA ffd;
|
||||||
|
|
||||||
// Start iterating over the files in the folder directory.
|
// Start iterating over the files in the folder directory.
|
||||||
HANDLE hFind = ::FindFirstFileA((folder + "\\*").c_str(), &ffd);
|
HANDLE hFind = ::FindFirstFileA((folder + "\\*").c_str(), &ffd);
|
||||||
@@ -92,7 +94,7 @@ std::size_t count_files(const std::string &folder)
|
|||||||
{
|
{
|
||||||
if (ffd.cFileName[0] != '.')
|
if (ffd.cFileName[0] != '.')
|
||||||
counter++;
|
counter++;
|
||||||
} while (::FindNextFile(hFind, &ffd) != 0);
|
} while (::FindNextFileA(hFind, &ffd) != 0);
|
||||||
::FindClose(hFind);
|
::FindClose(hFind);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
Reference in New Issue
Block a user