mirror of
https://github.com/gabime/spdlog.git
synced 2025-09-29 01:29:35 +08:00
Compare commits
381 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
813dcbcf63 | ||
![]() |
7663f58379 | ||
![]() |
8e3f968ba4 | ||
![]() |
f695e536dd | ||
![]() |
f257e4ea8c | ||
![]() |
0ed3e4cf76 | ||
![]() |
6d355fd602 | ||
![]() |
dd0b7b2d13 | ||
![]() |
42e5d98a48 | ||
![]() |
c060a10c10 | ||
![]() |
79a3a633c7 | ||
![]() |
52dfd478d6 | ||
![]() |
48eca46680 | ||
![]() |
f93277d271 | ||
![]() |
dda8af0092 | ||
![]() |
ed5498a2e9 | ||
![]() |
6dd928dc3c | ||
![]() |
de595fe2b7 | ||
![]() |
d460c3026a | ||
![]() |
88fe218741 | ||
![]() |
b1be7b9fea | ||
![]() |
bec6919587 | ||
![]() |
2f81ff7f17 | ||
![]() |
4db70c2078 | ||
![]() |
26b390bb19 | ||
![]() |
a9149c6d46 | ||
![]() |
859b7f1d58 | ||
![]() |
49989e0678 | ||
![]() |
70274924b7 | ||
![]() |
f5939f9e56 | ||
![]() |
84e307521d | ||
![]() |
3c4a2bf531 | ||
![]() |
60ce47a814 | ||
![]() |
613f024d42 | ||
![]() |
799ba2a57b | ||
![]() |
adbc22096a | ||
![]() |
e7cf25a1c0 | ||
![]() |
dcc7b347ca | ||
![]() |
9a8f5c59e2 | ||
![]() |
c41b6d28b5 | ||
![]() |
fd170b0fe1 | ||
![]() |
587b528292 | ||
![]() |
36c8e79a4a | ||
![]() |
ecec210d0e | ||
![]() |
76d2620dad | ||
![]() |
8ca1d84a32 | ||
![]() |
63d7c64618 | ||
![]() |
10772eadae | ||
![]() |
b220bbb349 | ||
![]() |
5153b44507 | ||
![]() |
27e7412640 | ||
![]() |
93be7713e0 | ||
![]() |
6ab2f0e099 | ||
![]() |
34a9f24dba | ||
![]() |
f70b2ef3b8 | ||
![]() |
79e97fa1ec | ||
![]() |
a66e9bbaf1 | ||
![]() |
f5fe681a41 | ||
![]() |
6fd5f3c866 | ||
![]() |
f4f3e3fb66 | ||
![]() |
23dd8d3559 | ||
![]() |
47c17be9a1 | ||
![]() |
1f3d939009 | ||
![]() |
fbb8244f7d | ||
![]() |
039b34e83a | ||
![]() |
69e6af0bf9 | ||
![]() |
147bdbf591 | ||
![]() |
4974743ee8 | ||
![]() |
1c8cc65f6c | ||
![]() |
a6b5ef55a4 | ||
![]() |
cefea324cb | ||
![]() |
7f953c115d | ||
![]() |
20a4143537 | ||
![]() |
87a44555b6 | ||
![]() |
21ed31844c | ||
![]() |
4a159ad66d | ||
![]() |
19264b8399 | ||
![]() |
709948ff4a | ||
![]() |
30d5fd16d7 | ||
![]() |
9688689938 | ||
![]() |
e8b7f4194a | ||
![]() |
ee525f9bef | ||
![]() |
552d6214e0 | ||
![]() |
dc8ac4c671 | ||
![]() |
cd6a8c9a4c | ||
![]() |
375b7fdda5 | ||
![]() |
3a21b765cb | ||
![]() |
f7fabfb2c4 | ||
![]() |
a4e6d8877c | ||
![]() |
e303f02cce | ||
![]() |
b242fb087d | ||
![]() |
02b9f970d3 | ||
![]() |
e6b9fa577d | ||
![]() |
fe2fa4087d | ||
![]() |
ab25004242 | ||
![]() |
5504630e46 | ||
![]() |
c4d93ae0b5 | ||
![]() |
47cf62f878 | ||
![]() |
4fba14c79f | ||
![]() |
cb02b344a3 | ||
![]() |
fced34e3d8 | ||
![]() |
268222e496 | ||
![]() |
6340c164ce | ||
![]() |
f546036e70 | ||
![]() |
c69b356c86 | ||
![]() |
e35c7fd065 | ||
![]() |
5ed426980b | ||
![]() |
3173422786 | ||
![]() |
fc14ac23e9 | ||
![]() |
13a938fc7f | ||
![]() |
b75da32f4d | ||
![]() |
3a40f0c34d | ||
![]() |
4371092309 | ||
![]() |
f06f3f1468 | ||
![]() |
8fd16fc45e | ||
![]() |
9a04e37275 | ||
![]() |
765095db66 | ||
![]() |
431fb423fa | ||
![]() |
66a2c4993b | ||
![]() |
1e4f14c789 | ||
![]() |
c0f0fd713d | ||
![]() |
97be4532cc | ||
![]() |
868c0cedb0 | ||
![]() |
a767f07ba3 | ||
![]() |
de0154c525 | ||
![]() |
380233b727 | ||
![]() |
b463b06ab5 | ||
![]() |
9a189badbd | ||
![]() |
ed7c3a83f8 | ||
![]() |
d3e013a567 | ||
![]() |
8ee90d3349 | ||
![]() |
e6cbc22da5 | ||
![]() |
2e84ca4fa4 | ||
![]() |
fab55c8a14 | ||
![]() |
9470bdd3ec | ||
![]() |
5d5f2f3ac3 | ||
![]() |
7e09f01847 | ||
![]() |
d98d54896b | ||
![]() |
18a0455b91 | ||
![]() |
5c5080d304 | ||
![]() |
13fb2550c6 | ||
![]() |
c6ad598af9 | ||
![]() |
f8d509d010 | ||
![]() |
e1d4c4651b | ||
![]() |
8d164f47a8 | ||
![]() |
42a56f6669 | ||
![]() |
d56fa23eb9 | ||
![]() |
095cb1f560 | ||
![]() |
e215758b42 | ||
![]() |
87f5a60b90 | ||
![]() |
361344912e | ||
![]() |
7da3e47ddc | ||
![]() |
1f9f9c09a2 | ||
![]() |
8c240faa7d | ||
![]() |
7bbab6889d | ||
![]() |
6cc7e7382e | ||
![]() |
40fc5becff | ||
![]() |
3899e8a560 | ||
![]() |
7f84daffd5 | ||
![]() |
98addad888 | ||
![]() |
1ab63ffdca | ||
![]() |
ef6eb376d3 | ||
![]() |
f29ff77ae7 | ||
![]() |
bf3a415b1d | ||
![]() |
c6c5a46560 | ||
![]() |
def86e6e20 | ||
![]() |
1d6842f0f9 | ||
![]() |
eb92cc35df | ||
![]() |
bd25f59a42 | ||
![]() |
a39f71dbd4 | ||
![]() |
8329d97d90 | ||
![]() |
57dc87732f | ||
![]() |
6547675e43 | ||
![]() |
13199034f0 | ||
![]() |
038733345a | ||
![]() |
2b008efb06 | ||
![]() |
0f25b25b20 | ||
![]() |
42258a1059 | ||
![]() |
862d2f6f35 | ||
![]() |
0aeaf9e28e | ||
![]() |
8ee6d38501 | ||
![]() |
47006c4e8e | ||
![]() |
84a4f56eae | ||
![]() |
e9b8286714 | ||
![]() |
81965bc300 | ||
![]() |
52292fb526 | ||
![]() |
6927aa1544 | ||
![]() |
f0f4a52190 | ||
![]() |
2f205a6dbc | ||
![]() |
4a25802312 | ||
![]() |
260a74509a | ||
![]() |
4da5fa256c | ||
![]() |
53138c20fb | ||
![]() |
9e6d81de08 | ||
![]() |
45e3e7041d | ||
![]() |
8b11ffe163 | ||
![]() |
0c89beaa58 | ||
![]() |
d35e229c44 | ||
![]() |
0a70ef8438 | ||
![]() |
6670d3b925 | ||
![]() |
82404f6f65 | ||
![]() |
8d5ecc1b58 | ||
![]() |
682d2e057f | ||
![]() |
15af514951 | ||
![]() |
129781fd17 | ||
![]() |
b9e8fd209a | ||
![]() |
ea359254d6 | ||
![]() |
a3e84cb347 | ||
![]() |
51b3cc0aef | ||
![]() |
d315bba1f8 | ||
![]() |
b5d838cc32 | ||
![]() |
6a41bc40af | ||
![]() |
b638c71d26 | ||
![]() |
2b5c3615fd | ||
![]() |
f85a08622e | ||
![]() |
0c276beaaf | ||
![]() |
5a8cecdfb6 | ||
![]() |
397d4866b3 | ||
![]() |
27df6eb4ca | ||
![]() |
ad1c18704d | ||
![]() |
029e6ed40f | ||
![]() |
a55615c984 | ||
![]() |
e8da69ebe1 | ||
![]() |
8192c13379 | ||
![]() |
270c08b275 | ||
![]() |
a4714a6571 | ||
![]() |
5585299b03 | ||
![]() |
fd8df5b820 | ||
![]() |
d7a8db8f63 | ||
![]() |
93d84e5d59 | ||
![]() |
311937815e | ||
![]() |
ef665e959f | ||
![]() |
20f4428696 | ||
![]() |
b65c7bad9f | ||
![]() |
06b8193a14 | ||
![]() |
a626ebbbec | ||
![]() |
054eb555ca | ||
![]() |
4f50c5d143 | ||
![]() |
7481a8ecf7 | ||
![]() |
a76a5cfc9c | ||
![]() |
904bed92c3 | ||
![]() |
8650c15749 | ||
![]() |
35865ee54e | ||
![]() |
50c181ea4b | ||
![]() |
2ec188041f | ||
![]() |
e7ec922c0a | ||
![]() |
38456118d0 | ||
![]() |
0a3a3f0ee2 | ||
![]() |
58853d9c95 | ||
![]() |
5ee14906c5 | ||
![]() |
2d873785a5 | ||
![]() |
fec467da7b | ||
![]() |
b5f34c5320 | ||
![]() |
92db8115b7 | ||
![]() |
af35f9c086 | ||
![]() |
0e016882d9 | ||
![]() |
b1a55ca3a4 | ||
![]() |
68cc3e6856 | ||
![]() |
f7574eb4c7 | ||
![]() |
33494049a8 | ||
![]() |
5d23e88c16 | ||
![]() |
6d8efa8d7f | ||
![]() |
343904b56d | ||
![]() |
cf73f02eaf | ||
![]() |
cda27d2bff | ||
![]() |
b61be7320a | ||
![]() |
b2ce64625d | ||
![]() |
f058d3aa74 | ||
![]() |
1c31800210 | ||
![]() |
61cdd170fd | ||
![]() |
d6b34d7b5c | ||
![]() |
bd6fe569b5 | ||
![]() |
d142f13551 | ||
![]() |
e12916c070 | ||
![]() |
f6cece206a | ||
![]() |
817371b225 | ||
![]() |
9eee823041 | ||
![]() |
4e768c146b | ||
![]() |
3cd497ee95 | ||
![]() |
e9fc4ac095 | ||
![]() |
9ccb6af2bd | ||
![]() |
5259b3dbf4 | ||
![]() |
8c67d6e05e | ||
![]() |
b4cb1febf2 | ||
![]() |
0c16b9ae1e | ||
![]() |
83d192b1f1 | ||
![]() |
87ddb9a6c1 | ||
![]() |
6128a87da2 | ||
![]() |
698783861c | ||
![]() |
f14d1c002b | ||
![]() |
0cfdad4d0b | ||
![]() |
94dbefe9b6 | ||
![]() |
73e53c7cb6 | ||
![]() |
9b218d4d12 | ||
![]() |
541dd88a97 | ||
![]() |
ca928bc1b0 | ||
![]() |
b642482432 | ||
![]() |
703a1d9736 | ||
![]() |
6ce507eceb | ||
![]() |
56678a5f6a | ||
![]() |
12800ac466 | ||
![]() |
faa184ce24 | ||
![]() |
fa175d6300 | ||
![]() |
66b08294ca | ||
![]() |
9e37f5cef9 | ||
![]() |
811eeef7a6 | ||
![]() |
1f1f6a5f3b | ||
![]() |
214c67788f | ||
![]() |
e97621d61d | ||
![]() |
b0f8230783 | ||
![]() |
9c04504881 | ||
![]() |
5653e5c9d7 | ||
![]() |
6312748cc7 | ||
![]() |
87d79eb95d | ||
![]() |
96267654e6 | ||
![]() |
72a6fd65da | ||
![]() |
bf02f57475 | ||
![]() |
f2c9692438 | ||
![]() |
59bf37f769 | ||
![]() |
d79af47a28 | ||
![]() |
b67076fc85 | ||
![]() |
c69df8ae44 | ||
![]() |
b18d235b63 | ||
![]() |
01ef3d3114 | ||
![]() |
d01e288afc | ||
![]() |
3ee1bab316 | ||
![]() |
c3757c99d5 | ||
![]() |
2d64a1de1c | ||
![]() |
0d35df584c | ||
![]() |
3afabcd17e | ||
![]() |
d2a367fa7c | ||
![]() |
e562e001cf | ||
![]() |
1df30a0733 | ||
![]() |
3a12f3c560 | ||
![]() |
f310cc460f | ||
![]() |
2fa29987ca | ||
![]() |
8cf39857ab | ||
![]() |
cb75569541 | ||
![]() |
bf327be7f5 | ||
![]() |
6dfdaa3532 | ||
![]() |
6e1bd45d66 | ||
![]() |
cfcd44ca36 | ||
![]() |
42dfa1975a | ||
![]() |
0be736c7fc | ||
![]() |
332b7c0d7f | ||
![]() |
4f52cc4dec | ||
![]() |
dfa2c7a950 | ||
![]() |
73a3a32325 | ||
![]() |
742922f029 | ||
![]() |
b13735dc22 | ||
![]() |
20cb73e9da | ||
![]() |
c68a0de2b6 | ||
![]() |
097ba5a359 | ||
![]() |
e277f9b05c | ||
![]() |
2678c37b56 | ||
![]() |
e556daebc3 | ||
![]() |
2705e35a8c | ||
![]() |
86de264da9 | ||
![]() |
5b2bd79b7e | ||
![]() |
aa0f62292b | ||
![]() |
916a686f8f | ||
![]() |
2c32f826ab | ||
![]() |
c4298a989e | ||
![]() |
c16f89d044 | ||
![]() |
f09e0bd047 | ||
![]() |
23060c6c9d | ||
![]() |
efc8de6d64 | ||
![]() |
4efbd950d6 | ||
![]() |
d8f01c3a72 | ||
![]() |
3af247fbd3 | ||
![]() |
1c4da3eef3 | ||
![]() |
4fcde3b850 | ||
![]() |
e7debaacd7 | ||
![]() |
bdbe908693 | ||
![]() |
39cdd08a54 | ||
![]() |
2fc332abdc | ||
![]() |
f05451650d | ||
![]() |
9a888cde56 | ||
![]() |
d9304f17f5 | ||
![]() |
98af71c585 | ||
![]() |
cee155c1dd |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -45,6 +45,8 @@ example/*
|
||||
!example/example.sln
|
||||
!example/example.vcxproj
|
||||
!example/CMakeLists.txt
|
||||
!example/multisink.cpp
|
||||
!example/jni
|
||||
|
||||
# generated files
|
||||
generated
|
||||
@@ -59,4 +61,7 @@ install_manifest.txt
|
||||
/tests/tests.VC.VC.opendb
|
||||
/tests/tests.VC.db
|
||||
/tests/tests
|
||||
/tests/logs/file_helper_test.txt
|
||||
/tests/logs/*
|
||||
|
||||
# idea
|
||||
.idea/
|
@@ -85,6 +85,7 @@ script:
|
||||
- ./"${BIN}"
|
||||
- valgrind --trace-children=yes --leak-check=full ./"${BIN}"
|
||||
- cd $CHECKOUT_PATH/tests; make rebuild; ./tests
|
||||
- cd $CHECKOUT_PATH/tests; STYLE=printf make rebuild; ./tests
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
|
@@ -4,77 +4,113 @@
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(spdlog VERSION 1.0.0)
|
||||
project(spdlog VERSION 0.16.2)
|
||||
include(CTest)
|
||||
include(CMakeDependentOption)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
# compiler config
|
||||
#---------------------------------------------------------------------------------------
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
set(CMAKE_CXX_FLAGS "-Wall ${CMAKE_CXX_FLAGS}")
|
||||
endif()
|
||||
|
||||
#---------------------------------------------------------------------------------------
|
||||
# spdlog target
|
||||
#---------------------------------------------------------------------------------------
|
||||
add_library(spdlog INTERFACE)
|
||||
|
||||
option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF)
|
||||
option(SPDLOG_BUILD_TESTS "Build tests" OFF)
|
||||
cmake_dependent_option(SPDLOG_BUILD_TESTING
|
||||
"Build spdlog tests" ON
|
||||
"BUILD_TESTING" OFF
|
||||
)
|
||||
|
||||
target_include_directories(
|
||||
spdlog
|
||||
INTERFACE
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
||||
"$<INSTALL_INTERFACE:include>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
)
|
||||
|
||||
set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
|
||||
include(CTest)
|
||||
if(SPDLOG_BUILD_EXAMPLES)
|
||||
add_subdirectory(example)
|
||||
endif()
|
||||
|
||||
if(SPDLOG_BUILD_TESTS)
|
||||
if(SPDLOG_BUILD_TESTING)
|
||||
add_subdirectory(tests)
|
||||
endif()
|
||||
|
||||
### Install ###
|
||||
# * https://github.com/forexample/package-example
|
||||
set(generated_dir "${CMAKE_CURRENT_BINARY_DIR}/generated")
|
||||
|
||||
set(config_install_dir "lib/cmake/${PROJECT_NAME}")
|
||||
set(include_install_dir "include")
|
||||
set(pkgconfig_install_dir "lib/pkgconfig")
|
||||
|
||||
set(version_config "${generated_dir}/${PROJECT_NAME}ConfigVersion.cmake")
|
||||
set(project_config "${generated_dir}/${PROJECT_NAME}Config.cmake")
|
||||
set(pkg_config "${generated_dir}/${PROJECT_NAME}.pc")
|
||||
#---------------------------------------------------------------------------------------
|
||||
# Install/export targets and files
|
||||
#---------------------------------------------------------------------------------------
|
||||
# set files and directories
|
||||
set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
||||
set(include_install_dir "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
set(pkgconfig_install_dir "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||
set(version_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
|
||||
set(project_config "${PROJECT_NAME}Config.cmake")
|
||||
set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc")
|
||||
set(targets_export_name "${PROJECT_NAME}Targets")
|
||||
set(namespace "${PROJECT_NAME}::")
|
||||
|
||||
# generate package version file
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file(
|
||||
"${version_config}" COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
# Note: use 'targets_export_name'
|
||||
configure_file("cmake/Config.cmake.in" "${project_config}" @ONLY)
|
||||
# configure pkg config file
|
||||
configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY)
|
||||
|
||||
# install targets
|
||||
install(
|
||||
TARGETS spdlog
|
||||
EXPORT "${targets_export_name}"
|
||||
INCLUDES DESTINATION "${include_install_dir}"
|
||||
)
|
||||
|
||||
install(DIRECTORY "include/spdlog" DESTINATION "${include_install_dir}")
|
||||
|
||||
# install headers
|
||||
install(
|
||||
FILES "${project_config}" "${version_config}"
|
||||
DIRECTORY "${HEADER_BASE}/${PROJECT_NAME}"
|
||||
DESTINATION "${include_install_dir}"
|
||||
)
|
||||
|
||||
# install project version file
|
||||
install(
|
||||
FILES "${version_config}"
|
||||
DESTINATION "${config_install_dir}"
|
||||
)
|
||||
|
||||
# install pkg config file
|
||||
install(
|
||||
FILES "${pkg_config}"
|
||||
DESTINATION "${pkgconfig_install_dir}"
|
||||
)
|
||||
|
||||
# install project config file
|
||||
install(
|
||||
EXPORT "${targets_export_name}"
|
||||
NAMESPACE "${namespace}"
|
||||
DESTINATION "${config_install_dir}"
|
||||
FILE ${project_config}
|
||||
)
|
||||
|
||||
# export build directory config file
|
||||
export(
|
||||
EXPORT ${targets_export_name}
|
||||
NAMESPACE "${namespace}"
|
||||
FILE ${project_config}
|
||||
)
|
||||
|
||||
# register project in CMake user registry
|
||||
export(PACKAGE ${PROJECT_NAME})
|
||||
|
||||
file(GLOB_RECURSE spdlog_include_SRCS "${HEADER_BASE}/*.h")
|
||||
add_custom_target(spdlog_headers_for_ide SOURCES ${spdlog_include_SRCS})
|
||||
|
188
README.md
188
README.md
@@ -4,26 +4,42 @@ Very fast, header only, C++ logging library. [ to your build tree and use a C++11 compiler
|
||||
#### Just copy the headers:
|
||||
|
||||
* Copy the source [folder](https://github.com/gabime/spdlog/tree/master/include/spdlog) to your build tree and use a C++11 compiler.
|
||||
|
||||
#### Or use your favorite package manager:
|
||||
|
||||
* Ubuntu: `apt-get install libspdlog-dev`
|
||||
* Homebrew: `brew install spdlog`
|
||||
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
|
||||
* Fedora: `yum install spdlog`
|
||||
* Gentoo: `emerge dev-libs/spdlog`
|
||||
* Arch Linux: `yaourt -S spdlog-git`
|
||||
* vcpkg: `vcpkg install spdlog`
|
||||
|
||||
|
||||
## Platforms
|
||||
* Linux (gcc 4.8.1+, clang 3.5+)
|
||||
* Windows (visual studio 2013+, cygwin/mingw with g++ 4.9.1+)
|
||||
* Linux, FreeBSD, Solaris
|
||||
* Windows (vc 2013+, cygwin/mingw)
|
||||
* Mac OSX (clang 3.5+)
|
||||
* Android
|
||||
|
||||
##Features
|
||||
## Features
|
||||
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
|
||||
* Headers only.
|
||||
* No dependencies - just copy and use.
|
||||
* Headers only, just copy and use.
|
||||
* Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library.
|
||||
* Optional printf syntax support.
|
||||
* Extremely fast asynchronous mode (optional) - using lockfree queues and other tricks to reach millions of calls/sec.
|
||||
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
|
||||
* Conditional Logging
|
||||
* Multi/Single threaded loggers.
|
||||
* Various log targets:
|
||||
* Rotating log files.
|
||||
* Daily log files.
|
||||
* Console logging (colors supported).
|
||||
* Linux syslog.
|
||||
* syslog.
|
||||
* Windows debugger (```OutputDebugString(..)```)
|
||||
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
|
||||
* Severity based filtering - threshold levels can be modified in runtime as well as in compile time.
|
||||
|
||||
@@ -57,95 +73,147 @@ Time needed to log 1,000,000 lines in asynchronous mode, i.e. the time it takes
|
||||
|
||||
## Usage Example
|
||||
```c++
|
||||
#include <iostream>
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
int main(int, char* [])
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
void async_example();
|
||||
void syslog_example();
|
||||
void user_defined_example();
|
||||
void err_handler_example();
|
||||
|
||||
namespace spd = spdlog;
|
||||
int main(int, char*[])
|
||||
{
|
||||
namespace spd = spdlog;
|
||||
try
|
||||
{
|
||||
// console logger (multithreaded and with color)
|
||||
auto console = spd::stdout_logger_mt("console", true);
|
||||
console->info("Welcome to spdlog!") ;
|
||||
console->info("An info message example {}..", 1);
|
||||
// Console logger with color
|
||||
auto console = spd::stdout_color_mt("console");
|
||||
console->info("Welcome to spdlog!");
|
||||
console->error("Some error message with arg{}..", 1);
|
||||
|
||||
//Formatting examples
|
||||
console->info("Easy padding in numbers like {:08d}", 12);
|
||||
console->info("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||
// Formatting examples
|
||||
console->warn("Easy padding in numbers like {:08d}", 12);
|
||||
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||
console->info("Support for floats {:03.2f}", 1.23456);
|
||||
console->info("Positional args are {1} {0}..", "too", "supported");
|
||||
|
||||
console->info("{:<30}", "left aligned");
|
||||
console->info("{:>30}", "right aligned");
|
||||
console->info("{:^30}", "centered");
|
||||
|
||||
//
|
||||
// Runtime log levels
|
||||
//
|
||||
spd::set_level(spd::level::info); //Set global log level to info
|
||||
console->debug("This message shold not be displayed!");
|
||||
console->set_level(spd::level::debug); // Set specific logger's log level
|
||||
console->debug("Now it should..");
|
||||
// Use global registry to retrieve loggers
|
||||
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
|
||||
|
||||
//
|
||||
// Create a basic multithreaded file logger (or "basic_logger_st" for single threaded logger)
|
||||
//
|
||||
// Create basic file logger (not rotated)
|
||||
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt");
|
||||
my_logger->info("Some log message");
|
||||
|
||||
//
|
||||
// Create a file rotating logger with 5mb size max and 3 rotated files
|
||||
//
|
||||
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3);
|
||||
for(int i = 0; i < 10; ++i)
|
||||
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
|
||||
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile.txt", 1048576 * 5, 3);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
|
||||
|
||||
//
|
||||
// Create a daily logger - a new file is created every day on 2:30am
|
||||
//
|
||||
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
|
||||
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
||||
// trigger flush if the log severity is error or higher
|
||||
daily_logger->flush_on(spd::level::err);
|
||||
daily_logger->info(123.44);
|
||||
|
||||
//
|
||||
// Customize msg format for all messages
|
||||
//
|
||||
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
|
||||
rotating_logger->info("This is another message with custom format");
|
||||
|
||||
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
|
||||
|
||||
//
|
||||
// Compile time debug or trace macros.
|
||||
// Enabled #ifdef SPDLOG_DEBUG_ON or #ifdef SPDLOG_TRACE_ON
|
||||
//
|
||||
// Runtime log levels
|
||||
spd::set_level(spd::level::info); //Set global log level to info
|
||||
console->debug("This message should not be displayed!");
|
||||
console->set_level(spd::level::debug); // Set specific logger's log level
|
||||
console->debug("This message should be displayed..");
|
||||
|
||||
// Compile time log levels
|
||||
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
|
||||
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
|
||||
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
|
||||
|
||||
//
|
||||
// Asynchronous logging is very fast..
|
||||
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
|
||||
//
|
||||
size_t q_size = 8192; //queue size must be power of 2
|
||||
spdlog::set_async_mode(q_size);
|
||||
auto async_file= spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
|
||||
async_file->info("This is async log..Should be very fast!");
|
||||
async_example();
|
||||
|
||||
//
|
||||
// syslog example. linux only..
|
||||
//
|
||||
#ifdef __linux__
|
||||
std::string ident = "spdlog-example";
|
||||
auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID);
|
||||
syslog_logger->warn("This is warning that will end up in syslog. This is Linux only!");
|
||||
#endif
|
||||
// syslog example. linux/osx only
|
||||
syslog_example();
|
||||
|
||||
// android example. compile with NDK
|
||||
android_example();
|
||||
|
||||
// Log user-defined types example
|
||||
user_defined_example();
|
||||
|
||||
// Change default log error handler
|
||||
err_handler_example();
|
||||
|
||||
// Apply a function on all registered loggers
|
||||
spd::apply_all([&](std::shared_ptr<spd::logger> l)
|
||||
{
|
||||
l->info("End of example.");
|
||||
});
|
||||
|
||||
// Release and close all loggers
|
||||
spd::drop_all();
|
||||
}
|
||||
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
|
||||
catch (const spd::spdlog_ex& ex)
|
||||
{
|
||||
std::cout << "Log failed: " << ex.what() << std::endl;
|
||||
std::cout << "Log init failed: " << ex.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
void async_example()
|
||||
{
|
||||
size_t q_size = 4096; //queue size must be power of 2
|
||||
spd::set_async_mode(q_size);
|
||||
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
|
||||
for (int i = 0; i < 100; ++i)
|
||||
async_file->info("Async message #{}", i);
|
||||
}
|
||||
|
||||
//syslog example
|
||||
void syslog_example()
|
||||
{
|
||||
#ifdef SPDLOG_ENABLE_SYSLOG
|
||||
std::string ident = "spdlog-example";
|
||||
auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID);
|
||||
syslog_logger->warn("This is warning that will end up in syslog..");
|
||||
#endif
|
||||
}
|
||||
|
||||
// user defined types logging by implementing operator<<
|
||||
struct my_type
|
||||
{
|
||||
int i;
|
||||
template<typename OStream>
|
||||
friend OStream& operator<<(OStream& os, const my_type &c)
|
||||
{
|
||||
return os << "[my_type i="<<c.i << "]";
|
||||
}
|
||||
};
|
||||
|
||||
#include <spdlog/fmt/ostr.h> // must be included
|
||||
void user_defined_example()
|
||||
{
|
||||
spd::get("console")->info("user defined type: {}", my_type { 14 });
|
||||
}
|
||||
|
||||
//
|
||||
//custom error handler
|
||||
//
|
||||
void err_handler_example()
|
||||
{
|
||||
spd::set_error_handler([](const std::string& msg) {
|
||||
std::cerr << "my err handler: " << msg << std::endl;
|
||||
});
|
||||
// (or logger->set_error_handler(..) to set for specific logger)
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
32
appveyor.yml
Normal file
32
appveyor.yml
Normal file
@@ -0,0 +1,32 @@
|
||||
version: 1.0.{build}
|
||||
image: Visual Studio 2015
|
||||
environment:
|
||||
matrix:
|
||||
- GENERATOR: '"MinGW Makefiles"'
|
||||
BUILD_TYPE: Debug
|
||||
- GENERATOR: '"MinGW Makefiles"'
|
||||
BUILD_TYPE: Release
|
||||
- GENERATOR: '"Visual Studio 14 2015"'
|
||||
BUILD_TYPE: Debug
|
||||
- GENERATOR: '"Visual Studio 14 2015"'
|
||||
BUILD_TYPE: Release
|
||||
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
||||
BUILD_TYPE: Debug
|
||||
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
||||
BUILD_TYPE: Release
|
||||
build_script:
|
||||
- cmd: >-
|
||||
set
|
||||
|
||||
mkdir build
|
||||
|
||||
cd build
|
||||
|
||||
set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
|
||||
|
||||
set PATH=C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin;%PATH%
|
||||
|
||||
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE%
|
||||
|
||||
cmake --build . --config %BUILD_TYPE%
|
||||
test: off
|
@@ -1,9 +1,9 @@
|
||||
CXX ?= g++
|
||||
CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
|
||||
CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -I../include
|
||||
CXX_RELEASE_FLAGS = -O3 -flto -DNDEBUG
|
||||
|
||||
|
||||
binaries=spdlog-bench spdlog-bench-mt spdlog-async zf_log-bench zf_log-bench-mt boost-bench boost-bench-mt glog-bench glog-bench-mt g2log-async easylogging-bench easylogging-bench-mt
|
||||
binaries=spdlog-bench spdlog-bench-mt spdlog-async spdlog-null-async boost-bench boost-bench-mt glog-bench glog-bench-mt g2log-async easylogging-bench easylogging-bench-mt
|
||||
|
||||
all: $(binaries)
|
||||
|
||||
@@ -17,12 +17,9 @@ spdlog-async: spdlog-async.cpp
|
||||
$(CXX) spdlog-async.cpp -o spdlog-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
|
||||
ZF_LOG_FLAGS = -I../../zf_log.git/zf_log/
|
||||
zf_log-bench: zf_log-bench.cpp
|
||||
$(CXX) zf_log-bench.cpp -o zf_log-bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS) $(ZF_LOG_FLAGS)
|
||||
spdlog-null-async: spdlog-null-async.cpp
|
||||
$(CXX) spdlog-null-async.cpp -o spdlog-null-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||
|
||||
zf_log-bench-mt: zf_log-bench-mt.cpp
|
||||
$(CXX) zf_log-bench-mt.cpp -o zf_log-bench-mt $(CXXFLAGS) $(CXX_RELEASE_FLAGS) $(ZF_LOG_FLAGS)
|
||||
|
||||
|
||||
BOOST_FLAGS = -DBOOST_LOG_DYN_LINK -I/usr/include -lboost_log -lboost_log_setup -lboost_filesystem -lboost_system -lboost_thread -lboost_regex -lboost_date_time -lboost_chrono
|
||||
|
@@ -2,7 +2,6 @@
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#include <boost/log/core.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/log/expressions.hpp>
|
||||
|
32
bench/latency/Makefile
Normal file
32
bench/latency/Makefile
Normal file
@@ -0,0 +1,32 @@
|
||||
CXX ?= g++
|
||||
CXXFLAGS = -march=native -Wall -std=c++11 -pthread
|
||||
CXX_RELEASE_FLAGS = -O2 -DNDEBUG
|
||||
|
||||
|
||||
binaries=spdlog-latency g3log-latency g3log-crush
|
||||
|
||||
all: $(binaries)
|
||||
|
||||
spdlog-latency: spdlog-latency.cpp
|
||||
$(CXX) spdlog-latency.cpp -o spdlog-latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -I../../include
|
||||
|
||||
|
||||
|
||||
g3log-latency: g3log-latency.cpp
|
||||
$(CXX) g3log-latency.cpp -o g3log-latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -I../../../g3log/src -L. -lg3logger
|
||||
|
||||
|
||||
g3log-crush: g3log-crush.cpp
|
||||
$(CXX) g3log-crush.cpp -o g3log-crush $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -I../../../g3log/src -L. -lg3logger
|
||||
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
clean:
|
||||
rm -f *.o *.log $(binaries)
|
||||
|
||||
|
||||
rebuild: clean all
|
||||
|
||||
|
||||
|
13
bench/latency/compare.sh
Executable file
13
bench/latency/compare.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
echo "running spdlog and g3log tests 10 time with ${1:-10} threads each (total 1,000,000 entries).."
|
||||
rm -f *.log
|
||||
for i in {1..10}
|
||||
|
||||
do
|
||||
echo
|
||||
sleep 0.5
|
||||
./spdlog-latency ${1:-10} 2>/dev/null || exit
|
||||
sleep 0.5
|
||||
./g3log-latency ${1:-10} 2>/dev/null || exit
|
||||
|
||||
done
|
37
bench/latency/g3log-crush.cpp
Normal file
37
bench/latency/g3log-crush.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
#include <iostream>
|
||||
|
||||
#include <g3log/g3log.hpp>
|
||||
#include <g3log/logworker.hpp>
|
||||
|
||||
void CrusherLoop()
|
||||
{
|
||||
size_t counter = 0;
|
||||
while (true)
|
||||
{
|
||||
LOGF(INFO, "Some text to crush you machine. thread:");
|
||||
if(++counter % 1000000 == 0)
|
||||
{
|
||||
std::cout << "Wrote " << counter << " entries" << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
std::cout << "WARNING: This test will exaust all your machine memory and will crush it!" << std::endl;
|
||||
std::cout << "Are you sure you want to continue ? " << std::endl;
|
||||
char c;
|
||||
std::cin >> c;
|
||||
if (toupper( c ) != 'Y')
|
||||
return 0;
|
||||
|
||||
auto worker = g3::LogWorker::createLogWorker();
|
||||
auto handle= worker->addDefaultLogger(argv[0], "g3log.txt");
|
||||
g3::initializeLogging(worker.get());
|
||||
CrusherLoop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
129
bench/latency/g3log-latency.cpp
Normal file
129
bench/latency/g3log-latency.cpp
Normal file
@@ -0,0 +1,129 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <numeric>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include "utils.h"
|
||||
#include <g3log/g3log.hpp>
|
||||
#include <g3log/logworker.hpp>
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
const uint64_t g_iterations = 1000000;
|
||||
|
||||
|
||||
std::atomic<size_t> g_counter = {0};
|
||||
|
||||
|
||||
void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t>& result)
|
||||
{
|
||||
|
||||
while (true)
|
||||
{
|
||||
const size_t value_now = ++g_counter;
|
||||
if (value_now > g_iterations)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto start_time = std::chrono::high_resolution_clock::now();
|
||||
LOGF(INFO, "Some text to log for thread: %ld", id);
|
||||
auto stop_time = std::chrono::high_resolution_clock::now();
|
||||
uint64_t time_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time - start_time).count();
|
||||
result.push_back(time_us);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void PrintResults(const std::map<size_t, std::vector<uint64_t>>& threads_result, size_t total_us)
|
||||
{
|
||||
|
||||
std::vector<uint64_t> all_measurements;
|
||||
all_measurements.reserve(g_iterations);
|
||||
for (auto& t_result : threads_result)
|
||||
{
|
||||
all_measurements.insert(all_measurements.end(), t_result.second.begin(), t_result.second.end());
|
||||
}
|
||||
|
||||
// calc worst latenct
|
||||
auto worst = *std::max_element(all_measurements.begin(), all_measurements.end());
|
||||
|
||||
// calc avg
|
||||
auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus<uint64_t>());
|
||||
auto avg = double(total)/all_measurements.size();
|
||||
|
||||
std::cout << "[g3log] worst: " << std::setw(10) << std::right << worst << "\tAvg: " << avg << "\tTotal: " << utils::format(total_us) << " us" << std::endl;
|
||||
|
||||
}
|
||||
}// anonymous
|
||||
|
||||
|
||||
// The purpose of this test is NOT to see how fast
|
||||
// each thread can possibly write. It is to see what
|
||||
// the worst latency is for writing a log entry
|
||||
//
|
||||
// In the test 1 million log entries will be written
|
||||
// an atomic counter is used to give each thread what
|
||||
// it is to write next. The overhead of atomic
|
||||
// synchronization between the threads are not counted in the worst case latency
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
size_t number_of_threads {0};
|
||||
if (argc == 2)
|
||||
{
|
||||
number_of_threads = atoi(argv[1]);
|
||||
}
|
||||
if (argc != 2 || number_of_threads == 0)
|
||||
{
|
||||
std::cerr << "USAGE is: " << argv[0] << " number_threads" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::thread> threads(number_of_threads);
|
||||
std::map<size_t, std::vector<uint64_t>> threads_result;
|
||||
|
||||
for (size_t idx = 0; idx < number_of_threads; ++idx)
|
||||
{
|
||||
// reserve to 1 million for all the result
|
||||
// it's a test so let's not care about the wasted space
|
||||
threads_result[idx].reserve(g_iterations);
|
||||
}
|
||||
|
||||
const std::string g_path = "./" ;
|
||||
const std::string g_prefix_log_name = "g3log-performance-";
|
||||
const std::string g_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt";
|
||||
|
||||
auto worker = g3::LogWorker::createLogWorker();
|
||||
auto handle= worker->addDefaultLogger(argv[0], "g3log.txt");
|
||||
g3::initializeLogging(worker.get());
|
||||
|
||||
auto start_time_application_total = std::chrono::high_resolution_clock::now();
|
||||
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
|
||||
{
|
||||
threads[idx] = std::thread(MeasurePeakDuringLogWrites, idx, std::ref(threads_result[idx]));
|
||||
}
|
||||
for (size_t idx = 0; idx < number_of_threads; ++idx)
|
||||
{
|
||||
threads[idx].join();
|
||||
}
|
||||
auto stop_time_application_total = std::chrono::high_resolution_clock::now();
|
||||
|
||||
uint64_t total_time_in_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time_application_total - start_time_application_total).count();
|
||||
PrintResults(threads_result, total_time_in_us);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
128
bench/latency/spdlog-latency.cpp
Normal file
128
bench/latency/spdlog-latency.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <numeric>
|
||||
#include <functional>
|
||||
#include "utils.h"
|
||||
#include <thread>
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
namespace spd = spdlog;
|
||||
|
||||
namespace
|
||||
{
|
||||
const uint64_t g_iterations = 1000000;
|
||||
|
||||
|
||||
std::atomic<size_t> g_counter = {0};
|
||||
|
||||
|
||||
void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t>& result)
|
||||
{
|
||||
auto logger = spd::get("file_logger");
|
||||
while (true)
|
||||
{
|
||||
const size_t value_now = ++g_counter;
|
||||
if (value_now > g_iterations)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto start_time = std::chrono::high_resolution_clock::now();
|
||||
logger->info("Some text to log for thread: [somemore text...............................] {}", id);
|
||||
auto stop_time = std::chrono::high_resolution_clock::now();
|
||||
uint64_t time_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time - start_time).count();
|
||||
result.push_back(time_us);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PrintResults(const std::map<size_t, std::vector<uint64_t>>& threads_result, size_t total_us)
|
||||
{
|
||||
|
||||
std::vector<uint64_t> all_measurements;
|
||||
all_measurements.reserve(g_iterations);
|
||||
for (auto& t_result : threads_result)
|
||||
{
|
||||
all_measurements.insert(all_measurements.end(), t_result.second.begin(), t_result.second.end());
|
||||
}
|
||||
|
||||
// calc worst latenct
|
||||
auto worst = *std::max_element(all_measurements.begin(), all_measurements.end());
|
||||
|
||||
// calc avg
|
||||
auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus<uint64_t>());
|
||||
auto avg = double(total)/all_measurements.size();
|
||||
|
||||
std::cout << "[spdlog] worst: " << std::setw(10) << std::right << worst << "\tAvg: " << avg << "\tTotal: " << utils::format(total_us) << " us" << std::endl;
|
||||
|
||||
}
|
||||
}// anonymous
|
||||
|
||||
|
||||
// The purpose of this test is NOT to see how fast
|
||||
// each thread can possibly write. It is to see what
|
||||
// the worst latency is for writing a log entry
|
||||
//
|
||||
// In the test 1 million log entries will be written
|
||||
// an atomic counter is used to give each thread what
|
||||
// it is to write next. The overhead of atomic
|
||||
// synchronization between the threads are not counted in the worst case latency
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
size_t number_of_threads {0};
|
||||
if (argc == 2)
|
||||
{
|
||||
number_of_threads = atoi(argv[1]);
|
||||
}
|
||||
if (argc != 2 || number_of_threads == 0)
|
||||
{
|
||||
std::cerr << "usage: " << argv[0] << " number_threads" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
std::vector<std::thread> threads(number_of_threads);
|
||||
std::map<size_t, std::vector<uint64_t>> threads_result;
|
||||
|
||||
for (size_t idx = 0; idx < number_of_threads; ++idx)
|
||||
{
|
||||
// reserve to 1 million for all the result
|
||||
// it's a test so let's not care about the wasted space
|
||||
threads_result[idx].reserve(g_iterations);
|
||||
}
|
||||
|
||||
int queue_size = 1048576; // 2 ^ 20
|
||||
spdlog::set_async_mode(queue_size);
|
||||
auto logger = spdlog::create<spd::sinks::simple_file_sink_mt>("file_logger", "spdlog.log", true);
|
||||
|
||||
//force flush on every call to compare with g3log
|
||||
auto s = (spd::sinks::simple_file_sink_mt*)logger->sinks()[0].get();
|
||||
s->set_force_flush(true);
|
||||
|
||||
auto start_time_application_total = std::chrono::high_resolution_clock::now();
|
||||
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
|
||||
{
|
||||
threads[idx] = std::thread(MeasurePeakDuringLogWrites, idx, std::ref(threads_result[idx]));
|
||||
}
|
||||
for (size_t idx = 0; idx < number_of_threads; ++idx)
|
||||
{
|
||||
threads[idx].join();
|
||||
}
|
||||
auto stop_time_application_total = std::chrono::high_resolution_clock::now();
|
||||
|
||||
uint64_t total_time_in_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time_application_total - start_time_application_total).count();
|
||||
|
||||
PrintResults(threads_result, total_time_in_us);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
35
bench/latency/utils.h
Normal file
35
bench/latency/utils.h
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <locale>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
inline std::string format(const T& value)
|
||||
{
|
||||
static std::locale loc("");
|
||||
std::stringstream ss;
|
||||
ss.imbue(loc);
|
||||
ss << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline std::string format(const double & value)
|
||||
{
|
||||
static std::locale loc("");
|
||||
std::stringstream ss;
|
||||
ss.imbue(loc);
|
||||
ss << std::fixed << std::setprecision(1) << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
}
|
@@ -28,7 +28,7 @@ int main(int argc, char* argv[])
|
||||
logger->set_pattern("[%Y-%b-%d %T.%e]: %v");
|
||||
|
||||
std::atomic<int > msg_counter {0};
|
||||
vector<thread> threads;
|
||||
std::vector<thread> threads;
|
||||
|
||||
for (int t = 0; t < thread_count; ++t)
|
||||
{
|
||||
|
112
bench/spdlog-null-async.cpp
Normal file
112
bench/spdlog-null-async.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
//
|
||||
// bench.cpp : spdlog benchmarks
|
||||
//
|
||||
#include <atomic>
|
||||
#include <cstdlib> // EXIT_FAILURE
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/async_logger.h"
|
||||
#include "spdlog/sinks/null_sink.h"
|
||||
#include "utils.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
using namespace spdlog;
|
||||
using namespace spdlog::sinks;
|
||||
using namespace utils;
|
||||
|
||||
|
||||
|
||||
size_t bench_as(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
||||
int queue_size = 1048576;
|
||||
int howmany = 1000000;
|
||||
int threads = 10;
|
||||
int iters = 10;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
if(argc > 1)
|
||||
howmany = atoi(argv[1]);
|
||||
if (argc > 2)
|
||||
threads = atoi(argv[2]);
|
||||
if (argc > 3)
|
||||
queue_size = atoi(argv[3]);
|
||||
|
||||
|
||||
cout << "\n*******************************************************************************\n";
|
||||
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " messages " << endl;
|
||||
cout << "*******************************************************************************\n";
|
||||
|
||||
spdlog::set_async_mode(queue_size);
|
||||
|
||||
size_t total_rate = 0;
|
||||
|
||||
for(int i = 0; i < iters; ++i)
|
||||
{
|
||||
//auto as = spdlog::daily_logger_st("as", "logs/daily_async");
|
||||
auto as = spdlog::create<null_sink_st>("async(null-sink)");
|
||||
total_rate+= bench_as(howmany, as, threads);
|
||||
spdlog::drop("async(null-sink)");
|
||||
}
|
||||
std::cout << endl;
|
||||
std::cout << "Avg rate: " << format(total_rate/iters) << "/sec" <<std::endl;
|
||||
|
||||
}
|
||||
catch (std::exception &ex)
|
||||
{
|
||||
std::cerr << "Error: " << ex.what() << std::endl;
|
||||
perror("Last error");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//return rate/sec
|
||||
size_t bench_as(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
|
||||
{
|
||||
cout << log->name() << "...\t\t" << flush;
|
||||
std::atomic<int > msg_counter {0};
|
||||
vector<thread> threads;
|
||||
auto start = system_clock::now();
|
||||
for (int t = 0; t < thread_count; ++t)
|
||||
{
|
||||
threads.push_back(std::thread([&]()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
int counter = ++msg_counter;
|
||||
if (counter > howmany) break;
|
||||
log->info("Hello logger: msg number {}", counter);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
for(auto &t:threads)
|
||||
{
|
||||
t.join();
|
||||
};
|
||||
|
||||
|
||||
auto delta = system_clock::now() - start;
|
||||
auto delta_d = duration_cast<duration<double>> (delta).count();
|
||||
auto per_sec = size_t(howmany / delta_d);
|
||||
cout << format(per_sec) << "/sec" << endl;
|
||||
return per_sec;
|
||||
}
|
35
bench/utils.h
Normal file
35
bench/utils.h
Normal file
@@ -0,0 +1,35 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <locale>
|
||||
|
||||
namespace utils
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
inline std::string format(const T& value)
|
||||
{
|
||||
static std::locale loc("");
|
||||
std::stringstream ss;
|
||||
ss.imbue(loc);
|
||||
ss << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template<>
|
||||
inline std::string format(const double & value)
|
||||
{
|
||||
static std::locale loc("");
|
||||
std::stringstream ss;
|
||||
ss.imbue(loc);
|
||||
ss << std::fixed << std::setprecision(1) << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
}
|
@@ -1,56 +0,0 @@
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
#include <atomic>
|
||||
#include <cstdlib>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <zf_log.c>
|
||||
|
||||
const char g_path[] = "logs/zf_log.txt";
|
||||
int g_fd;
|
||||
|
||||
static void output_callback(zf_log_message *msg)
|
||||
{
|
||||
*msg->p = '\n';
|
||||
write(g_fd, msg->buf, msg->p - msg->buf + 1);
|
||||
}
|
||||
|
||||
using namespace std;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
g_fd = open(g_path, O_APPEND|O_CREAT|O_WRONLY);
|
||||
if (0 > g_fd)
|
||||
{
|
||||
ZF_LOGE_AUX(ZF_LOG_STDERR, "Failed to open log file: %s", g_path);
|
||||
return -1;
|
||||
}
|
||||
zf_log_set_output_callback(ZF_LOG_PUT_STD, output_callback);
|
||||
|
||||
int thread_count = 10;
|
||||
if(argc > 1)
|
||||
thread_count = std::atoi(argv[1]);
|
||||
int howmany = 1000000;
|
||||
std::atomic<int > msg_counter {0};
|
||||
vector<thread> threads;
|
||||
|
||||
for (int t = 0; t < thread_count; ++t)
|
||||
{
|
||||
threads.push_back(std::thread([&]()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
int counter = ++msg_counter;
|
||||
if (counter > howmany) break;
|
||||
ZF_LOGI("zf_log message #%i: This is some text for your pleasure", counter);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
for (auto &t:threads)
|
||||
{
|
||||
t.join();
|
||||
};
|
||||
close(g_fd);
|
||||
return 0;
|
||||
}
|
@@ -1,28 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <zf_log.c>
|
||||
|
||||
const char g_path[] = "logs/zf_log.txt";
|
||||
static FILE *g_f;
|
||||
|
||||
static void output_callback(zf_log_message *msg)
|
||||
{
|
||||
*msg->p = '\n';
|
||||
fwrite(msg->buf, msg->p - msg->buf + 1, 1, g_f);
|
||||
}
|
||||
|
||||
int main(int, char* [])
|
||||
{
|
||||
g_f = fopen(g_path, "wb");
|
||||
if (!g_f)
|
||||
{
|
||||
ZF_LOGE_AUX(ZF_LOG_STDERR, "Failed to open log file: %s", g_path);
|
||||
return -1;
|
||||
}
|
||||
zf_log_set_output_callback(ZF_LOG_PUT_STD, output_callback);
|
||||
|
||||
const int howmany = 1000000;
|
||||
for(int i = 0 ; i < howmany; ++i)
|
||||
ZF_LOGI("zf_log message #%i: This is some text for your pleasure", i);
|
||||
fclose(g_f);
|
||||
return 0;
|
||||
}
|
@@ -40,6 +40,9 @@ target_link_libraries(example spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT})
|
||||
add_executable(benchmark bench.cpp)
|
||||
target_link_libraries(benchmark spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
add_executable(multisink multisink.cpp)
|
||||
target_link_libraries(multisink spdlog::spdlog ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
||||
enable_testing()
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
|
||||
add_test(NAME RunExample COMMAND example)
|
||||
|
@@ -1,5 +1,5 @@
|
||||
CXX ?= g++
|
||||
CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
|
||||
CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -Wshadow -pedantic -std=gnu++0x -pthread -Wl,--no-as-needed -I../include
|
||||
CXX_RELEASE_FLAGS = -O3
|
||||
CXX_DEBUG_FLAGS= -g
|
||||
|
||||
|
@@ -35,7 +35,6 @@ int main(int argc, char* argv[])
|
||||
int queue_size = 1048576;
|
||||
int howmany = 1000000;
|
||||
int threads = 10;
|
||||
bool auto_flush = false;
|
||||
int file_size = 30 * 1024 * 1024;
|
||||
int rotating_files = 5;
|
||||
|
||||
@@ -51,29 +50,29 @@ int main(int argc, char* argv[])
|
||||
|
||||
|
||||
cout << "*******************************************************************************\n";
|
||||
cout << "Single thread, " << format(howmany) << " iterations, auto flush=" << auto_flush << endl;
|
||||
cout << "Single thread, " << format(howmany) << " iterations" << endl;
|
||||
cout << "*******************************************************************************\n";
|
||||
|
||||
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st", file_size, rotating_files, auto_flush);
|
||||
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st", file_size, rotating_files);
|
||||
bench(howmany, rotating_st);
|
||||
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st", auto_flush);
|
||||
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st");
|
||||
bench(howmany, daily_st);
|
||||
bench(howmany, spdlog::create<null_sink_st>("null_st"));
|
||||
|
||||
cout << "\n*******************************************************************************\n";
|
||||
cout << threads << " threads sharing same logger, " << format(howmany) << " iterations, auto_flush=" << auto_flush << endl;
|
||||
cout << threads << " threads sharing same logger, " << format(howmany) << " iterations" << endl;
|
||||
cout << "*******************************************************************************\n";
|
||||
|
||||
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt", file_size, rotating_files, auto_flush);
|
||||
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt", file_size, rotating_files);
|
||||
bench_mt(howmany, rotating_mt, threads);
|
||||
|
||||
|
||||
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt", auto_flush);
|
||||
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt");
|
||||
bench_mt(howmany, daily_mt, threads);
|
||||
bench(howmany, spdlog::create<null_sink_st>("null_mt"));
|
||||
|
||||
cout << "\n*******************************************************************************\n";
|
||||
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations, auto_flush=" << auto_flush << endl;
|
||||
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations " << endl;
|
||||
cout << "*******************************************************************************\n";
|
||||
|
||||
|
||||
@@ -81,7 +80,7 @@ int main(int argc, char* argv[])
|
||||
|
||||
for(int i = 0; i < 3; ++i)
|
||||
{
|
||||
auto as = spdlog::daily_logger_st("as", "logs/daily_async", auto_flush);
|
||||
auto as = spdlog::daily_logger_st("as", "logs/daily_async");
|
||||
bench_mt(howmany, as, threads);
|
||||
spdlog::drop("as");
|
||||
}
|
||||
|
@@ -5,56 +5,56 @@
|
||||
//
|
||||
// spdlog usage example
|
||||
//
|
||||
//
|
||||
|
||||
#define SPDLOG_TRACE_ON
|
||||
#define SPDLOG_DEBUG_ON
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
#include <cstdlib> // EXIT_FAILURE
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
void async_example();
|
||||
void syslog_example();
|
||||
void android_example();
|
||||
void user_defined_example();
|
||||
void err_handler_example();
|
||||
|
||||
namespace spd = spdlog;
|
||||
int main(int, char*[])
|
||||
{
|
||||
try
|
||||
{
|
||||
// Multithreaded color console
|
||||
auto console = spd::stdout_logger_mt("console", true);
|
||||
// Console logger with color
|
||||
auto console = spd::stdout_color_mt("console");
|
||||
console->info("Welcome to spdlog!");
|
||||
console->error("An info message example {}..", 1);
|
||||
console->error("Some error message with arg{}..", 1);
|
||||
|
||||
|
||||
// Formatting examples
|
||||
console->warn("Easy padding in numbers like {:08d}", 12);
|
||||
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||
console->info("Support for floats {:03.2f}", 1.23456);
|
||||
console->info("Positional args are {1} {0}..", "too", "supported");
|
||||
|
||||
console->info("{:<30}", "left aligned");
|
||||
console->info("{:>30}", "right aligned");
|
||||
console->info("{:^30}", "centered");
|
||||
|
||||
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
|
||||
|
||||
// Runtime log levels
|
||||
spd::set_level(spd::level::info); //Set global log level to info
|
||||
console->debug("This message shold not be displayed!");
|
||||
console->set_level(spd::level::debug); // Set specific logger's log level
|
||||
console->debug("This message shold be displayed..");
|
||||
|
||||
// Create basic file logger (not rotated)
|
||||
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt");
|
||||
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt");
|
||||
my_logger->info("Some log message");
|
||||
|
||||
|
||||
// Create a file rotating logger with 5mb size max and 3 rotated files
|
||||
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile", 1048576 * 5, 3);
|
||||
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
|
||||
|
||||
// Create a daily logger - a new file is created every day on 2:30am
|
||||
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily", 2, 30);
|
||||
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
||||
// trigger flush if the log severity is error or higher
|
||||
daily_logger->flush_on(spd::level::err);
|
||||
daily_logger->info(123.44);
|
||||
|
||||
// Customize msg format for all messages
|
||||
@@ -62,35 +62,51 @@ int main(int, char*[])
|
||||
rotating_logger->info("This is another message with custom format");
|
||||
|
||||
|
||||
// Compile time debug or trace macros.
|
||||
// Enabled #ifdef SPDLOG_DEBUG_ON or #ifdef SPDLOG_TRACE_ON
|
||||
// Runtime log levels
|
||||
spd::set_level(spd::level::info); //Set global log level to info
|
||||
console->debug("This message shold not be displayed!");
|
||||
console->set_level(spd::level::debug); // Set specific logger's log level
|
||||
console->debug("This message shold be displayed..");
|
||||
|
||||
// Compile time log levels
|
||||
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
|
||||
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
|
||||
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
|
||||
|
||||
|
||||
// Asynchronous logging is very fast..
|
||||
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
|
||||
async_example();
|
||||
|
||||
// syslog example. linux/osx only..
|
||||
// syslog example. linux/osx only
|
||||
syslog_example();
|
||||
|
||||
// log user-defined types example..
|
||||
// android example. compile with NDK
|
||||
android_example();
|
||||
|
||||
// Log user-defined types example
|
||||
user_defined_example();
|
||||
|
||||
// Change default log error handler
|
||||
err_handler_example();
|
||||
|
||||
// Apply a function on all registered loggers
|
||||
spd::apply_all([&](std::shared_ptr<spdlog::logger> l)
|
||||
{
|
||||
l->info("End of example.");
|
||||
});
|
||||
|
||||
// Release and close all loggers
|
||||
spdlog::drop_all();
|
||||
}
|
||||
|
||||
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
|
||||
catch (const spd::spdlog_ex& ex)
|
||||
{
|
||||
std::cout << "Log failed: " << ex.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
std::cout << "Log init failed: " << ex.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
void async_example()
|
||||
{
|
||||
size_t q_size = 4096; //queue size must be power of 2
|
||||
@@ -100,13 +116,23 @@ void async_example()
|
||||
async_file->info("Async message #{}", i);
|
||||
}
|
||||
|
||||
//syslog example (linux/osx only)
|
||||
//syslog example (linux/osx/freebsd)
|
||||
void syslog_example()
|
||||
{
|
||||
#if defined (__linux__) || defined(__APPLE__)
|
||||
#ifdef SPDLOG_ENABLE_SYSLOG
|
||||
std::string ident = "spdlog-example";
|
||||
auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID);
|
||||
syslog_logger->warn("This is warning that will end up in syslog. This is Linux only!");
|
||||
syslog_logger->warn("This is warning that will end up in syslog.");
|
||||
#endif
|
||||
}
|
||||
|
||||
// Android example
|
||||
void android_example()
|
||||
{
|
||||
#if defined(__ANDROID__)
|
||||
std::string tag = "spdlog-android";
|
||||
auto android_logger = spd::android_logger("android", tag);
|
||||
android_logger->critical("Use \"adb shell logcat\" to view this message.");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -121,9 +147,21 @@ struct my_type
|
||||
}
|
||||
};
|
||||
|
||||
#include <spdlog/fmt/ostr.h> // must be included
|
||||
#include "spdlog/fmt/ostr.h" // must be included
|
||||
void user_defined_example()
|
||||
{
|
||||
spd::get("console")->info("user defined type: {}", my_type { 14 });
|
||||
}
|
||||
|
||||
//
|
||||
//custom error handler
|
||||
//
|
||||
void err_handler_example()
|
||||
{
|
||||
//can be set globaly or per logger(logger->set_error_handler(..))
|
||||
spdlog::set_error_handler([](const std::string& msg)
|
||||
{
|
||||
std::cerr << "my err handler: " << msg << std::endl;
|
||||
});
|
||||
spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
|
||||
}
|
||||
|
@@ -42,6 +42,7 @@
|
||||
<ClInclude Include="..\include\spdlog\sinks\sink.h" />
|
||||
<ClInclude Include="..\include\spdlog\sinks\stdout_sinks.h" />
|
||||
<ClInclude Include="..\include\spdlog\sinks\syslog_sink.h" />
|
||||
<ClInclude Include="..\include\spdlog\sinks\wincolor_sink.h" />
|
||||
<ClInclude Include="..\include\spdlog\spdlog.h" />
|
||||
<ClInclude Include="..\include\spdlog\tweakme.h" />
|
||||
</ItemGroup>
|
||||
@@ -55,13 +56,13 @@
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v140</PlatformToolset>
|
||||
<PlatformToolset>v120</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
|
15
example/jni/Android.mk
Normal file
15
example/jni/Android.mk
Normal file
@@ -0,0 +1,15 @@
|
||||
# Setup a project
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE := example
|
||||
LOCAL_SRC_FILES := example.cpp
|
||||
LOCAL_CPPFLAGS += -Wall -Wshadow -Wextra -pedantic -std=c++11 -fPIE -pie
|
||||
LOCAL_LDFLAGS += -fPIE -pie
|
||||
|
||||
# Add exception support and set path for spdlog's headers
|
||||
LOCAL_CPPFLAGS += -fexceptions -I../include
|
||||
# Use android's log library
|
||||
LOCAL_LDFLAGS += -llog
|
||||
|
||||
include $(BUILD_EXECUTABLE)
|
2
example/jni/Application.mk
Normal file
2
example/jni/Application.mk
Normal file
@@ -0,0 +1,2 @@
|
||||
# Exceptions are used in spdlog. Link to an exception-ready C++ runtime.
|
||||
APP_STL = gnustl_static
|
1
example/jni/example.cpp
Symbolic link
1
example/jni/example.cpp
Symbolic link
@@ -0,0 +1 @@
|
||||
../example.cpp
|
47
example/multisink.cpp
Normal file
47
example/multisink.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
namespace spd = spdlog;
|
||||
int main(int, char*[])
|
||||
{
|
||||
bool enable_debug = true;
|
||||
try
|
||||
{
|
||||
// This other example use a single logger with multiple sinks.
|
||||
// This means that the same log_msg is forwarded to multiple sinks;
|
||||
// Each sink can have it's own log level and a message will be logged.
|
||||
std::vector<spdlog::sink_ptr> sinks;
|
||||
sinks.push_back( std::make_shared<spdlog::sinks::stdout_sink_mt>() );
|
||||
sinks.push_back( std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_regular_file.txt") );
|
||||
sinks.push_back( std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_debug_file.txt") );
|
||||
|
||||
spdlog::logger console_multisink("multisink", sinks.begin(), sinks.end() );
|
||||
console_multisink.set_level( spdlog::level::warn);
|
||||
|
||||
sinks[0]->set_level( spdlog::level::trace); // console. Allow everything. Default value
|
||||
sinks[1]->set_level( spdlog::level::trace); // regular file. Allow everything. Default value
|
||||
sinks[2]->set_level( spdlog::level::off); // regular file. Ignore everything.
|
||||
|
||||
console_multisink.warn("warn: will print only on console and regular file");
|
||||
|
||||
if( enable_debug )
|
||||
{
|
||||
console_multisink.set_level( spdlog::level::debug); // level of the logger
|
||||
sinks[1]->set_level( spdlog::level::debug); // regular file
|
||||
sinks[2]->set_level( spdlog::level::debug); // debug file
|
||||
}
|
||||
console_multisink.debug("Debug: you should see this on console and both files");
|
||||
|
||||
// Release and close all loggers
|
||||
spdlog::drop_all();
|
||||
}
|
||||
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
|
||||
catch (const spd::spdlog_ex& ex)
|
||||
{
|
||||
std::cout << "Log init failed: " << ex.what() << std::endl;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
@@ -15,8 +15,8 @@
|
||||
// 3. will throw spdlog_ex upon log exceptions
|
||||
// Upon destruction, logs all remaining messages in the queue before destructing..
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/logger.h>
|
||||
#include "common.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
@@ -31,7 +31,7 @@ namespace details
|
||||
class async_log_helper;
|
||||
}
|
||||
|
||||
class async_logger :public logger
|
||||
class async_logger SPDLOG_FINAL :public logger
|
||||
{
|
||||
public:
|
||||
template<class It>
|
||||
@@ -60,12 +60,18 @@ public:
|
||||
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
|
||||
const std::function<void()>& worker_teardown_cb = nullptr);
|
||||
|
||||
|
||||
//Wait for the queue to be empty, and flush synchronously
|
||||
//Warning: this can potentially last forever as we wait it to complete
|
||||
void flush() override;
|
||||
|
||||
// Error handler
|
||||
virtual void set_error_handler(log_err_handler) override;
|
||||
virtual log_err_handler error_handler() override;
|
||||
|
||||
protected:
|
||||
void _sink_it(details::log_msg& msg) override;
|
||||
void _set_formatter(spdlog::formatter_ptr msg_formatter) override;
|
||||
void _set_pattern(const std::string& pattern) override;
|
||||
void _set_pattern(const std::string& pattern, pattern_time_type pattern_time) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<details::async_log_helper> _async_log_helper;
|
||||
@@ -73,4 +79,4 @@ private:
|
||||
}
|
||||
|
||||
|
||||
#include <spdlog/details/async_logger_impl.h>
|
||||
#include "details/async_logger_impl.h"
|
||||
|
@@ -11,12 +11,14 @@
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <exception>
|
||||
#include<functional>
|
||||
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
#include <codecvt>
|
||||
#include <locale>
|
||||
#endif
|
||||
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
#include "details/null_mutex.h"
|
||||
|
||||
//visual studio upto 2013 does not support noexcept nor constexpr
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
@@ -27,16 +29,22 @@
|
||||
#define SPDLOG_CONSTEXPR constexpr
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define DEPRECATED __attribute__((deprecated))
|
||||
#elif defined(_MSC_VER)
|
||||
#define DEPRECATED __declspec(deprecated)
|
||||
// final keyword support. On by default. See tweakme.h
|
||||
#if defined(SPDLOG_NO_FINAL)
|
||||
#define SPDLOG_FINAL
|
||||
#else
|
||||
#define DEPRECATED
|
||||
#define SPDLOG_FINAL final
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define SPDLOG_DEPRECATED __attribute__((deprecated))
|
||||
#elif defined(_MSC_VER)
|
||||
#define SPDLOG_DEPRECATED __declspec(deprecated)
|
||||
#else
|
||||
#define SPDLOG_DEPRECATED
|
||||
#endif
|
||||
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
#include "fmt/fmt.h"
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
@@ -55,9 +63,10 @@ using formatter_ptr = std::shared_ptr<spdlog::formatter>;
|
||||
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
|
||||
using level_t = details::null_atomic_int;
|
||||
#else
|
||||
using level_t = std::atomic_int;
|
||||
using level_t = std::atomic<int>;
|
||||
#endif
|
||||
|
||||
using log_err_handler = std::function<void(const std::string &err_msg)>;
|
||||
|
||||
//Log level enum
|
||||
namespace level
|
||||
@@ -73,7 +82,10 @@ typedef enum
|
||||
off = 6
|
||||
} level_enum;
|
||||
|
||||
static const char* level_names[] { "trace", "debug", "info", "warning", "error", "critical", "off" };
|
||||
#if !defined(SPDLOG_LEVEL_NAMES)
|
||||
#define SPDLOG_LEVEL_NAMES { "trace", "debug", "info", "warning", "error", "critical", "off" }
|
||||
#endif
|
||||
static const char* level_names[] SPDLOG_LEVEL_NAMES;
|
||||
|
||||
static const char* short_level_names[] { "T", "D", "I", "W", "E", "C", "O" };
|
||||
|
||||
@@ -98,6 +110,15 @@ enum class async_overflow_policy
|
||||
discard_log_msg // Discard the message it enqueue fails
|
||||
};
|
||||
|
||||
//
|
||||
// Pattern time - specific time getting to use for pattern_formatter.
|
||||
// local time by default
|
||||
//
|
||||
enum class pattern_time_type
|
||||
{
|
||||
local, // log localtime
|
||||
utc // log utc
|
||||
};
|
||||
|
||||
//
|
||||
// Log exception
|
||||
|
@@ -9,17 +9,15 @@
|
||||
// If the internal queue of log messages reaches its max size,
|
||||
// then the client call will block until there is more room.
|
||||
//
|
||||
// If the back thread throws during logging, a spdlog::spdlog_ex exception
|
||||
// will be thrown in client's thread when tries to log the next message
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/sinks/sink.h>
|
||||
#include <spdlog/details/mpmc_bounded_q.h>
|
||||
#include <spdlog/details/log_msg.h>
|
||||
#include <spdlog/details/os.h>
|
||||
#include <spdlog/formatter.h>
|
||||
#include "../common.h"
|
||||
#include "../sinks/sink.h"
|
||||
#include "../details/mpmc_bounded_q.h"
|
||||
#include "../details/log_msg.h"
|
||||
#include "../details/os.h"
|
||||
#include "../formatter.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
@@ -53,6 +51,7 @@ class async_log_helper
|
||||
size_t thread_id;
|
||||
std::string txt;
|
||||
async_msg_type msg_type;
|
||||
size_t msg_id;
|
||||
|
||||
async_msg() = default;
|
||||
~async_msg() = default;
|
||||
@@ -62,12 +61,18 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
|
||||
logger_name(std::move(other.logger_name)),
|
||||
level(std::move(other.level)),
|
||||
time(std::move(other.time)),
|
||||
thread_id(other.thread_id),
|
||||
txt(std::move(other.txt)),
|
||||
msg_type(std::move(other.msg_type))
|
||||
msg_type(std::move(other.msg_type)),
|
||||
msg_id(other.msg_id)
|
||||
{}
|
||||
|
||||
async_msg(async_msg_type m_type) :msg_type(m_type)
|
||||
{};
|
||||
async_msg(async_msg_type m_type):
|
||||
level(level::info),
|
||||
thread_id(0),
|
||||
msg_type(m_type),
|
||||
msg_id(0)
|
||||
{}
|
||||
|
||||
async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT
|
||||
{
|
||||
@@ -77,20 +82,22 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
|
||||
thread_id = other.thread_id;
|
||||
txt = std::move(other.txt);
|
||||
msg_type = other.msg_type;
|
||||
msg_id = other.msg_id;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// never copy or assign. should only be moved..
|
||||
async_msg(const async_msg&) = delete;
|
||||
async_msg& operator=(async_msg& other) = delete;
|
||||
async_msg& operator=(const async_msg& other) = delete;
|
||||
|
||||
// construct from log_msg
|
||||
async_msg(const details::log_msg& m) :
|
||||
async_msg(const details::log_msg& m):
|
||||
level(m.level),
|
||||
time(m.time),
|
||||
thread_id(m.thread_id),
|
||||
txt(m.raw.data(), m.raw.size()),
|
||||
msg_type(async_msg_type::log)
|
||||
msg_type(async_msg_type::log),
|
||||
msg_id(m.msg_id)
|
||||
{
|
||||
#ifndef SPDLOG_NO_NAME
|
||||
logger_name = *m.logger_name;
|
||||
@@ -106,6 +113,7 @@ async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
|
||||
msg.time = time;
|
||||
msg.thread_id = thread_id;
|
||||
msg.raw << txt;
|
||||
msg.msg_id = msg_id;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -120,6 +128,7 @@ public:
|
||||
async_log_helper(formatter_ptr formatter,
|
||||
const std::vector<sink_ptr>& sinks,
|
||||
size_t queue_size,
|
||||
const log_err_handler err_handler,
|
||||
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
|
||||
const std::function<void()>& worker_warmup_cb = nullptr,
|
||||
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
|
||||
@@ -132,8 +141,9 @@ public:
|
||||
|
||||
void set_formatter(formatter_ptr);
|
||||
|
||||
void flush();
|
||||
void flush(bool wait_for_q);
|
||||
|
||||
void set_error_handler(spdlog::log_err_handler err_handler);
|
||||
|
||||
private:
|
||||
formatter_ptr _formatter;
|
||||
@@ -142,14 +152,13 @@ private:
|
||||
// queue of messages to log
|
||||
q_type _q;
|
||||
|
||||
log_err_handler _err_handler;
|
||||
|
||||
bool _flush_requested;
|
||||
|
||||
bool _terminate_requested;
|
||||
|
||||
|
||||
// last exception thrown from the worker thread
|
||||
std::shared_ptr<spdlog_ex> _last_workerthread_ex;
|
||||
|
||||
// overflow policy
|
||||
const async_overflow_policy _overflow_policy;
|
||||
|
||||
@@ -167,9 +176,6 @@ private:
|
||||
|
||||
void push_msg(async_msg&& new_msg);
|
||||
|
||||
// throw last worker thread exception or if worker thread is not active
|
||||
void throw_if_bad_worker();
|
||||
|
||||
// worker thread main loop
|
||||
void worker_loop();
|
||||
|
||||
@@ -179,9 +185,12 @@ private:
|
||||
|
||||
void handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush);
|
||||
|
||||
// sleep,yield or return immediatly using the time passed since last message as a hint
|
||||
// sleep,yield or return immediately using the time passed since last message as a hint
|
||||
static void sleep_or_yield(const spdlog::log_clock::time_point& now, const log_clock::time_point& last_op_time);
|
||||
|
||||
// wait until the queue is empty
|
||||
void wait_empty_q();
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -193,6 +202,7 @@ inline spdlog::details::async_log_helper::async_log_helper(
|
||||
formatter_ptr formatter,
|
||||
const std::vector<sink_ptr>& sinks,
|
||||
size_t queue_size,
|
||||
log_err_handler err_handler,
|
||||
const async_overflow_policy overflow_policy,
|
||||
const std::function<void()>& worker_warmup_cb,
|
||||
const std::chrono::milliseconds& flush_interval_ms,
|
||||
@@ -200,6 +210,7 @@ inline spdlog::details::async_log_helper::async_log_helper(
|
||||
_formatter(formatter),
|
||||
_sinks(sinks),
|
||||
_q(queue_size),
|
||||
_err_handler(err_handler),
|
||||
_flush_requested(false),
|
||||
_terminate_requested(false),
|
||||
_overflow_policy(overflow_policy),
|
||||
@@ -219,7 +230,8 @@ inline spdlog::details::async_log_helper::~async_log_helper()
|
||||
_worker_thread.join();
|
||||
}
|
||||
catch (...) // don't crash in destructor
|
||||
{}
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -227,13 +239,10 @@ inline spdlog::details::async_log_helper::~async_log_helper()
|
||||
inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
|
||||
{
|
||||
push_msg(async_msg(msg));
|
||||
|
||||
|
||||
}
|
||||
|
||||
inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg)
|
||||
{
|
||||
throw_if_bad_worker();
|
||||
if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg)
|
||||
{
|
||||
auto last_op_time = details::os::now();
|
||||
@@ -245,42 +254,48 @@ inline void spdlog::details::async_log_helper::push_msg(details::async_log_helpe
|
||||
}
|
||||
while (!_q.enqueue(std::move(new_msg)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline void spdlog::details::async_log_helper::flush()
|
||||
// optionally wait for the queue be empty and request flush from the sinks
|
||||
inline void spdlog::details::async_log_helper::flush(bool wait_for_q)
|
||||
{
|
||||
push_msg(async_msg(async_msg_type::flush));
|
||||
if (wait_for_q)
|
||||
wait_empty_q(); //return when queue is empty
|
||||
}
|
||||
|
||||
inline void spdlog::details::async_log_helper::worker_loop()
|
||||
{
|
||||
try
|
||||
if (_worker_warmup_cb) _worker_warmup_cb();
|
||||
auto last_pop = details::os::now();
|
||||
auto last_flush = last_pop;
|
||||
auto active = true;
|
||||
while (active)
|
||||
{
|
||||
if (_worker_warmup_cb) _worker_warmup_cb();
|
||||
auto last_pop = details::os::now();
|
||||
auto last_flush = last_pop;
|
||||
while(process_next_msg(last_pop, last_flush));
|
||||
if (_worker_teardown_cb) _worker_teardown_cb();
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
_last_workerthread_ex = std::make_shared<spdlog_ex>(std::string("async_logger worker thread exception: ") + ex.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
_last_workerthread_ex = std::make_shared<spdlog_ex>("async_logger worker thread exception");
|
||||
try
|
||||
{
|
||||
active = process_next_msg(last_pop, last_flush);
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
_err_handler(ex.what());
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
_err_handler("Unknown exeption in async logger worker loop.");
|
||||
}
|
||||
}
|
||||
if (_worker_teardown_cb) _worker_teardown_cb();
|
||||
|
||||
|
||||
}
|
||||
|
||||
// process next message in the queue
|
||||
// return true if this thread should still be active (while no terminate msg was received)
|
||||
inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush)
|
||||
{
|
||||
|
||||
async_msg incoming_async_msg;
|
||||
|
||||
|
||||
if (_q.dequeue(incoming_async_msg))
|
||||
{
|
||||
last_pop = details::os::now();
|
||||
@@ -300,7 +315,12 @@ inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_
|
||||
incoming_async_msg.fill_log_msg(incoming_log_msg);
|
||||
_formatter->format(incoming_log_msg);
|
||||
for (auto &s : _sinks)
|
||||
s->log(incoming_log_msg);
|
||||
{
|
||||
if (s->should_log(incoming_log_msg.level))
|
||||
{
|
||||
s->log(incoming_log_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -313,7 +333,6 @@ inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_
|
||||
handle_flush_interval(now, last_flush);
|
||||
sleep_or_yield(now, last_pop);
|
||||
return !_terminate_requested;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -351,30 +370,30 @@ inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_
|
||||
|
||||
// yield upto 150 micros
|
||||
if (time_since_op <= microseconds(100))
|
||||
return yield();
|
||||
|
||||
return std::this_thread::yield();
|
||||
|
||||
// sleep for 20 ms upto 200 ms
|
||||
if (time_since_op <= milliseconds(200))
|
||||
return sleep_for(milliseconds(20));
|
||||
|
||||
// sleep for 200 ms
|
||||
return sleep_for(milliseconds(200));
|
||||
// sleep for 500 ms
|
||||
return sleep_for(milliseconds(500));
|
||||
}
|
||||
|
||||
// throw if the worker thread threw an exception or not active
|
||||
inline void spdlog::details::async_log_helper::throw_if_bad_worker()
|
||||
// wait for the queue to be empty
|
||||
inline void spdlog::details::async_log_helper::wait_empty_q()
|
||||
{
|
||||
if (_last_workerthread_ex)
|
||||
auto last_op = details::os::now();
|
||||
while (!_q.is_empty())
|
||||
{
|
||||
auto ex = std::move(_last_workerthread_ex);
|
||||
throw *ex;
|
||||
sleep_or_yield(details::os::now(), last_op);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler)
|
||||
{
|
||||
_err_handler = err_handler;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@@ -8,8 +8,8 @@
|
||||
// Async Logger implementation
|
||||
// Use an async_sink (queue per logger) to perform the logging in a worker thread
|
||||
|
||||
#include <spdlog/details/async_log_helper.h>
|
||||
#include <spdlog/async_logger.h>
|
||||
#include "../details/async_log_helper.h"
|
||||
#include "../async_logger.h"
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
@@ -26,18 +26,18 @@ inline spdlog::async_logger::async_logger(const std::string& logger_name,
|
||||
const std::chrono::milliseconds& flush_interval_ms,
|
||||
const std::function<void()>& worker_teardown_cb) :
|
||||
logger(logger_name, begin, end),
|
||||
_async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb))
|
||||
_async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, _err_handler, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb))
|
||||
{
|
||||
}
|
||||
|
||||
inline spdlog::async_logger::async_logger(const std::string& logger_name,
|
||||
sinks_init_list sinks,
|
||||
sinks_init_list sinks_list,
|
||||
size_t queue_size,
|
||||
const async_overflow_policy overflow_policy,
|
||||
const std::function<void()>& worker_warmup_cb,
|
||||
const std::chrono::milliseconds& flush_interval_ms,
|
||||
const std::function<void()>& worker_teardown_cb) :
|
||||
async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
|
||||
async_logger(logger_name, sinks_list.begin(), sinks_list.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
|
||||
|
||||
inline spdlog::async_logger::async_logger(const std::string& logger_name,
|
||||
sink_ptr single_sink,
|
||||
@@ -54,24 +54,54 @@ inline spdlog::async_logger::async_logger(const std::string& logger_name,
|
||||
|
||||
inline void spdlog::async_logger::flush()
|
||||
{
|
||||
|
||||
_async_log_helper->flush();
|
||||
_async_log_helper->flush(true);
|
||||
}
|
||||
|
||||
// Error handler
|
||||
inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_handler)
|
||||
{
|
||||
_err_handler = err_handler;
|
||||
_async_log_helper->set_error_handler(err_handler);
|
||||
|
||||
}
|
||||
inline spdlog::log_err_handler spdlog::async_logger::error_handler()
|
||||
{
|
||||
return _err_handler;
|
||||
}
|
||||
|
||||
|
||||
inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)
|
||||
{
|
||||
_formatter = msg_formatter;
|
||||
_async_log_helper->set_formatter(_formatter);
|
||||
}
|
||||
|
||||
inline void spdlog::async_logger::_set_pattern(const std::string& pattern)
|
||||
inline void spdlog::async_logger::_set_pattern(const std::string& pattern, pattern_time_type pattern_time)
|
||||
{
|
||||
_formatter = std::make_shared<pattern_formatter>(pattern);
|
||||
_formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
|
||||
_async_log_helper->set_formatter(_formatter);
|
||||
}
|
||||
|
||||
|
||||
inline void spdlog::async_logger::_sink_it(details::log_msg& msg)
|
||||
{
|
||||
_async_log_helper->log(msg);
|
||||
try
|
||||
{
|
||||
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
|
||||
_incr_msg_counter(msg);
|
||||
#endif
|
||||
_async_log_helper->log(msg);
|
||||
if (_should_flush_on(msg))
|
||||
_async_log_helper->flush(false); // do async flush
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
_err_handler(ex.what());
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
_err_handler("Unknown exception in logger " + _name);
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -7,16 +7,16 @@
|
||||
|
||||
// Helper class for file sink
|
||||
// When failing to open a file, retry several times(5) with small delay between the tries(10 ms)
|
||||
// Can be set to auto flush on every line
|
||||
// Throw spdlog_ex exception on errors
|
||||
|
||||
#include <spdlog/details/os.h>
|
||||
#include <spdlog/details/log_msg.h>
|
||||
#include "../details/os.h"
|
||||
#include "../details/log_msg.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <cerrno>
|
||||
|
||||
namespace spdlog
|
||||
@@ -31,9 +31,8 @@ public:
|
||||
const int open_tries = 5;
|
||||
const int open_interval = 10;
|
||||
|
||||
explicit file_helper(bool force_flush) :
|
||||
_fd(nullptr),
|
||||
_force_flush(force_flush)
|
||||
explicit file_helper() :
|
||||
_fd(nullptr)
|
||||
{}
|
||||
|
||||
file_helper(const file_helper&) = delete;
|
||||
@@ -86,21 +85,17 @@ public:
|
||||
|
||||
void write(const log_msg& msg)
|
||||
{
|
||||
|
||||
size_t msg_size = msg.formatted.size();
|
||||
auto data = msg.formatted.data();
|
||||
if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
|
||||
throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
|
||||
|
||||
if (_force_flush)
|
||||
std::fflush(_fd);
|
||||
}
|
||||
|
||||
size_t size()
|
||||
size_t size() const
|
||||
{
|
||||
if (!_fd)
|
||||
throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
|
||||
return os::filesize(_fd);
|
||||
return os::filesize(_fd);
|
||||
}
|
||||
|
||||
const filename_t& filename() const
|
||||
@@ -108,16 +103,44 @@ public:
|
||||
return _filename;
|
||||
}
|
||||
|
||||
static bool file_exists(const filename_t& name)
|
||||
static bool file_exists(const filename_t& fname)
|
||||
{
|
||||
|
||||
return os::file_exists(name);
|
||||
return os::file_exists(fname);
|
||||
}
|
||||
|
||||
//
|
||||
// return file path and its extension:
|
||||
//
|
||||
// "mylog.txt" => ("mylog", ".txt")
|
||||
// "mylog" => ("mylog", "")
|
||||
// "mylog." => ("mylog.", "")
|
||||
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
|
||||
//
|
||||
// the starting dot in filenames is ignored (hidden files):
|
||||
//
|
||||
// ".mylog" => (".mylog". "")
|
||||
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
||||
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
||||
static std::tuple<filename_t, filename_t> split_by_extenstion(const spdlog::filename_t& fname)
|
||||
{
|
||||
auto ext_index = fname.rfind('.');
|
||||
|
||||
// no valid extension found - return whole path and empty string as extension
|
||||
if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
|
||||
return std::make_tuple(fname, spdlog::filename_t());
|
||||
|
||||
// treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
|
||||
//auto folder_index = fname.find('\\', ext_index);
|
||||
auto folder_index = fname.rfind(details::os::folder_sep);
|
||||
if (folder_index != fname.npos && folder_index >= ext_index - 1)
|
||||
return std::make_tuple(fname, spdlog::filename_t());
|
||||
|
||||
// finally - return a valid base and extnetion tuple
|
||||
return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
|
||||
}
|
||||
private:
|
||||
FILE* _fd;
|
||||
filename_t _filename;
|
||||
bool _force_flush;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -5,8 +5,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/os.h>
|
||||
#include "../common.h"
|
||||
#include "../details/os.h"
|
||||
|
||||
|
||||
#include <string>
|
||||
@@ -19,7 +19,10 @@ namespace details
|
||||
struct log_msg
|
||||
{
|
||||
log_msg() = default;
|
||||
log_msg(const std::string *loggers_name, level::level_enum lvl) : logger_name(loggers_name), level(lvl)
|
||||
log_msg(const std::string *loggers_name, level::level_enum lvl) :
|
||||
logger_name(loggers_name),
|
||||
level(lvl),
|
||||
msg_id(0)
|
||||
{
|
||||
#ifndef SPDLOG_NO_DATETIME
|
||||
time = os::now();
|
||||
@@ -41,6 +44,7 @@ struct log_msg
|
||||
size_t thread_id;
|
||||
fmt::MemoryWriter raw;
|
||||
fmt::MemoryWriter formatted;
|
||||
size_t msg_id;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/logger.h>
|
||||
#include "../logger.h"
|
||||
#include "../sinks/stdout_sinks.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -13,28 +14,34 @@
|
||||
// create logger with given name, sinks and the default pattern formatter
|
||||
// all other ctors will call this one
|
||||
template<class It>
|
||||
inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end) :
|
||||
inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end):
|
||||
_name(logger_name),
|
||||
_sinks(begin, end),
|
||||
_formatter(std::make_shared<pattern_formatter>("%+"))
|
||||
_formatter(std::make_shared<pattern_formatter>("%+")),
|
||||
_level(level::info),
|
||||
_flush_level(level::off),
|
||||
_last_err_time(0),
|
||||
_msg_counter(1) // message counter will start from 1. 0-message id will be reserved for controll messages
|
||||
{
|
||||
|
||||
// no support under vs2013 for member initialization for std::atomic
|
||||
_level = level::info;
|
||||
_flush_level = level::off;
|
||||
_err_handler = [this](const std::string &msg)
|
||||
{
|
||||
this->_default_err_handler(msg);
|
||||
};
|
||||
}
|
||||
|
||||
// ctor with sinks as init list
|
||||
inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list) :
|
||||
logger(logger_name, sinks_list.begin(), sinks_list.end()) {}
|
||||
inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list):
|
||||
logger(logger_name, sinks_list.begin(), sinks_list.end())
|
||||
{}
|
||||
|
||||
|
||||
// ctor with single sink
|
||||
inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink) :
|
||||
inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink):
|
||||
logger(logger_name,
|
||||
{
|
||||
single_sink
|
||||
}) {}
|
||||
})
|
||||
{}
|
||||
|
||||
|
||||
inline spdlog::logger::~logger() = default;
|
||||
@@ -45,89 +52,115 @@ inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter)
|
||||
_set_formatter(msg_formatter);
|
||||
}
|
||||
|
||||
inline void spdlog::logger::set_pattern(const std::string& pattern)
|
||||
inline void spdlog::logger::set_pattern(const std::string& pattern, pattern_time_type pattern_time)
|
||||
{
|
||||
_set_pattern(pattern);
|
||||
_set_pattern(pattern, pattern_time);
|
||||
}
|
||||
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args)
|
||||
{
|
||||
if (!should_log(lvl)) return;
|
||||
|
||||
details::log_msg log_msg(&_name, lvl);
|
||||
try
|
||||
{
|
||||
details::log_msg log_msg(&_name, lvl);
|
||||
|
||||
#if defined(SPDLOG_FMT_PRINTF)
|
||||
fmt::printf(log_msg.raw, fmt, args...);
|
||||
#else
|
||||
log_msg.raw.write(fmt, args...);
|
||||
#endif
|
||||
_sink_it(log_msg);
|
||||
}
|
||||
catch (fmt::FormatError &ex)
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
throw spdlog::spdlog_ex(std::string("format error in \"") + fmt + "\": " + ex.what());
|
||||
_err_handler(ex.what());
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
_err_handler("Unknown exception in logger " + _name);
|
||||
throw;
|
||||
}
|
||||
|
||||
_sink_it(log_msg);
|
||||
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::log(level::level_enum lvl, const char* msg)
|
||||
{
|
||||
if (!should_log(lvl)) return;
|
||||
|
||||
details::log_msg log_msg(&_name, lvl);
|
||||
log_msg.raw << msg;
|
||||
_sink_it(log_msg);
|
||||
|
||||
try
|
||||
{
|
||||
details::log_msg log_msg(&_name, lvl);
|
||||
log_msg.raw << msg;
|
||||
_sink_it(log_msg);
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
_err_handler(ex.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
_err_handler("Unknown exception in logger " + _name);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void spdlog::logger::log(level::level_enum lvl, const T& msg)
|
||||
{
|
||||
if (!should_log(lvl)) return;
|
||||
|
||||
details::log_msg log_msg(&_name, lvl);
|
||||
log_msg.raw << msg;
|
||||
_sink_it(log_msg);
|
||||
|
||||
try
|
||||
{
|
||||
details::log_msg log_msg(&_name, lvl);
|
||||
log_msg.raw << msg;
|
||||
_sink_it(log_msg);
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
_err_handler(ex.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
_err_handler("Unknown exception in logger " + _name);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::trace(const char* fmt, const Args&... args)
|
||||
template <typename Arg1, typename... Args>
|
||||
inline void spdlog::logger::trace(const char* fmt, const Arg1 &arg1, const Args&... args)
|
||||
{
|
||||
log(level::trace, fmt, args...);
|
||||
log(level::trace, fmt, arg1, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::debug(const char* fmt, const Args&... args)
|
||||
template <typename Arg1, typename... Args>
|
||||
inline void spdlog::logger::debug(const char* fmt, const Arg1 &arg1, const Args&... args)
|
||||
{
|
||||
log(level::debug, fmt, args...);
|
||||
log(level::debug, fmt, arg1, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::info(const char* fmt, const Args&... args)
|
||||
template <typename Arg1, typename... Args>
|
||||
inline void spdlog::logger::info(const char* fmt, const Arg1 &arg1, const Args&... args)
|
||||
{
|
||||
log(level::info, fmt, args...);
|
||||
log(level::info, fmt, arg1, args...);
|
||||
}
|
||||
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::warn(const char* fmt, const Args&... args)
|
||||
template <typename Arg1, typename... Args>
|
||||
inline void spdlog::logger::warn(const char* fmt, const Arg1 &arg1, const Args&... args)
|
||||
{
|
||||
log(level::warn, fmt, args...);
|
||||
log(level::warn, fmt, arg1, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::error(const char* fmt, const Args&... args)
|
||||
template <typename Arg1, typename... Args>
|
||||
inline void spdlog::logger::error(const char* fmt, const Arg1 &arg1, const Args&... args)
|
||||
{
|
||||
log(level::err, fmt, args...);
|
||||
log(level::err, fmt, arg1, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::critical(const char* fmt, const Args&... args)
|
||||
template <typename Arg1, typename... Args>
|
||||
inline void spdlog::logger::critical(const char* fmt, const Arg1 &arg1, const Args&... args)
|
||||
{
|
||||
log(level::critical, fmt, args...);
|
||||
log(level::critical, fmt, arg1, args...);
|
||||
}
|
||||
|
||||
|
||||
@@ -171,6 +204,67 @@ inline void spdlog::logger::critical(const T& msg)
|
||||
|
||||
|
||||
|
||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
#include <codecvt>
|
||||
#include <locale>
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* msg)
|
||||
{
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t> > conv;
|
||||
|
||||
log(lvl, conv.to_bytes(msg));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
fmt::WMemoryWriter wWriter;
|
||||
|
||||
wWriter.write(fmt, args...);
|
||||
log(lvl, wWriter.c_str());
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::trace(const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
log(level::trace, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::debug(const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
log(level::debug, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::info(const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
log(level::info, fmt, args...);
|
||||
}
|
||||
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::warn(const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
log(level::warn, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::error(const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
log(level::err, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::critical(const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
log(level::critical, fmt, args...);
|
||||
}
|
||||
|
||||
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
|
||||
|
||||
|
||||
//
|
||||
// name and level
|
||||
@@ -185,6 +279,17 @@ inline void spdlog::logger::set_level(spdlog::level::level_enum log_level)
|
||||
_level.store(log_level);
|
||||
}
|
||||
|
||||
inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler)
|
||||
{
|
||||
_err_handler = err_handler;
|
||||
}
|
||||
|
||||
inline spdlog::log_err_handler spdlog::logger::error_handler()
|
||||
{
|
||||
return _err_handler;
|
||||
}
|
||||
|
||||
|
||||
inline void spdlog::logger::flush_on(level::level_enum log_level)
|
||||
{
|
||||
_flush_level.store(log_level);
|
||||
@@ -205,18 +310,25 @@ inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) cons
|
||||
//
|
||||
inline void spdlog::logger::_sink_it(details::log_msg& msg)
|
||||
{
|
||||
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
|
||||
_incr_msg_counter(msg);
|
||||
#endif
|
||||
_formatter->format(msg);
|
||||
for (auto &sink : _sinks)
|
||||
sink->log(msg);
|
||||
{
|
||||
if( sink->should_log( msg.level))
|
||||
{
|
||||
sink->log(msg);
|
||||
}
|
||||
}
|
||||
|
||||
const auto flush_level = _flush_level.load(std::memory_order_relaxed);
|
||||
if (msg.level >= flush_level)
|
||||
if(_should_flush_on(msg))
|
||||
flush();
|
||||
}
|
||||
|
||||
inline void spdlog::logger::_set_pattern(const std::string& pattern)
|
||||
inline void spdlog::logger::_set_pattern(const std::string& pattern, pattern_time_type pattern_time)
|
||||
{
|
||||
_formatter = std::make_shared<pattern_formatter>(pattern);
|
||||
_formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
|
||||
}
|
||||
inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter)
|
||||
{
|
||||
@@ -228,3 +340,34 @@ inline void spdlog::logger::flush()
|
||||
for (auto& sink : _sinks)
|
||||
sink->flush();
|
||||
}
|
||||
|
||||
inline void spdlog::logger::_default_err_handler(const std::string &msg)
|
||||
{
|
||||
auto now = time(nullptr);
|
||||
if (now - _last_err_time < 60)
|
||||
return;
|
||||
auto tm_time = details::os::localtime(now);
|
||||
char date_buf[100];
|
||||
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
|
||||
details::log_msg err_msg;
|
||||
err_msg.formatted.write("[*** LOG ERROR ***] [{}] [{}] [{}]{}", name(), msg, date_buf, details::os::eol);
|
||||
sinks::stderr_sink_mt::instance()->log(err_msg);
|
||||
_last_err_time = now;
|
||||
}
|
||||
|
||||
inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg)
|
||||
{
|
||||
const auto flush_level = _flush_level.load(std::memory_order_relaxed);
|
||||
return (msg.level >= flush_level) && (msg.level != level::off);
|
||||
}
|
||||
|
||||
inline void spdlog::logger::_incr_msg_counter(details::log_msg &msg)
|
||||
{
|
||||
msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
inline const std::vector<spdlog::sink_ptr>& spdlog::logger::sinks() const
|
||||
{
|
||||
return _sinks;
|
||||
}
|
||||
|
||||
|
@@ -43,7 +43,7 @@ Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include "../common.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
@@ -60,11 +60,12 @@ public:
|
||||
|
||||
using item_type = T;
|
||||
mpmc_bounded_queue(size_t buffer_size)
|
||||
: buffer_(new cell_t [buffer_size]),
|
||||
buffer_mask_(buffer_size - 1)
|
||||
:max_size_(buffer_size),
|
||||
buffer_(new cell_t[buffer_size]),
|
||||
buffer_mask_(buffer_size - 1)
|
||||
{
|
||||
//queue size must be power of two
|
||||
if(!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0)))
|
||||
if (!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0)))
|
||||
throw spdlog_ex("async logger queue size must be power of two");
|
||||
|
||||
for (size_t i = 0; i != buffer_size; i += 1)
|
||||
@@ -75,7 +76,7 @@ public:
|
||||
|
||||
~mpmc_bounded_queue()
|
||||
{
|
||||
delete [] buffer_;
|
||||
delete[] buffer_;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,7 +88,7 @@ public:
|
||||
{
|
||||
cell = &buffer_[pos & buffer_mask_];
|
||||
size_t seq = cell->sequence_.load(std::memory_order_acquire);
|
||||
intptr_t dif = (intptr_t)seq - (intptr_t)pos;
|
||||
intptr_t dif = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos);
|
||||
if (dif == 0)
|
||||
{
|
||||
if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
|
||||
@@ -116,7 +117,7 @@ public:
|
||||
cell = &buffer_[pos & buffer_mask_];
|
||||
size_t seq =
|
||||
cell->sequence_.load(std::memory_order_acquire);
|
||||
intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1);
|
||||
intptr_t dif = static_cast<intptr_t>(seq) - static_cast<intptr_t>(pos + 1);
|
||||
if (dif == 0)
|
||||
{
|
||||
if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
|
||||
@@ -132,6 +133,20 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_empty()
|
||||
{
|
||||
size_t front, front1, back;
|
||||
// try to take a consistent snapshot of front/tail.
|
||||
do
|
||||
{
|
||||
front = enqueue_pos_.load(std::memory_order_acquire);
|
||||
back = dequeue_pos_.load(std::memory_order_acquire);
|
||||
front1 = enqueue_pos_.load(std::memory_order_relaxed);
|
||||
}
|
||||
while (front != front1);
|
||||
return back == front;
|
||||
}
|
||||
|
||||
private:
|
||||
struct cell_t
|
||||
{
|
||||
@@ -139,8 +154,10 @@ private:
|
||||
T data_;
|
||||
};
|
||||
|
||||
size_t const max_size_;
|
||||
|
||||
static size_t const cacheline_size = 64;
|
||||
typedef char cacheline_pad_t [cacheline_size];
|
||||
typedef char cacheline_pad_t[cacheline_size];
|
||||
|
||||
cacheline_pad_t pad0_;
|
||||
cell_t* const buffer_;
|
||||
@@ -151,8 +168,8 @@ private:
|
||||
std::atomic<size_t> dequeue_pos_;
|
||||
cacheline_pad_t pad3_;
|
||||
|
||||
mpmc_bounded_queue(mpmc_bounded_queue const&);
|
||||
void operator = (mpmc_bounded_queue const&);
|
||||
mpmc_bounded_queue(mpmc_bounded_queue const&) = delete;
|
||||
void operator= (mpmc_bounded_queue const&) = delete;
|
||||
};
|
||||
|
||||
} // ns details
|
||||
|
@@ -4,16 +4,19 @@
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/common.h>
|
||||
#include "../common.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
@@ -25,25 +28,32 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <process.h> // _get_pid support
|
||||
#include <io.h> // _get_osfhandle and _isatty support
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <share.h>
|
||||
#endif
|
||||
|
||||
#include <sys/types.h>
|
||||
#else // unix
|
||||
|
||||
#elif __linux__
|
||||
|
||||
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
|
||||
#include <unistd.h>
|
||||
#include <chrono>
|
||||
#include <fcntl.h>
|
||||
|
||||
#else
|
||||
|
||||
#include <thread>
|
||||
#ifdef __linux__
|
||||
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
|
||||
|
||||
#elif __FreeBSD__
|
||||
#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
|
||||
#endif
|
||||
|
||||
#endif //unix
|
||||
|
||||
#ifndef __has_feature // Clang - feature checking macros.
|
||||
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
|
||||
#endif
|
||||
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace details
|
||||
@@ -135,6 +145,28 @@ SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1;
|
||||
|
||||
|
||||
|
||||
// folder separator
|
||||
#ifdef _WIN32
|
||||
SPDLOG_CONSTEXPR static const char folder_sep = '\\';
|
||||
#else
|
||||
SPDLOG_CONSTEXPR static const char folder_sep = '/';
|
||||
#endif
|
||||
|
||||
|
||||
inline void prevent_child_fd(FILE *f)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
auto file_handle = (HANDLE)_get_osfhandle(_fileno(f));
|
||||
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
|
||||
throw spdlog_ex("SetHandleInformation failed", errno);
|
||||
#else
|
||||
auto fd = fileno(f);
|
||||
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
|
||||
throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//fopen_s on non windows for writing
|
||||
inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode)
|
||||
{
|
||||
@@ -144,13 +176,18 @@ inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode
|
||||
#else
|
||||
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
|
||||
#endif
|
||||
return *fp == nullptr;
|
||||
#else
|
||||
#else //unix
|
||||
*fp = fopen((filename.c_str()), mode.c_str());
|
||||
return *fp == nullptr;
|
||||
#endif
|
||||
|
||||
#ifdef SPDLOG_PREVENT_CHILD_FD
|
||||
if (*fp != nullptr)
|
||||
prevent_child_fd(*fp);
|
||||
#endif
|
||||
return *fp == nullptr;
|
||||
}
|
||||
|
||||
|
||||
inline int remove(const filename_t &filename)
|
||||
{
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
@@ -182,7 +219,7 @@ inline bool file_exists(const filename_t& filename)
|
||||
return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
|
||||
#else //common linux/unix all have the stat system call
|
||||
struct stat buffer;
|
||||
return (stat (filename.c_str(), &buffer) == 0);
|
||||
return (stat(filename.c_str(), &buffer) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -192,35 +229,35 @@ inline bool file_exists(const filename_t& filename)
|
||||
//Return file size according to open FILE* object
|
||||
inline size_t filesize(FILE *f)
|
||||
{
|
||||
if (f == nullptr)
|
||||
throw spdlog_ex("Failed getting file size. fd is null");
|
||||
#ifdef _WIN32
|
||||
int fd = _fileno(f);
|
||||
if (f == nullptr)
|
||||
throw spdlog_ex("Failed getting file size. fd is null");
|
||||
#if defined ( _WIN32) && !defined(__CYGWIN__)
|
||||
int fd = _fileno(f);
|
||||
#if _WIN64 //64 bits
|
||||
struct _stat64 st;
|
||||
if (_fstat64(fd, &st) == 0)
|
||||
return st.st_size;
|
||||
struct _stat64 st;
|
||||
if (_fstat64(fd, &st) == 0)
|
||||
return st.st_size;
|
||||
|
||||
#else //windows 32 bits
|
||||
struct _stat st;
|
||||
if (_fstat(fd, &st) == 0)
|
||||
return st.st_size;
|
||||
long ret = _filelength(fd);
|
||||
if (ret >= 0)
|
||||
return static_cast<size_t>(ret);
|
||||
#endif
|
||||
|
||||
#else // unix
|
||||
int fd = fileno(f);
|
||||
//64 bits(but not in osx, where fstat64 is deprecated)
|
||||
#if !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__))
|
||||
struct stat64 st;
|
||||
if (fstat64(fd, &st) == 0)
|
||||
return st.st_size;
|
||||
#else // unix 32 bits or osx
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) == 0)
|
||||
return st.st_size;
|
||||
int fd = fileno(f);
|
||||
//64 bits(but not in osx or cygwin, where fstat64 is deprecated)
|
||||
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__)
|
||||
struct stat64 st;
|
||||
if (fstat64(fd, &st) == 0)
|
||||
return static_cast<size_t>(st.st_size);
|
||||
#else // unix 32 bits or cygwin
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) == 0)
|
||||
return static_cast<size_t>(st.st_size);
|
||||
#endif
|
||||
#endif
|
||||
throw spdlog_ex("Failed getting file size from fd", errno);
|
||||
throw spdlog_ex("Failed getting file size from fd", errno);
|
||||
}
|
||||
|
||||
|
||||
@@ -248,13 +285,49 @@ inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
|
||||
offset -= tzinfo.StandardBias;
|
||||
return offset;
|
||||
#else
|
||||
return static_cast<int>(tm.tm_gmtoff / 60);
|
||||
|
||||
#if defined(sun) || defined(__sun)
|
||||
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
|
||||
struct helper
|
||||
{
|
||||
static long int calculate_gmt_offset(const std::tm & localtm = details::os::localtime(), const std::tm & gmtm = details::os::gmtime())
|
||||
{
|
||||
int local_year = localtm.tm_year + (1900 - 1);
|
||||
int gmt_year = gmtm.tm_year + (1900 - 1);
|
||||
|
||||
long int days = (
|
||||
// difference in day of year
|
||||
localtm.tm_yday - gmtm.tm_yday
|
||||
|
||||
// + intervening leap days
|
||||
+ ((local_year >> 2) - (gmt_year >> 2))
|
||||
- (local_year / 100 - gmt_year / 100)
|
||||
+ ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
|
||||
|
||||
// + difference in years * 365 */
|
||||
+ (long int)(local_year - gmt_year) * 365
|
||||
);
|
||||
|
||||
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
|
||||
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
|
||||
long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
|
||||
|
||||
return secs;
|
||||
}
|
||||
};
|
||||
|
||||
long int offset_seconds = helper::calculate_gmt_offset(tm);
|
||||
#else
|
||||
long int offset_seconds = tm.tm_gmtoff;
|
||||
#endif
|
||||
|
||||
return static_cast<int>(offset_seconds / 60);
|
||||
#endif
|
||||
}
|
||||
|
||||
//Return current thread id as size_t
|
||||
//It exists because the std::this_thread::get_id() is much slower(espcially under VS 2013)
|
||||
inline size_t thread_id()
|
||||
//It exists because the std::this_thread::get_id() is much slower(especially under VS 2013)
|
||||
inline size_t _thread_id()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return static_cast<size_t>(::GetCurrentThreadId());
|
||||
@@ -263,9 +336,29 @@ inline size_t thread_id()
|
||||
# define SYS_gettid __NR_gettid
|
||||
# endif
|
||||
return static_cast<size_t>(syscall(SYS_gettid));
|
||||
#else //Default to standard C++11 (OSX and other Unix)
|
||||
#elif __FreeBSD__
|
||||
long tid;
|
||||
thr_self(&tid);
|
||||
return static_cast<size_t>(tid);
|
||||
#elif __APPLE__
|
||||
uint64_t tid;
|
||||
pthread_threadid_np(nullptr, &tid);
|
||||
return static_cast<size_t>(tid);
|
||||
#else //Default to standard C++11 (other Unix)
|
||||
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
|
||||
#endif
|
||||
}
|
||||
|
||||
//Return current thread id as size_t (from thread local storage)
|
||||
inline size_t thread_id()
|
||||
{
|
||||
#if defined(SPDLOG_DISABLE_TID_CACHING) || (defined(_MSC_VER) && (_MSC_VER < 1900)) || (defined(__clang__) && !__has_feature(cxx_thread_local))
|
||||
return _thread_id();
|
||||
#else // cache thread id in tls
|
||||
static thread_local const size_t tid = _thread_id();
|
||||
return tid;
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -286,6 +379,22 @@ inline std::string filename_to_str(const filename_t& filename)
|
||||
}
|
||||
#endif
|
||||
|
||||
inline std::string errno_to_string(char[256], char* res)
|
||||
{
|
||||
return std::string(res);
|
||||
}
|
||||
|
||||
inline std::string errno_to_string(char buf[256], int res)
|
||||
{
|
||||
if (res == 0)
|
||||
{
|
||||
return std::string(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
// Return errno string (thread safe)
|
||||
inline std::string errno_str(int err_num)
|
||||
@@ -294,24 +403,77 @@ inline std::string errno_str(int err_num)
|
||||
SPDLOG_CONSTEXPR auto buf_size = sizeof(buf);
|
||||
|
||||
#ifdef _WIN32
|
||||
if(strerror_s(buf, buf_size, err_num) == 0)
|
||||
if (strerror_s(buf, buf_size, err_num) == 0)
|
||||
return std::string(buf);
|
||||
else
|
||||
return "Unkown error";
|
||||
return "Unknown error";
|
||||
|
||||
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \
|
||||
((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version
|
||||
|
||||
#elif defined(__APPLE__) || ((_POSIX_C_SOURCE >= 200112L) && ! _GNU_SOURCE) // posix version
|
||||
if (strerror_r(err_num, buf, buf_size) == 0)
|
||||
return std::string(buf);
|
||||
else
|
||||
return "Unkown error";
|
||||
return "Unknown error";
|
||||
|
||||
#else // gnu version (might not use the given buf, so its retval pointer must be used)
|
||||
return std::string(strerror_r(err_num, buf, buf_size));
|
||||
auto err = strerror_r(err_num, buf, buf_size); // let compiler choose type
|
||||
return errno_to_string(buf, err); // use overloading to select correct stringify function
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int pid()
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
return ::_getpid();
|
||||
#else
|
||||
return static_cast<int>(::getpid());
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Determine if the terminal supports colors
|
||||
// Source: https://github.com/agauniyal/rang/
|
||||
inline bool is_color_terminal()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return true;
|
||||
#else
|
||||
static constexpr const char* Terms[] =
|
||||
{
|
||||
"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm",
|
||||
"linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"
|
||||
};
|
||||
|
||||
const char *env_p = std::getenv("TERM");
|
||||
if (env_p == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static const bool result = std::any_of(
|
||||
std::begin(Terms), std::end(Terms), [&](const char* term)
|
||||
{
|
||||
return std::strstr(env_p, term) != nullptr;
|
||||
});
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Detrmine if the terminal attached
|
||||
// Source: https://github.com/agauniyal/rang/
|
||||
inline bool in_terminal(FILE* file)
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
return _isatty(_fileno(file)) ? true : false;
|
||||
#else
|
||||
return isatty(fileno(file)) ? true : false;
|
||||
#endif
|
||||
}
|
||||
} //os
|
||||
} //details
|
||||
} //spdlog
|
||||
|
||||
|
||||
|
@@ -5,10 +5,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/formatter.h>
|
||||
#include <spdlog/details/log_msg.h>
|
||||
#include <spdlog/details/os.h>
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
#include "../formatter.h"
|
||||
#include "../details/log_msg.h"
|
||||
#include "../details/os.h"
|
||||
#include "../fmt/fmt.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
@@ -26,7 +27,8 @@ namespace details
|
||||
class flag_formatter
|
||||
{
|
||||
public:
|
||||
virtual ~flag_formatter() {}
|
||||
virtual ~flag_formatter()
|
||||
{}
|
||||
virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0;
|
||||
};
|
||||
|
||||
@@ -35,7 +37,7 @@ public:
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
namespace
|
||||
{
|
||||
class name_formatter :public flag_formatter
|
||||
class name_formatter:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
@@ -45,7 +47,7 @@ class name_formatter :public flag_formatter
|
||||
}
|
||||
|
||||
// log level appender
|
||||
class level_formatter :public flag_formatter
|
||||
class level_formatter:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
@@ -54,7 +56,7 @@ class level_formatter :public flag_formatter
|
||||
};
|
||||
|
||||
// short log level appender
|
||||
class short_level_formatter :public flag_formatter
|
||||
class short_level_formatter:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
@@ -78,7 +80,7 @@ static int to12h(const tm& t)
|
||||
|
||||
//Abbreviated weekday name
|
||||
static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
|
||||
class a_formatter :public flag_formatter
|
||||
class a_formatter:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
@@ -88,7 +90,7 @@ class a_formatter :public flag_formatter
|
||||
|
||||
//Full weekday name
|
||||
static const std::string full_days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
|
||||
class A_formatter :public flag_formatter
|
||||
class A_formatter:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
@@ -97,18 +99,18 @@ class A_formatter :public flag_formatter
|
||||
};
|
||||
|
||||
//Abbreviated month
|
||||
static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" };
|
||||
class b_formatter :public flag_formatter
|
||||
static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec" };
|
||||
class b_formatter:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted<< months[tm_time.tm_mon];
|
||||
msg.formatted << months[tm_time.tm_mon];
|
||||
}
|
||||
};
|
||||
|
||||
//Full month name
|
||||
static const std::string full_months[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
|
||||
class B_formatter :public flag_formatter
|
||||
class B_formatter:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
@@ -117,14 +119,14 @@ class B_formatter :public flag_formatter
|
||||
};
|
||||
|
||||
|
||||
//write 2 ints seperated by sep with padding of 2
|
||||
//write 2 ints separated by sep with padding of 2
|
||||
static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, char sep)
|
||||
{
|
||||
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0');
|
||||
return w;
|
||||
}
|
||||
|
||||
//write 3 ints seperated by sep with padding of 2
|
||||
//write 3 ints separated by sep with padding of 2
|
||||
static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v3, char sep)
|
||||
{
|
||||
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0');
|
||||
@@ -133,7 +135,7 @@ static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v
|
||||
|
||||
|
||||
//Date and time representation (Thu Aug 23 15:35:46 2014)
|
||||
class c_formatter :public flag_formatter
|
||||
class c_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
@@ -144,7 +146,7 @@ class c_formatter :public flag_formatter
|
||||
|
||||
|
||||
// year - 2 digit
|
||||
class C_formatter :public flag_formatter
|
||||
class C_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
@@ -155,7 +157,7 @@ class C_formatter :public flag_formatter
|
||||
|
||||
|
||||
// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
|
||||
class D_formatter :public flag_formatter
|
||||
class D_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
@@ -165,7 +167,7 @@ class D_formatter :public flag_formatter
|
||||
|
||||
|
||||
// year - 4 digit
|
||||
class Y_formatter :public flag_formatter
|
||||
class Y_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
@@ -174,7 +176,7 @@ class Y_formatter :public flag_formatter
|
||||
};
|
||||
|
||||
// month 1-12
|
||||
class m_formatter :public flag_formatter
|
||||
class m_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
@@ -183,7 +185,7 @@ class m_formatter :public flag_formatter
|
||||
};
|
||||
|
||||
// day of month 1-31
|
||||
class d_formatter :public flag_formatter
|
||||
class d_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
@@ -192,7 +194,7 @@ class d_formatter :public flag_formatter
|
||||
};
|
||||
|
||||
// hours in 24 format 0-23
|
||||
class H_formatter :public flag_formatter
|
||||
class H_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
@@ -201,7 +203,7 @@ class H_formatter :public flag_formatter
|
||||
};
|
||||
|
||||
// hours in 12 format 1-12
|
||||
class I_formatter :public flag_formatter
|
||||
class I_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
@@ -210,7 +212,7 @@ class I_formatter :public flag_formatter
|
||||
};
|
||||
|
||||
// minutes 0-59
|
||||
class M_formatter :public flag_formatter
|
||||
class M_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
@@ -219,7 +221,7 @@ class M_formatter :public flag_formatter
|
||||
};
|
||||
|
||||
// seconds 0-59
|
||||
class S_formatter :public flag_formatter
|
||||
class S_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
@@ -228,7 +230,7 @@ class S_formatter :public flag_formatter
|
||||
};
|
||||
|
||||
// milliseconds
|
||||
class e_formatter :public flag_formatter
|
||||
class e_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
@@ -239,7 +241,7 @@ class e_formatter :public flag_formatter
|
||||
};
|
||||
|
||||
// microseconds
|
||||
class f_formatter :public flag_formatter
|
||||
class f_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
@@ -250,7 +252,7 @@ class f_formatter :public flag_formatter
|
||||
};
|
||||
|
||||
// nanoseconds
|
||||
class F_formatter :public flag_formatter
|
||||
class F_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
@@ -260,8 +262,18 @@ class F_formatter :public flag_formatter
|
||||
}
|
||||
};
|
||||
|
||||
class E_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
auto duration = msg.time.time_since_epoch();
|
||||
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
|
||||
msg.formatted << seconds;
|
||||
}
|
||||
};
|
||||
|
||||
// AM/PM
|
||||
class p_formatter :public flag_formatter
|
||||
class p_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
@@ -271,7 +283,7 @@ class p_formatter :public flag_formatter
|
||||
|
||||
|
||||
// 12 hour clock 02:55:02 pm
|
||||
class r_formatter :public flag_formatter
|
||||
class r_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
@@ -280,7 +292,7 @@ class r_formatter :public flag_formatter
|
||||
};
|
||||
|
||||
// 24-hour HH:MM time, equivalent to %H:%M
|
||||
class R_formatter :public flag_formatter
|
||||
class R_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
@@ -289,7 +301,7 @@ class R_formatter :public flag_formatter
|
||||
};
|
||||
|
||||
// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
|
||||
class T_formatter :public flag_formatter
|
||||
class T_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
@@ -297,14 +309,14 @@ class T_formatter :public flag_formatter
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// ISO 8601 offset from UTC in timezone (+-HH:MM)
|
||||
class z_formatter :public flag_formatter
|
||||
class z_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
public:
|
||||
const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
|
||||
|
||||
z_formatter() :_last_update(std::chrono::seconds(0)) {}
|
||||
z_formatter():_last_update(std::chrono::seconds(0)), _offset_minutes(0)
|
||||
{}
|
||||
z_formatter(const z_formatter&) = delete;
|
||||
z_formatter& operator=(const z_formatter&) = delete;
|
||||
|
||||
@@ -317,13 +329,21 @@ public:
|
||||
// it is very fast (already stored in tm.tm_gmtoff)
|
||||
int total_minutes = os::utc_minutes_offset(tm_time);
|
||||
#endif
|
||||
bool is_negative = total_minutes < 0;
|
||||
char sign;
|
||||
if (is_negative)
|
||||
{
|
||||
total_minutes = -total_minutes;
|
||||
sign = '-';
|
||||
}
|
||||
else
|
||||
{
|
||||
sign = '+';
|
||||
}
|
||||
|
||||
int h = total_minutes / 60;
|
||||
int m = total_minutes % 60;
|
||||
if (h >= 0) //minus sign will be printed anyway if negative
|
||||
{
|
||||
msg.formatted << '+';
|
||||
}
|
||||
msg.formatted << sign;
|
||||
pad_n_join(msg.formatted, h, m, ':');
|
||||
}
|
||||
private:
|
||||
@@ -346,8 +366,8 @@ private:
|
||||
|
||||
|
||||
|
||||
//Thread id
|
||||
class t_formatter :public flag_formatter
|
||||
// Thread id
|
||||
class t_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
@@ -355,8 +375,25 @@ class t_formatter :public flag_formatter
|
||||
}
|
||||
};
|
||||
|
||||
// Current pid
|
||||
class pid_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
msg.formatted << details::os::pid();
|
||||
}
|
||||
};
|
||||
|
||||
class v_formatter :public flag_formatter
|
||||
// message counter formatter
|
||||
class i_formatter SPDLOG_FINAL :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
msg.formatted << fmt::pad(msg.msg_id, 6, '0');
|
||||
}
|
||||
};
|
||||
|
||||
class v_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
@@ -364,10 +401,10 @@ class v_formatter :public flag_formatter
|
||||
}
|
||||
};
|
||||
|
||||
class ch_formatter :public flag_formatter
|
||||
class ch_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
public:
|
||||
explicit ch_formatter(char ch) : _ch(ch)
|
||||
explicit ch_formatter(char ch): _ch(ch)
|
||||
{}
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
@@ -379,7 +416,7 @@ private:
|
||||
|
||||
|
||||
//aggregate user chars to display as is
|
||||
class aggregate_formatter :public flag_formatter
|
||||
class aggregate_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
public:
|
||||
aggregate_formatter()
|
||||
@@ -398,7 +435,7 @@ private:
|
||||
|
||||
// Full info formatter
|
||||
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
|
||||
class full_formatter :public flag_formatter
|
||||
class full_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
@@ -429,7 +466,7 @@ class full_formatter :public flag_formatter
|
||||
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_sec), 2, '0') << '.'
|
||||
<< fmt::pad(static_cast<unsigned int>(millis), 3, '0') << "] ";
|
||||
|
||||
//no datetime needed
|
||||
//no datetime needed
|
||||
#else
|
||||
(void)tm_time;
|
||||
#endif
|
||||
@@ -443,12 +480,15 @@ class full_formatter :public flag_formatter
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// pattern_formatter inline impl
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern)
|
||||
inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern, pattern_time_type pattern_time)
|
||||
: _pattern_time(pattern_time)
|
||||
{
|
||||
compile_pattern(pattern);
|
||||
}
|
||||
@@ -499,101 +539,105 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::short_level_formatter()));
|
||||
break;
|
||||
|
||||
case('t') :
|
||||
case('t'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::t_formatter()));
|
||||
break;
|
||||
|
||||
case('v') :
|
||||
case('v'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::v_formatter()));
|
||||
break;
|
||||
|
||||
case('a') :
|
||||
case('a'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::a_formatter()));
|
||||
break;
|
||||
|
||||
case('A') :
|
||||
case('A'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::A_formatter()));
|
||||
break;
|
||||
|
||||
case('b') :
|
||||
case('h') :
|
||||
case('b'):
|
||||
case('h'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::b_formatter()));
|
||||
break;
|
||||
|
||||
case('B') :
|
||||
case('B'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::B_formatter()));
|
||||
break;
|
||||
case('c') :
|
||||
case('c'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::c_formatter()));
|
||||
break;
|
||||
|
||||
case('C') :
|
||||
case('C'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::C_formatter()));
|
||||
break;
|
||||
|
||||
case('Y') :
|
||||
case('Y'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::Y_formatter()));
|
||||
break;
|
||||
|
||||
case('D') :
|
||||
case('x') :
|
||||
case('D'):
|
||||
case('x'):
|
||||
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::D_formatter()));
|
||||
break;
|
||||
|
||||
case('m') :
|
||||
case('m'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::m_formatter()));
|
||||
break;
|
||||
|
||||
case('d') :
|
||||
case('d'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::d_formatter()));
|
||||
break;
|
||||
|
||||
case('H') :
|
||||
case('H'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::H_formatter()));
|
||||
break;
|
||||
|
||||
case('I') :
|
||||
case('I'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::I_formatter()));
|
||||
break;
|
||||
|
||||
case('M') :
|
||||
case('M'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::M_formatter()));
|
||||
break;
|
||||
|
||||
case('S') :
|
||||
case('S'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::S_formatter()));
|
||||
break;
|
||||
|
||||
case('e') :
|
||||
case('e'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::e_formatter()));
|
||||
break;
|
||||
|
||||
case('f') :
|
||||
case('f'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::f_formatter()));
|
||||
break;
|
||||
case('F') :
|
||||
case('F'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::F_formatter()));
|
||||
break;
|
||||
|
||||
case('p') :
|
||||
case('E'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::E_formatter()));
|
||||
break;
|
||||
|
||||
case('p'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::p_formatter()));
|
||||
break;
|
||||
|
||||
case('r') :
|
||||
case('r'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::r_formatter()));
|
||||
break;
|
||||
|
||||
case('R') :
|
||||
case('R'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::R_formatter()));
|
||||
break;
|
||||
|
||||
case('T') :
|
||||
case('X') :
|
||||
case('T'):
|
||||
case('X'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::T_formatter()));
|
||||
break;
|
||||
|
||||
case('z') :
|
||||
case('z'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::z_formatter()));
|
||||
break;
|
||||
|
||||
@@ -601,32 +645,42 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::full_formatter()));
|
||||
break;
|
||||
|
||||
default: //Unkown flag appears as is
|
||||
case ('P'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::pid_formatter()));
|
||||
break;
|
||||
|
||||
|
||||
case ('i'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::i_formatter()));
|
||||
break;
|
||||
|
||||
default: //Unknown flag appears as is
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter('%')));
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter(flag)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::tm spdlog::pattern_formatter::get_time(details::log_msg& msg)
|
||||
{
|
||||
if (_pattern_time == pattern_time_type::local)
|
||||
return details::os::localtime(log_clock::to_time_t(msg.time));
|
||||
else
|
||||
return details::os::gmtime(log_clock::to_time_t(msg.time));
|
||||
}
|
||||
|
||||
inline void spdlog::pattern_formatter::format(details::log_msg& msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
#ifndef SPDLOG_NO_DATETIME
|
||||
auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time));
|
||||
auto tm_time = get_time(msg);
|
||||
#else
|
||||
std::tm tm_time;
|
||||
std::tm tm_time;
|
||||
#endif
|
||||
for (auto &f : _formatters)
|
||||
{
|
||||
f->format(msg, tm_time);
|
||||
}
|
||||
//write eol
|
||||
msg.formatted.write(details::os::eol, details::os::eol_size);
|
||||
}
|
||||
catch(const fmt::FormatError& e)
|
||||
for (auto &f : _formatters)
|
||||
{
|
||||
throw spdlog_ex(fmt::format("formatting error while processing format string: {}", e.what()));
|
||||
f->format(msg, tm_time);
|
||||
}
|
||||
//write eol
|
||||
msg.formatted.write(details::os::eol, details::os::eol_size);
|
||||
}
|
||||
|
@@ -10,10 +10,10 @@
|
||||
// If user requests a non existing logger, nullptr will be returned
|
||||
// This class is thread safe
|
||||
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
#include <spdlog/logger.h>
|
||||
#include <spdlog/async_logger.h>
|
||||
#include <spdlog/common.h>
|
||||
#include "../details/null_mutex.h"
|
||||
#include "../logger.h"
|
||||
#include "../async_logger.h"
|
||||
#include "../common.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
@@ -60,12 +60,44 @@ public:
|
||||
if (_formatter)
|
||||
new_logger->set_formatter(_formatter);
|
||||
|
||||
if (_err_handler)
|
||||
new_logger->set_error_handler(_err_handler);
|
||||
|
||||
new_logger->set_level(_level);
|
||||
|
||||
|
||||
//Add to registry
|
||||
_loggers[logger_name] = new_logger;
|
||||
return new_logger;
|
||||
}
|
||||
|
||||
template<class It>
|
||||
std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, const It& sinks_begin, const It& sinks_end)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
throw_if_exists(logger_name);
|
||||
auto new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb);
|
||||
|
||||
if (_formatter)
|
||||
new_logger->set_formatter(_formatter);
|
||||
|
||||
if (_err_handler)
|
||||
new_logger->set_error_handler(_err_handler);
|
||||
|
||||
new_logger->set_level(_level);
|
||||
|
||||
//Add to registry
|
||||
_loggers[logger_name] = new_logger;
|
||||
return new_logger;
|
||||
}
|
||||
|
||||
void apply_all(std::function<void(std::shared_ptr<logger>)> fun)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
for (auto &l : _loggers)
|
||||
fun(l.second);
|
||||
}
|
||||
|
||||
void drop(const std::string& logger_name)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
@@ -87,6 +119,15 @@ public:
|
||||
return create(logger_name, { sink });
|
||||
}
|
||||
|
||||
std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, sinks_init_list sinks)
|
||||
{
|
||||
return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks.begin(), sinks.end());
|
||||
}
|
||||
|
||||
std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, sink_ptr sink)
|
||||
{
|
||||
return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, { sink });
|
||||
}
|
||||
|
||||
void formatter(formatter_ptr f)
|
||||
{
|
||||
@@ -112,6 +153,13 @@ public:
|
||||
_level = log_level;
|
||||
}
|
||||
|
||||
void set_error_handler(log_err_handler handler)
|
||||
{
|
||||
for (auto& l : _loggers)
|
||||
l.second->set_error_handler(handler);
|
||||
_err_handler = handler;
|
||||
}
|
||||
|
||||
void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
@@ -149,6 +197,7 @@ private:
|
||||
std::unordered_map <std::string, std::shared_ptr<logger>> _loggers;
|
||||
formatter_ptr _formatter;
|
||||
level::level_enum _level = level::info;
|
||||
log_err_handler _err_handler;
|
||||
bool _async_mode = false;
|
||||
size_t _async_q_size = 0;
|
||||
async_overflow_policy _overflow_policy = async_overflow_policy::block_retry;
|
||||
|
@@ -8,12 +8,24 @@
|
||||
//
|
||||
// Global registry functions
|
||||
//
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/details/registry.h>
|
||||
#include <spdlog/sinks/file_sinks.h>
|
||||
#include <spdlog/sinks/stdout_sinks.h>
|
||||
#include <spdlog/sinks/syslog_sink.h>
|
||||
#include <spdlog/sinks/ansicolor_sink.h>
|
||||
#include "../spdlog.h"
|
||||
#include "../details/registry.h"
|
||||
#include "../sinks/file_sinks.h"
|
||||
#include "../sinks/stdout_sinks.h"
|
||||
#ifdef SPDLOG_ENABLE_SYSLOG
|
||||
#include "../sinks/syslog_sink.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "../sinks/wincolor_sink.h"
|
||||
#else
|
||||
#include "../sinks/ansicolor_sink.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "../sinks/android_sink.h"
|
||||
#endif
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
@@ -36,71 +48,130 @@ inline void spdlog::drop(const std::string &name)
|
||||
}
|
||||
|
||||
// Create multi/single threaded simple file logger
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool force_flush)
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate)
|
||||
{
|
||||
return create<spdlog::sinks::simple_file_sink_mt>(logger_name, filename, force_flush);
|
||||
return create<spdlog::sinks::simple_file_sink_mt>(logger_name, filename, truncate);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_st(const std::string& logger_name, const filename_t& filename, bool force_flush)
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate)
|
||||
{
|
||||
return create<spdlog::sinks::simple_file_sink_st>(logger_name, filename, force_flush);
|
||||
return create<spdlog::sinks::simple_file_sink_st>(logger_name, filename, truncate);
|
||||
}
|
||||
|
||||
// Create multi/single threaded rotating file logger
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files, bool force_flush)
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
|
||||
{
|
||||
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files, force_flush);
|
||||
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files, bool force_flush)
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
|
||||
{
|
||||
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, SPDLOG_FILENAME_T("txt"), max_file_size, max_files, force_flush);
|
||||
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
|
||||
}
|
||||
|
||||
// Create file logger which creates new file at midnight):
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute, bool force_flush)
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute)
|
||||
{
|
||||
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute, force_flush);
|
||||
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, hour, minute);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour, int minute, bool force_flush)
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour, int minute)
|
||||
{
|
||||
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, SPDLOG_FILENAME_T("txt"), hour, minute, force_flush);
|
||||
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, hour, minute);
|
||||
}
|
||||
|
||||
// Create stdout/stderr loggers (with optinal color support)
|
||||
inline std::shared_ptr<spdlog::logger> create_console_logger(const std::string& logger_name, spdlog::sink_ptr sink, bool color)
|
||||
|
||||
//
|
||||
// stdout/stderr loggers
|
||||
//
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name)
|
||||
{
|
||||
if (color) //use color wrapper sink
|
||||
sink = std::make_shared<spdlog::sinks::ansicolor_sink>(sink);
|
||||
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance());
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name)
|
||||
{
|
||||
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance());
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name)
|
||||
{
|
||||
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance());
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name)
|
||||
{
|
||||
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance());
|
||||
}
|
||||
|
||||
//
|
||||
// stdout/stderr color loggers
|
||||
//
|
||||
#ifdef _WIN32
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name, bool color)
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
|
||||
{
|
||||
return create_console_logger(logger_name, sinks::stdout_sink_mt::instance(), color);
|
||||
auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_st>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name, bool color)
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
|
||||
{
|
||||
return create_console_logger(logger_name, sinks::stdout_sink_st::instance(), color);
|
||||
auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_mt>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name, bool color)
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
|
||||
{
|
||||
return create_console_logger(logger_name, sinks::stderr_sink_mt::instance(), color);
|
||||
auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_st>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name, bool color)
|
||||
#else //ansi terminal colors
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
|
||||
{
|
||||
return create_console_logger(logger_name, sinks::stderr_sink_st::instance(), color);
|
||||
auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_st>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_mt>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_st>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SPDLOG_ENABLE_SYSLOG
|
||||
// Create syslog logger
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option)
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option, int syslog_facility)
|
||||
{
|
||||
return create<spdlog::sinks::syslog_sink>(logger_name, syslog_ident, syslog_option);
|
||||
return create<spdlog::sinks::syslog_sink>(logger_name, syslog_ident, syslog_option, syslog_facility);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::android_logger(const std::string& logger_name, const std::string& tag)
|
||||
{
|
||||
return create<spdlog::sinks::android_sink>(logger_name, tag);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -132,6 +203,24 @@ inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_
|
||||
return details::registry::instance().create(logger_name, sinks_begin, sinks_end);
|
||||
}
|
||||
|
||||
// Create and register an async logger with a single sink
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, const sink_ptr& sink, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
|
||||
{
|
||||
return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sink);
|
||||
}
|
||||
|
||||
// Create and register an async logger with multiple sinks
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb )
|
||||
{
|
||||
return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks);
|
||||
}
|
||||
|
||||
template<class It>
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, const It& sinks_begin, const It& sinks_end, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
|
||||
{
|
||||
return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks_begin, sinks_end);
|
||||
}
|
||||
|
||||
inline void spdlog::set_formatter(spdlog::formatter_ptr f)
|
||||
{
|
||||
details::registry::instance().formatter(f);
|
||||
@@ -147,6 +236,11 @@ inline void spdlog::set_level(level::level_enum log_level)
|
||||
return details::registry::instance().set_level(log_level);
|
||||
}
|
||||
|
||||
inline void spdlog::set_error_handler(log_err_handler handler)
|
||||
{
|
||||
return details::registry::instance().set_error_handler(handler);
|
||||
}
|
||||
|
||||
|
||||
inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
|
||||
{
|
||||
@@ -158,6 +252,11 @@ inline void spdlog::set_sync_mode()
|
||||
details::registry::instance().set_sync_mode();
|
||||
}
|
||||
|
||||
inline void spdlog::apply_all(std::function<void(std::shared_ptr<logger>)> fun)
|
||||
{
|
||||
details::registry::instance().apply_all(fun);
|
||||
}
|
||||
|
||||
inline void spdlog::drop_all()
|
||||
{
|
||||
details::registry::instance().drop_all();
|
||||
|
23
include/spdlog/fmt/bundled/LICENSE.rst
Normal file
23
include/spdlog/fmt/bundled/LICENSE.rst
Normal file
@@ -0,0 +1,23 @@
|
||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@@ -25,9 +25,7 @@
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
// Commented out by spdlog to use header only
|
||||
// #include "fmt/format.h"
|
||||
// #include "fmt/printf.h"
|
||||
#include "format.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
@@ -43,6 +41,9 @@
|
||||
#endif
|
||||
|
||||
#if FMT_USE_WINDOWS_H
|
||||
# if !defined(FMT_HEADER_ONLY) && !defined(WIN32_LEAN_AND_MEAN)
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
|
||||
# include <windows.h>
|
||||
# else
|
||||
@@ -52,8 +53,6 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
using fmt::internal::Arg;
|
||||
|
||||
#if FMT_EXCEPTIONS
|
||||
# define FMT_TRY try
|
||||
# define FMT_CATCH(x) catch (x)
|
||||
@@ -82,9 +81,9 @@ static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
|
||||
|
||||
namespace fmt {
|
||||
|
||||
FMT_FUNC internal::RuntimeError::~RuntimeError() throw() {}
|
||||
FMT_FUNC FormatError::~FormatError() throw() {}
|
||||
FMT_FUNC SystemError::~SystemError() throw() {}
|
||||
FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {}
|
||||
FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {}
|
||||
FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {}
|
||||
|
||||
namespace {
|
||||
|
||||
@@ -172,7 +171,8 @@ int safe_strerror(
|
||||
: error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
|
||||
|
||||
int run() {
|
||||
strerror_r(0, 0, ""); // Suppress a warning about unused strerror_r.
|
||||
// Suppress a warning about unused strerror_r.
|
||||
strerror_r(0, FMT_NULL, "");
|
||||
return handle(strerror_r(error_code_, buffer_, buffer_size_));
|
||||
}
|
||||
};
|
||||
@@ -213,16 +213,6 @@ void report_error(FormatFunc func, int error_code,
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace internal {
|
||||
|
||||
// This method is used to preserve binary compatibility with fmt 3.0.
|
||||
// It can be removed in 4.0.
|
||||
FMT_FUNC void format_system_error(
|
||||
Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
|
||||
fmt::format_system_error(out, error_code, message);
|
||||
}
|
||||
} // namespace internal
|
||||
|
||||
FMT_FUNC void SystemError::init(
|
||||
int err_code, CStringRef format_str, ArgList args) {
|
||||
error_code_ = err_code;
|
||||
@@ -313,7 +303,7 @@ FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) {
|
||||
FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
|
||||
int s_size = static_cast<int>(s.size());
|
||||
int length = MultiByteToWideChar(
|
||||
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, 0, 0);
|
||||
CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
|
||||
if (length == 0)
|
||||
FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
|
||||
buffer_.resize(length + 1);
|
||||
@@ -335,12 +325,13 @@ FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) {
|
||||
if (s.size() > INT_MAX)
|
||||
return ERROR_INVALID_PARAMETER;
|
||||
int s_size = static_cast<int>(s.size());
|
||||
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, 0, 0, 0, 0);
|
||||
int length = WideCharToMultiByte(
|
||||
CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
|
||||
if (length == 0)
|
||||
return GetLastError();
|
||||
buffer_.resize(length + 1);
|
||||
length = WideCharToMultiByte(
|
||||
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, 0, 0);
|
||||
CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL);
|
||||
if (length == 0)
|
||||
return GetLastError();
|
||||
buffer_[length] = 0;
|
||||
@@ -363,9 +354,10 @@ FMT_FUNC void internal::format_windows_error(
|
||||
buffer.resize(INLINE_BUFFER_SIZE);
|
||||
for (;;) {
|
||||
wchar_t *system_message = &buffer[0];
|
||||
int result = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
0, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
system_message, static_cast<uint32_t>(buffer.size()), 0);
|
||||
int result = FormatMessageW(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
system_message, static_cast<uint32_t>(buffer.size()), FMT_NULL);
|
||||
if (result != 0) {
|
||||
UTF16ToUTF8 utf8_message;
|
||||
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
|
||||
@@ -409,7 +401,7 @@ void internal::ArgMap<Char>::init(const ArgList &args) {
|
||||
if (!map_.empty())
|
||||
return;
|
||||
typedef internal::NamedArg<Char> NamedArg;
|
||||
const NamedArg *named_arg = 0;
|
||||
const NamedArg *named_arg = FMT_NULL;
|
||||
bool use_values =
|
||||
args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE;
|
||||
if (use_values) {
|
||||
@@ -454,14 +446,14 @@ void internal::FixedBuffer<Char>::grow(std::size_t) {
|
||||
FMT_THROW(std::runtime_error("buffer overflow"));
|
||||
}
|
||||
|
||||
FMT_FUNC Arg internal::FormatterBase::do_get_arg(
|
||||
FMT_FUNC internal::Arg internal::FormatterBase::do_get_arg(
|
||||
unsigned arg_index, const char *&error) {
|
||||
Arg arg = args_[arg_index];
|
||||
internal::Arg arg = args_[arg_index];
|
||||
switch (arg.type) {
|
||||
case Arg::NONE:
|
||||
case internal::Arg::NONE:
|
||||
error = "argument index out of range";
|
||||
break;
|
||||
case Arg::NAMED_ARG:
|
||||
case internal::Arg::NAMED_ARG:
|
||||
arg = *static_cast<const internal::Arg*>(arg.pointer);
|
||||
break;
|
||||
default:
|
||||
@@ -502,16 +494,6 @@ FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) {
|
||||
std::fputs(RESET_COLOR, stdout);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args);
|
||||
|
||||
FMT_FUNC int fprintf(std::FILE *f, CStringRef format, ArgList args) {
|
||||
MemoryWriter w;
|
||||
printf(w, format, args);
|
||||
std::size_t size = w.size();
|
||||
return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
|
||||
}
|
||||
|
||||
#ifndef FMT_HEADER_ONLY
|
||||
|
||||
template struct internal::BasicData<void>;
|
||||
@@ -522,13 +504,11 @@ template void internal::FixedBuffer<char>::grow(std::size_t);
|
||||
|
||||
template void internal::ArgMap<char>::init(const ArgList &args);
|
||||
|
||||
template void PrintfFormatter<char>::format(CStringRef format);
|
||||
|
||||
template int internal::CharTraits<char>::format_float(
|
||||
template FMT_API int internal::CharTraits<char>::format_float(
|
||||
char *buffer, std::size_t size, const char *format,
|
||||
unsigned width, int precision, double value);
|
||||
|
||||
template int internal::CharTraits<char>::format_float(
|
||||
template FMT_API int internal::CharTraits<char>::format_float(
|
||||
char *buffer, std::size_t size, const char *format,
|
||||
unsigned width, int precision, long double value);
|
||||
|
||||
@@ -538,13 +518,11 @@ template void internal::FixedBuffer<wchar_t>::grow(std::size_t);
|
||||
|
||||
template void internal::ArgMap<wchar_t>::init(const ArgList &args);
|
||||
|
||||
template void PrintfFormatter<wchar_t>::format(WCStringRef format);
|
||||
|
||||
template int internal::CharTraits<wchar_t>::format_float(
|
||||
template FMT_API int internal::CharTraits<wchar_t>::format_float(
|
||||
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
||||
unsigned width, int precision, double value);
|
||||
|
||||
template int internal::CharTraits<wchar_t>::format_float(
|
||||
template FMT_API int internal::CharTraits<wchar_t>::format_float(
|
||||
wchar_t *buffer, std::size_t size, const wchar_t *format,
|
||||
unsigned width, int precision, long double value);
|
||||
|
||||
|
@@ -38,7 +38,10 @@
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <utility> // for std::pair
|
||||
|
||||
// The fmt library version in the form major * 10000 + minor * 100 + patch.
|
||||
#define FMT_VERSION 40000
|
||||
|
||||
#ifdef _SECURE_SCL
|
||||
# define FMT_SECURE_SCL _SECURE_SCL
|
||||
@@ -104,6 +107,7 @@ typedef __int64 intmax_t;
|
||||
#endif
|
||||
|
||||
#if defined(__clang__) && !defined(FMT_ICC_VERSION)
|
||||
# define FMT_CLANG_VERSION (__clang_major__ * 100 + __clang_minor__)
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdocumentation-unknown-command"
|
||||
# pragma clang diagnostic ignored "-Wpadded"
|
||||
@@ -152,10 +156,6 @@ typedef __int64 intmax_t;
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#if FMT_USE_RVALUE_REFERENCES
|
||||
# include <utility> // for std::move
|
||||
#endif
|
||||
|
||||
// Check if exceptions are disabled.
|
||||
#if defined(__GNUC__) && !defined(__EXCEPTIONS)
|
||||
# define FMT_EXCEPTIONS 0
|
||||
@@ -180,20 +180,50 @@ typedef __int64 intmax_t;
|
||||
# define FMT_USE_NOEXCEPT 0
|
||||
#endif
|
||||
|
||||
#ifndef FMT_NOEXCEPT
|
||||
# if FMT_EXCEPTIONS
|
||||
# if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
|
||||
#if FMT_USE_NOEXCEPT || FMT_HAS_FEATURE(cxx_noexcept) || \
|
||||
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
|
||||
FMT_MSC_VER >= 1900
|
||||
# define FMT_NOEXCEPT noexcept
|
||||
# else
|
||||
# define FMT_NOEXCEPT throw()
|
||||
# endif
|
||||
# define FMT_DETECTED_NOEXCEPT noexcept
|
||||
#else
|
||||
# define FMT_DETECTED_NOEXCEPT throw()
|
||||
#endif
|
||||
|
||||
#ifndef FMT_NOEXCEPT
|
||||
# if FMT_EXCEPTIONS
|
||||
# define FMT_NOEXCEPT FMT_DETECTED_NOEXCEPT
|
||||
# else
|
||||
# define FMT_NOEXCEPT
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// This is needed because GCC still uses throw() in its headers when exceptions
|
||||
// are disabled.
|
||||
#if FMT_GCC_VERSION
|
||||
# define FMT_DTOR_NOEXCEPT FMT_DETECTED_NOEXCEPT
|
||||
#else
|
||||
# define FMT_DTOR_NOEXCEPT FMT_NOEXCEPT
|
||||
#endif
|
||||
|
||||
#ifndef FMT_OVERRIDE
|
||||
# if (defined(FMT_USE_OVERRIDE) && FMT_USE_OVERRIDE) || FMT_HAS_FEATURE(cxx_override) || \
|
||||
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
|
||||
FMT_MSC_VER >= 1900
|
||||
# define FMT_OVERRIDE override
|
||||
# else
|
||||
# define FMT_OVERRIDE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef FMT_NULL
|
||||
# if FMT_HAS_FEATURE(cxx_nullptr) || \
|
||||
(FMT_GCC_VERSION >= 408 && FMT_HAS_GXX_CXX11) || \
|
||||
FMT_MSC_VER >= 1600
|
||||
# define FMT_NULL nullptr
|
||||
# else
|
||||
# define FMT_NULL NULL
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// A macro to disallow the copy constructor and operator= functions
|
||||
// This should be used in the private: declarations for a class
|
||||
#ifndef FMT_USE_DELETED_FUNCTIONS
|
||||
@@ -213,6 +243,20 @@ typedef __int64 intmax_t;
|
||||
TypeName& operator=(const TypeName&)
|
||||
#endif
|
||||
|
||||
#ifndef FMT_USE_DEFAULTED_FUNCTIONS
|
||||
# define FMT_USE_DEFAULTED_FUNCTIONS 0
|
||||
#endif
|
||||
|
||||
#ifndef FMT_DEFAULTED_COPY_CTOR
|
||||
# if FMT_USE_DEFAULTED_FUNCTIONS || FMT_HAS_FEATURE(cxx_defaulted_functions) || \
|
||||
(FMT_GCC_VERSION >= 404 && FMT_HAS_GXX_CXX11) || FMT_MSC_VER >= 1800
|
||||
# define FMT_DEFAULTED_COPY_CTOR(TypeName) \
|
||||
TypeName(const TypeName&) = default;
|
||||
# else
|
||||
# define FMT_DEFAULTED_COPY_CTOR(TypeName)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef FMT_USE_USER_DEFINED_LITERALS
|
||||
// All compilers which support UDLs also support variadic templates. This
|
||||
// makes the fmt::literals implementation easier. However, an explicit check
|
||||
@@ -225,23 +269,38 @@ typedef __int64 intmax_t;
|
||||
(!defined(FMT_ICC_VERSION) || FMT_ICC_VERSION >= 1500)
|
||||
#endif
|
||||
|
||||
#ifndef FMT_USE_EXTERN_TEMPLATES
|
||||
# define FMT_USE_EXTERN_TEMPLATES \
|
||||
(FMT_CLANG_VERSION >= 209 || (FMT_GCC_VERSION >= 303 && FMT_HAS_GXX_CXX11))
|
||||
#endif
|
||||
|
||||
#ifdef FMT_HEADER_ONLY
|
||||
// If header only do not use extern templates.
|
||||
# undef FMT_USE_EXTERN_TEMPLATES
|
||||
# define FMT_USE_EXTERN_TEMPLATES 0
|
||||
#endif
|
||||
|
||||
#ifndef FMT_ASSERT
|
||||
# define FMT_ASSERT(condition, message) assert((condition) && message)
|
||||
#endif
|
||||
|
||||
#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz)
|
||||
# define FMT_BUILTIN_CLZ(n) __builtin_clz(n)
|
||||
#endif
|
||||
// __builtin_clz is broken in clang with Microsoft CodeGen:
|
||||
// https://github.com/fmtlib/fmt/issues/519
|
||||
#ifndef _MSC_VER
|
||||
# if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clz)
|
||||
# define FMT_BUILTIN_CLZ(n) __builtin_clz(n)
|
||||
# endif
|
||||
|
||||
#if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll)
|
||||
# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n)
|
||||
# if FMT_GCC_VERSION >= 400 || FMT_HAS_BUILTIN(__builtin_clzll)
|
||||
# define FMT_BUILTIN_CLZLL(n) __builtin_clzll(n)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Some compilers masquerade as both MSVC and GCC-likes or
|
||||
// otherwise support __builtin_clz and __builtin_clzll, so
|
||||
// only define FMT_BUILTIN_CLZ using the MSVC intrinsics
|
||||
// if the clz and clzll builtins are not available.
|
||||
#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL)
|
||||
#if FMT_MSC_VER && !defined(FMT_BUILTIN_CLZLL) && !defined(_MANAGED)
|
||||
# include <intrin.h> // _BitScanReverse, _BitScanReverse64
|
||||
|
||||
namespace fmt
|
||||
@@ -387,8 +446,11 @@ public:
|
||||
static bool isnegative(double x)
|
||||
{
|
||||
using namespace fmt::internal;
|
||||
if (const_check(sizeof(signbit(x)) == sizeof(int)))
|
||||
if (const_check(sizeof(signbit(x)) == sizeof(bool) ||
|
||||
sizeof(signbit(x)) == sizeof(int)))
|
||||
{
|
||||
return signbit(x) != 0;
|
||||
}
|
||||
if (x < 0) return true;
|
||||
if (!isnotanumber(x)) return false;
|
||||
int dec = 0, sign = 0;
|
||||
@@ -420,7 +482,9 @@ typedef BasicWriter<wchar_t> WWriter;
|
||||
template <typename Char>
|
||||
class ArgFormatter;
|
||||
|
||||
template <typename Impl, typename Char>
|
||||
struct FormatSpec;
|
||||
|
||||
template <typename Impl, typename Char, typename Spec = fmt::FormatSpec>
|
||||
class BasicPrintfArgFormatter;
|
||||
|
||||
template <typename CharType,
|
||||
@@ -429,7 +493,8 @@ class BasicFormatter;
|
||||
|
||||
/**
|
||||
\rst
|
||||
A string reference. It can be constructed from a C string or ``std::string``.
|
||||
A string reference. It can be constructed from a C string or
|
||||
``std::basic_string``.
|
||||
|
||||
You can use one of the following typedefs for common character types:
|
||||
|
||||
@@ -473,10 +538,12 @@ public:
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a string reference from an ``std::string`` object.
|
||||
Constructs a string reference from a ``std::basic_string`` object.
|
||||
\endrst
|
||||
*/
|
||||
BasicStringRef(const std::basic_string<Char> &s)
|
||||
template <typename Allocator>
|
||||
BasicStringRef(
|
||||
const std::basic_string<Char, std::char_traits<Char>, Allocator> &s)
|
||||
: data_(s.c_str()), size_(s.size()) {}
|
||||
|
||||
/**
|
||||
@@ -543,7 +610,7 @@ typedef BasicStringRef<wchar_t> WStringRef;
|
||||
/**
|
||||
\rst
|
||||
A reference to a null terminated string. It can be constructed from a C
|
||||
string or ``std::string``.
|
||||
string or ``std::basic_string``.
|
||||
|
||||
You can use one of the following typedefs for common character types:
|
||||
|
||||
@@ -577,10 +644,13 @@ public:
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a string reference from an ``std::string`` object.
|
||||
Constructs a string reference from a ``std::basic_string`` object.
|
||||
\endrst
|
||||
*/
|
||||
BasicCStringRef(const std::basic_string<Char> &s) : data_(s.c_str()) {}
|
||||
template <typename Allocator>
|
||||
BasicCStringRef(
|
||||
const std::basic_string<Char, std::char_traits<Char>, Allocator> &s)
|
||||
: data_(s.c_str()) {}
|
||||
|
||||
/** Returns the pointer to a C string. */
|
||||
const Char *c_str() const
|
||||
@@ -598,7 +668,8 @@ class FormatError : public std::runtime_error
|
||||
public:
|
||||
explicit FormatError(CStringRef message)
|
||||
: std::runtime_error(message.c_str()) {}
|
||||
~FormatError() throw();
|
||||
FormatError(const FormatError &ferr) : std::runtime_error(ferr) {}
|
||||
FMT_API ~FormatError() FMT_DTOR_NOEXCEPT;
|
||||
};
|
||||
|
||||
namespace internal
|
||||
@@ -666,7 +737,7 @@ protected:
|
||||
std::size_t size_;
|
||||
std::size_t capacity_;
|
||||
|
||||
Buffer(T *ptr = 0, std::size_t capacity = 0)
|
||||
Buffer(T *ptr = FMT_NULL, std::size_t capacity = 0)
|
||||
: ptr_(ptr), size_(0), capacity_(capacity) {}
|
||||
|
||||
/**
|
||||
@@ -740,7 +811,8 @@ template <typename T>
|
||||
template <typename U>
|
||||
void Buffer<T>::append(const U *begin, const U *end)
|
||||
{
|
||||
std::size_t new_size = size_ + internal::to_unsigned(end - begin);
|
||||
FMT_ASSERT(end >= begin, "negative value");
|
||||
std::size_t new_size = size_ + (end - begin);
|
||||
if (new_size > capacity_)
|
||||
grow(new_size);
|
||||
std::uninitialized_copy(begin, end,
|
||||
@@ -766,7 +838,7 @@ private:
|
||||
}
|
||||
|
||||
protected:
|
||||
void grow(std::size_t size);
|
||||
void grow(std::size_t size) FMT_OVERRIDE;
|
||||
|
||||
public:
|
||||
explicit MemoryBuffer(const Allocator &alloc = Allocator())
|
||||
@@ -828,7 +900,7 @@ void MemoryBuffer<T, SIZE, Allocator>::grow(std::size_t size)
|
||||
std::size_t new_capacity = this->capacity_ + this->capacity_ / 2;
|
||||
if (size > new_capacity)
|
||||
new_capacity = size;
|
||||
T *new_ptr = this->allocate(new_capacity);
|
||||
T *new_ptr = this->allocate(new_capacity, FMT_NULL);
|
||||
// The following code doesn't throw, so the raw pointer above doesn't leak.
|
||||
std::uninitialized_copy(this->ptr_, this->ptr_ + this->size_,
|
||||
make_ptr(new_ptr, new_capacity));
|
||||
@@ -851,7 +923,7 @@ public:
|
||||
FixedBuffer(Char *array, std::size_t size) : fmt::Buffer<Char>(array, size) {}
|
||||
|
||||
protected:
|
||||
FMT_API void grow(std::size_t size);
|
||||
FMT_API void grow(std::size_t size) FMT_OVERRIDE;
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
@@ -891,6 +963,15 @@ public:
|
||||
const char *format, unsigned width, int precision, T value);
|
||||
};
|
||||
|
||||
#if FMT_USE_EXTERN_TEMPLATES
|
||||
extern template int CharTraits<char>::format_float<double>
|
||||
(char *buffer, std::size_t size,
|
||||
const char* format, unsigned width, int precision, double value);
|
||||
extern template int CharTraits<char>::format_float<long double>
|
||||
(char *buffer, std::size_t size,
|
||||
const char* format, unsigned width, int precision, long double value);
|
||||
#endif
|
||||
|
||||
template <>
|
||||
class CharTraits<wchar_t> : public BasicCharTraits<wchar_t>
|
||||
{
|
||||
@@ -909,6 +990,15 @@ public:
|
||||
const wchar_t *format, unsigned width, int precision, T value);
|
||||
};
|
||||
|
||||
#if FMT_USE_EXTERN_TEMPLATES
|
||||
extern template int CharTraits<wchar_t>::format_float<double>
|
||||
(wchar_t *buffer, std::size_t size,
|
||||
const wchar_t* format, unsigned width, int precision, double value);
|
||||
extern template int CharTraits<wchar_t>::format_float<long double>
|
||||
(wchar_t *buffer, std::size_t size,
|
||||
const wchar_t* format, unsigned width, int precision, long double value);
|
||||
#endif
|
||||
|
||||
// Checks if a number is negative - used to avoid warnings.
|
||||
template <bool IsSigned>
|
||||
struct SignChecker
|
||||
@@ -972,13 +1062,7 @@ struct FMT_API BasicData
|
||||
static const char DIGITS[];
|
||||
};
|
||||
|
||||
#ifndef FMT_USE_EXTERN_TEMPLATES
|
||||
// Clang doesn't have a feature check for extern templates so we check
|
||||
// for variadic templates which were introduced in the same version.
|
||||
# define FMT_USE_EXTERN_TEMPLATES (__clang__ && FMT_USE_VARIADIC_TEMPLATES)
|
||||
#endif
|
||||
|
||||
#if FMT_USE_EXTERN_TEMPLATES && !defined(FMT_HEADER_ONLY)
|
||||
#if FMT_USE_EXTERN_TEMPLATES
|
||||
extern template struct BasicData<void>;
|
||||
#endif
|
||||
|
||||
@@ -1087,7 +1171,8 @@ inline void format_decimal(Char *buffer, UInt value, unsigned num_digits,
|
||||
template <typename UInt, typename Char>
|
||||
inline void format_decimal(Char *buffer, UInt value, unsigned num_digits)
|
||||
{
|
||||
return format_decimal(buffer, value, num_digits, NoThousandsSep());
|
||||
format_decimal(buffer, value, num_digits, NoThousandsSep());
|
||||
return;
|
||||
}
|
||||
|
||||
#ifndef _WIN32
|
||||
@@ -1218,6 +1303,8 @@ struct Arg : Value
|
||||
|
||||
template <typename Char>
|
||||
struct NamedArg;
|
||||
template <typename Char, typename T>
|
||||
struct NamedArgWithType;
|
||||
|
||||
template <typename T = void>
|
||||
struct Null {};
|
||||
@@ -1273,7 +1360,10 @@ struct ConvertToIntImpl2<T, true>
|
||||
template<typename T>
|
||||
struct ConvertToInt
|
||||
{
|
||||
enum { enable_conversion = sizeof(convert(get<T>())) == sizeof(Yes) };
|
||||
enum
|
||||
{
|
||||
enable_conversion = sizeof(fmt::internal::convert(get<T>())) == sizeof(Yes)
|
||||
};
|
||||
enum { value = ConvertToIntImpl2<T, enable_conversion>::value };
|
||||
};
|
||||
|
||||
@@ -1308,19 +1398,25 @@ struct Conditional<false, T, F>
|
||||
};
|
||||
|
||||
// For bcc32 which doesn't understand ! in template arguments.
|
||||
template<bool>
|
||||
template <bool>
|
||||
struct Not
|
||||
{
|
||||
enum { value = 0 };
|
||||
};
|
||||
|
||||
template<>
|
||||
template <>
|
||||
struct Not<false>
|
||||
{
|
||||
enum { value = 1 };
|
||||
};
|
||||
|
||||
template<typename T, T> struct LConvCheck
|
||||
template <typename T>
|
||||
struct FalseType
|
||||
{
|
||||
enum { value = 0 };
|
||||
};
|
||||
|
||||
template <typename T, T> struct LConvCheck
|
||||
{
|
||||
LConvCheck(int) {}
|
||||
};
|
||||
@@ -1340,6 +1436,36 @@ inline fmt::StringRef thousands_sep(...)
|
||||
return "";
|
||||
}
|
||||
|
||||
#define FMT_CONCAT(a, b) a##b
|
||||
|
||||
#if FMT_GCC_VERSION >= 303
|
||||
# define FMT_UNUSED __attribute__((unused))
|
||||
#else
|
||||
# define FMT_UNUSED
|
||||
#endif
|
||||
|
||||
#ifndef FMT_USE_STATIC_ASSERT
|
||||
# define FMT_USE_STATIC_ASSERT 0
|
||||
#endif
|
||||
|
||||
#if FMT_USE_STATIC_ASSERT || FMT_HAS_FEATURE(cxx_static_assert) || \
|
||||
(FMT_GCC_VERSION >= 403 && FMT_HAS_GXX_CXX11) || _MSC_VER >= 1600
|
||||
# define FMT_STATIC_ASSERT(cond, message) static_assert(cond, message)
|
||||
#else
|
||||
# define FMT_CONCAT_(a, b) FMT_CONCAT(a, b)
|
||||
# define FMT_STATIC_ASSERT(cond, message) \
|
||||
typedef int FMT_CONCAT_(Assert, __LINE__)[(cond) ? 1 : -1] FMT_UNUSED
|
||||
#endif
|
||||
|
||||
template <typename Formatter, typename Char, typename T>
|
||||
void format_arg(Formatter &, const Char *, const T &)
|
||||
{
|
||||
FMT_STATIC_ASSERT(FalseType<T>::value,
|
||||
"Cannot format argument. To enable the use of ostream "
|
||||
"operator<< include fmt/ostream.h. Otherwise provide "
|
||||
"an overload of format_arg.");
|
||||
}
|
||||
|
||||
// Makes an Arg object from any type.
|
||||
template <typename Formatter>
|
||||
class MakeValue : public Arg
|
||||
@@ -1387,9 +1513,9 @@ private:
|
||||
static void format_custom_arg(
|
||||
void *formatter, const void *arg, void *format_str_ptr)
|
||||
{
|
||||
format(*static_cast<Formatter*>(formatter),
|
||||
*static_cast<const Char**>(format_str_ptr),
|
||||
*static_cast<const T*>(arg));
|
||||
format_arg(*static_cast<Formatter*>(formatter),
|
||||
*static_cast<const Char**>(format_str_ptr),
|
||||
*static_cast<const T*>(arg));
|
||||
}
|
||||
|
||||
public:
|
||||
@@ -1461,7 +1587,9 @@ public:
|
||||
|
||||
FMT_MAKE_VALUE(char *, string.value, CSTRING)
|
||||
FMT_MAKE_VALUE(const char *, string.value, CSTRING)
|
||||
FMT_MAKE_VALUE(signed char *, sstring.value, CSTRING)
|
||||
FMT_MAKE_VALUE(const signed char *, sstring.value, CSTRING)
|
||||
FMT_MAKE_VALUE(unsigned char *, ustring.value, CSTRING)
|
||||
FMT_MAKE_VALUE(const unsigned char *, ustring.value, CSTRING)
|
||||
FMT_MAKE_STR_VALUE(const std::string &, STRING)
|
||||
FMT_MAKE_STR_VALUE(StringRef, STRING)
|
||||
@@ -1491,16 +1619,10 @@ public:
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
MakeValue(const T &value,
|
||||
typename EnableIf<ConvertToInt<T>::value, int>::type = 0)
|
||||
static typename EnableIf<Not<ConvertToInt<T>::value>::value, uint64_t>::type
|
||||
type(const T &)
|
||||
{
|
||||
int_value = value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static uint64_t type(const T &)
|
||||
{
|
||||
return ConvertToInt<T>::value ? Arg::INT : Arg::CUSTOM;
|
||||
return Arg::CUSTOM;
|
||||
}
|
||||
|
||||
// Additional template param `Char_` is needed here because make_type always
|
||||
@@ -1510,12 +1632,22 @@ public:
|
||||
{
|
||||
pointer = &value;
|
||||
}
|
||||
template <typename Char_, typename T>
|
||||
MakeValue(const NamedArgWithType<Char_, T> &value)
|
||||
{
|
||||
pointer = &value;
|
||||
}
|
||||
|
||||
template <typename Char_>
|
||||
static uint64_t type(const NamedArg<Char_> &)
|
||||
{
|
||||
return Arg::NAMED_ARG;
|
||||
}
|
||||
template <typename Char_, typename T>
|
||||
static uint64_t type(const NamedArgWithType<Char_, T> &)
|
||||
{
|
||||
return Arg::NAMED_ARG;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Formatter>
|
||||
@@ -1545,11 +1677,19 @@ struct NamedArg : Arg
|
||||
: Arg(MakeArg< BasicFormatter<Char> >(value)), name(argname) {}
|
||||
};
|
||||
|
||||
template <typename Char, typename T>
|
||||
struct NamedArgWithType : NamedArg<Char>
|
||||
{
|
||||
NamedArgWithType(BasicStringRef<Char> argname, const T &value)
|
||||
: NamedArg<Char>(argname, value) {}
|
||||
};
|
||||
|
||||
class RuntimeError : public std::runtime_error
|
||||
{
|
||||
protected:
|
||||
RuntimeError() : std::runtime_error("") {}
|
||||
~RuntimeError() throw();
|
||||
RuntimeError(const RuntimeError &rerr) : std::runtime_error(rerr) {}
|
||||
FMT_API ~RuntimeError() FMT_DTOR_NOEXCEPT;
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
@@ -1576,10 +1716,7 @@ private:
|
||||
|
||||
internal::Arg::Type type(unsigned index) const
|
||||
{
|
||||
unsigned shift = index * 4;
|
||||
uint64_t mask = 0xf;
|
||||
return static_cast<internal::Arg::Type>(
|
||||
(types_ & (mask << shift)) >> shift);
|
||||
return type(types_, index);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
@@ -1596,6 +1733,11 @@ public:
|
||||
ArgList(ULongLong types, const internal::Arg *args)
|
||||
: types_(types), args_(args) {}
|
||||
|
||||
uint64_t types() const
|
||||
{
|
||||
return types_;
|
||||
}
|
||||
|
||||
/** Returns the argument at specified index. */
|
||||
internal::Arg operator[](unsigned index) const
|
||||
{
|
||||
@@ -1625,6 +1767,14 @@ public:
|
||||
}
|
||||
return args_[index];
|
||||
}
|
||||
|
||||
static internal::Arg::Type type(uint64_t types, unsigned index)
|
||||
{
|
||||
unsigned shift = index * 4;
|
||||
uint64_t mask = 0xf;
|
||||
return static_cast<internal::Arg::Type>(
|
||||
(types & (mask << shift)) >> shift);
|
||||
}
|
||||
};
|
||||
|
||||
#define FMT_DISPATCH(call) static_cast<Impl*>(this)->call
|
||||
@@ -1846,6 +1996,10 @@ struct TypeSpec : EmptySpec
|
||||
{
|
||||
return TYPE;
|
||||
}
|
||||
char type_prefix() const
|
||||
{
|
||||
return TYPE;
|
||||
}
|
||||
char fill() const
|
||||
{
|
||||
return ' ';
|
||||
@@ -1905,6 +2059,10 @@ struct AlignTypeSpec : AlignSpec
|
||||
{
|
||||
return TYPE;
|
||||
}
|
||||
char type_prefix() const
|
||||
{
|
||||
return TYPE;
|
||||
}
|
||||
};
|
||||
|
||||
// A full format specifier.
|
||||
@@ -1930,6 +2088,10 @@ struct FormatSpec : AlignSpec
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
char type_prefix() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
};
|
||||
|
||||
// An integer format specifier.
|
||||
@@ -2107,7 +2269,7 @@ private:
|
||||
public:
|
||||
FMT_API void init(const ArgList &args);
|
||||
|
||||
const internal::Arg* find(const fmt::BasicStringRef<Char> &name) const
|
||||
const internal::Arg *find(const fmt::BasicStringRef<Char> &name) const
|
||||
{
|
||||
// The list is unsorted, so just return the first matching name.
|
||||
for (typename MapType::const_iterator it = map_.begin(), end = map_.end();
|
||||
@@ -2116,16 +2278,16 @@ public:
|
||||
if (it->first == name)
|
||||
return &it->second;
|
||||
}
|
||||
return 0;
|
||||
return FMT_NULL;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Impl, typename Char>
|
||||
template <typename Impl, typename Char, typename Spec = fmt::FormatSpec>
|
||||
class ArgFormatterBase : public ArgVisitor<Impl, void>
|
||||
{
|
||||
private:
|
||||
BasicWriter<Char> &writer_;
|
||||
FormatSpec &spec_;
|
||||
Spec &spec_;
|
||||
|
||||
FMT_DISALLOW_COPY_AND_ASSIGN(ArgFormatterBase);
|
||||
|
||||
@@ -2136,12 +2298,15 @@ private:
|
||||
writer_.write_int(reinterpret_cast<uintptr_t>(p), spec_);
|
||||
}
|
||||
|
||||
// workaround MSVC two-phase lookup issue
|
||||
typedef internal::Arg Arg;
|
||||
|
||||
protected:
|
||||
BasicWriter<Char> &writer()
|
||||
{
|
||||
return writer_;
|
||||
}
|
||||
FormatSpec &spec()
|
||||
Spec &spec()
|
||||
{
|
||||
return spec_;
|
||||
}
|
||||
@@ -2155,12 +2320,14 @@ protected:
|
||||
|
||||
void write(const char *value)
|
||||
{
|
||||
Arg::StringValue<char> str = {value, value != 0 ? std::strlen(value) : 0};
|
||||
Arg::StringValue<char> str = {value, value ? std::strlen(value) : 0};
|
||||
writer_.write_str(str, spec_);
|
||||
}
|
||||
|
||||
public:
|
||||
ArgFormatterBase(BasicWriter<Char> &w, FormatSpec &s)
|
||||
typedef Spec SpecType;
|
||||
|
||||
ArgFormatterBase(BasicWriter<Char> &w, Spec &s)
|
||||
: writer_(w), spec_(s) {}
|
||||
|
||||
template <typename T>
|
||||
@@ -2178,7 +2345,10 @@ public:
|
||||
void visit_bool(bool value)
|
||||
{
|
||||
if (spec_.type_)
|
||||
return visit_any_int(value);
|
||||
{
|
||||
visit_any_int(value);
|
||||
return;
|
||||
}
|
||||
write(value);
|
||||
}
|
||||
|
||||
@@ -2195,29 +2365,29 @@ public:
|
||||
typedef typename BasicWriter<Char>::CharPtr CharPtr;
|
||||
Char fill = internal::CharTraits<Char>::cast(spec_.fill());
|
||||
CharPtr out = CharPtr();
|
||||
const unsigned CHAR_WIDTH = 1;
|
||||
if (spec_.width_ > CHAR_WIDTH)
|
||||
const unsigned CHAR_SIZE = 1;
|
||||
if (spec_.width_ > CHAR_SIZE)
|
||||
{
|
||||
out = writer_.grow_buffer(spec_.width_);
|
||||
if (spec_.align_ == ALIGN_RIGHT)
|
||||
{
|
||||
std::uninitialized_fill_n(out, spec_.width_ - CHAR_WIDTH, fill);
|
||||
out += spec_.width_ - CHAR_WIDTH;
|
||||
std::uninitialized_fill_n(out, spec_.width_ - CHAR_SIZE, fill);
|
||||
out += spec_.width_ - CHAR_SIZE;
|
||||
}
|
||||
else if (spec_.align_ == ALIGN_CENTER)
|
||||
{
|
||||
out = writer_.fill_padding(out, spec_.width_,
|
||||
internal::const_check(CHAR_WIDTH), fill);
|
||||
internal::const_check(CHAR_SIZE), fill);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::uninitialized_fill_n(out + CHAR_WIDTH,
|
||||
spec_.width_ - CHAR_WIDTH, fill);
|
||||
std::uninitialized_fill_n(out + CHAR_SIZE,
|
||||
spec_.width_ - CHAR_SIZE, fill);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out = writer_.grow_buffer(CHAR_WIDTH);
|
||||
out = writer_.grow_buffer(CHAR_SIZE);
|
||||
}
|
||||
*out = internal::CharTraits<Char>::cast(value);
|
||||
}
|
||||
@@ -2229,14 +2399,15 @@ public:
|
||||
write(value);
|
||||
}
|
||||
|
||||
void visit_string(Arg::StringValue<char> value)
|
||||
// Qualification with "internal" here and below is a workaround for nvcc.
|
||||
void visit_string(internal::Arg::StringValue<char> value)
|
||||
{
|
||||
writer_.write_str(value, spec_);
|
||||
}
|
||||
|
||||
using ArgVisitor<Impl, void>::visit_wstring;
|
||||
|
||||
void visit_wstring(Arg::StringValue<Char> value)
|
||||
void visit_wstring(internal::Arg::StringValue<Char> value)
|
||||
{
|
||||
writer_.write_str(value, spec_);
|
||||
}
|
||||
@@ -2323,8 +2494,8 @@ protected:
|
||||
will be called.
|
||||
\endrst
|
||||
*/
|
||||
template <typename Impl, typename Char>
|
||||
class BasicArgFormatter : public internal::ArgFormatterBase<Impl, Char>
|
||||
template <typename Impl, typename Char, typename Spec = fmt::FormatSpec>
|
||||
class BasicArgFormatter : public internal::ArgFormatterBase<Impl, Char, Spec>
|
||||
{
|
||||
private:
|
||||
BasicFormatter<Char, Impl> &formatter_;
|
||||
@@ -2340,8 +2511,8 @@ public:
|
||||
\endrst
|
||||
*/
|
||||
BasicArgFormatter(BasicFormatter<Char, Impl> &formatter,
|
||||
FormatSpec &spec, const Char *fmt)
|
||||
: internal::ArgFormatterBase<Impl, Char>(formatter.writer(), spec),
|
||||
Spec &spec, const Char *fmt)
|
||||
: internal::ArgFormatterBase<Impl, Char, Spec>(formatter.writer(), spec),
|
||||
formatter_(formatter), format_(fmt) {}
|
||||
|
||||
/** Formats an argument of a custom (user-defined) type. */
|
||||
@@ -2353,13 +2524,15 @@ public:
|
||||
|
||||
/** The default argument formatter. */
|
||||
template <typename Char>
|
||||
class ArgFormatter : public BasicArgFormatter<ArgFormatter<Char>, Char>
|
||||
class ArgFormatter :
|
||||
public BasicArgFormatter<ArgFormatter<Char>, Char, FormatSpec>
|
||||
{
|
||||
public:
|
||||
/** Constructs an argument formatter object. */
|
||||
ArgFormatter(BasicFormatter<Char> &formatter,
|
||||
FormatSpec &spec, const Char *fmt)
|
||||
: BasicArgFormatter<ArgFormatter<Char>, Char>(formatter, spec, fmt) {}
|
||||
: BasicArgFormatter<ArgFormatter<Char>,
|
||||
Char, FormatSpec>(formatter, spec, fmt) {}
|
||||
};
|
||||
|
||||
/** This template formats data and writes the output to a writer. */
|
||||
@@ -2444,10 +2617,10 @@ inline uint64_t make_type(const T &arg)
|
||||
return MakeValue< BasicFormatter<char> >::type(arg);
|
||||
}
|
||||
|
||||
template <unsigned N, bool/*IsPacked*/= (N < ArgList::MAX_PACKED_ARGS)>
|
||||
template <std::size_t N, bool/*IsPacked*/= (N < ArgList::MAX_PACKED_ARGS)>
|
||||
struct ArgArray;
|
||||
|
||||
template <unsigned N>
|
||||
template <std::size_t N>
|
||||
struct ArgArray<N, true/*IsPacked*/>
|
||||
{
|
||||
typedef Value Type[N > 0 ? N : 1];
|
||||
@@ -2467,7 +2640,7 @@ static Value make(const T &value)
|
||||
}
|
||||
};
|
||||
|
||||
template <unsigned N>
|
||||
template <std::size_t N>
|
||||
struct ArgArray<N, false/*IsPacked*/>
|
||||
{
|
||||
typedef Arg Type[N + 1]; // +1 for the list end Arg::NONE
|
||||
@@ -2615,7 +2788,7 @@ inline uint64_t make_type(FMT_GEN15(FMT_ARG_TYPE_DEFAULT))
|
||||
class SystemError : public internal::RuntimeError
|
||||
{
|
||||
private:
|
||||
void init(int err_code, CStringRef format_str, ArgList args);
|
||||
FMT_API void init(int err_code, CStringRef format_str, ArgList args);
|
||||
|
||||
protected:
|
||||
int error_code_;
|
||||
@@ -2647,9 +2820,10 @@ public:
|
||||
{
|
||||
init(error_code, message, ArgList());
|
||||
}
|
||||
FMT_DEFAULTED_COPY_CTOR(SystemError)
|
||||
FMT_VARIADIC_CTOR(SystemError, init, int, CStringRef)
|
||||
|
||||
~SystemError() throw();
|
||||
FMT_API ~SystemError() FMT_DTOR_NOEXCEPT;
|
||||
|
||||
int error_code() const
|
||||
{
|
||||
@@ -2778,16 +2952,16 @@ private:
|
||||
void write_int(T value, Spec spec);
|
||||
|
||||
// Formats a floating-point number (double or long double).
|
||||
template <typename T>
|
||||
void write_double(T value, const FormatSpec &spec);
|
||||
template <typename T, typename Spec>
|
||||
void write_double(T value, const Spec &spec);
|
||||
|
||||
// Writes a formatted string.
|
||||
template <typename StrChar>
|
||||
CharPtr write_str(const StrChar *s, std::size_t size, const AlignSpec &spec);
|
||||
|
||||
template <typename StrChar>
|
||||
template <typename StrChar, typename Spec>
|
||||
void write_str(const internal::Arg::StringValue<StrChar> &str,
|
||||
const FormatSpec &spec);
|
||||
const Spec &spec);
|
||||
|
||||
// This following methods are private to disallow writing wide characters
|
||||
// and strings to a char stream. If you want to print a wide string as a
|
||||
@@ -2807,10 +2981,10 @@ private:
|
||||
template<typename T>
|
||||
void append_float_length(Char *&, T) {}
|
||||
|
||||
template <typename Impl, typename Char_>
|
||||
template <typename Impl, typename Char_, typename Spec_>
|
||||
friend class internal::ArgFormatterBase;
|
||||
|
||||
template <typename Impl, typename Char_>
|
||||
template <typename Impl, typename Char_, typename Spec_>
|
||||
friend class BasicPrintfArgFormatter;
|
||||
|
||||
protected:
|
||||
@@ -3039,9 +3213,9 @@ typename BasicWriter<Char>::CharPtr BasicWriter<Char>::write_str(
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
template <typename StrChar>
|
||||
template <typename StrChar, typename Spec>
|
||||
void BasicWriter<Char>::write_str(
|
||||
const internal::Arg::StringValue<StrChar> &s, const FormatSpec &spec)
|
||||
const internal::Arg::StringValue<StrChar> &s, const Spec &spec)
|
||||
{
|
||||
// Check if StrChar is convertible to Char.
|
||||
internal::CharTraits<Char>::convert(StrChar());
|
||||
@@ -3193,7 +3367,7 @@ void BasicWriter<Char>::write_int(T value, Spec spec)
|
||||
if (spec.flag(HASH_FLAG))
|
||||
{
|
||||
prefix[prefix_size++] = '0';
|
||||
prefix[prefix_size++] = spec.type();
|
||||
prefix[prefix_size++] = spec.type_prefix();
|
||||
}
|
||||
unsigned num_digits = 0;
|
||||
do
|
||||
@@ -3220,7 +3394,7 @@ void BasicWriter<Char>::write_int(T value, Spec spec)
|
||||
if (spec.flag(HASH_FLAG))
|
||||
{
|
||||
prefix[prefix_size++] = '0';
|
||||
prefix[prefix_size++] = spec.type();
|
||||
prefix[prefix_size++] = spec.type_prefix();
|
||||
}
|
||||
unsigned num_digits = 0;
|
||||
do
|
||||
@@ -3260,7 +3434,10 @@ void BasicWriter<Char>::write_int(T value, Spec spec)
|
||||
case 'n':
|
||||
{
|
||||
unsigned num_digits = internal::count_digits(abs_value);
|
||||
fmt::StringRef sep = internal::thousands_sep(std::localeconv());
|
||||
fmt::StringRef sep = "";
|
||||
#if !(defined(ANDROID) || defined(__ANDROID__))
|
||||
sep = internal::thousands_sep(std::localeconv());
|
||||
#endif
|
||||
unsigned size = static_cast<unsigned>(
|
||||
num_digits + sep.size() * ((num_digits - 1) / 3));
|
||||
CharPtr p = prepare_int_buffer(size, spec, prefix, prefix_size) + 1;
|
||||
@@ -3275,8 +3452,8 @@ void BasicWriter<Char>::write_int(T value, Spec spec)
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
template <typename T>
|
||||
void BasicWriter<Char>::write_double(T value, const FormatSpec &spec)
|
||||
template <typename T, typename Spec>
|
||||
void BasicWriter<Char>::write_double(T value, const Spec &spec)
|
||||
{
|
||||
// Check type.
|
||||
char type = spec.type();
|
||||
@@ -3396,7 +3573,7 @@ void BasicWriter<Char>::write_double(T value, const FormatSpec &spec)
|
||||
// Format using snprintf.
|
||||
Char fill = internal::CharTraits<Char>::cast(spec.fill());
|
||||
unsigned n = 0;
|
||||
Char *start = 0;
|
||||
Char *start = FMT_NULL;
|
||||
for (;;)
|
||||
{
|
||||
std::size_t buffer_size = buffer_.capacity() - offset;
|
||||
@@ -3834,15 +4011,15 @@ inline void format_decimal(char *&buffer, T value)
|
||||
\endrst
|
||||
*/
|
||||
template <typename T>
|
||||
inline internal::NamedArg<char> arg(StringRef name, const T &arg)
|
||||
inline internal::NamedArgWithType<char, T> arg(StringRef name, const T &arg)
|
||||
{
|
||||
return internal::NamedArg<char>(name, arg);
|
||||
return internal::NamedArgWithType<char, T>(name, arg);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline internal::NamedArg<wchar_t> arg(WStringRef name, const T &arg)
|
||||
inline internal::NamedArgWithType<wchar_t, T> arg(WStringRef name, const T &arg)
|
||||
{
|
||||
return internal::NamedArg<wchar_t>(name, arg);
|
||||
return internal::NamedArgWithType<wchar_t, T>(name, arg);
|
||||
}
|
||||
|
||||
// The following two functions are deleted intentionally to disable
|
||||
@@ -3871,7 +4048,6 @@ void arg(WStringRef, const internal::NamedArg<Char>&) FMT_DELETED_OR_UNDEFINED;
|
||||
#define FMT_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
|
||||
#define FMT_RSEQ_N() 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0
|
||||
|
||||
#define FMT_CONCAT(a, b) a##b
|
||||
#define FMT_FOR_EACH_(N, f, ...) \
|
||||
FMT_EXPAND(FMT_CONCAT(FMT_FOR_EACH, N)(f, __VA_ARGS__))
|
||||
#define FMT_FOR_EACH(f, ...) \
|
||||
@@ -4064,7 +4240,7 @@ inline internal::Arg BasicFormatter<Char, AF>::get_arg(
|
||||
template <typename Char, typename AF>
|
||||
inline internal::Arg BasicFormatter<Char, AF>::parse_arg_index(const Char *&s)
|
||||
{
|
||||
const char *error = 0;
|
||||
const char *error = FMT_NULL;
|
||||
internal::Arg arg = *s < '0' || *s > '9' ?
|
||||
next_arg(error) : get_arg(internal::parse_nonnegative_int(s), error);
|
||||
if (error)
|
||||
@@ -4086,7 +4262,7 @@ inline internal::Arg BasicFormatter<Char, AF>::parse_arg_name(const Char *&s)
|
||||
c = *++s;
|
||||
}
|
||||
while (internal::is_name_start(c) || ('0' <= c && c <= '9'));
|
||||
const char *error = 0;
|
||||
const char *error = FMT_NULL;
|
||||
internal::Arg arg = get_arg(BasicStringRef<Char>(start, s - start), error);
|
||||
if (error)
|
||||
FMT_THROW(FormatError(error));
|
||||
@@ -4099,7 +4275,7 @@ const Char *BasicFormatter<Char, ArgFormatter>::format(
|
||||
{
|
||||
using internal::Arg;
|
||||
const Char *s = format_str;
|
||||
FormatSpec spec;
|
||||
typename ArgFormatter::SpecType spec;
|
||||
if (*s == ':')
|
||||
{
|
||||
if (arg.type == Arg::CUSTOM)
|
||||
@@ -4312,6 +4488,74 @@ void BasicFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
|
||||
}
|
||||
write(writer_, start, s);
|
||||
}
|
||||
|
||||
template <typename Char, typename It>
|
||||
struct ArgJoin
|
||||
{
|
||||
It first;
|
||||
It last;
|
||||
BasicCStringRef<Char> sep;
|
||||
|
||||
ArgJoin(It first, It last, const BasicCStringRef<Char>& sep) :
|
||||
first(first),
|
||||
last(last),
|
||||
sep(sep) {}
|
||||
};
|
||||
|
||||
template <typename It>
|
||||
ArgJoin<char, It> join(It first, It last, const BasicCStringRef<char>& sep)
|
||||
{
|
||||
return ArgJoin<char, It>(first, last, sep);
|
||||
}
|
||||
|
||||
template <typename It>
|
||||
ArgJoin<wchar_t, It> join(It first, It last, const BasicCStringRef<wchar_t>& sep)
|
||||
{
|
||||
return ArgJoin<wchar_t, It>(first, last, sep);
|
||||
}
|
||||
|
||||
#if FMT_HAS_GXX_CXX11
|
||||
template <typename Range>
|
||||
auto join(const Range& range, const BasicCStringRef<char>& sep)
|
||||
-> ArgJoin<char, decltype(std::begin(range))>
|
||||
{
|
||||
return join(std::begin(range), std::end(range), sep);
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
auto join(const Range& range, const BasicCStringRef<wchar_t>& sep)
|
||||
-> ArgJoin<wchar_t, decltype(std::begin(range))>
|
||||
{
|
||||
return join(std::begin(range), std::end(range), sep);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename ArgFormatter, typename Char, typename It>
|
||||
void format_arg(fmt::BasicFormatter<Char, ArgFormatter> &f,
|
||||
const Char *&format_str, const ArgJoin<Char, It>& e)
|
||||
{
|
||||
const Char* end = format_str;
|
||||
if (*end == ':')
|
||||
++end;
|
||||
while (*end && *end != '}')
|
||||
++end;
|
||||
if (*end != '}')
|
||||
FMT_THROW(FormatError("missing '}' in format string"));
|
||||
|
||||
It it = e.first;
|
||||
if (it != e.last)
|
||||
{
|
||||
const Char* save = format_str;
|
||||
f.format(format_str, internal::MakeArg<fmt::BasicFormatter<Char, ArgFormatter> >(*it++));
|
||||
while (it != e.last)
|
||||
{
|
||||
f.writer().write(e.sep);
|
||||
format_str = save;
|
||||
f.format(format_str, internal::MakeArg<fmt::BasicFormatter<Char, ArgFormatter> >(*it++));
|
||||
}
|
||||
}
|
||||
format_str = end + 1;
|
||||
}
|
||||
} // namespace fmt
|
||||
|
||||
#if FMT_USE_USER_DEFINED_LITERALS
|
||||
@@ -4339,7 +4583,7 @@ struct UdlArg
|
||||
const Char *str;
|
||||
|
||||
template <typename T>
|
||||
NamedArg<Char> operator=(T &&value) const
|
||||
NamedArgWithType<Char, T> operator=(T &&value) const
|
||||
{
|
||||
return {str, std::forward<T>(value)};
|
||||
}
|
||||
|
@@ -7,15 +7,12 @@
|
||||
For the license information refer to format.h.
|
||||
*/
|
||||
|
||||
// Commented out by spdlog to use header only
|
||||
// #include "fmt/ostream.h"
|
||||
// #include "fmt/printf.h"
|
||||
#include "ostream.h"
|
||||
|
||||
namespace fmt {
|
||||
|
||||
namespace {
|
||||
// Write the content of w to os.
|
||||
void write(std::ostream &os, Writer &w) {
|
||||
namespace internal {
|
||||
FMT_FUNC void write(std::ostream &os, Writer &w) {
|
||||
const char *data = w.data();
|
||||
typedef internal::MakeUnsigned<std::streamsize>::Type UnsignedStreamSize;
|
||||
UnsignedStreamSize size = w.size();
|
||||
@@ -33,13 +30,6 @@ void write(std::ostream &os, Writer &w) {
|
||||
FMT_FUNC void print(std::ostream &os, CStringRef format_str, ArgList args) {
|
||||
MemoryWriter w;
|
||||
w.write(format_str, args);
|
||||
write(os, w);
|
||||
}
|
||||
|
||||
FMT_FUNC int fprintf(std::ostream &os, CStringRef format, ArgList args) {
|
||||
MemoryWriter w;
|
||||
printf(w, format, args);
|
||||
write(os, w);
|
||||
return static_cast<int>(w.size());
|
||||
internal::write(os, w);
|
||||
}
|
||||
} // namespace fmt
|
||||
|
@@ -10,8 +10,7 @@
|
||||
#ifndef FMT_OSTREAM_H_
|
||||
#define FMT_OSTREAM_H_
|
||||
|
||||
// Commented out by spdlog to use header only
|
||||
// #include "fmt/format.h"
|
||||
#include "format.h"
|
||||
#include <ostream>
|
||||
|
||||
namespace fmt
|
||||
@@ -28,32 +27,29 @@ private:
|
||||
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
|
||||
|
||||
Buffer<Char> &buffer_;
|
||||
Char *start_;
|
||||
|
||||
public:
|
||||
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0])
|
||||
{
|
||||
this->setp(start_, start_ + buffer_.capacity());
|
||||
}
|
||||
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer) {}
|
||||
|
||||
int_type overflow(int_type ch = traits_type::eof())
|
||||
protected:
|
||||
// The put-area is actually always empty. This makes the implementation
|
||||
// simpler and has the advantage that the streambuf and the buffer are always
|
||||
// in sync and sputc never writes into uninitialized memory. The obvious
|
||||
// disadvantage is that each call to sputc always results in a (virtual) call
|
||||
// to overflow. There is no disadvantage here for sputn since this always
|
||||
// results in a call to xsputn.
|
||||
|
||||
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE
|
||||
{
|
||||
if (!traits_type::eq_int_type(ch, traits_type::eof()))
|
||||
{
|
||||
size_t buf_size = size();
|
||||
buffer_.resize(buf_size);
|
||||
buffer_.reserve(buf_size * 2);
|
||||
|
||||
start_ = &buffer_[0];
|
||||
start_[buf_size] = traits_type::to_char_type(ch);
|
||||
this->setp(start_+ buf_size + 1, start_ + buf_size * 2);
|
||||
}
|
||||
buffer_.push_back(static_cast<Char>(ch));
|
||||
return ch;
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE
|
||||
{
|
||||
return to_unsigned(this->pptr() - start_);
|
||||
buffer_.append(s, s + count);
|
||||
return count;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -77,12 +73,15 @@ struct ConvertToIntImpl<T, true>
|
||||
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
|
||||
};
|
||||
};
|
||||
|
||||
// Write the content of w to os.
|
||||
FMT_API void write(std::ostream &os, Writer &w);
|
||||
} // namespace internal
|
||||
|
||||
// Formats a value.
|
||||
template <typename Char, typename ArgFormatter, typename T>
|
||||
void format(BasicFormatter<Char, ArgFormatter> &f,
|
||||
const Char *&format_str, const T &value)
|
||||
template <typename Char, typename ArgFormatter_, typename T>
|
||||
void format_arg(BasicFormatter<Char, ArgFormatter_> &f,
|
||||
const Char *&format_str, const T &value)
|
||||
{
|
||||
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
|
||||
|
||||
@@ -90,7 +89,7 @@ void format(BasicFormatter<Char, ArgFormatter> &f,
|
||||
std::basic_ostream<Char> output(&format_buf);
|
||||
output << value;
|
||||
|
||||
BasicStringRef<Char> str(&buffer[0], format_buf.size());
|
||||
BasicStringRef<Char> str(&buffer[0], buffer.size());
|
||||
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
|
||||
format_str = f.format(format_str, MakeArg(str));
|
||||
}
|
||||
@@ -106,18 +105,6 @@ void format(BasicFormatter<Char, ArgFormatter> &f,
|
||||
*/
|
||||
FMT_API void print(std::ostream &os, CStringRef format_str, ArgList args);
|
||||
FMT_VARIADIC(void, print, std::ostream &, CStringRef)
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to the stream *os*.
|
||||
|
||||
**Example**::
|
||||
|
||||
fprintf(cerr, "Don't %s!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
FMT_API int fprintf(std::ostream &os, CStringRef format_str, ArgList args);
|
||||
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
|
||||
} // namespace fmt
|
||||
|
||||
#ifdef FMT_HEADER_ONLY
|
||||
|
241
include/spdlog/fmt/bundled/posix.cc
Normal file
241
include/spdlog/fmt/bundled/posix.cc
Normal file
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
A C++ interface to POSIX functions.
|
||||
|
||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
All rights reserved.
|
||||
|
||||
For the license information refer to format.h.
|
||||
*/
|
||||
|
||||
// Disable bogus MSVC warnings.
|
||||
#ifndef _CRT_SECURE_NO_WARNINGS
|
||||
# define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include "posix.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
# include <unistd.h>
|
||||
#else
|
||||
# ifndef WIN32_LEAN_AND_MEAN
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include <windows.h>
|
||||
# include <io.h>
|
||||
|
||||
# define O_CREAT _O_CREAT
|
||||
# define O_TRUNC _O_TRUNC
|
||||
|
||||
# ifndef S_IRUSR
|
||||
# define S_IRUSR _S_IREAD
|
||||
# endif
|
||||
|
||||
# ifndef S_IWUSR
|
||||
# define S_IWUSR _S_IWRITE
|
||||
# endif
|
||||
|
||||
# ifdef __MINGW32__
|
||||
# define _SH_DENYNO 0x40
|
||||
# endif
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
#ifdef fileno
|
||||
# undef fileno
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
#ifdef _WIN32
|
||||
// Return type of read and write functions.
|
||||
typedef int RWResult;
|
||||
|
||||
// On Windows the count argument to read and write is unsigned, so convert
|
||||
// it from size_t preventing integer overflow.
|
||||
inline unsigned convert_rwcount(std::size_t count) {
|
||||
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
|
||||
}
|
||||
#else
|
||||
// Return type of read and write functions.
|
||||
typedef ssize_t RWResult;
|
||||
|
||||
inline std::size_t convert_rwcount(std::size_t count) { return count; }
|
||||
#endif
|
||||
}
|
||||
|
||||
fmt::BufferedFile::~BufferedFile() FMT_NOEXCEPT {
|
||||
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
|
||||
fmt::report_system_error(errno, "cannot close file");
|
||||
}
|
||||
|
||||
fmt::BufferedFile::BufferedFile(
|
||||
fmt::CStringRef filename, fmt::CStringRef mode) {
|
||||
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), 0);
|
||||
if (!file_)
|
||||
FMT_THROW(SystemError(errno, "cannot open file {}", filename));
|
||||
}
|
||||
|
||||
void fmt::BufferedFile::close() {
|
||||
if (!file_)
|
||||
return;
|
||||
int result = FMT_SYSTEM(fclose(file_));
|
||||
file_ = FMT_NULL;
|
||||
if (result != 0)
|
||||
FMT_THROW(SystemError(errno, "cannot close file"));
|
||||
}
|
||||
|
||||
// A macro used to prevent expansion of fileno on broken versions of MinGW.
|
||||
#define FMT_ARGS
|
||||
|
||||
int fmt::BufferedFile::fileno() const {
|
||||
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
|
||||
if (fd == -1)
|
||||
FMT_THROW(SystemError(errno, "cannot get file descriptor"));
|
||||
return fd;
|
||||
}
|
||||
|
||||
fmt::File::File(fmt::CStringRef path, int oflag) {
|
||||
int mode = S_IRUSR | S_IWUSR;
|
||||
#if defined(_WIN32) && !defined(__MINGW32__)
|
||||
fd_ = -1;
|
||||
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
|
||||
#else
|
||||
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
|
||||
#endif
|
||||
if (fd_ == -1)
|
||||
FMT_THROW(SystemError(errno, "cannot open file {}", path));
|
||||
}
|
||||
|
||||
fmt::File::~File() FMT_NOEXCEPT {
|
||||
// Don't retry close in case of EINTR!
|
||||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
|
||||
fmt::report_system_error(errno, "cannot close file");
|
||||
}
|
||||
|
||||
void fmt::File::close() {
|
||||
if (fd_ == -1)
|
||||
return;
|
||||
// Don't retry close in case of EINTR!
|
||||
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
|
||||
int result = FMT_POSIX_CALL(close(fd_));
|
||||
fd_ = -1;
|
||||
if (result != 0)
|
||||
FMT_THROW(SystemError(errno, "cannot close file"));
|
||||
}
|
||||
|
||||
fmt::LongLong fmt::File::size() const {
|
||||
#ifdef _WIN32
|
||||
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
|
||||
// is less than 0x0500 as is the case with some default MinGW builds.
|
||||
// Both functions support large file sizes.
|
||||
DWORD size_upper = 0;
|
||||
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
|
||||
DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
|
||||
if (size_lower == INVALID_FILE_SIZE) {
|
||||
DWORD error = GetLastError();
|
||||
if (error != NO_ERROR)
|
||||
FMT_THROW(WindowsError(GetLastError(), "cannot get file size"));
|
||||
}
|
||||
fmt::ULongLong long_size = size_upper;
|
||||
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
|
||||
#else
|
||||
typedef struct stat Stat;
|
||||
Stat file_stat = Stat();
|
||||
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
|
||||
FMT_THROW(SystemError(errno, "cannot get file attributes"));
|
||||
FMT_STATIC_ASSERT(sizeof(fmt::LongLong) >= sizeof(file_stat.st_size),
|
||||
"return type of File::size is not large enough");
|
||||
return file_stat.st_size;
|
||||
#endif
|
||||
}
|
||||
|
||||
std::size_t fmt::File::read(void *buffer, std::size_t count) {
|
||||
RWResult result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
|
||||
if (result < 0)
|
||||
FMT_THROW(SystemError(errno, "cannot read from file"));
|
||||
return internal::to_unsigned(result);
|
||||
}
|
||||
|
||||
std::size_t fmt::File::write(const void *buffer, std::size_t count) {
|
||||
RWResult result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
|
||||
if (result < 0)
|
||||
FMT_THROW(SystemError(errno, "cannot write to file"));
|
||||
return internal::to_unsigned(result);
|
||||
}
|
||||
|
||||
fmt::File fmt::File::dup(int fd) {
|
||||
// Don't retry as dup doesn't return EINTR.
|
||||
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
|
||||
int new_fd = FMT_POSIX_CALL(dup(fd));
|
||||
if (new_fd == -1)
|
||||
FMT_THROW(SystemError(errno, "cannot duplicate file descriptor {}", fd));
|
||||
return File(new_fd);
|
||||
}
|
||||
|
||||
void fmt::File::dup2(int fd) {
|
||||
int result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||
if (result == -1) {
|
||||
FMT_THROW(SystemError(errno,
|
||||
"cannot duplicate file descriptor {} to {}", fd_, fd));
|
||||
}
|
||||
}
|
||||
|
||||
void fmt::File::dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT {
|
||||
int result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
|
||||
if (result == -1)
|
||||
ec = ErrorCode(errno);
|
||||
}
|
||||
|
||||
void fmt::File::pipe(File &read_end, File &write_end) {
|
||||
// Close the descriptors first to make sure that assignments don't throw
|
||||
// and there are no leaks.
|
||||
read_end.close();
|
||||
write_end.close();
|
||||
int fds[2] = {};
|
||||
#ifdef _WIN32
|
||||
// Make the default pipe capacity same as on Linux 2.6.11+.
|
||||
enum { DEFAULT_CAPACITY = 65536 };
|
||||
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
|
||||
#else
|
||||
// Don't retry as the pipe function doesn't return EINTR.
|
||||
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
|
||||
int result = FMT_POSIX_CALL(pipe(fds));
|
||||
#endif
|
||||
if (result != 0)
|
||||
FMT_THROW(SystemError(errno, "cannot create pipe"));
|
||||
// The following assignments don't throw because read_fd and write_fd
|
||||
// are closed.
|
||||
read_end = File(fds[0]);
|
||||
write_end = File(fds[1]);
|
||||
}
|
||||
|
||||
fmt::BufferedFile fmt::File::fdopen(const char *mode) {
|
||||
// Don't retry as fdopen doesn't return EINTR.
|
||||
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
|
||||
if (!f)
|
||||
FMT_THROW(SystemError(errno, "cannot associate stream with file descriptor"));
|
||||
BufferedFile file(f);
|
||||
fd_ = -1;
|
||||
return file;
|
||||
}
|
||||
|
||||
long fmt::getpagesize() {
|
||||
#ifdef _WIN32
|
||||
SYSTEM_INFO si;
|
||||
GetSystemInfo(&si);
|
||||
return si.dwPageSize;
|
||||
#else
|
||||
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
|
||||
if (size < 0)
|
||||
FMT_THROW(SystemError(errno, "cannot get memory page size"));
|
||||
return size;
|
||||
#endif
|
||||
}
|
424
include/spdlog/fmt/bundled/posix.h
Normal file
424
include/spdlog/fmt/bundled/posix.h
Normal file
@@ -0,0 +1,424 @@
|
||||
/*
|
||||
A C++ interface to POSIX functions.
|
||||
|
||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
All rights reserved.
|
||||
|
||||
For the license information refer to format.h.
|
||||
*/
|
||||
|
||||
#ifndef FMT_POSIX_H_
|
||||
#define FMT_POSIX_H_
|
||||
|
||||
#if defined(__MINGW32__) || defined(__CYGWIN__)
|
||||
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
|
||||
# undef __STRICT_ANSI__
|
||||
#endif
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h> // for O_RDONLY
|
||||
#include <locale.h> // for locale_t
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // for strtod_l
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||
#endif
|
||||
|
||||
#include "format.h"
|
||||
|
||||
#ifndef FMT_POSIX
|
||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX(call) _##call
|
||||
# else
|
||||
# define FMT_POSIX(call) call
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
|
||||
#ifdef FMT_SYSTEM
|
||||
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
|
||||
#else
|
||||
# define FMT_SYSTEM(call) call
|
||||
# ifdef _WIN32
|
||||
// Fix warnings about deprecated symbols.
|
||||
# define FMT_POSIX_CALL(call) ::_##call
|
||||
# else
|
||||
# define FMT_POSIX_CALL(call) ::call
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// Retries the expression while it evaluates to error_result and errno
|
||||
// equals to EINTR.
|
||||
#ifndef _WIN32
|
||||
# define FMT_RETRY_VAL(result, expression, error_result) \
|
||||
do { \
|
||||
result = (expression); \
|
||||
} while (result == error_result && errno == EINTR)
|
||||
#else
|
||||
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
|
||||
#endif
|
||||
|
||||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
|
||||
// An error code.
|
||||
class ErrorCode
|
||||
{
|
||||
private:
|
||||
int value_;
|
||||
|
||||
public:
|
||||
explicit ErrorCode(int value = 0) FMT_NOEXCEPT :
|
||||
value_(value) {}
|
||||
|
||||
int get() const FMT_NOEXCEPT
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
};
|
||||
|
||||
// A buffered file.
|
||||
class BufferedFile
|
||||
{
|
||||
private:
|
||||
FILE *file_;
|
||||
|
||||
friend class File;
|
||||
|
||||
explicit BufferedFile(FILE *f) : file_(f) {}
|
||||
|
||||
public:
|
||||
// Constructs a BufferedFile object which doesn't represent any file.
|
||||
BufferedFile() FMT_NOEXCEPT :
|
||||
file_(FMT_NULL) {}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~BufferedFile() FMT_NOEXCEPT;
|
||||
|
||||
#if !FMT_USE_RVALUE_REFERENCES
|
||||
// Emulate a move constructor and a move assignment operator if rvalue
|
||||
// references are not supported.
|
||||
|
||||
private:
|
||||
// A proxy object to emulate a move constructor.
|
||||
// It is private to make it impossible call operator Proxy directly.
|
||||
struct Proxy
|
||||
{
|
||||
FILE *file;
|
||||
};
|
||||
|
||||
public:
|
||||
// A "move constructor" for moving from a temporary.
|
||||
BufferedFile(Proxy p) FMT_NOEXCEPT :
|
||||
file_(p.file) {}
|
||||
|
||||
// A "move constructor" for moving from an lvalue.
|
||||
BufferedFile(BufferedFile &f) FMT_NOEXCEPT :
|
||||
file_(f.file_)
|
||||
{
|
||||
f.file_ = FMT_NULL;
|
||||
}
|
||||
|
||||
// A "move assignment operator" for moving from a temporary.
|
||||
BufferedFile &operator=(Proxy p)
|
||||
{
|
||||
close();
|
||||
file_ = p.file;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// A "move assignment operator" for moving from an lvalue.
|
||||
BufferedFile &operator=(BufferedFile &other)
|
||||
{
|
||||
close();
|
||||
file_ = other.file_;
|
||||
other.file_ = FMT_NULL;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Returns a proxy object for moving from a temporary:
|
||||
// BufferedFile file = BufferedFile(...);
|
||||
operator Proxy() FMT_NOEXCEPT
|
||||
{
|
||||
Proxy p = {file_};
|
||||
file_ = FMT_NULL;
|
||||
return p;
|
||||
}
|
||||
|
||||
#else
|
||||
private:
|
||||
FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
|
||||
|
||||
public:
|
||||
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT :
|
||||
file_(other.file_)
|
||||
{
|
||||
other.file_ = FMT_NULL;
|
||||
}
|
||||
|
||||
BufferedFile& operator=(BufferedFile &&other)
|
||||
{
|
||||
close();
|
||||
file_ = other.file_;
|
||||
other.file_ = FMT_NULL;
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Opens a file.
|
||||
FMT_API BufferedFile(CStringRef filename, CStringRef mode);
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
|
||||
// Returns the pointer to a FILE object representing this file.
|
||||
FILE *get() const FMT_NOEXCEPT
|
||||
{
|
||||
return file_;
|
||||
}
|
||||
|
||||
// We place parentheses around fileno to workaround a bug in some versions
|
||||
// of MinGW that define fileno as a macro.
|
||||
FMT_API int (fileno)() const;
|
||||
|
||||
void print(CStringRef format_str, const ArgList &args)
|
||||
{
|
||||
fmt::print(file_, format_str, args);
|
||||
}
|
||||
FMT_VARIADIC(void, print, CStringRef)
|
||||
};
|
||||
|
||||
// A file. Closed file is represented by a File object with descriptor -1.
|
||||
// Methods that are not declared with FMT_NOEXCEPT may throw
|
||||
// fmt::SystemError in case of failure. Note that some errors such as
|
||||
// closing the file multiple times will cause a crash on Windows rather
|
||||
// than an exception. You can get standard behavior by overriding the
|
||||
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||
class File
|
||||
{
|
||||
private:
|
||||
int fd_; // File descriptor.
|
||||
|
||||
// Constructs a File object with a given descriptor.
|
||||
explicit File(int fd) : fd_(fd) {}
|
||||
|
||||
public:
|
||||
// Possible values for the oflag argument to the constructor.
|
||||
enum
|
||||
{
|
||||
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
|
||||
};
|
||||
|
||||
// Constructs a File object which doesn't represent any file.
|
||||
File() FMT_NOEXCEPT :
|
||||
fd_(-1) {}
|
||||
|
||||
// Opens a file and constructs a File object representing this file.
|
||||
FMT_API File(CStringRef path, int oflag);
|
||||
|
||||
#if !FMT_USE_RVALUE_REFERENCES
|
||||
// Emulate a move constructor and a move assignment operator if rvalue
|
||||
// references are not supported.
|
||||
|
||||
private:
|
||||
// A proxy object to emulate a move constructor.
|
||||
// It is private to make it impossible call operator Proxy directly.
|
||||
struct Proxy
|
||||
{
|
||||
int fd;
|
||||
};
|
||||
|
||||
public:
|
||||
// A "move constructor" for moving from a temporary.
|
||||
File(Proxy p) FMT_NOEXCEPT :
|
||||
fd_(p.fd) {}
|
||||
|
||||
// A "move constructor" for moving from an lvalue.
|
||||
File(File &other) FMT_NOEXCEPT :
|
||||
fd_(other.fd_)
|
||||
{
|
||||
other.fd_ = -1;
|
||||
}
|
||||
|
||||
// A "move assignment operator" for moving from a temporary.
|
||||
File &operator=(Proxy p)
|
||||
{
|
||||
close();
|
||||
fd_ = p.fd;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// A "move assignment operator" for moving from an lvalue.
|
||||
File &operator=(File &other)
|
||||
{
|
||||
close();
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Returns a proxy object for moving from a temporary:
|
||||
// File file = File(...);
|
||||
operator Proxy() FMT_NOEXCEPT
|
||||
{
|
||||
Proxy p = {fd_};
|
||||
fd_ = -1;
|
||||
return p;
|
||||
}
|
||||
|
||||
#else
|
||||
private:
|
||||
FMT_DISALLOW_COPY_AND_ASSIGN(File);
|
||||
|
||||
public:
|
||||
File(File &&other) FMT_NOEXCEPT :
|
||||
fd_(other.fd_)
|
||||
{
|
||||
other.fd_ = -1;
|
||||
}
|
||||
|
||||
File& operator=(File &&other)
|
||||
{
|
||||
close();
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~File() FMT_NOEXCEPT;
|
||||
|
||||
// Returns the file descriptor.
|
||||
int descriptor() const FMT_NOEXCEPT
|
||||
{
|
||||
return fd_;
|
||||
}
|
||||
|
||||
// Closes the file.
|
||||
FMT_API void close();
|
||||
|
||||
// Returns the file size. The size has signed type for consistency with
|
||||
// stat::st_size.
|
||||
FMT_API LongLong size() const;
|
||||
|
||||
// Attempts to read count bytes from the file into the specified buffer.
|
||||
FMT_API std::size_t read(void *buffer, std::size_t count);
|
||||
|
||||
// Attempts to write count bytes from the specified buffer to the file.
|
||||
FMT_API std::size_t write(const void *buffer, std::size_t count);
|
||||
|
||||
// Duplicates a file descriptor with the dup function and returns
|
||||
// the duplicate as a file object.
|
||||
FMT_API static File dup(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
FMT_API void dup2(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
FMT_API void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT;
|
||||
|
||||
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||
// and writing respectively.
|
||||
FMT_API static void pipe(File &read_end, File &write_end);
|
||||
|
||||
// Creates a BufferedFile object associated with this file and detaches
|
||||
// this File object from the file.
|
||||
FMT_API BufferedFile fdopen(const char *mode);
|
||||
};
|
||||
|
||||
// Returns the memory page size.
|
||||
long getpagesize();
|
||||
|
||||
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
|
||||
!defined(__ANDROID__) && !defined(__CYGWIN__)
|
||||
# define FMT_LOCALE
|
||||
#endif
|
||||
|
||||
#ifdef FMT_LOCALE
|
||||
// A "C" numeric locale.
|
||||
class Locale
|
||||
{
|
||||
private:
|
||||
# ifdef _MSC_VER
|
||||
typedef _locale_t locale_t;
|
||||
|
||||
enum { LC_NUMERIC_MASK = LC_NUMERIC };
|
||||
|
||||
static locale_t newlocale(int category_mask, const char *locale, locale_t)
|
||||
{
|
||||
return _create_locale(category_mask, locale);
|
||||
}
|
||||
|
||||
static void freelocale(locale_t locale)
|
||||
{
|
||||
_free_locale(locale);
|
||||
}
|
||||
|
||||
static double strtod_l(const char *nptr, char **endptr, _locale_t locale)
|
||||
{
|
||||
return _strtod_l(nptr, endptr, locale);
|
||||
}
|
||||
# endif
|
||||
|
||||
locale_t locale_;
|
||||
|
||||
FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
|
||||
|
||||
public:
|
||||
typedef locale_t Type;
|
||||
|
||||
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL))
|
||||
{
|
||||
if (!locale_)
|
||||
FMT_THROW(fmt::SystemError(errno, "cannot create locale"));
|
||||
}
|
||||
~Locale()
|
||||
{
|
||||
freelocale(locale_);
|
||||
}
|
||||
|
||||
Type get() const
|
||||
{
|
||||
return locale_;
|
||||
}
|
||||
|
||||
// Converts string to floating-point number and advances str past the end
|
||||
// of the parsed input.
|
||||
double strtod(const char *&str) const
|
||||
{
|
||||
char *end = FMT_NULL;
|
||||
double result = strtod_l(str, &end, locale_);
|
||||
str = end;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
#endif // FMT_LOCALE
|
||||
} // namespace fmt
|
||||
|
||||
#if !FMT_USE_RVALUE_REFERENCES
|
||||
namespace std
|
||||
{
|
||||
// For compatibility with C++98.
|
||||
inline fmt::BufferedFile &move(fmt::BufferedFile &f)
|
||||
{
|
||||
return f;
|
||||
}
|
||||
inline fmt::File &move(fmt::File &f)
|
||||
{
|
||||
return f;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // FMT_POSIX_H_
|
32
include/spdlog/fmt/bundled/printf.cc
Normal file
32
include/spdlog/fmt/bundled/printf.cc
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Formatting library for C++
|
||||
|
||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
All rights reserved.
|
||||
|
||||
For the license information refer to format.h.
|
||||
*/
|
||||
|
||||
#include "format.h"
|
||||
#include "printf.h"
|
||||
|
||||
namespace fmt {
|
||||
|
||||
template <typename Char>
|
||||
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args);
|
||||
|
||||
FMT_FUNC int fprintf(std::FILE *f, CStringRef format, ArgList args) {
|
||||
MemoryWriter w;
|
||||
printf(w, format, args);
|
||||
std::size_t size = w.size();
|
||||
return std::fwrite(w.data(), 1, size, f) < size ? -1 : static_cast<int>(size);
|
||||
}
|
||||
|
||||
#ifndef FMT_HEADER_ONLY
|
||||
|
||||
template void PrintfFormatter<char>::format(CStringRef format);
|
||||
template void PrintfFormatter<wchar_t>::format(WCStringRef format);
|
||||
|
||||
#endif // FMT_HEADER_ONLY
|
||||
|
||||
} // namespace fmt
|
@@ -13,7 +13,7 @@
|
||||
#include <algorithm> // std::fill_n
|
||||
#include <limits> // std::numeric_limits
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "ostream.h"
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
@@ -80,6 +80,43 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// returns the default type for format specific "%s"
|
||||
class DefaultType : public ArgVisitor<DefaultType, char>
|
||||
{
|
||||
public:
|
||||
char visit_char(int)
|
||||
{
|
||||
return 'c';
|
||||
}
|
||||
|
||||
char visit_bool(bool)
|
||||
{
|
||||
return 's';
|
||||
}
|
||||
|
||||
char visit_pointer(const void *)
|
||||
{
|
||||
return 'p';
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
char visit_any_int(T)
|
||||
{
|
||||
return 'd';
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
char visit_any_double(T)
|
||||
{
|
||||
return 'g';
|
||||
}
|
||||
|
||||
char visit_unhandled_arg()
|
||||
{
|
||||
return 's';
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename U>
|
||||
struct is_same
|
||||
{
|
||||
@@ -115,10 +152,21 @@ public:
|
||||
visit_any_int(value);
|
||||
}
|
||||
|
||||
void visit_char(char value)
|
||||
{
|
||||
if (type_ != 's')
|
||||
visit_any_int(value);
|
||||
}
|
||||
|
||||
template <typename U>
|
||||
void visit_any_int(U value)
|
||||
{
|
||||
bool is_signed = type_ == 'd' || type_ == 'i';
|
||||
if (type_ == 's')
|
||||
{
|
||||
is_signed = std::numeric_limits<U>::is_signed;
|
||||
}
|
||||
|
||||
using internal::Arg;
|
||||
typedef typename internal::Conditional<
|
||||
is_same<T, void>::value, U, T>::type TargetType;
|
||||
@@ -228,8 +276,9 @@ public:
|
||||
superclass will be called.
|
||||
\endrst
|
||||
*/
|
||||
template <typename Impl, typename Char>
|
||||
class BasicPrintfArgFormatter : public internal::ArgFormatterBase<Impl, Char>
|
||||
template <typename Impl, typename Char, typename Spec>
|
||||
class BasicPrintfArgFormatter :
|
||||
public internal::ArgFormatterBase<Impl, Char, Spec>
|
||||
{
|
||||
private:
|
||||
void write_null_pointer()
|
||||
@@ -238,7 +287,7 @@ private:
|
||||
this->write("(nil)");
|
||||
}
|
||||
|
||||
typedef internal::ArgFormatterBase<Impl, Char> Base;
|
||||
typedef internal::ArgFormatterBase<Impl, Char, Spec> Base;
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -248,13 +297,13 @@ public:
|
||||
specifier information for standard argument types.
|
||||
\endrst
|
||||
*/
|
||||
BasicPrintfArgFormatter(BasicWriter<Char> &writer, FormatSpec &spec)
|
||||
: internal::ArgFormatterBase<Impl, Char>(writer, spec) {}
|
||||
BasicPrintfArgFormatter(BasicWriter<Char> &w, Spec &s)
|
||||
: internal::ArgFormatterBase<Impl, Char, Spec>(w, s) {}
|
||||
|
||||
/** Formats an argument of type ``bool``. */
|
||||
void visit_bool(bool value)
|
||||
{
|
||||
FormatSpec &fmt_spec = this->spec();
|
||||
Spec &fmt_spec = this->spec();
|
||||
if (fmt_spec.type_ != 's')
|
||||
return this->visit_any_int(value);
|
||||
fmt_spec.type_ = 0;
|
||||
@@ -264,7 +313,7 @@ public:
|
||||
/** Formats a character. */
|
||||
void visit_char(int value)
|
||||
{
|
||||
const FormatSpec &fmt_spec = this->spec();
|
||||
const Spec &fmt_spec = this->spec();
|
||||
BasicWriter<Char> &w = this->writer();
|
||||
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
|
||||
w.write_int(value, fmt_spec);
|
||||
@@ -323,13 +372,13 @@ public:
|
||||
|
||||
/** The default printf argument formatter. */
|
||||
template <typename Char>
|
||||
class PrintfArgFormatter
|
||||
: public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char>
|
||||
class PrintfArgFormatter :
|
||||
public BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>
|
||||
{
|
||||
public:
|
||||
/** Constructs an argument formatter object. */
|
||||
PrintfArgFormatter(BasicWriter<Char> &w, FormatSpec &s)
|
||||
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char>(w, s) {}
|
||||
: BasicPrintfArgFormatter<PrintfArgFormatter<Char>, Char, FormatSpec>(w, s) {}
|
||||
};
|
||||
|
||||
/** This template formats data and writes the output to a writer. */
|
||||
@@ -358,11 +407,11 @@ public:
|
||||
appropriate lifetimes.
|
||||
\endrst
|
||||
*/
|
||||
explicit PrintfFormatter(const ArgList &args, BasicWriter<Char> &w)
|
||||
: FormatterBase(args), writer_(w) {}
|
||||
explicit PrintfFormatter(const ArgList &al, BasicWriter<Char> &w)
|
||||
: FormatterBase(al), writer_(w) {}
|
||||
|
||||
/** Formats stored arguments and writes the output to the writer. */
|
||||
FMT_API void format(BasicCStringRef<Char> format_str);
|
||||
void format(BasicCStringRef<Char> format_str);
|
||||
};
|
||||
|
||||
template <typename Char, typename AF>
|
||||
@@ -399,7 +448,7 @@ internal::Arg PrintfFormatter<Char, AF>::get_arg(const Char *s,
|
||||
unsigned arg_index)
|
||||
{
|
||||
(void)s;
|
||||
const char *error = 0;
|
||||
const char *error = FMT_NULL;
|
||||
internal::Arg arg = arg_index == std::numeric_limits<unsigned>::max() ?
|
||||
next_arg(error) : FormatterBase::get_arg(arg_index - 1, error);
|
||||
if (error)
|
||||
@@ -486,6 +535,10 @@ void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
|
||||
++s;
|
||||
spec.precision_ = internal::PrecisionHandler().visit(get_arg(s));
|
||||
}
|
||||
else
|
||||
{
|
||||
spec.precision_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
using internal::Arg;
|
||||
@@ -538,6 +591,13 @@ void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
|
||||
if (!*s)
|
||||
FMT_THROW(FormatError("invalid format string"));
|
||||
spec.type_ = static_cast<char>(*s++);
|
||||
|
||||
if (spec.type_ == 's')
|
||||
{
|
||||
// set the format type to the default if 's' is specified
|
||||
spec.type_ = internal::DefaultType().visit(arg);
|
||||
}
|
||||
|
||||
if (arg.type <= Arg::LAST_INTEGER_TYPE)
|
||||
{
|
||||
// Normalize type.
|
||||
@@ -562,11 +622,17 @@ void PrintfFormatter<Char, AF>::format(BasicCStringRef<Char> format_str)
|
||||
write(writer_, start, s);
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
void printf(BasicWriter<Char> &w, BasicCStringRef<Char> format, ArgList args)
|
||||
inline void printf(Writer &w, CStringRef format, ArgList args)
|
||||
{
|
||||
PrintfFormatter<Char>(args, w).format(format);
|
||||
PrintfFormatter<char>(args, w).format(format);
|
||||
}
|
||||
FMT_VARIADIC(void, printf, Writer &, CStringRef)
|
||||
|
||||
inline void printf(WWriter &w, WCStringRef format, ArgList args)
|
||||
{
|
||||
PrintfFormatter<wchar_t>(args, w).format(format);
|
||||
}
|
||||
FMT_VARIADIC(void, printf, WWriter &, WCStringRef)
|
||||
|
||||
/**
|
||||
\rst
|
||||
@@ -619,6 +685,28 @@ inline int printf(CStringRef format, ArgList args)
|
||||
return fprintf(stdout, format, args);
|
||||
}
|
||||
FMT_VARIADIC(int, printf, CStringRef)
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to the stream *os*.
|
||||
|
||||
**Example**::
|
||||
|
||||
fprintf(cerr, "Don't %s!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
inline int fprintf(std::ostream &os, CStringRef format_str, ArgList args)
|
||||
{
|
||||
MemoryWriter w;
|
||||
printf(w, format_str, args);
|
||||
internal::write(os, w);
|
||||
return static_cast<int>(w.size());
|
||||
}
|
||||
FMT_VARIADIC(int, fprintf, std::ostream &, CStringRef)
|
||||
} // namespace fmt
|
||||
|
||||
#ifdef FMT_HEADER_ONLY
|
||||
# include "printf.cc"
|
||||
#endif
|
||||
|
||||
#endif // FMT_PRINTF_H_
|
||||
|
183
include/spdlog/fmt/bundled/time.h
Normal file
183
include/spdlog/fmt/bundled/time.h
Normal file
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
Formatting library for C++ - time formatting
|
||||
|
||||
Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
All rights reserved.
|
||||
|
||||
For the license information refer to format.h.
|
||||
*/
|
||||
|
||||
#ifndef FMT_TIME_H_
|
||||
#define FMT_TIME_H_
|
||||
|
||||
#include "format.h"
|
||||
#include <ctime>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4702) // unreachable code
|
||||
# pragma warning(disable: 4996) // "deprecated" functions
|
||||
#endif
|
||||
|
||||
namespace fmt
|
||||
{
|
||||
template <typename ArgFormatter>
|
||||
void format_arg(BasicFormatter<char, ArgFormatter> &f,
|
||||
const char *&format_str, const std::tm &tm)
|
||||
{
|
||||
if (*format_str == ':')
|
||||
++format_str;
|
||||
const char *end = format_str;
|
||||
while (*end && *end != '}')
|
||||
++end;
|
||||
if (*end != '}')
|
||||
FMT_THROW(FormatError("missing '}' in format string"));
|
||||
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format;
|
||||
format.append(format_str, end + 1);
|
||||
format[format.size() - 1] = '\0';
|
||||
Buffer<char> &buffer = f.writer().buffer();
|
||||
std::size_t start = buffer.size();
|
||||
for (;;)
|
||||
{
|
||||
std::size_t size = buffer.capacity() - start;
|
||||
std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm);
|
||||
if (count != 0)
|
||||
{
|
||||
buffer.resize(start + count);
|
||||
break;
|
||||
}
|
||||
if (size >= format.size() * 256)
|
||||
{
|
||||
// If the buffer is 256 times larger than the format string, assume
|
||||
// that `strftime` gives an empty result. There doesn't seem to be a
|
||||
// better way to distinguish the two cases:
|
||||
// https://github.com/fmtlib/fmt/issues/367
|
||||
break;
|
||||
}
|
||||
const std::size_t MIN_GROWTH = 10;
|
||||
buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
||||
}
|
||||
format_str = end + 1;
|
||||
}
|
||||
|
||||
namespace internal
|
||||
{
|
||||
inline Null<> localtime_r(...)
|
||||
{
|
||||
return Null<>();
|
||||
}
|
||||
inline Null<> localtime_s(...)
|
||||
{
|
||||
return Null<>();
|
||||
}
|
||||
inline Null<> gmtime_r(...)
|
||||
{
|
||||
return Null<>();
|
||||
}
|
||||
inline Null<> gmtime_s(...)
|
||||
{
|
||||
return Null<>();
|
||||
}
|
||||
}
|
||||
|
||||
// Thread-safe replacement for std::localtime
|
||||
inline std::tm localtime(std::time_t time)
|
||||
{
|
||||
struct LocalTime
|
||||
{
|
||||
std::time_t time_;
|
||||
std::tm tm_;
|
||||
|
||||
LocalTime(std::time_t t): time_(t) {}
|
||||
|
||||
bool run()
|
||||
{
|
||||
using namespace fmt::internal;
|
||||
return handle(localtime_r(&time_, &tm_));
|
||||
}
|
||||
|
||||
bool handle(std::tm *tm)
|
||||
{
|
||||
return tm != FMT_NULL;
|
||||
}
|
||||
|
||||
bool handle(internal::Null<>)
|
||||
{
|
||||
using namespace fmt::internal;
|
||||
return fallback(localtime_s(&tm_, &time_));
|
||||
}
|
||||
|
||||
bool fallback(int res)
|
||||
{
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
bool fallback(internal::Null<>)
|
||||
{
|
||||
using namespace fmt::internal;
|
||||
std::tm *tm = std::localtime(&time_);
|
||||
if (tm) tm_ = *tm;
|
||||
return tm != FMT_NULL;
|
||||
}
|
||||
};
|
||||
LocalTime lt(time);
|
||||
if (lt.run())
|
||||
return lt.tm_;
|
||||
// Too big time values may be unsupported.
|
||||
FMT_THROW(fmt::FormatError("time_t value out of range"));
|
||||
return std::tm();
|
||||
}
|
||||
|
||||
// Thread-safe replacement for std::gmtime
|
||||
inline std::tm gmtime(std::time_t time)
|
||||
{
|
||||
struct GMTime
|
||||
{
|
||||
std::time_t time_;
|
||||
std::tm tm_;
|
||||
|
||||
GMTime(std::time_t t): time_(t) {}
|
||||
|
||||
bool run()
|
||||
{
|
||||
using namespace fmt::internal;
|
||||
return handle(gmtime_r(&time_, &tm_));
|
||||
}
|
||||
|
||||
bool handle(std::tm *tm)
|
||||
{
|
||||
return tm != FMT_NULL;
|
||||
}
|
||||
|
||||
bool handle(internal::Null<>)
|
||||
{
|
||||
using namespace fmt::internal;
|
||||
return fallback(gmtime_s(&tm_, &time_));
|
||||
}
|
||||
|
||||
bool fallback(int res)
|
||||
{
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
bool fallback(internal::Null<>)
|
||||
{
|
||||
std::tm *tm = std::gmtime(&time_);
|
||||
if (tm != FMT_NULL) tm_ = *tm;
|
||||
return tm != FMT_NULL;
|
||||
}
|
||||
};
|
||||
GMTime gt(time);
|
||||
if (gt.run())
|
||||
return gt.tm_;
|
||||
// Too big time values may be unsupported.
|
||||
FMT_THROW(fmt::FormatError("time_t value out of range"));
|
||||
return std::tm();
|
||||
}
|
||||
} //namespace fmt
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#endif // FMT_TIME_H_
|
@@ -18,11 +18,17 @@
|
||||
#ifndef FMT_USE_WINDOWS_H
|
||||
#define FMT_USE_WINDOWS_H 0
|
||||
#endif
|
||||
#include <spdlog/fmt/bundled/format.h>
|
||||
#include "bundled/format.h"
|
||||
#if defined(SPDLOG_FMT_PRINTF)
|
||||
#include "bundled/printf.h"
|
||||
#endif
|
||||
|
||||
#else //external fmtlib
|
||||
|
||||
#include <fmt/format.h>
|
||||
#if defined(SPDLOG_FMT_PRINTF)
|
||||
#include <fmt/printf.h>
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
@@ -8,8 +8,8 @@
|
||||
// include external or bundled copy of fmtlib's ostream support
|
||||
//
|
||||
#if !defined(SPDLOG_FMT_EXTERNAL)
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
#include <spdlog/fmt/bundled/ostream.h>
|
||||
#include "fmt.h"
|
||||
#include "bundled/ostream.h"
|
||||
#else
|
||||
#include <fmt/ostream.h>
|
||||
#endif
|
||||
|
@@ -5,7 +5,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/details/log_msg.h>
|
||||
#include "details/log_msg.h"
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
@@ -25,21 +25,23 @@ public:
|
||||
virtual void format(details::log_msg& msg) = 0;
|
||||
};
|
||||
|
||||
class pattern_formatter : public formatter
|
||||
class pattern_formatter SPDLOG_FINAL : public formatter
|
||||
{
|
||||
|
||||
public:
|
||||
explicit pattern_formatter(const std::string& pattern);
|
||||
explicit pattern_formatter(const std::string& pattern, pattern_time_type pattern_time = pattern_time_type::local);
|
||||
pattern_formatter(const pattern_formatter&) = delete;
|
||||
pattern_formatter& operator=(const pattern_formatter&) = delete;
|
||||
void format(details::log_msg& msg) override;
|
||||
private:
|
||||
const std::string _pattern;
|
||||
const pattern_time_type _pattern_time;
|
||||
std::vector<std::unique_ptr<details::flag_formatter>> _formatters;
|
||||
std::tm get_time(details::log_msg& msg);
|
||||
void handle_flag(char flag);
|
||||
void compile_pattern(const std::string& pattern);
|
||||
};
|
||||
}
|
||||
|
||||
#include <spdlog/details/pattern_formatter_impl.h>
|
||||
#include "details/pattern_formatter_impl.h"
|
||||
|
||||
|
@@ -5,21 +5,20 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
// Thread safe logger
|
||||
// Thread safe logger (except for set_pattern(..), set_formatter(..) and set_error_handler())
|
||||
// Has name, log level, vector of std::shared sink pointers and formatter
|
||||
// Upon each log write the logger:
|
||||
// 1. Checks if its log level is enough to log the message
|
||||
// 2. Format the message using the formatter function
|
||||
// 3. Pass the formatted message to its sinks to performa the actual logging
|
||||
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
#include <spdlog/common.h>
|
||||
#include "sinks/base_sink.h"
|
||||
#include "common.h"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
|
||||
@@ -38,12 +37,24 @@ public:
|
||||
|
||||
template <typename... Args> void log(level::level_enum lvl, const char* fmt, const Args&... args);
|
||||
template <typename... Args> void log(level::level_enum lvl, const char* msg);
|
||||
template <typename... Args> void trace(const char* fmt, const Args&... args);
|
||||
template <typename... Args> void debug(const char* fmt, const Args&... args);
|
||||
template <typename... Args> void info(const char* fmt, const Args&... args);
|
||||
template <typename... Args> void warn(const char* fmt, const Args&... args);
|
||||
template <typename... Args> void error(const char* fmt, const Args&... args);
|
||||
template <typename... Args> void critical(const char* fmt, const Args&... args);
|
||||
template <typename Arg1, typename... Args> void trace(const char* fmt, const Arg1&, const Args&... args);
|
||||
template <typename Arg1, typename... Args> void debug(const char* fmt, const Arg1&, const Args&... args);
|
||||
template <typename Arg1, typename... Args> void info(const char* fmt, const Arg1&, const Args&... args);
|
||||
template <typename Arg1, typename... Args> void warn(const char* fmt, const Arg1&, const Args&... args);
|
||||
template <typename Arg1, typename... Args> void error(const char* fmt, const Arg1&, const Args&... args);
|
||||
template <typename Arg1, typename... Args> void critical(const char* fmt, const Arg1&, const Args&... args);
|
||||
|
||||
|
||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
template <typename... Args> void log(level::level_enum lvl, const wchar_t* msg);
|
||||
template <typename... Args> void log(level::level_enum lvl, const wchar_t* fmt, const Args&... args);
|
||||
template <typename... Args> void trace(const wchar_t* fmt, const Args&... args);
|
||||
template <typename... Args> void debug(const wchar_t* fmt, const Args&... args);
|
||||
template <typename... Args> void info(const wchar_t* fmt, const Args&... args);
|
||||
template <typename... Args> void warn(const wchar_t* fmt, const Args&... args);
|
||||
template <typename... Args> void error(const wchar_t* fmt, const Args&... args);
|
||||
template <typename... Args> void critical(const wchar_t* fmt, const Args&... args);
|
||||
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
|
||||
template <typename T> void log(level::level_enum lvl, const T&);
|
||||
template <typename T> void trace(const T&);
|
||||
@@ -53,31 +64,47 @@ public:
|
||||
template <typename T> void error(const T&);
|
||||
template <typename T> void critical(const T&);
|
||||
|
||||
|
||||
bool should_log(level::level_enum) const;
|
||||
void set_level(level::level_enum);
|
||||
level::level_enum level() const;
|
||||
const std::string& name() const;
|
||||
void set_pattern(const std::string&);
|
||||
void set_pattern(const std::string&, pattern_time_type = pattern_time_type::local);
|
||||
void set_formatter(formatter_ptr);
|
||||
|
||||
// automatically call flush() if message level >= log_level
|
||||
void flush_on(level::level_enum log_level);
|
||||
|
||||
virtual void flush();
|
||||
|
||||
const std::vector<sink_ptr>& sinks() const;
|
||||
|
||||
// error handler
|
||||
virtual void set_error_handler(log_err_handler);
|
||||
virtual log_err_handler error_handler();
|
||||
|
||||
protected:
|
||||
virtual void _sink_it(details::log_msg&);
|
||||
virtual void _set_pattern(const std::string&);
|
||||
virtual void _set_pattern(const std::string&, pattern_time_type);
|
||||
virtual void _set_formatter(formatter_ptr);
|
||||
|
||||
// default error handler: print the error to stderr with the max rate of 1 message/minute
|
||||
virtual void _default_err_handler(const std::string &msg);
|
||||
|
||||
// return true if the given message level should trigger a flush
|
||||
bool _should_flush_on(const details::log_msg&);
|
||||
|
||||
// increment the message count (only if defined(SPDLOG_ENABLE_MESSAGE_COUNTER))
|
||||
void _incr_msg_counter(details::log_msg &msg);
|
||||
|
||||
const std::string _name;
|
||||
std::vector<sink_ptr> _sinks;
|
||||
formatter_ptr _formatter;
|
||||
spdlog::level_t _level;
|
||||
spdlog::level_t _flush_level;
|
||||
log_err_handler _err_handler;
|
||||
std::atomic<time_t> _last_err_time;
|
||||
std::atomic<size_t> _msg_counter;
|
||||
};
|
||||
}
|
||||
|
||||
#include <spdlog/details/logger_impl.h>
|
||||
|
||||
|
||||
#include "details/logger_impl.h"
|
||||
|
@@ -7,51 +7,57 @@
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
|
||||
#include <android/log.h>
|
||||
#include "sink.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <android/log.h>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#if !defined(SPDLOG_ANDROID_RETRIES)
|
||||
#define SPDLOG_ANDROID_RETRIES 2
|
||||
#endif
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace sinks
|
||||
{
|
||||
|
||||
/*
|
||||
* Android sink (logging using __android_log_write)
|
||||
* __android_log_write is thread-safe. No lock is needed.
|
||||
*/
|
||||
template<class Mutex>
|
||||
class base_android_sink : public base_sink < Mutex >
|
||||
class android_sink : public sink
|
||||
{
|
||||
public:
|
||||
explicit base_android_sink(std::string tag="spdlog"): _tag(tag)
|
||||
explicit android_sink(const std::string& tag = "spdlog", bool use_raw_msg = false): _tag(tag), _use_raw_msg(use_raw_msg) {}
|
||||
|
||||
void log(const details::log_msg& msg) override
|
||||
{
|
||||
const android_LogPriority priority = convert_to_android(msg.level);
|
||||
const char *msg_output = (_use_raw_msg ? msg.raw.c_str() : msg.formatted.c_str());
|
||||
|
||||
// See system/core/liblog/logger_write.c for explanation of return value
|
||||
int ret = __android_log_write(priority, _tag.c_str(), msg_output);
|
||||
int retry_count = 0;
|
||||
while ((ret == -11/*EAGAIN*/) && (retry_count < SPDLOG_ANDROID_RETRIES))
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
ret = __android_log_write(priority, _tag.c_str(), msg_output);
|
||||
retry_count++;
|
||||
}
|
||||
|
||||
if (ret < 0)
|
||||
{
|
||||
throw spdlog_ex("__android_log_write() failed", ret);
|
||||
}
|
||||
}
|
||||
|
||||
void flush() override
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
void _sink_it(const details::log_msg& msg) override
|
||||
{
|
||||
const android_LogPriority priority = convert_to_android(msg.level);
|
||||
const int expected_size = msg.formatted.size();
|
||||
const int size = __android_log_write(
|
||||
priority, _tag.c_str(), msg.formatted.c_str()
|
||||
);
|
||||
if (size > expected_size)
|
||||
{
|
||||
// Will write a little bit more than original message
|
||||
}
|
||||
else
|
||||
{
|
||||
throw spdlog_ex("Send to Android logcat failed");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static android_LogPriority convert_to_android(spdlog::level::level_enum level)
|
||||
{
|
||||
@@ -63,29 +69,21 @@ private:
|
||||
return ANDROID_LOG_DEBUG;
|
||||
case spdlog::level::info:
|
||||
return ANDROID_LOG_INFO;
|
||||
case spdlog::level::notice:
|
||||
return ANDROID_LOG_INFO;
|
||||
case spdlog::level::warn:
|
||||
return ANDROID_LOG_WARN;
|
||||
case spdlog::level::err:
|
||||
return ANDROID_LOG_ERROR;
|
||||
case spdlog::level::critical:
|
||||
return ANDROID_LOG_FATAL;
|
||||
case spdlog::level::alert:
|
||||
return ANDROID_LOG_FATAL;
|
||||
case spdlog::level::emerg:
|
||||
return ANDROID_LOG_FATAL;
|
||||
default:
|
||||
throw spdlog_ex("Incorrect level value");
|
||||
return ANDROID_LOG_DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
std::string _tag;
|
||||
bool _use_raw_msg;
|
||||
};
|
||||
|
||||
typedef base_android_sink<std::mutex> android_sink_mt;
|
||||
typedef base_android_sink<details::null_mutex> android_sink_st;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,12 +1,13 @@
|
||||
//
|
||||
// Copyright(c) 2016 Kevin M. Godby (a modified version by spdlog).
|
||||
// Copyright(c) 2017 spdlog authors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
#include <spdlog/common.h>
|
||||
#include "base_sink.h"
|
||||
#include "../common.h"
|
||||
#include "../details/os.h"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
@@ -17,99 +18,115 @@ namespace sinks
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The ansi_color_sink is a decorator around another sink and prefixes
|
||||
* the output with an ANSI escape sequence color code depending on the severity
|
||||
* This sink prefixes the output with an ANSI escape sequence color code depending on the severity
|
||||
* of the message.
|
||||
* If no color terminal detected, omit the escape codes.
|
||||
*/
|
||||
class ansicolor_sink : public sink
|
||||
template <class Mutex>
|
||||
class ansicolor_sink: public base_sink<Mutex>
|
||||
{
|
||||
public:
|
||||
ansicolor_sink(sink_ptr wrapped_sink);
|
||||
virtual ~ansicolor_sink();
|
||||
ansicolor_sink(FILE* file): target_file_(file)
|
||||
{
|
||||
should_do_colors_ = details::os::in_terminal(file) && details::os::is_color_terminal();
|
||||
colors_[level::trace] = cyan;
|
||||
colors_[level::debug] = cyan;
|
||||
colors_[level::info] = reset;
|
||||
colors_[level::warn] = yellow + bold;
|
||||
colors_[level::err] = red + bold;
|
||||
colors_[level::critical] = bold + on_red;
|
||||
colors_[level::off] = reset;
|
||||
}
|
||||
virtual ~ansicolor_sink()
|
||||
{
|
||||
_flush();
|
||||
}
|
||||
|
||||
ansicolor_sink(const ansicolor_sink& other) = delete;
|
||||
ansicolor_sink& operator=(const ansicolor_sink& other) = delete;
|
||||
|
||||
virtual void log(const details::log_msg& msg) override;
|
||||
virtual void flush() override;
|
||||
|
||||
void set_color(level::level_enum level, const std::string& color);
|
||||
void set_color(level::level_enum color_level, const std::string& color)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
|
||||
colors_[color_level] = color;
|
||||
}
|
||||
|
||||
/// Formatting codes
|
||||
const std::string reset = "\033[00m";
|
||||
const std::string bold = "\033[1m";
|
||||
const std::string dark = "\033[2m";
|
||||
const std::string underline = "\033[4m";
|
||||
const std::string blink = "\033[5m";
|
||||
const std::string reverse = "\033[7m";
|
||||
const std::string concealed = "\033[8m";
|
||||
const std::string reset = "\033[00m";
|
||||
const std::string bold = "\033[1m";
|
||||
const std::string dark = "\033[2m";
|
||||
const std::string underline = "\033[4m";
|
||||
const std::string blink = "\033[5m";
|
||||
const std::string reverse = "\033[7m";
|
||||
const std::string concealed = "\033[8m";
|
||||
|
||||
// Foreground colors
|
||||
const std::string grey = "\033[30m";
|
||||
const std::string red = "\033[31m";
|
||||
const std::string green = "\033[32m";
|
||||
const std::string yellow = "\033[33m";
|
||||
const std::string blue = "\033[34m";
|
||||
const std::string magenta = "\033[35m";
|
||||
const std::string cyan = "\033[36m";
|
||||
const std::string white = "\033[37m";
|
||||
const std::string grey = "\033[30m";
|
||||
const std::string red = "\033[31m";
|
||||
const std::string green = "\033[32m";
|
||||
const std::string yellow = "\033[33m";
|
||||
const std::string blue = "\033[34m";
|
||||
const std::string magenta = "\033[35m";
|
||||
const std::string cyan = "\033[36m";
|
||||
const std::string white = "\033[37m";
|
||||
|
||||
/// Background colors
|
||||
const std::string on_grey = "\033[40m";
|
||||
const std::string on_red = "\033[41m";
|
||||
const std::string on_green = "\033[42m";
|
||||
const std::string on_yellow = "\033[43m";
|
||||
const std::string on_blue = "\033[44m";
|
||||
const std::string on_grey = "\033[40m";
|
||||
const std::string on_red = "\033[41m";
|
||||
const std::string on_green = "\033[42m";
|
||||
const std::string on_yellow = "\033[43m";
|
||||
const std::string on_blue = "\033[44m";
|
||||
const std::string on_magenta = "\033[45m";
|
||||
const std::string on_cyan = "\033[46m";
|
||||
const std::string on_white = "\033[47m";
|
||||
|
||||
const std::string on_cyan = "\033[46m";
|
||||
const std::string on_white = "\033[47m";
|
||||
|
||||
protected:
|
||||
sink_ptr sink_;
|
||||
virtual void _sink_it(const details::log_msg& msg) override
|
||||
{
|
||||
// Wrap the originally formatted message in color codes.
|
||||
// If color is not supported in the terminal, log as is instead.
|
||||
if (should_do_colors_)
|
||||
{
|
||||
const std::string& prefix = colors_[msg.level];
|
||||
fwrite(prefix.data(), sizeof(char), prefix.size(), target_file_);
|
||||
fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), target_file_);
|
||||
fwrite(reset.data(), sizeof(char), reset.size(), target_file_);
|
||||
}
|
||||
else
|
||||
{
|
||||
fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), target_file_);
|
||||
}
|
||||
_flush();
|
||||
}
|
||||
|
||||
void _flush() override
|
||||
{
|
||||
fflush(target_file_);
|
||||
}
|
||||
FILE* target_file_;
|
||||
bool should_do_colors_;
|
||||
std::map<level::level_enum, std::string> colors_;
|
||||
};
|
||||
|
||||
inline ansicolor_sink::ansicolor_sink(sink_ptr wrapped_sink) : sink_(wrapped_sink)
|
||||
{
|
||||
colors_[level::trace] = cyan;
|
||||
colors_[level::debug] = cyan;
|
||||
colors_[level::info] = bold;
|
||||
colors_[level::warn] = yellow + bold;
|
||||
colors_[level::err] = red + bold;
|
||||
colors_[level::critical] = bold + on_red;
|
||||
colors_[level::off] = reset;
|
||||
}
|
||||
|
||||
inline void ansicolor_sink::log(const details::log_msg& msg)
|
||||
template<class Mutex>
|
||||
class ansicolor_stdout_sink: public ansicolor_sink<Mutex>
|
||||
{
|
||||
// Wrap the originally formatted message in color codes
|
||||
const std::string& prefix = colors_[msg.level];
|
||||
const std::string& s = msg.formatted.str();
|
||||
const std::string& suffix = reset;
|
||||
details::log_msg m;
|
||||
m.level = msg.level;
|
||||
m.logger_name = msg.logger_name;
|
||||
m.time = msg.time;
|
||||
m.thread_id = msg.thread_id;
|
||||
m.formatted << prefix << s << suffix;
|
||||
sink_->log(m);
|
||||
}
|
||||
public:
|
||||
ansicolor_stdout_sink(): ansicolor_sink<Mutex>(stdout)
|
||||
{}
|
||||
};
|
||||
|
||||
inline void ansicolor_sink::flush()
|
||||
template<class Mutex>
|
||||
class ansicolor_stderr_sink: public ansicolor_sink<Mutex>
|
||||
{
|
||||
sink_->flush();
|
||||
}
|
||||
public:
|
||||
ansicolor_stderr_sink(): ansicolor_sink<Mutex>(stderr)
|
||||
{}
|
||||
};
|
||||
|
||||
inline void ansicolor_sink::set_color(level::level_enum level, const std::string& color)
|
||||
{
|
||||
colors_[level] = color;
|
||||
}
|
||||
typedef ansicolor_stdout_sink<std::mutex> ansicolor_stdout_sink_mt;
|
||||
typedef ansicolor_stdout_sink<details::null_mutex> ansicolor_stdout_sink_st;
|
||||
|
||||
inline ansicolor_sink::~ansicolor_sink()
|
||||
{
|
||||
flush();
|
||||
}
|
||||
typedef ansicolor_stderr_sink<std::mutex> ansicolor_stderr_sink_mt;
|
||||
typedef ansicolor_stderr_sink<details::null_mutex> ansicolor_stderr_sink_st;
|
||||
|
||||
} // namespace sinks
|
||||
} // namespace spdlog
|
||||
|
@@ -5,15 +5,15 @@
|
||||
|
||||
#pragma once
|
||||
//
|
||||
// base sink templated over a mutex (either dummy or realy)
|
||||
// concrete implementation should only overrid the _sink_it method.
|
||||
// all locking is taken care of here so no locking needed by the implementors..
|
||||
// base sink templated over a mutex (either dummy or real)
|
||||
// concrete implementation should only override the _sink_it method.
|
||||
// all locking is taken care of here so no locking needed by the implementers..
|
||||
//
|
||||
|
||||
#include <spdlog/sinks/sink.h>
|
||||
#include <spdlog/formatter.h>
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/log_msg.h>
|
||||
#include "sink.h"
|
||||
#include "../formatter.h"
|
||||
#include "../common.h"
|
||||
#include "../details/log_msg.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
@@ -31,14 +31,20 @@ public:
|
||||
base_sink(const base_sink&) = delete;
|
||||
base_sink& operator=(const base_sink&) = delete;
|
||||
|
||||
void log(const details::log_msg& msg) override
|
||||
void log(const details::log_msg& msg) SPDLOG_FINAL override
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
_sink_it(msg);
|
||||
}
|
||||
void flush() SPDLOG_FINAL override
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
_flush();
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void _sink_it(const details::log_msg& msg) = 0;
|
||||
virtual void _flush() = 0;
|
||||
Mutex _mutex;
|
||||
};
|
||||
}
|
||||
|
@@ -5,16 +5,18 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/details/log_msg.h>
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
#include <spdlog/sinks/sink.h>
|
||||
#include "../details/log_msg.h"
|
||||
#include "../details/null_mutex.h"
|
||||
#include "base_sink.h"
|
||||
#include "sink.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
// Distribution sink (mux). Stores a vector of sinks which get called when log is called
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace sinks
|
||||
@@ -29,40 +31,38 @@ public:
|
||||
virtual ~dist_sink() = default;
|
||||
|
||||
protected:
|
||||
void _sink_it(const details::log_msg& msg) override
|
||||
{
|
||||
for (auto iter = _sinks.begin(); iter != _sinks.end(); iter++)
|
||||
(*iter)->log(msg);
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<sink>> _sinks;
|
||||
|
||||
public:
|
||||
void flush() override
|
||||
void _sink_it(const details::log_msg& msg) override
|
||||
{
|
||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
|
||||
for (auto iter = _sinks.begin(); iter != _sinks.end(); iter++)
|
||||
(*iter)->flush();
|
||||
for (auto &sink : _sinks)
|
||||
{
|
||||
if( sink->should_log( msg.level))
|
||||
{
|
||||
sink->log(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _flush() override
|
||||
{
|
||||
for (auto &sink : _sinks)
|
||||
sink->flush();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
|
||||
void add_sink(std::shared_ptr<sink> sink)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
|
||||
if (sink &&
|
||||
_sinks.end() == std::find(_sinks.begin(), _sinks.end(), sink))
|
||||
{
|
||||
_sinks.push_back(sink);
|
||||
}
|
||||
_sinks.push_back(sink);
|
||||
}
|
||||
|
||||
void remove_sink(std::shared_ptr<sink> sink)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
|
||||
auto pos = std::find(_sinks.begin(), _sinks.end(), sink);
|
||||
if (pos != _sinks.end())
|
||||
{
|
||||
_sinks.erase(pos);
|
||||
}
|
||||
_sinks.erase(std::remove(_sinks.begin(), _sinks.end(), sink), _sinks.end());
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -5,10 +5,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
#include <spdlog/details/file_helper.h>
|
||||
#include <spdlog/fmt/fmt.h>
|
||||
#include "base_sink.h"
|
||||
#include "../details/null_mutex.h"
|
||||
#include "../details/file_helper.h"
|
||||
#include "../fmt/fmt.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
@@ -23,59 +23,76 @@ namespace spdlog
|
||||
namespace sinks
|
||||
{
|
||||
/*
|
||||
* Trivial file sink with single file as target
|
||||
*/
|
||||
* Trivial file sink with single file as target
|
||||
*/
|
||||
template<class Mutex>
|
||||
class simple_file_sink : public base_sink < Mutex >
|
||||
class simple_file_sink SPDLOG_FINAL : public base_sink < Mutex >
|
||||
{
|
||||
public:
|
||||
explicit simple_file_sink(const filename_t &filename,
|
||||
bool force_flush = false) :
|
||||
_file_helper(force_flush)
|
||||
explicit simple_file_sink(const filename_t &filename, bool truncate = false):_force_flush(false)
|
||||
{
|
||||
_file_helper.open(filename);
|
||||
_file_helper.open(filename, truncate);
|
||||
}
|
||||
void flush() override
|
||||
|
||||
void set_force_flush(bool force_flush)
|
||||
{
|
||||
_file_helper.flush();
|
||||
_force_flush = force_flush;
|
||||
}
|
||||
|
||||
protected:
|
||||
void _sink_it(const details::log_msg& msg) override
|
||||
{
|
||||
_file_helper.write(msg);
|
||||
if(_force_flush)
|
||||
_file_helper.flush();
|
||||
}
|
||||
void _flush() override
|
||||
{
|
||||
_file_helper.flush();
|
||||
}
|
||||
private:
|
||||
details::file_helper _file_helper;
|
||||
bool _force_flush;
|
||||
};
|
||||
|
||||
typedef simple_file_sink<std::mutex> simple_file_sink_mt;
|
||||
typedef simple_file_sink<details::null_mutex> simple_file_sink_st;
|
||||
|
||||
/*
|
||||
* Rotating file sink based on size
|
||||
*/
|
||||
* Rotating file sink based on size
|
||||
*/
|
||||
template<class Mutex>
|
||||
class rotating_file_sink : public base_sink < Mutex >
|
||||
class rotating_file_sink SPDLOG_FINAL : public base_sink < Mutex >
|
||||
{
|
||||
public:
|
||||
rotating_file_sink(const filename_t &base_filename, const filename_t &extension,
|
||||
std::size_t max_size, std::size_t max_files,
|
||||
bool force_flush = false) :
|
||||
rotating_file_sink(const filename_t &base_filename,
|
||||
std::size_t max_size, std::size_t max_files) :
|
||||
_base_filename(base_filename),
|
||||
_extension(extension),
|
||||
_max_size(max_size),
|
||||
_max_files(max_files),
|
||||
_current_size(0),
|
||||
_file_helper(force_flush)
|
||||
_file_helper()
|
||||
{
|
||||
_file_helper.open(calc_filename(_base_filename, 0, _extension));
|
||||
_file_helper.open(calc_filename(_base_filename, 0));
|
||||
_current_size = _file_helper.size(); //expensive. called only once
|
||||
}
|
||||
|
||||
void flush() override
|
||||
// calc filename according to index and file extension if exists.
|
||||
// e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt".
|
||||
static filename_t calc_filename(const filename_t& filename, std::size_t index)
|
||||
{
|
||||
_file_helper.flush();
|
||||
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
|
||||
if (index)
|
||||
{
|
||||
filename_t basename, ext;
|
||||
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
|
||||
w.write(SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext);
|
||||
}
|
||||
else
|
||||
{
|
||||
w.write(SPDLOG_FILENAME_T("{}"), filename);
|
||||
}
|
||||
return w.str();
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -90,31 +107,26 @@ protected:
|
||||
_file_helper.write(msg);
|
||||
}
|
||||
|
||||
private:
|
||||
static filename_t calc_filename(const filename_t& filename, std::size_t index, const filename_t& extension)
|
||||
void _flush() override
|
||||
{
|
||||
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
|
||||
if (index)
|
||||
w.write(SPDLOG_FILENAME_T("{}.{}.{}"), filename, index, extension);
|
||||
else
|
||||
w.write(SPDLOG_FILENAME_T("{}.{}"), filename, extension);
|
||||
return w.str();
|
||||
_file_helper.flush();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
// Rotate files:
|
||||
// log.txt -> log.1.txt
|
||||
// log.1.txt -> log2.txt
|
||||
// log.2.txt -> log3.txt
|
||||
// log.1.txt -> log.2.txt
|
||||
// log.2.txt -> log.3.txt
|
||||
// log.3.txt -> delete
|
||||
|
||||
void _rotate()
|
||||
{
|
||||
using details::os::filename_to_str;
|
||||
_file_helper.close();
|
||||
for (auto i = _max_files; i > 0; --i)
|
||||
{
|
||||
filename_t src = calc_filename(_base_filename, i - 1, _extension);
|
||||
filename_t target = calc_filename(_base_filename, i, _extension);
|
||||
filename_t src = calc_filename(_base_filename, i - 1);
|
||||
filename_t target = calc_filename(_base_filename, i);
|
||||
|
||||
if (details::file_helper::file_exists(target))
|
||||
{
|
||||
@@ -131,7 +143,6 @@ private:
|
||||
_file_helper.reopen(true);
|
||||
}
|
||||
filename_t _base_filename;
|
||||
filename_t _extension;
|
||||
std::size_t _max_size;
|
||||
std::size_t _max_files;
|
||||
std::size_t _current_size;
|
||||
@@ -142,76 +153,77 @@ typedef rotating_file_sink<std::mutex> rotating_file_sink_mt;
|
||||
typedef rotating_file_sink<details::null_mutex>rotating_file_sink_st;
|
||||
|
||||
/*
|
||||
* Default generator of daily log file names.
|
||||
*/
|
||||
* Default generator of daily log file names.
|
||||
*/
|
||||
struct default_daily_file_name_calculator
|
||||
{
|
||||
//Create filename for the form basename.YYYY-MM-DD_hh-mm.extension
|
||||
static filename_t calc_filename(const filename_t& basename, const filename_t& extension)
|
||||
// Create filename for the form filename.YYYY-MM-DD_hh-mm.ext
|
||||
static filename_t calc_filename(const filename_t& filename)
|
||||
{
|
||||
std::tm tm = spdlog::details::os::localtime();
|
||||
filename_t basename, ext;
|
||||
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
|
||||
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
|
||||
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}.{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, extension);
|
||||
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, ext);
|
||||
return w.str();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Generator of daily log file names in format basename.YYYY-MM-DD.extension
|
||||
*/
|
||||
* Generator of daily log file names in format basename.YYYY-MM-DD.ext
|
||||
*/
|
||||
struct dateonly_daily_file_name_calculator
|
||||
{
|
||||
//Create filename for the form basename.YYYY-MM-DD.extension
|
||||
static filename_t calc_filename(const filename_t& basename, const filename_t& extension)
|
||||
// Create filename for the form basename.YYYY-MM-DD
|
||||
static filename_t calc_filename(const filename_t& filename)
|
||||
{
|
||||
std::tm tm = spdlog::details::os::localtime();
|
||||
filename_t basename, ext;
|
||||
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
|
||||
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
|
||||
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}.{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, extension);
|
||||
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, ext);
|
||||
return w.str();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Rotating file sink based on date. rotates at midnight
|
||||
*/
|
||||
* Rotating file sink based on date. rotates at midnight
|
||||
*/
|
||||
template<class Mutex, class FileNameCalc = default_daily_file_name_calculator>
|
||||
class daily_file_sink :public base_sink < Mutex >
|
||||
class daily_file_sink SPDLOG_FINAL :public base_sink < Mutex >
|
||||
{
|
||||
public:
|
||||
//create daily file sink which rotates on given time
|
||||
daily_file_sink(
|
||||
const filename_t& base_filename,
|
||||
const filename_t& extension,
|
||||
int rotation_hour,
|
||||
int rotation_minute,
|
||||
bool force_flush = false) : _base_filename(base_filename),
|
||||
_extension(extension),
|
||||
int rotation_minute) : _base_filename(base_filename),
|
||||
_rotation_h(rotation_hour),
|
||||
_rotation_m(rotation_minute),
|
||||
_file_helper(force_flush)
|
||||
_rotation_m(rotation_minute)
|
||||
{
|
||||
if (rotation_hour < 0 || rotation_hour > 23 || rotation_minute < 0 || rotation_minute > 59)
|
||||
throw spdlog_ex("daily_file_sink: Invalid rotation time in ctor");
|
||||
_rotation_tp = _next_rotation_tp();
|
||||
_file_helper.open(FileNameCalc::calc_filename(_base_filename, _extension));
|
||||
_file_helper.open(FileNameCalc::calc_filename(_base_filename));
|
||||
}
|
||||
|
||||
void flush() override
|
||||
{
|
||||
_file_helper.flush();
|
||||
}
|
||||
|
||||
protected:
|
||||
void _sink_it(const details::log_msg& msg) override
|
||||
{
|
||||
if (std::chrono::system_clock::now() >= _rotation_tp)
|
||||
{
|
||||
_file_helper.open(FileNameCalc::calc_filename(_base_filename, _extension));
|
||||
_file_helper.open(FileNameCalc::calc_filename(_base_filename));
|
||||
_rotation_tp = _next_rotation_tp();
|
||||
}
|
||||
_file_helper.write(msg);
|
||||
}
|
||||
|
||||
void _flush() override
|
||||
{
|
||||
_file_helper.flush();
|
||||
}
|
||||
|
||||
private:
|
||||
std::chrono::system_clock::time_point _next_rotation_tp()
|
||||
{
|
||||
@@ -229,7 +241,6 @@ private:
|
||||
}
|
||||
|
||||
filename_t _base_filename;
|
||||
filename_t _extension;
|
||||
int _rotation_h;
|
||||
int _rotation_m;
|
||||
std::chrono::system_clock::time_point _rotation_tp;
|
||||
|
@@ -5,12 +5,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
#include "base_sink.h"
|
||||
#include "../details/null_mutex.h"
|
||||
|
||||
#include <WinBase.h>
|
||||
#include <winbase.h>
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
@@ -30,15 +30,16 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void flush() override
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
void _sink_it(const details::log_msg& msg) override
|
||||
{
|
||||
OutputDebugStringA(msg.formatted.c_str());
|
||||
}
|
||||
|
||||
void _flush() override
|
||||
{}
|
||||
};
|
||||
|
||||
typedef msvc_sink<std::mutex> msvc_sink_mt;
|
||||
|
@@ -5,8 +5,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
#include "base_sink.h"
|
||||
#include "../details/null_mutex.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
@@ -22,12 +22,12 @@ protected:
|
||||
void _sink_it(const details::log_msg&) override
|
||||
{}
|
||||
|
||||
void flush() override
|
||||
void _flush() override
|
||||
{}
|
||||
|
||||
};
|
||||
typedef null_sink<details::null_mutex> null_sink_st;
|
||||
typedef null_sink<std::mutex> null_sink_mt;
|
||||
typedef null_sink<details::null_mutex> null_sink_mt;
|
||||
|
||||
}
|
||||
}
|
||||
|
@@ -5,8 +5,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
#include "../details/null_mutex.h"
|
||||
#include "base_sink.h"
|
||||
|
||||
#include <ostream>
|
||||
#include <mutex>
|
||||
@@ -32,7 +32,7 @@ protected:
|
||||
_ostream.flush();
|
||||
}
|
||||
|
||||
void flush() override
|
||||
void _flush() override
|
||||
{
|
||||
_ostream.flush();
|
||||
}
|
||||
|
@@ -6,7 +6,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/details/log_msg.h>
|
||||
#include "../details/log_msg.h"
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
@@ -15,10 +15,39 @@ namespace sinks
|
||||
class sink
|
||||
{
|
||||
public:
|
||||
sink()
|
||||
{
|
||||
_level = level::trace;
|
||||
}
|
||||
|
||||
virtual ~sink() {}
|
||||
virtual void log(const details::log_msg& msg) = 0;
|
||||
virtual void flush() = 0;
|
||||
|
||||
bool should_log(level::level_enum msg_level) const;
|
||||
void set_level(level::level_enum log_level);
|
||||
level::level_enum level() const;
|
||||
|
||||
private:
|
||||
level_t _level;
|
||||
|
||||
};
|
||||
|
||||
inline bool sink::should_log(level::level_enum msg_level) const
|
||||
{
|
||||
return msg_level >= _level.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
inline void sink::set_level(level::level_enum log_level)
|
||||
{
|
||||
_level.store(log_level);
|
||||
}
|
||||
|
||||
inline level::level_enum sink::level() const
|
||||
{
|
||||
return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -5,8 +5,8 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/details/null_mutex.h>
|
||||
#include <spdlog/sinks/base_sink.h>
|
||||
#include "../details/null_mutex.h"
|
||||
#include "base_sink.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
@@ -18,24 +18,25 @@ namespace sinks
|
||||
{
|
||||
|
||||
template <class Mutex>
|
||||
class stdout_sink : public base_sink<Mutex>
|
||||
class stdout_sink SPDLOG_FINAL : public base_sink<Mutex>
|
||||
{
|
||||
using MyType = stdout_sink<Mutex>;
|
||||
public:
|
||||
stdout_sink() {}
|
||||
stdout_sink()
|
||||
{}
|
||||
static std::shared_ptr<MyType> instance()
|
||||
{
|
||||
static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
|
||||
return instance;
|
||||
}
|
||||
|
||||
protected:
|
||||
void _sink_it(const details::log_msg& msg) override
|
||||
{
|
||||
fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stdout);
|
||||
flush();
|
||||
_flush();
|
||||
}
|
||||
|
||||
void flush() override
|
||||
void _flush() override
|
||||
{
|
||||
fflush(stdout);
|
||||
}
|
||||
@@ -46,24 +47,25 @@ typedef stdout_sink<std::mutex> stdout_sink_mt;
|
||||
|
||||
|
||||
template <class Mutex>
|
||||
class stderr_sink : public base_sink<Mutex>
|
||||
class stderr_sink SPDLOG_FINAL : public base_sink<Mutex>
|
||||
{
|
||||
using MyType = stderr_sink<Mutex>;
|
||||
public:
|
||||
stderr_sink() {}
|
||||
stderr_sink()
|
||||
{}
|
||||
static std::shared_ptr<MyType> instance()
|
||||
{
|
||||
static std::shared_ptr<MyType> instance = std::make_shared<MyType>();
|
||||
return instance;
|
||||
}
|
||||
|
||||
protected:
|
||||
void _sink_it(const details::log_msg& msg) override
|
||||
{
|
||||
fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), stderr);
|
||||
flush();
|
||||
_flush();
|
||||
}
|
||||
|
||||
void flush() override
|
||||
void _flush() override
|
||||
{
|
||||
fflush(stderr);
|
||||
}
|
||||
|
@@ -5,11 +5,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
#include "../common.h"
|
||||
|
||||
#include <spdlog/sinks/sink.h>
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/details/log_msg.h>
|
||||
#ifdef SPDLOG_ENABLE_SYSLOG
|
||||
|
||||
#include "sink.h"
|
||||
#include "../details/log_msg.h"
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
|
121
include/spdlog/sinks/wincolor_sink.h
Normal file
121
include/spdlog/sinks/wincolor_sink.h
Normal file
@@ -0,0 +1,121 @@
|
||||
//
|
||||
// Copyright(c) 2016 spdlog
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "base_sink.h"
|
||||
#include "../details/null_mutex.h"
|
||||
#include "../common.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <wincon.h>
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace sinks
|
||||
{
|
||||
/*
|
||||
* Windows color console sink. Uses WriteConsoleA to write to the console with colors
|
||||
*/
|
||||
template<class Mutex>
|
||||
class wincolor_sink: public base_sink<Mutex>
|
||||
{
|
||||
public:
|
||||
const WORD BOLD = FOREGROUND_INTENSITY;
|
||||
const WORD RED = FOREGROUND_RED;
|
||||
const WORD CYAN = FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN;
|
||||
|
||||
wincolor_sink(HANDLE std_handle): out_handle_(std_handle)
|
||||
{
|
||||
colors_[level::trace] = CYAN;
|
||||
colors_[level::debug] = CYAN;
|
||||
colors_[level::info] = WHITE | BOLD;
|
||||
colors_[level::warn] = YELLOW | BOLD;
|
||||
colors_[level::err] = RED | BOLD; // red bold
|
||||
colors_[level::critical] = BACKGROUND_RED | WHITE | BOLD; // white bold on red background
|
||||
colors_[level::off] = 0;
|
||||
}
|
||||
|
||||
virtual ~wincolor_sink()
|
||||
{
|
||||
this->flush();
|
||||
}
|
||||
|
||||
wincolor_sink(const wincolor_sink& other) = delete;
|
||||
wincolor_sink& operator=(const wincolor_sink& other) = delete;
|
||||
|
||||
protected:
|
||||
virtual void _sink_it(const details::log_msg& msg) override
|
||||
{
|
||||
auto color = colors_[msg.level];
|
||||
auto orig_attribs = set_console_attribs(color);
|
||||
WriteConsoleA(out_handle_, msg.formatted.data(), static_cast<DWORD>(msg.formatted.size()), nullptr, nullptr);
|
||||
SetConsoleTextAttribute(out_handle_, orig_attribs); //reset to orig colors
|
||||
}
|
||||
|
||||
virtual void _flush() override
|
||||
{
|
||||
// windows console always flushed?
|
||||
}
|
||||
|
||||
// change the color for the given level
|
||||
void set_color(level::level_enum level, WORD color)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::_mutex);
|
||||
colors_[level] = color;
|
||||
}
|
||||
|
||||
private:
|
||||
HANDLE out_handle_;
|
||||
std::map<level::level_enum, WORD> colors_;
|
||||
|
||||
// set color and return the orig console attributes (for resetting later)
|
||||
WORD set_console_attribs(WORD attribs)
|
||||
{
|
||||
CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
|
||||
GetConsoleScreenBufferInfo(out_handle_, &orig_buffer_info);
|
||||
WORD back_color = orig_buffer_info.wAttributes;
|
||||
// retrieve the current background color
|
||||
back_color &= ~(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY);
|
||||
// keep the background color unchanged
|
||||
SetConsoleTextAttribute(out_handle_, attribs | back_color);
|
||||
return orig_buffer_info.wAttributes; //return orig attribs
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// windows color console to stdout
|
||||
//
|
||||
template<class Mutex>
|
||||
class wincolor_stdout_sink: public wincolor_sink<Mutex>
|
||||
{
|
||||
public:
|
||||
wincolor_stdout_sink() : wincolor_sink<Mutex>(GetStdHandle(STD_OUTPUT_HANDLE))
|
||||
{}
|
||||
};
|
||||
|
||||
typedef wincolor_stdout_sink<std::mutex> wincolor_stdout_sink_mt;
|
||||
typedef wincolor_stdout_sink<details::null_mutex> wincolor_stdout_sink_st;
|
||||
|
||||
//
|
||||
// windows color console to stderr
|
||||
//
|
||||
template<class Mutex>
|
||||
class wincolor_stderr_sink: public wincolor_sink<Mutex>
|
||||
{
|
||||
public:
|
||||
wincolor_stderr_sink() : wincolor_sink<Mutex>(GetStdHandle(STD_ERROR_HANDLE))
|
||||
{}
|
||||
};
|
||||
|
||||
typedef wincolor_stderr_sink<std::mutex> wincolor_stderr_sink_mt;
|
||||
typedef wincolor_stderr_sink<details::null_mutex> wincolor_stderr_sink_st;
|
||||
|
||||
}
|
||||
}
|
29
include/spdlog/sinks/windebug_sink.h
Normal file
29
include/spdlog/sinks/windebug_sink.h
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Copyright(c) 2017 Alexander Dalshov.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include "msvc_sink.h"
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace sinks
|
||||
{
|
||||
|
||||
/*
|
||||
* Windows debug sink (logging using OutputDebugStringA, synonym for msvc_sink)
|
||||
*/
|
||||
template<class Mutex>
|
||||
using windebug_sink = msvc_sink<Mutex>;
|
||||
|
||||
typedef msvc_sink_mt windebug_sink_mt;
|
||||
typedef msvc_sink_st windebug_sink_st;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@@ -2,15 +2,16 @@
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
// spdlog main header file.
|
||||
// see example.cpp for usage example
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <spdlog/tweakme.h>
|
||||
#include <spdlog/common.h>
|
||||
#include <spdlog/logger.h>
|
||||
#define SPDLOG_VERSION "0.16.2"
|
||||
|
||||
#include "tweakme.h"
|
||||
#include "common.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
@@ -20,15 +21,13 @@
|
||||
namespace spdlog
|
||||
{
|
||||
|
||||
// Return an existing logger or nullptr if a logger with such name doesn't exist.
|
||||
// Examples:
|
||||
//
|
||||
// spdlog::get("mylog")->info("Hello");
|
||||
// auto logger = spdlog::get("mylog");
|
||||
// logger.info("This is another message" , x, y, z);
|
||||
// logger.info() << "This is another message" << x << y << z;
|
||||
// Return an existing logger or nullptr if a logger with such name doesn't exist.
|
||||
// example: spdlog::get("my_logger")->info("hello {}", "world");
|
||||
//
|
||||
std::shared_ptr<logger> get(const std::string& name);
|
||||
|
||||
|
||||
//
|
||||
// Set global formatting
|
||||
// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v");
|
||||
@@ -41,6 +40,11 @@ void set_formatter(formatter_ptr f);
|
||||
//
|
||||
void set_level(level::level_enum log_level);
|
||||
|
||||
//
|
||||
// Set global error handler
|
||||
//
|
||||
void set_error_handler(log_err_handler);
|
||||
|
||||
//
|
||||
// Turn on async mode (off by default) and set the queue size for each async_logger.
|
||||
// effective only for loggers created after this call.
|
||||
@@ -64,41 +68,52 @@ void set_sync_mode();
|
||||
|
||||
|
||||
//
|
||||
// Create and register multi/single basic file logger
|
||||
// Create and register multi/single threaded basic file logger.
|
||||
// Basic logger simply writes to given file without any limitations or rotations.
|
||||
//
|
||||
std::shared_ptr<logger> basic_logger_mt(const std::string& logger_name, const filename_t& filename,bool force_flush = false);
|
||||
std::shared_ptr<logger> basic_logger_st(const std::string& logger_name, const filename_t& filename, bool force_flush = false);
|
||||
std::shared_ptr<logger> basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate = false);
|
||||
std::shared_ptr<logger> basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate = false);
|
||||
|
||||
//
|
||||
// Create and register multi/single threaded rotating file logger
|
||||
//
|
||||
std::shared_ptr<logger> rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files, bool force_flush = false);
|
||||
std::shared_ptr<logger> rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files, bool force_flush = false);
|
||||
std::shared_ptr<logger> rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files);
|
||||
std::shared_ptr<logger> rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files);
|
||||
|
||||
//
|
||||
// Create file logger which creates new file on the given time (default in midnight):
|
||||
//
|
||||
std::shared_ptr<logger> daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0, bool force_flush = false);
|
||||
std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0, bool force_flush = false);
|
||||
std::shared_ptr<logger> daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0);
|
||||
std::shared_ptr<logger> daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour=0, int minute=0);
|
||||
|
||||
//
|
||||
// Create and register stdout/stderr loggers
|
||||
//
|
||||
std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name, bool color = false);
|
||||
std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name, bool color = false);
|
||||
std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name, bool color = false);
|
||||
std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name, bool color = false);
|
||||
std::shared_ptr<logger> stdout_logger_mt(const std::string& logger_name);
|
||||
std::shared_ptr<logger> stdout_logger_st(const std::string& logger_name);
|
||||
std::shared_ptr<logger> stderr_logger_mt(const std::string& logger_name);
|
||||
std::shared_ptr<logger> stderr_logger_st(const std::string& logger_name);
|
||||
//
|
||||
// Create and register colored stdout/stderr loggers
|
||||
//
|
||||
std::shared_ptr<logger> stdout_color_mt(const std::string& logger_name);
|
||||
std::shared_ptr<logger> stdout_color_st(const std::string& logger_name);
|
||||
std::shared_ptr<logger> stderr_color_mt(const std::string& logger_name);
|
||||
std::shared_ptr<logger> stderr_color_st(const std::string& logger_name);
|
||||
|
||||
|
||||
//
|
||||
// Create and register a syslog logger
|
||||
//
|
||||
#if defined(__linux__) || defined(__APPLE__)
|
||||
std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0);
|
||||
#ifdef SPDLOG_ENABLE_SYSLOG
|
||||
std::shared_ptr<logger> syslog_logger(const std::string& logger_name, const std::string& ident = "", int syslog_option = 0, int syslog_facilty = (1<<3));
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
std::shared_ptr<logger> android_logger(const std::string& logger_name, const std::string& tag = "spdlog");
|
||||
#endif
|
||||
|
||||
// Create and register a logger a single sink
|
||||
// Create and register a logger with a single sink
|
||||
std::shared_ptr<logger> create(const std::string& logger_name, const sink_ptr& sink);
|
||||
|
||||
// Create and register a logger with multiple sinks
|
||||
@@ -108,25 +123,38 @@ std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_b
|
||||
|
||||
|
||||
// Create and register a logger with templated sink type
|
||||
// Example: spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename", "txt");
|
||||
// Example:
|
||||
// spdlog::create<daily_file_sink_st>("mylog", "dailylog_filename");
|
||||
template <typename Sink, typename... Args>
|
||||
std::shared_ptr<spdlog::logger> create(const std::string& logger_name, Args...);
|
||||
|
||||
// Create and register an async logger with a single sink
|
||||
std::shared_ptr<logger> create_async(const std::string& logger_name, const sink_ptr& sink, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr);
|
||||
|
||||
// Create and register an async logger with multiple sinks
|
||||
std::shared_ptr<logger> create_async(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr);
|
||||
template<class It>
|
||||
std::shared_ptr<logger> create_async(const std::string& logger_name, const It& sinks_begin, const It& sinks_end, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function<void()>& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function<void()>& worker_teardown_cb = nullptr);
|
||||
|
||||
// Register the given logger with the given name
|
||||
void register_logger(std::shared_ptr<logger> logger);
|
||||
|
||||
// Apply a user defined function on all registered loggers
|
||||
// Example:
|
||||
// spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) {l->flush();});
|
||||
void apply_all(std::function<void(std::shared_ptr<logger>)> fun);
|
||||
|
||||
// Drop the reference to the given logger
|
||||
void drop(const std::string &name);
|
||||
|
||||
// Drop all references
|
||||
// Drop all references from the registry
|
||||
void drop_all();
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Trace & Debug can be switched on/off at compile time for zero cost debug statements.
|
||||
// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in teakme.h to enable.
|
||||
// Uncomment SPDLOG_DEBUG_ON/SPDLOG_TRACE_ON in tweakme.h to enable.
|
||||
// SPDLOG_TRACE(..) will also print current file and line.
|
||||
//
|
||||
// Example:
|
||||
@@ -139,7 +167,11 @@ void drop_all();
|
||||
#ifdef SPDLOG_TRACE_ON
|
||||
#define SPDLOG_STR_H(x) #x
|
||||
#define SPDLOG_STR_HELPER(x) SPDLOG_STR_H(x)
|
||||
#define SPDLOG_TRACE(logger, ...) logger->trace("[" __FILE__ " line #" SPDLOG_STR_HELPER(__LINE__) "] " __VA_ARGS__)
|
||||
#ifdef _MSC_VER
|
||||
#define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ "(" SPDLOG_STR_HELPER(__LINE__) ") ] " __VA_ARGS__)
|
||||
#else
|
||||
#define SPDLOG_TRACE(logger, ...) logger->trace("[ " __FILE__ ":" SPDLOG_STR_HELPER(__LINE__) " ] " __VA_ARGS__)
|
||||
#endif
|
||||
#else
|
||||
#define SPDLOG_TRACE(logger, ...)
|
||||
#endif
|
||||
@@ -150,8 +182,7 @@ void drop_all();
|
||||
#define SPDLOG_DEBUG(logger, ...)
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#include <spdlog/details/spdlog_impl.h>
|
||||
#include "details/spdlog_impl.h"
|
||||
|
@@ -11,6 +11,7 @@
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Under Linux, the much faster CLOCK_REALTIME_COARSE clock can be used.
|
||||
// This clock is less accurate - can be off by dozens of millis - depending on the kernel HZ.
|
||||
@@ -22,7 +23,7 @@
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Uncomment if date/time logging is not needed and never appear in the log pattern.
|
||||
// This will prevent spdlog from quering the clock on each log call.
|
||||
// This will prevent spdlog from querying the clock on each log call.
|
||||
//
|
||||
// WARNING: If the log pattern contains any date/time while this flag is on, the result is undefined.
|
||||
// You must set new pattern(spdlog::set_pattern(..") without any date/time in it
|
||||
@@ -33,7 +34,7 @@
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Uncomment if thread id logging is not needed (i.e. no %t in the log pattern).
|
||||
// This will prevent spdlog from quering the thread id on each log call.
|
||||
// This will prevent spdlog from querying the thread id on each log call.
|
||||
//
|
||||
// WARNING: If the log pattern contains thread id (i.e, %t) while this flag is on, the result is undefined.
|
||||
//
|
||||
@@ -41,6 +42,16 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Uncomment to prevent spdlog from caching thread ids in thread local storage.
|
||||
// By default spdlog saves thread ids in tls to gain a few micros for each call.
|
||||
//
|
||||
// WARNING: if your program forks, UNCOMMENT this flag to prevent undefined thread ids in the children logs.
|
||||
//
|
||||
// #define SPDLOG_DISABLE_TID_CACHING
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Uncomment if logger name logging is not needed.
|
||||
// This will prevent spdlog from copying the logger name on each log call.
|
||||
@@ -48,7 +59,6 @@
|
||||
// #define SPDLOG_NO_NAME
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Uncomment to enable the SPDLOG_DEBUG/SPDLOG_TRACE macros.
|
||||
//
|
||||
@@ -59,7 +69,7 @@
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Uncomment to avoid locking in the registry operations (spdlog::get(), spdlog::drop() spdlog::register()).
|
||||
// Use only if your code never modifes concurrently the registry.
|
||||
// Use only if your code never modifies concurrently the registry.
|
||||
// Note that upon creating a logger the registry is modified by spdlog..
|
||||
//
|
||||
// #define SPDLOG_NO_REGISTRY_MUTEX
|
||||
@@ -73,12 +83,14 @@
|
||||
// #define SPDLOG_NO_ATOMIC_LEVELS
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Uncomment to enable usage of wchar_t for file names on Windows.
|
||||
//
|
||||
// #define SPDLOG_WCHAR_FILENAMES
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Uncomment to override default eol ("\n" or "\r\n" under Linux/Windows)
|
||||
//
|
||||
@@ -92,3 +104,57 @@
|
||||
//
|
||||
// #define SPDLOG_FMT_EXTERNAL
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Uncomment to use printf-style messages in your logs instead of the usual
|
||||
// format-style used by default.
|
||||
//
|
||||
// #define SPDLOG_FMT_PRINTF
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Uncomment to enable syslog (disabled by default)
|
||||
//
|
||||
// #define SPDLOG_ENABLE_SYSLOG
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Uncomment to enable wchar_t support (convert to utf8)
|
||||
//
|
||||
// #define SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Uncomment to prevent child processes from inheriting log file descriptors
|
||||
//
|
||||
// #define SPDLOG_PREVENT_CHILD_FD
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Uncomment if your compiler doesn't support the "final" keyword.
|
||||
// The final keyword allows more optimizations in release
|
||||
// mode with recent compilers. See GCC's documentation for -Wsuggest-final-types
|
||||
// for instance.
|
||||
//
|
||||
// #define SPDLOG_NO_FINAL
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Uncomment to enable message counting feature.
|
||||
// Use the %i in the logger pattern to display log message sequence id.
|
||||
//
|
||||
// #define SPDLOG_ENABLE_MESSAGE_COUNTER
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Uncomment to customize level names (e.g. "MT TRACE")
|
||||
//
|
||||
// #define SPDLOG_LEVEL_NAMES { "MY TRACE", "MY DEBUG", "MY INFO", "MY WARNING", "MY ERROR", "MY CRITICAL", "OFF" }
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@@ -10,7 +10,7 @@ find_package(Threads)
|
||||
add_library(catch INTERFACE)
|
||||
target_include_directories(catch INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
file(GLOB catch_tests LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp)
|
||||
file(GLOB catch_tests LIST_DIRECTORIES false RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.cpp *.h *.hpp)
|
||||
|
||||
add_executable(catch_tests ${catch_tests})
|
||||
target_link_libraries(catch_tests spdlog ${CMAKE_THREAD_LIBS_INIT})
|
||||
|
@@ -1,5 +1,11 @@
|
||||
CXX ?= g++
|
||||
CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -O2 -I../include
|
||||
ifeq ($(STYLE),printf)
|
||||
$(info *** PRINTF STYLE ***)
|
||||
CXXFLAGS = -DSPDLOG_FMT_PRINTF -Wall -pedantic -std=c++11 -pthread -O2 -I../include
|
||||
else
|
||||
$(info *** FORMAT STYLE ***)
|
||||
CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -O2 -I../include
|
||||
endif
|
||||
LDPFALGS = -pthread
|
||||
|
||||
CPP_FILES := $(wildcard *.cpp)
|
||||
|
126
tests/errors.cpp
Normal file
126
tests/errors.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
|
||||
*/
|
||||
#include "includes.h"
|
||||
|
||||
#include<iostream>
|
||||
|
||||
|
||||
|
||||
|
||||
class failing_sink: public spdlog::sinks::sink
|
||||
{
|
||||
void log(const spdlog::details::log_msg& msg) override
|
||||
{
|
||||
throw std::runtime_error("some error happened during log");
|
||||
}
|
||||
|
||||
void flush() override
|
||||
{}
|
||||
};
|
||||
|
||||
TEST_CASE("default_error_handler", "[errors]]")
|
||||
{
|
||||
prepare_logdir();
|
||||
std::string filename = "logs/simple_log.txt";
|
||||
|
||||
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
|
||||
logger->set_pattern("%v");
|
||||
#if !defined(SPDLOG_FMT_PRINTF)
|
||||
logger->info("Test message {} {}", 1);
|
||||
logger->info("Test message {}", 2);
|
||||
#else
|
||||
logger->info("Test message %d %d", 1);
|
||||
logger->info("Test message %d", 2);
|
||||
#endif
|
||||
logger->flush();
|
||||
|
||||
REQUIRE(file_contents(filename) == std::string("Test message 2\n"));
|
||||
REQUIRE(count_lines(filename) == 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
struct custom_ex
|
||||
{};
|
||||
TEST_CASE("custom_error_handler", "[errors]]")
|
||||
{
|
||||
prepare_logdir();
|
||||
std::string filename = "logs/simple_log.txt";
|
||||
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
|
||||
logger->flush_on(spdlog::level::info);
|
||||
logger->set_error_handler([=](const std::string& msg)
|
||||
{
|
||||
throw custom_ex();
|
||||
});
|
||||
logger->info("Good message #1");
|
||||
#if !defined(SPDLOG_FMT_PRINTF)
|
||||
REQUIRE_THROWS_AS(logger->info("Bad format msg {} {}", "xxx"), custom_ex);
|
||||
#else
|
||||
REQUIRE_THROWS_AS(logger->info("Bad format msg %s %s", "xxx"), custom_ex);
|
||||
#endif
|
||||
logger->info("Good message #2");
|
||||
REQUIRE(count_lines(filename) == 2);
|
||||
}
|
||||
|
||||
TEST_CASE("default_error_handler2", "[errors]]")
|
||||
{
|
||||
|
||||
auto logger = spdlog::create<failing_sink>("failed_logger");
|
||||
logger->set_error_handler([=](const std::string& msg)
|
||||
{
|
||||
throw custom_ex();
|
||||
});
|
||||
REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex);
|
||||
}
|
||||
|
||||
TEST_CASE("async_error_handler", "[errors]]")
|
||||
{
|
||||
prepare_logdir();
|
||||
std::string err_msg("log failed with some msg");
|
||||
spdlog::set_async_mode(128);
|
||||
std::string filename = "logs/simple_async_log.txt";
|
||||
{
|
||||
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
|
||||
logger->set_error_handler([=](const std::string& msg)
|
||||
{
|
||||
std::ofstream ofs("logs/custom_err.txt");
|
||||
if (!ofs) throw std::runtime_error("Failed open logs/custom_err.txt");
|
||||
ofs << err_msg;
|
||||
});
|
||||
logger->info("Good message #1");
|
||||
#if !defined(SPDLOG_FMT_PRINTF)
|
||||
logger->info("Bad format msg {} {}", "xxx");
|
||||
#else
|
||||
logger->info("Bad format msg %s %s", "xxx");
|
||||
#endif
|
||||
logger->info("Good message #2");
|
||||
spdlog::drop("logger"); //force logger to drain the queue and shutdown
|
||||
spdlog::set_sync_mode();
|
||||
}
|
||||
REQUIRE(count_lines(filename) == 2);
|
||||
REQUIRE(file_contents("logs/custom_err.txt") == err_msg);
|
||||
}
|
||||
|
||||
// Make sure async error handler is executed
|
||||
TEST_CASE("async_error_handler2", "[errors]]")
|
||||
{
|
||||
prepare_logdir();
|
||||
std::string err_msg("This is async handler error message");
|
||||
spdlog::set_async_mode(128);
|
||||
{
|
||||
auto logger = spdlog::create<failing_sink>("failed_logger");
|
||||
logger->set_error_handler([=](const std::string& msg)
|
||||
{
|
||||
std::ofstream ofs("logs/custom_err2.txt");
|
||||
if (!ofs) throw std::runtime_error("Failed open logs/custom_err2.txt");
|
||||
ofs << err_msg;
|
||||
});
|
||||
logger->info("Hello failure");
|
||||
spdlog::drop("failed_logger"); //force logger to drain the queue and shutdown
|
||||
spdlog::set_sync_mode();
|
||||
}
|
||||
|
||||
REQUIRE(file_contents("logs/custom_err2.txt") == err_msg);
|
||||
}
|
@@ -12,6 +12,7 @@ static void write_with_helper(file_helper &helper, size_t howmany)
|
||||
log_msg msg;
|
||||
msg.formatted << std::string(howmany, '1');
|
||||
helper.write(msg);
|
||||
helper.flush();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +20,7 @@ TEST_CASE("file_helper_filename", "[file_helper::filename()]]")
|
||||
{
|
||||
prepare_logdir();
|
||||
|
||||
file_helper helper(false);
|
||||
file_helper helper;
|
||||
helper.open(target_filename);
|
||||
REQUIRE(helper.filename() == target_filename);
|
||||
}
|
||||
@@ -31,7 +32,7 @@ TEST_CASE("file_helper_size", "[file_helper::size()]]")
|
||||
prepare_logdir();
|
||||
size_t expected_size = 123;
|
||||
{
|
||||
file_helper helper(true);
|
||||
file_helper helper;
|
||||
helper.open(target_filename);
|
||||
write_with_helper(helper, expected_size);
|
||||
REQUIRE(static_cast<size_t>(helper.size()) == expected_size);
|
||||
@@ -44,7 +45,7 @@ TEST_CASE("file_helper_exists", "[file_helper::file_exists()]]")
|
||||
{
|
||||
prepare_logdir();
|
||||
REQUIRE(!file_helper::file_exists(target_filename));
|
||||
file_helper helper(false);
|
||||
file_helper helper;
|
||||
helper.open(target_filename);
|
||||
REQUIRE(file_helper::file_exists(target_filename));
|
||||
}
|
||||
@@ -52,7 +53,7 @@ TEST_CASE("file_helper_exists", "[file_helper::file_exists()]]")
|
||||
TEST_CASE("file_helper_reopen", "[file_helper::reopen()]]")
|
||||
{
|
||||
prepare_logdir();
|
||||
file_helper helper(true);
|
||||
file_helper helper;
|
||||
helper.open(target_filename);
|
||||
write_with_helper(helper, 12);
|
||||
REQUIRE(helper.size() == 12);
|
||||
@@ -64,7 +65,7 @@ TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
|
||||
{
|
||||
prepare_logdir();
|
||||
size_t expected_size = 14;
|
||||
file_helper helper(true);
|
||||
file_helper helper;
|
||||
helper.open(target_filename);
|
||||
write_with_helper(helper, expected_size);
|
||||
REQUIRE(helper.size() == expected_size);
|
||||
@@ -74,4 +75,44 @@ TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]]")
|
||||
|
||||
|
||||
|
||||
static void test_split_ext(const char* fname, const char* expect_base, const char* expect_ext)
|
||||
{
|
||||
spdlog::filename_t filename(fname);
|
||||
spdlog::filename_t expected_base(expect_base);
|
||||
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, ext;
|
||||
std::tie(basename, ext) = file_helper::split_by_extenstion(filename);
|
||||
REQUIRE(basename == expected_base);
|
||||
REQUIRE(ext == expected_ext);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("file_helper_split_by_extenstion", "[file_helper::split_by_extenstion()]]")
|
||||
{
|
||||
test_split_ext("mylog.txt", "mylog", ".txt");
|
||||
test_split_ext(".mylog.txt", ".mylog", ".txt");
|
||||
test_split_ext(".mylog", ".mylog", "");
|
||||
test_split_ext("/aaa/bb.d/mylog", "/aaa/bb.d/mylog", "");
|
||||
test_split_ext("/aaa/bb.d/mylog.txt", "/aaa/bb.d/mylog", ".txt");
|
||||
test_split_ext("aaa/bbb/ccc/mylog.txt", "aaa/bbb/ccc/mylog", ".txt");
|
||||
test_split_ext("aaa/bbb/ccc/mylog.", "aaa/bbb/ccc/mylog.", "");
|
||||
test_split_ext("aaa/bbb/ccc/.mylog.txt", "aaa/bbb/ccc/.mylog", ".txt");
|
||||
test_split_ext("/aaa/bbb/ccc/mylog.txt", "/aaa/bbb/ccc/mylog", ".txt");
|
||||
test_split_ext("/aaa/bbb/ccc/.mylog", "/aaa/bbb/ccc/.mylog", "");
|
||||
test_split_ext("../mylog.txt", "../mylog", ".txt");
|
||||
test_split_ext(".././mylog.txt", ".././mylog", ".txt");
|
||||
test_split_ext(".././mylog.txt/xxx", ".././mylog.txt/xxx", "");
|
||||
test_split_ext("/mylog.txt", "/mylog", ".txt");
|
||||
test_split_ext("//mylog.txt", "//mylog", ".txt");
|
||||
test_split_ext("", "", "");
|
||||
test_split_ext(".", ".", "");
|
||||
test_split_ext("..txt", ".", ".txt");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@@ -7,33 +7,66 @@
|
||||
TEST_CASE("simple_file_logger", "[simple_logger]]")
|
||||
{
|
||||
prepare_logdir();
|
||||
std::string filename = "logs/simple_log.txt";
|
||||
std::string filename = "logs/simple_log";
|
||||
|
||||
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
|
||||
logger->set_pattern("%v");
|
||||
|
||||
|
||||
#if !defined(SPDLOG_FMT_PRINTF)
|
||||
logger->info("Test message {}", 1);
|
||||
logger->info("Test message {}", 2);
|
||||
#else
|
||||
logger->info("Test message %d", 1);
|
||||
logger->info("Test message %d", 2);
|
||||
#endif
|
||||
logger->flush();
|
||||
REQUIRE(file_contents(filename) == std::string("Test message 1\nTest message 2\n"));
|
||||
REQUIRE(count_lines(filename) == 2);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("flush_on", "[flush_on]]")
|
||||
{
|
||||
prepare_logdir();
|
||||
std::string filename = "logs/simple_log";
|
||||
|
||||
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
|
||||
logger->set_pattern("%v");
|
||||
logger->set_level(spdlog::level::trace);
|
||||
logger->flush_on(spdlog::level::info);
|
||||
logger->trace("Should not be flushed");
|
||||
REQUIRE(count_lines(filename) == 0);
|
||||
|
||||
#if !defined(SPDLOG_FMT_PRINTF)
|
||||
logger->info("Test message {}", 1);
|
||||
logger->info("Test message {}", 2);
|
||||
#else
|
||||
logger->info("Test message %d", 1);
|
||||
logger->info("Test message %d", 2);
|
||||
#endif
|
||||
logger->flush();
|
||||
REQUIRE(file_contents(filename) == std::string("Should not be flushed\nTest message 1\nTest message 2\n"));
|
||||
REQUIRE(count_lines(filename) == 3);
|
||||
}
|
||||
|
||||
TEST_CASE("rotating_file_logger1", "[rotating_logger]]")
|
||||
{
|
||||
prepare_logdir();
|
||||
std::string basename = "logs/rotating_log";
|
||||
auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0, true);
|
||||
auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 0);
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
#if !defined(SPDLOG_FMT_PRINTF)
|
||||
logger->info("Test message {}", i);
|
||||
#else
|
||||
logger->info("Test message %d", i);
|
||||
#endif
|
||||
}
|
||||
|
||||
auto filename = basename + ".txt";
|
||||
logger->flush();
|
||||
auto filename = basename;
|
||||
REQUIRE(count_lines(filename) == 10);
|
||||
for (int i = 0; i < 1000; i++)
|
||||
logger->info("Test message {}", i);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -41,36 +74,48 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]")
|
||||
{
|
||||
prepare_logdir();
|
||||
std::string basename = "logs/rotating_log";
|
||||
auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 1, false);
|
||||
auto logger = spdlog::rotating_logger_mt("logger", basename, 1024, 1);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
logger->info("Test message {}", i);
|
||||
|
||||
logger->flush();
|
||||
auto filename = basename + ".txt";
|
||||
auto filename = basename;
|
||||
REQUIRE(count_lines(filename) == 10);
|
||||
for (int i = 0; i < 1000; i++)
|
||||
{
|
||||
#if !defined(SPDLOG_FMT_PRINTF)
|
||||
logger->info("Test message {}", i);
|
||||
#else
|
||||
logger->info("Test message %d", i);
|
||||
#endif
|
||||
}
|
||||
|
||||
logger->flush();
|
||||
REQUIRE(get_filesize(filename) <= 1024);
|
||||
auto filename1 = basename + ".1.txt";
|
||||
auto filename1 = basename + ".1";
|
||||
REQUIRE(get_filesize(filename1) <= 1024);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("daily_logger", "[daily_logger]]")
|
||||
{
|
||||
|
||||
prepare_logdir();
|
||||
//calculate filename (time based)
|
||||
std::string basename = "logs/daily_log";
|
||||
std::tm tm = spdlog::details::os::localtime();
|
||||
fmt::MemoryWriter w;
|
||||
w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
|
||||
w.write("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
|
||||
|
||||
auto logger = spdlog::daily_logger_mt("logger", basename, 0, 0, true);
|
||||
auto logger = spdlog::daily_logger_mt("logger", basename, 0, 0);
|
||||
logger->flush_on(spdlog::level::info);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
#if !defined(SPDLOG_FMT_PRINTF)
|
||||
logger->info("Test message {}", i);
|
||||
#else
|
||||
logger->info("Test message %d", i);
|
||||
#endif
|
||||
}
|
||||
|
||||
auto filename = w.str();
|
||||
REQUIRE(count_lines(filename) == 10);
|
||||
@@ -88,23 +133,29 @@ TEST_CASE("daily_logger with dateonly calculator", "[daily_logger_dateonly]]")
|
||||
std::string basename = "logs/daily_dateonly";
|
||||
std::tm tm = spdlog::details::os::localtime();
|
||||
fmt::MemoryWriter w;
|
||||
w.write("{}_{:04d}-{:02d}-{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
|
||||
w.write("{}_{:04d}-{:02d}-{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
|
||||
|
||||
auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0, true);
|
||||
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
#if !defined(SPDLOG_FMT_PRINTF)
|
||||
logger->info("Test message {}", i);
|
||||
|
||||
#else
|
||||
logger->info("Test message %d", i);
|
||||
#endif
|
||||
}
|
||||
logger->flush();
|
||||
auto filename = w.str();
|
||||
REQUIRE(count_lines(filename) == 10);
|
||||
}
|
||||
|
||||
struct custom_daily_file_name_calculator
|
||||
{
|
||||
static spdlog::filename_t calc_filename(const spdlog::filename_t& basename, const spdlog::filename_t& extension)
|
||||
static spdlog::filename_t calc_filename(const spdlog::filename_t& basename)
|
||||
{
|
||||
std::tm tm = spdlog::details::os::localtime();
|
||||
fmt::MemoryWriter w;
|
||||
w.write("{}{:04d}{:02d}{:02d}.{}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, extension);
|
||||
w.write("{}{:04d}{:02d}{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
|
||||
return w.str();
|
||||
}
|
||||
};
|
||||
@@ -120,13 +171,78 @@ TEST_CASE("daily_logger with custom calculator", "[daily_logger_custom]]")
|
||||
std::string basename = "logs/daily_dateonly";
|
||||
std::tm tm = spdlog::details::os::localtime();
|
||||
fmt::MemoryWriter w;
|
||||
w.write("{}{:04d}{:02d}{:02d}.txt", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
|
||||
w.write("{}{:04d}{:02d}{:02d}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
|
||||
|
||||
auto logger = spdlog::create<sink_type>("logger", basename, "txt", 0, 0, true);
|
||||
auto logger = spdlog::create<sink_type>("logger", basename, 0, 0);
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
#if !defined(SPDLOG_FMT_PRINTF)
|
||||
logger->info("Test message {}", i);
|
||||
#else
|
||||
logger->info("Test message %d", i);
|
||||
#endif
|
||||
}
|
||||
|
||||
logger->flush();
|
||||
auto filename = w.str();
|
||||
REQUIRE(count_lines(filename) == 10);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* File name calculations
|
||||
*/
|
||||
|
||||
TEST_CASE("rotating_file_sink::calc_filename1", "[rotating_file_sink]]")
|
||||
{
|
||||
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename("rotated.txt", 3);
|
||||
REQUIRE(filename == "rotated.3.txt");
|
||||
}
|
||||
|
||||
TEST_CASE("rotating_file_sink::calc_filename2", "[rotating_file_sink]]")
|
||||
{
|
||||
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename("rotated", 3);
|
||||
REQUIRE(filename == "rotated.3");
|
||||
}
|
||||
|
||||
TEST_CASE("rotating_file_sink::calc_filename3", "[rotating_file_sink]]")
|
||||
{
|
||||
auto filename = spdlog::sinks::rotating_file_sink_st::calc_filename("rotated.txt", 0);
|
||||
REQUIRE(filename == "rotated.txt");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// regex supported only from gcc 4.9 and above
|
||||
#if defined (_MSC_VER) || !(__GNUC__ <= 4 && __GNUC_MINOR__ < 9)
|
||||
#include <regex>
|
||||
TEST_CASE("daily_file_sink::default_daily_file_name_calculator1", "[daily_file_sink]]")
|
||||
{
|
||||
// daily_YYYY-MM-DD_hh-mm.txt
|
||||
auto filename = spdlog::sinks::default_daily_file_name_calculator::calc_filename("daily.txt");
|
||||
std::regex re(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])_\d\d-[0-5][0-9].txt$)");
|
||||
std::smatch match;
|
||||
REQUIRE(std::regex_match(filename, match, re));
|
||||
}
|
||||
|
||||
TEST_CASE("daily_file_sink::default_daily_file_name_calculator2", "[daily_file_sink]]")
|
||||
{
|
||||
// daily_YYYY-MM-DD_hh-mm.txt
|
||||
auto filename = spdlog::sinks::default_daily_file_name_calculator::calc_filename("daily");
|
||||
std::regex re(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])_\d\d-[0-5][0-9]$)");
|
||||
std::smatch match;
|
||||
REQUIRE(std::regex_match(filename, match, re));
|
||||
}
|
||||
|
||||
TEST_CASE("daily_file_sink::dateonly_daily_file_name_calculator", "[daily_file_sink]]")
|
||||
{
|
||||
// daily_YYYY-MM-DD_hh-mm.txt
|
||||
auto filename = spdlog::sinks::dateonly_daily_file_name_calculator::calc_filename("daily.txt");
|
||||
// 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::smatch match;
|
||||
REQUIRE(std::regex_match(filename, match, re));
|
||||
}
|
||||
#endif
|
||||
|
@@ -49,24 +49,7 @@ TEST_CASE("log_levels", "[log_levels]")
|
||||
REQUIRE(log_info("Hello", spdlog::level::trace) == "Hello");
|
||||
}
|
||||
|
||||
TEST_CASE("invalid_format", "[format]")
|
||||
{
|
||||
|
||||
using namespace spdlog::sinks;
|
||||
spdlog::logger null_logger("null_logger", std::make_shared<null_sink_st>());
|
||||
REQUIRE_THROWS_AS(
|
||||
null_logger.info("{} {}", "first"),
|
||||
spdlog::spdlog_ex);
|
||||
|
||||
REQUIRE_THROWS_AS(
|
||||
null_logger.info("{0:f}", "aads"),
|
||||
spdlog::spdlog_ex);
|
||||
|
||||
REQUIRE_THROWS_AS(
|
||||
null_logger.info("{0:kk}", 123),
|
||||
spdlog::spdlog_ex);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@@ -6,10 +6,12 @@
|
||||
#include <ostream>
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
|
||||
#include "catch.hpp"
|
||||
#include "utils.h"
|
||||
|
||||
#define SPDLOG_TRACE_ON
|
||||
#define SPDLOG_DEBUG_ON
|
||||
|
||||
#include "../include/spdlog/spdlog.h"
|
||||
#include "../include/spdlog/sinks/null_sink.h"
|
||||
#include "../include/spdlog/sinks/ostream_sink.h"
|
||||
|
@@ -1,6 +1,7 @@
|
||||
#include "includes.h"
|
||||
|
||||
static const char *tested_logger_name = "null_logger";
|
||||
static const char *tested_logger_name2 = "null_logger2";
|
||||
|
||||
TEST_CASE("register_drop", "[registry]")
|
||||
{
|
||||
@@ -22,6 +23,34 @@ TEST_CASE("explicit register" "[registry]")
|
||||
REQUIRE_THROWS_AS(spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name), spdlog::spdlog_ex);
|
||||
}
|
||||
|
||||
TEST_CASE("apply_all" "[registry]")
|
||||
{
|
||||
spdlog::drop_all();
|
||||
auto logger = std::make_shared<spdlog::logger>(tested_logger_name, std::make_shared<spdlog::sinks::null_sink_st>());
|
||||
spdlog::register_logger(logger);
|
||||
auto logger2 = std::make_shared<spdlog::logger>(tested_logger_name2, std::make_shared<spdlog::sinks::null_sink_st>());
|
||||
spdlog::register_logger(logger2);
|
||||
|
||||
int counter = 0;
|
||||
spdlog::apply_all([&counter](std::shared_ptr<spdlog::logger> l)
|
||||
{
|
||||
counter++;
|
||||
});
|
||||
REQUIRE(counter == 2);
|
||||
|
||||
counter = 0;
|
||||
spdlog::drop(tested_logger_name2);
|
||||
spdlog::apply_all([&counter](std::shared_ptr<spdlog::logger> l)
|
||||
{
|
||||
REQUIRE(l->name() == tested_logger_name);
|
||||
counter++;
|
||||
}
|
||||
);
|
||||
REQUIRE(counter == 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
TEST_CASE("drop" "[registry]")
|
||||
{
|
||||
spdlog::drop_all();
|
||||
@@ -34,10 +63,10 @@ TEST_CASE("drop_all" "[registry]")
|
||||
{
|
||||
spdlog::drop_all();
|
||||
spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name);
|
||||
spdlog::create<spdlog::sinks::null_sink_mt>("name2");
|
||||
spdlog::create<spdlog::sinks::null_sink_mt>(tested_logger_name2);
|
||||
spdlog::drop_all();
|
||||
REQUIRE_FALSE(spdlog::get(tested_logger_name));
|
||||
REQUIRE_FALSE(spdlog::get("name2"));
|
||||
REQUIRE_FALSE(spdlog::get(tested_logger_name));
|
||||
}
|
||||
|
||||
|
||||
@@ -51,3 +80,5 @@ TEST_CASE("drop non existing" "[registry]")
|
||||
spdlog::drop_all();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
50
tests/test_macros.cpp
Normal file
50
tests/test_macros.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE
|
||||
*/
|
||||
|
||||
#include "includes.h"
|
||||
|
||||
TEST_CASE("debug and trace w/o format string", "[macros]]")
|
||||
{
|
||||
prepare_logdir();
|
||||
std::string filename = "logs/simple_log";
|
||||
|
||||
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
|
||||
logger->set_pattern("%v");
|
||||
logger->set_level(spdlog::level::trace);
|
||||
|
||||
SPDLOG_TRACE(logger, "Test message 1");
|
||||
//SPDLOG_DEBUG(logger, "Test message 2");
|
||||
SPDLOG_DEBUG(logger, "Test message 2");
|
||||
logger->flush();
|
||||
|
||||
REQUIRE(ends_with(file_contents(filename), "Test message 2\n"));
|
||||
REQUIRE(count_lines(filename) == 2);
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("debug and trace with format strings", "[macros]]")
|
||||
{
|
||||
prepare_logdir();
|
||||
std::string filename = "logs/simple_log";
|
||||
|
||||
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
|
||||
logger->set_pattern("%v");
|
||||
logger->set_level(spdlog::level::trace);
|
||||
|
||||
#if !defined(SPDLOG_FMT_PRINTF)
|
||||
SPDLOG_TRACE(logger, "Test message {}", 1);
|
||||
//SPDLOG_DEBUG(logger, "Test message 2");
|
||||
SPDLOG_DEBUG(logger, "Test message {}", 222);
|
||||
#else
|
||||
SPDLOG_TRACE(logger, "Test message %d", 1);
|
||||
//SPDLOG_DEBUG(logger, "Test message 2");
|
||||
SPDLOG_DEBUG(logger, "Test message %d", 222);
|
||||
#endif
|
||||
|
||||
logger->flush();
|
||||
|
||||
REQUIRE(ends_with(file_contents(filename), "Test message 222\n"));
|
||||
REQUIRE(count_lines(filename) == 2);
|
||||
}
|
||||
|
@@ -125,11 +125,13 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="errors.cpp" />
|
||||
<ClCompile Include="file_helper.cpp" />
|
||||
<ClCompile Include="file_log.cpp" />
|
||||
<ClCompile Include="format.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="registry.cpp" />
|
||||
<ClCompile Include="test_macros.cpp" />
|
||||
<ClCompile Include="utils.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@@ -33,6 +33,12 @@
|
||||
<ClCompile Include="utils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="errors.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="test_macros.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="includes.h">
|
||||
|
@@ -1,14 +1,17 @@
|
||||
#include "includes.h"
|
||||
|
||||
|
||||
void prepare_logdir()
|
||||
{
|
||||
spdlog::drop_all();
|
||||
#ifdef _WIN32
|
||||
auto rv = system("del /F /Q logs\\*");
|
||||
system("if not exist logs mkdir logs");
|
||||
system("del /F /Q logs\\*");
|
||||
#else
|
||||
auto rv = system("rm -f logs/*");
|
||||
#endif
|
||||
auto rv = system("mkdir -p logs");
|
||||
rv = system("rm -f logs/*");
|
||||
(void)rv;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -43,3 +46,11 @@ std::size_t get_filesize(const std::string& filename)
|
||||
|
||||
return static_cast<std::size_t>(ifs.tellg());
|
||||
}
|
||||
|
||||
|
||||
// source: https://stackoverflow.com/a/2072890/192001
|
||||
bool ends_with(std::string const & value, std::string const & ending)
|
||||
{
|
||||
if (ending.size() > value.size()) return false;
|
||||
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
|
||||
}
|
||||
|
@@ -13,3 +13,4 @@ std::size_t count_lines(const std::string& filename);
|
||||
|
||||
std::size_t get_filesize(const std::string& filename);
|
||||
|
||||
bool ends_with(std::string const & value, std::string const & ending);
|
Reference in New Issue
Block a user