diff --git a/.clang-format b/.clang-format index aa95f7a5..bd1caad1 100644 --- a/.clang-format +++ b/.clang-format @@ -32,9 +32,9 @@ BraceWrapping: BeforeCatch: true BeforeElse: true IndentBraces: false - SplitEmptyFunction: true - SplitEmptyRecord: true - SplitEmptyNamespace: true + SplitEmptyFunction: false + SplitEmptyRecord: false + SplitEmptyNamespace: false BreakBeforeBinaryOperators: None BreakBeforeBraces: Custom BreakBeforeInheritanceComma: false diff --git a/.clang-tidy b/.clang-tidy index 309c7e94..9079dd9a 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,6 +1,6 @@ Checks: 'modernize-*,modernize-use-override,google-*,-google-runtime-references,misc-*,clang-analyzer-*' WarningsAsErrors: '' -HeaderFilterRegex: 'async.h|async_logger.h|common.h|details|formatter.h|logger.h|sinks|spdlog.h|tweakme.h|version.h' +HeaderFilterRegex: 'async.h|async_logger.h|common.h|details|formatter.h|logger.h|sinks|spdlog.h|tweakme.h|version.h|spdlite.h' AnalyzeTemporaryDtors: false FormatStyle: none diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..fe505b27 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +* text=false diff --git a/.gitignore b/.gitignore index b1a41919..fafa874a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ # Auto generated files -build/* +build/* *.slo *.lo *.o @@ -65,4 +65,5 @@ install_manifest.txt /tests/logs/* # idea -.idea/ \ No newline at end of file +.idea/ +cmake-build-*/ diff --git a/.travis.yml b/.travis.yml index 2a0a35b7..bb4f9f86 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,7 +46,7 @@ matrix: - env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11 os: linux addons: *gcc48 - + - env: GCC_VERSION=7 BUILD_TYPE=Release CPP=11 os: linux addons: *gcc7 @@ -64,24 +64,15 @@ matrix: - env: CLANG_VERSION=6.0 BUILD_TYPE=Debug CPP=11 ASAN=On TSAN=Off os: linux addons: *clang6 - + - env: CLANG_VERSION=6.0 BUILD_TYPE=Release CPP=11 ASAN=On TSAN=Off os: linux addons: *clang6 - - # Test clang-6.0: C++11, Build=Debug, TSAN=On - - env: CLANG_VERSION=6.0 BUILD_TYPE=Debug CPP=11 ASAN=Off TSAN=On - os: linux - addons: *clang6 - - - env: CLANG_VERSION=6.0 BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=On - os: linux - addons: *clang6 # osx - env: BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=Off os: osx - + diff --git a/CMakeLists.txt b/CMakeLists.txt index 12fe8bb5..2e336521 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,5 @@ -# -# Copyright(c) 2015 Ruslan Baratov. +# Copyright(c) 2019 spdlog authors # Distributed under the MIT License (http://opensource.org/licenses/MIT) -# cmake_minimum_required(VERSION 3.1) project(spdlog VERSION 1.3.1 LANGUAGES CXX) @@ -15,8 +13,6 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE) endif() -message(STATUS "Build type: " ${CMAKE_BUILD_TYPE}) - #--------------------------------------------------------------------------------------- # compiler config #--------------------------------------------------------------------------------------- @@ -24,61 +20,83 @@ set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang") - add_compile_options("-Wall") - add_compile_options("-Wextra") - add_compile_options("-Wconversion") - add_compile_options("-pedantic") - add_compile_options("-Wfatal-errors") -endif() - #--------------------------------------------------------------------------------------- -# address sanitizers check +# Set SPDLOG_MASTER_PROJECT to ON if we are building spdlog #--------------------------------------------------------------------------------------- -include(cmake/sanitizers.cmake) - -#--------------------------------------------------------------------------------------- -# spdlog target -#--------------------------------------------------------------------------------------- -add_library(spdlog INTERFACE) -add_library(spdlog::spdlog ALIAS spdlog) - -# Check if spdlog is being used directly or via add_subdirectory -set(SPDLOG_MASTER_PROJECT OFF) -if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) - set(SPDLOG_MASTER_PROJECT ON) -endif() - -option(SPDLOG_BUILD_EXAMPLES "Build examples" ${SPDLOG_MASTER_PROJECT}) -option(SPDLOG_BUILD_BENCH "Build benchmarks (Requires https://github.com/google/benchmark.git to be installed)" OFF) -option(SPDLOG_BUILD_TESTS "Build tests" ${SPDLOG_MASTER_PROJECT}) -option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF) -option(SPDLOG_FMT_HEADER_ONLY "Use header-only variant of external fmt library" ON) -option(SPDLOG_INSTALL "Generate the install target." ${SPDLOG_MASTER_PROJECT}) - -target_include_directories( - spdlog - INTERFACE - "$" - "$" -) - -if(SPDLOG_FMT_EXTERNAL) - target_compile_definitions(spdlog INTERFACE SPDLOG_FMT_EXTERNAL) - - if(NOT TARGET fmt::fmt) - find_package(fmt REQUIRED CONFIG) - endif() - - if(SPDLOG_FMT_HEADER_ONLY) - target_link_libraries(spdlog INTERFACE fmt::fmt-header-only) +# Check if spdlog is being used directly or via add_subdirectory, but allow overriding +if (NOT DEFINED SPDLOG_MASTER_PROJECT) + if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + set(SPDLOG_MASTER_PROJECT ON) else() - target_link_libraries(spdlog INTERFACE fmt::fmt) + set(SPDLOG_MASTER_PROJECT OFF) endif() +endif () + +option(SPDLOG_BUILD_EXAMPLES "Build examples" ON) +option(SPDLOG_BUILD_BENCH "Build benchmarks (Requires https://github.com/google/benchmark.git to be installed)" OFF) +option(SPDLOG_BUILD_TESTS "Build tests" OFF) +option(SPDLOG_INSTALL "Generate the install target." ${SPDLOG_MASTER_PROJECT}) +option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF) + +message(STATUS "Build type: " ${CMAKE_BUILD_TYPE}) + +find_package(Threads REQUIRED) + +#--------------------------------------------------------------------------------------- +# Static library version +#--------------------------------------------------------------------------------------- +add_library(spdlog STATIC src/spdlog.cpp) +target_compile_definitions(spdlog PUBLIC SPDLOG_COMPILED_LIB) +target_include_directories(spdlog PUBLIC + "$" + "$") +target_link_libraries(spdlog PUBLIC Threads::Threads) + +#--------------------------------------------------------------------------------------- +# Header only version +#--------------------------------------------------------------------------------------- +add_library(spdlog_header_only INTERFACE) + +target_include_directories(spdlog_header_only INTERFACE + "$" + "$") +target_link_libraries(spdlog_header_only INTERFACE Threads::Threads) + + +#--------------------------------------------------------------------------------------- +# Turn on compiler warnings and sanitizers if we build our own project +#--------------------------------------------------------------------------------------- +if(SPDLOG_MASTER_PROJECT) + if (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang") + target_compile_options( spdlog PUBLIC -Wall -Wextra -Wconversion -pedantic -Wfatal-errors) + target_compile_options( spdlog_header_only INTERFACE -Wall -Wextra -Wconversion -pedantic -Wfatal-errors) + endif() + if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + target_compile_options( spdlog PUBLIC /W3 /WX ) + target_compile_options( spdlog_header_only INTERFACE /W3 /WX) + endif() + + include(cmake/sanitizers.cmake) endif() -set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include") +#--------------------------------------------------------------------------------------- +# use fmt package if using exertnal fmt +#--------------------------------------------------------------------------------------- +if(SPDLOG_FMT_EXTERNAL) + if (NOT TARGET fmt::fmt) + find_package(fmt REQUIRED) + endif () + target_compile_definitions(spdlog PUBLIC SPDLOG_FMT_EXTERNAL) + target_link_libraries(spdlog PUBLIC fmt::fmt) + + target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FMT_EXTERNAL) + target_link_libraries(spdlog_header_only INTERFACE fmt::fmt) +endif() + +#--------------------------------------------------------------------------------------- +# build binries +#--------------------------------------------------------------------------------------- if(SPDLOG_BUILD_EXAMPLES) add_subdirectory(example) endif() @@ -93,74 +111,40 @@ if(SPDLOG_BUILD_BENCH) endif() #--------------------------------------------------------------------------------------- -# Install/export targets and files +# install #--------------------------------------------------------------------------------------- -if(SPDLOG_INSTALL) - # 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_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake") - set(project_config "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake") - set(targets_config "${PROJECT_NAME}Targets.cmake") - set(pkg_config "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc") - set(targets_export_name "${PROJECT_NAME}Targets") - set(namespace "${PROJECT_NAME}::") +if (SPDLOG_INSTALL) + set(project_config_in "${CMAKE_CURRENT_LIST_DIR}/cmake/spdlogConfig.cmake.in") + set(project_config_out "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfig.cmake") + set(config_targets_file "spdlogConfigTargets.cmake") + set(version_config_file "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfigVersion.cmake") + set(export_dest_dir "${CMAKE_INSTALL_LIBDIR}/spdlog/cmake") + + #--------------------------------------------------------------------------------------- + # include files + #--------------------------------------------------------------------------------------- + install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") + install(TARGETS spdlog spdlog_header_only EXPORT spdlog DESTINATION "${CMAKE_INSTALL_LIBDIR}/spdlog") + + #--------------------------------------------------------------------------------------- + # package and version files + #--------------------------------------------------------------------------------------- + install(EXPORT spdlog + DESTINATION ${export_dest_dir} + NAMESPACE spdlog:: + FILE ${config_targets_file}) - # generate package version file include(CMakePackageConfigHelpers) - write_basic_package_version_file( - "${version_config}" COMPATIBILITY SameMajorVersion - ) + configure_file("${project_config_in}" "${project_config_out}" @ONLY) + write_basic_package_version_file("${version_config_file}" COMPATIBILITY SameMajorVersion) + install(FILES + "${project_config_out}" + "${version_config_file}" DESTINATION "${export_dest_dir}") - # configure pkg config file - configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY) - # configure spdlogConfig.cmake file - configure_file("cmake/Config.cmake.in" "${project_config}" @ONLY) + #--------------------------------------------------------------------------------------- + # Support creation of installable packages + #--------------------------------------------------------------------------------------- + include(cmake/SpdlogCPack.cmake) - # install targets - install( - TARGETS spdlog - EXPORT "${targets_export_name}" - ) +endif () - # install headers - install( - DIRECTORY "${HEADER_BASE}/${PROJECT_NAME}" - DESTINATION "${include_install_dir}" - ) - - # install project config and version file - install( - FILES "${project_config}" "${version_config}" - DESTINATION "${config_install_dir}" - ) - - # install pkg config file - install( - FILES "${pkg_config}" - DESTINATION "${pkgconfig_install_dir}" - ) - - # install targets config file - install( - EXPORT "${targets_export_name}" - NAMESPACE "${namespace}" - DESTINATION "${config_install_dir}" - FILE ${targets_config} - ) - -# export build directory targets file -export( - EXPORT ${targets_export_name} - NAMESPACE "${namespace}" - FILE ${targets_config} -) - -# register project in CMake user registry -export(PACKAGE ${PROJECT_NAME}) - -endif() - -file(GLOB_RECURSE spdlog_include_SRCS "${HEADER_BASE}/*.h") -add_custom_target(spdlog_headers_for_ide SOURCES ${spdlog_include_SRCS}) diff --git a/appveyor.yml b/appveyor.yml index 98ce69ba..6aeee9a8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -26,7 +26,7 @@ build_script: 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% -DSPDLOG_BUILD_BENCH=OFF + cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DSPDLOG_BUILD_EXAMPLES=ON -DSPDLOG_BUILD_TESTS=ON cmake --build . --config %BUILD_TYPE% diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index 3c4a3f9d..5b88d41a 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -1,25 +1,5 @@ -# *************************************************************************/ -# * Copyright (c) 2015 Ruslan Baratov. */ -# * */ -# * Permission is hereby granted, free of charge, to any person obtaining */ -# * a copy of this software and associated documentation files (the */ -# * "Software"), to deal in the Software without restriction, including */ -# * without limitation the rights to use, copy, modify, merge, publish, */ -# * distribute, sublicense, and/or sell copies of the Software, and to */ -# * permit persons to whom the Software is furnished to do so, subject to */ -# * the following conditions: */ -# * */ -# * The above copyright notice and this permission notice shall be */ -# * included in all copies or substantial portions of the Software. */ -# * */ -# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -# *************************************************************************/ +# Copyright(c) 2019 spdlog authors +# Distributed under the MIT License (http://opensource.org/licenses/MIT) cmake_minimum_required(VERSION 3.1) project(SpdlogBench CXX) @@ -33,16 +13,16 @@ find_package(Threads REQUIRED) find_package(benchmark CONFIG REQUIRED) add_executable(bench bench.cpp) -target_link_libraries(bench PRIVATE spdlog::spdlog Threads::Threads) +target_link_libraries(bench PRIVATE spdlog::spdlog) add_executable(async_bench async_bench.cpp) -target_link_libraries(async_bench PRIVATE spdlog::spdlog Threads::Threads) +target_link_libraries(async_bench PRIVATE spdlog::spdlog) add_executable(latency latency.cpp) -target_link_libraries(latency PRIVATE benchmark::benchmark spdlog::spdlog Threads::Threads) +target_link_libraries(latency PRIVATE benchmark::benchmark spdlog::spdlog) add_executable(formatter-bench formatter-bench.cpp) -target_link_libraries(formatter-bench PRIVATE benchmark::benchmark spdlog::spdlog Threads::Threads) +target_link_libraries(formatter-bench PRIVATE benchmark::benchmark spdlog::spdlog) file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") diff --git a/bench/Makefile b/bench/Makefile deleted file mode 100644 index 868a7335..00000000 --- a/bench/Makefile +++ /dev/null @@ -1,31 +0,0 @@ -CXX ?= g++ -CXXFLAGS = -march=native -Wall -Wextra -pedantic -Wconversion -std=c++11 -pthread -I../include -fmax-errors=1 -CXX_RELEASE_FLAGS = -O3 -flto -Wl,--no-as-needed - - -binaries=bench async_bench latency formatter-bench - -all: $(binaries) - -bench: bench.cpp - $(CXX) bench.cpp -o bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS) - - -async_bench: async_bench.cpp - $(CXX) async_bench.cpp -o async_bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS) - - -latency: latency.cpp - $(CXX) latency.cpp -o latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -lbenchmark - - -formatter-bench: formatter-bench.cpp - $(CXX) formatter-bench.cpp -o formatter-bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -lbenchmark - - -.PHONY: clean - -clean: - rm -f *.o logs/* latecy_logs $(binaries) - -rebuild: clean all diff --git a/bench/formatter-bench.cpp b/bench/formatter-bench.cpp index 857f4c78..a4ddaa7b 100644 --- a/bench/formatter-bench.cpp +++ b/bench/formatter-bench.cpp @@ -8,19 +8,6 @@ #include "spdlog/spdlog.h" #include "spdlog/details/pattern_formatter.h" -void bench_scoped_pad(benchmark::State &state, size_t wrapped_size, spdlog::details::padding_info padinfo) -{ - fmt::memory_buffer dest; - for (auto _ : state) - { - { - spdlog::details::scoped_pad p(wrapped_size, padinfo, dest); - benchmark::DoNotOptimize(p); - dest.clear(); - } - } -} - void bench_formatter(benchmark::State &state, std::string pattern) { auto formatter = spdlog::details::make_unique(pattern); diff --git a/clang_tidy.sh b/clang_tidy.sh index 6e043e27..c09054c5 100755 --- a/clang_tidy.sh +++ b/clang_tidy.sh @@ -1,2 +1,2 @@ #!/bin/bash -clang-tidy example/example.cpp -- -I ./include +clang-tidy example/example.cpp -- -I ./include diff --git a/cmake/Config.cmake.in b/cmake/Config.cmake.in deleted file mode 100644 index 0b0fd119..00000000 --- a/cmake/Config.cmake.in +++ /dev/null @@ -1,31 +0,0 @@ -# *************************************************************************/ -# * Copyright (c) 2015 Ruslan Baratov. */ -# * */ -# * Permission is hereby granted, free of charge, to any person obtaining */ -# * a copy of this software and associated documentation files (the */ -# * "Software"), to deal in the Software without restriction, including */ -# * without limitation the rights to use, copy, modify, merge, publish, */ -# * distribute, sublicense, and/or sell copies of the Software, and to */ -# * permit persons to whom the Software is furnished to do so, subject to */ -# * the following conditions: */ -# * */ -# * The above copyright notice and this permission notice shall be */ -# * included in all copies or substantial portions of the Software. */ -# * */ -# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -# *************************************************************************/ - -set(SPDLOG_FMT_EXTERNAL @SPDLOG_FMT_EXTERNAL@) - -include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake") - -if(SPDLOG_FMT_EXTERNAL) - include(CMakeFindDependencyMacro) - find_dependency(fmt CONFIG) -endif() diff --git a/cmake/SpdlogCPack.cmake b/cmake/SpdlogCPack.cmake new file mode 100644 index 00000000..432503cb --- /dev/null +++ b/cmake/SpdlogCPack.cmake @@ -0,0 +1,26 @@ +set(CPACK_GENERATOR + TGZ + ZIP + ) + +set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0) +set(CPACK_INSTALL_CMAKE_PROJECTS + "${CMAKE_BINARY_DIR}" + "${PROJECT_NAME}" + ALL + . + ) + +set(CPACK_PROJECT_URL "https://github.com/gabime/spdlog") +set(CPACK_PACKAGE_VENDOR "Gabi Melman") +set(CPACK_PACKAGE_CONTACT "Gabi Melman ") +set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) +set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) +set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) +set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}) +if (PROJECT_VERSION_TWEAK) + set(CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION}.${PROJECT_VERSION_TWEAK}) +endif () +set(CPACK_PACKAGE_RELOCATABLE ON) + +include(CPack) diff --git a/cmake/spdlog.pc.in b/cmake/spdlog.pc.in deleted file mode 100644 index 262248a7..00000000 --- a/cmake/spdlog.pc.in +++ /dev/null @@ -1,6 +0,0 @@ -prefix=@CMAKE_INSTALL_PREFIX@ -includedir=${prefix}/include - -Name: @PROJECT_NAME@ -Description: Super fast C++ logging library. -Version: @PROJECT_VERSION@ diff --git a/cmake/spdlogConfig.cmake.in b/cmake/spdlogConfig.cmake.in new file mode 100644 index 00000000..43ffcf7e --- /dev/null +++ b/cmake/spdlogConfig.cmake.in @@ -0,0 +1,15 @@ +# Copyright(c) 2019 spdlog authors +# Distributed under the MIT License (http://opensource.org/licenses/MIT) + +find_package(Threads REQUIRED) + +set(SPDLOG_FMT_EXTERNAL @SPDLOG_FMT_EXTERNAL@) +set(config_targets_file @config_targets_file@) + +if(SPDLOG_FMT_EXTERNAL) + include(CMakeFindDependencyMacro) + find_dependency(fmt CONFIG) +endif() + + +include("${CMAKE_CURRENT_LIST_DIR}/${config_targets_file}") diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index a404682a..2fbbf1d2 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -1,49 +1,31 @@ -# *************************************************************************/ -# * Copyright (c) 2015 Ruslan Baratov. */ -# * */ -# * Permission is hereby granted, free of charge, to any person obtaining */ -# * a copy of this software and associated documentation files (the */ -# * "Software"), to deal in the Software without restriction, including */ -# * without limitation the rights to use, copy, modify, merge, publish, */ -# * distribute, sublicense, and/or sell copies of the Software, and to */ -# * permit persons to whom the Software is furnished to do so, subject to */ -# * the following conditions: */ -# * */ -# * The above copyright notice and this permission notice shall be */ -# * included in all copies or substantial portions of the Software. */ -# * */ -# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -# *************************************************************************/ +# Copyright(c) 2019 spdlog authors +# Distributed under the MIT License (http://opensource.org/licenses/MIT) cmake_minimum_required(VERSION 3.1) project(SpdlogExamples CXX) -if(NOT TARGET spdlog) - # Stand-alone build - find_package(spdlog CONFIG REQUIRED) -endif() - -find_package(Threads REQUIRED) - -add_executable(example example.cpp) -if(CMAKE_SYSTEM_NAME STREQUAL "Android") - find_library(log-lib log) - target_link_libraries(example spdlog::spdlog Threads::Threads log) +if(TARGET spdlog) + # If we're running this example as part of the primary spdlog applciation + # then add an alias. This allows us to use the same "spdlog::spdlog" + # below that a user would use (with the namespace) + add_library(spdlog::spdlog ALIAS spdlog) + add_library(spdlog::spdlog_header_only ALIAS spdlog_header_only) else() - target_link_libraries(example spdlog::spdlog Threads::Threads) + # Stand-alone build + find_package(spdlog REQUIRED) endif() +#--------------------------------------------------------------------------------------- +# Example of using pre-compiled library +#--------------------------------------------------------------------------------------- +add_executable(example example.cpp) +target_link_libraries(example spdlog::spdlog) -add_executable(multisink multisink.cpp) -target_link_libraries(multisink spdlog::spdlog Threads::Threads) +#--------------------------------------------------------------------------------------- +# Example of using header-only library +#--------------------------------------------------------------------------------------- +add_executable(example_header_only example.cpp) +target_link_libraries(example_header_only spdlog::spdlog_header_only) +# Create logs directory file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") - -enable_testing() -add_test(NAME example COMMAND example) diff --git a/example/Makefile b/example/Makefile deleted file mode 100644 index bd6b2922..00000000 --- a/example/Makefile +++ /dev/null @@ -1,21 +0,0 @@ -CXX ?= g++ -CXX_FLAGS = -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1 -Wconversion -CXX_RELEASE_FLAGS = -O3 -march=native -CXX_DEBUG_FLAGS= -g - -all: example -debug: example-debug - -example: example.cpp - $(CXX) example.cpp -o example $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS) - - -example-debug: example.cpp - $(CXX) example.cpp -o example-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS) - -clean: - rm -f *.o logs/*.txt example example-debug - - -rebuild: clean all -rebuild-debug: clean debug diff --git a/example/Makefile-all-warn b/example/Makefile-all-warn deleted file mode 100644 index 2ba68e2a..00000000 --- a/example/Makefile-all-warn +++ /dev/null @@ -1,22 +0,0 @@ -#-Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -CXX ?= g++ -CXX_FLAGS = -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1 -Wconversion -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-weak-vtables -Wno-global-constructors -CXX_RELEASE_FLAGS = -O3 -march=native -CXX_DEBUG_FLAGS= -g - -all: example -debug: example-debug - -example: example.cpp - $(CXX) example.cpp -o example $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS) - - -example-debug: example.cpp - $(CXX) example.cpp -o example-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS) - -clean: - rm -f *.o logs/*.txt example example-debug - - -rebuild: clean all -rebuild-debug: clean debug diff --git a/example/Makefile.clang b/example/Makefile.clang deleted file mode 100644 index 475855db..00000000 --- a/example/Makefile.clang +++ /dev/null @@ -1,26 +0,0 @@ -CXX = clang++ -CXXFLAGS = -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -I../include -CXX_RELEASE_FLAGS = -O2 -CXX_DEBUG_FLAGS= -g - - -all: example -debug: example-debug - -example: example.cpp - $(CXX) example.cpp -o example-clang $(CXXFLAGS) $(CXX_RELEASE_FLAGS) - - - -example-debug: example.cpp - $(CXX) example.cpp -o example-clang-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS) - - -clean: - rm -f *.o logs/*.txt example-clang example-clang-debug - - -rebuild: clean all -rebuild-debug: clean debug - - diff --git a/example/Makefile.mingw b/example/Makefile.mingw deleted file mode 100644 index 247a818b..00000000 --- a/example/Makefile.mingw +++ /dev/null @@ -1,25 +0,0 @@ -CXX ?= g++ -CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -pedantic -std=gnu++0x -pthread -Wl,--no-as-needed -I../include -CXX_RELEASE_FLAGS = -O3 -CXX_DEBUG_FLAGS= -g - - -all: example -debug: example-debug - -example: example.cpp - $(CXX) example.cpp -o example $(CXXFLAGS) $(CXX_RELEASE_FLAGS) - - -example-debug: example.cpp - $(CXX) example.cpp -o example-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS) - - -clean: - rm -f *.o logs/*.txt example example-debug - - -rebuild: clean all -rebuild-debug: clean debug - - diff --git a/example/example.cpp b/example/example.cpp index 345f8bd1..ce57fc9d 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -1,11 +1,8 @@ // // Copyright(c) 2015 Gabi Melman. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// -// + // spdlog usage example -// -// #include @@ -26,7 +23,7 @@ void clone_example(); int main(int, char *[]) { - spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH); + spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH); spdlog::warn("Easy padding in numbers like {:08d}", 12); spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); spdlog::info("Support for floats {:03.2f}", 1.23456); diff --git a/example/example.sln b/example/example.sln deleted file mode 100644 index b29a9e47..00000000 --- a/example/example.sln +++ /dev/null @@ -1,106 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27703.2018 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example", "example.vcxproj", "{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "spdlog", "spdlog", "{7FC6AB76-AD88-4135-888C-0568E81475AF}" - ProjectSection(SolutionItems) = preProject - ..\include\spdlog\async.h = ..\include\spdlog\async.h - ..\include\spdlog\async_logger.h = ..\include\spdlog\async_logger.h - ..\include\spdlog\common.h = ..\include\spdlog\common.h - ..\include\spdlog\formatter.h = ..\include\spdlog\formatter.h - ..\include\spdlog\logger.h = ..\include\spdlog\logger.h - ..\include\spdlog\spdlog.h = ..\include\spdlog\spdlog.h - ..\include\spdlog\tweakme.h = ..\include\spdlog\tweakme.h - ..\include\spdlog\version.h = ..\include\spdlog\version.h - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "details", "details", "{08E93803-E650-42D9-BBB4-3C16979F850E}" - ProjectSection(SolutionItems) = preProject - ..\include\spdlog\details\async_logger_impl.h = ..\include\spdlog\details\async_logger_impl.h - ..\include\spdlog\details\circular_q.h = ..\include\spdlog\details\circular_q.h - ..\include\spdlog\details\console_globals.h = ..\include\spdlog\details\console_globals.h - ..\include\spdlog\details\file_helper.h = ..\include\spdlog\details\file_helper.h - ..\include\spdlog\details\fmt_helper.h = ..\include\spdlog\details\fmt_helper.h - ..\include\spdlog\details\log_msg.h = ..\include\spdlog\details\log_msg.h - ..\include\spdlog\details\logger_impl.h = ..\include\spdlog\details\logger_impl.h - ..\include\spdlog\details\mpmc_blocking_q.h = ..\include\spdlog\details\mpmc_blocking_q.h - ..\include\spdlog\details\null_mutex.h = ..\include\spdlog\details\null_mutex.h - ..\include\spdlog\details\os.h = ..\include\spdlog\details\os.h - ..\include\spdlog\details\pattern_formatter.h = ..\include\spdlog\details\pattern_formatter.h - ..\include\spdlog\details\periodic_worker.h = ..\include\spdlog\details\periodic_worker.h - ..\include\spdlog\details\registry.h = ..\include\spdlog\details\registry.h - ..\include\spdlog\details\spdlog_impl.h = ..\include\spdlog\details\spdlog_impl.h - ..\include\spdlog\details\thread_pool.h = ..\include\spdlog\details\thread_pool.h - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fmt", "fmt", "{82378DE1-8463-4F91-91A0-C2C40E2AEA2A}" - ProjectSection(SolutionItems) = preProject - ..\include\spdlog\fmt\fmt.h = ..\include\spdlog\fmt\fmt.h - ..\include\spdlog\fmt\ostr.h = ..\include\spdlog\fmt\ostr.h - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "bundled", "bundled", "{D9CA4494-80D1-48D1-A897-D3564F7B27FF}" - ProjectSection(SolutionItems) = preProject - ..\include\spdlog\fmt\bundled\format.cc = ..\include\spdlog\fmt\bundled\format.cc - ..\include\spdlog\fmt\bundled\format.h = ..\include\spdlog\fmt\bundled\format.h - ..\include\spdlog\fmt\bundled\LICENSE.rst = ..\include\spdlog\fmt\bundled\LICENSE.rst - ..\include\spdlog\fmt\bundled\ostream.cc = ..\include\spdlog\fmt\bundled\ostream.cc - ..\include\spdlog\fmt\bundled\ostream.h = ..\include\spdlog\fmt\bundled\ostream.h - ..\include\spdlog\fmt\bundled\posix.cc = ..\include\spdlog\fmt\bundled\posix.cc - ..\include\spdlog\fmt\bundled\posix.h = ..\include\spdlog\fmt\bundled\posix.h - ..\include\spdlog\fmt\bundled\printf.cc = ..\include\spdlog\fmt\bundled\printf.cc - ..\include\spdlog\fmt\bundled\printf.h = ..\include\spdlog\fmt\bundled\printf.h - ..\include\spdlog\fmt\bundled\time.h = ..\include\spdlog\fmt\bundled\time.h - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sinks", "sinks", "{27D16BB9-2B81-4F61-80EC-0C7A777248E4}" - ProjectSection(SolutionItems) = preProject - ..\include\spdlog\sinks\android_sink.h = ..\include\spdlog\sinks\android_sink.h - ..\include\spdlog\sinks\ansicolor_sink.h = ..\include\spdlog\sinks\ansicolor_sink.h - ..\include\spdlog\sinks\base_sink.h = ..\include\spdlog\sinks\base_sink.h - ..\include\spdlog\sinks\dist_sink.h = ..\include\spdlog\sinks\dist_sink.h - ..\include\spdlog\sinks\file_sinks.h = ..\include\spdlog\sinks\file_sinks.h - ..\include\spdlog\sinks\msvc_sink.h = ..\include\spdlog\sinks\msvc_sink.h - ..\include\spdlog\sinks\null_sink.h = ..\include\spdlog\sinks\null_sink.h - ..\include\spdlog\sinks\ostream_sink.h = ..\include\spdlog\sinks\ostream_sink.h - ..\include\spdlog\sinks\sink.h = ..\include\spdlog\sinks\sink.h - ..\include\spdlog\sinks\stdout_color_sinks.h = ..\include\spdlog\sinks\stdout_color_sinks.h - ..\include\spdlog\sinks\stdout_sinks.h = ..\include\spdlog\sinks\stdout_sinks.h - ..\include\spdlog\sinks\syslog_sink.h = ..\include\spdlog\sinks\syslog_sink.h - ..\include\spdlog\sinks\wincolor_sink.h = ..\include\spdlog\sinks\wincolor_sink.h - ..\include\spdlog\sinks\windebug_sink.h = ..\include\spdlog\sinks\windebug_sink.h - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.ActiveCfg = Debug|Win32 - {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.Build.0 = Debug|Win32 - {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|x64.ActiveCfg = Debug|x64 - {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|x64.Build.0 = Debug|x64 - {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.ActiveCfg = Release|Win32 - {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.Build.0 = Release|Win32 - {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|x64.ActiveCfg = Release|x64 - {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {08E93803-E650-42D9-BBB4-3C16979F850E} = {7FC6AB76-AD88-4135-888C-0568E81475AF} - {82378DE1-8463-4F91-91A0-C2C40E2AEA2A} = {7FC6AB76-AD88-4135-888C-0568E81475AF} - {D9CA4494-80D1-48D1-A897-D3564F7B27FF} = {82378DE1-8463-4F91-91A0-C2C40E2AEA2A} - {27D16BB9-2B81-4F61-80EC-0C7A777248E4} = {7FC6AB76-AD88-4135-888C-0568E81475AF} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {1BF53532-C5DC-4236-B195-9E17CBE40A48} - EndGlobalSection -EndGlobal diff --git a/example/example.vcxproj b/example/example.vcxproj deleted file mode 100644 index c752a8e9..00000000 --- a/example/example.vcxproj +++ /dev/null @@ -1,167 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - - - - {9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2} - Win32Proj - . - - - - Application - true - v141 - Unicode - - - Application - true - v141 - Unicode - - - Application - false - v141 - true - Unicode - - - Application - false - v141 - true - Unicode - - - - - - - - - - - - - - - - - - - true - - - true - - - false - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - ..\include;%(AdditionalIncludeDirectories) - - - - - Console - true - - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - ..\include;%(AdditionalIncludeDirectories) - - - - - - - Console - true - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - ..\include;%(AdditionalIncludeDirectories) - - - - - Console - true - true - true - %(AdditionalLibraryDirectories) - %(AdditionalDependencies) - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - ..\include;%(AdditionalIncludeDirectories) - - - - - - - Console - true - true - true - %(AdditionalLibraryDirectories) - %(AdditionalDependencies) - - - - - - \ No newline at end of file diff --git a/example/jni/Android.mk b/example/jni/Android.mk deleted file mode 100644 index 7accbad3..00000000 --- a/example/jni/Android.mk +++ /dev/null @@ -1,15 +0,0 @@ -# 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) diff --git a/example/jni/Application.mk b/example/jni/Application.mk deleted file mode 100644 index dccd2a5a..00000000 --- a/example/jni/Application.mk +++ /dev/null @@ -1,2 +0,0 @@ -# Exceptions are used in spdlog. Link to an exception-ready C++ runtime. -APP_STL = gnustl_static diff --git a/example/jni/example.cpp b/example/jni/example.cpp deleted file mode 100644 index 48c4b19e..00000000 --- a/example/jni/example.cpp +++ /dev/null @@ -1,157 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// -// -// spdlog usage example -// -// - -#define SPDLOG_TRACE_ON -#define SPDLOG_DEBUG_ON - -#include "spdlog/spdlog.h" - -#include -#include - -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 - { - // 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->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"); - - spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function"); - - // Create basic file logger (not rotated) - auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-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/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.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"); - - // 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.. - async_example(); - - // 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 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 init failed: " << ex.what() << std::endl; - return 1; - } -} - -void async_example() -{ - size_t q_size = 4096; // queue size must be power of 2 - spdlog::set_async_mode(q_size); - auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt"); - for (int i = 0; i < 100; ++i) - async_file->info("Async message #{}", i); -} - -// syslog example (linux/osx/freebsd) -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 -} - -// 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 -} - -// user defined types logging by implementing operator<< -struct my_type -{ - int i; - template - 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() -{ - // 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); -} diff --git a/example/logs/.gitignore b/example/logs/.gitignore deleted file mode 100644 index 20325135..00000000 --- a/example/logs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.txt diff --git a/example/multisink.cpp b/example/multisink.cpp deleted file mode 100644 index fd79231c..00000000 --- a/example/multisink.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "spdlog/spdlog.h" -#include "spdlog/sinks/basic_file_sink.h" -#include "spdlog/sinks/stdout_sinks.h" - -#include -#include - -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 sinks; - sinks.push_back(std::make_shared()); - sinks.push_back(std::make_shared("./log_regular_file.txt")); - sinks.push_back(std::make_shared("./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 spdlog::spdlog_ex &ex) - { - std::cout << "Log init failed: " << ex.what() << std::endl; - return 1; - } -} diff --git a/example/utils.h b/example/utils.h deleted file mode 100644 index 91610128..00000000 --- a/example/utils.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include -#include -#include - -namespace utils { - -template -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(); -} - -} // namespace utils diff --git a/include/spdlog/async.h b/include/spdlog/async.h index 971becd7..a13cbdc7 100644 --- a/include/spdlog/async.h +++ b/include/spdlog/async.h @@ -1,8 +1,5 @@ - -// -// Copyright(c) 2018 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once diff --git a/include/spdlog/details/async_logger_impl.h b/include/spdlog/async_logger-inl.h similarity index 67% rename from include/spdlog/details/async_logger_impl.h rename to include/spdlog/async_logger-inl.h index aafcae65..ce2e8d2b 100644 --- a/include/spdlog/details/async_logger_impl.h +++ b/include/spdlog/async_logger-inl.h @@ -1,13 +1,13 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once -// async logger implementation -// uses a thread pool to perform the actual logging +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/async_logger.h" +#endif +#include "spdlog/sinks/sink.h" #include "spdlog/details/thread_pool.h" #include @@ -15,32 +15,26 @@ #include template -inline spdlog::async_logger::async_logger( +SPDLOG_INLINE spdlog::async_logger::async_logger( std::string logger_name, It begin, It end, std::weak_ptr tp, async_overflow_policy overflow_policy) : logger(std::move(logger_name), begin, end) , thread_pool_(std::move(tp)) , overflow_policy_(overflow_policy) -{ -} +{} -inline spdlog::async_logger::async_logger( +SPDLOG_INLINE spdlog::async_logger::async_logger( std::string logger_name, sinks_init_list sinks_list, std::weak_ptr tp, async_overflow_policy overflow_policy) : async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), std::move(tp), overflow_policy) -{ -} +{} -inline spdlog::async_logger::async_logger( +SPDLOG_INLINE spdlog::async_logger::async_logger( std::string logger_name, sink_ptr single_sink, std::weak_ptr tp, async_overflow_policy overflow_policy) : async_logger(std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy) -{ -} +{} // send the log message to the thread pool -inline void spdlog::async_logger::sink_it_(details::log_msg &msg) +SPDLOG_INLINE void spdlog::async_logger::sink_it_(details::log_msg &msg) { -#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) - incr_msg_counter_(msg); -#endif if (auto pool_ptr = thread_pool_.lock()) { pool_ptr->post_log(shared_from_this(), msg, overflow_policy_); @@ -52,7 +46,7 @@ inline void spdlog::async_logger::sink_it_(details::log_msg &msg) } // send flush request to the thread pool -inline void spdlog::async_logger::flush_() +SPDLOG_INLINE void spdlog::async_logger::flush_() { if (auto pool_ptr = thread_pool_.lock()) { @@ -67,7 +61,7 @@ inline void spdlog::async_logger::flush_() // // backend functions - called from the thread pool to do the actual job // -inline void spdlog::async_logger::backend_log_(const details::log_msg &incoming_log_msg) +SPDLOG_INLINE void spdlog::async_logger::backend_log_(const details::log_msg &incoming_log_msg) { try { @@ -79,7 +73,14 @@ inline void spdlog::async_logger::backend_log_(const details::log_msg &incoming_ } } } - SPDLOG_CATCH_AND_HANDLE + catch (const std::exception &ex) + { + err_handler_(ex.what()); + } + catch (...) + { + err_handler_("Unknown exception in logger"); + } if (should_flush_(incoming_log_msg)) { @@ -87,7 +88,7 @@ inline void spdlog::async_logger::backend_log_(const details::log_msg &incoming_ } } -inline void spdlog::async_logger::backend_flush_() +SPDLOG_INLINE void spdlog::async_logger::backend_flush_() { try { @@ -96,15 +97,22 @@ inline void spdlog::async_logger::backend_flush_() sink->flush(); } } - SPDLOG_CATCH_AND_HANDLE + catch (const std::exception &ex) + { + err_handler_(ex.what()); + } + catch (...) + { + err_handler_("Unknown exception in logger"); + } } -inline std::shared_ptr spdlog::async_logger::clone(std::string new_name) +SPDLOG_INLINE std::shared_ptr spdlog::async_logger::clone(std::string new_name) { auto cloned = std::make_shared(std::move(new_name), sinks_.begin(), sinks_.end(), thread_pool_, overflow_policy_); cloned->set_level(this->level()); cloned->flush_on(this->flush_level()); - cloned->set_error_handler(this->error_handler()); + cloned->set_error_handler(this->custom_err_handler_); return std::move(cloned); } diff --git a/include/spdlog/async_logger.h b/include/spdlog/async_logger.h index a7ecb787..9651da4d 100644 --- a/include/spdlog/async_logger.h +++ b/include/spdlog/async_logger.h @@ -1,31 +1,21 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once -// Very fast asynchronous logger (millions of logs per second on an average -// desktop) -// Uses pre allocated lockfree queue for maximum throughput even under large -// number of threads. +// Fast asynchronous logger. +// Uses pre allocated queue. // Creates a single back thread to pop messages from the queue and log them. // // Upon each log write the logger: // 1. Checks if its log level is enough to log the message // 2. Push a new copy of the message to a queue (or block the caller until // space is available in the queue) -// 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 -#include -#include - namespace spdlog { // Async overflow policy - block by default. @@ -70,4 +60,6 @@ private: }; } // namespace spdlog -#include "details/async_logger_impl.h" +#ifdef SPDLOG_HEADER_ONLY +#include "async_logger-inl.h" +#endif diff --git a/include/spdlog/common-inl.h b/include/spdlog/common-inl.h new file mode 100644 index 00000000..6076b8cb --- /dev/null +++ b/include/spdlog/common-inl.h @@ -0,0 +1,57 @@ +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/common.h" +#endif + +namespace spdlog { +namespace level { +static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES; + +static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES; + +SPDLOG_INLINE string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT +{ + return level_string_views[l]; +} + +SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT +{ + return short_level_names[l]; +} + +SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT +{ + int level = 0; + for (const auto &level_str : level_string_views) + { + if (level_str == name) + { + return static_cast(level); + } + level++; + } + return level::off; +} +} // namespace level + +SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg) + : msg_(std::move(msg)) +{} + +SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno) +{ + fmt::memory_buffer outbuf; + fmt::format_system_error(outbuf, last_errno, msg); + msg_ = fmt::to_string(outbuf); +} + +SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT +{ + return msg_.c_str(); +} + +} // namespace spdlog diff --git a/include/spdlog/common.h b/include/spdlog/common.h index 85e3cecd..905f469b 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once @@ -9,21 +7,25 @@ #include #include -#include #include #include #include #include -#include #include -#include +#include #if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) #include #include #endif -#include "spdlog/details/null_mutex.h" +#ifdef SPDLOG_COMPILED_LIB +#undef SPDLOG_HEADER_ONLY +#define SPDLOG_INLINE +#else +#define SPDLOG_HEADER_ONLY +#define SPDLOG_INLINE inline +#endif #include "spdlog/fmt/fmt.h" @@ -76,10 +78,23 @@ namespace sinks { class sink; } +#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) +using filename_t = std::wstring; +#define SPDLOG_FILENAME_T(s) L##s +inline std::string filename_to_str(const filename_t &filename) +{ + std::wstring_convert, wchar_t> c; + return c.to_bytes(filename); +} +#else +using filename_t = std::string; +#define SPDLOG_FILENAME_T(s) s +#endif + using log_clock = std::chrono::system_clock; using sink_ptr = std::shared_ptr; using sinks_init_list = std::initializer_list; -using log_err_handler = std::function; +using err_handler = std::function; // string_view type - either std::string_view or fmt::string_view (pre c++17) #if defined(FMT_USE_STD_STRING_VIEW) @@ -125,36 +140,18 @@ enum level_enum "trace", "debug", "info", "warning", "error", "critical", "off" \ } #endif -static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES; #if !defined(SPDLOG_SHORT_LEVEL_NAMES) -#define SPDLOG_SHORT_LEVEL_NAMES {"T", "D", "I", "W", "E", "C", "O"} -#endif -static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES; -inline string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT -{ - return level_string_views[l]; -} - -inline const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT -{ - return short_level_names[l]; -} - -inline spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT -{ - int level = 0; - for (const auto &level_str : level_string_views) - { - if (level_str == name) - { - return static_cast(level); - } - level++; +#define SPDLOG_SHORT_LEVEL_NAMES \ + { \ + "T", "D", "I", "W", "E", "C", "O" \ } - return level::off; -} +#endif + +string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT; +const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT; +spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT; using level_hasher = std::hash; } // namespace level @@ -185,58 +182,30 @@ enum class pattern_time_type class spdlog_ex : public std::exception { public: - explicit spdlog_ex(std::string msg) - : msg_(std::move(msg)) - { - } - - spdlog_ex(const std::string &msg, int last_errno) - { - fmt::memory_buffer outbuf; - fmt::format_system_error(outbuf, last_errno, msg); - msg_ = fmt::to_string(outbuf); - } - - const char *what() const SPDLOG_NOEXCEPT override - { - return msg_.c_str(); - } + explicit spdlog_ex(std::string msg); + spdlog_ex(const std::string &msg, int last_errno); + const char *what() const SPDLOG_NOEXCEPT override; private: std::string msg_; }; -// -// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) -// -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) -using filename_t = std::wstring; -#else -using filename_t = std::string; -#endif - struct source_loc { - SPDLOG_CONSTEXPR source_loc() - : filename{""} - , line{0} - , funcname{""} - { - } + SPDLOG_CONSTEXPR source_loc() = default; SPDLOG_CONSTEXPR source_loc(const char *filename_in, int line_in, const char *funcname_in) : filename{filename_in} - , line{static_cast(line_in)} + , line{line_in} , funcname{funcname_in} - { - } + {} SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT { return line == 0; } - const char *filename; - uint32_t line; - const char *funcname; + const char *filename{nullptr}; + int line{0}; + const char *funcname{nullptr}; }; namespace details { @@ -254,3 +223,7 @@ std::unique_ptr make_unique(Args &&... args) #endif } // namespace details } // namespace spdlog + +#ifdef SPDLOG_HEADER_ONLY +#include "common-inl.h" +#endif diff --git a/include/spdlog/details/circular_q.h b/include/spdlog/details/circular_q.h index b01325bb..b6324e06 100644 --- a/include/spdlog/details/circular_q.h +++ b/include/spdlog/details/circular_q.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2018 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// // cirucal q view of std::vector. #pragma once @@ -19,8 +17,7 @@ public: explicit circular_q(size_t max_items) : max_items_(max_items + 1) // one item is reserved as marker for full q , v_(max_items_) - { - } + {} // push back, overrun (oldest) item if no room left void push_back(T &&item) diff --git a/include/spdlog/details/console_globals.h b/include/spdlog/details/console_globals.h index e2afb6bf..85ff9023 100644 --- a/include/spdlog/details/console_globals.h +++ b/include/spdlog/details/console_globals.h @@ -1,8 +1,7 @@ -#pragma once -// -// Copyright(c) 2018 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// + +#pragma once #include "spdlog/details/null_mutex.h" #include diff --git a/include/spdlog/details/file_helper-inl.h b/include/spdlog/details/file_helper-inl.h new file mode 100644 index 00000000..1460fc2b --- /dev/null +++ b/include/spdlog/details/file_helper-inl.h @@ -0,0 +1,132 @@ +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/details/file_helper.h" +#endif + +#include "spdlog/details/os.h" + +#include +#include +#include +#include +#include +#include + +namespace spdlog { +namespace details { + +SPDLOG_INLINE file_helper::~file_helper() +{ + close(); +} + +SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate) +{ + close(); + auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab"); + _filename = fname; + for (int tries = 0; tries < open_tries; ++tries) + { + if (!os::fopen_s(&fd_, fname, mode)) + { + return; + } + + details::os::sleep_for_millis(open_interval); + } + + throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno); +} + +SPDLOG_INLINE void file_helper::reopen(bool truncate) +{ + if (_filename.empty()) + { + throw spdlog_ex("Failed re opening file - was not opened before"); + } + open(_filename, truncate); +} + +SPDLOG_INLINE void file_helper::flush() +{ + std::fflush(fd_); +} + +SPDLOG_INLINE void file_helper::close() +{ + if (fd_ != nullptr) + { + std::fclose(fd_); + fd_ = nullptr; + } +} + +SPDLOG_INLINE void file_helper::write(const fmt::memory_buffer &buf) +{ + size_t msg_size = buf.size(); + auto data = buf.data(); + if (std::fwrite(data, 1, msg_size, fd_) != msg_size) + { + throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno); + } +} + +SPDLOG_INLINE size_t file_helper::size() const +{ + if (fd_ == nullptr) + { + throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename)); + } + return os::filesize(fd_); +} + +SPDLOG_INLINE const filename_t &file_helper::filename() const +{ + return _filename; +} + +SPDLOG_INLINE bool file_helper::file_exists(const filename_t &fname) +{ + 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") +SPDLOG_INLINE std::tuple file_helper::split_by_extension(const 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, filename_t()); + } + + // treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile" + auto folder_index = fname.rfind(details::os::folder_sep); + if (folder_index != filename_t::npos && folder_index >= ext_index - 1) + { + return std::make_tuple(fname, filename_t()); + } + + // finally - return a valid base and extension tuple + return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index)); +} +} // namespace details +} // namespace spdlog diff --git a/include/spdlog/details/file_helper.h b/include/spdlog/details/file_helper.h index 8c1132d9..afacc4c6 100644 --- a/include/spdlog/details/file_helper.h +++ b/include/spdlog/details/file_helper.h @@ -1,113 +1,37 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once -// Helper class for file sinks. -// When failing to open a file, retry several times(5) with a delay interval(10 ms). -// Throw spdlog_ex exception on errors. - -#include "spdlog/details/log_msg.h" -#include "spdlog/details/os.h" - -#include -#include -#include -#include -#include +#include "spdlog/common.h" #include namespace spdlog { namespace details { +// Helper class for file sinks. +// When failing to open a file, retry several times(5) with a delay interval(10 ms). +// Throw spdlog_ex exception on errors. + class file_helper { - public: const int open_tries = 5; const int open_interval = 10; - explicit file_helper() = default; file_helper(const file_helper &) = delete; file_helper &operator=(const file_helper &) = delete; + ~file_helper(); - ~file_helper() - { - close(); - } - - void open(const filename_t &fname, bool truncate = false) - { - close(); - auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab"); - _filename = fname; - for (int tries = 0; tries < open_tries; ++tries) - { - if (!os::fopen_s(&fd_, fname, mode)) - { - return; - } - - details::os::sleep_for_millis(open_interval); - } - - throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno); - } - - void reopen(bool truncate) - { - if (_filename.empty()) - { - throw spdlog_ex("Failed re opening file - was not opened before"); - } - open(_filename, truncate); - } - - void flush() - { - std::fflush(fd_); - } - - void close() - { - if (fd_ != nullptr) - { - std::fclose(fd_); - fd_ = nullptr; - } - } - - void write(const fmt::memory_buffer &buf) - { - size_t msg_size = buf.size(); - auto data = buf.data(); - if (std::fwrite(data, 1, msg_size, fd_) != msg_size) - { - throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno); - } - } - - size_t size() const - { - if (fd_ == nullptr) - { - throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename)); - } - return os::filesize(fd_); - } - - const filename_t &filename() const - { - return _filename; - } - - static bool file_exists(const filename_t &fname) - { - return os::file_exists(fname); - } + void open(const filename_t &fname, bool truncate = false); + void reopen(bool truncate); + void flush(); + void close(); + void write(const fmt::memory_buffer &buf); + size_t size() const; + const filename_t &filename() const; + static bool file_exists(const filename_t &fname); // // return file path and its extension: @@ -122,27 +46,7 @@ public: // ".mylog" => (".mylog". "") // "my_folder/.mylog" => ("my_folder/.mylog", "") // "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt") - static std::tuple split_by_extension(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.rfind(details::os::folder_sep); - if (folder_index != filename_t::npos && folder_index >= ext_index - 1) - { - return std::make_tuple(fname, spdlog::filename_t()); - } - - // finally - return a valid base and extension tuple - return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index)); - } + static std::tuple split_by_extension(const filename_t &fname); private: std::FILE *fd_{nullptr}; @@ -150,3 +54,7 @@ private: }; } // namespace details } // namespace spdlog + +#ifdef SPDLOG_HEADER_ONLY +#include "file_helper-inl.h" +#endif diff --git a/include/spdlog/details/fmt_helper.h b/include/spdlog/details/fmt_helper.h index d76aac45..da253720 100644 --- a/include/spdlog/details/fmt_helper.h +++ b/include/spdlog/details/fmt_helper.h @@ -1,12 +1,11 @@ -// -// Created by gabi on 6/15/18. -// - +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) #pragma once #include #include #include "spdlog/fmt/fmt.h" +#include "spdlog/common.h" // Some fmt helpers to efficiently format and pad ints and strings namespace spdlog { diff --git a/include/spdlog/details/log_msg-inl.h b/include/spdlog/details/log_msg-inl.h new file mode 100644 index 00000000..0df717f9 --- /dev/null +++ b/include/spdlog/details/log_msg-inl.h @@ -0,0 +1,36 @@ +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/details/log_msg.h" +#endif + +#include "spdlog/details/os.h" +#include "spdlog/sinks/sink.h" + +namespace spdlog { +namespace details { + +SPDLOG_INLINE log_msg::log_msg( + spdlog::source_loc loc, const std::string *loggers_name, spdlog::level::level_enum lvl, spdlog::string_view_t view) + : logger_name(loggers_name) + , level(lvl) +#ifndef SPDLOG_NO_DATETIME + , time(os::now()) +#endif + +#ifndef SPDLOG_NO_THREAD_ID + , thread_id(os::thread_id()) +#endif + , source(loc) + , payload(view) +{} + +SPDLOG_INLINE log_msg::log_msg(const std::string *loggers_name, spdlog::level::level_enum lvl, spdlog::string_view_t view) + : log_msg(source_loc{}, loggers_name, lvl, view) +{} + +} // namespace details +} // namespace spdlog diff --git a/include/spdlog/details/log_msg.h b/include/spdlog/details/log_msg.h index 69422ba3..152d27ad 100644 --- a/include/spdlog/details/log_msg.h +++ b/include/spdlog/details/log_msg.h @@ -1,41 +1,17 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once #include "spdlog/common.h" -#include "spdlog/details/os.h" - #include -#include namespace spdlog { namespace details { struct log_msg { - - log_msg(source_loc loc, const std::string *loggers_name, level::level_enum lvl, string_view_t view) - : logger_name(loggers_name) - , level(lvl) -#ifndef SPDLOG_NO_DATETIME - , time(os::now()) -#endif - -#ifndef SPDLOG_NO_THREAD_ID - , thread_id(os::thread_id()) -#endif - , source(loc) - , payload(view) - { - } - - log_msg(const std::string *loggers_name, level::level_enum lvl, string_view_t view) - : log_msg(source_loc{}, loggers_name, lvl, view) - { - } - + log_msg(source_loc loc, const std::string *loggers_name, level::level_enum lvl, string_view_t view); + log_msg(const std::string *loggers_name, level::level_enum lvl, string_view_t view); log_msg(const log_msg &other) = default; const std::string *logger_name{nullptr}; @@ -53,3 +29,7 @@ struct log_msg }; } // namespace details } // namespace spdlog + +#ifdef SPDLOG_HEADER_ONLY +#include "log_msg-inl.h" +#endif diff --git a/include/spdlog/details/logger_impl.h b/include/spdlog/details/logger_impl.h deleted file mode 100644 index d44652b5..00000000 --- a/include/spdlog/details/logger_impl.h +++ /dev/null @@ -1,435 +0,0 @@ -// -// Copyright(c) 2015 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include "spdlog/details/fmt_helper.h" - -#include -#include - -#define SPDLOG_CATCH_AND_HANDLE \ - catch (const std::exception &ex) \ - { \ - err_handler_(ex.what()); \ - } \ - catch (...) \ - { \ - err_handler_("Unknown exception in logger"); \ - } - -// create logger with given name, sinks and the default pattern formatter -// all other ctors will call this one -template -inline spdlog::logger::logger(std::string logger_name, It begin, It end) - : name_(std::move(logger_name)) - , sinks_(begin, end) -{ -} - -// ctor with sinks as init list -inline spdlog::logger::logger(std::string logger_name, sinks_init_list sinks_list) - : logger(std::move(logger_name), sinks_list.begin(), sinks_list.end()) -{ -} - -// ctor with single sink -inline spdlog::logger::logger(std::string logger_name, spdlog::sink_ptr single_sink) - : logger(std::move(logger_name), {std::move(single_sink)}) -{ -} - -inline spdlog::logger::~logger() = default; - -inline void spdlog::logger::set_formatter(std::unique_ptr f) -{ - for (auto &sink : sinks_) - { - sink->set_formatter(f->clone()); - } -} - -inline void spdlog::logger::set_pattern(std::string pattern, pattern_time_type time_type) -{ - auto new_formatter = details::make_unique(std::move(pattern), time_type); - set_formatter(std::move(new_formatter)); -} - -template -inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const char *fmt, const Args &... args) -{ - if (!should_log(lvl)) - { - return; - } - - try - { - using details::fmt_helper::to_string_view; - fmt::memory_buffer buf; - fmt::format_to(buf, fmt, args...); - details::log_msg log_msg(source, &name_, lvl, to_string_view(buf)); - sink_it_(log_msg); - } - SPDLOG_CATCH_AND_HANDLE -} - -template -inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args) -{ - log(source_loc{}, lvl, fmt, args...); -} - -inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const char *msg) -{ - if (!should_log(lvl)) - { - return; - } - - try - { - details::log_msg log_msg(source, &name_, lvl, spdlog::string_view_t(msg)); - sink_it_(log_msg); - } - SPDLOG_CATCH_AND_HANDLE -} - -inline void spdlog::logger::log(level::level_enum lvl, const char *msg) -{ - log(source_loc{}, lvl, msg); -} - -template -inline void spdlog::logger::log(level::level_enum lvl, const T &msg) -{ - log(source_loc{}, lvl, msg); -} - -template::value, T>::type *> -inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const T &msg) -{ - if (!should_log(lvl)) - { - return; - } - try - { - details::log_msg log_msg(source, &name_, lvl, msg); - sink_it_(log_msg); - } - SPDLOG_CATCH_AND_HANDLE -} - -template::value, T>::type *> -inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const T &msg) -{ - if (!should_log(lvl)) - { - return; - } - try - { - using details::fmt_helper::to_string_view; - fmt::memory_buffer buf; - fmt::format_to(buf, "{}", msg); - details::log_msg log_msg(source, &name_, lvl, to_string_view(buf)); - sink_it_(log_msg); - } - SPDLOG_CATCH_AND_HANDLE -} - -template -inline void spdlog::logger::trace(const char *fmt, const Args &... args) -{ - log(level::trace, fmt, args...); -} - -template -inline void spdlog::logger::debug(const char *fmt, const Args &... args) -{ - log(level::debug, fmt, args...); -} - -template -inline void spdlog::logger::info(const char *fmt, const Args &... args) -{ - log(level::info, fmt, args...); -} - -template -inline void spdlog::logger::warn(const char *fmt, const Args &... args) -{ - log(level::warn, fmt, args...); -} - -template -inline void spdlog::logger::error(const char *fmt, const Args &... args) -{ - log(level::err, fmt, args...); -} - -template -inline void spdlog::logger::critical(const char *fmt, const Args &... args) -{ - log(level::critical, fmt, args...); -} - -template -inline void spdlog::logger::trace(const T &msg) -{ - log(level::trace, msg); -} - -template -inline void spdlog::logger::debug(const T &msg) -{ - log(level::debug, msg); -} - -template -inline void spdlog::logger::info(const T &msg) -{ - log(level::info, msg); -} - -template -inline void spdlog::logger::warn(const T &msg) -{ - log(level::warn, msg); -} - -template -inline void spdlog::logger::error(const T &msg) -{ - log(level::err, msg); -} - -template -inline void spdlog::logger::critical(const T &msg) -{ - log(level::critical, msg); -} - -#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT - -inline void wbuf_to_utf8buf(const fmt::wmemory_buffer &wbuf, fmt::memory_buffer &target) -{ - int wbuf_size = static_cast(wbuf.size()); - if (wbuf_size == 0) - { - return; - } - - auto result_size = ::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, NULL, 0, NULL, NULL); - - if (result_size > 0) - { - target.resize(result_size); - ::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, &target.data()[0], result_size, NULL, NULL); - } - else - { - throw spdlog::spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError())); - } -} - -template -inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const wchar_t *fmt, const Args &... args) -{ - if (!should_log(lvl)) - { - return; - } - - try - { - // format to wmemory_buffer and convert to utf8 - using details::fmt_helper::to_string_view; - fmt::wmemory_buffer wbuf; - fmt::format_to(wbuf, fmt, args...); - fmt::memory_buffer buf; - wbuf_to_utf8buf(wbuf, buf); - details::log_msg log_msg(source, &name_, lvl, to_string_view(buf)); - sink_it_(log_msg); - } - SPDLOG_CATCH_AND_HANDLE -} - -template -inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args) -{ - log(source_loc{}, lvl, fmt, args...); -} - -template -inline void spdlog::logger::trace(const wchar_t *fmt, const Args &... args) -{ - log(level::trace, fmt, args...); -} - -template -inline void spdlog::logger::debug(const wchar_t *fmt, const Args &... args) -{ - log(level::debug, fmt, args...); -} - -template -inline void spdlog::logger::info(const wchar_t *fmt, const Args &... args) -{ - log(level::info, fmt, args...); -} - -template -inline void spdlog::logger::warn(const wchar_t *fmt, const Args &... args) -{ - log(level::warn, fmt, args...); -} - -template -inline void spdlog::logger::error(const wchar_t *fmt, const Args &... args) -{ - log(level::err, fmt, args...); -} - -template -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 -// -inline const std::string &spdlog::logger::name() const -{ - return name_; -} - -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_ = std::move(err_handler); -} - -inline spdlog::log_err_handler spdlog::logger::error_handler() const -{ - return err_handler_; -} - -inline void spdlog::logger::flush() -{ - try - { - flush_(); - } - SPDLOG_CATCH_AND_HANDLE -} - -inline void spdlog::logger::flush_on(level::level_enum log_level) -{ - flush_level_.store(log_level); -} - -inline spdlog::level::level_enum spdlog::logger::flush_level() const -{ - return static_cast(flush_level_.load(std::memory_order_relaxed)); -} - -inline bool spdlog::logger::should_flush_(const details::log_msg &msg) -{ - auto flush_level = flush_level_.load(std::memory_order_relaxed); - return (msg.level >= flush_level) && (msg.level != level::off); -} - -inline spdlog::level::level_enum spdlog::logger::default_level() -{ - return static_cast(SPDLOG_ACTIVE_LEVEL); -} - -inline spdlog::level::level_enum spdlog::logger::level() const -{ - return static_cast(level_.load(std::memory_order_relaxed)); -} - -inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const -{ - return msg_level >= level_.load(std::memory_order_relaxed); -} - -// -// protected virtual called at end of each user log call (if enabled) by the -// line_logger -// -inline void spdlog::logger::sink_it_(details::log_msg &msg) -{ -#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER) - incr_msg_counter_(msg); -#endif - for (auto &sink : sinks_) - { - if (sink->should_log(msg.level)) - { - sink->log(msg); - } - } - - if (should_flush_(msg)) - { - flush_(); - } -} - -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; - } - last_err_time_ = now; - 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); - fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), msg); -} - -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::logger::sinks() const -{ - return sinks_; -} - -inline std::vector &spdlog::logger::sinks() -{ - return sinks_; -} - -inline std::shared_ptr spdlog::logger::clone(std::string logger_name) -{ - auto cloned = std::make_shared(std::move(logger_name), sinks_.begin(), sinks_.end()); - cloned->set_level(this->level()); - cloned->flush_on(this->flush_level()); - cloned->set_error_handler(this->error_handler()); - return cloned; -} diff --git a/include/spdlog/details/mpmc_blocking_q.h b/include/spdlog/details/mpmc_blocking_q.h index ca789fc6..4d162d83 100644 --- a/include/spdlog/details/mpmc_blocking_q.h +++ b/include/spdlog/details/mpmc_blocking_q.h @@ -1,9 +1,7 @@ -#pragma once - -// -// Copyright(c) 2018 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// + +#pragma once // multi producer-multi consumer blocking queue. // enqueue(..) - will block until room found to put the new message. @@ -27,8 +25,7 @@ public: using item_type = T; explicit mpmc_blocking_queue(size_t max_items) : q_(max_items) - { - } + {} #ifndef __MINGW32__ // try to enqueue and block if no room left diff --git a/include/spdlog/details/null_mutex.h b/include/spdlog/details/null_mutex.h index 3f495bd9..aa54f79a 100644 --- a/include/spdlog/details/null_mutex.h +++ b/include/spdlog/details/null_mutex.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once @@ -27,8 +25,7 @@ struct null_atomic_int explicit null_atomic_int(int val) : value(val) - { - } + {} int load(std::memory_order) const { diff --git a/include/spdlog/details/os-inl.h b/include/spdlog/details/os-inl.h new file mode 100644 index 00000000..d6af8048 --- /dev/null +++ b/include/spdlog/details/os-inl.h @@ -0,0 +1,425 @@ +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/details/os.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 + +#ifndef NOMINMAX +#define NOMINMAX // prevent windows redefining min/max +#endif + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include // _get_osfhandle and _isatty support +#include // _get_pid support +#include + +#ifdef __MINGW32__ +#include +#endif + +#else // unix + +#include +#include + +#ifdef __linux__ +#include //Use gettid() syscall under linux to get thread id + +#elif __FreeBSD__ +#include //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 { +namespace os { + +SPDLOG_INLINE spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT +{ + +#if defined __linux__ && defined SPDLOG_CLOCK_COARSE + timespec ts; + ::clock_gettime(CLOCK_REALTIME_COARSE, &ts); + return std::chrono::time_point( + std::chrono::duration_cast(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec))); + +#else + return log_clock::now(); +#endif +} +SPDLOG_INLINE std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT +{ + +#ifdef _WIN32 + std::tm tm; + localtime_s(&tm, &time_tt); +#else + std::tm tm; + localtime_r(&time_tt, &tm); +#endif + return tm; +} + +SPDLOG_INLINE std::tm localtime() SPDLOG_NOEXCEPT +{ + std::time_t now_t = time(nullptr); + return localtime(now_t); +} + +SPDLOG_INLINE std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT +{ + +#ifdef _WIN32 + std::tm tm; + gmtime_s(&tm, &time_tt); +#else + std::tm tm; + gmtime_r(&time_tt, &tm); +#endif + return tm; +} + +SPDLOG_INLINE std::tm gmtime() SPDLOG_NOEXCEPT +{ + std::time_t now_t = time(nullptr); + return gmtime(now_t); +} + +SPDLOG_INLINE void prevent_child_fd(FILE *f) +{ + +#ifdef _WIN32 +#if !defined(__cplusplus_winrt) + auto file_handle = (HANDLE)_get_osfhandle(_fileno(f)); + if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) + throw spdlog_ex("SetHandleInformation failed", errno); +#endif +#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 +SPDLOG_INLINE bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) +{ +#ifdef _WIN32 +#ifdef SPDLOG_WCHAR_FILENAMES + *fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); +#else + *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); +#endif +#else // unix + *fp = fopen((filename.c_str()), mode.c_str()); +#endif + +#ifdef SPDLOG_PREVENT_CHILD_FD + if (*fp != nullptr) + { + prevent_child_fd(*fp); + } +#endif + return *fp == nullptr; +} + +SPDLOG_INLINE int remove(const filename_t &filename) SPDLOG_NOEXCEPT +{ +#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) + return _wremove(filename.c_str()); +#else + return std::remove(filename.c_str()); +#endif +} + +SPDLOG_INLINE int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT +{ +#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) + return _wrename(filename1.c_str(), filename2.c_str()); +#else + return std::rename(filename1.c_str(), filename2.c_str()); +#endif +} + +// Return if file exists +SPDLOG_INLINE bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT +{ +#ifdef _WIN32 +#ifdef SPDLOG_WCHAR_FILENAMES + auto attribs = GetFileAttributesW(filename.c_str()); +#else + auto attribs = GetFileAttributesA(filename.c_str()); +#endif + 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); +#endif +} + +// Return file size according to open FILE* object +SPDLOG_INLINE size_t filesize(FILE *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 + __int64 ret = _filelengthi64(fd); + if (ret >= 0) + { + return static_cast(ret); + } + +#else // windows 32 bits + long ret = _filelength(fd); + if (ret >= 0) + { + return static_cast(ret); + } +#endif + +#else // unix + 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(st.st_size); + } +#else // unix 32 bits or cygwin + struct stat st; + + if (::fstat(fd, &st) == 0) + { + return static_cast(st.st_size); + } +#endif +#endif + throw spdlog_ex("Failed getting file size from fd", errno); +} + +// Return utc offset in minutes or throw spdlog_ex on failure +SPDLOG_INLINE int utc_minutes_offset(const std::tm &tm) +{ + +#ifdef _WIN32 +#if _WIN32_WINNT < _WIN32_WINNT_WS08 + TIME_ZONE_INFORMATION tzinfo; + auto rv = GetTimeZoneInformation(&tzinfo); +#else + DYNAMIC_TIME_ZONE_INFORMATION tzinfo; + auto rv = GetDynamicTimeZoneInformation(&tzinfo); +#endif + if (rv == TIME_ZONE_ID_INVALID) + throw spdlog::spdlog_ex("Failed getting timezone info. ", errno); + + int offset = -tzinfo.Bias; + if (tm.tm_isdst) + { + offset -= tzinfo.DaylightBias; + } + else + { + offset -= tzinfo.StandardBias; + } + return offset; +#else + +#if defined(sun) || defined(__sun) || defined(_AIX) + // '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; + } + }; + + auto offset_seconds = helper::calculate_gmt_offset(tm); +#else + auto offset_seconds = tm.tm_gmtoff; +#endif + + return static_cast(offset_seconds / 60); +#endif +} + +// Return current thread id as size_t +// It exists because the std::this_thread::get_id() is much slower(especially +// under VS 2013) +SPDLOG_INLINE size_t _thread_id() SPDLOG_NOEXCEPT +{ +#ifdef _WIN32 + return static_cast(::GetCurrentThreadId()); +#elif __linux__ +#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21) +#define SYS_gettid __NR_gettid +#endif + return static_cast(syscall(SYS_gettid)); +#elif __FreeBSD__ + long tid; + thr_self(&tid); + return static_cast(tid); +#elif __APPLE__ + uint64_t tid; + pthread_threadid_np(nullptr, &tid); + return static_cast(tid); +#else // Default to standard C++11 (other Unix) + return static_cast(std::hash()(std::this_thread::get_id())); +#endif +} + +// Return current thread id as size_t (from thread local storage) +SPDLOG_INLINE size_t thread_id() SPDLOG_NOEXCEPT +{ +#if defined(SPDLOG_NO_TLS) + return _thread_id(); +#else // cache thread id in tls + static thread_local const size_t tid = _thread_id(); + return tid; +#endif +} + +// This is avoid msvc issue in sleep_for that happens if the clock changes. +// See https://github.com/gabime/spdlog/issues/609 +SPDLOG_INLINE void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT +{ +#if defined(_WIN32) + ::Sleep(milliseconds); +#else + std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); +#endif +} + +// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) +#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) +SPDLOG_INLINE std::string filename_to_str(const filename_t &filename) SPDLOG_NOEXCEPT +{ + std::wstring_convert, wchar_t> c; + return c.to_bytes(filename); +} +#else +SPDLOG_INLINE std::string filename_to_str(const filename_t &filename) SPDLOG_NOEXCEPT +{ + return filename; +} +#endif + +SPDLOG_INLINE int pid() SPDLOG_NOEXCEPT +{ + +#ifdef _WIN32 + return static_cast(::GetCurrentProcessId()); +#else + return static_cast(::getpid()); +#endif +} + +// Determine if the terminal supports colors +// Source: https://github.com/agauniyal/rang/ +SPDLOG_INLINE bool is_color_terminal() SPDLOG_NOEXCEPT +{ +#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/ +SPDLOG_INLINE bool in_terminal(FILE *file) SPDLOG_NOEXCEPT +{ + +#ifdef _WIN32 + return _isatty(_fileno(file)) != 0; +#else + return isatty(fileno(file)) != 0; +#endif +} + +#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) && defined(_WIN32) +SPDLOG_INLINE void wbuf_to_utf8buf(const fmt::wmemory_buffer &wbuf, fmt::memory_buffer &target) +{ + int wbuf_size = static_cast(wbuf.size()); + if (wbuf_size == 0) + { + return; + } + + auto result_size = ::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, NULL, 0, NULL, NULL); + + if (result_size > 0) + { + target.resize(result_size); + ::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, &target.data()[0], result_size, NULL, NULL); + } + else + { + throw spdlog::spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError())); + } +} +#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT) && _WIN32 + +} // namespace os +} // namespace details +} // namespace spdlog diff --git a/include/spdlog/details/os.h b/include/spdlog/details/os.h index 8e8476f0..3197ec69 100644 --- a/include/spdlog/details/os.h +++ b/include/spdlog/details/os.h @@ -1,112 +1,24 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// + #pragma once -#include "../common.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef _WIN32 - -#ifndef NOMINMAX -#define NOMINMAX // prevent windows redefining min/max -#endif - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include // _get_osfhandle and _isatty support -#include // _get_pid support -#include - -#ifdef __MINGW32__ -#include -#endif - -#else // unix - -#include -#include - -#ifdef __linux__ -#include //Use gettid() syscall under linux to get thread id - -#elif __FreeBSD__ -#include //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 +#include "spdlog/common.h" +#include // std::time_t namespace spdlog { namespace details { namespace os { -inline spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT -{ +spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT; -#if defined __linux__ && defined SPDLOG_CLOCK_COARSE - timespec ts; - ::clock_gettime(CLOCK_REALTIME_COARSE, &ts); - return std::chrono::time_point( - std::chrono::duration_cast(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec))); +std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT; -#else - return log_clock::now(); -#endif -} -inline std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT -{ +std::tm localtime() SPDLOG_NOEXCEPT; -#ifdef _WIN32 - std::tm tm; - localtime_s(&tm, &time_tt); -#else - std::tm tm; - localtime_r(&time_tt, &tm); -#endif - return tm; -} +std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT; -inline std::tm localtime() SPDLOG_NOEXCEPT -{ - std::time_t now_t = time(nullptr); - return localtime(now_t); -} - -inline std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT -{ - -#ifdef _WIN32 - std::tm tm; - gmtime_s(&tm, &time_tt); -#else - std::tm tm; - gmtime_r(&time_tt, &tm); -#endif - return tm; -} - -inline std::tm gmtime() SPDLOG_NOEXCEPT -{ - std::time_t now_t = time(nullptr); - return gmtime(now_t); -} +std::tm gmtime() SPDLOG_NOEXCEPT; // eol definition #if !defined(SPDLOG_EOL) @@ -121,301 +33,61 @@ SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL; // folder separator #ifdef _WIN32 -SPDLOG_CONSTEXPR static const char folder_sep = '\\'; +const char folder_sep = '\\'; #else SPDLOG_CONSTEXPR static const char folder_sep = '/'; #endif -inline void prevent_child_fd(FILE *f) -{ - -#ifdef _WIN32 -#if !defined(__cplusplus_winrt) - auto file_handle = (HANDLE)_get_osfhandle(_fileno(f)); - if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0)) - throw spdlog_ex("SetHandleInformation failed", errno); -#endif -#else - auto fd = fileno(f); - if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) - { - throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno); - } -#endif -} +void prevent_child_fd(FILE *f); // fopen_s on non windows for writing -inline bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode) -{ -#ifdef _WIN32 -#ifdef SPDLOG_WCHAR_FILENAMES - *fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); -#else - *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); -#endif -#else // unix - *fp = fopen((filename.c_str()), mode.c_str()); -#endif +bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode); -#ifdef SPDLOG_PREVENT_CHILD_FD - if (*fp != nullptr) - { - prevent_child_fd(*fp); - } -#endif - return *fp == nullptr; -} +int remove(const filename_t &filename) SPDLOG_NOEXCEPT; -inline int remove(const filename_t &filename) SPDLOG_NOEXCEPT -{ -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) - return _wremove(filename.c_str()); -#else - return std::remove(filename.c_str()); -#endif -} - -inline int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT -{ -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) - return _wrename(filename1.c_str(), filename2.c_str()); -#else - return std::rename(filename1.c_str(), filename2.c_str()); -#endif -} +int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT; // Return if file exists -inline bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT -{ -#ifdef _WIN32 -#ifdef SPDLOG_WCHAR_FILENAMES - auto attribs = GetFileAttributesW(filename.c_str()); -#else - auto attribs = GetFileAttributesA(filename.c_str()); -#endif - 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); -#endif -} +bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT; // Return file size according to open FILE* object -inline size_t filesize(FILE *f) -{ - if (f == nullptr) - { - throw spdlog_ex("Failed getting file size. fd is null"); - } -#if defined(_WIN32) && !defined(__CYGWIN__) - int fd = _fileno(f); -#if _WIN64 // 64 bits - __int64 ret = _filelengthi64(fd); - if (ret >= 0) - { - return static_cast(ret); - } - -#else // windows 32 bits - long ret = _filelength(fd); - if (ret >= 0) - { - return static_cast(ret); - } -#endif - -#else // unix - 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(st.st_size); - } -#else // unix 32 bits or cygwin - struct stat st; - - if (::fstat(fd, &st) == 0) - { - return static_cast(st.st_size); - } -#endif -#endif - throw spdlog_ex("Failed getting file size from fd", errno); -} +size_t filesize(FILE *f); // Return utc offset in minutes or throw spdlog_ex on failure -inline int utc_minutes_offset(const std::tm &tm = details::os::localtime()) -{ - -#ifdef _WIN32 -#if _WIN32_WINNT < _WIN32_WINNT_WS08 - TIME_ZONE_INFORMATION tzinfo; - auto rv = GetTimeZoneInformation(&tzinfo); -#else - DYNAMIC_TIME_ZONE_INFORMATION tzinfo; - auto rv = GetDynamicTimeZoneInformation(&tzinfo); -#endif - if (rv == TIME_ZONE_ID_INVALID) - throw spdlog::spdlog_ex("Failed getting timezone info. ", errno); - - int offset = -tzinfo.Bias; - if (tm.tm_isdst) - { - offset -= tzinfo.DaylightBias; - } - else - { - offset -= tzinfo.StandardBias; - } - return offset; -#else - -#if defined(sun) || defined(__sun) || defined(_AIX) - // '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; - } - }; - - auto offset_seconds = helper::calculate_gmt_offset(tm); -#else - auto offset_seconds = tm.tm_gmtoff; -#endif - - return static_cast(offset_seconds / 60); -#endif -} +int utc_minutes_offset(const std::tm &tm = details::os::localtime()); // Return current thread id as size_t // It exists because the std::this_thread::get_id() is much slower(especially // under VS 2013) -inline size_t _thread_id() SPDLOG_NOEXCEPT -{ -#ifdef _WIN32 - return static_cast(::GetCurrentThreadId()); -#elif __linux__ -#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21) -#define SYS_gettid __NR_gettid -#endif - return static_cast(syscall(SYS_gettid)); -#elif __FreeBSD__ - long tid; - thr_self(&tid); - return static_cast(tid); -#elif __APPLE__ - uint64_t tid; - pthread_threadid_np(nullptr, &tid); - return static_cast(tid); -#else // Default to standard C++11 (other Unix) - return static_cast(std::hash()(std::this_thread::get_id())); -#endif -} +size_t _thread_id() SPDLOG_NOEXCEPT; // Return current thread id as size_t (from thread local storage) -inline size_t thread_id() SPDLOG_NOEXCEPT -{ -#if defined(SPDLOG_NO_TLS) - return _thread_id(); -#else // cache thread id in tls - static thread_local const size_t tid = _thread_id(); - return tid; -#endif -} +size_t thread_id() SPDLOG_NOEXCEPT; // This is avoid msvc issue in sleep_for that happens if the clock changes. // See https://github.com/gabime/spdlog/issues/609 -inline void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT -{ -#if defined(_WIN32) - ::Sleep(milliseconds); -#else - std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds)); -#endif -} +void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT; -// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined) -#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES) -#define SPDLOG_FILENAME_T(s) L##s -inline std::string filename_to_str(const filename_t &filename) -{ - std::wstring_convert, wchar_t> c; - return c.to_bytes(filename); -} -#else -#define SPDLOG_FILENAME_T(s) s -inline std::string filename_to_str(const filename_t &filename) -{ - return filename; -} -#endif +std::string filename_to_str(const filename_t &filename) SPDLOG_NOEXCEPT; -inline int pid() -{ - -#ifdef _WIN32 - return static_cast(::GetCurrentProcessId()); -#else - return static_cast(::getpid()); -#endif -} +int pid() SPDLOG_NOEXCEPT; // Determine if the terminal supports colors // Source: https://github.com/agauniyal/rang/ -inline bool is_color_terminal() SPDLOG_NOEXCEPT -{ -#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 -} +bool is_color_terminal() SPDLOG_NOEXCEPT; // Detrmine if the terminal attached // Source: https://github.com/agauniyal/rang/ -inline bool in_terminal(FILE *file) SPDLOG_NOEXCEPT -{ +bool in_terminal(FILE *file) SPDLOG_NOEXCEPT; -#ifdef _WIN32 - return _isatty(_fileno(file)) != 0; -#else - return isatty(fileno(file)) != 0; +#if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) && defined(_WIN32) +void wbuf_to_utf8buf(const fmt::wmemory_buffer &wbuf, fmt::memory_buffer &target); #endif -} + } // namespace os } // namespace details } // namespace spdlog + +#ifdef SPDLOG_HEADER_ONLY +#include "os-inl.h" +#endif diff --git a/include/spdlog/details/pattern_formatter-inl.h b/include/spdlog/details/pattern_formatter-inl.h new file mode 100644 index 00000000..b798dc0d --- /dev/null +++ b/include/spdlog/details/pattern_formatter-inl.h @@ -0,0 +1,1244 @@ +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/details/pattern_formatter.h" +#endif + +#include "spdlog/details/fmt_helper.h" +#include "spdlog/details/log_msg.h" +#include "spdlog/details/os.h" +#include "spdlog/fmt/fmt.h" +#include "spdlog/formatter.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace spdlog { +namespace details { + +/////////////////////////////////////////////////////////////////////// +// name & level pattern appender +/////////////////////////////////////////////////////////////////////// + +class scoped_pad +{ +public: + scoped_pad(size_t wrapped_size, padding_info &padinfo, fmt::memory_buffer &dest) + : padinfo_(padinfo) + , dest_(dest) + { + + if (padinfo_.width_ <= wrapped_size) + { + total_pad_ = 0; + return; + } + + total_pad_ = padinfo.width_ - wrapped_size; + if (padinfo_.side_ == padding_info::left) + { + pad_it(total_pad_); + total_pad_ = 0; + } + else if (padinfo_.side_ == padding_info::center) + { + auto half_pad = total_pad_ / 2; + auto reminder = total_pad_ & 1; + pad_it(half_pad); + total_pad_ = half_pad + reminder; // for the right side + } + } + + scoped_pad(spdlog::string_view_t txt, padding_info &padinfo, fmt::memory_buffer &dest) + : scoped_pad(txt.size(), padinfo, dest) + {} + + ~scoped_pad() + { + if (total_pad_) + { + pad_it(total_pad_); + } + } + +private: + void pad_it(size_t count) + { + // count = std::min(count, spaces_.size()); + assert(count <= spaces_.size()); + fmt_helper::append_string_view(string_view_t(spaces_.data(), count), dest_); + } + + const padding_info &padinfo_; + fmt::memory_buffer &dest_; + size_t total_pad_; + string_view_t spaces_{" " + " ", + 128}; +}; + +class name_formatter : public flag_formatter +{ +public: + explicit name_formatter(padding_info padinfo) + : flag_formatter(padinfo) + {} + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + if (padinfo_.enabled()) + { + scoped_pad p(*msg.logger_name, padinfo_, dest); + fmt_helper::append_string_view(*msg.logger_name, dest); + } + else + { + fmt_helper::append_string_view(*msg.logger_name, dest); + } + } +}; + +// log level appender +class level_formatter : public flag_formatter +{ +public: + explicit level_formatter(padding_info padinfo) + : flag_formatter(padinfo) + {} + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + string_view_t &level_name = level::to_string_view(msg.level); + if (padinfo_.enabled()) + { + scoped_pad p(level_name, padinfo_, dest); + fmt_helper::append_string_view(level_name, dest); + } + else + { + fmt_helper::append_string_view(level_name, dest); + } + } +}; + +// short log level appender +class short_level_formatter : public flag_formatter +{ +public: + explicit short_level_formatter(padding_info padinfo) + : flag_formatter(padinfo) + {} + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + string_view_t level_name{level::to_short_c_str(msg.level)}; + scoped_pad p(level_name, padinfo_, dest); + fmt_helper::append_string_view(level_name, dest); + } +}; + +/////////////////////////////////////////////////////////////////////// +// Date time pattern appenders +/////////////////////////////////////////////////////////////////////// + +static const char *ampm(const tm &t) +{ + return t.tm_hour >= 12 ? "PM" : "AM"; +} + +static int to12h(const tm &t) +{ + return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour; +} + +// Abbreviated weekday name +static const char *days[]{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; +class a_formatter : public flag_formatter +{ +public: + explicit a_formatter(padding_info padinfo) + : flag_formatter(padinfo) + {} + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + string_view_t field_value{days[tm_time.tm_wday]}; + scoped_pad p(field_value, padinfo_, dest); + fmt_helper::append_string_view(field_value, dest); + } +}; + +// Full weekday name +static const char *full_days[]{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; +class A_formatter : public flag_formatter +{ +public: + explicit A_formatter(padding_info padinfo) + : flag_formatter(padinfo) + {} + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + string_view_t field_value{full_days[tm_time.tm_wday]}; + scoped_pad p(field_value, padinfo_, dest); + fmt_helper::append_string_view(field_value, dest); + } +}; + +// Abbreviated month +static const char *months[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}; +class b_formatter : public flag_formatter +{ +public: + explicit b_formatter(padding_info padinfo) + : flag_formatter(padinfo) + {} + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + string_view_t field_value{months[tm_time.tm_mon]}; + scoped_pad p(field_value, padinfo_, dest); + fmt_helper::append_string_view(field_value, dest); + } +}; + +// Full month name +static const char *full_months[]{ + "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; +class B_formatter : public flag_formatter +{ +public: + explicit B_formatter(padding_info padinfo) + : flag_formatter(padinfo) + {} + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + string_view_t field_value{full_months[tm_time.tm_mon]}; + scoped_pad p(field_value, padinfo_, dest); + fmt_helper::append_string_view(field_value, dest); + } +}; + +// Date and time representation (Thu Aug 23 15:35:46 2014) +class c_formatter final : public flag_formatter +{ +public: + explicit c_formatter(padding_info padinfo) + : flag_formatter(padinfo) + {} + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 24; + scoped_pad p(field_size, padinfo_, dest); + + fmt_helper::append_string_view(days[tm_time.tm_wday], dest); + dest.push_back(' '); + fmt_helper::append_string_view(months[tm_time.tm_mon], dest); + dest.push_back(' '); + fmt_helper::append_int(tm_time.tm_mday, dest); + dest.push_back(' '); + // time + + fmt_helper::pad2(tm_time.tm_hour, dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_min, dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_sec, dest); + dest.push_back(' '); + fmt_helper::append_int(tm_time.tm_year + 1900, dest); + } +}; + +// year - 2 digit +class C_formatter final : public flag_formatter +{ +public: + explicit C_formatter(padding_info padinfo) + : flag_formatter(padinfo) + {} + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 2; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad2(tm_time.tm_year % 100, dest); + } +}; + +// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01 +class D_formatter final : public flag_formatter +{ +public: + explicit D_formatter(padding_info padinfo) + : flag_formatter(padinfo) + {} + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 10; + scoped_pad p(field_size, padinfo_, dest); + + fmt_helper::pad2(tm_time.tm_mon + 1, dest); + dest.push_back('/'); + fmt_helper::pad2(tm_time.tm_mday, dest); + dest.push_back('/'); + fmt_helper::pad2(tm_time.tm_year % 100, dest); + } +}; + +// year - 4 digit +class Y_formatter final : public flag_formatter +{ +public: + explicit Y_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 4; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::append_int(tm_time.tm_year + 1900, dest); + } +}; + +// month 1-12 +class m_formatter final : public flag_formatter +{ +public: + explicit m_formatter(padding_info padinfo) + : flag_formatter(padinfo) + {} + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 2; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad2(tm_time.tm_mon + 1, dest); + } +}; + +// day of month 1-31 +class d_formatter final : public flag_formatter +{ +public: + explicit d_formatter(padding_info padinfo) + : flag_formatter(padinfo) + {} + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 2; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad2(tm_time.tm_mday, dest); + } +}; + +// hours in 24 format 0-23 +class H_formatter final : public flag_formatter +{ +public: + explicit H_formatter(padding_info padinfo) + : flag_formatter(padinfo) + {} + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 2; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad2(tm_time.tm_hour, dest); + } +}; + +// hours in 12 format 1-12 +class I_formatter final : public flag_formatter +{ +public: + explicit I_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 2; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad2(to12h(tm_time), dest); + } +}; + +// minutes 0-59 +class M_formatter final : public flag_formatter +{ +public: + explicit M_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 2; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad2(tm_time.tm_min, dest); + } +}; + +// seconds 0-59 +class S_formatter final : public flag_formatter +{ +public: + explicit S_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 2; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad2(tm_time.tm_sec, dest); + } +}; + +// milliseconds +class e_formatter final : public flag_formatter +{ +public: + explicit e_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + auto millis = fmt_helper::time_fraction(msg.time); + if (padinfo_.enabled()) + { + const size_t field_size = 3; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad3(static_cast(millis.count()), dest); + } + else + { + fmt_helper::pad3(static_cast(millis.count()), dest); + } + } +}; + +// microseconds +class f_formatter final : public flag_formatter +{ +public: + explicit f_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + auto micros = fmt_helper::time_fraction(msg.time); + if (padinfo_.enabled()) + { + const size_t field_size = 6; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad6(static_cast(micros.count()), dest); + } + else + { + fmt_helper::pad6(static_cast(micros.count()), dest); + } + } +}; + +// nanoseconds +class F_formatter final : public flag_formatter +{ +public: + explicit F_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + auto ns = fmt_helper::time_fraction(msg.time); + if (padinfo_.enabled()) + { + const size_t field_size = 9; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::pad9(static_cast(ns.count()), dest); + } + else + { + fmt_helper::pad9(static_cast(ns.count()), dest); + } + } +}; + +// seconds since epoch +class E_formatter final : public flag_formatter +{ +public: + explicit E_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + const size_t field_size = 10; + scoped_pad p(field_size, padinfo_, dest); + auto duration = msg.time.time_since_epoch(); + auto seconds = std::chrono::duration_cast(duration).count(); + fmt_helper::append_int(seconds, dest); + } +}; + +// AM/PM +class p_formatter final : public flag_formatter +{ +public: + explicit p_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 2; + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::append_string_view(ampm(tm_time), dest); + } +}; + +// 12 hour clock 02:55:02 pm +class r_formatter final : public flag_formatter +{ +public: + explicit r_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 11; + scoped_pad p(field_size, padinfo_, dest); + + fmt_helper::pad2(to12h(tm_time), dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_min, dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_sec, dest); + dest.push_back(' '); + fmt_helper::append_string_view(ampm(tm_time), dest); + } +}; + +// 24-hour HH:MM time, equivalent to %H:%M +class R_formatter final : public flag_formatter +{ +public: + explicit R_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 5; + scoped_pad p(field_size, padinfo_, dest); + + fmt_helper::pad2(tm_time.tm_hour, dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_min, dest); + } +}; + +// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S +class T_formatter final : public flag_formatter +{ +public: + explicit T_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 8; + scoped_pad p(field_size, padinfo_, dest); + + fmt_helper::pad2(tm_time.tm_hour, dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_min, dest); + dest.push_back(':'); + fmt_helper::pad2(tm_time.tm_sec, dest); + } +}; + +// ISO 8601 offset from UTC in timezone (+-HH:MM) +class z_formatter final : public flag_formatter +{ +public: + explicit z_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + const std::chrono::seconds cache_refresh = std::chrono::seconds(5); + + z_formatter() = default; + z_formatter(const z_formatter &) = delete; + z_formatter &operator=(const z_formatter &) = delete; + + void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + const size_t field_size = 6; + scoped_pad p(field_size, padinfo_, dest); + +#ifdef _WIN32 + int total_minutes = get_cached_offset(msg, tm_time); +#else + // No need to chache under gcc, + // it is very fast (already stored in tm.tm_gmtoff) + (void)(msg); + int total_minutes = os::utc_minutes_offset(tm_time); +#endif + bool is_negative = total_minutes < 0; + if (is_negative) + { + total_minutes = -total_minutes; + dest.push_back('-'); + } + else + { + dest.push_back('+'); + } + + fmt_helper::pad2(total_minutes / 60, dest); // hours + dest.push_back(':'); + fmt_helper::pad2(total_minutes % 60, dest); // minutes + } + +private: + log_clock::time_point last_update_{std::chrono::seconds(0)}; +#ifdef _WIN32 + int offset_minutes_{0}; + + int get_cached_offset(const log_msg &msg, const std::tm &tm_time) + { + if (msg.time - last_update_ >= cache_refresh) + { + offset_minutes_ = os::utc_minutes_offset(tm_time); + last_update_ = msg.time; + } + return offset_minutes_; + } +#endif +}; + +// Thread id +class t_formatter final : public flag_formatter +{ +public: + explicit t_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + if (padinfo_.enabled()) + { + const auto field_size = fmt_helper::count_digits(msg.thread_id); + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::append_int(msg.thread_id, dest); + } + else + { + fmt_helper::append_int(msg.thread_id, dest); + } + } +}; + +// Current pid +class pid_formatter final : public flag_formatter +{ +public: + explicit pid_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override + { + const auto pid = static_cast(details::os::pid()); + if (padinfo_.enabled()) + { + auto field_size = fmt_helper::count_digits(pid); + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::append_int(pid, dest); + } + else + { + fmt_helper::append_int(pid, dest); + } + } +}; + +class v_formatter final : public flag_formatter +{ +public: + explicit v_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + if (padinfo_.enabled()) + { + scoped_pad p(msg.payload, padinfo_, dest); + fmt_helper::append_string_view(msg.payload, dest); + } + else + { + fmt_helper::append_string_view(msg.payload, dest); + } + } +}; + +class ch_formatter final : public flag_formatter +{ +public: + explicit ch_formatter(char ch) + : ch_(ch) + {} + + void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override + { + const size_t field_size = 1; + scoped_pad p(field_size, padinfo_, dest); + dest.push_back(ch_); + } + +private: + char ch_; +}; + +// aggregate user chars to display as is +class aggregate_formatter final : public flag_formatter +{ +public: + aggregate_formatter() = default; + + void add_ch(char ch) + { + str_ += ch; + } + void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override + { + fmt_helper::append_string_view(str_, dest); + } + +private: + std::string str_; +}; + +// mark the color range. expect it to be in the form of "%^colored text%$" +class color_start_formatter final : public flag_formatter +{ +public: + explicit color_start_formatter(padding_info padinfo) + : flag_formatter(padinfo) + {} + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + msg.color_range_start = dest.size(); + } +}; +class color_stop_formatter final : public flag_formatter +{ +public: + explicit color_stop_formatter(padding_info padinfo) + : flag_formatter(padinfo) + {} + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + msg.color_range_end = dest.size(); + } +}; + +// print source location +class source_location_formatter final : public flag_formatter +{ +public: + explicit source_location_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + if (msg.source.empty()) + { + return; + } + if (padinfo_.enabled()) + { + const auto text_size = std::char_traits::length(msg.source.filename) + fmt_helper::count_digits(msg.source.line) + 1; + scoped_pad p(text_size, padinfo_, dest); + fmt_helper::append_string_view(msg.source.filename, dest); + dest.push_back(':'); + fmt_helper::append_int(msg.source.line, dest); + } + else + { + fmt_helper::append_string_view(msg.source.filename, dest); + dest.push_back(':'); + fmt_helper::append_int(msg.source.line, dest); + } + } +}; +// print source filename +class source_filename_formatter final : public flag_formatter +{ +public: + explicit source_filename_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + if (msg.source.empty()) + { + return; + } + scoped_pad p(msg.source.filename, padinfo_, dest); + fmt_helper::append_string_view(msg.source.filename, dest); + } +}; + +class source_linenum_formatter final : public flag_formatter +{ +public: + explicit source_linenum_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + if (msg.source.empty()) + { + return; + } + if (padinfo_.enabled()) + { + auto field_size = fmt_helper::count_digits(msg.source.line); + scoped_pad p(field_size, padinfo_, dest); + fmt_helper::append_int(msg.source.line, dest); + } + else + { + fmt_helper::append_int(msg.source.line, dest); + } + } +}; +// print source funcname +class source_funcname_formatter final : public flag_formatter +{ +public: + explicit source_funcname_formatter(padding_info padinfo) + : flag_formatter(padinfo){} + + void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override + { + if (msg.source.empty()) + { + return; + } + scoped_pad p(msg.source.funcname, padinfo_, dest); + fmt_helper::append_string_view(msg.source.funcname, dest); + } +}; + +// Full info formatter +// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v +class full_formatter final : public flag_formatter +{ +public: + explicit full_formatter(padding_info padinfo) + : flag_formatter(padinfo) + {} + + void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override + { + using std::chrono::duration_cast; + using std::chrono::milliseconds; + using std::chrono::seconds; + +#ifndef SPDLOG_NO_DATETIME + + // cache the date/time part for the next second. + auto duration = msg.time.time_since_epoch(); + auto secs = duration_cast(duration); + + if (cache_timestamp_ != secs || cached_datetime_.size() == 0) + { + cached_datetime_.clear(); + cached_datetime_.push_back('['); + fmt_helper::append_int(tm_time.tm_year + 1900, cached_datetime_); + cached_datetime_.push_back('-'); + + fmt_helper::pad2(tm_time.tm_mon + 1, cached_datetime_); + cached_datetime_.push_back('-'); + + fmt_helper::pad2(tm_time.tm_mday, cached_datetime_); + cached_datetime_.push_back(' '); + + fmt_helper::pad2(tm_time.tm_hour, cached_datetime_); + cached_datetime_.push_back(':'); + + fmt_helper::pad2(tm_time.tm_min, cached_datetime_); + cached_datetime_.push_back(':'); + + fmt_helper::pad2(tm_time.tm_sec, cached_datetime_); + cached_datetime_.push_back('.'); + + cache_timestamp_ = secs; + } + fmt_helper::append_buf(cached_datetime_, dest); + + auto millis = fmt_helper::time_fraction(msg.time); + fmt_helper::pad3(static_cast(millis.count()), dest); + dest.push_back(']'); + dest.push_back(' '); + +#else // no datetime needed + (void)tm_time; +#endif + +#ifndef SPDLOG_NO_NAME + if (!msg.logger_name->empty()) + { + dest.push_back('['); + // fmt_helper::append_str(*msg.logger_name, dest); + fmt_helper::append_string_view(*msg.logger_name, dest); + dest.push_back(']'); + dest.push_back(' '); + } +#endif + + dest.push_back('['); + // wrap the level name with color + msg.color_range_start = dest.size(); + // fmt_helper::append_string_view(level::to_c_str(msg.level), dest); + fmt_helper::append_string_view(level::to_string_view(msg.level), dest); + msg.color_range_end = dest.size(); + dest.push_back(']'); + dest.push_back(' '); + + // add source location if present + if (!msg.source.empty()) + { + dest.push_back('['); + fmt_helper::append_string_view(msg.source.filename, dest); + dest.push_back(':'); + fmt_helper::append_int(msg.source.line, dest); + dest.push_back(']'); + dest.push_back(' '); + } + // fmt_helper::append_string_view(msg.msg(), dest); + fmt_helper::append_string_view(msg.payload, dest); + } + +private: + std::chrono::seconds cache_timestamp_{0}; + fmt::basic_memory_buffer cached_datetime_; +}; + +} // namespace details + +SPDLOG_INLINE pattern_formatter::pattern_formatter(std::string pattern, pattern_time_type time_type, std::string eol) + : pattern_(std::move(pattern)) + , eol_(std::move(eol)) + , pattern_time_type_(time_type) + , last_log_secs_(0) +{ + std::memset(&cached_tm_, 0, sizeof(cached_tm_)); + compile_pattern_(pattern_); +} + +// use by default full formatter for if pattern is not given +SPDLOG_INLINE pattern_formatter::pattern_formatter(pattern_time_type time_type, std::string eol) + : pattern_("%+") + , eol_(std::move(eol)) + , pattern_time_type_(time_type) + , last_log_secs_(0) +{ + std::memset(&cached_tm_, 0, sizeof(cached_tm_)); + formatters_.push_back(details::make_unique(details::padding_info{})); +} + +SPDLOG_INLINE std::unique_ptr pattern_formatter::clone() const +{ + return details::make_unique(pattern_, pattern_time_type_, eol_); +} + +SPDLOG_INLINE void pattern_formatter::format(const details::log_msg &msg, fmt::memory_buffer &dest) +{ +#ifndef SPDLOG_NO_DATETIME + auto secs = std::chrono::duration_cast(msg.time.time_since_epoch()); + if (secs != last_log_secs_) + { + cached_tm_ = get_time_(msg); + last_log_secs_ = secs; + } +#endif + for (auto &f : formatters_) + { + f->format(msg, cached_tm_, dest); + } + // write eol + details::fmt_helper::append_string_view(eol_, dest); +} + +SPDLOG_INLINE std::tm pattern_formatter::get_time_(const details::log_msg &msg) +{ + if (pattern_time_type_ == pattern_time_type::local) + { + return details::os::localtime(log_clock::to_time_t(msg.time)); + } + return details::os::gmtime(log_clock::to_time_t(msg.time)); +} + +SPDLOG_INLINE void pattern_formatter::handle_flag_(char flag, details::padding_info padding) +{ + switch (flag) + { + + case ('+'): // default formatter + formatters_.push_back(details::make_unique(padding)); + break; + + case 'n': // logger name + formatters_.push_back(details::make_unique(padding)); + break; + + case 'l': // level + formatters_.push_back(details::make_unique(padding)); + break; + + case 'L': // short level + formatters_.push_back(details::make_unique(padding)); + break; + + case ('t'): // thread id + formatters_.push_back(details::make_unique(padding)); + break; + + case ('v'): // the message text + formatters_.push_back(details::make_unique(padding)); + break; + + case ('a'): // weekday + formatters_.push_back(details::make_unique(padding)); + break; + + case ('A'): // short weekday + formatters_.push_back(details::make_unique(padding)); + break; + + case ('b'): + case ('h'): // month + formatters_.push_back(details::make_unique(padding)); + break; + + case ('B'): // short month + formatters_.push_back(details::make_unique(padding)); + break; + + case ('c'): // datetime + formatters_.push_back(details::make_unique(padding)); + break; + + case ('C'): // year 2 digits + formatters_.push_back(details::make_unique(padding)); + break; + + case ('Y'): // year 4 digits + formatters_.push_back(details::make_unique(padding)); + break; + + case ('D'): + case ('x'): // datetime MM/DD/YY + formatters_.push_back(details::make_unique(padding)); + break; + + case ('m'): // month 1-12 + formatters_.push_back(details::make_unique(padding)); + break; + + case ('d'): // day of month 1-31 + formatters_.push_back(details::make_unique(padding)); + break; + + case ('H'): // hours 24 + formatters_.push_back(details::make_unique(padding)); + break; + + case ('I'): // hours 12 + formatters_.push_back(details::make_unique(padding)); + break; + + case ('M'): // minutes + formatters_.push_back(details::make_unique(padding)); + break; + + case ('S'): // seconds + formatters_.push_back(details::make_unique(padding)); + break; + + case ('e'): // milliseconds + formatters_.push_back(details::make_unique(padding)); + break; + + case ('f'): // microseconds + formatters_.push_back(details::make_unique(padding)); + break; + + case ('F'): // nanoseconds + formatters_.push_back(details::make_unique(padding)); + break; + + case ('E'): // seconds since epoch + formatters_.push_back(details::make_unique(padding)); + break; + + case ('p'): // am/pm + formatters_.push_back(details::make_unique(padding)); + break; + + case ('r'): // 12 hour clock 02:55:02 pm + formatters_.push_back(details::make_unique(padding)); + break; + + case ('R'): // 24-hour HH:MM time + formatters_.push_back(details::make_unique(padding)); + break; + + case ('T'): + case ('X'): // ISO 8601 time format (HH:MM:SS) + formatters_.push_back(details::make_unique(padding)); + break; + + case ('z'): // timezone + formatters_.push_back(details::make_unique(padding)); + break; + + case ('P'): // pid + formatters_.push_back(details::make_unique(padding)); + break; + + case ('^'): // color range start + formatters_.push_back(details::make_unique(padding)); + break; + + case ('$'): // color range end + formatters_.push_back(details::make_unique(padding)); + break; + + case ('@'): // source location (filename:filenumber) + formatters_.push_back(details::make_unique(padding)); + break; + + case ('s'): // source filename + formatters_.push_back(details::make_unique(padding)); + break; + + case ('#'): // source line number + formatters_.push_back(details::make_unique(padding)); + break; + + case ('!'): // source funcname + formatters_.push_back(details::make_unique(padding)); + break; + + case ('%'): // % char + formatters_.push_back(details::make_unique('%')); + break; + + default: // Unknown flag appears as is + auto unknown_flag = details::make_unique(); + unknown_flag->add_ch('%'); + unknown_flag->add_ch(flag); + formatters_.push_back((std::move(unknown_flag))); + break; + } +} + +// Extract given pad spec (e.g. %8X) +// Advance the given it pass the end of the padding spec found (if any) +// Return padding. +SPDLOG_INLINE details::padding_info pattern_formatter::handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end) +{ + using details::padding_info; + using details::scoped_pad; + const size_t max_width = 128; + if (it == end) + { + return padding_info{}; + } + + padding_info::pad_side side; + switch (*it) + { + case '-': + side = padding_info::right; + ++it; + break; + case '=': + side = padding_info::center; + ++it; + break; + default: + side = details::padding_info::left; + break; + } + + if (it == end || !std::isdigit(static_cast(*it))) + { + return padding_info{0, side}; + } + + auto width = static_cast(*it - '0'); + for (++it; it != end && std::isdigit(static_cast(*it)); ++it) + { + auto digit = static_cast(*it - '0'); + width = width * 10 + digit; + } + return details::padding_info{std::min(width, max_width), side}; +} + +SPDLOG_INLINE void pattern_formatter::compile_pattern_(const std::string &pattern) +{ + auto end = pattern.end(); + std::unique_ptr user_chars; + formatters_.clear(); + for (auto it = pattern.begin(); it != end; ++it) + { + if (*it == '%') + { + if (user_chars) // append user chars found so far + { + formatters_.push_back(std::move(user_chars)); + } + + auto padding = handle_padspec_(++it, end); + + if (it != end) + { + handle_flag_(*it, padding); + } + else + { + break; + } + } + else // chars not following the % sign should be displayed as is + { + if (!user_chars) + { + user_chars = details::make_unique(); + } + user_chars->add_ch(*it); + } + } + if (user_chars) // append raw chars found so far + { + formatters_.push_back(std::move(user_chars)); + } +} +} // namespace spdlog diff --git a/include/spdlog/details/pattern_formatter.h b/include/spdlog/details/pattern_formatter.h index 60ed72ad..b6052c1b 100644 --- a/include/spdlog/details/pattern_formatter.h +++ b/include/spdlog/details/pattern_formatter.h @@ -1,25 +1,18 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once -#include "spdlog/details/fmt_helper.h" +#include "spdlog/common.h" #include "spdlog/details/log_msg.h" #include "spdlog/details/os.h" -#include "spdlog/fmt/fmt.h" #include "spdlog/formatter.h" -#include #include #include -#include #include -#include + #include -#include -#include #include namespace spdlog { @@ -39,8 +32,7 @@ struct padding_info padding_info(size_t width, padding_info::pad_side side) : width_(width) , side_(side) - { - } + {} bool enabled() const { @@ -50,71 +42,12 @@ struct padding_info const pad_side side_ = left; }; -class scoped_pad -{ -public: - scoped_pad(size_t wrapped_size, padding_info &padinfo, fmt::memory_buffer &dest) - : padinfo_(padinfo) - , dest_(dest) - { - - if (padinfo_.width_ <= wrapped_size) - { - total_pad_ = 0; - return; - } - - total_pad_ = padinfo.width_ - wrapped_size; - if (padinfo_.side_ == padding_info::left) - { - pad_it(total_pad_); - total_pad_ = 0; - } - else if (padinfo_.side_ == padding_info::center) - { - auto half_pad = total_pad_ / 2; - auto reminder = total_pad_ & 1; - pad_it(half_pad); - total_pad_ = half_pad + reminder; // for the right side - } - } - - scoped_pad(spdlog::string_view_t txt, padding_info &padinfo, fmt::memory_buffer &dest) - : scoped_pad(txt.size(), padinfo, dest) - { - } - - ~scoped_pad() - { - if (total_pad_) - { - pad_it(total_pad_); - } - } - -private: - void pad_it(size_t count) - { - // count = std::min(count, spaces_.size()); - assert(count <= spaces_.size()); - fmt_helper::append_string_view(string_view_t(spaces_.data(), count), dest_); - } - - const padding_info &padinfo_; - fmt::memory_buffer &dest_; - size_t total_pad_; - string_view_t spaces_{" " - " ", - 128}; -}; - class flag_formatter { public: explicit flag_formatter(padding_info padinfo) : padinfo_(padinfo) - { - } + {} flag_formatter() = default; virtual ~flag_formatter() = default; virtual void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) = 0; @@ -123,944 +56,22 @@ protected: padding_info padinfo_; }; -/////////////////////////////////////////////////////////////////////// -// name & level pattern appender -/////////////////////////////////////////////////////////////////////// -class name_formatter : public flag_formatter -{ -public: - explicit name_formatter(padding_info padinfo) - : flag_formatter(padinfo) - { - } - - void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override - { - if (padinfo_.enabled()) - { - scoped_pad p(*msg.logger_name, padinfo_, dest); - fmt_helper::append_string_view(*msg.logger_name, dest); - } - else - { - fmt_helper::append_string_view(*msg.logger_name, dest); - } - } -}; - -// log level appender -class level_formatter : public flag_formatter -{ -public: - explicit level_formatter(padding_info padinfo) - : flag_formatter(padinfo) - { - } - - void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override - { - string_view_t &level_name = level::to_string_view(msg.level); - if (padinfo_.enabled()) - { - scoped_pad p(level_name, padinfo_, dest); - fmt_helper::append_string_view(level_name, dest); - } - else - { - fmt_helper::append_string_view(level_name, dest); - } - } -}; - -// short log level appender -class short_level_formatter : public flag_formatter -{ -public: - explicit short_level_formatter(padding_info padinfo) - : flag_formatter(padinfo) - { - } - - void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override - { - string_view_t level_name{level::to_short_c_str(msg.level)}; - scoped_pad p(level_name, padinfo_, dest); - fmt_helper::append_string_view(level_name, dest); - } -}; - -/////////////////////////////////////////////////////////////////////// -// Date time pattern appenders -/////////////////////////////////////////////////////////////////////// - -static const char *ampm(const tm &t) -{ - return t.tm_hour >= 12 ? "PM" : "AM"; -} - -static int to12h(const tm &t) -{ - return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour; -} - -// Abbreviated weekday name -static const char *days[]{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; -class a_formatter : public flag_formatter -{ -public: - explicit a_formatter(padding_info padinfo) - : flag_formatter(padinfo) - { - } - - void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - string_view_t field_value{days[tm_time.tm_wday]}; - scoped_pad p(field_value, padinfo_, dest); - fmt_helper::append_string_view(field_value, dest); - } -}; - -// Full weekday name -static const char *full_days[]{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; -class A_formatter : public flag_formatter -{ -public: - explicit A_formatter(padding_info padinfo) - : flag_formatter(padinfo) - { - } - - void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - string_view_t field_value{full_days[tm_time.tm_wday]}; - scoped_pad p(field_value, padinfo_, dest); - fmt_helper::append_string_view(field_value, dest); - } -}; - -// Abbreviated month -static const char *months[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"}; -class b_formatter : public flag_formatter -{ -public: - explicit b_formatter(padding_info padinfo) - : flag_formatter(padinfo) - { - } - - void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - string_view_t field_value{months[tm_time.tm_mon]}; - scoped_pad p(field_value, padinfo_, dest); - fmt_helper::append_string_view(field_value, dest); - } -}; - -// Full month name -static const char *full_months[]{ - "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; -class B_formatter : public flag_formatter -{ -public: - explicit B_formatter(padding_info padinfo) - : flag_formatter(padinfo) - { - } - - void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - string_view_t field_value{full_months[tm_time.tm_mon]}; - scoped_pad p(field_value, padinfo_, dest); - fmt_helper::append_string_view(field_value, dest); - } -}; - -// Date and time representation (Thu Aug 23 15:35:46 2014) -class c_formatter final : public flag_formatter -{ -public: - explicit c_formatter(padding_info padinfo) - : flag_formatter(padinfo) - { - } - - void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - const size_t field_size = 24; - scoped_pad p(field_size, padinfo_, dest); - - fmt_helper::append_string_view(days[tm_time.tm_wday], dest); - dest.push_back(' '); - fmt_helper::append_string_view(months[tm_time.tm_mon], dest); - dest.push_back(' '); - fmt_helper::append_int(tm_time.tm_mday, dest); - dest.push_back(' '); - // time - - fmt_helper::pad2(tm_time.tm_hour, dest); - dest.push_back(':'); - fmt_helper::pad2(tm_time.tm_min, dest); - dest.push_back(':'); - fmt_helper::pad2(tm_time.tm_sec, dest); - dest.push_back(' '); - fmt_helper::append_int(tm_time.tm_year + 1900, dest); - } -}; - -// year - 2 digit -class C_formatter final : public flag_formatter -{ -public: - explicit C_formatter(padding_info padinfo) - : flag_formatter(padinfo) - { - } - - void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - const size_t field_size = 2; - scoped_pad p(field_size, padinfo_, dest); - fmt_helper::pad2(tm_time.tm_year % 100, dest); - } -}; - -// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01 -class D_formatter final : public flag_formatter -{ -public: - explicit D_formatter(padding_info padinfo) - : flag_formatter(padinfo) - { - } - - void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - const size_t field_size = 10; - scoped_pad p(field_size, padinfo_, dest); - - fmt_helper::pad2(tm_time.tm_mon + 1, dest); - dest.push_back('/'); - fmt_helper::pad2(tm_time.tm_mday, dest); - dest.push_back('/'); - fmt_helper::pad2(tm_time.tm_year % 100, dest); - } -}; - -// year - 4 digit -class Y_formatter final : public flag_formatter -{ -public: - explicit Y_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - const size_t field_size = 4; - scoped_pad p(field_size, padinfo_, dest); - fmt_helper::append_int(tm_time.tm_year + 1900, dest); - } -}; - -// month 1-12 -class m_formatter final : public flag_formatter -{ -public: - explicit m_formatter(padding_info padinfo) - : flag_formatter(padinfo) - { - } - - void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - const size_t field_size = 2; - scoped_pad p(field_size, padinfo_, dest); - fmt_helper::pad2(tm_time.tm_mon + 1, dest); - } -}; - -// day of month 1-31 -class d_formatter final : public flag_formatter -{ -public: - explicit d_formatter(padding_info padinfo) - : flag_formatter(padinfo) - { - } - - void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - const size_t field_size = 2; - scoped_pad p(field_size, padinfo_, dest); - fmt_helper::pad2(tm_time.tm_mday, dest); - } -}; - -// hours in 24 format 0-23 -class H_formatter final : public flag_formatter -{ -public: - explicit H_formatter(padding_info padinfo) - : flag_formatter(padinfo) - { - } - - void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - const size_t field_size = 2; - scoped_pad p(field_size, padinfo_, dest); - fmt_helper::pad2(tm_time.tm_hour, dest); - } -}; - -// hours in 12 format 1-12 -class I_formatter final : public flag_formatter -{ -public: - explicit I_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - const size_t field_size = 2; - scoped_pad p(field_size, padinfo_, dest); - fmt_helper::pad2(to12h(tm_time), dest); - } -}; - -// minutes 0-59 -class M_formatter final : public flag_formatter -{ -public: - explicit M_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - const size_t field_size = 2; - scoped_pad p(field_size, padinfo_, dest); - fmt_helper::pad2(tm_time.tm_min, dest); - } -}; - -// seconds 0-59 -class S_formatter final : public flag_formatter -{ -public: - explicit S_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - const size_t field_size = 2; - scoped_pad p(field_size, padinfo_, dest); - fmt_helper::pad2(tm_time.tm_sec, dest); - } -}; - -// milliseconds -class e_formatter final : public flag_formatter -{ -public: - explicit e_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override - { - auto millis = fmt_helper::time_fraction(msg.time); - if (padinfo_.enabled()) - { - const size_t field_size = 3; - scoped_pad p(field_size, padinfo_, dest); - fmt_helper::pad3(static_cast(millis.count()), dest); - } - else - { - fmt_helper::pad3(static_cast(millis.count()), dest); - } - } -}; - -// microseconds -class f_formatter final : public flag_formatter -{ -public: - explicit f_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override - { - auto micros = fmt_helper::time_fraction(msg.time); - if (padinfo_.enabled()) - { - const size_t field_size = 6; - scoped_pad p(field_size, padinfo_, dest); - fmt_helper::pad6(static_cast(micros.count()), dest); - } - else - { - fmt_helper::pad6(static_cast(micros.count()), dest); - } - } -}; - -// nanoseconds -class F_formatter final : public flag_formatter -{ -public: - explicit F_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override - { - auto ns = fmt_helper::time_fraction(msg.time); - if (padinfo_.enabled()) - { - const size_t field_size = 9; - scoped_pad p(field_size, padinfo_, dest); - fmt_helper::pad9(static_cast(ns.count()), dest); - } - else - { - fmt_helper::pad9(static_cast(ns.count()), dest); - } - } -}; - -// seconds since epoch -class E_formatter final : public flag_formatter -{ -public: - explicit E_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override - { - const size_t field_size = 10; - scoped_pad p(field_size, padinfo_, dest); - auto duration = msg.time.time_since_epoch(); - auto seconds = std::chrono::duration_cast(duration).count(); - fmt_helper::append_int(seconds, dest); - } -}; - -// AM/PM -class p_formatter final : public flag_formatter -{ -public: - explicit p_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - const size_t field_size = 2; - scoped_pad p(field_size, padinfo_, dest); - fmt_helper::append_string_view(ampm(tm_time), dest); - } -}; - -// 12 hour clock 02:55:02 pm -class r_formatter final : public flag_formatter -{ -public: - explicit r_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - const size_t field_size = 11; - scoped_pad p(field_size, padinfo_, dest); - - fmt_helper::pad2(to12h(tm_time), dest); - dest.push_back(':'); - fmt_helper::pad2(tm_time.tm_min, dest); - dest.push_back(':'); - fmt_helper::pad2(tm_time.tm_sec, dest); - dest.push_back(' '); - fmt_helper::append_string_view(ampm(tm_time), dest); - } -}; - -// 24-hour HH:MM time, equivalent to %H:%M -class R_formatter final : public flag_formatter -{ -public: - explicit R_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - const size_t field_size = 5; - scoped_pad p(field_size, padinfo_, dest); - - fmt_helper::pad2(tm_time.tm_hour, dest); - dest.push_back(':'); - fmt_helper::pad2(tm_time.tm_min, dest); - } -}; - -// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S -class T_formatter final : public flag_formatter -{ -public: - explicit T_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - const size_t field_size = 8; - scoped_pad p(field_size, padinfo_, dest); - - fmt_helper::pad2(tm_time.tm_hour, dest); - dest.push_back(':'); - fmt_helper::pad2(tm_time.tm_min, dest); - dest.push_back(':'); - fmt_helper::pad2(tm_time.tm_sec, dest); - } -}; - -// ISO 8601 offset from UTC in timezone (+-HH:MM) -class z_formatter final : public flag_formatter -{ -public: - explicit z_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - const std::chrono::seconds cache_refresh = std::chrono::seconds(5); - - z_formatter() = default; - z_formatter(const z_formatter &) = delete; - z_formatter &operator=(const z_formatter &) = delete; - - void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - const size_t field_size = 6; - scoped_pad p(field_size, padinfo_, dest); - -#ifdef _WIN32 - int total_minutes = get_cached_offset(msg, tm_time); -#else - // No need to chache under gcc, - // it is very fast (already stored in tm.tm_gmtoff) - (void)(msg); - int total_minutes = os::utc_minutes_offset(tm_time); -#endif - bool is_negative = total_minutes < 0; - if (is_negative) - { - total_minutes = -total_minutes; - dest.push_back('-'); - } - else - { - dest.push_back('+'); - } - - fmt_helper::pad2(total_minutes / 60, dest); // hours - dest.push_back(':'); - fmt_helper::pad2(total_minutes % 60, dest); // minutes - } - -private: - log_clock::time_point last_update_{std::chrono::seconds(0)}; -#ifdef _WIN32 - int offset_minutes_{0}; - - int get_cached_offset(const log_msg &msg, const std::tm &tm_time) - { - if (msg.time - last_update_ >= cache_refresh) - { - offset_minutes_ = os::utc_minutes_offset(tm_time); - last_update_ = msg.time; - } - return offset_minutes_; - } -#endif -}; - -// Thread id -class t_formatter final : public flag_formatter -{ -public: - explicit t_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override - { - if (padinfo_.enabled()) - { - const auto field_size = fmt_helper::count_digits(msg.thread_id); - scoped_pad p(field_size, padinfo_, dest); - fmt_helper::append_int(msg.thread_id, dest); - } - else - { - fmt_helper::append_int(msg.thread_id, dest); - } - } -}; - -// Current pid -class pid_formatter final : public flag_formatter -{ -public: - explicit pid_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override - { - const auto pid = static_cast(details::os::pid()); - if (padinfo_.enabled()) - { - auto field_size = fmt_helper::count_digits(pid); - scoped_pad p(field_size, padinfo_, dest); - fmt_helper::append_int(pid, dest); - } - else - { - fmt_helper::append_int(pid, dest); - } - } -}; - -// message counter formatter -class i_formatter final : public flag_formatter -{ -public: - explicit i_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override - { - const size_t field_size = 6; - scoped_pad p(field_size, padinfo_, dest); - fmt_helper::pad6(msg.msg_id, dest); - } -}; - -class v_formatter final : public flag_formatter -{ -public: - explicit v_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override - { - if (padinfo_.enabled()) - { - scoped_pad p(msg.payload, padinfo_, dest); - fmt_helper::append_string_view(msg.payload, dest); - } - else - { - fmt_helper::append_string_view(msg.payload, dest); - } - } -}; - -class ch_formatter final : public flag_formatter -{ -public: - explicit ch_formatter(char ch) - : ch_(ch) - { - } - - void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override - { - const size_t field_size = 1; - scoped_pad p(field_size, padinfo_, dest); - dest.push_back(ch_); - } - -private: - char ch_; -}; - -// aggregate user chars to display as is -class aggregate_formatter final : public flag_formatter -{ -public: - aggregate_formatter() = default; - - void add_ch(char ch) - { - str_ += ch; - } - void format(const details::log_msg &, const std::tm &, fmt::memory_buffer &dest) override - { - fmt_helper::append_string_view(str_, dest); - } - -private: - std::string str_; -}; - -// mark the color range. expect it to be in the form of "%^colored text%$" -class color_start_formatter final : public flag_formatter -{ -public: - explicit color_start_formatter(padding_info padinfo) - : flag_formatter(padinfo) - { - } - - void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override - { - msg.color_range_start = dest.size(); - } -}; -class color_stop_formatter final : public flag_formatter -{ -public: - explicit color_stop_formatter(padding_info padinfo) - : flag_formatter(padinfo) - { - } - - void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override - { - msg.color_range_end = dest.size(); - } -}; - -// print source location -class source_location_formatter final : public flag_formatter -{ -public: - explicit source_location_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override - { - if (msg.source.empty()) - { - return; - } - if (padinfo_.enabled()) - { - const auto text_size = std::char_traits::length(msg.source.filename) + fmt_helper::count_digits(msg.source.line) + 1; - scoped_pad p(text_size, padinfo_, dest); - fmt_helper::append_string_view(msg.source.filename, dest); - dest.push_back(':'); - fmt_helper::append_int(msg.source.line, dest); - } - else - { - fmt_helper::append_string_view(msg.source.filename, dest); - dest.push_back(':'); - fmt_helper::append_int(msg.source.line, dest); - } - } -}; -// print source filename -class source_filename_formatter final : public flag_formatter -{ -public: - explicit source_filename_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override - { - if (msg.source.empty()) - { - return; - } - scoped_pad p(msg.source.filename, padinfo_, dest); - fmt_helper::append_string_view(msg.source.filename, dest); - } -}; - -class source_linenum_formatter final : public flag_formatter -{ -public: - explicit source_linenum_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override - { - if (msg.source.empty()) - { - return; - } - if (padinfo_.enabled()) - { - auto field_size = fmt_helper::count_digits(msg.source.line); - scoped_pad p(field_size, padinfo_, dest); - fmt_helper::append_int(msg.source.line, dest); - } - else - { - fmt_helper::append_int(msg.source.line, dest); - } - } -}; -// print source funcname -class source_funcname_formatter final : public flag_formatter -{ -public: - explicit source_funcname_formatter(padding_info padinfo) - : flag_formatter(padinfo){} - - void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override - { - if (msg.source.empty()) - { - return; - } - scoped_pad p(msg.source.funcname, padinfo_, dest); - fmt_helper::append_string_view(msg.source.funcname, dest); - } -}; - -// Full info formatter -// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v -class full_formatter final : public flag_formatter -{ -public: - explicit full_formatter(padding_info padinfo) - : flag_formatter(padinfo) - { - } - - void format(const details::log_msg &msg, const std::tm &tm_time, fmt::memory_buffer &dest) override - { - using std::chrono::duration_cast; - using std::chrono::milliseconds; - using std::chrono::seconds; - -#ifndef SPDLOG_NO_DATETIME - - // cache the date/time part for the next second. - auto duration = msg.time.time_since_epoch(); - auto secs = duration_cast(duration); - - if (cache_timestamp_ != secs || cached_datetime_.size() == 0) - { - cached_datetime_.clear(); - cached_datetime_.push_back('['); - fmt_helper::append_int(tm_time.tm_year + 1900, cached_datetime_); - cached_datetime_.push_back('-'); - - fmt_helper::pad2(tm_time.tm_mon + 1, cached_datetime_); - cached_datetime_.push_back('-'); - - fmt_helper::pad2(tm_time.tm_mday, cached_datetime_); - cached_datetime_.push_back(' '); - - fmt_helper::pad2(tm_time.tm_hour, cached_datetime_); - cached_datetime_.push_back(':'); - - fmt_helper::pad2(tm_time.tm_min, cached_datetime_); - cached_datetime_.push_back(':'); - - fmt_helper::pad2(tm_time.tm_sec, cached_datetime_); - cached_datetime_.push_back('.'); - - cache_timestamp_ = secs; - } - fmt_helper::append_buf(cached_datetime_, dest); - - auto millis = fmt_helper::time_fraction(msg.time); - fmt_helper::pad3(static_cast(millis.count()), dest); - dest.push_back(']'); - dest.push_back(' '); - -#else // no datetime needed - (void)tm_time; -#endif - -#ifndef SPDLOG_NO_NAME - if (!msg.logger_name->empty()) - { - dest.push_back('['); - // fmt_helper::append_str(*msg.logger_name, dest); - fmt_helper::append_string_view(*msg.logger_name, dest); - dest.push_back(']'); - dest.push_back(' '); - } -#endif - - dest.push_back('['); - // wrap the level name with color - msg.color_range_start = dest.size(); - // fmt_helper::append_string_view(level::to_c_str(msg.level), dest); - fmt_helper::append_string_view(level::to_string_view(msg.level), dest); - msg.color_range_end = dest.size(); - dest.push_back(']'); - dest.push_back(' '); - - // add source location if present - if (!msg.source.empty()) - { - dest.push_back('['); - fmt_helper::append_string_view(msg.source.filename, dest); - dest.push_back(':'); - fmt_helper::append_int(msg.source.line, dest); - dest.push_back(']'); - dest.push_back(' '); - } - // fmt_helper::append_string_view(msg.msg(), dest); - fmt_helper::append_string_view(msg.payload, dest); - } - -private: - std::chrono::seconds cache_timestamp_{0}; - fmt::basic_memory_buffer cached_datetime_; -}; - } // namespace details class pattern_formatter final : public formatter { public: explicit pattern_formatter( - std::string pattern, pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol) - : pattern_(std::move(pattern)) - , eol_(std::move(eol)) - , pattern_time_type_(time_type) - , last_log_secs_(0) - { - std::memset(&cached_tm_, 0, sizeof(cached_tm_)); - compile_pattern_(pattern_); - } + std::string pattern, pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol); - // use by default full formatter for if pattern is not given - explicit pattern_formatter(pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol) - : pattern_("%+") - , eol_(std::move(eol)) - , pattern_time_type_(time_type) - , last_log_secs_(0) - { - std::memset(&cached_tm_, 0, sizeof(cached_tm_)); - formatters_.push_back(details::make_unique(details::padding_info{})); - } + // use default pattern is not given + explicit pattern_formatter(pattern_time_type time_type = pattern_time_type::local, std::string eol = spdlog::details::os::default_eol); pattern_formatter(const pattern_formatter &other) = delete; pattern_formatter &operator=(const pattern_formatter &other) = delete; - std::unique_ptr clone() const override - { - return details::make_unique(pattern_, pattern_time_type_, eol_); - } - - void format(const details::log_msg &msg, fmt::memory_buffer &dest) override - { -#ifndef SPDLOG_NO_DATETIME - auto secs = std::chrono::duration_cast(msg.time.time_since_epoch()); - if (secs != last_log_secs_) - { - cached_tm_ = get_time_(msg); - last_log_secs_ = secs; - } -#endif - for (auto &f : formatters_) - { - f->format(msg, cached_tm_, dest); - } - // write eol - details::fmt_helper::append_string_view(eol_, dest); - } + std::unique_ptr clone() const override; + void format(const details::log_msg &msg, fmt::memory_buffer &dest) override; private: std::string pattern_; @@ -1068,269 +79,20 @@ private: pattern_time_type pattern_time_type_; std::tm cached_tm_; std::chrono::seconds last_log_secs_; - std::vector> formatters_; - std::tm get_time_(const details::log_msg &msg) - { - if (pattern_time_type_ == pattern_time_type::local) - { - return details::os::localtime(log_clock::to_time_t(msg.time)); - } - return details::os::gmtime(log_clock::to_time_t(msg.time)); - } - - void handle_flag_(char flag, details::padding_info padding) - { - switch (flag) - { - - case ('+'): // default formatter - formatters_.push_back(details::make_unique(padding)); - break; - - case 'n': // logger name - formatters_.push_back(details::make_unique(padding)); - break; - - case 'l': // level - formatters_.push_back(details::make_unique(padding)); - break; - - case 'L': // short level - formatters_.push_back(details::make_unique(padding)); - break; - - case ('t'): // thread id - formatters_.push_back(details::make_unique(padding)); - break; - - case ('v'): // the message text - formatters_.push_back(details::make_unique(padding)); - break; - - case ('a'): // weekday - formatters_.push_back(details::make_unique(padding)); - break; - - case ('A'): // short weekday - formatters_.push_back(details::make_unique(padding)); - break; - - case ('b'): - case ('h'): // month - formatters_.push_back(details::make_unique(padding)); - break; - - case ('B'): // short month - formatters_.push_back(details::make_unique(padding)); - break; - - case ('c'): // datetime - formatters_.push_back(details::make_unique(padding)); - break; - - case ('C'): // year 2 digits - formatters_.push_back(details::make_unique(padding)); - break; - - case ('Y'): // year 4 digits - formatters_.push_back(details::make_unique(padding)); - break; - - case ('D'): - case ('x'): // datetime MM/DD/YY - formatters_.push_back(details::make_unique(padding)); - break; - - case ('m'): // month 1-12 - formatters_.push_back(details::make_unique(padding)); - break; - - case ('d'): // day of month 1-31 - formatters_.push_back(details::make_unique(padding)); - break; - - case ('H'): // hours 24 - formatters_.push_back(details::make_unique(padding)); - break; - - case ('I'): // hours 12 - formatters_.push_back(details::make_unique(padding)); - break; - - case ('M'): // minutes - formatters_.push_back(details::make_unique(padding)); - break; - - case ('S'): // seconds - formatters_.push_back(details::make_unique(padding)); - break; - - case ('e'): // milliseconds - formatters_.push_back(details::make_unique(padding)); - break; - - case ('f'): // microseconds - formatters_.push_back(details::make_unique(padding)); - break; - - case ('F'): // nanoseconds - formatters_.push_back(details::make_unique(padding)); - break; - - case ('E'): // seconds since epoch - formatters_.push_back(details::make_unique(padding)); - break; - - case ('p'): // am/pm - formatters_.push_back(details::make_unique(padding)); - break; - - case ('r'): // 12 hour clock 02:55:02 pm - formatters_.push_back(details::make_unique(padding)); - break; - - case ('R'): // 24-hour HH:MM time - formatters_.push_back(details::make_unique(padding)); - break; - - case ('T'): - case ('X'): // ISO 8601 time format (HH:MM:SS) - formatters_.push_back(details::make_unique(padding)); - break; - - case ('z'): // timezone - formatters_.push_back(details::make_unique(padding)); - break; - - case ('P'): // pid - formatters_.push_back(details::make_unique(padding)); - break; - -#ifdef SPDLOG_ENABLE_MESSAGE_COUNTER - case ('i'): - formatters_.push_back(details::make_unique(padding)); - break; -#endif - case ('^'): // color range start - formatters_.push_back(details::make_unique(padding)); - break; - - case ('$'): // color range end - formatters_.push_back(details::make_unique(padding)); - break; - - case ('@'): // source location (filename:filenumber) - formatters_.push_back(details::make_unique(padding)); - break; - - case ('s'): // source filename - formatters_.push_back(details::make_unique(padding)); - break; - - case ('#'): // source line number - formatters_.push_back(details::make_unique(padding)); - break; - - case ('!'): // source funcname - formatters_.push_back(details::make_unique(padding)); - break; - - case ('%'): // % char - formatters_.push_back(details::make_unique('%')); - break; - - default: // Unknown flag appears as is - auto unknown_flag = details::make_unique(); - unknown_flag->add_ch('%'); - unknown_flag->add_ch(flag); - formatters_.push_back((std::move(unknown_flag))); - break; - } - } + std::tm get_time_(const details::log_msg &msg); + void handle_flag_(char flag, details::padding_info padding); // Extract given pad spec (e.g. %8X) // Advance the given it pass the end of the padding spec found (if any) // Return padding. - details::padding_info handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end) - { - using details::padding_info; - using details::scoped_pad; - const size_t max_width = 128; - if (it == end) - { - return padding_info{}; - } + details::padding_info handle_padspec_(std::string::const_iterator &it, std::string::const_iterator end); - padding_info::pad_side side; - switch (*it) - { - case '-': - side = padding_info::right; - ++it; - break; - case '=': - side = padding_info::center; - ++it; - break; - default: - side = details::padding_info::left; - break; - } - - if (it == end || !std::isdigit(static_cast(*it))) - { - return padding_info{0, side}; - } - - auto width = static_cast(*it - '0'); - for (++it; it != end && std::isdigit(static_cast(*it)); ++it) - { - auto digit = static_cast(*it - '0'); - width = width * 10 + digit; - } - return details::padding_info{std::min(width, max_width), side}; - } - - void compile_pattern_(const std::string &pattern) - { - auto end = pattern.end(); - std::unique_ptr user_chars; - formatters_.clear(); - for (auto it = pattern.begin(); it != end; ++it) - { - if (*it == '%') - { - if (user_chars) // append user chars found so far - { - formatters_.push_back(std::move(user_chars)); - } - - auto padding = handle_padspec_(++it, end); - - if (it != end) - { - handle_flag_(*it, padding); - } - else - { - break; - } - } - else // chars not following the % sign should be displayed as is - { - if (!user_chars) - { - user_chars = details::make_unique(); - } - user_chars->add_ch(*it); - } - } - if (user_chars) // append raw chars found so far - { - formatters_.push_back(std::move(user_chars)); - } - } + void compile_pattern_(const std::string &pattern); }; } // namespace spdlog + +#ifdef SPDLOG_HEADER_ONLY +#include "pattern_formatter-inl.h" +#endif diff --git a/include/spdlog/details/periodic_worker-inl.h b/include/spdlog/details/periodic_worker-inl.h new file mode 100644 index 00000000..f97ed4d1 --- /dev/null +++ b/include/spdlog/details/periodic_worker-inl.h @@ -0,0 +1,49 @@ +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/details/periodic_worker.h" +#endif + +namespace spdlog { +namespace details { + +SPDLOG_INLINE periodic_worker::periodic_worker(const std::function &callback_fun, std::chrono::seconds interval) +{ + active_ = (interval > std::chrono::seconds::zero()); + if (!active_) + { + return; + } + + worker_thread_ = std::thread([this, callback_fun, interval]() { + for (;;) + { + std::unique_lock lock(this->mutex_); + if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; })) + { + return; // active_ == false, so exit this thread + } + callback_fun(); + } + }); +} + +// stop the worker thread and join it +SPDLOG_INLINE periodic_worker::~periodic_worker() +{ + if (worker_thread_.joinable()) + { + { + std::lock_guard lock(mutex_); + active_ = false; + } + cv_.notify_one(); + worker_thread_.join(); + } +} + +} // namespace details +} // namespace spdlog diff --git a/include/spdlog/details/periodic_worker.h b/include/spdlog/details/periodic_worker.h index fa6488d1..24b9b96c 100644 --- a/include/spdlog/details/periodic_worker.h +++ b/include/spdlog/details/periodic_worker.h @@ -1,8 +1,5 @@ - -// -// Copyright(c) 2018 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once @@ -23,43 +20,11 @@ namespace details { class periodic_worker { public: - periodic_worker(const std::function &callback_fun, std::chrono::seconds interval) - { - active_ = (interval > std::chrono::seconds::zero()); - if (!active_) - { - return; - } - - worker_thread_ = std::thread([this, callback_fun, interval]() { - for (;;) - { - std::unique_lock lock(this->mutex_); - if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; })) - { - return; // active_ == false, so exit this thread - } - callback_fun(); - } - }); - } - + periodic_worker(const std::function &callback_fun, std::chrono::seconds interval); periodic_worker(const periodic_worker &) = delete; periodic_worker &operator=(const periodic_worker &) = delete; - // stop the worker thread and join it - ~periodic_worker() - { - if (worker_thread_.joinable()) - { - { - std::lock_guard lock(mutex_); - active_ = false; - } - cv_.notify_one(); - worker_thread_.join(); - } - } + ~periodic_worker(); private: bool active_; @@ -69,3 +34,7 @@ private: }; } // namespace details } // namespace spdlog + +#ifdef SPDLOG_HEADER_ONLY +#include "periodic_worker-inl.h" +#endif diff --git a/include/spdlog/details/registry-inl.h b/include/spdlog/details/registry-inl.h new file mode 100644 index 00000000..9ea46237 --- /dev/null +++ b/include/spdlog/details/registry-inl.h @@ -0,0 +1,259 @@ +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/details/registry.h" +#endif + +#include "spdlog/common.h" +#include "spdlog/details/periodic_worker.h" +#include "spdlog/logger.h" +#include "spdlog/details/pattern_formatter.h" + +#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER +// support for the default stdout color logger +#ifdef _WIN32 +#include "spdlog/sinks/wincolor_sink.h" +#else +#include "spdlog/sinks/ansicolor_sink.h" +#endif +#endif // SPDLOG_DISABLE_DEFAULT_LOGGER + +#include +#include +#include +#include +#include + +namespace spdlog { +namespace details { + +SPDLOG_INLINE registry::registry() + : formatter_(new pattern_formatter()) + , level_(spdlog::logger::default_level()) +{ + +#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER + // create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows). +#ifdef _WIN32 + auto color_sink = std::make_shared(); +#else + auto color_sink = std::make_shared(); +#endif + + const char *default_logger_name = ""; + default_logger_ = std::make_shared(default_logger_name, std::move(color_sink)); + loggers_[default_logger_name] = default_logger_; + +#endif // SPDLOG_DISABLE_DEFAULT_LOGGER +} +SPDLOG_INLINE void registry::register_logger(std::shared_ptr new_logger) +{ + std::lock_guard lock(logger_map_mutex_); + register_logger_(std::move(new_logger)); +} + +SPDLOG_INLINE void registry::initialize_logger(std::shared_ptr new_logger) +{ + std::lock_guard lock(logger_map_mutex_); + new_logger->set_formatter(formatter_->clone()); + + if (err_handler_) + { + new_logger->set_error_handler(err_handler_); + } + + new_logger->set_level(level_); + new_logger->flush_on(flush_level_); + + if (automatic_registration_) + { + register_logger_(std::move(new_logger)); + } +} + +SPDLOG_INLINE std::shared_ptr registry::get(const std::string &logger_name) +{ + std::lock_guard lock(logger_map_mutex_); + auto found = loggers_.find(logger_name); + return found == loggers_.end() ? nullptr : found->second; +} + +SPDLOG_INLINE std::shared_ptr registry::default_logger() +{ + std::lock_guard lock(logger_map_mutex_); + return default_logger_; +} + +// Return raw ptr to the default logger. +// To be used directly by the spdlog default api (e.g. spdlog::info) +// This make the default API faster, but cannot be used concurrently with set_default_logger(). +// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another. +SPDLOG_INLINE logger *registry::get_default_raw() +{ + return default_logger_.get(); +} + +// set default logger. +// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map. +SPDLOG_INLINE void registry::set_default_logger(std::shared_ptr new_default_logger) +{ + std::lock_guard lock(logger_map_mutex_); + // remove previous default logger from the map + if (default_logger_ != nullptr) + { + loggers_.erase(default_logger_->name()); + } + if (new_default_logger != nullptr) + { + loggers_[new_default_logger->name()] = new_default_logger; + } + default_logger_ = std::move(new_default_logger); +} + +SPDLOG_INLINE void registry::set_tp(std::shared_ptr tp) +{ + std::lock_guard lock(tp_mutex_); + tp_ = std::move(tp); +} + +SPDLOG_INLINE std::shared_ptr registry::get_tp() +{ + std::lock_guard lock(tp_mutex_); + return tp_; +} + +// Set global formatter. Each sink in each logger will get a clone of this object +SPDLOG_INLINE void registry::set_formatter(std::unique_ptr formatter) +{ + std::lock_guard lock(logger_map_mutex_); + formatter_ = std::move(formatter); + for (auto &l : loggers_) + { + l.second->set_formatter(formatter_->clone()); + } +} + +SPDLOG_INLINE void registry::set_level(level::level_enum log_level) +{ + std::lock_guard lock(logger_map_mutex_); + for (auto &l : loggers_) + { + l.second->set_level(log_level); + } + level_ = log_level; +} + +SPDLOG_INLINE void registry::flush_on(level::level_enum log_level) +{ + std::lock_guard lock(logger_map_mutex_); + for (auto &l : loggers_) + { + l.second->flush_on(log_level); + } + flush_level_ = log_level; +} + +SPDLOG_INLINE void registry::flush_every(std::chrono::seconds interval) +{ + std::lock_guard lock(flusher_mutex_); + std::function clbk = std::bind(®istry::flush_all, this); + periodic_flusher_ = details::make_unique(clbk, interval); +} + +SPDLOG_INLINE void registry::set_error_handler(void (*handler)(const std::string &msg)) +{ + std::lock_guard lock(logger_map_mutex_); + for (auto &l : loggers_) + { + l.second->set_error_handler(handler); + } + err_handler_ = handler; +} + +SPDLOG_INLINE void registry::apply_all(const std::function)> &fun) +{ + std::lock_guard lock(logger_map_mutex_); + for (auto &l : loggers_) + { + fun(l.second); + } +} + +SPDLOG_INLINE void registry::flush_all() +{ + std::lock_guard lock(logger_map_mutex_); + for (auto &l : loggers_) + { + l.second->flush(); + } +} + +SPDLOG_INLINE void registry::drop(const std::string &logger_name) +{ + std::lock_guard lock(logger_map_mutex_); + loggers_.erase(logger_name); + if (default_logger_ && default_logger_->name() == logger_name) + { + default_logger_.reset(); + } +} + +SPDLOG_INLINE void registry::drop_all() +{ + std::lock_guard lock(logger_map_mutex_); + loggers_.clear(); + default_logger_.reset(); +} + +// clean all resources and threads started by the registry +SPDLOG_INLINE void registry::shutdown() +{ + { + std::lock_guard lock(flusher_mutex_); + periodic_flusher_.reset(); + } + + drop_all(); + + { + std::lock_guard lock(tp_mutex_); + tp_.reset(); + } +} + +SPDLOG_INLINE std::recursive_mutex ®istry::tp_mutex() +{ + return tp_mutex_; +} + +SPDLOG_INLINE void registry::set_automatic_registration(bool automatic_regsistration) +{ + std::lock_guard lock(logger_map_mutex_); + automatic_registration_ = automatic_regsistration; +} + +SPDLOG_INLINE registry ®istry::instance() +{ + static registry s_instance; + return s_instance; +} + +SPDLOG_INLINE void registry::throw_if_exists_(const std::string &logger_name) +{ + if (loggers_.find(logger_name) != loggers_.end()) + { + throw spdlog_ex("logger with name '" + logger_name + "' already exists"); + } +} + +SPDLOG_INLINE void registry::register_logger_(std::shared_ptr new_logger) +{ + auto logger_name = new_logger->name(); + throw_if_exists_(logger_name); + loggers_[logger_name] = std::move(new_logger); +} +} // namespace details +} // namespace spdlog diff --git a/include/spdlog/details/registry.h b/include/spdlog/details/registry.h index ccd53955..bc6b2cc2 100644 --- a/include/spdlog/details/registry.h +++ b/include/spdlog/details/registry.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once @@ -11,27 +9,20 @@ // This class is thread safe #include "spdlog/common.h" -#include "spdlog/details/periodic_worker.h" -#include "spdlog/logger.h" - -#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER -// support for the default stdout color logger -#ifdef _WIN32 -#include "spdlog/sinks/wincolor_sink.h" -#else -#include "spdlog/sinks/ansicolor_sink.h" -#endif -#endif // SPDLOG_DISABLE_DEFAULT_LOGGER #include #include #include #include #include +#include namespace spdlog { +class logger; + namespace details { class thread_pool; +class periodic_worker; class registry { @@ -39,242 +30,66 @@ public: registry(const registry &) = delete; registry &operator=(const registry &) = delete; - void register_logger(std::shared_ptr new_logger) - { - std::lock_guard lock(logger_map_mutex_); - register_logger_(std::move(new_logger)); - } - - void initialize_logger(std::shared_ptr new_logger) - { - std::lock_guard lock(logger_map_mutex_); - new_logger->set_formatter(formatter_->clone()); - - if (err_handler_) - { - new_logger->set_error_handler(err_handler_); - } - - new_logger->set_level(level_); - new_logger->flush_on(flush_level_); - - if (automatic_registration_) - { - register_logger_(std::move(new_logger)); - } - } - - std::shared_ptr get(const std::string &logger_name) - { - std::lock_guard lock(logger_map_mutex_); - auto found = loggers_.find(logger_name); - return found == loggers_.end() ? nullptr : found->second; - } - - std::shared_ptr default_logger() - { - std::lock_guard lock(logger_map_mutex_); - return default_logger_; - } + void register_logger(std::shared_ptr new_logger); + void initialize_logger(std::shared_ptr new_logger); + std::shared_ptr get(const std::string &logger_name); + std::shared_ptr default_logger(); // Return raw ptr to the default logger. // To be used directly by the spdlog default api (e.g. spdlog::info) // This make the default API faster, but cannot be used concurrently with set_default_logger(). // e.g do not call set_default_logger() from one thread while calling spdlog::info() from another. - logger *get_default_raw() - { - return default_logger_.get(); - } + logger *get_default_raw(); // set default logger. // default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map. - void set_default_logger(std::shared_ptr new_default_logger) - { - std::lock_guard lock(logger_map_mutex_); - // remove previous default logger from the map - if (default_logger_ != nullptr) - { - loggers_.erase(default_logger_->name()); - } - if (new_default_logger != nullptr) - { - loggers_[new_default_logger->name()] = new_default_logger; - } - default_logger_ = std::move(new_default_logger); - } + void set_default_logger(std::shared_ptr new_default_logger); - void set_tp(std::shared_ptr tp) - { - std::lock_guard lock(tp_mutex_); - tp_ = std::move(tp); - } + void set_tp(std::shared_ptr tp); - std::shared_ptr get_tp() - { - std::lock_guard lock(tp_mutex_); - return tp_; - } + std::shared_ptr get_tp(); // Set global formatter. Each sink in each logger will get a clone of this object - void set_formatter(std::unique_ptr formatter) - { - std::lock_guard lock(logger_map_mutex_); - formatter_ = std::move(formatter); - for (auto &l : loggers_) - { - l.second->set_formatter(formatter_->clone()); - } - } + void set_formatter(std::unique_ptr formatter); - void set_level(level::level_enum log_level) - { - std::lock_guard lock(logger_map_mutex_); - for (auto &l : loggers_) - { - l.second->set_level(log_level); - } - level_ = log_level; - } + void set_level(level::level_enum log_level); - void flush_on(level::level_enum log_level) - { - std::lock_guard lock(logger_map_mutex_); - for (auto &l : loggers_) - { - l.second->flush_on(log_level); - } - flush_level_ = log_level; - } + void flush_on(level::level_enum log_level); - void flush_every(std::chrono::seconds interval) - { - std::lock_guard lock(flusher_mutex_); - std::function clbk = std::bind(®istry::flush_all, this); - periodic_flusher_ = details::make_unique(clbk, interval); - } + void flush_every(std::chrono::seconds interval); - void set_error_handler(log_err_handler handler) - { - std::lock_guard lock(logger_map_mutex_); - for (auto &l : loggers_) - { - l.second->set_error_handler(handler); - } - err_handler_ = handler; - } + void set_error_handler(void (*handler)(const std::string &msg)); - void apply_all(const std::function)> &fun) - { - std::lock_guard lock(logger_map_mutex_); - for (auto &l : loggers_) - { - fun(l.second); - } - } + void apply_all(const std::function)> &fun); - void flush_all() - { - std::lock_guard lock(logger_map_mutex_); - for (auto &l : loggers_) - { - l.second->flush(); - } - } + void flush_all(); - void drop(const std::string &logger_name) - { - std::lock_guard lock(logger_map_mutex_); - loggers_.erase(logger_name); - if (default_logger_ && default_logger_->name() == logger_name) - { - default_logger_.reset(); - } - } + void drop(const std::string &logger_name); - void drop_all() - { - std::lock_guard lock(logger_map_mutex_); - loggers_.clear(); - default_logger_.reset(); - } + void drop_all(); // clean all resources and threads started by the registry - void shutdown() - { - { - std::lock_guard lock(flusher_mutex_); - periodic_flusher_.reset(); - } + void shutdown(); - drop_all(); + std::recursive_mutex &tp_mutex(); - { - std::lock_guard lock(tp_mutex_); - tp_.reset(); - } - } + void set_automatic_registration(bool automatic_regsistration); - std::recursive_mutex &tp_mutex() - { - return tp_mutex_; - } - - void set_automatic_registration(bool automatic_regsistration) - { - std::lock_guard lock(logger_map_mutex_); - automatic_registration_ = automatic_regsistration; - } - - static registry &instance() - { - static registry s_instance; - return s_instance; - } + static registry &instance(); private: - registry() - : formatter_(new pattern_formatter()) - { - -#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER - // create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows). -#ifdef _WIN32 - auto color_sink = std::make_shared(); -#else - auto color_sink = std::make_shared(); -#endif - - const char *default_logger_name = ""; - default_logger_ = std::make_shared(default_logger_name, std::move(color_sink)); - loggers_[default_logger_name] = default_logger_; - -#endif // SPDLOG_DISABLE_DEFAULT_LOGGER - } - + registry(); ~registry() = default; - void throw_if_exists_(const std::string &logger_name) - { - if (loggers_.find(logger_name) != loggers_.end()) - { - throw spdlog_ex("logger with name '" + logger_name + "' already exists"); - } - } - - void register_logger_(std::shared_ptr new_logger) - { - auto logger_name = new_logger->name(); - throw_if_exists_(logger_name); - loggers_[logger_name] = std::move(new_logger); - } - + void throw_if_exists_(const std::string &logger_name); + void register_logger_(std::shared_ptr new_logger); std::mutex logger_map_mutex_, flusher_mutex_; std::recursive_mutex tp_mutex_; std::unordered_map> loggers_; std::unique_ptr formatter_; - level::level_enum level_ = spdlog::logger::default_level(); + level::level_enum level_ = level::info; level::level_enum flush_level_ = level::off; - log_err_handler err_handler_; + void (*err_handler_)(const std::string &msg); std::shared_ptr tp_; std::unique_ptr periodic_flusher_; std::shared_ptr default_logger_; @@ -283,3 +98,7 @@ private: } // namespace details } // namespace spdlog + +#ifdef SPDLOG_HEADER_ONLY +#include "registry-inl.h" +#endif diff --git a/include/spdlog/details/thread_pool-inl.h b/include/spdlog/details/thread_pool-inl.h new file mode 100644 index 00000000..61b94380 --- /dev/null +++ b/include/spdlog/details/thread_pool-inl.h @@ -0,0 +1,116 @@ +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/details/thread_pool.h" +#endif + +#include "spdlog/common.h" + +namespace spdlog { +namespace details { +SPDLOG_INLINE thread_pool::thread_pool(size_t q_max_items, size_t threads_n) + : q_(q_max_items) +{ + if (threads_n == 0 || threads_n > 1000) + { + throw spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid " + "range is 1-1000)"); + } + for (size_t i = 0; i < threads_n; i++) + { + threads_.emplace_back(&thread_pool::worker_loop_, this); + } +} + +// message all threads to terminate gracefully join them +SPDLOG_INLINE thread_pool::~thread_pool() +{ + try + { + for (size_t i = 0; i < threads_.size(); i++) + { + post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block); + } + + for (auto &t : threads_) + { + t.join(); + } + } + catch (...) + {} +} + +void SPDLOG_INLINE thread_pool::post_log(async_logger_ptr &&worker_ptr, details::log_msg &msg, async_overflow_policy overflow_policy) +{ + async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg); + post_async_msg_(std::move(async_m), overflow_policy); +} + +void SPDLOG_INLINE thread_pool::post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy) +{ + post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy); +} + +size_t SPDLOG_INLINE thread_pool::overrun_counter() +{ + return q_.overrun_counter(); +} + +void SPDLOG_INLINE thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy) +{ + if (overflow_policy == async_overflow_policy::block) + { + q_.enqueue(std::move(new_msg)); + } + else + { + q_.enqueue_nowait(std::move(new_msg)); + } +} + +void SPDLOG_INLINE thread_pool::worker_loop_() +{ + while (process_next_msg_()) {}; +} + +// process next message in the queue +// return true if this thread should still be active (while no terminate msg +// was received) +bool SPDLOG_INLINE thread_pool::process_next_msg_() +{ + async_msg incoming_async_msg; + bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10)); + if (!dequeued) + { + return true; + } + + switch (incoming_async_msg.msg_type) + { + case async_msg_type::log: + { + auto msg = incoming_async_msg.to_log_msg(); + incoming_async_msg.worker_ptr->backend_log_(msg); + return true; + } + case async_msg_type::flush: + { + incoming_async_msg.worker_ptr->backend_flush_(); + return true; + } + + case async_msg_type::terminate: + { + return false; + } + } + assert(false && "Unexpected async_msg_type"); + return true; +} + +} // namespace details +} // namespace spdlog diff --git a/include/spdlog/details/thread_pool.h b/include/spdlog/details/thread_pool.h index 35578971..bf1b61e8 100644 --- a/include/spdlog/details/thread_pool.h +++ b/include/spdlog/details/thread_pool.h @@ -1,6 +1,8 @@ +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + #pragma once -#include "spdlog/details/fmt_helper.h" #include "spdlog/details/log_msg.h" #include "spdlog/details/mpmc_blocking_q.h" #include "spdlog/details/os.h" @@ -11,6 +13,8 @@ #include namespace spdlog { +class async_logger; + namespace details { using async_logger_ptr = std::shared_ptr; @@ -52,8 +56,7 @@ struct async_msg msg_id(other.msg_id), source(other.source), worker_ptr(std::move(other.worker_ptr)) - { - } + {} async_msg &operator=(async_msg &&other) SPDLOG_NOEXCEPT { @@ -82,7 +85,7 @@ struct async_msg , source(m.source) , worker_ptr(std::move(worker)) { - fmt_helper::append_string_view(m.payload, raw); + raw.append(m.payload.data(), m.payload.data() + m.payload.size()); } async_msg(async_logger_ptr &&worker, async_msg_type the_type) @@ -93,13 +96,11 @@ struct async_msg , msg_id(0) , source() , worker_ptr(std::move(worker)) - { - } + {} explicit async_msg(async_msg_type the_type) : async_msg(nullptr, the_type) - { - } + {} // copy into log_msg log_msg to_log_msg() @@ -121,118 +122,34 @@ public: using item_type = async_msg; using q_type = details::mpmc_blocking_queue; - thread_pool(size_t q_max_items, size_t threads_n) - : q_(q_max_items) - { - // std::cout << "thread_pool() q_size_bytes: " << q_size_bytes << - // "\tthreads_n: " << threads_n << std::endl; - if (threads_n == 0 || threads_n > 1000) - { - throw spdlog_ex("spdlog::thread_pool(): invalid threads_n param (valid " - "range is 1-1000)"); - } - for (size_t i = 0; i < threads_n; i++) - { - threads_.emplace_back(&thread_pool::worker_loop_, this); - } - } - + thread_pool(size_t q_max_items, size_t threads_n); // message all threads to terminate gracefully join them - ~thread_pool() - { - try - { - for (size_t i = 0; i < threads_.size(); i++) - { - post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block); - } - - for (auto &t : threads_) - { - t.join(); - } - } - catch (...) - { - } - } + ~thread_pool(); thread_pool(const thread_pool &) = delete; thread_pool &operator=(thread_pool &&) = delete; - void post_log(async_logger_ptr &&worker_ptr, details::log_msg &msg, async_overflow_policy overflow_policy) - { - async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg); - post_async_msg_(std::move(async_m), overflow_policy); - } - - void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy) - { - post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy); - } - - size_t overrun_counter() - { - return q_.overrun_counter(); - } + void post_log(async_logger_ptr &&worker_ptr, details::log_msg &msg, async_overflow_policy overflow_policy); + void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy); + size_t overrun_counter(); private: q_type q_; std::vector threads_; - void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy) - { - if (overflow_policy == async_overflow_policy::block) - { - q_.enqueue(std::move(new_msg)); - } - else - { - q_.enqueue_nowait(std::move(new_msg)); - } - } - - void worker_loop_() - { - while (process_next_msg_()) {}; - } + void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy); + void worker_loop_(); // process next message in the queue // return true if this thread should still be active (while no terminate msg // was received) - bool process_next_msg_() - { - async_msg incoming_async_msg; - bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10)); - if (!dequeued) - { - return true; - } - - switch (incoming_async_msg.msg_type) - { - case async_msg_type::log: - { - auto msg = incoming_async_msg.to_log_msg(); - incoming_async_msg.worker_ptr->backend_log_(msg); - return true; - } - case async_msg_type::flush: - { - incoming_async_msg.worker_ptr->backend_flush_(); - return true; - } - - case async_msg_type::terminate: - { - return false; - } - } - assert(false && "Unexpected async_msg_type"); - return true; - } + bool process_next_msg_(); }; } // namespace details } // namespace spdlog + +#ifdef SPDLOG_HEADER_ONLY +#include "thread_pool-inl.h" +#endif \ No newline at end of file diff --git a/include/spdlog/fmt/bin_to_hex.h b/include/spdlog/fmt/bin_to_hex.h index 35233802..4a091dde 100644 --- a/include/spdlog/fmt/bin_to_hex.h +++ b/include/spdlog/fmt/bin_to_hex.h @@ -31,8 +31,7 @@ public: bytes_range(It range_begin, It range_end) : begin_(range_begin) , end_(range_end) - { - } + {} It begin() const { diff --git a/include/spdlog/fmt/fmt.h b/include/spdlog/fmt/fmt.h index 616af0cc..5d039b8c 100644 --- a/include/spdlog/fmt/fmt.h +++ b/include/spdlog/fmt/fmt.h @@ -11,15 +11,17 @@ // #if !defined(SPDLOG_FMT_EXTERNAL) +#ifdef SPDLOG_HEADER_ONLY #ifndef FMT_HEADER_ONLY #define FMT_HEADER_ONLY #endif +#endif #ifndef FMT_USE_WINDOWS_H #define FMT_USE_WINDOWS_H 0 #endif #include "bundled/core.h" #include "bundled/format.h" -#else // external fmtlib -#include -#include +#else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib +#include "fmt/core.h" +#include "fmt/format.h" #endif diff --git a/include/spdlog/formatter.h b/include/spdlog/formatter.h index a7ef6b8b..16d86969 100644 --- a/include/spdlog/formatter.h +++ b/include/spdlog/formatter.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once diff --git a/include/spdlog/logger-inl.h b/include/spdlog/logger-inl.h new file mode 100644 index 00000000..374376be --- /dev/null +++ b/include/spdlog/logger-inl.h @@ -0,0 +1,182 @@ +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/logger.h" +#endif + +#include "spdlog/sinks/sink.h" +#include "spdlog/details/pattern_formatter.h" + +namespace spdlog { +// public methods +SPDLOG_INLINE void logger::log(source_loc loc, level::level_enum lvl, const char *msg) +{ + if (!should_log(lvl)) + { + return; + } + + try + { + details::log_msg log_msg(loc, &name_, lvl, string_view_t(msg)); + sink_it_(log_msg); + } + catch (const std::exception &ex) + { + err_handler_(ex.what()); + } + catch (...) + { + err_handler_("Unknown exception in logger"); + } +} + +SPDLOG_INLINE void logger::log(level::level_enum lvl, const char *msg) +{ + log(source_loc{}, lvl, msg); +} + +SPDLOG_INLINE bool logger::should_log(level::level_enum msg_level) const +{ + return msg_level >= level_.load(std::memory_order_relaxed); +} + +SPDLOG_INLINE void logger::set_level(level::level_enum log_level) +{ + level_.store(log_level); +} + +SPDLOG_INLINE level::level_enum logger::default_level() +{ + return static_cast(SPDLOG_ACTIVE_LEVEL); +} + +SPDLOG_INLINE level::level_enum logger::level() const +{ + return static_cast(level_.load(std::memory_order_relaxed)); +} + +SPDLOG_INLINE const std::string &logger::name() const +{ + return name_; +} + +// set formatting for the sinks in this logger. +// each sink will get a seperate instance of the formatter object. +SPDLOG_INLINE void logger::set_formatter(std::unique_ptr f) +{ + for (auto &sink : sinks_) + { + sink->set_formatter(f->clone()); + } +} + +SPDLOG_INLINE void logger::set_pattern(std::string pattern, pattern_time_type time_type) +{ + auto new_formatter = details::make_unique(std::move(pattern), time_type); + set_formatter(std::move(new_formatter)); +} + +// flush functions +SPDLOG_INLINE void logger::flush() +{ + try + { + flush_(); + } + catch (const std::exception &ex) + { + err_handler_(ex.what()); + } + catch (...) + { + err_handler_("Unknown exception in logger"); + } +} + +SPDLOG_INLINE void logger::flush_on(level::level_enum log_level) +{ + flush_level_.store(log_level); +} + +SPDLOG_INLINE level::level_enum logger::flush_level() const +{ + return static_cast(flush_level_.load(std::memory_order_relaxed)); +} + +// sinks +SPDLOG_INLINE const std::vector &logger::sinks() const +{ + return sinks_; +} + +SPDLOG_INLINE std::vector &logger::sinks() +{ + return sinks_; +} + +// error handler +SPDLOG_INLINE void logger::set_error_handler(err_handler handler) +{ + custom_err_handler_ = handler; +} + +// create new logger with same sinks and configuration. +SPDLOG_INLINE std::shared_ptr logger::clone(std::string logger_name) +{ + auto cloned = std::make_shared(std::move(logger_name), sinks_.begin(), sinks_.end()); + cloned->set_level(this->level()); + cloned->flush_on(this->flush_level()); + cloned->set_error_handler(this->custom_err_handler_); + return cloned; +} + +// protected methods +SPDLOG_INLINE void logger::sink_it_(details::log_msg &msg) +{ + for (auto &sink : sinks_) + { + if (sink->should_log(msg.level)) + { + sink->log(msg); + } + } + + if (should_flush_(msg)) + { + flush_(); + } +} + +SPDLOG_INLINE void logger::flush_() +{ + for (auto &sink : sinks_) + { + sink->flush(); + } +} + +SPDLOG_INLINE bool logger::should_flush_(const details::log_msg &msg) +{ + auto flush_level = flush_level_.load(std::memory_order_relaxed); + return (msg.level >= flush_level) && (msg.level != level::off); +} + +SPDLOG_INLINE void logger::err_handler_(const std::string &msg) +{ + if (custom_err_handler_) + { + custom_err_handler_(msg); + } + else + { + auto tm_time = details::os::localtime(); + char date_buf[64]; + std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time); + fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), msg); + } +} +} // namespace spdlog \ No newline at end of file diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h index ec06828f..89fed05d 100644 --- a/include/spdlog/logger.h +++ b/include/spdlog/logger.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2015-2108 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once @@ -15,130 +13,292 @@ // and send to its destination. // // The use of private formatter per sink provides the opportunity to cache some -// formatted data, -// and support customize format per each sink. +// formatted data, and support for different format per sink. #include "spdlog/common.h" -#include "spdlog/formatter.h" -#include "spdlog/sinks/sink.h" +#include "spdlog/details/log_msg.h" + +#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT +#include "spdlog/details/os.h" +#endif #include #include #include +#include namespace spdlog { - class logger { public: - logger(std::string name, sink_ptr single_sink); - logger(std::string name, sinks_init_list sinks); - template - logger(std::string name, It begin, It end); + logger(std::string name, It begin, It end) + : name_(std::move(name)) + , sinks_(begin, end) + {} - virtual ~logger(); + logger(std::string name, sink_ptr single_sink) + : logger(std::move(name), {std::move(single_sink)}) + {} + logger(std::string name, sinks_init_list sinks) + : logger(std::move(name), sinks.begin(), sinks.end()) + {} + + virtual ~logger() = default; logger(const logger &) = delete; logger &operator=(const logger &) = delete; template - void log(level::level_enum lvl, const char *fmt, const Args &... args); + void log(source_loc loc, level::level_enum lvl, const char *fmt, const Args &... args) + { + if (!should_log(lvl)) + { + return; + } + + try + { + fmt::memory_buffer buf; + fmt::format_to(buf, fmt, args...); + details::log_msg log_msg(loc, &name_, lvl, string_view_t(buf.data(), buf.size())); + sink_it_(log_msg); + } + catch (const std::exception &ex) + { + err_handler_(ex.what()); + } + catch (...) + { + err_handler_("Unknown exception in logger"); + } + } template - void log(source_loc loc, level::level_enum lvl, const char *fmt, const Args &... args); - - void log(level::level_enum lvl, const char *msg); + void log(level::level_enum lvl, const char *fmt, const Args &... args) + { + log(source_loc{}, lvl, fmt, args...); + } void log(source_loc loc, level::level_enum lvl, const char *msg); + void log(level::level_enum lvl, const char *msg); template - void trace(const char *fmt, const Args &... args); + void trace(const char *fmt, const Args &... args) + { + log(level::trace, fmt, args...); + } template - void debug(const char *fmt, const Args &... args); + void debug(const char *fmt, const Args &... args) + { + log(level::debug, fmt, args...); + } template - void info(const char *fmt, const Args &... args); + void info(const char *fmt, const Args &... args) + { + log(level::info, fmt, args...); + } template - void warn(const char *fmt, const Args &... args); + void warn(const char *fmt, const Args &... args) + { + log(level::warn, fmt, args...); + } template - void error(const char *fmt, const Args &... args); + void error(const char *fmt, const Args &... args) + { + log(level::err, fmt, args...); + } template - void critical(const char *fmt, const Args &... args); + void critical(const char *fmt, const Args &... args) + { + log(level::critical, fmt, args...); + } + + template + void log(level::level_enum lvl, const T &msg) + { + log(source_loc{}, lvl, msg); + } + + // T can be statically converted to string_view + template::value, T>::type * = nullptr> + void log(source_loc loc, level::level_enum lvl, const T &msg) + { + if (!should_log(lvl)) + { + return; + } + try + { + details::log_msg log_msg(loc, &name_, lvl, msg); + sink_it_(log_msg); + } + catch (const std::exception &ex) + { + err_handler_(ex.what()); + } + catch (...) + { + err_handler_("Unknown exception in logger"); + } + } + + // T cannot be statically converted to string_view + template::value, T>::type * = nullptr> + void log(source_loc loc, level::level_enum lvl, const T &msg) + { + if (!should_log(lvl)) + { + return; + } + try + { + fmt::memory_buffer buf; + fmt::format_to(buf, "{}", msg); + details::log_msg log_msg(loc, &name_, lvl, string_view_t(buf.data(), buf.size())); + sink_it_(log_msg); + } + catch (const std::exception &ex) + { + err_handler_(ex.what()); + } + catch (...) + { + err_handler_("Unknown exception in logger"); + } + } + + template + void trace(const T &msg) + { + log(level::trace, msg); + } + + template + void debug(const T &msg) + { + log(level::debug, msg); + } + + template + void info(const T &msg) + { + log(level::info, msg); + } + + template + void warn(const T &msg) + { + log(level::warn, msg); + } + + template + void error(const T &msg) + { + log(level::err, msg); + } + + template + void critical(const T &msg) + { + log(level::critical, msg); + } #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT #ifndef _WIN32 #error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows #else template - void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args); + void log(source_loc source, level::level_enum lvl, const wchar_t *fmt, const Args &... args) + { + if (!should_log(lvl)) + { + return; + } + + try + { + // format to wmemory_buffer and convert to utf8 + fmt::wmemory_buffer wbuf; + fmt::format_to(wbuf, fmt, args...); + fmt::memory_buffer buf; + details::os::wbuf_to_utf8buf(wbuf, buf); + details::log_msg log_msg(source, &name_, lvl, string_view_t(buf.data(), buf.size())); + sink_it_(log_msg); + } + catch (const std::exception &ex) + { + err_handler_(ex.what()); + } + catch (...) + { + err_handler_("Unknown exception in logger"); + } + } template - void log(source_loc source, level::level_enum lvl, const wchar_t *fmt, const Args &... args); + void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args) + { + log(source_loc{}, lvl, fmt, args...); + } template - void trace(const wchar_t *fmt, const Args &... args); + void trace(const wchar_t *fmt, const Args &... args) + { + log(level::trace, fmt, args...); + } template - void debug(const wchar_t *fmt, const Args &... args); + void debug(const wchar_t *fmt, const Args &... args) + { + log(level::debug, fmt, args...); + } template - void info(const wchar_t *fmt, const Args &... args); + void info(const wchar_t *fmt, const Args &... args) + { + log(level::info, fmt, args...); + } template - void warn(const wchar_t *fmt, const Args &... args); + void warn(const wchar_t *fmt, const Args &... args) + { + log(level::warn, fmt, args...); + } template - void error(const wchar_t *fmt, const Args &... args); + void error(const wchar_t *fmt, const Args &... args) + { + log(level::err, fmt, args...); + } template - void critical(const wchar_t *fmt, const Args &... args); + void critical(const wchar_t *fmt, const Args &... args) + { + log(level::critical, fmt, args...); + } #endif // _WIN32 #endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT - template - void log(level::level_enum lvl, const T &); - - // T can be statically converted to string_view - template::value, T>::type * = nullptr> - void log(source_loc loc, level::level_enum lvl, const T &); - - // T cannot be statically converted to string_view - template::value, T>::type * = nullptr> - void log(source_loc loc, level::level_enum lvl, const T &); - - template - void trace(const T &msg); - - template - void debug(const T &msg); - - template - void info(const T &msg); - - template - void warn(const T &msg); - - template - void error(const T &msg); - - template - void critical(const T &msg); - bool should_log(level::level_enum msg_level) const; + void set_level(level::level_enum log_level); static level::level_enum default_level(); + level::level_enum level() const; + const std::string &name() const; // set formatting for the sinks in this logger. // each sink will get a seperate instance of the formatter object. - void set_formatter(std::unique_ptr formatter); + void set_formatter(std::unique_ptr f); + void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local); // flush functions @@ -148,36 +308,33 @@ public: // sinks const std::vector &sinks() const; + std::vector &sinks(); // error handler - void set_error_handler(log_err_handler err_handler); - log_err_handler error_handler() const; + void set_error_handler(err_handler); // create new logger with same sinks and configuration. virtual std::shared_ptr clone(std::string logger_name); protected: virtual void sink_it_(details::log_msg &msg); - virtual void flush_(); + virtual void flush_(); bool should_flush_(const details::log_msg &msg); // default error handler. // print the error to stderr with the max rate of 1 message/minute. - void default_err_handler_(const std::string &msg); - - // increment the message count (only if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)) - void incr_msg_counter_(details::log_msg &msg); + void err_handler_(const std::string &msg); const std::string name_; std::vector sinks_; spdlog::level_t level_{spdlog::logger::default_level()}; spdlog::level_t flush_level_{level::off}; - log_err_handler err_handler_{[this](const std::string &msg) { this->default_err_handler_(msg); }}; - std::atomic last_err_time_{0}; - std::atomic msg_counter_{1}; + err_handler custom_err_handler_{nullptr}; }; } // namespace spdlog -#include "details/logger_impl.h" +#ifdef SPDLOG_HEADER_ONLY +#include "logger-inl.h" +#endif diff --git a/include/spdlog/sinks/android_sink.h b/include/spdlog/sinks/android_sink.h index ae7f7733..52b1ac8c 100644 --- a/include/spdlog/sinks/android_sink.h +++ b/include/spdlog/sinks/android_sink.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once @@ -37,8 +35,7 @@ public: explicit android_sink(std::string tag = "spdlog", bool use_raw_msg = false) : tag_(std::move(tag)) , use_raw_msg_(use_raw_msg) - { - } + {} protected: void sink_it_(const details::log_msg &msg) override diff --git a/include/spdlog/sinks/ansicolor_sink-inl.h b/include/spdlog/sinks/ansicolor_sink-inl.h new file mode 100644 index 00000000..c7c3fa2d --- /dev/null +++ b/include/spdlog/sinks/ansicolor_sink-inl.h @@ -0,0 +1,119 @@ +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/sinks/ansicolor_sink.h" +#endif + +#include "spdlog/details/os.h" + +template +SPDLOG_INLINE spdlog::sinks::ansicolor_sink::ansicolor_sink(color_mode mode) + : target_file_(TargetStream::stream()) + , mutex_(ConsoleMutex::mutex()) + +{ + set_color_mode(mode); + colors_[level::trace] = white; + colors_[level::debug] = cyan; + colors_[level::info] = green; + colors_[level::warn] = yellow + bold; + colors_[level::err] = red + bold; + colors_[level::critical] = bold + on_red; + colors_[level::off] = reset; +} + +template +SPDLOG_INLINE void spdlog::sinks::ansicolor_sink::set_color( + level::level_enum color_level, const std::string &color) +{ + std::lock_guard lock(mutex_); + colors_[color_level] = color; +} + +template +SPDLOG_INLINE void spdlog::sinks::ansicolor_sink::log(const details::log_msg &msg) +{ + // Wrap the originally formatted message in color codes. + // If color is not supported in the terminal, log as is instead. + std::lock_guard lock(mutex_); + + fmt::memory_buffer formatted; + formatter_->format(msg, formatted); + if (should_do_colors_ && msg.color_range_end > msg.color_range_start) + { + // before color range + print_range_(formatted, 0, msg.color_range_start); + // in color range + print_ccode_(colors_[msg.level]); + print_range_(formatted, msg.color_range_start, msg.color_range_end); + print_ccode_(reset); + // after color range + print_range_(formatted, msg.color_range_end, formatted.size()); + } + else // no color + { + print_range_(formatted, 0, formatted.size()); + } + fflush(target_file_); +} + +template +SPDLOG_INLINE void spdlog::sinks::ansicolor_sink::flush() +{ + std::lock_guard lock(mutex_); + fflush(target_file_); +} + +template +SPDLOG_INLINE void spdlog::sinks::ansicolor_sink::set_pattern(const std::string &pattern) +{ + std::lock_guard lock(mutex_); + formatter_ = std::unique_ptr(new pattern_formatter(pattern)); +} + +template +SPDLOG_INLINE void spdlog::sinks::ansicolor_sink::set_formatter( + std::unique_ptr sink_formatter) +{ + std::lock_guard lock(mutex_); + formatter_ = std::move(sink_formatter); +} + +template +SPDLOG_INLINE bool spdlog::sinks::ansicolor_sink::should_color() +{ + return should_do_colors_; +} + +template +SPDLOG_INLINE void spdlog::sinks::ansicolor_sink::set_color_mode(color_mode mode) +{ + switch (mode) + { + case color_mode::always: + should_do_colors_ = true; + return; + case color_mode::automatic: + should_do_colors_ = details::os::in_terminal(target_file_) && details::os::is_color_terminal(); + return; + case color_mode::never: + should_do_colors_ = false; + return; + } +} + +template +SPDLOG_INLINE void spdlog::sinks::ansicolor_sink::print_ccode_(const std::string &color_code) +{ + fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_); +} + +template +SPDLOG_INLINE void spdlog::sinks::ansicolor_sink::print_range_( + const fmt::memory_buffer &formatted, size_t start, size_t end) +{ + fwrite(formatted.data() + start, sizeof(char), end - start, target_file_); +} diff --git a/include/spdlog/sinks/ansicolor_sink.h b/include/spdlog/sinks/ansicolor_sink.h index 3fbbf27b..f940255e 100644 --- a/include/spdlog/sinks/ansicolor_sink.h +++ b/include/spdlog/sinks/ansicolor_sink.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2017 spdlog authors. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once @@ -11,9 +9,7 @@ #include "spdlog/details/console_globals.h" #include "spdlog/details/null_mutex.h" -#include "spdlog/details/os.h" #include "spdlog/sinks/sink.h" - #include #include #include @@ -28,36 +24,23 @@ namespace sinks { * of the message. * If no color terminal detected, omit the escape codes. */ -template +template class ansicolor_sink final : public sink { public: using mutex_t = typename ConsoleMutex::mutex_t; - ansicolor_sink(color_mode mode = color_mode::automatic) - : target_file_(TargetStream::stream()) - , mutex_(ConsoleMutex::mutex()) - - { - set_color_mode_(mode); - colors_[level::trace] = white; - colors_[level::debug] = cyan; - colors_[level::info] = green; - colors_[level::warn] = yellow + bold; - colors_[level::err] = red + bold; - colors_[level::critical] = bold + on_red; - colors_[level::off] = reset; - } - + ansicolor_sink(color_mode mode = color_mode::automatic); ~ansicolor_sink() override = default; ansicolor_sink(const ansicolor_sink &other) = delete; ansicolor_sink &operator=(const ansicolor_sink &other) = delete; - - void set_color(level::level_enum color_level, const std::string &color) - { - std::lock_guard lock(mutex_); - colors_[color_level] = color; - } + void set_color(level::level_enum color_level, const std::string &color); + void log(const details::log_msg &msg) override; + void flush() override; + void set_pattern(const std::string &pattern) final; + void set_formatter(std::unique_ptr sink_formatter) override; + bool should_color(); + void set_color_mode(color_mode mode); /// Formatting codes const std::string reset = "\033[m"; @@ -89,93 +72,13 @@ public: const std::string on_cyan = "\033[46m"; const std::string on_white = "\033[47m"; - void log(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. - std::lock_guard lock(mutex_); - - fmt::memory_buffer formatted; - formatter_->format(msg, formatted); - if (should_do_colors_ && msg.color_range_end > msg.color_range_start) - { - // before color range - print_range_(formatted, 0, msg.color_range_start); - // in color range - print_ccode_(colors_[msg.level]); - print_range_(formatted, msg.color_range_start, msg.color_range_end); - print_ccode_(reset); - // after color range - print_range_(formatted, msg.color_range_end, formatted.size()); - } - else // no color - { - print_range_(formatted, 0, formatted.size()); - } - fflush(target_file_); - } - - void flush() override - { - std::lock_guard lock(mutex_); - fflush(target_file_); - } - - void set_pattern(const std::string &pattern) final - { - std::lock_guard lock(mutex_); - formatter_ = std::unique_ptr(new pattern_formatter(pattern)); - } - - void set_formatter(std::unique_ptr sink_formatter) override - { - std::lock_guard lock(mutex_); - formatter_ = std::move(sink_formatter); - } - - bool should_color() - { - std::lock_guard lock(mutex_); - return should_do_colors_; - } - - void set_color_mode(color_mode mode) - { - std::lock_guard lock(mutex_); - set_color_mode_(mode); - } - private: - void set_color_mode_(color_mode mode) - { - switch (mode) - { - case color_mode::always: - should_do_colors_ = true; - break; - case color_mode::automatic: - should_do_colors_ = details::os::in_terminal(target_file_) && details::os::is_color_terminal(); - break; - case color_mode::never: - should_do_colors_ = false; - break; - } - } - - void print_ccode_(const std::string &color_code) - { - fwrite(color_code.data(), sizeof(char), color_code.size(), target_file_); - } - void print_range_(const fmt::memory_buffer &formatted, size_t start, size_t end) - { - fwrite(formatted.data() + start, sizeof(char), end - start, target_file_); - } - FILE *target_file_; mutex_t &mutex_; - bool should_do_colors_; std::unordered_map colors_; + void print_ccode_(const std::string &color_code); + void print_range_(const fmt::memory_buffer &formatted, size_t start, size_t end); }; using ansicolor_stdout_sink_mt = ansicolor_sink; @@ -185,5 +88,8 @@ using ansicolor_stderr_sink_mt = ansicolor_sink; } // namespace sinks - } // namespace spdlog + +#ifdef SPDLOG_HEADER_ONLY +#include "ansicolor_sink-inl.h" +#endif diff --git a/include/spdlog/sinks/base_sink-inl.h b/include/spdlog/sinks/base_sink-inl.h new file mode 100644 index 00000000..4ee23439 --- /dev/null +++ b/include/spdlog/sinks/base_sink-inl.h @@ -0,0 +1,51 @@ +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/sinks/base_sink.h" +#endif + +#include "spdlog/common.h" +#include "spdlog/details/pattern_formatter.h" + +template +void SPDLOG_INLINE spdlog::sinks::base_sink::log(const details::log_msg &msg) +{ + std::lock_guard lock(mutex_); + sink_it_(msg); +} + +template +void SPDLOG_INLINE spdlog::sinks::base_sink::flush() +{ + std::lock_guard lock(mutex_); + flush_(); +} + +template +void SPDLOG_INLINE spdlog::sinks::base_sink::set_pattern(const std::string &pattern) +{ + std::lock_guard lock(mutex_); + set_pattern_(pattern); +} + +template +void SPDLOG_INLINE spdlog::sinks::base_sink::set_formatter(std::unique_ptr sink_formatter) +{ + std::lock_guard lock(mutex_); + set_formatter_(std::move(sink_formatter)); +} + +template +void SPDLOG_INLINE spdlog::sinks::base_sink::set_pattern_(const std::string &pattern) +{ + set_formatter_(details::make_unique(pattern)); +} + +template +void SPDLOG_INLINE spdlog::sinks::base_sink::set_formatter_(std::unique_ptr sink_formatter) +{ + formatter_ = std::move(sink_formatter); +} diff --git a/include/spdlog/sinks/base_sink.h b/include/spdlog/sinks/base_sink.h index 22595182..c9f5670e 100644 --- a/include/spdlog/sinks/base_sink.h +++ b/include/spdlog/sinks/base_sink.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once // @@ -13,7 +11,6 @@ #include "spdlog/common.h" #include "spdlog/details/log_msg.h" -#include "spdlog/formatter.h" #include "spdlog/sinks/sink.h" namespace spdlog { @@ -25,45 +22,21 @@ public: base_sink() = default; base_sink(const base_sink &) = delete; base_sink &operator=(const base_sink &) = delete; - - void log(const details::log_msg &msg) final - { - std::lock_guard lock(mutex_); - sink_it_(msg); - } - - void flush() final - { - std::lock_guard lock(mutex_); - flush_(); - } - - void set_pattern(const std::string &pattern) final - { - std::lock_guard lock(mutex_); - set_pattern_(pattern); - } - - void set_formatter(std::unique_ptr sink_formatter) final - { - std::lock_guard lock(mutex_); - set_formatter_(std::move(sink_formatter)); - } + void log(const details::log_msg &msg) final; + void flush() final; + void set_pattern(const std::string &pattern) final; + void set_formatter(std::unique_ptr sink_formatter) final; protected: virtual void sink_it_(const details::log_msg &msg) = 0; virtual void flush_() = 0; - - virtual void set_pattern_(const std::string &pattern) - { - set_formatter_(details::make_unique(pattern)); - } - - virtual void set_formatter_(std::unique_ptr sink_formatter) - { - formatter_ = std::move(sink_formatter); - } + virtual void set_pattern_(const std::string &pattern); + virtual void set_formatter_(std::unique_ptr sink_formatter); Mutex mutex_; }; } // namespace sinks } // namespace spdlog + +#ifndef SPDLOG_COMPILED_LIB +#include "base_sink-inl.h" +#endif diff --git a/include/spdlog/sinks/basic_file_sink-inl.h b/include/spdlog/sinks/basic_file_sink-inl.h new file mode 100644 index 00000000..254e5085 --- /dev/null +++ b/include/spdlog/sinks/basic_file_sink-inl.h @@ -0,0 +1,43 @@ +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/sinks/basic_file_sink.h" +#endif + +#include "spdlog/common.h" +#include "spdlog/details/os.h" + +namespace spdlog { +namespace sinks { + +template +SPDLOG_INLINE basic_file_sink::basic_file_sink(const filename_t &filename, bool truncate) +{ + file_helper_.open(filename, truncate); +} + +template +SPDLOG_INLINE const filename_t &basic_file_sink::filename() const +{ + return file_helper_.filename(); +} + +template +SPDLOG_INLINE void basic_file_sink::sink_it_(const details::log_msg &msg) +{ + fmt::memory_buffer formatted; + sink::formatter_->format(msg, formatted); + file_helper_.write(formatted); +} + +template +SPDLOG_INLINE void basic_file_sink::flush_() +{ + file_helper_.flush(); +} + +} // namespace sinks +} // namespace spdlog diff --git a/include/spdlog/sinks/basic_file_sink.h b/include/spdlog/sinks/basic_file_sink.h index facc7209..95a7a336 100644 --- a/include/spdlog/sinks/basic_file_sink.h +++ b/include/spdlog/sinks/basic_file_sink.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2015-2018 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once @@ -25,28 +23,12 @@ template class basic_file_sink final : public base_sink { public: - explicit basic_file_sink(const filename_t &filename, bool truncate = false) - { - file_helper_.open(filename, truncate); - } - - const filename_t &filename() const - { - return file_helper_.filename(); - } + explicit basic_file_sink(const filename_t &filename, bool truncate = false); + const filename_t &filename() const; protected: - void sink_it_(const details::log_msg &msg) override - { - fmt::memory_buffer formatted; - sink::formatter_->format(msg, formatted); - file_helper_.write(formatted); - } - - void flush_() override - { - file_helper_.flush(); - } + void sink_it_(const details::log_msg &msg) override; + void flush_() override; private: details::file_helper file_helper_; @@ -73,3 +55,7 @@ inline std::shared_ptr basic_logger_st(const std::string &logger_name, c } } // namespace spdlog + +#ifdef SPDLOG_HEADER_ONLY +#include "basic_file_sink-inl.h" +#endif \ No newline at end of file diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h index 08392c16..23ca6f0b 100644 --- a/include/spdlog/sinks/daily_file_sink.h +++ b/include/spdlog/sinks/daily_file_sink.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once @@ -13,6 +11,7 @@ #include "spdlog/details/null_mutex.h" #include "spdlog/fmt/fmt.h" #include "spdlog/sinks/base_sink.h" +#include "spdlog/details/os.h" #include #include diff --git a/include/spdlog/sinks/dist_sink.h b/include/spdlog/sinks/dist_sink.h index 44de3913..90da7dd2 100644 --- a/include/spdlog/sinks/dist_sink.h +++ b/include/spdlog/sinks/dist_sink.h @@ -1,7 +1,5 @@ -// -// Copyright (c) 2015 David Schury, Gabi Melman +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once diff --git a/include/spdlog/sinks/msvc_sink.h b/include/spdlog/sinks/msvc_sink.h index f06c16c7..6866dd7a 100644 --- a/include/spdlog/sinks/msvc_sink.h +++ b/include/spdlog/sinks/msvc_sink.h @@ -1,7 +1,5 @@ -// // Copyright(c) 2016 Alexander Dalshov. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once diff --git a/include/spdlog/sinks/null_sink.h b/include/spdlog/sinks/null_sink.h index 54f322c4..ab681575 100644 --- a/include/spdlog/sinks/null_sink.h +++ b/include/spdlog/sinks/null_sink.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once diff --git a/include/spdlog/sinks/ostream_sink.h b/include/spdlog/sinks/ostream_sink.h index 22e377b6..4cc5fb38 100644 --- a/include/spdlog/sinks/ostream_sink.h +++ b/include/spdlog/sinks/ostream_sink.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once @@ -24,8 +22,7 @@ public: explicit ostream_sink(std::ostream &os, bool force_flush = false) : ostream_(os) , force_flush_(force_flush) - { - } + {} ostream_sink(const ostream_sink &) = delete; ostream_sink &operator=(const ostream_sink &) = delete; diff --git a/include/spdlog/sinks/rotating_file_sink-inl.h b/include/spdlog/sinks/rotating_file_sink-inl.h new file mode 100644 index 00000000..f871c820 --- /dev/null +++ b/include/spdlog/sinks/rotating_file_sink-inl.h @@ -0,0 +1,133 @@ +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/sinks/rotating_file_sink.h" +#endif + +#include "spdlog/common.h" + +#include "spdlog/details/file_helper.h" +#include "spdlog/details/null_mutex.h" +#include "spdlog/fmt/fmt.h" + +#include +#include +#include +#include +#include +#include + +namespace spdlog { +namespace sinks { + +template +SPDLOG_INLINE rotating_file_sink::rotating_file_sink( + filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open) + : base_filename_(std::move(base_filename)) + , max_size_(max_size) + , max_files_(max_files) +{ + file_helper_.open(calc_filename(base_filename_, 0)); + current_size_ = file_helper_.size(); // expensive. called only once + if (rotate_on_open && current_size_ > 0) + { + rotate_(); + } +} + +// calc filename according to index and file extension if exists. +// e.g. calc_filename("logs/mylog.txt, 3) => "logs/mylog.3.txt". +template +SPDLOG_INLINE filename_t rotating_file_sink::calc_filename(const filename_t &filename, std::size_t index) +{ + typename std::conditional::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w; + if (index != 0u) + { + filename_t basename, ext; + std::tie(basename, ext) = details::file_helper::split_by_extension(filename); + fmt::format_to(w, SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext); + } + else + { + fmt::format_to(w, SPDLOG_FILENAME_T("{}"), filename); + } + return fmt::to_string(w); +} + +template +SPDLOG_INLINE const filename_t &rotating_file_sink::filename() const +{ + return file_helper_.filename(); +} + +template +SPDLOG_INLINE void rotating_file_sink::sink_it_(const details::log_msg &msg) +{ + fmt::memory_buffer formatted; + sink::formatter_->format(msg, formatted); + current_size_ += formatted.size(); + if (current_size_ > max_size_) + { + rotate_(); + current_size_ = formatted.size(); + } + file_helper_.write(formatted); +} + +template +SPDLOG_INLINE void rotating_file_sink::flush_() +{ + file_helper_.flush(); +} + +// Rotate files: +// log.txt -> log.1.txt +// log.1.txt -> log.2.txt +// log.2.txt -> log.3.txt +// log.3.txt -> delete +template +SPDLOG_INLINE void rotating_file_sink::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); + if (!details::file_helper::file_exists(src)) + { + continue; + } + filename_t target = calc_filename(base_filename_, i); + + if (!rename_file(src, target)) + { + // if failed try again after a small delay. + // this is a workaround to a windows issue, where very high rotation + // rates can cause the rename to fail with permission denied (because of antivirus?). + details::os::sleep_for_millis(100); + if (!rename_file(src, target)) + { + file_helper_.reopen(true); // truncate the log file anyway to prevent it to grow beyond its limit! + current_size_ = 0; + throw spdlog_ex("rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno); + } + } + } + file_helper_.reopen(true); +} + +// delete the target if exists, and rename the src file to target +// return true on success, false otherwise. +template +SPDLOG_INLINE bool rotating_file_sink::rename_file(const filename_t &src_filename, const filename_t &target_filename) +{ + // try to delete the target file in case it already exists. + (void)details::os::remove(target_filename); + return details::os::rename(src_filename, target_filename) == 0; +} + +} // namespace sinks +} // namespace spdlog diff --git a/include/spdlog/sinks/rotating_file_sink.h b/include/spdlog/sinks/rotating_file_sink.h index ae0f70f6..a454086b 100644 --- a/include/spdlog/sinks/rotating_file_sink.h +++ b/include/spdlog/sinks/rotating_file_sink.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once @@ -9,17 +7,12 @@ #include "spdlog/spdlog.h" #endif -#include "spdlog/details/file_helper.h" -#include "spdlog/details/null_mutex.h" -#include "spdlog/fmt/fmt.h" #include "spdlog/sinks/base_sink.h" +#include "spdlog/details/file_helper.h" -#include #include -#include #include #include -#include namespace spdlog { namespace sinks { @@ -31,60 +24,13 @@ template class rotating_file_sink final : public base_sink { public: - rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open=false) - : base_filename_(std::move(base_filename)) - , max_size_(max_size) - , max_files_(max_files) - { - file_helper_.open(calc_filename(base_filename_, 0)); - current_size_ = file_helper_.size(); // expensive. called only once - if (rotate_on_open && current_size_ > 0) - { - rotate_(); - } - } - - // 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) - { - typename std::conditional::value, fmt::memory_buffer, fmt::wmemory_buffer>::type w; - if (index != 0u) - { - filename_t basename, ext; - std::tie(basename, ext) = details::file_helper::split_by_extension(filename); - fmt::format_to(w, SPDLOG_FILENAME_T("{}.{}{}"), basename, index, ext); - } - else - { - fmt::format_to(w, SPDLOG_FILENAME_T("{}"), filename); - } - return fmt::to_string(w); - } - - const filename_t &filename() const - { - return file_helper_.filename(); - } + rotating_file_sink(filename_t base_filename, std::size_t max_size, std::size_t max_files, bool rotate_on_open = false); + static filename_t calc_filename(const filename_t &filename, std::size_t index); + const filename_t &filename() const; protected: - void sink_it_(const details::log_msg &msg) override - { - fmt::memory_buffer formatted; - sink::formatter_->format(msg, formatted); - current_size_ += formatted.size(); - if (current_size_ > max_size_) - { - rotate_(); - current_size_ = formatted.size(); - } - file_helper_.write(formatted); - } - - void flush_() override - { - file_helper_.flush(); - } + void sink_it_(const details::log_msg &msg) override; + void flush_() override; private: // Rotate files: @@ -92,45 +38,11 @@ private: // 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); - if (!details::file_helper::file_exists(src)) - { - continue; - } - filename_t target = calc_filename(base_filename_, i); - - if (!rename_file(src, target)) - { - // if failed try again after a small delay. - // this is a workaround to a windows issue, where very high rotation - // rates can cause the rename to fail with permission denied (because of antivirus?). - details::os::sleep_for_millis(100); - if (!rename_file(src, target)) - { - file_helper_.reopen(true); // truncate the log file anyway to prevent it to grow beyond its limit! - current_size_ = 0; - throw spdlog_ex( - "rotating_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno); - } - } - } - file_helper_.reopen(true); - } + void rotate_(); // delete the target if exists, and rename the src file to target // return true on success, false otherwise. - bool rename_file(const filename_t &src_filename, const filename_t &target_filename) - { - // try to delete the target file in case it already exists. - (void)details::os::remove(target_filename); - return details::os::rename(src_filename, target_filename) == 0; - } + bool rename_file(const filename_t &src_filename, const filename_t &target_filename); filename_t base_filename_; std::size_t max_size_; @@ -150,7 +62,7 @@ using rotating_file_sink_st = rotating_file_sink; template inline std::shared_ptr rotating_logger_mt( - const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open=false) + const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files, bool rotate_on_open = false) { return Factory::template create(logger_name, filename, max_file_size, max_files, rotate_on_open); } @@ -162,3 +74,7 @@ inline std::shared_ptr rotating_logger_st( return Factory::template create(logger_name, filename, max_file_size, max_files, rotate_on_open); } } // namespace spdlog + +#ifdef SPDLOG_HEADER_ONLY +#include "rotating_file_sink-inl.h" +#endif \ No newline at end of file diff --git a/include/spdlog/sinks/sink-inl.h b/include/spdlog/sinks/sink-inl.h new file mode 100644 index 00000000..52227696 --- /dev/null +++ b/include/spdlog/sinks/sink-inl.h @@ -0,0 +1,34 @@ +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/sinks/sink.h" +#endif + +#include "spdlog/common.h" +#include "spdlog/details/pattern_formatter.h" + +SPDLOG_INLINE spdlog::sinks::sink::sink() + : formatter_{details::make_unique()} +{} + +SPDLOG_INLINE spdlog::sinks::sink::sink(std::unique_ptr formatter) + : formatter_{std::move(formatter)} +{} + +SPDLOG_INLINE bool spdlog::sinks::sink::should_log(spdlog::level::level_enum msg_level) const +{ + return msg_level >= level_.load(std::memory_order_relaxed); +} + +SPDLOG_INLINE void spdlog::sinks::sink::set_level(level::level_enum log_level) +{ + level_.store(log_level); +} + +SPDLOG_INLINE spdlog::level::level_enum spdlog::sinks::sink::level() const +{ + return static_cast(level_.load(std::memory_order_relaxed)); +} diff --git a/include/spdlog/sinks/sink.h b/include/spdlog/sinks/sink.h index d8325233..263cbe5b 100644 --- a/include/spdlog/sinks/sink.h +++ b/include/spdlog/sinks/sink.h @@ -1,54 +1,43 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once #include "spdlog/details/log_msg.h" -#include "spdlog/details/pattern_formatter.h" #include "spdlog/formatter.h" namespace spdlog { + namespace sinks { class sink { public: - sink() = default; - - explicit sink(std::unique_ptr formatter) - : formatter_{std::move(formatter)} - { - } + sink(); + explicit sink(std::unique_ptr formatter); virtual ~sink() = default; virtual void log(const details::log_msg &msg) = 0; virtual void flush() = 0; virtual void set_pattern(const std::string &pattern) = 0; virtual void set_formatter(std::unique_ptr sink_formatter) = 0; - bool should_log(level::level_enum msg_level) const - { - return msg_level >= level_.load(std::memory_order_relaxed); - } + bool should_log(level::level_enum msg_level) const; - void set_level(level::level_enum log_level) - { - level_.store(log_level); - } + void set_level(level::level_enum log_level); - level::level_enum level() const - { - return static_cast(level_.load(std::memory_order_relaxed)); - } + level::level_enum level() const; protected: // sink log level - default is all level_t level_{level::trace}; - // sink formatter - default is full format - std::unique_ptr formatter_{details::make_unique()}; + // sink formatter + std::unique_ptr formatter_; }; } // namespace sinks } // namespace spdlog + +#ifdef SPDLOG_HEADER_ONLY +#include "sink-inl.h" +#endif diff --git a/include/spdlog/sinks/stdout_color_sinks-inl.h b/include/spdlog/sinks/stdout_color_sinks-inl.h new file mode 100644 index 00000000..56314c15 --- /dev/null +++ b/include/spdlog/sinks/stdout_color_sinks-inl.h @@ -0,0 +1,38 @@ +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/sinks/stdout_color_sinks.h" +#endif + +#include "spdlog/logger.h" +#include "spdlog/common.h" + +namespace spdlog { + +template +SPDLOG_INLINE std::shared_ptr stdout_color_mt(const std::string &logger_name, color_mode mode) +{ + return Factory::template create(logger_name, mode); +} + +template +SPDLOG_INLINE std::shared_ptr stdout_color_st(const std::string &logger_name, color_mode mode) +{ + return Factory::template create(logger_name, mode); +} + +template +SPDLOG_INLINE std::shared_ptr stderr_color_mt(const std::string &logger_name, color_mode mode) +{ + return Factory::template create(logger_name, mode); +} + +template +SPDLOG_INLINE std::shared_ptr stderr_color_st(const std::string &logger_name, color_mode mode) +{ + return Factory::template create(logger_name, mode); +} +} // namespace spdlog \ No newline at end of file diff --git a/include/spdlog/sinks/stdout_color_sinks.h b/include/spdlog/sinks/stdout_color_sinks.h index 89271666..7812f5e2 100644 --- a/include/spdlog/sinks/stdout_color_sinks.h +++ b/include/spdlog/sinks/stdout_color_sinks.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2018 spdlog +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once @@ -31,26 +29,19 @@ using stderr_color_sink_st = ansicolor_stderr_sink_st; } // namespace sinks template -inline std::shared_ptr stdout_color_mt(const std::string &logger_name, color_mode mode = color_mode::automatic) -{ - return Factory::template create(logger_name, mode); -} +std::shared_ptr stdout_color_mt(const std::string &logger_name, color_mode mode = color_mode::automatic); template -inline std::shared_ptr stdout_color_st(const std::string &logger_name, color_mode mode = color_mode::automatic) -{ - return Factory::template create(logger_name, mode); -} +std::shared_ptr stdout_color_st(const std::string &logger_name, color_mode mode = color_mode::automatic); template -inline std::shared_ptr stderr_color_mt(const std::string &logger_name, color_mode mode = color_mode::automatic) -{ - return Factory::template create(logger_name, mode); -} +std::shared_ptr stderr_color_mt(const std::string &logger_name, color_mode mode = color_mode::automatic); template -inline std::shared_ptr stderr_color_st(const std::string &logger_name, color_mode mode = color_mode::automatic) -{ - return Factory::template create(logger_name, mode); -} +std::shared_ptr stderr_color_st(const std::string &logger_name, color_mode mode = color_mode::automatic); + } // namespace spdlog + +#ifdef SPDLOG_HEADER_ONLY +#include "stdout_color_sinks-inl.h" +#endif diff --git a/include/spdlog/sinks/stdout_sinks.h b/include/spdlog/sinks/stdout_sinks.h index bf8e9790..91bed987 100644 --- a/include/spdlog/sinks/stdout_sinks.h +++ b/include/spdlog/sinks/stdout_sinks.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once @@ -11,6 +9,7 @@ #include "spdlog/details/console_globals.h" #include "spdlog/details/null_mutex.h" +#include "spdlog/details/pattern_formatter.h" #include #include @@ -28,8 +27,7 @@ public: stdout_sink() : mutex_(ConsoleMutex::mutex()) , file_(TargetStream::stream()) - { - } + {} ~stdout_sink() override = default; stdout_sink(const stdout_sink &other) = delete; diff --git a/include/spdlog/sinks/syslog_sink.h b/include/spdlog/sinks/syslog_sink.h index c3bcd844..53226d6a 100644 --- a/include/spdlog/sinks/syslog_sink.h +++ b/include/spdlog/sinks/syslog_sink.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once diff --git a/include/spdlog/sinks/systemd_sink.h b/include/spdlog/sinks/systemd_sink.h index fa9efd83..dfdc8b72 100644 --- a/include/spdlog/sinks/systemd_sink.h +++ b/include/spdlog/sinks/systemd_sink.h @@ -1,7 +1,5 @@ -// // Copyright(c) 2019 ZVYAGIN.Alexander@gmail.com // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once @@ -18,22 +16,24 @@ namespace spdlog { namespace sinks { -inline int syslog_level(level::level_enum l) { - switch(l) { - case level::off: - case level::trace: - case level::debug: - return LOG_DEBUG; - case level::info: - return LOG_INFO; - case level::warn: - return LOG_WARNING; - case level::err: - return LOG_ERR; - case level::critical: - return LOG_CRIT; - default: - throw std::invalid_argument("systemd_sink.h syslog_level()"); +inline int syslog_level(level::level_enum l) +{ + switch (l) + { + case level::off: + case level::trace: + case level::debug: + return LOG_DEBUG; + case level::info: + return LOG_INFO; + case level::warn: + return LOG_WARNING; + case level::err: + return LOG_ERR; + case level::critical: + return LOG_CRIT; + default: + throw std::invalid_argument("systemd_sink.h syslog_level()"); } } @@ -57,13 +57,7 @@ public: protected: void sink_it_(const details::log_msg &msg) override { - if( sd_journal_print( - syslog_level(msg.level), - "%.*s", - static_cast(msg.payload.size()), - msg.payload.data() - ) - ) + if (sd_journal_print(syslog_level(msg.level), "%.*s", static_cast(msg.payload.size()), msg.payload.data())) throw spdlog_ex("Failed writing to systemd"); } @@ -76,15 +70,13 @@ using systemd_sink_st = systemd_sink; // Create and register a syslog logger template -inline std::shared_ptr systemd_logger_mt( - const std::string &logger_name) +inline std::shared_ptr systemd_logger_mt(const std::string &logger_name) { return Factory::template create(logger_name); } template -inline std::shared_ptr systemd_logger_st( - const std::string &logger_name) +inline std::shared_ptr systemd_logger_st(const std::string &logger_name) { return Factory::template create(logger_name); } diff --git a/include/spdlog/sinks/wincolor_sink-inl.h b/include/spdlog/sinks/wincolor_sink-inl.h new file mode 100644 index 00000000..e107d1a2 --- /dev/null +++ b/include/spdlog/sinks/wincolor_sink-inl.h @@ -0,0 +1,129 @@ +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#ifndef SPDLOG_HEADER_ONLY +#include "spdlog/sinks/wincolor_sink.h" +#endif + +#include "spdlog/common.h" + +namespace spdlog { +namespace sinks { + +template +SPDLOG_INLINE wincolor_sink::wincolor_sink(color_mode mode) + : out_handle_(TargetStream::handle()) + , mutex_(ConsoleMutex::mutex()) +{ + set_color_mode(mode); + colors_[level::trace] = WHITE; + colors_[level::debug] = CYAN; + colors_[level::info] = GREEN; + 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; +} + +template +SPDLOG_INLINE wincolor_sink::~wincolor_sink() +{ + this->flush(); +} + +// change the color for the given level +template +void SPDLOG_INLINE wincolor_sink::set_color(level::level_enum level, WORD color) +{ + std::lock_guard lock(mutex_); + colors_[level] = color; +} + +template +void SPDLOG_INLINE wincolor_sink::log(const details::log_msg &msg) +{ + std::lock_guard lock(mutex_); + fmt::memory_buffer formatted; + formatter_->format(msg, formatted); + if (should_do_colors_ && msg.color_range_end > msg.color_range_start) + { + // before color range + print_range_(formatted, 0, msg.color_range_start); + + // in color range + auto orig_attribs = set_console_attribs(colors_[msg.level]); + print_range_(formatted, msg.color_range_start, msg.color_range_end); + ::SetConsoleTextAttribute(out_handle_, + orig_attribs); // reset to orig colors + // after color range + print_range_(formatted, msg.color_range_end, formatted.size()); + } + else // print without colors if color range is invalid (or color is disabled) + { + print_range_(formatted, 0, formatted.size()); + } +} + +template +void SPDLOG_INLINE wincolor_sink::flush() +{ + // windows console always flushed? +} + +template +void SPDLOG_INLINE wincolor_sink::set_pattern(const std::string &pattern) +{ + std::lock_guard lock(mutex_); + formatter_ = std::unique_ptr(new pattern_formatter(pattern)); +} + +template +void SPDLOG_INLINE wincolor_sink::set_formatter(std::unique_ptr sink_formatter) +{ + std::lock_guard lock(mutex_); + formatter_ = std::move(sink_formatter); +} + +template +void SPDLOG_INLINE wincolor_sink::set_color_mode(color_mode mode) +{ + switch (mode) + { + case color_mode::always: + case color_mode::automatic: + should_do_colors_ = true; + break; + case color_mode::never: + should_do_colors_ = false; + break; + default: + should_do_colors_ = true; + } +} + +// set color and return the orig console attributes (for resetting later) +template +WORD SPDLOG_INLINE wincolor_sink::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 &= static_cast(~(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 +} + +// print a range of formatted message to console +template +void SPDLOG_INLINE wincolor_sink::print_range_(const fmt::memory_buffer &formatted, size_t start, size_t end) +{ + auto size = static_cast(end - start); + ::WriteConsoleA(out_handle_, formatted.data() + start, size, nullptr, nullptr); +} + +} // namespace sinks +} // namespace spdlog \ No newline at end of file diff --git a/include/spdlog/sinks/wincolor_sink.h b/include/spdlog/sinks/wincolor_sink.h index 7c51014b..de1a02ed 100644 --- a/include/spdlog/sinks/wincolor_sink.h +++ b/include/spdlog/sinks/wincolor_sink.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2016 spdlog +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once @@ -26,7 +24,7 @@ namespace sinks { * Windows color console sink. Uses WriteConsoleA to write to the console with * colors */ -template +template class wincolor_sink : public sink { public: @@ -37,123 +35,31 @@ public: const WORD WHITE = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN; - wincolor_sink(color_mode mode = color_mode::automatic) - : out_handle_(OutHandle::handle()) - , mutex_(ConsoleMutex::mutex()) - { - set_color_mode_(mode); - colors_[level::trace] = WHITE; - colors_[level::debug] = CYAN; - colors_[level::info] = GREEN; - 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; - } - - ~wincolor_sink() override - { - this->flush(); - } + wincolor_sink(color_mode mode = color_mode::automatic); + ~wincolor_sink() override; wincolor_sink(const wincolor_sink &other) = delete; wincolor_sink &operator=(const wincolor_sink &other) = delete; // change the color for the given level - void set_color(level::level_enum level, WORD color) - { - std::lock_guard lock(mutex_); - colors_[level] = color; - } - - void log(const details::log_msg &msg) final override - { - std::lock_guard lock(mutex_); - fmt::memory_buffer formatted; - formatter_->format(msg, formatted); - if (should_do_colors_ && msg.color_range_end > msg.color_range_start) - { - // before color range - print_range_(formatted, 0, msg.color_range_start); - - // in color range - auto orig_attribs = set_console_attribs(colors_[msg.level]); - print_range_(formatted, msg.color_range_start, msg.color_range_end); - ::SetConsoleTextAttribute(out_handle_, - orig_attribs); // reset to orig colors - // after color range - print_range_(formatted, msg.color_range_end, formatted.size()); - } - else // print without colors if color range is invalid (or color is disabled) - { - print_range_(formatted, 0, formatted.size()); - } - } - - void flush() final override - { - // windows console always flushed? - } - - void set_pattern(const std::string &pattern) override final - { - std::lock_guard lock(mutex_); - formatter_ = std::unique_ptr(new pattern_formatter(pattern)); - } - - void set_formatter(std::unique_ptr sink_formatter) override final - { - std::lock_guard lock(mutex_); - formatter_ = std::move(sink_formatter); - } - - void set_color_mode(color_mode mode) - { - std::lock_guard lock(mutex_); - set_color_mode_(mode); - } + void set_color(level::level_enum level, WORD color); + void log(const details::log_msg &msg) final override; + void flush() final override; + void set_pattern(const std::string &pattern) override final; + void set_formatter(std::unique_ptr sink_formatter) override final; + void set_color_mode(color_mode mode); private: using mutex_t = typename ConsoleMutex::mutex_t; - - void set_color_mode_(color_mode mode) - { - switch (mode) - { - case color_mode::always: - case color_mode::automatic: - should_do_colors_ = true; - break; - case color_mode::never: - should_do_colors_ = false; - break; - } - } - - // 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 &= static_cast(~(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 - } - - // print a range of formatted message to console - void print_range_(const fmt::memory_buffer &formatted, size_t start, size_t end) - { - auto size = static_cast(end - start); - ::WriteConsoleA(out_handle_, formatted.data() + start, size, nullptr, nullptr); - } - HANDLE out_handle_; mutex_t &mutex_; bool should_do_colors_; std::unordered_map colors_; + + // set color and return the orig console attributes (for resetting later) + WORD set_console_attribs(WORD attribs); + // print a range of formatted message to console + void print_range_(const fmt::memory_buffer &formatted, size_t start, size_t end); }; using wincolor_stdout_sink_mt = wincolor_sink; @@ -164,3 +70,7 @@ using wincolor_stderr_sink_st = wincolor_sink logger) +{ + details::registry::instance().initialize_logger(std::move(logger)); +} + +SPDLOG_INLINE std::shared_ptr get(const std::string &name) +{ + return details::registry::instance().get(name); +} + +SPDLOG_INLINE void set_formatter(std::unique_ptr formatter) +{ + details::registry::instance().set_formatter(std::move(formatter)); +} + +SPDLOG_INLINE void set_pattern(std::string pattern, pattern_time_type time_type) +{ + set_formatter(std::unique_ptr(new pattern_formatter(std::move(pattern), time_type))); +} + +SPDLOG_INLINE void set_level(level::level_enum log_level) +{ + details::registry::instance().set_level(log_level); +} + +SPDLOG_INLINE void flush_on(level::level_enum log_level) +{ + details::registry::instance().flush_on(log_level); +} + +SPDLOG_INLINE void flush_every(std::chrono::seconds interval) +{ + details::registry::instance().flush_every(interval); +} + +SPDLOG_INLINE void set_error_handler(void (*handler)(const std::string &msg)) +{ + details::registry::instance().set_error_handler(handler); +} + +SPDLOG_INLINE void register_logger(std::shared_ptr logger) +{ + details::registry::instance().register_logger(std::move(logger)); +} + +SPDLOG_INLINE void apply_all(const std::function)> &fun) +{ + details::registry::instance().apply_all(fun); +} + +SPDLOG_INLINE void drop(const std::string &name) +{ + details::registry::instance().drop(name); +} + +SPDLOG_INLINE void drop_all() +{ + details::registry::instance().drop_all(); +} + +SPDLOG_INLINE void shutdown() +{ + details::registry::instance().shutdown(); +} + +SPDLOG_INLINE void set_automatic_registration(bool automatic_registation) +{ + details::registry::instance().set_automatic_registration(automatic_registation); +} + +SPDLOG_INLINE std::shared_ptr default_logger() +{ + return details::registry::instance().default_logger(); +} + +SPDLOG_INLINE spdlog::logger *default_logger_raw() +{ + return details::registry::instance().get_default_raw(); +} + +SPDLOG_INLINE void set_default_logger(std::shared_ptr default_logger) +{ + details::registry::instance().set_default_logger(std::move(default_logger)); +} + +} // namespace spdlog \ No newline at end of file diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h index 87a7c184..56a69e4c 100644 --- a/include/spdlog/spdlog.h +++ b/include/spdlog/spdlog.h @@ -1,12 +1,12 @@ -// -// Copyright(c) 2015-2018 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// + // spdlog main header file. // see example.cpp for usage example #ifndef SPDLOG_H #define SPDLOG_H + #pragma once #include "spdlog/common.h" @@ -57,94 +57,52 @@ inline std::shared_ptr create(std::string logger_name, SinkArgs // auto console_sink = std::make_shared(); // auto console_logger = std::make_shared("console_logger", console_sink); // spdlog::initialize_logger(console_logger); -inline void initialize_logger(std::shared_ptr logger) -{ - details::registry::instance().initialize_logger(std::move(logger)); -} +void initialize_logger(std::shared_ptr logger); // Return an existing logger or nullptr if a logger with such name doesn't // exist. // example: spdlog::get("my_logger")->info("hello {}", "world"); -inline std::shared_ptr get(const std::string &name) -{ - return details::registry::instance().get(name); -} +std::shared_ptr get(const std::string &name); // Set global formatter. Each sink in each logger will get a clone of this object -inline void set_formatter(std::unique_ptr formatter) -{ - details::registry::instance().set_formatter(std::move(formatter)); -} +void set_formatter(std::unique_ptr formatter); // Set global format string. // example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v"); -inline void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local) -{ - set_formatter(std::unique_ptr(new pattern_formatter(std::move(pattern), time_type))); -} +void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local); // Set global logging level -inline void set_level(level::level_enum log_level) -{ - details::registry::instance().set_level(log_level); -} +void set_level(level::level_enum log_level); // Set global flush level -inline void flush_on(level::level_enum log_level) -{ - details::registry::instance().flush_on(log_level); -} +void flush_on(level::level_enum log_level); // Start/Restart a periodic flusher thread // Warning: Use only if all your loggers are thread safe! -inline void flush_every(std::chrono::seconds interval) -{ - details::registry::instance().flush_every(interval); -} +void flush_every(std::chrono::seconds interval); // Set global error handler -inline void set_error_handler(log_err_handler handler) -{ - details::registry::instance().set_error_handler(std::move(handler)); -} +void set_error_handler(void (*handler)(const std::string &msg)); // Register the given logger with the given name -inline void register_logger(std::shared_ptr logger) -{ - details::registry::instance().register_logger(std::move(logger)); -} +void register_logger(std::shared_ptr logger); // Apply a user defined function on all registered loggers // Example: // spdlog::apply_all([&](std::shared_ptr l) {l->flush();}); -inline void apply_all(const std::function)> &fun) -{ - details::registry::instance().apply_all(fun); -} +void apply_all(const std::function)> &fun); // Drop the reference to the given logger -inline void drop(const std::string &name) -{ - details::registry::instance().drop(name); -} +void drop(const std::string &name); // Drop all references from the registry -inline void drop_all() -{ - details::registry::instance().drop_all(); -} +void drop_all(); // stop any running threads started by spdlog and clean registry loggers -inline void shutdown() -{ - details::registry::instance().shutdown(); -} +void shutdown(); // Automatic registration of loggers when using spdlog::create() or spdlog::create_async -inline void set_automatic_registration(bool automatic_registation) -{ - details::registry::instance().set_automatic_registration(automatic_registation); -} +void set_automatic_registration(bool automatic_registation); // API for using default logger (stdout_color_mt), // e.g: spdlog::info("Message {}", 1); @@ -161,20 +119,11 @@ inline void set_automatic_registration(bool automatic_registation) // set_default_logger() *should not* be used concurrently with the default API. // e.g do not call set_default_logger() from one thread while calling spdlog::info() from another. -inline std::shared_ptr default_logger() -{ - return details::registry::instance().default_logger(); -} +std::shared_ptr default_logger(); -inline spdlog::logger *default_logger_raw() -{ - return details::registry::instance().get_default_raw(); -} +spdlog::logger *default_logger_raw(); -inline void set_default_logger(std::shared_ptr default_logger) -{ - details::registry::instance().set_default_logger(std::move(default_logger)); -} +void set_default_logger(std::shared_ptr default_logger); template inline void log(source_loc source, level::level_enum lvl, const char *fmt, const Args &... args) @@ -327,7 +276,8 @@ inline void critical(const wchar_t *fmt, const Args &... args) // #define SPDLOG_LOGGER_CALL(logger, level, ...) \ - do { \ + do \ + { \ if (logger->should_log(level)) \ logger->log(spdlog::source_loc{SPDLOG_FILE_BASENAME(__FILE__), __LINE__, SPDLOG_FUNCTION}, level, __VA_ARGS__); \ } while (0) @@ -380,4 +330,8 @@ inline void critical(const wchar_t *fmt, const Args &... args) #define SPDLOG_CRITICAL(...) (void)0 #endif +#ifdef SPDLOG_HEADER_ONLY +#include "spdlog-inl.h" +#endif + #endif // SPDLOG_H diff --git a/include/spdlog/tweakme.h b/include/spdlog/tweakme.h index dfcb09d2..37c2e6cf 100644 --- a/include/spdlog/tweakme.h +++ b/include/spdlog/tweakme.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once @@ -107,13 +105,6 @@ // #define SPDLOG_PREVENT_CHILD_FD /////////////////////////////////////////////////////////////////////////////// -/////////////////////////////////////////////////////////////////////////////// -// 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") // diff --git a/include/spdlog/version.h b/include/spdlog/version.h index 87a68bd1..0bbf338d 100644 --- a/include/spdlog/version.h +++ b/include/spdlog/version.h @@ -1,7 +1,5 @@ -// -// Copyright(c) 2015 Gabi Melman. +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. // Distributed under the MIT License (http://opensource.org/licenses/MIT) -// #pragma once diff --git a/src/spdlog.cpp b/src/spdlog.cpp new file mode 100644 index 00000000..2633cfb9 --- /dev/null +++ b/src/spdlog.cpp @@ -0,0 +1,103 @@ +// Copyright(c) 2015-present Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#ifndef SPDLOG_COMPILED_LIB +#error Please define SPDLOG_COMPILED_LIB to compile this file. +#endif + +#include +#include + +#include "spdlog/details/null_mutex.h" +#include "spdlog/async.h" + +#include "spdlog/spdlog-inl.h" +#include "spdlog/common-inl.h" + +#include "spdlog/logger-inl.h" +template spdlog::logger::logger(std::string name, sinks_init_list::iterator begin, sinks_init_list::iterator end); + +#include "spdlog/async_logger-inl.h" +#include "spdlog/details/log_msg-inl.h" +#include "spdlog/sinks/sink-inl.h" + +#include "spdlog/sinks/base_sink-inl.h" +template class spdlog::sinks::base_sink; +template class spdlog::sinks::base_sink; + +#include "spdlog/sinks/basic_file_sink-inl.h" +template class spdlog::sinks::basic_file_sink; +template class spdlog::sinks::basic_file_sink; + +#include "spdlog/sinks/rotating_file_sink-inl.h" +template class spdlog::sinks::rotating_file_sink; +template class spdlog::sinks::rotating_file_sink; + +#include "spdlog/details/registry-inl.h" + +#include "spdlog/details/os-inl.h" +#include "spdlog/details/periodic_worker-inl.h" +#include "spdlog/details/file_helper-inl.h" +#include "spdlog/details/pattern_formatter-inl.h" + +#include "spdlog/details/thread_pool-inl.h" +template class spdlog::details::mpmc_blocking_queue; + +#ifdef _WIN32 +#include "spdlog/sinks/wincolor_sink-inl.h" +template class spdlog::sinks::wincolor_sink; +template class spdlog::sinks::wincolor_sink; +template class spdlog::sinks::wincolor_sink; +template class spdlog::sinks::wincolor_sink; +#else +#include "spdlog/sinks/ansicolor_sink-inl.h" +template class spdlog::sinks::ansicolor_sink; +template class spdlog::sinks::ansicolor_sink; +template class spdlog::sinks::ansicolor_sink; +template class spdlog::sinks::ansicolor_sink; +#endif + +#include "spdlog/sinks/stdout_color_sinks-inl.h" +template std::shared_ptr spdlog::stdout_color_mt(const std::string &logger_name, color_mode mode); +template std::shared_ptr spdlog::stdout_color_st(const std::string &logger_name, color_mode mode); +template std::shared_ptr spdlog::stderr_color_mt(const std::string &logger_name, color_mode mode); +template std::shared_ptr spdlog::stderr_color_st(const std::string &logger_name, color_mode mode); + +template std::shared_ptr spdlog::stdout_color_mt(const std::string &logger_name, color_mode mode); +template std::shared_ptr spdlog::stdout_color_st(const std::string &logger_name, color_mode mode); +template std::shared_ptr spdlog::stderr_color_mt(const std::string &logger_name, color_mode mode); +template std::shared_ptr spdlog::stderr_color_st(const std::string &logger_name, color_mode mode); + +// Slightly modified version of fmt lib's format.cc source file. +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. + +#if !defined(SPDLOG_FMT_EXTERNAL) +#include "spdlog/fmt/bundled/format-inl.h" + +FMT_BEGIN_NAMESPACE +template struct internal::basic_data; +template FMT_API internal::locale_ref::locale_ref(const std::locale &loc); +template FMT_API std::locale internal::locale_ref::get() const; + +// Explicit instantiations for char. +template FMT_API char internal::thousands_sep_impl(locale_ref); +template FMT_API void internal::basic_buffer::append(const char *, const char *); +template FMT_API void internal::arg_map::init(const basic_format_args &args); +template FMT_API int internal::char_traits::format_float(char *, std::size_t, const char *, int, double); +template FMT_API int internal::char_traits::format_float(char *, std::size_t, const char *, int, long double); +template FMT_API std::string internal::vformat(string_view, basic_format_args); +template FMT_API format_context::iterator internal::vformat_to(internal::buffer &, string_view, basic_format_args); +template FMT_API void internal::sprintf_format(double, internal::buffer &, core_format_specs); +template FMT_API void internal::sprintf_format(long double, internal::buffer &, core_format_specs); + +// Explicit instantiations for wchar_t. +template FMT_API wchar_t internal::thousands_sep_impl(locale_ref); +template FMT_API void internal::basic_buffer::append(const wchar_t *, const wchar_t *); +template FMT_API void internal::arg_map::init(const basic_format_args &); +template FMT_API int internal::char_traits::format_float(wchar_t *, std::size_t, const wchar_t *, int, double); +template FMT_API int internal::char_traits::format_float(wchar_t *, std::size_t, const wchar_t *, int, long double); +template FMT_API std::wstring internal::vformat(wstring_view, basic_format_args); +FMT_END_NAMESPACE + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 48f80e0e..3275694a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,7 +1,3 @@ -project(spdlog-utests CXX) - -find_package(Threads REQUIRED) - set(SPDLOG_UTESTS_SOURCES test_errors.cpp test_file_helper.cpp @@ -19,11 +15,10 @@ set(SPDLOG_UTESTS_SOURCES test_sink.h test_fmt_helper.cpp) -add_executable(${PROJECT_NAME} ${SPDLOG_UTESTS_SOURCES}) -target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads) -target_link_libraries(${PROJECT_NAME} PRIVATE spdlog::spdlog) +add_executable(spdlog-utests ${SPDLOG_UTESTS_SOURCES}) +target_link_libraries(spdlog-utests spdlog) file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") enable_testing() -add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME}) +add_test(NAME spdlog-utests COMMAND spdlog-utests) diff --git a/tests/Makefile b/tests/Makefile deleted file mode 100644 index c9ce3c01..00000000 --- a/tests/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -CXX ?= g++ -CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -Wconversion -O3 -I../include -fmax-errors=1 -LDPFALGS = -pthread -lsystemd - -CPP_FILES := $(wildcard *.cpp) -OBJ_FILES := $(addprefix ./,$(notdir $(CPP_FILES:.cpp=.o))) - - -tests: $(OBJ_FILES) - $(CXX) $(CXXFLAGS) $(LDPFALGS) -o $@ $^ - mkdir -p logs - -%.o: %.cpp - $(CXX) $(CXXFLAGS) -c -o $@ $< - -clean: - rm -f tests *.o logs/*.txt - -rebuild: clean tests - - - diff --git a/tests/includes.h b/tests/includes.h index 54601602..4e0eaf8a 100644 --- a/tests/includes.h +++ b/tests/includes.h @@ -11,7 +11,6 @@ #include #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG -#define SPDLOG_ENABLE_MESSAGE_COUNTER #include "spdlog/spdlog.h" #include "spdlog/async.h" @@ -21,3 +20,4 @@ #include "spdlog/sinks/ostream_sink.h" #include "spdlog/sinks/rotating_file_sink.h" #include "spdlog/sinks/stdout_color_sinks.h" +#include "spdlog/details/pattern_formatter.h" \ No newline at end of file diff --git a/tests/test_errors.cpp b/tests/test_errors.cpp index 24075634..65185d3c 100644 --- a/tests/test_errors.cpp +++ b/tests/test_errors.cpp @@ -39,8 +39,7 @@ TEST_CASE("default_error_handler", "[errors]]") } struct custom_ex -{ -}; +{}; TEST_CASE("custom_error_handler", "[errors]]") { prepare_logdir(); diff --git a/tests/test_file_logging.cpp b/tests/test_file_logging.cpp index dd0c8999..4de3a742 100644 --- a/tests/test_file_logging.cpp +++ b/tests/test_file_logging.cpp @@ -69,7 +69,7 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]]") logger->info("Test message {}", i); } // drop causes the logger destructor to be called, which is required so the - // next logger can rename the first output file. + // next logger can rename the first output file. spdlog::drop(logger->name()); } diff --git a/tests/test_fmt_helper.cpp b/tests/test_fmt_helper.cpp index 97a61960..5af6f8a7 100644 --- a/tests/test_fmt_helper.cpp +++ b/tests/test_fmt_helper.cpp @@ -1,5 +1,6 @@ #include "includes.h" +#include "spdlog/details/fmt_helper.h" void test_pad2(int n, const char *expected) { diff --git a/tests/test_misc.cpp b/tests/test_misc.cpp index 1b880835..7400dd8f 100644 --- a/tests/test_misc.cpp +++ b/tests/test_misc.cpp @@ -107,7 +107,6 @@ TEST_CASE("clone-logger", "[clone]") cloned->info("Some message 2"); auto test_sink = std::static_pointer_cast(cloned->sinks()[0]); - REQUIRE(test_sink->msg_counter() == 2); spdlog::drop_all(); } @@ -130,7 +129,6 @@ TEST_CASE("clone async", "[clone]") spdlog::details::os::sleep_for_millis(10); auto test_sink = std::static_pointer_cast(cloned->sinks()[0]); - REQUIRE(test_sink->msg_counter() == 2); spdlog::drop_all(); } @@ -176,22 +174,6 @@ TEST_CASE("to_hex_no_delimiter", "[to_hex]") REQUIRE(ends_with(output, "0000: 090A0B0CFFFF" + std::string(spdlog::details::os::default_eol))); } -TEST_CASE("message_counter", "[message_counter]") -{ - std::ostringstream oss; - auto oss_sink = std::make_shared(oss); - spdlog::logger oss_logger("oss", oss_sink); - oss_logger.set_pattern("%i %v"); - - oss_logger.info("Hello"); - REQUIRE(oss.str() == "000001 Hello" + std::string(spdlog::details::os::default_eol)); - - oss.str(""); - oss_logger.info("Hello again"); - - REQUIRE(oss.str() == "000002 Hello again" + std::string(spdlog::details::os::default_eol)); -} - TEST_CASE("default logger API", "[default logger]") { std::ostringstream oss; diff --git a/tests/tests.sln b/tests/tests.sln deleted file mode 100644 index 511c99c8..00000000 --- a/tests/tests.sln +++ /dev/null @@ -1,98 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27428.2037 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tests", "tests.vcxproj", "{59A07559-5F38-4DD6-A7FA-DB4153690B42}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "spdlog", "spdlog", "{0C043010-E40A-43B9-A5EA-B931FC8B6EFB}" - ProjectSection(SolutionItems) = preProject - ..\include\spdlog\async.h = ..\include\spdlog\async.h - ..\include\spdlog\async_logger.h = ..\include\spdlog\async_logger.h - ..\include\spdlog\common.h = ..\include\spdlog\common.h - ..\include\spdlog\formatter.h = ..\include\spdlog\formatter.h - ..\include\spdlog\logger.h = ..\include\spdlog\logger.h - ..\include\spdlog\spdlog.h = ..\include\spdlog\spdlog.h - ..\include\spdlog\tweakme.h = ..\include\spdlog\tweakme.h - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "details", "details", "{C8A207B7-6930-4D28-85BB-EA79ADFD7DAC}" - ProjectSection(SolutionItems) = preProject - ..\include\spdlog\details\async_logger_impl.h = ..\include\spdlog\details\async_logger_impl.h - ..\include\spdlog\details\file_helper.h = ..\include\spdlog\details\file_helper.h - ..\include\spdlog\details\log_msg.h = ..\include\spdlog\details\log_msg.h - ..\include\spdlog\details\logger_impl.h = ..\include\spdlog\details\logger_impl.h - ..\include\spdlog\details\mpmc_bounded_q.h = ..\include\spdlog\details\mpmc_bounded_q.h - ..\include\spdlog\details\null_mutex.h = ..\include\spdlog\details\null_mutex.h - ..\include\spdlog\details\os.h = ..\include\spdlog\details\os.h - ..\include\spdlog\details\pattern_formatter.h = ..\include\spdlog\details\pattern_formatter.h - ..\include\spdlog\details\registry.h = ..\include\spdlog\details\registry.h - ..\include\spdlog\details\spdlog_impl.h = ..\include\spdlog\details\spdlog_impl.h - ..\include\spdlog\details\thread_pool.h = ..\include\spdlog\details\thread_pool.h - ..\include\spdlog\details\traits.h = ..\include\spdlog\details\traits.h - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fmt", "fmt", "{6512BA57-E9B9-4971-BC3A-83C784CC59A5}" - ProjectSection(SolutionItems) = preProject - ..\include\spdlog\fmt\fmt.h = ..\include\spdlog\fmt\fmt.h - ..\include\spdlog\fmt\ostr.h = ..\include\spdlog\fmt\ostr.h - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "bundled", "bundled", "{F0D4A944-E0C7-4575-8254-06AC92B384E6}" - ProjectSection(SolutionItems) = preProject - ..\include\spdlog\fmt\bundled\format.cc = ..\include\spdlog\fmt\bundled\format.cc - ..\include\spdlog\fmt\bundled\format.h = ..\include\spdlog\fmt\bundled\format.h - ..\include\spdlog\fmt\bundled\LICENSE.rst = ..\include\spdlog\fmt\bundled\LICENSE.rst - ..\include\spdlog\fmt\bundled\ostream.cc = ..\include\spdlog\fmt\bundled\ostream.cc - ..\include\spdlog\fmt\bundled\ostream.h = ..\include\spdlog\fmt\bundled\ostream.h - ..\include\spdlog\fmt\bundled\posix.cc = ..\include\spdlog\fmt\bundled\posix.cc - ..\include\spdlog\fmt\bundled\posix.h = ..\include\spdlog\fmt\bundled\posix.h - ..\include\spdlog\fmt\bundled\time.h = ..\include\spdlog\fmt\bundled\time.h - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sinks", "sinks", "{093AE34A-B617-467E-8F31-3C5A85EF51EB}" - ProjectSection(SolutionItems) = preProject - ..\include\spdlog\sinks\android_sink.h = ..\include\spdlog\sinks\android_sink.h - ..\include\spdlog\sinks\ansicolor_sink.h = ..\include\spdlog\sinks\ansicolor_sink.h - ..\include\spdlog\sinks\base_sink.h = ..\include\spdlog\sinks\base_sink.h - ..\include\spdlog\sinks\dist_sink.h = ..\include\spdlog\sinks\dist_sink.h - ..\include\spdlog\sinks\msvc_sink.h = ..\include\spdlog\sinks\msvc_sink.h - ..\include\spdlog\sinks\null_sink.h = ..\include\spdlog\sinks\null_sink.h - ..\include\spdlog\sinks\ostream_sink.h = ..\include\spdlog\sinks\ostream_sink.h - ..\include\spdlog\sinks\sink.h = ..\include\spdlog\sinks\sink.h - ..\include\spdlog\sinks\stdout_color_sinks.h = ..\include\spdlog\sinks\stdout_color_sinks.h - ..\include\spdlog\sinks\stdout_sinks.h = ..\include\spdlog\sinks\stdout_sinks.h - ..\include\spdlog\sinks\syslog_sink.h = ..\include\spdlog\sinks\syslog_sink.h - ..\include\spdlog\sinks\wincolor_sink.h = ..\include\spdlog\sinks\wincolor_sink.h - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Debug|x64 = Debug|x64 - Release|Win32 = Release|Win32 - Release|x64 = Release|x64 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Debug|Win32.ActiveCfg = Debug|Win32 - {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Debug|Win32.Build.0 = Debug|Win32 - {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Debug|x64.ActiveCfg = Debug|x64 - {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Debug|x64.Build.0 = Debug|x64 - {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Release|Win32.ActiveCfg = Release|Win32 - {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Release|Win32.Build.0 = Release|Win32 - {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Release|x64.ActiveCfg = Release|x64 - {59A07559-5F38-4DD6-A7FA-DB4153690B42}.Release|x64.Build.0 = Release|x64 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {C8A207B7-6930-4D28-85BB-EA79ADFD7DAC} = {0C043010-E40A-43B9-A5EA-B931FC8B6EFB} - {6512BA57-E9B9-4971-BC3A-83C784CC59A5} = {0C043010-E40A-43B9-A5EA-B931FC8B6EFB} - {F0D4A944-E0C7-4575-8254-06AC92B384E6} = {6512BA57-E9B9-4971-BC3A-83C784CC59A5} - {093AE34A-B617-467E-8F31-3C5A85EF51EB} = {0C043010-E40A-43B9-A5EA-B931FC8B6EFB} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {1D363F0E-2DC2-4544-963A-9812679AFF6B} - EndGlobalSection -EndGlobal diff --git a/tests/tests.vcxproj b/tests/tests.vcxproj deleted file mode 100644 index a246fd21..00000000 --- a/tests/tests.vcxproj +++ /dev/null @@ -1,148 +0,0 @@ - - - - - Debug - Win32 - - - Debug - x64 - - - Release - Win32 - - - Release - x64 - - - - {59A07559-5F38-4DD6-A7FA-DB4153690B42} - tests - 10.0.16299.0 - - - - Application - true - v141 - MultiByte - - - Application - true - v141 - MultiByte - - - Application - false - v141 - true - MultiByte - - - Application - false - v141 - true - MultiByte - - - - - - - - - - - - - - - - - - - - - Level3 - Disabled - true - $(SolutionDir)\..\include;%(AdditionalIncludeDirectories) - - - true - Console - - - - - Level3 - Disabled - true - _MBCS;%(PreprocessorDefinitions) - $(SolutionDir)..\include;%(AdditionalIncludeDirectories) - - - true - Console - - - - - Level3 - MaxSpeed - true - true - true - $(SolutionDir)\..\include;%(AdditionalIncludeDirectories) - - - true - true - true - Console - - - - - Level3 - MaxSpeed - true - true - true - _MBCS;%(PreprocessorDefinitions) - $(SolutionDir)..\include;%(AdditionalIncludeDirectories) - - - true - true - true - Console - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/tests/tests.vcxproj.filters b/tests/tests.vcxproj.filters deleted file mode 100644 index c70cc496..00000000 --- a/tests/tests.vcxproj.filters +++ /dev/null @@ -1,60 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - Header Files - - - Header Files - - - Header Files - - - \ No newline at end of file diff --git a/tests/tests.vcxproj.user b/tests/tests.vcxproj.user deleted file mode 100644 index be250787..00000000 --- a/tests/tests.vcxproj.user +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file