diff --git a/.travis.yml b/.travis.yml index 85273844..49ea5476 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,91 +1,115 @@ -# Adapted from various sources, including: -# - Louis Dionne's Hana: https://github.com/ldionne/hana -# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit -# - Eric Niebler's range-v3: https://github.com/ericniebler/range-v3 -language: cpp - -# Test matrix: -# - Build matrix per compiler: C++11/C++14 + Debug/Release -# - Optionally: AddressSanitizer (ASAN) -# - Valgrind: all release builds are also tested with valgrind -# - clang 3.4, 3.5, 3.6, trunk -# - Note: 3.4 and trunk are tested with/without ASAN, -# the rest is only tested with ASAN=On. -# - gcc 4.9, 5.0 -# -matrix: - include: - -# Test gcc-4.8: C++11, Build=Debug/Release, ASAN=Off - - env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11 ASAN=Off LIBCXX=Off - os: linux - addons: &gcc48 - apt: - packages: - - g++-4.8 - - valgrind - sources: - - ubuntu-toolchain-r-test - - - env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=Off - os: linux - addons: *gcc48 - - # Test gcc-4.9: C++11, Build=Debug/Release, ASAN=Off - - env: GCC_VERSION=4.9 BUILD_TYPE=Debug CPP=11 ASAN=Off LIBCXX=Off - os: linux - addons: &gcc49 - apt: - packages: - - g++-4.9 - - valgrind - sources: - - ubuntu-toolchain-r-test - - - env: GCC_VERSION=4.9 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=Off - os: linux - addons: *gcc49 - -# Install dependencies -before_install: - - export CHECKOUT_PATH=`pwd`; - - if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi - - if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi - - if [ "$CLANG_VERSION" == "3.4" ]; then export CXX="/usr/local/clang-3.4/bin/clang++" CC="/usr/local/clang-3.4/bin/clang"; fi - - which $CXX - - which $CC - - which valgrind - - if [ -n "$CLANG_VERSION" ]; then sudo CXX=$CXX CC=$CC ./tests/install_libcxx.sh; fi - -install: - - cd $CHECKOUT_PATH - - # Workaround for valgrind bug: https://bugs.kde.org/show_bug.cgi?id=326469. - # It is fixed in valgrind 3.10 so this won't be necessary if someone - # replaces the current valgrind (3.7) with valgrind-3.10 - - sed -i 's/march=native/msse4.2/' example/Makefile - - - if [ ! -d build ]; then mkdir build; fi - - export CXX_FLAGS="-I${CHECKOUT_PATH}/include" - - export CXX_LINKER_FLAGS="" - - if [ -z "$BUILD_TYPE" ]; then export BUILD_TYPE=Release; fi - - if [ "$ASAN" == "On"]; then export CXX_FLAGS="${CXX_FLAGS} -fsanitize=address,undefined,integer -fno-omit-frame-pointer -fno-sanitize=unsigned-integer-overflow"; fi - - if [ -n "$CLANG_VERSION" ]; then CXX_FLAGS="${CXX_FLAGS} -D__extern_always_inline=inline"; fi - - if [ "$LIBCXX" == "On" ]; then CXX_FLAGS="${CXX_FLAGS} -stdlib=libc++ -I/usr/include/c++/v1/"; fi - - if [ "$LIBCXX" == "On" ]; then CXX_LINKER_FLAGS="${CXX_FLAGS} -L/usr/lib/ -lc++"; fi - - CXX_FLAGS="${CXX_FLAGS} -std=c++${CPP}" - - # Build examples - - cd example - - if [ "$BUILD_TYPE" == "Release" ]; then make rebuild CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example; fi - - if [ "$BUILD_TYPE" == "Debug" ]; then make rebuild debug CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example-debug; fi - - -script: - - ./"${BIN}" - - valgrind --trace-children=yes --leak-check=full ./"${BIN}" - - cd $CHECKOUT_PATH/tests; make rebuild; ./tests - - cd $CHECKOUT_PATH/tests; STYLE=printf make rebuild; ./tests - -notifications: - email: false +# Adapted from various sources, including: +# - Louis Dionne's Hana: https://github.com/ldionne/hana +# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit +# - Eric Niebler's range-v3: https://github.com/ericniebler/range-v3 +sudo: required +language: cpp + +addons: &gcc48 + apt: + packages: + - g++-4.8 + sources: + - ubuntu-toolchain-r-test + +addons: &gcc7 + apt: + packages: + - g++-7 + sources: + - ubuntu-toolchain-r-test + +addons: &clang35 + apt: + packages: + - clang-3.5 + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-precise-3.5 + +addons: &clang6 + apt: + packages: + - clang-6.0 + sources: + - ubuntu-toolchain-r-test + - llvm-toolchain-trusty-6.0 + + +matrix: + include: + # Test gcc-4.8: C++11, Build=Debug/Release + - env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11 + os: linux + addons: *gcc48 + + - 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 + + # Test clang-3.5: C++11, Build=Debug/Release + - env: CLANG_VERSION=3.5 BUILD_TYPE=Debug CPP=11 + os: linux + addons: *clang35 + + - env: CLANG_VERSION=3.5 BUILD_TYPE=Release CPP=11 + os: linux + addons: *clang35 + + # Test clang-6.0: C++11, Build=Debug, ASAN=On + - 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 + + + + +before_script: + - if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi + - if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi + - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export CXX="clang++" CC="clang"; fi + - which $CXX + - which $CC + - $CXX --version + - cmake --version + +script: + - cd ${TRAVIS_BUILD_DIR} + - mkdir -p build && cd build + - | + cmake .. \ + --warn-uninitialized \ + -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ + -DCMAKE_CXX_STANDARD=$CPP \ + -DSPDLOG_BUILD_EXAMPLES=ON \ + -DSPDLOG_BUILD_BENCH=OFF \ + -DSPDLOG_SANITIZE_ADDRESS=$ASAN \ + -DSPDLOG_SANITIZE_THREAD=$TSAN + - make VERBOSE=1 -j2 + - ctest -j2 --output-on-failure + + + +notifications: + email: false diff --git a/CMakeLists.txt b/CMakeLists.txt index 6d55b8ef..c0b8a373 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,27 +4,50 @@ # cmake_minimum_required(VERSION 3.1) -project(spdlog VERSION 1.0.0 LANGUAGES CXX) +project(spdlog VERSION 1.1.0 LANGUAGES CXX) include(CTest) include(CMakeDependentOption) include(GNUInstallDirs) +#--------------------------------------------------------------------------------------- +# set default build to release +#--------------------------------------------------------------------------------------- +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 #--------------------------------------------------------------------------------------- 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") - set(CMAKE_CXX_FLAGS "-Wall -O3 ${CMAKE_CXX_FLAGS}") + 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 +#--------------------------------------------------------------------------------------- +include(cmake/sanitizers.cmake) + #--------------------------------------------------------------------------------------- # spdlog target #--------------------------------------------------------------------------------------- add_library(spdlog INTERFACE) +add_library(spdlog::spdlog ALIAS spdlog) + +option(SPDLOG_BUILD_EXAMPLES "Build examples" ON) +option(SPDLOG_BUILD_BENCH "Build benchmarks" ON) -option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF) cmake_dependent_option(SPDLOG_BUILD_TESTING "Build spdlog tests" ON "BUILD_TESTING" OFF @@ -47,6 +70,10 @@ if(SPDLOG_BUILD_TESTING) add_subdirectory(tests) endif() +if(SPDLOG_BUILD_BENCH) + add_subdirectory(bench) +endif() + #--------------------------------------------------------------------------------------- # Install/export targets and files #--------------------------------------------------------------------------------------- diff --git a/README.md b/README.md index 6cf15068..a8fa9c20 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=master)](https://travis-ci.org/gabime/spdlog)  [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true)](https://ci.appveyor.com/project/gabime/spdlog) + ## Install #### Just copy the headers: @@ -28,7 +29,7 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci. ## Features * Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below). * Headers only, just copy and use. -* Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library. +* Feature rich using the excellent [fmt](https://github.com/fmtlib/fmt) library. * Fast asynchronous mode (optional) * [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting. * Conditional Logging @@ -53,27 +54,28 @@ Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/ben ******************************************************************************* Single thread, 1,000,000 iterations ******************************************************************************* -basic_st... Elapsed: 0.231041 4,328,228/sec -rotating... Elapsed: 0.233466 4,283,282/sec -daily_st... Elapsed: 0.244491 4,090,136/sec -null_st... Elapsed: 0.162708 6,145,995/sec +basic_st... Elapsed: 0.226664 4,411,806/sec +rotating_st... Elapsed: 0.214339 4,665,499/sec +daily_st... Elapsed: 0.211292 4,732,797/sec +null_st... Elapsed: 0.102815 9,726,227/sec ******************************************************************************* 10 threads sharing same logger, 1,000,000 iterations ******************************************************************************* -basic_mt... Elapsed: 0.854029 1,170,920/sec -rotating_mt Elapsed: 0.867038 1,153,351/sec -daily_mt... Elapsed: 0.869593 1,149,963/sec -null_mt... Elapsed: 0.171215 2,033,537/sec -``` +basic_mt... Elapsed: 0.882268 1,133,441/sec +rotating_mt... Elapsed: 0.875515 1,142,184/sec +daily_mt... Elapsed: 0.879573 1,136,915/sec +null_mt... Elapsed: 0.220114 4,543,105/sec +``` #### Asynchronous mode ``` ******************************************************************************* 10 threads sharing same logger, 1,000,000 iterations ******************************************************************************* -async... Elapsed: 0.442731 2,258,706/sec -async... Elapsed: 0.427072 2,341,527/sec -async... Elapsed: 0.449768 2,223,369/sec +async... Elapsed: 0.429088 2,330,524/sec +async... Elapsed: 0.411501 2,430,126/sec +async... Elapsed: 0.428979 2,331,116/sec + ``` ## Usage samples @@ -157,6 +159,18 @@ void daily_example() ``` +--- +#### Cloning loggers +```c++ +// clone a logger and give it new name. +// Useful for creating subsystem loggers from some "root" logger +void clone_example() +{ + auto network_logger = spdlog::get("root")->clone("network"); + network_logger->info("Logging network stuff.."); +} +``` + --- #### Periodic flush ```c++ @@ -166,22 +180,9 @@ spdlog::flush_every(std::chrono::seconds(3)); ``` ---- -#### Asynchronous logging -```c++ -#include "spdlog/async.h" -void async_example() -{ - // default thread pool settings can be modified *before* creating the async logger: - // spdlog::init_thread_pool(8192, 1); // queue with 8k items and 1 backing thread. - auto async_file = spdlog::basic_logger_mt("async_file_logger", "logs/async_log.txt"); - // alternatively: - // auto async_file = spdlog::create_async("async_file_logger", "logs/async_log.txt"); -} -``` --- -#### Logger with multi targets - each with different format and log level +#### Logger with multi sinks - each with different format and log level ```c++ // create logger with 2 targets with different log levels and formats. @@ -201,6 +202,40 @@ void multi_sink_example() logger.info("this message should not appear in the console, only in the file"); } ``` + +--- +#### Asynchronous logging +```c++ +#include "spdlog/async.h" +#include "spdlog/sinks/basic_file_sink.h" +void async_example() +{ + // default thread pool settings can be modified *before* creating the async logger: + // spdlog::init_thread_pool(8192, 1); // queue with 8k items and 1 backing thread. + auto async_file = spdlog::basic_logger_mt("async_file_logger", "logs/async_log.txt"); + // alternatively: + // auto async_file = spdlog::create_async("async_file_logger", "logs/async_log.txt"); +} + +``` + +--- +#### Asynchronous logger with multi sinks +```c++ +#include "spdlog/sinks/stdout_color_sinks.h" +#include "spdlog/sinks/rotating_file_sink.h" + +void multi_sink_example2() +{ + spdlog::init_thread_pool(8192, 1); + auto stdout_sink = std::make_shared(); + auto rotating_sink = std::make_shared("mylog.txt", 1024*1024*10, 3); + std::vector sinks {stdout_sink, rotating_sink}; + auto logger = std::make_shared("loggername", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block); + spdlog::register_logger(logger); +} +``` + --- #### User defined types ```c++ @@ -240,7 +275,7 @@ void err_handler_example() void syslog_example() { std::string ident = "spdlog-example"; - auto syslog_logger = spdlog::syslog_logger("syslog", ident, LOG_PID); + auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID); syslog_logger->warn("This is warning that will end up in syslog."); } ``` diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt new file mode 100644 index 00000000..5e492c0b --- /dev/null +++ b/bench/CMakeLists.txt @@ -0,0 +1,43 @@ +# *************************************************************************/ +# * 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. */ +# *************************************************************************/ + +cmake_minimum_required(VERSION 3.1) +project(SpdlogBench CXX) + +if(NOT TARGET spdlog) + # Stand-alone build + find_package(spdlog CONFIG REQUIRED) +endif() + +find_package(Threads REQUIRED) + +add_executable(bench bench.cpp) +target_link_libraries(bench spdlog::spdlog Threads::Threads) + +add_executable(async_bench async_bench.cpp) +target_link_libraries(async_bench spdlog::spdlog Threads::Threads) + +add_executable(latency latency.cpp) +target_link_libraries(latency spdlog::spdlog Threads::Threads) + +file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") diff --git a/bench/bench.cpp b/bench/bench.cpp index f5a8060b..db14e1af 100644 --- a/bench/bench.cpp +++ b/bench/bench.cpp @@ -35,8 +35,8 @@ int main(int argc, char *argv[]) int howmany = 1000000; int queue_size = howmany + 2; int threads = 10; - int file_size = 30 * 1024 * 1024; - int rotating_files = 5; + size_t file_size = 30 * 1024 * 1024; + size_t rotating_files = 5; try { @@ -89,7 +89,7 @@ int main(int argc, char *argv[]) for (int i = 0; i < 3; ++i) { - spdlog::init_thread_pool(queue_size, 1); + spdlog::init_thread_pool(static_cast(queue_size), 1); auto as = spdlog::basic_logger_mt("async", "logs/basic_async.log", true); bench_mt(howmany, as, threads); spdlog::drop("async"); diff --git a/bench/latency.cpp b/bench/latency.cpp index 374eafb0..dcc6965c 100644 --- a/bench/latency.cpp +++ b/bench/latency.cpp @@ -35,8 +35,8 @@ int main(int, char *[]) int howmany = 1000000; int queue_size = howmany + 2; int threads = 10; - int file_size = 30 * 1024 * 1024; - int rotating_files = 5; + size_t file_size = 30 * 1024 * 1024; + size_t rotating_files = 5; try { @@ -82,7 +82,7 @@ int main(int, char *[]) for (int i = 0; i < 3; ++i) { - spdlog::init_thread_pool(queue_size, 1); + spdlog::init_thread_pool(static_cast(queue_size), 1); auto as = spdlog::basic_logger_mt("async", "logs/basic_async.log", true); bench_mt(howmany, as, threads); spdlog::drop("async"); diff --git a/cmake/sanitizers.cmake b/cmake/sanitizers.cmake new file mode 100644 index 00000000..3b090835 --- /dev/null +++ b/cmake/sanitizers.cmake @@ -0,0 +1,21 @@ +if(SPDLOG_SANITIZE_THREAD AND SPDLOG_SANITIZE_ADDRESS) + message(FATAL_ERROR "AddressSanitizer is not compatible with ThreadSanitizer.") +endif() + +if(SPDLOG_SANITIZE_ADDRESS) + message(STATUS "AddressSanitizer enabled") + set(SANITIZER_FLAGS "-fsanitize=address,undefined") + add_compile_options("-fno-sanitize=signed-integer-overflow") +endif() + +if(SPDLOG_SANITIZE_THREAD) + message(STATUS "ThreadSanitizer enabled") + set(SANITIZER_FLAGS "-fsanitize=thread") +endif() + +if(SPDLOG_SANITIZE_THREAD OR SPDLOG_SANITIZE_ADDRESS) + add_compile_options(${SANITIZER_FLAGS}) + add_compile_options("-fno-sanitize-recover=all") + add_compile_options("-fno-omit-frame-pointer") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${SANITIZER_FLAGS} -fuse-ld=gold") +endif() diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index cc699729..695053cd 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -24,10 +24,7 @@ cmake_minimum_required(VERSION 3.1) project(SpdlogExamples CXX) -if(TARGET spdlog) - # Part of the main project - add_library(spdlog::spdlog ALIAS spdlog) -else() +if(NOT TARGET spdlog) # Stand-alone build find_package(spdlog CONFIG REQUIRED) endif() @@ -37,13 +34,10 @@ find_package(Threads REQUIRED) add_executable(example example.cpp) target_link_libraries(example spdlog::spdlog Threads::Threads) -add_executable(benchmark bench.cpp) -target_link_libraries(benchmark spdlog::spdlog Threads::Threads) - add_executable(multisink multisink.cpp) target_link_libraries(multisink spdlog::spdlog Threads::Threads) -enable_testing() file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") -add_test(NAME RunExample COMMAND example) -add_test(NAME RunBenchmark COMMAND benchmark) + +enable_testing() +add_test(NAME example COMMAND example) diff --git a/example/Makefile b/example/Makefile index 01bdf507..bd6b2922 100644 --- a/example/Makefile +++ b/example/Makefile @@ -1,5 +1,5 @@ CXX ?= g++ -CXX_FLAGS = -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1 +CXX_FLAGS = -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1 -Wconversion CXX_RELEASE_FLAGS = -O3 -march=native CXX_DEBUG_FLAGS= -g diff --git a/example/Makefile-all-warn b/example/Makefile-all-warn new file mode 100644 index 00000000..2ba68e2a --- /dev/null +++ b/example/Makefile-all-warn @@ -0,0 +1,22 @@ +#-Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded +CXX ?= g++ +CXX_FLAGS = -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1 -Wconversion -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded -Wno-weak-vtables -Wno-global-constructors +CXX_RELEASE_FLAGS = -O3 -march=native +CXX_DEBUG_FLAGS= -g + +all: example +debug: example-debug + +example: example.cpp + $(CXX) example.cpp -o example $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS) + + +example-debug: example.cpp + $(CXX) example.cpp -o example-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS) + +clean: + rm -f *.o logs/*.txt example example-debug + + +rebuild: clean all +rebuild-debug: clean debug diff --git a/example/example.cpp b/example/example.cpp index 4222c829..b7221447 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -18,6 +18,7 @@ void multi_sink_example(); void user_defined_example(); void err_handler_example(); void syslog_example(); +void clone_example(); #include "spdlog/spdlog.h" @@ -34,6 +35,8 @@ int main(int, char *[]) rotating_example(); daily_example(); + clone_example(); + // async logging using a backing thread pool async_example(); @@ -122,6 +125,14 @@ void daily_example() auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30); } +// clone a logger and give it new name. +// Useful for creating component/subsystem loggers from some "root" logger +void clone_example() +{ + auto network_logger = spdlog::get("console")->clone("network"); + network_logger->info("Logging network stuff.."); +} + #include "spdlog/async.h" void async_example() { diff --git a/example/multisink.cpp b/example/multisink.cpp index 4b3f2d9c..7dc267d2 100644 --- a/example/multisink.cpp +++ b/example/multisink.cpp @@ -1,10 +1,9 @@ -#include "spdlog/sinks/file_sinks.h" +#include "spdlog/sinks/basic_file_sink.h" #include "spdlog/sinks/stdout_sinks.h" #include "spdlog/spdlog.h" #include #include -namespace spd = spdlog; int main(int, char *[]) { bool enable_debug = true; @@ -39,7 +38,7 @@ int main(int, char *[]) spdlog::drop_all(); } // Exceptions will only be thrown upon failed logger or sink construction (not during logging) - catch (const spd::spdlog_ex &ex) + catch (const spdlog::spdlog_ex &ex) { std::cout << "Log init failed: " << ex.what() << std::endl; return 1; diff --git a/include/spdlog/async.h b/include/spdlog/async.h index f70c4a3e..9c3e9551 100644 --- a/include/spdlog/async.h +++ b/include/spdlog/async.h @@ -42,7 +42,7 @@ struct async_factory_impl auto ®istry_inst = details::registry::instance(); // create global thread pool if not already exists.. - std::lock_guard(registry_inst.tp_mutex()); + std::lock_guard tp_lock(registry_inst.tp_mutex()); auto tp = registry_inst.get_tp(); if (tp == nullptr) { diff --git a/include/spdlog/async_logger.h b/include/spdlog/async_logger.h index 2d58f98b..690b1c6a 100644 --- a/include/spdlog/async_logger.h +++ b/include/spdlog/async_logger.h @@ -49,12 +49,14 @@ public: async_logger(std::string logger_name, const It &begin, const It &end, std::weak_ptr tp, async_overflow_policy overflow_policy = async_overflow_policy::block); - async_logger(std::string logger_name, sinks_init_list sinks, std::weak_ptr tp, + async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr tp, async_overflow_policy overflow_policy = async_overflow_policy::block); async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr tp, async_overflow_policy overflow_policy = async_overflow_policy::block); + std::shared_ptr clone(std::string new_name) override; + protected: void sink_it_(details::log_msg &msg) override; void flush_() override; diff --git a/include/spdlog/common.h b/include/spdlog/common.h index b8d45175..379a6d75 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -100,6 +100,7 @@ inline const char *to_short_c_str(spdlog::level::level_enum l) { return short_level_names[l]; } + inline spdlog::level::level_enum from_str(const std::string &name) { static std::unordered_map name_to_level = // map string->level @@ -131,35 +132,28 @@ enum class pattern_time_type // // Log exception // -class spdlog_ex : public std::runtime_error +class spdlog_ex : public std::exception { public: explicit spdlog_ex(const std::string &msg) - : runtime_error(msg) + : msg_(msg) { } - spdlog_ex(std::string msg, int last_errno) - : runtime_error(std::move(msg)) - , last_errno_(last_errno) + + 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 { - if (last_errno_) - { - fmt::memory_buffer buf; - std::string msg(runtime_error::what()); - fmt::format_system_error(buf, last_errno_, msg); - return fmt::to_string(buf).c_str(); - } - else - { - return runtime_error::what(); - } + return msg_.c_str(); } private: - int last_errno_{0}; + std::string msg_; }; // @@ -180,4 +174,17 @@ using filename_t = std::string; { \ err_handler_("Unknown exeption in logger"); \ } + +// +// make_unique support +// +#if __cplusplus >= 201402L // C++14 and beyond +using std::make_unique; +#else +template +std::unique_ptr make_unique(Args &&... args) +{ + return std::unique_ptr(new T(std::forward(args)...)); +} +#endif } // namespace spdlog diff --git a/include/spdlog/details/async_logger_impl.h b/include/spdlog/details/async_logger_impl.h index a42ade94..3ad70a3b 100644 --- a/include/spdlog/details/async_logger_impl.h +++ b/include/spdlog/details/async_logger_impl.h @@ -47,7 +47,7 @@ inline void spdlog::async_logger::sink_it_(details::log_msg &msg) } else { - throw spdlog_ex("async log: thread pool doens't exist anymore"); + throw spdlog_ex("async log: thread pool doesn't exist anymore"); } } @@ -98,3 +98,13 @@ inline void spdlog::async_logger::backend_flush_() } SPDLOG_CATCH_AND_HANDLE } + +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()); + return std::move(cloned); +} diff --git a/include/spdlog/details/circular_q.h b/include/spdlog/details/circular_q.h index 48f9a8fa..b01325bb 100644 --- a/include/spdlog/details/circular_q.h +++ b/include/spdlog/details/circular_q.h @@ -6,6 +6,8 @@ // cirucal q view of std::vector. #pragma once +#include + namespace spdlog { namespace details { template @@ -29,6 +31,7 @@ public: if (tail_ == head_) // overrun last item if full { head_ = (head_ + 1) % max_items_; + ++overrun_counter_; } } @@ -51,12 +54,19 @@ public: return ((tail_ + 1) % max_items_) == head_; } + size_t overrun_counter() const + { + return overrun_counter_; + } + private: size_t max_items_; typename std::vector::size_type head_ = 0; typename std::vector::size_type tail_ = 0; std::vector v_; + + size_t overrun_counter_ = 0; }; } // namespace details -} // namespace spdlog \ No newline at end of file +} // namespace spdlog diff --git a/include/spdlog/details/console_globals.h b/include/spdlog/details/console_globals.h index 52de06a5..e2afb6bf 100644 --- a/include/spdlog/details/console_globals.h +++ b/include/spdlog/details/console_globals.h @@ -5,14 +5,27 @@ // #include "spdlog/details/null_mutex.h" -#include "stdio.h" +#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 +#endif + namespace spdlog { namespace details { struct console_stdout { - static FILE *stream() + static std::FILE *stream() { return stdout; } @@ -26,7 +39,7 @@ struct console_stdout struct console_stderr { - static FILE *stream() + static std::FILE *stream() { return stderr; } diff --git a/include/spdlog/details/file_helper.h b/include/spdlog/details/file_helper.h index ef70d561..f7282000 100644 --- a/include/spdlog/details/file_helper.h +++ b/include/spdlog/details/file_helper.h @@ -5,13 +5,12 @@ #pragma once -// Helper class for file sink -// When failing to open a file, retry several times(5) with small delay between -// the tries(10 ms) -// Throw spdlog_ex exception on errors +// 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 "../details/log_msg.h" -#include "../details/os.h" +#include "spdlog/details/log_msg.h" +#include "spdlog/details/os.h" #include #include @@ -136,7 +135,7 @@ public: // treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile" auto folder_index = fname.rfind(details::os::folder_sep); - if (folder_index != fname.npos && folder_index >= ext_index - 1) + if (folder_index != filename_t::npos && folder_index >= ext_index - 1) { return std::make_tuple(fname, spdlog::filename_t()); } @@ -146,7 +145,7 @@ public: } private: - FILE *fd_{nullptr}; + std::FILE *fd_{nullptr}; filename_t _filename; }; } // namespace details diff --git a/include/spdlog/details/fmt_helper.h b/include/spdlog/details/fmt_helper.h index 0f670539..1d84e5eb 100644 --- a/include/spdlog/details/fmt_helper.h +++ b/include/spdlog/details/fmt_helper.h @@ -22,12 +22,8 @@ inline void append_str(const std::string &str, fmt::basic_memory_buffer inline void append_c_str(const char *c_str, fmt::basic_memory_buffer &dest) { - char ch; - while ((ch = *c_str) != '\0') - { - dest.push_back(ch); - ++c_str; - } + auto len = std::char_traits::length(c_str); + dest.append(c_str, c_str + len); } template @@ -54,14 +50,14 @@ inline void pad2(int n, fmt::basic_memory_buffer &dest) } if (n > 9) // 10-99 { - dest.push_back('0' + static_cast(n / 10)); - dest.push_back('0' + static_cast(n % 10)); + dest.push_back(static_cast('0' + n / 10)); + dest.push_back(static_cast('0' + n % 10)); return; } if (n >= 0) // 0-9 { dest.push_back('0'); - dest.push_back('0' + static_cast(n)); + dest.push_back(static_cast('0' + n)); return; } // negatives (unlikely, but just in case, let fmt deal with it) @@ -86,15 +82,15 @@ inline void pad3(int n, fmt::basic_memory_buffer &dest) if (n > 9) // 10-99 { dest.push_back('0'); - dest.push_back('0' + static_cast(n / 10)); - dest.push_back('0' + static_cast(n % 10)); + dest.push_back(static_cast('0' + n / 10)); + dest.push_back(static_cast('0' + n % 10)); return; } if (n >= 0) { dest.push_back('0'); dest.push_back('0'); - dest.push_back('0' + static_cast(n)); + dest.push_back(static_cast('0' + n)); return; } // negatives (unlikely, but just in case let fmt deal with it) @@ -127,4 +123,4 @@ inline ToDuration time_fraction(const log_clock::time_point &tp) } // namespace fmt_helper } // namespace details -} // namespace spdlog \ No newline at end of file +} // namespace spdlog diff --git a/include/spdlog/details/log_msg.h b/include/spdlog/details/log_msg.h index ce988e4f..3272dd5d 100644 --- a/include/spdlog/details/log_msg.h +++ b/include/spdlog/details/log_msg.h @@ -16,17 +16,18 @@ namespace details { struct log_msg { log_msg() = default; + log_msg(const std::string *loggers_name, level::level_enum lvl) : logger_name(loggers_name) , level(lvl) - { #ifndef SPDLOG_NO_DATETIME - time = os::now(); + , time(os::now()) #endif #ifndef SPDLOG_NO_THREAD_ID - thread_id = os::thread_id(); + , thread_id(os::thread_id()) #endif + { } log_msg(const log_msg &other) = delete; diff --git a/include/spdlog/details/logger_impl.h b/include/spdlog/details/logger_impl.h index 0a7c5b6b..7291e631 100644 --- a/include/spdlog/details/logger_impl.h +++ b/include/spdlog/details/logger_impl.h @@ -5,6 +5,8 @@ #pragma once +#include "spdlog/details/fmt_helper.h" + #include #include @@ -77,7 +79,7 @@ inline void spdlog::logger::log(level::level_enum lvl, const char *msg) try { details::log_msg log_msg(&name_, lvl); - fmt::format_to(log_msg.raw, "{}", msg); + details::fmt_helper::append_c_str(msg, log_msg.raw); sink_it_(log_msg); } SPDLOG_CATCH_AND_HANDLE @@ -268,6 +270,11 @@ 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); @@ -343,3 +350,12 @@ 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 d607da2c..ca789fc6 100644 --- a/include/spdlog/details/mpmc_blocking_q.h +++ b/include/spdlog/details/mpmc_blocking_q.h @@ -105,6 +105,12 @@ public: #endif + size_t overrun_counter() + { + std::unique_lock lock(queue_mutex_); + return q_.overrun_counter(); + } + private: std::mutex queue_mutex_; std::condition_variable push_cv_; diff --git a/include/spdlog/details/os.h b/include/spdlog/details/os.h index d7f7430f..7ee42542 100644 --- a/include/spdlog/details/os.h +++ b/include/spdlog/details/os.h @@ -159,9 +159,9 @@ inline bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mod { #ifdef _WIN32 #ifdef SPDLOG_WCHAR_FILENAMES - *fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); + *fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); #else - *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR); + *fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYNO); #endif #else // unix *fp = fopen((filename.c_str()), mode.c_str()); diff --git a/include/spdlog/details/pattern_formatter.h b/include/spdlog/details/pattern_formatter.h index 765f9305..ecdabff3 100644 --- a/include/spdlog/details/pattern_formatter.h +++ b/include/spdlog/details/pattern_formatter.h @@ -69,7 +69,7 @@ static const char *ampm(const tm &t) return t.tm_hour >= 12 ? "PM" : "AM"; } -static unsigned int to12h(const tm &t) +static int to12h(const tm &t) { return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour; } @@ -242,7 +242,7 @@ class f_formatter SPDLOG_FINAL : public flag_formatter void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override { auto micros = fmt_helper::time_fraction(msg.time); - fmt_helper::pad6(static_cast(micros.count()), dest); + fmt_helper::pad6(static_cast(micros.count()), dest); } }; @@ -545,9 +545,9 @@ public: pattern_formatter(const pattern_formatter &other) = delete; pattern_formatter &operator=(const pattern_formatter &other) = delete; - virtual std::unique_ptr clone() const override + std::unique_ptr clone() const override { - return std::unique_ptr(new pattern_formatter(pattern_, pattern_time_type_, eol_)); + return spdlog::make_unique(pattern_, pattern_time_type_, eol_); } void format(const details::log_msg &msg, fmt::memory_buffer &dest) override @@ -592,141 +592,141 @@ private: { // logger name case 'n': - formatters_.emplace_back(new details::name_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case 'l': - formatters_.emplace_back(new details::level_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case 'L': - formatters_.emplace_back(new details::short_level_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('t'): - formatters_.emplace_back(new details::t_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('v'): - formatters_.emplace_back(new details::v_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('a'): - formatters_.emplace_back(new details::a_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('A'): - formatters_.emplace_back(new details::A_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('b'): case ('h'): - formatters_.emplace_back(new details::b_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('B'): - formatters_.emplace_back(new details::B_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('c'): - formatters_.emplace_back(new details::c_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('C'): - formatters_.emplace_back(new details::C_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('Y'): - formatters_.emplace_back(new details::Y_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('D'): case ('x'): - formatters_.emplace_back(new details::D_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('m'): - formatters_.emplace_back(new details::m_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('d'): - formatters_.emplace_back(new details::d_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('H'): - formatters_.emplace_back(new details::H_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('I'): - formatters_.emplace_back(new details::I_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('M'): - formatters_.emplace_back(new details::M_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('S'): - formatters_.emplace_back(new details::S_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('e'): - formatters_.emplace_back(new details::e_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('f'): - formatters_.emplace_back(new details::f_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('F'): - formatters_.emplace_back(new details::F_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('E'): - formatters_.emplace_back(new details::E_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('p'): - formatters_.emplace_back(new details::p_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('r'): - formatters_.emplace_back(new details::r_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('R'): - formatters_.emplace_back(new details::R_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('T'): case ('X'): - formatters_.emplace_back(new details::T_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('z'): - formatters_.emplace_back(new details::z_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('+'): - formatters_.emplace_back(new details::full_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('P'): - formatters_.emplace_back(new details::pid_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('i'): - formatters_.emplace_back(new details::i_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('^'): - formatters_.emplace_back(new details::color_start_formatter()); + formatters_.push_back(spdlog::make_unique()); break; case ('$'): - formatters_.emplace_back(new details::color_stop_formatter()); + formatters_.push_back(spdlog::make_unique()); break; default: // Unknown flag appears as is - formatters_.emplace_back(new details::ch_formatter('%')); - formatters_.emplace_back(new details::ch_formatter(flag)); + formatters_.push_back(spdlog::make_unique('%')); + formatters_.push_back(spdlog::make_unique(flag)); break; } } @@ -744,7 +744,6 @@ private: { formatters_.push_back(std::move(user_chars)); } - // if( if (++it != end) { handle_flag_(*it); @@ -758,7 +757,7 @@ private: { if (!user_chars) { - user_chars = std::unique_ptr(new details::aggregate_formatter()); + user_chars = spdlog::make_unique(); } user_chars->add_ch(*it); } diff --git a/include/spdlog/details/periodic_worker.h b/include/spdlog/details/periodic_worker.h index acc13117..57e5fa77 100644 --- a/include/spdlog/details/periodic_worker.h +++ b/include/spdlog/details/periodic_worker.h @@ -23,7 +23,7 @@ namespace details { class periodic_worker { public: - periodic_worker(std::function callback_fun, std::chrono::seconds interval) + periodic_worker(const std::function &callback_fun, std::chrono::seconds interval) { active_ = (interval > std::chrono::seconds::zero()); if (!active_) diff --git a/include/spdlog/details/registry.h b/include/spdlog/details/registry.h index b3db19a2..52c3bb7d 100644 --- a/include/spdlog/details/registry.h +++ b/include/spdlog/details/registry.h @@ -35,7 +35,7 @@ public: std::lock_guard lock(logger_map_mutex_); auto logger_name = new_logger->name(); throw_if_exists_(logger_name); - loggers_[logger_name] = new_logger; + loggers_[logger_name] = std::move(new_logger); } void register_and_init(std::shared_ptr new_logger) @@ -56,7 +56,7 @@ public: new_logger->flush_on(flush_level_); // add to registry - loggers_[logger_name] = new_logger; + loggers_[logger_name] = std::move(new_logger); } std::shared_ptr get(const std::string &logger_name) @@ -112,8 +112,8 @@ public: void flush_every(std::chrono::seconds interval) { std::lock_guard lock(flusher_mutex_); - std::function clbk(std::bind(®istry::flush_all, this)); - periodic_flusher_.reset(new periodic_worker(clbk, interval)); + std::function clbk = std::bind(®istry::flush_all, this); + periodic_flusher_ = spdlog::make_unique(clbk, interval); } void set_error_handler(log_err_handler handler) @@ -126,7 +126,7 @@ public: err_handler_ = handler; } - void apply_all(std::function)> fun) + void apply_all(const std::function)> &fun) { std::lock_guard lock(logger_map_mutex_); for (auto &l : loggers_) @@ -189,11 +189,7 @@ private: { } - ~registry() - { - /*std::lock_guard lock(flusher_mutex_); - periodic_flusher_.reset();*/ - } + ~registry() = default; void throw_if_exists_(const std::string &logger_name) { diff --git a/include/spdlog/details/thread_pool.h b/include/spdlog/details/thread_pool.h index 2620a19e..b7eb6caa 100644 --- a/include/spdlog/details/thread_pool.h +++ b/include/spdlog/details/thread_pool.h @@ -64,8 +64,8 @@ struct async_msg return *this; } #else // (_MSC_VER) && _MSC_VER <= 1800 - async_msg(async_msg &&other) = default; - async_msg &operator=(async_msg &&other) = default; + async_msg(async_msg &&) = default; + async_msg &operator=(async_msg &&) = default; #endif // construct from log_msg with given type @@ -122,7 +122,7 @@ public: } for (size_t i = 0; i < threads_n; i++) { - threads_.emplace_back(std::bind(&thread_pool::worker_loop_, this)); + threads_.emplace_back(&thread_pool::worker_loop_, this); } } @@ -157,6 +157,11 @@ public: post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy); } + size_t overrun_counter() + { + return q_.overrun_counter(); + } + private: q_type q_; @@ -193,6 +198,13 @@ private: switch (incoming_async_msg.msg_type) { + case async_msg_type::log: + { + log_msg msg; + incoming_async_msg.to_log_msg(msg); + incoming_async_msg.worker_ptr->backend_log_(msg); + return true; + } case async_msg_type::flush: { incoming_async_msg.worker_ptr->backend_flush_(); @@ -203,18 +215,11 @@ private: { return false; } - - default: - { - log_msg msg; - incoming_async_msg.to_log_msg(msg); - incoming_async_msg.worker_ptr->backend_log_(msg); - return true; } - } - return true; // should not be reached + assert(false && "Unexpected async_msg_type"); + return true; } }; } // namespace details -} // namespace spdlog \ No newline at end of file +} // namespace spdlog diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h index 91eec4bc..aa524f70 100644 --- a/include/spdlog/logger.h +++ b/include/spdlog/logger.h @@ -120,16 +120,22 @@ public: void set_formatter(std::unique_ptr formatter); void set_pattern(std::string pattern, pattern_time_type time_type = pattern_time_type::local); + // flush functions void flush(); void flush_on(level::level_enum log_level); + level::level_enum flush_level() const; + // 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(); + // 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_(); diff --git a/include/spdlog/sinks/android_sink.h b/include/spdlog/sinks/android_sink.h index be70db00..fcb9ccbf 100644 --- a/include/spdlog/sinks/android_sink.h +++ b/include/spdlog/sinks/android_sink.h @@ -30,8 +30,8 @@ template class android_sink SPDLOG_FINAL : public base_sink { public: - explicit android_sink(const std::string &tag = "spdlog", bool use_raw_msg = false) - : tag_(tag) + explicit android_sink(std::string tag = "spdlog", bool use_raw_msg = false) + : tag_(std::move(tag)) , use_raw_msg_(use_raw_msg) { } @@ -43,11 +43,11 @@ protected: fmt::memory_buffer formatted; if (use_raw_msg_) { - fmt_helper::append_buf(msg.raw, formatted); + details::fmt_helper::append_buf(msg.raw, formatted); } else { - formatter_->format(msg, formatted); + sink::formatter_->format(msg, formatted); } formatted.push_back('\0'); const char *msg_output = formatted.data(); diff --git a/include/spdlog/sinks/ansicolor_sink.h b/include/spdlog/sinks/ansicolor_sink.h index 3b79976d..3f6c63b6 100644 --- a/include/spdlog/sinks/ansicolor_sink.h +++ b/include/spdlog/sinks/ansicolor_sink.h @@ -24,7 +24,7 @@ namespace sinks { * If no color terminal detected, omit the escape codes. */ template -class ansicolor_sink : public sink +class ansicolor_sink SPDLOG_FINAL : public sink { public: using mutex_t = typename ConsoleMutex::mutex_t; @@ -84,7 +84,7 @@ public: const std::string on_cyan = "\033[46m"; const std::string on_white = "\033[47m"; - void log(const details::log_msg &msg) SPDLOG_FINAL override + 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. @@ -110,19 +110,19 @@ public: fflush(target_file_); } - void flush() SPDLOG_FINAL override + void flush() override { std::lock_guard lock(mutex_); fflush(target_file_); } - void set_pattern(const std::string &pattern) override SPDLOG_FINAL + void set_pattern(const std::string &pattern) SPDLOG_FINAL { std::lock_guard lock(mutex_); formatter_ = std::unique_ptr(new pattern_formatter(pattern)); } - void set_formatter(std::unique_ptr sink_formatter) override SPDLOG_FINAL + void set_formatter(std::unique_ptr sink_formatter) override { std::lock_guard lock(mutex_); formatter_ = std::move(sink_formatter); diff --git a/include/spdlog/sinks/base_sink.h b/include/spdlog/sinks/base_sink.h index 5a3821bc..43863eae 100644 --- a/include/spdlog/sinks/base_sink.h +++ b/include/spdlog/sinks/base_sink.h @@ -30,7 +30,7 @@ public: base_sink(const base_sink &) = delete; base_sink &operator=(const base_sink &) = delete; - void log(const details::log_msg &msg) SPDLOG_FINAL override + void log(const details::log_msg &msg) SPDLOG_FINAL { std::lock_guard lock(mutex_); sink_it_(msg); diff --git a/include/spdlog/sinks/dist_sink.h b/include/spdlog/sinks/dist_sink.h index 8d2f6f9e..b3e2e525 100644 --- a/include/spdlog/sinks/dist_sink.h +++ b/include/spdlog/sinks/dist_sink.h @@ -40,6 +40,12 @@ public: sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end()); } + void set_sinks(std::vector> sinks) + { + std::lock_guard lock(base_sink::mutex_); + sinks_ = std::move(sinks); + } + protected: void sink_it_(const details::log_msg &msg) override { diff --git a/include/spdlog/sinks/ostream_sink.h b/include/spdlog/sinks/ostream_sink.h index 5f31993c..00c15959 100644 --- a/include/spdlog/sinks/ostream_sink.h +++ b/include/spdlog/sinks/ostream_sink.h @@ -30,7 +30,7 @@ protected: { fmt::memory_buffer formatted; sink::formatter_->format(msg, formatted); - ostream_.write(formatted.data(), formatted.size()); + ostream_.write(formatted.data(), static_cast(formatted.size())); if (force_flush_) ostream_.flush(); } diff --git a/include/spdlog/sinks/sink.h b/include/spdlog/sinks/sink.h index 288d5438..9f84c378 100644 --- a/include/spdlog/sinks/sink.h +++ b/include/spdlog/sinks/sink.h @@ -20,9 +20,11 @@ public: { } - sink(std::unique_ptr formatter) + explicit sink(std::unique_ptr formatter) : level_(level::trace) - , formatter_(std::move(formatter)){}; + , formatter_(std::move(formatter)) + { + } virtual ~sink() = default; virtual void log(const details::log_msg &msg) = 0; diff --git a/include/spdlog/sinks/stdout_sinks.h b/include/spdlog/sinks/stdout_sinks.h index 64c0ab70..6106cd08 100644 --- a/include/spdlog/sinks/stdout_sinks.h +++ b/include/spdlog/sinks/stdout_sinks.h @@ -12,14 +12,13 @@ #include #include #include -#include namespace spdlog { namespace sinks { template -class stdout_sink : public sink +class stdout_sink SPDLOG_FINAL : public sink { public: using mutex_t = typename ConsoleMutex::mutex_t; @@ -28,7 +27,7 @@ public: , file_(TargetStream::stream()) { } - ~stdout_sink() = default; + ~stdout_sink() override = default; stdout_sink(const stdout_sink &other) = delete; stdout_sink &operator=(const stdout_sink &other) = delete; @@ -48,13 +47,13 @@ public: fflush(file_); } - void set_pattern(const std::string &pattern) override SPDLOG_FINAL + void set_pattern(const std::string &pattern) override { std::lock_guard lock(mutex_); formatter_ = std::unique_ptr(new pattern_formatter(pattern)); } - void set_formatter(std::unique_ptr sink_formatter) override SPDLOG_FINAL + void set_formatter(std::unique_ptr sink_formatter) override { std::lock_guard lock(mutex_); formatter_ = std::move(sink_formatter); diff --git a/include/spdlog/sinks/syslog_sink.h b/include/spdlog/sinks/syslog_sink.h index e171bf9a..151e7a11 100644 --- a/include/spdlog/sinks/syslog_sink.h +++ b/include/spdlog/sinks/syslog_sink.h @@ -24,8 +24,8 @@ class syslog_sink : public base_sink { public: // - syslog_sink(const std::string &ident = "", int syslog_option = 0, int syslog_facility = LOG_USER) - : ident_(ident) + explicit syslog_sink(std::string ident = "", int syslog_option = 0, int syslog_facility = LOG_USER) + : ident_(std::move(ident)) { priorities_[static_cast(level::trace)] = LOG_DEBUG; priorities_[static_cast(level::debug)] = LOG_DEBUG; diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h index 2963a2a0..40640ab9 100644 --- a/include/spdlog/spdlog.h +++ b/include/spdlog/spdlog.h @@ -64,7 +64,7 @@ inline void set_formatter(std::unique_ptr formatter) // 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(pattern, time_type))); + set_formatter(std::unique_ptr(new pattern_formatter(std::move(pattern), time_type))); } // Set global logging level @@ -101,9 +101,9 @@ inline 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(std::function)> fun) +inline void apply_all(const std::function)> &fun) { - details::registry::instance().apply_all(std::move(fun)); + details::registry::instance().apply_all(fun); } // Drop the reference to the given logger diff --git a/include/spdlog/version.h b/include/spdlog/version.h index 729fcba0..f14078ec 100644 --- a/include/spdlog/version.h +++ b/include/spdlog/version.h @@ -6,7 +6,7 @@ #pragma once #define SPDLOG_VER_MAJOR 1 -#define SPDLOG_VER_MINOR 0 +#define SPDLOG_VER_MINOR 1 #define SPDLOG_VER_PATCH 0 #define SPDLOG_VERSION (SPDLOG_VER_MAJOR * 10000 + SPDLOG_VER_MINOR * 100 + SPDLOG_VER_PATCH) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ba8b7487..2fb0b4b2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,5 @@ project(spdlog-utests CXX) -enable_testing() + find_package(Threads REQUIRED) set(SPDLOG_UTESTS_SOURCES @@ -7,18 +7,23 @@ set(SPDLOG_UTESTS_SOURCES file_helper.cpp file_log.cpp test_misc.cpp - test_pattern_formatter.cpp + test_pattern_formatter.cpp test_async.cpp includes.h registry.cpp test_macros.cpp utils.cpp utils.h - main.cpp) + main.cpp + test_mpmc_q.cpp + test_sink.h +) add_executable(${PROJECT_NAME} ${SPDLOG_UTESTS_SOURCES}) target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads) -target_link_libraries(${PROJECT_NAME} PRIVATE spdlog) +target_link_libraries(${PROJECT_NAME} PRIVATE spdlog::spdlog) -add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME}) file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs") + +enable_testing() +add_test(NAME ${PROJECT_NAME} COMMAND ${PROJECT_NAME}) diff --git a/tests/Makefile b/tests/Makefile index 00c7c589..bec3026d 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -1,5 +1,5 @@ CXX ?= g++ -CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -O3 -I../include -fmax-errors=1 +CXXFLAGS = -Wall -pedantic -std=c++11 -pthread -Wconversion -O3 -I../include -fmax-errors=1 LDPFALGS = -pthread CPP_FILES := $(wildcard *.cpp) diff --git a/tests/errors.cpp b/tests/errors.cpp index 32209b43..40f3723f 100644 --- a/tests/errors.cpp +++ b/tests/errors.cpp @@ -9,15 +9,15 @@ class failing_sink : public spdlog::sinks::base_sink { public: failing_sink() = default; - ~failing_sink() = default; + ~failing_sink() final = default; protected: - void sink_it_(const spdlog::details::log_msg &) override + void sink_it_(const spdlog::details::log_msg &) final { throw std::runtime_error("some error happened during log"); } - void flush_() override + void flush_() final { throw std::runtime_error("some error happened during flush"); } @@ -25,7 +25,6 @@ protected: TEST_CASE("default_error_handler", "[errors]]") { - prepare_logdir(); std::string filename = "logs/simple_log.txt"; diff --git a/tests/file_log.cpp b/tests/file_log.cpp index dbaab390..4a2df969 100644 --- a/tests/file_log.cpp +++ b/tests/file_log.cpp @@ -33,7 +33,7 @@ TEST_CASE("flush_on", "[flush_on]]") logger->info("Test message {}", 1); logger->info("Test message {}", 2); - logger->flush(); + REQUIRE(file_contents(filename) == std::string("Should not be flushed\nTest message 1\nTest message 2\n")); REQUIRE(count_lines(filename) == 3); } diff --git a/tests/includes.h b/tests/includes.h index c9056a02..bce67418 100644 --- a/tests/includes.h +++ b/tests/includes.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include diff --git a/tests/install_libcxx.sh b/tests/install_libcxx.sh deleted file mode 100755 index cee97692..00000000 --- a/tests/install_libcxx.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -# -# Install libc++ under travis - -svn --quiet co http://llvm.org/svn/llvm-project/libcxx/trunk libcxx -mkdir libcxx/build -(cd libcxx/build && cmake .. -DLIBCXX_CXX_ABI=libstdc++ -DLIBCXX_CXX_ABI_INCLUDE_PATHS="/usr/include/c++/4.6;/usr/include/c++/4.6/x86_64-linux-gnu") -make -C libcxx/build cxx -j2 -sudo cp libcxx/build/lib/libc++.so.1.0 /usr/lib/ -sudo cp -r libcxx/build/include/c++/v1 /usr/include/c++/v1/ -sudo ln -sf /usr/lib/libc++.so.1.0 /usr/lib/libc++.so -sudo ln -sf /usr/lib/libc++.so.1.0 /usr/lib/libc++.so.1 diff --git a/tests/registry.cpp b/tests/registry.cpp index ff84cacf..1b841a4c 100644 --- a/tests/registry.cpp +++ b/tests/registry.cpp @@ -9,7 +9,7 @@ TEST_CASE("register_drop", "[registry]") spdlog::create(tested_logger_name); REQUIRE(spdlog::get(tested_logger_name) != nullptr); // Throw if registring existing name - REQUIRE_THROWS_AS(spdlog::create(tested_logger_name), spdlog::spdlog_ex); + REQUIRE_THROWS_AS(spdlog::create(tested_logger_name), const spdlog::spdlog_ex &); } TEST_CASE("explicit register" @@ -20,7 +20,7 @@ TEST_CASE("explicit register" spdlog::register_logger(logger); REQUIRE(spdlog::get(tested_logger_name) != nullptr); // Throw if registring existing name - REQUIRE_THROWS_AS(spdlog::create(tested_logger_name), spdlog::spdlog_ex); + REQUIRE_THROWS_AS(spdlog::create(tested_logger_name), const spdlog::spdlog_ex &); } TEST_CASE("apply_all" @@ -33,7 +33,7 @@ TEST_CASE("apply_all" spdlog::register_logger(logger2); int counter = 0; - spdlog::apply_all([&counter](std::shared_ptr l) { counter++; }); + spdlog::apply_all([&counter](std::shared_ptr) { counter++; }); REQUIRE(counter == 2); counter = 0; @@ -62,7 +62,7 @@ TEST_CASE("drop_all" spdlog::create(tested_logger_name2); spdlog::drop_all(); REQUIRE_FALSE(spdlog::get(tested_logger_name)); - REQUIRE_FALSE(spdlog::get(tested_logger_name)); + REQUIRE_FALSE(spdlog::get(tested_logger_name2)); } TEST_CASE("drop non existing" diff --git a/tests/test_async.cpp b/tests/test_async.cpp index 112fede7..6f86cf6c 100644 --- a/tests/test_async.cpp +++ b/tests/test_async.cpp @@ -7,6 +7,7 @@ TEST_CASE("basic async test ", "[async]") { using namespace spdlog; auto test_sink = std::make_shared(); + size_t overrun_counter = 0; size_t queue_size = 128; size_t messages = 256; { @@ -17,17 +18,20 @@ TEST_CASE("basic async test ", "[async]") logger->info("Hello message #{}", i); } logger->flush(); + overrun_counter = tp->overrun_counter(); } REQUIRE(test_sink->msg_counter() == messages); REQUIRE(test_sink->flush_counter() == 1); + REQUIRE(overrun_counter == 0); } TEST_CASE("discard policy ", "[async]") { using namespace spdlog; auto test_sink = std::make_shared(); - size_t queue_size = 2; - size_t messages = 10240; + test_sink->set_delay(std::chrono::milliseconds(1)); + size_t queue_size = 4; + size_t messages = 1024; auto tp = std::make_shared(queue_size, 1); auto logger = std::make_shared("as", test_sink, tp, async_overflow_policy::overrun_oldest); @@ -36,22 +40,26 @@ TEST_CASE("discard policy ", "[async]") logger->info("Hello message"); } REQUIRE(test_sink->msg_counter() < messages); + REQUIRE(tp->overrun_counter() > 0); } TEST_CASE("discard policy using factory ", "[async]") { using namespace spdlog; - size_t queue_size = 2; - size_t messages = 10240; + size_t queue_size = 4; + size_t messages = 1024; spdlog::init_thread_pool(queue_size, 1); auto logger = spdlog::create_async_nb("as2"); + auto test_sink = std::static_pointer_cast(logger->sinks()[0]); + test_sink->set_delay(std::chrono::milliseconds(1)); + for (size_t i = 0; i < messages; i++) { logger->info("Hello message"); } - auto sink = std::static_pointer_cast(logger->sinks()[0]); - REQUIRE(sink->msg_counter() < messages); + + REQUIRE(test_sink->msg_counter() < messages); spdlog::drop_all(); } diff --git a/tests/test_misc.cpp b/tests/test_misc.cpp index 2de4fb26..f1fb80e9 100644 --- a/tests/test_misc.cpp +++ b/tests/test_misc.cpp @@ -91,3 +91,46 @@ TEST_CASE("periodic flush", "[periodic_flush]") spdlog::flush_every(std::chrono::seconds(0)); spdlog::drop_all(); } + +TEST_CASE("clone", "[clone]") +{ + using namespace spdlog; + + auto logger = spdlog::create("orig"); + auto cloned = logger->clone("clone"); + + REQUIRE(cloned->name() == "clone"); + REQUIRE(logger->sinks() == cloned->sinks()); + REQUIRE(logger->level() == cloned->level()); + REQUIRE(logger->flush_level() == cloned->flush_level()); + logger->info("Some message 1"); + cloned->info("Some message 2"); + + auto test_sink = std::static_pointer_cast(cloned->sinks()[0]); + REQUIRE(test_sink->msg_counter() == 2); + + spdlog::drop_all(); +} + +TEST_CASE("clone async", "[clone]") +{ + using namespace spdlog; + + auto logger = spdlog::create_async("orig"); + auto cloned = logger->clone("clone"); + + REQUIRE(cloned->name() == "clone"); + REQUIRE(logger->sinks() == cloned->sinks()); + REQUIRE(logger->level() == cloned->level()); + REQUIRE(logger->flush_level() == cloned->flush_level()); + + logger->info("Some message 1"); + cloned->info("Some message 2"); + + spdlog::details::os::sleep_for_millis(10); + + auto test_sink = std::static_pointer_cast(cloned->sinks()[0]); + REQUIRE(test_sink->msg_counter() == 2); + + spdlog::drop_all(); +} diff --git a/tests/test_mpmc_q.cpp b/tests/test_mpmc_q.cpp new file mode 100644 index 00000000..7c56496d --- /dev/null +++ b/tests/test_mpmc_q.cpp @@ -0,0 +1,107 @@ +#include "includes.h" + +using namespace std::chrono; +using std::chrono::milliseconds; +using test_clock = std::chrono::high_resolution_clock; + +static milliseconds millis_from(const test_clock::time_point &tp0) +{ + return std::chrono::duration_cast(test_clock::now() - tp0); +} +TEST_CASE("dequeue-empty-nowait", "[mpmc_blocking_q]") +{ + size_t q_size = 100; + milliseconds tolerance_wait(10); + spdlog::details::mpmc_blocking_queue q(q_size); + int popped_item; + + auto start = test_clock::now(); + auto rv = q.dequeue_for(popped_item, milliseconds::zero()); + auto delta_ms = millis_from(start); + + REQUIRE(rv == false); + INFO("Delta " << delta_ms.count() << " millis"); + REQUIRE(delta_ms <= tolerance_wait); +} + +TEST_CASE("dequeue-empty-wait", "[mpmc_blocking_q]") +{ + + size_t q_size = 100; + milliseconds wait_ms(250); + milliseconds tolerance_wait(100); + + spdlog::details::mpmc_blocking_queue q(q_size); + int popped_item; + auto start = test_clock::now(); + auto rv = q.dequeue_for(popped_item, wait_ms); + auto delta_ms = millis_from(start); + + REQUIRE(rv == false); + + INFO("Delta " << delta_ms.count() << " millis"); + REQUIRE(delta_ms >= wait_ms); + REQUIRE(delta_ms <= wait_ms + tolerance_wait); +} + +TEST_CASE("enqueue_nowait", "[mpmc_blocking_q]") +{ + + size_t q_size = 1; + spdlog::details::mpmc_blocking_queue q(q_size); + milliseconds tolerance_wait(10); + + q.enqueue(1); + REQUIRE(q.overrun_counter() == 0); + + auto start = test_clock::now(); + q.enqueue_nowait(2); + auto delta_ms = millis_from(start); + + INFO("Delta " << delta_ms.count() << " millis"); + REQUIRE(delta_ms <= tolerance_wait); + REQUIRE(q.overrun_counter() == 1); +} + +TEST_CASE("bad_queue", "[mpmc_blocking_q]") +{ + size_t q_size = 0; + spdlog::details::mpmc_blocking_queue q(q_size); + q.enqueue_nowait(1); + REQUIRE(q.overrun_counter() == 1); + int i; + REQUIRE(q.dequeue_for(i, milliseconds(0)) == false); +} + +TEST_CASE("empty_queue", "[mpmc_blocking_q]") +{ + size_t q_size = 10; + spdlog::details::mpmc_blocking_queue q(q_size); + int i; + REQUIRE(q.dequeue_for(i, milliseconds(10)) == false); +} + +TEST_CASE("full_queue", "[mpmc_blocking_q]") +{ + size_t q_size = 100; + spdlog::details::mpmc_blocking_queue q(q_size); + for (int i = 0; i < static_cast(q_size); i++) + { + q.enqueue(std::move(i)); + } + + q.enqueue_nowait(123456); + REQUIRE(q.overrun_counter() == 1); + + for (int i = 1; i < static_cast(q_size); i++) + { + int item = -1; + q.dequeue_for(item, milliseconds(0)); + REQUIRE(item == i); + } + + // last item pushed has overridden the oldest. + int item = -1; + q.dequeue_for(item, milliseconds(0)); + REQUIRE(item == 123456); +} \ No newline at end of file diff --git a/tests/test_sink.h b/tests/test_sink.h index 101c15f1..ae08e980 100644 --- a/tests/test_sink.h +++ b/tests/test_sink.h @@ -21,11 +21,13 @@ class test_sink : public base_sink public: size_t msg_counter() { + std::lock_guard lock(base_sink::mutex_); return msg_counter_; } size_t flush_counter() { + std::lock_guard lock(base_sink::mutex_); return flush_counter_; } diff --git a/tests/tests.sln b/tests/tests.sln index 319bf954..511c99c8 100644 --- a/tests/tests.sln +++ b/tests/tests.sln @@ -56,7 +56,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sinks", "sinks", "{093AE34A ..\include\spdlog\sinks\ansicolor_sink.h = ..\include\spdlog\sinks\ansicolor_sink.h ..\include\spdlog\sinks\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 @@ -65,7 +64,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sinks", "sinks", "{093AE34A ..\include\spdlog\sinks\stdout_sinks.h = ..\include\spdlog\sinks\stdout_sinks.h ..\include\spdlog\sinks\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