From 83c9ede9e62af703be4289cc6102bc76e1bff7c9 Mon Sep 17 00:00:00 2001 From: Gabi Melman Date: Sun, 5 Jan 2025 02:17:31 +0200 Subject: [PATCH] Asink sink (#3309) Replace async logger with async sink --- CMakeLists.txt | 291 +++++++++---------- README.md | 24 +- bench/async_bench.cpp | 62 ++-- bench/bench.cpp | 13 +- bench/latency.cpp | 33 ++- example/example.cpp | 43 ++- include/spdlog/async.h | 85 ------ include/spdlog/async_logger.h | 69 ----- include/spdlog/{fmt => }/bin_to_hex.h | 2 +- include/spdlog/common.h | 9 +- include/spdlog/details/async_log_msg.h | 37 +++ include/spdlog/details/circular_q.h | 2 - include/spdlog/details/context.h | 59 ---- include/spdlog/details/err_helper.h | 23 ++ include/spdlog/details/fmt_helper.h | 1 - include/spdlog/details/log_msg_buffer.h | 28 -- include/spdlog/details/mpmc_blocking_q.h | 2 +- include/spdlog/details/periodic_worker.h | 57 ---- include/spdlog/details/synchronous_factory.h | 21 -- include/spdlog/details/thread_pool.h | 112 ------- include/spdlog/fmt/fmt.h | 9 - include/spdlog/logger.h | 67 ++--- include/spdlog/sinks/android_sink.h | 14 - include/spdlog/sinks/ansicolor_sink.h | 1 - include/spdlog/sinks/async_sink.h | 86 ++++++ include/spdlog/sinks/base_sink.h | 1 + include/spdlog/sinks/basic_file_sink.h | 21 -- include/spdlog/sinks/callback_sink.h | 15 - include/spdlog/sinks/daily_file_sink.h | 52 ---- include/spdlog/sinks/dist_sink.h | 6 +- include/spdlog/sinks/hourly_file_sink.h | 23 -- include/spdlog/sinks/kafka_sink.h | 23 -- include/spdlog/sinks/mongo_sink.h | 25 +- include/spdlog/sinks/msvc_sink.h | 1 - include/spdlog/sinks/null_sink.h | 16 - include/spdlog/sinks/qt_sinks.h | 68 +---- include/spdlog/sinks/ringbuffer_sink.h | 10 +- include/spdlog/sinks/rotating_file_sink.h | 32 +- include/spdlog/sinks/sink.h | 6 +- include/spdlog/sinks/stdout_color_sinks.h | 18 +- include/spdlog/sinks/stdout_sinks.h | 16 +- include/spdlog/sinks/syslog_sink.h | 26 +- include/spdlog/sinks/systemd_sink.h | 17 +- include/spdlog/sinks/tcp_sink.h | 2 +- include/spdlog/sinks/udp_sink.h | 13 +- include/spdlog/sinks/wincolor_sink.h | 2 +- include/spdlog/spdlog.h | 10 +- include/spdlog/stopwatch.h | 2 +- src/async_logger.cpp | 79 ----- src/details/async_log_msg.cpp | 61 ++++ src/details/context.cpp | 49 ---- src/details/err_helper.cpp | 40 +++ src/details/log_msg_buffer.cpp | 54 ---- src/details/thread_pool.cpp | 117 -------- src/logger.cpp | 34 +-- src/pattern_formatter.cpp | 1 - src/sinks/ansicolor_sink.cpp | 4 +- src/sinks/async_sink.cpp | 130 +++++++++ src/sinks/base_sink.cpp | 27 +- src/sinks/basic_file_sink.cpp | 4 +- src/sinks/rotating_file_sink.cpp | 3 +- src/sinks/sink.cpp | 14 - src/sinks/stdout_color_sinks.cpp | 57 ---- src/sinks/stdout_sinks.cpp | 51 +--- src/sinks/wincolor_sink.cpp | 11 +- src/spdlog.cpp | 21 +- tests/includes.h | 1 - tests/test_async.cpp | 261 ++++++++++++----- tests/test_bin_to_hex.cpp | 3 +- tests/test_custom_callbacks.cpp | 1 - tests/test_errors.cpp | 83 ++---- tests/test_file_helper.cpp | 2 +- tests/test_file_logging.cpp | 21 +- tests/test_macros.cpp | 4 +- tests/test_misc.cpp | 35 +-- tests/test_mpmc_q.cpp | 1 + tests/test_ringbuffer_sink.cpp | 4 +- tests/test_sink.h | 21 +- tests/test_stdout_api.cpp | 12 +- tests/test_time_point.cpp | 1 - tests/utils.cpp | 9 +- tests/utils.h | 4 +- 82 files changed, 986 insertions(+), 1789 deletions(-) delete mode 100644 include/spdlog/async.h delete mode 100644 include/spdlog/async_logger.h rename include/spdlog/{fmt => }/bin_to_hex.h (99%) create mode 100644 include/spdlog/details/async_log_msg.h delete mode 100644 include/spdlog/details/context.h create mode 100644 include/spdlog/details/err_helper.h delete mode 100644 include/spdlog/details/log_msg_buffer.h delete mode 100644 include/spdlog/details/periodic_worker.h delete mode 100644 include/spdlog/details/synchronous_factory.h delete mode 100644 include/spdlog/details/thread_pool.h delete mode 100644 include/spdlog/fmt/fmt.h create mode 100644 include/spdlog/sinks/async_sink.h delete mode 100644 src/async_logger.cpp create mode 100644 src/details/async_log_msg.cpp delete mode 100644 src/details/context.cpp create mode 100644 src/details/err_helper.cpp delete mode 100644 src/details/log_msg_buffer.cpp delete mode 100644 src/details/thread_pool.cpp create mode 100644 src/sinks/async_sink.cpp delete mode 100644 src/sinks/sink.cpp delete mode 100644 src/sinks/stdout_color_sinks.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 64c81f70f..81df3bf88 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,38 +19,38 @@ set_property(GLOBAL PROPERTY USE_FOLDERS ON) # --------------------------------------------------------------------------------------- # Set default build to release # --------------------------------------------------------------------------------------- -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) +if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE) -endif() +endif () # --------------------------------------------------------------------------------------- # Compiler config # --------------------------------------------------------------------------------------- # c++ standard >=17 is required -if(NOT DEFINED CMAKE_CXX_STANDARD) +if (NOT DEFINED CMAKE_CXX_STANDARD) set(CMAKE_CXX_STANDARD 17) -elseif(CMAKE_CXX_STANDARD LESS 17) +elseif (CMAKE_CXX_STANDARD LESS 17) message(FATAL_ERROR "Minimum supported CMAKE_CXX_STANDARD is 17, but it is set to ${CMAKE_CXX_STANDARD}") -endif() +endif () set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN" OR CMAKE_SYSTEM_NAME MATCHES "MSYS" OR CMAKE_SYSTEM_NAME MATCHES "MINGW") +if (CMAKE_SYSTEM_NAME MATCHES "CYGWIN" OR CMAKE_SYSTEM_NAME MATCHES "MSYS" OR CMAKE_SYSTEM_NAME MATCHES "MINGW") set(CMAKE_CXX_EXTENSIONS ON) -endif() +endif () # --------------------------------------------------------------------------------------- # Set SPDLOG_MASTER_PROJECT to ON if we are building spdlog # --------------------------------------------------------------------------------------- # 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) +if (NOT DEFINED SPDLOG_MASTER_PROJECT) + if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) set(SPDLOG_MASTER_PROJECT ON) - else() + else () set(SPDLOG_MASTER_PROJECT OFF) - endif() -endif() + endif () +endif () option(SPDLOG_BUILD_ALL "Build all artifacts" OFF) @@ -77,11 +77,11 @@ option(SPDLOG_SYSTEM_INCLUDES "Include as system headers (skip for clang-tidy)." option(SPDLOG_INSTALL "Generate the install target" ${SPDLOG_MASTER_PROJECT}) option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of of fetching from gitub." OFF) -if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") +if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") option(SPDLOG_CLOCK_COARSE "Use CLOCK_REALTIME_COARSE instead of the regular clock," OFF) -else() +else () set(SPDLOG_CLOCK_COARSE OFF CACHE BOOL "non supported option" FORCE) -endif() +endif () option(SPDLOG_PREVENT_CHILD_FD "Prevent from child processes to inherit log file descriptors" OFF) option(SPDLOG_NO_THREAD_ID "prevent spdlog from querying the thread id on each log call if thread id is not needed" OFF) @@ -91,18 +91,18 @@ option(SPDLOG_NO_TLS "Disable thread local storage" OFF) # clang-tidy option(SPDLOG_TIDY "run clang-tidy" OFF) -if(SPDLOG_TIDY) +if (SPDLOG_TIDY) set(CMAKE_CXX_CLANG_TIDY "clang-tidy") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) message(STATUS "Enabled clang-tidy") -endif() +endif () -if(SPDLOG_BUILD_SHARED) +if (SPDLOG_BUILD_SHARED) set(BUILD_SHARED_LIBS ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) -endif() +endif () -if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") +if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") # place dlls and libs and executables in the same directory set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/$) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/$) @@ -113,7 +113,7 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") add_compile_options(/Zc:__cplusplus) # enable parallel build for the solution add_compile_options(/MP) -endif() +endif () message(STATUS "spdlog version: ${SPDLOG_VERSION}") message(STATUS "spdlog build type: " ${CMAKE_BUILD_TYPE}) @@ -123,12 +123,12 @@ message(STATUS "spdlog fmt external: " ${SPDLOG_FMT_EXTERNAL}) # --------------------------------------------------------------------------------------- # Find {fmt} library # --------------------------------------------------------------------------------------- -if(SPDLOG_FMT_EXTERNAL) +if (SPDLOG_FMT_EXTERNAL) find_package(fmt REQUIRED) message(STATUS "Using external fmt lib version: ${fmt_VERSION}") -else() +else () include(cmake/fmtlib.cmake) -endif() +endif () # --------------------------------------------------------------------------------------- # Threads library is required @@ -139,207 +139,198 @@ find_package(Threads REQUIRED) # Library sources # --------------------------------------------------------------------------------------- set(SPDLOG_HEADERS - "include/spdlog/async.h" - "include/spdlog/async_logger.h" - "include/spdlog/common.h" - "include/spdlog/formatter.h" - "include/spdlog/fwd.h" - "include/spdlog/logger.h" - "include/spdlog/pattern_formatter.h" - "include/spdlog/source_loc.h" - "include/spdlog/spdlog.h" - "include/spdlog/stopwatch.h" - "include/spdlog/version.h" - "include/spdlog/details/circular_q.h" - "include/spdlog/details/file_helper.h" - "include/spdlog/details/fmt_helper.h" - "include/spdlog/details/log_msg.h" - "include/spdlog/details/log_msg_buffer.h" - "include/spdlog/details/mpmc_blocking_q.h" - "include/spdlog/details/null_mutex.h" - "include/spdlog/details/os.h" - "include/spdlog/details/periodic_worker.h" - "include/spdlog/details/context.h" - "include/spdlog/details/synchronous_factory.h" - "include/spdlog/details/thread_pool.h" - "include/spdlog/fmt/bin_to_hex.h" - "include/spdlog/fmt/fmt.h" - "include/spdlog/sinks/android_sink.h" - "include/spdlog/sinks/base_sink.h" - "include/spdlog/sinks/basic_file_sink.h" - "include/spdlog/sinks/callback_sink.h" - "include/spdlog/sinks/daily_file_sink.h" - "include/spdlog/sinks/dist_sink.h" - "include/spdlog/sinks/dup_filter_sink.h" - "include/spdlog/sinks/hourly_file_sink.h" - "include/spdlog/sinks/kafka_sink.h" - "include/spdlog/sinks/mongo_sink.h" - "include/spdlog/sinks/msvc_sink.h" - "include/spdlog/sinks/null_sink.h" - "include/spdlog/sinks/ostream_sink.h" - "include/spdlog/sinks/qt_sinks.h" - "include/spdlog/sinks/ringbuffer_sink.h" - "include/spdlog/sinks/rotating_file_sink.h" - "include/spdlog/sinks/sink.h" - "include/spdlog/sinks/stdout_color_sinks.h" - "include/spdlog/sinks/stdout_sinks.h" - "include/spdlog/sinks/syslog_sink.h" - "include/spdlog/sinks/systemd_sink.h" - "include/spdlog/sinks/tcp_sink.h" - "include/spdlog/sinks/udp_sink.h") + "include/spdlog/common.h" + "include/spdlog/formatter.h" + "include/spdlog/fwd.h" + "include/spdlog/logger.h" + "include/spdlog/pattern_formatter.h" + "include/spdlog/source_loc.h" + "include/spdlog/spdlog.h" + "include/spdlog/stopwatch.h" + "include/spdlog/version.h" + "include/spdlog/details/circular_q.h" + "include/spdlog/details/file_helper.h" + "include/spdlog/details/fmt_helper.h" + "include/spdlog/details/log_msg.h" + "include/spdlog/details/async_log_msg.h" + "include/spdlog/details/mpmc_blocking_q.h" + "include/spdlog/details/null_mutex.h" + "include/spdlog/details/os.h" + "include/spdlog/details/err_helper.h" + "include/spdlog/bin_to_hex.h" + "include/spdlog/sinks/android_sink.h" + "include/spdlog/sinks/base_sink.h" + "include/spdlog/sinks/basic_file_sink.h" + "include/spdlog/sinks/callback_sink.h" + "include/spdlog/sinks/daily_file_sink.h" + "include/spdlog/sinks/dist_sink.h" + "include/spdlog/sinks/dup_filter_sink.h" + "include/spdlog/sinks/hourly_file_sink.h" + "include/spdlog/sinks/kafka_sink.h" + "include/spdlog/sinks/mongo_sink.h" + "include/spdlog/sinks/msvc_sink.h" + "include/spdlog/sinks/null_sink.h" + "include/spdlog/sinks/ostream_sink.h" + "include/spdlog/sinks/qt_sinks.h" + "include/spdlog/sinks/ringbuffer_sink.h" + "include/spdlog/sinks/rotating_file_sink.h" + "include/spdlog/sinks/sink.h" + "include/spdlog/sinks/stdout_color_sinks.h" + "include/spdlog/sinks/stdout_sinks.h" + "include/spdlog/sinks/syslog_sink.h" + "include/spdlog/sinks/systemd_sink.h" + "include/spdlog/sinks/tcp_sink.h" + "include/spdlog/sinks/udp_sink.h" + "include/spdlog/sinks/async_sink.h") set(SPDLOG_SRCS - "src/async_logger.cpp" - "src/common.cpp" - "src/logger.cpp" - "src/pattern_formatter.cpp" - "src/spdlog.cpp" - "src/details/file_helper.cpp" - "src/details/os_filesystem.cpp" - "src/details/log_msg.cpp" - "src/details/log_msg_buffer.cpp" - "src/details/context.cpp" - "src/details/thread_pool.cpp" - "src/sinks/base_sink.cpp" - "src/sinks/basic_file_sink.cpp" - "src/sinks/rotating_file_sink.cpp" - "src/sinks/sink.cpp" - "src/sinks/stdout_color_sinks.cpp" - "src/sinks/stdout_sinks.cpp") - -if(WIN32) + "src/common.cpp" + "src/logger.cpp" + "src/pattern_formatter.cpp" + "src/spdlog.cpp" + "src/details/file_helper.cpp" + "src/details/os_filesystem.cpp" + "src/details/log_msg.cpp" + "src/details/async_log_msg.cpp" + "src/details/err_helper.cpp" + "src/sinks/base_sink.cpp" + "src/sinks/basic_file_sink.cpp" + "src/sinks/rotating_file_sink.cpp" + "src/sinks/stdout_sinks.cpp" + "src/sinks/async_sink.cpp") + +if (WIN32) list(APPEND SPDLOG_SRCS "src/details/os_windows.cpp" "src/sinks/wincolor_sink.cpp") - list( - APPEND SPDLOG_HEADERS + list(APPEND SPDLOG_HEADERS "include/spdlog/sinks/wincolor_sink.h" "include/spdlog/details/tcp_client_windows.h" "include/spdlog/details/udp_client_windows.h" "include/spdlog/details/windows_include.h" "include/spdlog/sinks/win_eventlog_sink.h") -else() +else () list(APPEND SPDLOG_SRCS "src/details/os_unix.cpp" "src/sinks/ansicolor_sink.cpp") list(APPEND SPDLOG_HEADERS - "include/spdlog/details/tcp_client_unix.h" - "include/spdlog/details/udp_client_unix.h" - "include/spdlog/sinks/ansicolor_sink.h") -endif() + "include/spdlog/details/tcp_client_unix.h" + "include/spdlog/details/udp_client_unix.h" + "include/spdlog/sinks/ansicolor_sink.h") +endif () # --------------------------------------------------------------------------------------- # Check if fwrite_unlocked/_fwrite_nolock is available # --------------------------------------------------------------------------------------- include(CheckSymbolExists) -if(WIN32) +if (WIN32) check_symbol_exists(_fwrite_nolock "stdio.h" HAVE_FWRITE_UNLOCKED) -else() +else () check_symbol_exists(fwrite_unlocked "stdio.h" HAVE_FWRITE_UNLOCKED) -endif() -if(HAVE_FWRITE_UNLOCKED) +endif () +if (HAVE_FWRITE_UNLOCKED) set(SPDLOG_FWRITE_UNLOCKED 1) -endif() +endif () # --------------------------------------------------------------------------------------- # spdlog library # --------------------------------------------------------------------------------------- -if(BUILD_SHARED_LIBS) - if(WIN32) +if (BUILD_SHARED_LIBS) + if (WIN32) set(VERSION_RC ${CMAKE_CURRENT_BINARY_DIR}/version.rc) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY) - endif() + endif () add_library(spdlog SHARED ${VERSION_RC}) target_compile_definitions(spdlog PUBLIC SPDLOG_SHARED_LIB) - if(MSVC) + if (MSVC) # disable dlls related warnings on msvc target_compile_options(spdlog PUBLIC $<$,$>>:/wd4251 - /wd4275>) - endif() -else() + /wd4275>) + endif () +else () add_library(spdlog STATIC) -endif() +endif () add_library(spdlog::spdlog ALIAS spdlog) target_sources(spdlog PRIVATE ${SPDLOG_SRCS}) target_sources( - spdlog - PUBLIC FILE_SET pub_headers - TYPE HEADERS - BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include - FILES ${SPDLOG_HEADERS}) + spdlog + PUBLIC FILE_SET pub_headers + TYPE HEADERS + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include + FILES ${SPDLOG_HEADERS}) set(SPDLOG_INCLUDES_LEVEL "") -if(SPDLOG_SYSTEM_INCLUDES) +if (SPDLOG_SYSTEM_INCLUDES) set(SPDLOG_INCLUDES_LEVEL "SYSTEM") -endif() +endif () target_include_directories(spdlog ${SPDLOG_INCLUDES_LEVEL} PUBLIC "$" - "$") + "$") target_link_libraries(spdlog PUBLIC Threads::Threads) target_link_libraries(spdlog PUBLIC fmt::fmt) spdlog_enable_warnings(spdlog) set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION - ${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR}) + ${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR}) set(SPDLOG_NAME spdlog-${SPDLOG_VERSION_MAJOR}) -set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX "-${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR}d") +set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX "-${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR}d") # --------------------------------------------------------------------------------------- # set source groups for visual studio # --------------------------------------------------------------------------------------- -if(CMAKE_GENERATOR MATCHES "Visual Studio") +if (CMAKE_GENERATOR MATCHES "Visual Studio") source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/include PREFIX include FILES ${SPDLOG_HEADERS}) source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/src PREFIX sources FILES ${SPDLOG_SRCS}) source_group(sources FILES ${VERSION_RC}) -endif() +endif () # --------------------------------------------------------------------------------------- # Add required libraries for Android CMake build # --------------------------------------------------------------------------------------- -if(ANDROID) +if (ANDROID) target_link_libraries(spdlog PUBLIC log) -endif() +endif () # --------------------------------------------------------------------------------------- # spdlog private defines according to the options # --------------------------------------------------------------------------------------- -foreach(SPDLOG_OPTION - SPDLOG_CLOCK_COARSE - SPDLOG_PREVENT_CHILD_FD - SPDLOG_NO_THREAD_ID - SPDLOG_DISABLE_GLOBAL_LOGGER - SPDLOG_NO_TLS - SPDLOG_FWRITE_UNLOCKED) - if(${SPDLOG_OPTION}) +foreach (SPDLOG_OPTION + SPDLOG_CLOCK_COARSE + SPDLOG_PREVENT_CHILD_FD + SPDLOG_NO_THREAD_ID + SPDLOG_DISABLE_GLOBAL_LOGGER + SPDLOG_NO_TLS + SPDLOG_FWRITE_UNLOCKED) + if (${SPDLOG_OPTION}) target_compile_definitions(spdlog PRIVATE ${SPDLOG_OPTION}) - endif() -endforeach() + endif () +endforeach () # --------------------------------------------------------------------------------------- # Build binaries # --------------------------------------------------------------------------------------- -if(SPDLOG_BUILD_EXAMPLE OR SPDLOG_BUILD_ALL) +if (SPDLOG_BUILD_EXAMPLE OR SPDLOG_BUILD_ALL) message(STATUS "Generating example(s)") add_subdirectory(example) spdlog_enable_warnings(example) -endif() +endif () -if(SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_ALL) +if (SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_ALL) message(STATUS "Generating tests") enable_testing() add_subdirectory(tests) -endif() +endif () -if(SPDLOG_BUILD_BENCH OR SPDLOG_BUILD_ALL) +if (SPDLOG_BUILD_BENCH OR SPDLOG_BUILD_ALL) message(STATUS "Generating benchmarks") add_subdirectory(bench) -endif() +endif () # --------------------------------------------------------------------------------------- # Install # --------------------------------------------------------------------------------------- -if(SPDLOG_INSTALL) +if (SPDLOG_INSTALL) message(STATUS "Generating install") set(project_config_in "${CMAKE_CURRENT_LIST_DIR}/cmake/spdlogConfig.cmake.in") set(project_config_out "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfig.cmake") @@ -352,13 +343,13 @@ if(SPDLOG_INSTALL) # --------------------------------------------------------------------------------------- set(installed_include_dir "${CMAKE_INSTALL_INCLUDEDIR}/${SPDLOG_NAME}") install( - TARGETS spdlog - EXPORT spdlogTargets - LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/${SPDLOG_NAME}" - ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/${SPDLOG_NAME}" - RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/${SPDLOG_NAME}" - FILE_SET pub_headers - DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${SPDLOG_NAME}") + TARGETS spdlog + EXPORT spdlogTargets + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/${SPDLOG_NAME}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/${SPDLOG_NAME}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/${SPDLOG_NAME}" + FILE_SET pub_headers + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${SPDLOG_NAME}") message(STATUS "Installing spdlog in ${CMAKE_INSTALL_LIBDIR}/${SPDLOG_NAME}") # --------------------------------------------------------------------------------------- @@ -376,4 +367,4 @@ if(SPDLOG_INSTALL) # Support creation of installable packages # --------------------------------------------------------------------------------------- include(cmake/spdlogCPack.cmake) -endif() +endif () diff --git a/README.md b/README.md index 31c845bb3..89c83611c 100644 --- a/README.md +++ b/README.md @@ -245,33 +245,11 @@ void callback_example() #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"); + // TODO } ``` ---- -#### Asynchronous logger with multi sinks -```c++ -#include "spdlog/async.h" -#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++ diff --git a/bench/async_bench.cpp b/bench/async_bench.cpp index 4eb6ce4a7..183703285 100644 --- a/bench/async_bench.cpp +++ b/bench/async_bench.cpp @@ -8,11 +8,14 @@ // #include #include +#include #include #include #include +#include +#include -#include "spdlog/async.h" +#include "spdlog/sinks/async_sink.h" #include "spdlog/sinks/basic_file_sink.h" #include "spdlog/spdlog.h" @@ -23,21 +26,9 @@ using namespace spdlog::sinks; void bench_mt(int howmany, std::shared_ptr log, int thread_count); -#ifdef _MSC_VER - #pragma warning(push) - #pragma warning(disable : 4996) // disable fopen warning under msvc -#endif // _MSC_VER - int count_lines(const char *filename) { - int counter = 0; - auto *infile = fopen(filename, "r"); - int ch; - while (EOF != (ch = getc(infile))) { - if ('\n' == ch) counter++; - } - fclose(infile); - - return counter; + std::ifstream ifs(filename); + return std::count(std::istreambuf_iterator(ifs), std::istreambuf_iterator(), '\n'); } void verify_file(const char *filename, int expected_count) { @@ -54,7 +45,11 @@ void verify_file(const char *filename, int expected_count) { #pragma warning(pop) #endif +using namespace spdlog::sinks; + int main(int argc, char *argv[]) { + // setlocale to show thousands separators + std::locale::global(std::locale("en_US.UTF-8")); int howmany = 1000000; int queue_size = std::min(howmany + 2, 8192); int threads = 10; @@ -62,7 +57,7 @@ int main(int argc, char *argv[]) { try { spdlog::set_pattern("[%^%l%$] %v"); - if (argc == 1) { + if (argc > 1 && (std::string(argv[1]) == "-h" || std::string(argv[1]) == "--help")) { spdlog::info("Usage: {} ", argv[0]); return 0; } @@ -78,8 +73,13 @@ int main(int argc, char *argv[]) { } if (argc > 4) iters = atoi(argv[4]); + // validate all argc values + if (howmany < 1 || threads < 1 || queue_size < 1 || iters < 1) { + spdlog::error("Invalid input values"); + exit(1); + } - auto slot_size = sizeof(spdlog::details::async_msg); + auto slot_size = sizeof(details::async_log_msg); spdlog::info("-------------------------------------------------"); spdlog::info("Messages : {:L}", howmany); spdlog::info("Threads : {:L}", threads); @@ -94,14 +94,17 @@ int main(int argc, char *argv[]) { spdlog::info("Queue Overflow Policy: block"); spdlog::info("*********************************"); for (int i = 0; i < iters; i++) { - auto tp = std::make_shared(queue_size, 1); - auto file_sink = std::make_shared(filename, true); - auto logger = - std::make_shared("async_logger", std::move(file_sink), std::move(tp), async_overflow_policy::block); - bench_mt(howmany, std::move(logger), threads); - // verify_file(filename, howmany); + { + auto file_sink = std::make_shared(filename, true); + auto cfg = async_sink::config(); + cfg.queue_size = queue_size; + cfg.sinks.push_back(std::move(file_sink)); + auto async_sink = std::make_shared(cfg); + auto logger = std::make_shared("async_logger", std::move(async_sink)); + bench_mt(howmany, std::move(logger), threads); + } + //verify_file(filename, howmany); // in separate scope to ensure logger is destroyed and all logs were written } - spdlog::info(""); spdlog::info("*********************************"); spdlog::info("Queue Overflow Policy: overrun"); @@ -109,10 +112,13 @@ int main(int argc, char *argv[]) { // do same test but discard the oldest if queue is full instead of blocking filename = "logs/basic_async-overrun.log"; for (int i = 0; i < iters; i++) { - auto tp = std::make_shared(queue_size, 1); - auto file_sink = std::make_shared(filename, true); - auto logger = std::make_shared("async_logger", std::move(file_sink), std::move(tp), - async_overflow_policy::overrun_oldest); + async_sink::config cfg; + cfg.policy = async_sink::overflow_policy::overrun_oldest; + cfg.queue_size = queue_size; + auto file_sink = std::make_shared(filename, true); + cfg.sinks.push_back(std::move(file_sink)); + auto async_sink = std::make_shared(cfg); + auto logger = std::make_shared("async_logger", std::move(async_sink)); bench_mt(howmany, std::move(logger), threads); } spdlog::shutdown(); diff --git a/bench/bench.cpp b/bench/bench.cpp index 2885f4f97..42934576b 100644 --- a/bench/bench.cpp +++ b/bench/bench.cpp @@ -31,21 +31,22 @@ static const size_t file_size = 30 * 1024 * 1024; static const size_t rotating_files = 5; static const int max_threads = 1000; +using namespace spdlog::sinks; void bench_threaded_logging(size_t threads, int iters) { spdlog::info("**************************************************************"); spdlog::info( spdlog::fmt_lib::format(std::locale("en_US.UTF-8"), "Multi threaded: {:L} threads, {:L} messages", threads, iters)); spdlog::info("**************************************************************"); - auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true); + auto basic_mt = spdlog::create("basic_mt", "logs/basic_mt.log", true); bench_mt(iters, std::move(basic_mt), threads); spdlog::info(""); - auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files); + auto rotating_mt = spdlog::create("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files); bench_mt(iters, std::move(rotating_mt), threads); spdlog::info(""); - auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log"); + auto daily_mt = spdlog::create("daily_mt", "logs/daily_mt.log", 0, 1); bench_mt(iters, std::move(daily_mt), threads); spdlog::info(""); @@ -59,15 +60,15 @@ void bench_single_threaded(int iters) { spdlog::info(spdlog::fmt_lib::format(std::locale("en_US.UTF-8"), "Single threaded: {} messages", iters)); spdlog::info("**************************************************************"); - auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true); + auto basic_st = spdlog::create("basic_st", "logs/basic_st.log", true); bench(iters, std::move(basic_st)); spdlog::info(""); - auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files); + auto rotating_st = spdlog::create("rotating_st", "logs/rotating_st.log", file_size, rotating_files); bench(iters, std::move(rotating_st)); spdlog::info(""); - auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log"); + auto daily_st = spdlog::create("daily_st", "logs/daily_st.log", 0, 1); bench(iters, std::move(daily_st)); spdlog::info(""); diff --git a/bench/latency.cpp b/bench/latency.cpp index ea746d3a8..55e324fe2 100644 --- a/bench/latency.cpp +++ b/bench/latency.cpp @@ -8,13 +8,14 @@ // #include "benchmark/benchmark.h" -#include "spdlog/async.h" +#include "spdlog/sinks/async_sink.h" #include "spdlog/sinks/basic_file_sink.h" #include "spdlog/sinks/daily_file_sink.h" #include "spdlog/sinks/null_sink.h" #include "spdlog/sinks/rotating_file_sink.h" #include "spdlog/spdlog.h" +using namespace spdlog::sinks; void bench_c_string(benchmark::State &state, std::shared_ptr logger) { const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus " @@ -69,10 +70,10 @@ void bench_disabled_macro_global_logger(benchmark::State &state, std::shared_ptr #ifdef __linux__ void bench_dev_null() { - auto dev_null_st = spdlog::basic_logger_st("/dev/null_st", "/dev/null"); + auto dev_null_st = spdlog::create("/dev/null_st", "/dev/null"); benchmark::RegisterBenchmark("/dev/null_st", bench_logger, std::move(dev_null_st))->UseRealTime(); - auto dev_null_mt = spdlog::basic_logger_mt("/dev/null_mt", "/dev/null"); + auto dev_null_mt = spdlog::create("/dev/null_mt", "/dev/null"); benchmark::RegisterBenchmark("/dev/null_mt", bench_logger, std::move(dev_null_mt))->UseRealTime(); } @@ -85,7 +86,7 @@ static std::string prepare_null_loggers() { const std::string some_logger_name = "Some logger name"; const int null_logger_count = 9; for (int i = 0; i < null_logger_count; i++) { - spdlog::create(some_logger_name + std::to_string(i)); + spdlog::create(some_logger_name + std::to_string(i)); } return some_logger_name + std::to_string(null_logger_count / 2); } @@ -119,15 +120,15 @@ int main(int argc, char *argv[]) { if (full_bench) { // basic_st - auto basic_st = spdlog::basic_logger_st("basic_st", "latency_logs/basic_st.log", true); + auto basic_st = spdlog::create("basic_st", "latency_logs/basic_st.log", true); benchmark::RegisterBenchmark("basic_st", bench_logger, std::move(basic_st))->UseRealTime(); // rotating st - auto rotating_st = spdlog::rotating_logger_st("rotating_st", "latency_logs/rotating_st.log", file_size, rotating_files); + auto rotating_st = spdlog::create("rotating_st", "latency_logs/rotating_st.log", file_size, rotating_files); benchmark::RegisterBenchmark("rotating_st", bench_logger, std::move(rotating_st))->UseRealTime(); // daily st - auto daily_st = spdlog::daily_logger_mt("daily_st", "latency_logs/daily_st.log"); + auto daily_st = spdlog::create("daily_st", "latency_logs/daily_st.log", 0, 1); benchmark::RegisterBenchmark("daily_st", bench_logger, std::move(daily_st))->UseRealTime(); // @@ -137,23 +138,23 @@ int main(int argc, char *argv[]) { benchmark::RegisterBenchmark("null_sink_mt", bench_logger, null_logger_mt)->Threads(n_threads)->UseRealTime(); // basic_mt - auto basic_mt = spdlog::basic_logger_mt("basic_mt", "latency_logs/basic_mt.log", true); + auto basic_mt = spdlog::create("basic_mt", "latency_logs/basic_mt.log", true); benchmark::RegisterBenchmark("basic_mt", bench_logger, std::move(basic_mt))->Threads(n_threads)->UseRealTime(); // rotating mt - auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "latency_logs/rotating_mt.log", file_size, rotating_files); + auto rotating_mt = spdlog::create("rotating_mt", "latency_logs/rotating_mt.log", file_size, rotating_files); benchmark::RegisterBenchmark("rotating_mt", bench_logger, std::move(rotating_mt))->Threads(n_threads)->UseRealTime(); // daily mt - auto daily_mt = spdlog::daily_logger_mt("daily_mt", "latency_logs/daily_mt.log"); + auto daily_mt = spdlog::create("daily_mt", "latency_logs/daily_mt.log", 0, 1); benchmark::RegisterBenchmark("daily_mt", bench_logger, std::move(daily_mt))->Threads(n_threads)->UseRealTime(); } - - // async - auto queue_size = 1024 * 1024 * 3; - auto tp = std::make_shared(queue_size, 1); - auto async_logger = std::make_shared("async_logger", std::make_shared(), std::move(tp), - spdlog::async_overflow_policy::overrun_oldest); + using spdlog::sinks::async_sink; + async_sink::config config; + config.queue_size = 3 * 1024 * 1024;; + config.sinks.push_back(std::make_shared()); + config.policy = async_sink::overflow_policy::overrun_oldest; + auto async_logger = std::make_shared("async_logger", std::make_shared(config)); benchmark::RegisterBenchmark("async_logger", bench_logger, async_logger)->Threads(n_threads)->UseRealTime(); benchmark::Initialize(&argc, argv); diff --git a/example/example.cpp b/example/example.cpp index 032e62c53..f1dafa2cc 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -29,6 +29,8 @@ void replace_global_logger_example(); #include "spdlog/spdlog.h" #include "spdlog/version.h" +using namespace spdlog::sinks; + int main(int, char *[]) { spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH); spdlog::warn("Easy padding in numbers like {:08d}", 12); @@ -84,49 +86,44 @@ int main(int, char *[]) { // or #include "spdlog/sinks/stdout_sinks.h" if no colors needed. void stdout_logger_example() { // Create color multithreading logger. - auto console = spdlog::stdout_color_mt("console"); + auto console = spdlog::create("console"); // or for stderr: - // auto console = spdlog::stderr_color_mt("error-logger"); + //auto console = spdlog::create("console"); } #include "spdlog/sinks/basic_file_sink.h" void basic_example() { // Create basic file logger (not rotated). - auto my_logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt", true); + auto my_logger = spdlog::create("file_logger", "logs/basic-log.txt", true); } #include "spdlog/sinks/rotating_file_sink.h" void rotating_example() { // Create a file rotating logger with 5mb size max and 3 rotated files. - auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3); + auto rotating_logger = spdlog::create("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3); } #include "spdlog/sinks/daily_file_sink.h" void daily_example() { // Create a daily logger - a new file is created every day on 2:30am. - auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30); + auto daily_logger = spdlog::create("daily_logger", "logs/daily.txt", 2, 30); } #include "spdlog/sinks/callback_sink.h" void callback_example() { // Create the logger - auto logger = spdlog::callback_logger_mt("custom_callback_logger", [](const spdlog::details::log_msg & /*msg*/) { + auto logger = spdlog::create("custom_callback_logger", [](const spdlog::details::log_msg & /*msg*/) { // do what you need to do with msg }); } -#include "spdlog/async.h" +#include "spdlog/sinks/async_sink.h" void async_example() { - // Default thread pool settings can be modified *before* creating the async logger: - // spdlog::init_thread_pool(32768, 1); // queue with max 32k items 1 backing thread. - 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"); - + using spdlog::sinks::async_sink; + auto sink = async_sink::with("logs/async_log.txt", true); + auto logger = std::make_shared("async_logger", sink); for (int i = 1; i < 101; ++i) { - async_file->info("Async message #{}", i); + logger->info("Async message #{}", i); } } @@ -139,7 +136,7 @@ void async_example() { // {:p} - don't print the position on each line start. // {:n} - don't split the output to lines. -#include "spdlog/fmt/bin_to_hex.h" +#include "spdlog/bin_to_hex.h" void binary_example() { std::vector buf; for (int i = 0; i < 80; i++) { @@ -183,19 +180,19 @@ void stopwatch_example() { #include "spdlog/sinks/udp_sink.h" void udp_example() { - spdlog::sinks::udp_sink_config cfg("127.0.0.1", 11091); - auto my_logger = spdlog::udp_logger_mt("udplog", cfg); + udp_sink_config cfg("127.0.0.1", 11091); + auto my_logger = spdlog::create("udplog", cfg); my_logger->set_level(spdlog::level::debug); my_logger->info("hello world"); } // A logger with multiple sinks (stdout and file) - each with a different format and log level. void multi_sink_example() { - auto console_sink = std::make_shared(); + auto console_sink = std::make_shared(); console_sink->set_level(spdlog::level::warn); console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v"); - auto file_sink = std::make_shared("logs/multisink.txt", true); + auto file_sink = std::make_shared("logs/multisink.txt", true); file_sink->set_level(spdlog::level::trace); spdlog::logger logger("multi_sink", {console_sink, file_sink}); @@ -231,7 +228,7 @@ void err_handler_example() { #include "spdlog/sinks/syslog_sink.h" void syslog_example() { std::string ident = "spdlog-example"; - auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID); + auto syslog_logger = spdlog::create("syslog", ident, LOG_PID); syslog_logger->warn("This is warning that will end up in syslog."); } #endif @@ -291,7 +288,7 @@ void replace_global_logger_example() { // store the old logger so we don't break other examples. auto old_logger = spdlog::global_logger(); - auto new_logger = spdlog::basic_logger_mt("new_global_logger", "logs/new-default-log.txt", true); + auto new_logger = spdlog::create("new_global_logger", "logs/new-default-log.txt", true); spdlog::set_global_logger(new_logger); spdlog::set_level(spdlog::level::info); spdlog::debug("This message should not be displayed!"); diff --git a/include/spdlog/async.h b/include/spdlog/async.h deleted file mode 100644 index 8dcb54622..000000000 --- a/include/spdlog/async.h +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -// -// Async logging using global thread pool -// All loggers created here share same global thread pool. -// Each log message is pushed to a queue along with a shared pointer to the -// logger. -// If a logger deleted while having pending messages in the queue, it's actual -// destruction will defer -// until all its messages are processed by the thread pool. -// This is because each message in the queue holds a shared_ptr to the -// originating logger. - -#include -#include -#include - -#include "./async_logger.h" -#include "./details/context.h" -#include "./details/thread_pool.h" -#include "spdlog.h" - -namespace spdlog { -namespace details { -static constexpr size_t default_async_q_size = 8192; -} - -// async logger factory - creates async loggers backed with thread pool. -// if a global thread pool doesn't already exist, create it with default queue -// size of 8192 items and single thread. -template -struct async_factory_impl { - template - static std::shared_ptr create(std::string logger_name, SinkArgs &&...args) { - auto context = spdlog::context(); - // create global thread pool if not already exists - auto &mutex = context->tp_mutex(); - std::lock_guard tp_lock(mutex); - auto tp = context->get_tp(); - if (tp == nullptr) { - tp = std::make_shared(details::default_async_q_size, 1U); - context->set_tp(tp); - } - auto sink = std::make_shared(std::forward(args)...); - auto new_logger = std::make_shared(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy); - return new_logger; - } -}; - -using async_factory = async_factory_impl; -using async_factory_nonblock = async_factory_impl; - -template -std::shared_ptr create_async(std::string logger_name, SinkArgs &&...sink_args) { - return async_factory::create(std::move(logger_name), std::forward(sink_args)...); -} - -template -std::shared_ptr create_async_nb(std::string logger_name, SinkArgs &&...sink_args) { - return async_factory_nonblock::create(std::move(logger_name), std::forward(sink_args)...); -} - -// set global thread pool. -inline void init_thread_pool(size_t q_size, - size_t thread_count, - std::function on_thread_start, - std::function on_thread_stop) { - auto tp = std::make_shared(q_size, thread_count, on_thread_start, on_thread_stop); - spdlog::context()->set_tp(std::move(tp)); -} - -inline void init_thread_pool(size_t q_size, size_t thread_count, std::function on_thread_start) { - init_thread_pool(q_size, thread_count, on_thread_start, [] {}); -} - -inline void init_thread_pool(size_t q_size, size_t thread_count) { - init_thread_pool(q_size, thread_count, [] {}, [] {}); -} - -// get the global thread pool. -inline std::shared_ptr thread_pool() { return spdlog::context()->get_tp(); } -} // namespace spdlog diff --git a/include/spdlog/async_logger.h b/include/spdlog/async_logger.h deleted file mode 100644 index 591d3cfcc..000000000 --- a/include/spdlog/async_logger.h +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -// 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) -// Upon destruction, logs all remaining messages in the queue before -// destructing - -#include "./logger.h" - -namespace spdlog { - -// Async overflow policy - block by default. -enum class async_overflow_policy { - block, // Block until message can be enqueued - overrun_oldest, // Discard the oldest message in the queue if full when trying to - // add new item. - discard_new // Discard new message if the queue is full when trying to add new item. -}; - -namespace details { -class thread_pool; -} - -class SPDLOG_API async_logger final : public std::enable_shared_from_this, public logger { - friend class details::thread_pool; - -public: - template - async_logger(std::string logger_name, - It begin, - It end, - std::weak_ptr tp, - async_overflow_policy overflow_policy = async_overflow_policy::block) - : logger(std::move(logger_name), begin, end), - thread_pool_(std::move(tp)), - overflow_policy_(overflow_policy) {} - - 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_(const details::log_msg &msg) override; - void flush_() override; - void backend_sink_it_(const details::log_msg &incoming_log_msg); - void backend_flush_(); - -private: - std::weak_ptr thread_pool_; - async_overflow_policy overflow_policy_; -}; -} // namespace spdlog diff --git a/include/spdlog/fmt/bin_to_hex.h b/include/spdlog/bin_to_hex.h similarity index 99% rename from include/spdlog/fmt/bin_to_hex.h rename to include/spdlog/bin_to_hex.h index 86bfed1ec..08941b9e1 100644 --- a/include/spdlog/fmt/bin_to_hex.h +++ b/include/spdlog/bin_to_hex.h @@ -7,7 +7,7 @@ #include -#include "../common.h" +#include "common.h" #if defined(__has_include) #if __has_include() diff --git a/include/spdlog/common.h b/include/spdlog/common.h index 8b70901f4..2d69fb798 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -12,8 +12,11 @@ #include #include #include +#include #include "./source_loc.h" +#include "fmt/base.h" +#include "fmt/xchar.h" #if defined(SPDLOG_SHARED_LIB) #if defined(_WIN32) @@ -29,8 +32,6 @@ #define SPDLOG_API #endif -#include "fmt/fmt.h" - #define SPDLOG_FMT_RUNTIME(format_string) fmt::runtime(format_string) #define SPDLOG_FMT_STRING(format_string) FMT_STRING(format_string) @@ -73,7 +74,7 @@ using format_string_t = fmt::format_string; #endif // Log level enum -enum class level { +enum class level : std::uint8_t { trace = SPDLOG_LEVEL_TRACE, debug = SPDLOG_LEVEL_DEBUG, info = SPDLOG_LEVEL_INFO, @@ -81,7 +82,7 @@ enum class level { err = SPDLOG_LEVEL_ERROR, critical = SPDLOG_LEVEL_CRITICAL, off = SPDLOG_LEVEL_OFF, - n_levels + n_levels = SPDLOG_LEVEL_OFF + 1 }; using atomic_level_t = std::atomic; diff --git a/include/spdlog/details/async_log_msg.h b/include/spdlog/details/async_log_msg.h new file mode 100644 index 000000000..52dee73dd --- /dev/null +++ b/include/spdlog/details/async_log_msg.h @@ -0,0 +1,37 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#include +#include "./log_msg.h" + +namespace spdlog { +namespace details { + + +// Extend log_msg with internal buffer to store its payload. +// This is needed since log_msg holds string_views that points to stack data. + +class SPDLOG_API async_log_msg : public log_msg { +public: + enum class type:std::uint8_t { log, flush, terminate }; + async_log_msg() = default; + explicit async_log_msg(type type); + async_log_msg(type type, const log_msg &orig_msg); + + ~async_log_msg() = default; + async_log_msg(const async_log_msg &other); + async_log_msg(async_log_msg &&other) noexcept; + async_log_msg &operator=(const async_log_msg &other); + async_log_msg &operator=(async_log_msg &&other) noexcept; + + type message_type() const {return msg_type_;} +private: + type msg_type_{type::log}; + memory_buf_t buffer_; + void update_string_views(); +}; + +} // namespace details +} // namespace spdlog diff --git a/include/spdlog/details/circular_q.h b/include/spdlog/details/circular_q.h index d77322d62..f858a48b8 100644 --- a/include/spdlog/details/circular_q.h +++ b/include/spdlog/details/circular_q.h @@ -7,8 +7,6 @@ #include #include -#include "spdlog/common.h" - namespace spdlog { namespace details { template diff --git a/include/spdlog/details/context.h b/include/spdlog/details/context.h deleted file mode 100644 index 6059c7b5b..000000000 --- a/include/spdlog/details/context.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -// Loggers registry of unique name->logger pointer -// An attempt to create a logger with an already existing name will result with spdlog_ex exception. -// If user requests a non-existing logger, nullptr will be returned -// This class is thread safe - -#include -#include -#include - -#include "../common.h" -#include "./periodic_worker.h" - -namespace spdlog { -class logger; - -namespace details { -class thread_pool; - -class SPDLOG_API context { -public: - context() = default; - explicit context(std::unique_ptr global_logger); - ~context() = default; - context(const context &) = delete; - context &operator=(const context &) = delete; - - [[nodiscard]] std::shared_ptr global_logger(); - - // Return raw ptr to the global logger. - // To be used directly by the spdlog global api (e.g. spdlog::info) - // This make the global API faster, but cannot be used concurrently with set_global_logger(). - // e.g do not call set_global_logger() from one thread while calling spdlog::info() from - // another. - [[nodiscard]] logger *global_logger_raw() const noexcept; - - // set logger instance. - void set_logger(std::shared_ptr new_logger); - - void set_tp(std::shared_ptr tp); - - [[nodiscard]] std::shared_ptr get_tp(); - - // clean all resources - void shutdown(); - [[nodiscard]] std::recursive_mutex &tp_mutex(); - -private: - std::recursive_mutex tp_mutex_; - std::shared_ptr tp_; - std::shared_ptr global_logger_; -}; - -} // namespace details -} // namespace spdlog diff --git a/include/spdlog/details/err_helper.h b/include/spdlog/details/err_helper.h new file mode 100644 index 000000000..bb6434b3d --- /dev/null +++ b/include/spdlog/details/err_helper.h @@ -0,0 +1,23 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#include +#include +#include +#include "spdlog/common.h" + +// by default, prints the error to stderr, thread safe +namespace spdlog { +namespace details { +class SPDLOG_API err_helper { + err_handler custom_err_handler_; +public: + void handle_ex(const std::string& origin, const source_loc& loc, const std::exception& ex) const; + void handle_unknown_ex(const std::string& origin, const source_loc& loc) const; + void set_err_handler(err_handler handler); +}; + + +}} // namespace spdlog::details diff --git a/include/spdlog/details/fmt_helper.h b/include/spdlog/details/fmt_helper.h index ff10dcc31..625818913 100644 --- a/include/spdlog/details/fmt_helper.h +++ b/include/spdlog/details/fmt_helper.h @@ -7,7 +7,6 @@ #include #include "../common.h" -#include "../fmt/fmt.h" // Some fmt helpers to efficiently format and pad ints and strings namespace spdlog { diff --git a/include/spdlog/details/log_msg_buffer.h b/include/spdlog/details/log_msg_buffer.h deleted file mode 100644 index 14d409a1b..000000000 --- a/include/spdlog/details/log_msg_buffer.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#include "./log_msg.h" - -namespace spdlog { -namespace details { - -// Extend log_msg with internal buffer to store its payload. -// This is needed since log_msg holds string_views that points to stack data. - -class SPDLOG_API log_msg_buffer : public log_msg { - memory_buf_t buffer; - void update_string_views(); - -public: - log_msg_buffer() = default; - explicit log_msg_buffer(const log_msg &orig_msg); - log_msg_buffer(const log_msg_buffer &other); - log_msg_buffer(log_msg_buffer &&other) noexcept; - log_msg_buffer &operator=(const log_msg_buffer &other); - log_msg_buffer &operator=(log_msg_buffer &&other) noexcept; -}; - -} // namespace details -} // namespace spdlog diff --git a/include/spdlog/details/mpmc_blocking_q.h b/include/spdlog/details/mpmc_blocking_q.h index 67e3fde8f..af7d12fde 100644 --- a/include/spdlog/details/mpmc_blocking_q.h +++ b/include/spdlog/details/mpmc_blocking_q.h @@ -37,7 +37,7 @@ class mpmc_blocking_queue { push_cv_.notify_one(); } - // enqueue immediately. overrun oldest message in the queue if no room left. + // enqueue immediately. overrun the oldest message in the queue if no room left. void enqueue_nowait(T &&item) { { std::unique_lock lock(queue_mutex_); diff --git a/include/spdlog/details/periodic_worker.h b/include/spdlog/details/periodic_worker.h deleted file mode 100644 index a01f66a8c..000000000 --- a/include/spdlog/details/periodic_worker.h +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -// periodic worker thread - periodically executes the given callback function. -// -// RAII over the owned thread: -// creates the thread on construction. -// stops and joins the thread on destruction (if the thread is executing a callback, wait for it -// to finish first). - -#include -#include -#include -#include -#include - -#include "../common.h" - -namespace spdlog { -namespace details { - -class SPDLOG_API periodic_worker { -public: - template - periodic_worker(const std::function &callback_fun, std::chrono::duration interval) { - active_ = (interval > std::chrono::duration::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(); - } - }); - } - std::thread &get_thread() { return worker_thread_; } - periodic_worker(const periodic_worker &) = delete; - periodic_worker &operator=(const periodic_worker &) = delete; - // stop the worker thread and join it - ~periodic_worker(); - -private: - bool active_; - std::thread worker_thread_; - std::mutex mutex_; - std::condition_variable cv_; -}; - -} // namespace details -} // namespace spdlog diff --git a/include/spdlog/details/synchronous_factory.h b/include/spdlog/details/synchronous_factory.h deleted file mode 100644 index 3aee14c6e..000000000 --- a/include/spdlog/details/synchronous_factory.h +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#include "./context.h" - -namespace spdlog { - -// Default logger factory- creates synchronous loggers -class logger; - -struct synchronous_factory { - template - static std::shared_ptr create(std::string logger_name, SinkArgs &&...args) { - auto sink = std::make_shared(std::forward(args)...); - auto new_logger = std::make_shared(std::move(logger_name), std::move(sink)); - return new_logger; - } -}; -} // namespace spdlog diff --git a/include/spdlog/details/thread_pool.h b/include/spdlog/details/thread_pool.h deleted file mode 100644 index 76f9bdd39..000000000 --- a/include/spdlog/details/thread_pool.h +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#pragma once - -#include -#include -#include -#include -#include - -#include "../async.h" -#include "./log_msg_buffer.h" -#include "./mpmc_blocking_q.h" -#include "./os.h" - -namespace spdlog { -class async_logger; - -namespace details { - -using async_logger_ptr = std::shared_ptr; - -enum class async_msg_type { log, flush, terminate }; - -// Async msg to move to/from the queue -// Movable only. should never be copied -struct async_msg : log_msg_buffer { - async_msg_type msg_type{async_msg_type::log}; - async_logger_ptr worker_ptr; - - async_msg() = default; - ~async_msg() = default; - - // should only be moved in or out of the queue.. - async_msg(const async_msg &) = delete; - -// support for vs2013 move -#if defined(_MSC_VER) && _MSC_VER <= 1800 - async_msg(async_msg &&other) - : log_msg_buffer(std::move(other)), - msg_type(other.msg_type), - worker_ptr(std::move(other.worker_ptr)) {} - - async_msg &operator=(async_msg &&other) { - *static_cast(this) = std::move(other); - msg_type = other.msg_type; - worker_ptr = std::move(other.worker_ptr); - return *this; - } -#else // (_MSC_VER) && _MSC_VER <= 1800 - async_msg(async_msg &&) = default; - async_msg &operator=(async_msg &&) = default; -#endif - - // construct from log_msg with given type - async_msg(async_logger_ptr &&worker, async_msg_type the_type, const details::log_msg &m) - : log_msg_buffer{m}, - msg_type{the_type}, - worker_ptr{std::move(worker)} {} - - async_msg(async_logger_ptr &&worker, async_msg_type the_type) - : log_msg_buffer{}, - msg_type{the_type}, - worker_ptr{std::move(worker)} {} - - explicit async_msg(async_msg_type the_type) - : async_msg{nullptr, the_type} {} -}; - -class SPDLOG_API thread_pool { -public: - using item_type = async_msg; - using q_type = details::mpmc_blocking_queue; - - thread_pool(size_t q_max_items, - size_t threads_n, - std::function on_thread_start, - std::function on_thread_stop); - thread_pool(size_t q_max_items, size_t threads_n, std::function on_thread_start); - thread_pool(size_t q_max_items, size_t threads_n); - - // message all threads to terminate gracefully and join them - ~thread_pool(); - - thread_pool(const thread_pool &) = delete; - thread_pool &operator=(thread_pool &&) = delete; - - void post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy); - void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy); - size_t overrun_counter(); - void reset_overrun_counter(); - size_t discard_counter(); - void reset_discard_counter(); - size_t queue_size(); - -private: - q_type q_; - - std::vector threads_; - - 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_(); -}; - -} // namespace details -} // namespace spdlog diff --git a/include/spdlog/fmt/fmt.h b/include/spdlog/fmt/fmt.h deleted file mode 100644 index 4db292538..000000000 --- a/include/spdlog/fmt/fmt.h +++ /dev/null @@ -1,9 +0,0 @@ -// -// Copyright(c) 2016-2018 Gabi Melman. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) -// - -#pragma once - -#include "fmt/format.h" -#include "fmt/xchar.h" diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h index f8a79de23..21618b43a 100644 --- a/include/spdlog/logger.h +++ b/include/spdlog/logger.h @@ -3,37 +3,22 @@ #pragma once -// Thread safe logger (except for set_error_handler()) -// Has name, log level, vector of std::shared sink pointers and formatter -// Upon each log write the logger: -// 1. Checks if its log level is enough to log the message and if yes: -// 2. Call the underlying sinks to do the job. -// 3. Each sink use its own private copy of a formatter to format the message -// and send to its destination. -// -// The use of private formatter per sink provides the opportunity to cache some -// formatted data, and support for different format per sink. +// Thread-safe logger, with exceptions for these non-thread-safe methods: +// set_pattern() - Modifies the log pattern. +// set_formatter() - Sets a new formatter. +// set_error_handler() - Assigns a new error handler. +// sinks() (non-const) - Accesses and potentially modifies the sinks directly. +// By default, the logger does not throw exceptions during logging. +// To enable exception throwing for logging errors, set a custom error handler. #include #include #include -#include "./common.h" -#include "./details/log_msg.h" -#include "./sinks/sink.h" - -#define SPDLOG_LOGGER_CATCH(location) \ - catch (const std::exception &ex) { \ - if (!location.empty()) { \ - err_handler_(fmt_lib::format(SPDLOG_FMT_STRING("{} [{}({})]"), ex.what(), location.filename, location.line)); \ - } else { \ - err_handler_(ex.what()); \ - } \ - } \ - catch (...) { \ - err_handler_("Rethrowing unknown exception in logger"); \ - throw; \ - } +#include "common.h" +#include "details/err_helper.h" +#include "details/log_msg.h" +#include "sinks/sink.h" namespace spdlog { @@ -60,7 +45,7 @@ class SPDLOG_API logger { logger(const logger &other) noexcept; logger(logger &&other) noexcept; - virtual ~logger() = default; + ~logger() = default; template void log(source_loc loc, level lvl, format_string_t fmt, Args &&...args) { @@ -168,14 +153,14 @@ class SPDLOG_API logger { void set_error_handler(err_handler); // create new logger with same sinks and configuration. - virtual std::shared_ptr clone(std::string logger_name); + std::shared_ptr clone(std::string logger_name); -protected: +private: std::string name_; std::vector sinks_; - spdlog::atomic_level_t level_{level::info}; - spdlog::atomic_level_t flush_level_{level::off}; - err_handler custom_err_handler_{nullptr}; + atomic_level_t level_{level::info}; + atomic_level_t flush_level_{level::off}; + details::err_helper err_helper_; // common implementation for after templated public api has been resolved to format string and // args @@ -186,19 +171,25 @@ class SPDLOG_API logger { memory_buf_t buf; fmt::vformat_to(std::back_inserter(buf), format_string, fmt::make_format_args(args...)); sink_it_(details::log_msg(loc, name_, lvl, string_view_t(buf.data(), buf.size()))); + } catch (const std::exception &ex) { + err_helper_.handle_ex(name_, loc, ex); + } catch (...) { + err_helper_.handle_unknown_ex(name_, loc); } - SPDLOG_LOGGER_CATCH(loc) } // log the given message (if the given log level is high enough) - virtual void sink_it_(const details::log_msg &msg) { + void sink_it_(const details::log_msg &msg) { assert(should_log(msg.log_level)); for (auto &sink : sinks_) { if (sink->should_log(msg.log_level)) { try { sink->log(msg); + } catch (const std::exception &ex) { + err_helper_.handle_ex(name_, msg.source, ex); + } catch (...) { + err_helper_.handle_unknown_ex(name_, msg.source); } - SPDLOG_LOGGER_CATCH(msg.source) } } @@ -206,12 +197,8 @@ class SPDLOG_API logger { flush_(); } } - virtual void flush_(); + void flush_(); [[nodiscard]] bool should_flush_(const details::log_msg &msg) const; - - // handle errors during logging. - // default handler prints the error to stderr at max rate of 1 message/sec. - void err_handler_(const std::string &msg); }; } // namespace spdlog diff --git a/include/spdlog/sinks/android_sink.h b/include/spdlog/sinks/android_sink.h index e2d1607e3..72e716cb0 100644 --- a/include/spdlog/sinks/android_sink.h +++ b/include/spdlog/sinks/android_sink.h @@ -16,7 +16,6 @@ #include "../details/fmt_helper.h" #include "../details/null_mutex.h" #include "../details/os.h" - #include "../details/synchronous_factory.h" #include "./base_sink.h" #if !defined(SPDLOG_ANDROID_RETRIES) @@ -120,19 +119,6 @@ template using android_sink_buf_st = android_sink; } // namespace sinks - -// Create and register android syslog logger - -template -inline std::shared_ptr android_logger_mt(const std::string &logger_name, const std::string &tag = "spdlog") { - return Factory::template create(logger_name, tag); -} - -template -inline std::shared_ptr android_logger_st(const std::string &logger_name, const std::string &tag = "spdlog") { - return Factory::template create(logger_name, tag); -} - } // namespace spdlog #endif // __ANDROID__ diff --git a/include/spdlog/sinks/ansicolor_sink.h b/include/spdlog/sinks/ansicolor_sink.h index bc1500af2..30b19628b 100644 --- a/include/spdlog/sinks/ansicolor_sink.h +++ b/include/spdlog/sinks/ansicolor_sink.h @@ -94,7 +94,6 @@ class ansicolor_stderr_sink final : public ansicolor_sink { using ansicolor_stdout_sink_mt = ansicolor_stdout_sink; using ansicolor_stdout_sink_st = ansicolor_stdout_sink; - using ansicolor_stderr_sink_mt = ansicolor_stderr_sink; using ansicolor_stderr_sink_st = ansicolor_stderr_sink; diff --git a/include/spdlog/sinks/async_sink.h b/include/spdlog/sinks/async_sink.h new file mode 100644 index 000000000..35ea6df89 --- /dev/null +++ b/include/spdlog/sinks/async_sink.h @@ -0,0 +1,86 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "../details/async_log_msg.h" +#include "../details/err_helper.h" +#include "sink.h" + +// async_sink is a sink that sends log messages to a dist_sink in a separate thread using a queue. +// The worker thread dequeues the messages and sends them to the dist_sink to perform the actual logging. +// Once the sink is destroyed, the worker thread empties the queue and exits. + +namespace spdlog::details { // forward declaration +template +class mpmc_blocking_queue; +} + +namespace spdlog { +namespace sinks { + +class SPDLOG_API async_sink final : public sink { +public: + enum class overflow_policy : std::uint8_t { + block, // Block until the log message can be enqueued (default). + overrun_oldest, // Overrun the oldest message in the queue if full. + discard_new // Discard the log message if the queue is full + }; + + enum { default_queue_size = 8192, max_queue_size = 10 * 1024 * 1024 }; + + struct config { + size_t queue_size = default_queue_size; + overflow_policy policy = overflow_policy::block; + std::vector> sinks; + std::function on_thread_start = nullptr; + std::function on_thread_stop = nullptr; + err_handler custom_err_handler = nullptr; + }; + + explicit async_sink(config async_config); + + // create an async_sink with one backend sink + template + static std::shared_ptr with(SinkArgs &&...sink_args) { + config cfg{}; + cfg.sinks.emplace_back(std::make_shared(std::forward(sink_args)...)); + return std::make_shared(cfg); + } + + ~async_sink() override; + + // sink interface implementation + void log(const details::log_msg &msg) override; + void flush() override; + void set_pattern(const std::string &pattern) override; + void set_formatter(std::unique_ptr sink_formatter) override; + + // async sink specific methods + [[nodiscard]] size_t get_overrun_counter() const; + void reset_overrun_counter() const; + [[nodiscard]] size_t get_discard_counter() const; + void reset_discard_counter() const; + [[nodiscard]] const config &get_config() const; + +private: + using async_log_msg = details::async_log_msg; + using queue_t = details::mpmc_blocking_queue; + + void send_message_(async_log_msg::type msg_type, const details::log_msg &msg) const; + void backend_loop_(); + void backend_log_(const details::log_msg &msg) ; + void backend_flush_(); + + config config_; + std::unique_ptr q_; + std::thread worker_thread_; + details::err_helper err_helper_; +}; + +} // namespace sinks +} // namespace spdlog diff --git a/include/spdlog/sinks/base_sink.h b/include/spdlog/sinks/base_sink.h index 9dedbfc03..04cc6883c 100644 --- a/include/spdlog/sinks/base_sink.h +++ b/include/spdlog/sinks/base_sink.h @@ -43,5 +43,6 @@ class SPDLOG_API base_sink : public sink { virtual void set_pattern_(const std::string &pattern); virtual void set_formatter_(std::unique_ptr sink_formatter); }; + } // namespace sinks } // namespace spdlog diff --git a/include/spdlog/sinks/basic_file_sink.h b/include/spdlog/sinks/basic_file_sink.h index 1ffb06048..3377061a8 100644 --- a/include/spdlog/sinks/basic_file_sink.h +++ b/include/spdlog/sinks/basic_file_sink.h @@ -8,7 +8,6 @@ #include "../details/file_helper.h" #include "../details/null_mutex.h" -#include "../details/synchronous_factory.h" #include "./base_sink.h" namespace spdlog { @@ -34,24 +33,4 @@ using basic_file_sink_mt = basic_file_sink; using basic_file_sink_st = basic_file_sink; } // namespace sinks - -// -// factory functions -// -template -std::shared_ptr basic_logger_mt(const std::string &logger_name, - const filename_t &filename, - bool truncate = false, - const file_event_handlers &event_handlers = {}) { - return Factory::template create(logger_name, filename, truncate, event_handlers); -} - -template -std::shared_ptr basic_logger_st(const std::string &logger_name, - const filename_t &filename, - bool truncate = false, - const file_event_handlers &event_handlers = {}) { - return Factory::template create(logger_name, filename, truncate, event_handlers); -} - } // namespace spdlog diff --git a/include/spdlog/sinks/callback_sink.h b/include/spdlog/sinks/callback_sink.h index ad0ec8eee..856c80b0d 100644 --- a/include/spdlog/sinks/callback_sink.h +++ b/include/spdlog/sinks/callback_sink.h @@ -7,7 +7,6 @@ #include #include "../details/null_mutex.h" -#include "../details/synchronous_factory.h" #include "./base_sink.h" namespace spdlog { @@ -37,18 +36,4 @@ using callback_sink_mt = callback_sink; using callback_sink_st = callback_sink; } // namespace sinks - -// -// factory functions -// -template -inline std::shared_ptr callback_logger_mt(const std::string &logger_name, const custom_log_callback &callback) { - return Factory::template create(logger_name, callback); -} - -template -inline std::shared_ptr callback_logger_st(const std::string &logger_name, const custom_log_callback &callback) { - return Factory::template create(logger_name, callback); -} - } // namespace spdlog diff --git a/include/spdlog/sinks/daily_file_sink.h b/include/spdlog/sinks/daily_file_sink.h index 80145dbb5..481321e3a 100644 --- a/include/spdlog/sinks/daily_file_sink.h +++ b/include/spdlog/sinks/daily_file_sink.h @@ -15,7 +15,6 @@ #include "../details/file_helper.h" #include "../details/null_mutex.h" #include "../details/os.h" -#include "../details/synchronous_factory.h" #include "./base_sink.h" namespace spdlog { @@ -190,55 +189,4 @@ using daily_file_format_sink_mt = daily_file_sink; } // namespace sinks - -// -// factory functions -// -template -inline std::shared_ptr daily_logger_mt(const std::string &logger_name, - const filename_t &filename, - int hour = 0, - int minute = 0, - bool truncate = false, - uint16_t max_files = 0, - const file_event_handlers &event_handlers = {}) { - return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, - event_handlers); -} - -template -inline std::shared_ptr daily_logger_format_mt(const std::string &logger_name, - const filename_t &filename, - int hour = 0, - int minute = 0, - bool truncate = false, - uint16_t max_files = 0, - const file_event_handlers &event_handlers = {}) { - return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, - event_handlers); -} - -template -inline std::shared_ptr daily_logger_st(const std::string &logger_name, - const filename_t &filename, - int hour = 0, - int minute = 0, - bool truncate = false, - uint16_t max_files = 0, - const file_event_handlers &event_handlers = {}) { - return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, - event_handlers); -} - -template -inline std::shared_ptr daily_logger_format_st(const std::string &logger_name, - const filename_t &filename, - int hour = 0, - int minute = 0, - bool truncate = false, - uint16_t max_files = 0, - const file_event_handlers &event_handlers = {}) { - return Factory::template create(logger_name, filename, hour, minute, truncate, max_files, - event_handlers); -} } // namespace spdlog diff --git a/include/spdlog/sinks/dist_sink.h b/include/spdlog/sinks/dist_sink.h index 7decc0b2a..869c4cad3 100644 --- a/include/spdlog/sinks/dist_sink.h +++ b/include/spdlog/sinks/dist_sink.h @@ -24,14 +24,14 @@ class dist_sink : public base_sink { public: dist_sink() = default; explicit dist_sink(std::vector> sinks) - : sinks_(sinks) {} + : sinks_(std::move(sinks)) {} dist_sink(const dist_sink &) = delete; dist_sink &operator=(const dist_sink &) = delete; void add_sink(std::shared_ptr sub_sink) { std::lock_guard lock(base_sink::mutex_); - sinks_.push_back(sub_sink); + sinks_.push_back(std::move(sub_sink)); } void remove_sink(std::shared_ptr sub_sink) { @@ -48,7 +48,7 @@ class dist_sink : public base_sink { protected: void sink_it_(const details::log_msg &msg) override { - for (auto &sub_sink : sinks_) { + for (const auto &sub_sink : sinks_) { if (sub_sink->should_log(msg.log_level)) { sub_sink->log(msg); } diff --git a/include/spdlog/sinks/hourly_file_sink.h b/include/spdlog/sinks/hourly_file_sink.h index cff218f08..15dd0f4d4 100644 --- a/include/spdlog/sinks/hourly_file_sink.h +++ b/include/spdlog/sinks/hourly_file_sink.h @@ -14,8 +14,6 @@ #include "../details/file_helper.h" #include "../details/null_mutex.h" #include "../details/os.h" -#include "../details/synchronous_factory.h" -#include "../fmt/fmt.h" #include "./base_sink.h" namespace spdlog { @@ -167,25 +165,4 @@ using hourly_file_sink_mt = hourly_file_sink; using hourly_file_sink_st = hourly_file_sink; } // namespace sinks - -// -// factory functions -// -template -inline std::shared_ptr hourly_logger_mt(const std::string &logger_name, - const filename_t &filename, - bool truncate = false, - uint16_t max_files = 0, - const file_event_handlers &event_handlers = {}) { - return Factory::template create(logger_name, filename, truncate, max_files, event_handlers); -} - -template -inline std::shared_ptr hourly_logger_st(const std::string &logger_name, - const filename_t &filename, - bool truncate = false, - uint16_t max_files = 0, - const file_event_handlers &event_handlers = {}) { - return Factory::template create(logger_name, filename, truncate, max_files, event_handlers); -} } // namespace spdlog diff --git a/include/spdlog/sinks/kafka_sink.h b/include/spdlog/sinks/kafka_sink.h index 06f0d7fd8..7cd52fd16 100644 --- a/include/spdlog/sinks/kafka_sink.h +++ b/include/spdlog/sinks/kafka_sink.h @@ -12,11 +12,9 @@ #include -#include "../async.h" #include "../common.h" #include "../details/log_msg.h" #include "../details/null_mutex.h" -#include "../details/synchronous_factory.h" #include "./base_sink.h" // kafka header @@ -89,25 +87,4 @@ using kafka_sink_mt = kafka_sink; using kafka_sink_st = kafka_sink; } // namespace sinks - -template -inline std::shared_ptr kafka_logger_mt(const std::string &logger_name, spdlog::sinks::kafka_sink_config config) { - return Factory::template create(logger_name, config); -} - -template -inline std::shared_ptr kafka_logger_st(const std::string &logger_name, spdlog::sinks::kafka_sink_config config) { - return Factory::template create(logger_name, config); -} - -template -inline std::shared_ptr kafka_logger_async_mt(std::string logger_name, spdlog::sinks::kafka_sink_config config) { - return Factory::template create(logger_name, config); -} - -template -inline std::shared_ptr kafka_logger_async_st(std::string logger_name, spdlog::sinks::kafka_sink_config config) { - return Factory::template create(logger_name, config); -} - } // namespace spdlog diff --git a/include/spdlog/sinks/mongo_sink.h b/include/spdlog/sinks/mongo_sink.h index 56e213437..36d8945c0 100644 --- a/include/spdlog/sinks/mongo_sink.h +++ b/include/spdlog/sinks/mongo_sink.h @@ -17,9 +17,10 @@ #include #include +#include +#include "../details/null_mutex.h" #include "../common.h" #include "../details/log_msg.h" -#include "../details/synchronous_factory.h" #include "./base_sink.h" namespace spdlog { @@ -75,28 +76,8 @@ class mongo_sink : public base_sink { std::unique_ptr client_ = nullptr; }; -#include - -#include "../details/null_mutex.h" using mongo_sink_mt = mongo_sink; -using mongo_sink_st = mongo_sink; +using mongo_sink_st = mongo_sink; } // namespace sinks - -template -inline std::shared_ptr mongo_logger_mt(const std::string &logger_name, - const std::string &db_name, - const std::string &collection_name, - const std::string &uri = "mongodb://localhost:27017") { - return Factory::template create(logger_name, db_name, collection_name, uri); -} - -template -inline std::shared_ptr mongo_logger_st(const std::string &logger_name, - const std::string &db_name, - const std::string &collection_name, - const std::string &uri = "mongodb://localhost:27017") { - return Factory::template create(logger_name, db_name, collection_name, uri); -} - } // namespace spdlog diff --git a/include/spdlog/sinks/msvc_sink.h b/include/spdlog/sinks/msvc_sink.h index e2e0dd139..6aa7d036c 100644 --- a/include/spdlog/sinks/msvc_sink.h +++ b/include/spdlog/sinks/msvc_sink.h @@ -4,7 +4,6 @@ #pragma once #include - #include "../details/null_mutex.h" #include "./base_sink.h" diff --git a/include/spdlog/sinks/null_sink.h b/include/spdlog/sinks/null_sink.h index e8ea21f15..3e45cc668 100644 --- a/include/spdlog/sinks/null_sink.h +++ b/include/spdlog/sinks/null_sink.h @@ -4,7 +4,6 @@ #pragma once #include "../details/null_mutex.h" -#include "../details/synchronous_factory.h" #include "./base_sink.h" namespace spdlog { @@ -21,19 +20,4 @@ using null_sink_mt = null_sink; using null_sink_st = null_sink; } // namespace sinks - -template -std::shared_ptr null_logger_mt(const std::string &logger_name) { - auto null_logger = Factory::template create(logger_name); - null_logger->set_level(level::off); - return null_logger; -} - -template -std::shared_ptr null_logger_st(const std::string &logger_name) { - auto null_logger = Factory::template create(logger_name); - null_logger->set_level(level::off); - return null_logger; -} - } // namespace spdlog diff --git a/include/spdlog/sinks/qt_sinks.h b/include/spdlog/sinks/qt_sinks.h index ceeeb59d0..303e95098 100644 --- a/include/spdlog/sinks/qt_sinks.h +++ b/include/spdlog/sinks/qt_sinks.h @@ -17,7 +17,6 @@ #include "../common.h" #include "../details/log_msg.h" -#include "../details/synchronous_factory.h" #include "./base_sink.h" // @@ -218,75 +217,10 @@ class qt_color_sink : public base_sink { std::array colors_; }; -#include - -#include "../details/null_mutex.h" - using qt_sink_mt = qt_sink; using qt_sink_st = qt_sink; using qt_color_sink_mt = qt_color_sink; using qt_color_sink_st = qt_color_sink; -} // namespace sinks - -// -// Factory functions -// - -// log to QTextEdit -template -inline std::shared_ptr qt_logger_mt(const std::string &logger_name, - QTextEdit *qt_object, - const std::string &meta_method = "append") { - return Factory::template create(logger_name, qt_object, meta_method); -} - -template -inline std::shared_ptr qt_logger_st(const std::string &logger_name, - QTextEdit *qt_object, - const std::string &meta_method = "append") { - return Factory::template create(logger_name, qt_object, meta_method); -} - -// log to QPlainTextEdit -template -inline std::shared_ptr qt_logger_mt(const std::string &logger_name, - QPlainTextEdit *qt_object, - const std::string &meta_method = "appendPlainText") { - return Factory::template create(logger_name, qt_object, meta_method); -} - -template -inline std::shared_ptr qt_logger_st(const std::string &logger_name, - QPlainTextEdit *qt_object, - const std::string &meta_method = "appendPlainText") { - return Factory::template create(logger_name, qt_object, meta_method); -} -// log to QObject -template -inline std::shared_ptr qt_logger_mt(const std::string &logger_name, QObject *qt_object, const std::string &meta_method) { - return Factory::template create(logger_name, qt_object, meta_method); -} - -template -inline std::shared_ptr qt_logger_st(const std::string &logger_name, QObject *qt_object, const std::string &meta_method) { - return Factory::template create(logger_name, qt_object, meta_method); -} - -// log to QTextEdit with colorized output -template -inline std::shared_ptr qt_color_logger_mt(const std::string &logger_name, - QTextEdit *qt_text_edit, - int max_lines, - bool is_utf8 = false) { - return Factory::template create(logger_name, qt_text_edit, max_lines, false, is_utf8); -} - -template -inline std::shared_ptr qt_color_logger_st(const std::string &logger_name, - QTextEdit *qt_text_edit, - int max_lines, - bool is_utf8 = false) { - return Factory::template create(logger_name, qt_text_edit, max_lines, false, is_utf8); -} +} // namespace sinks } // namespace spdlog diff --git a/include/spdlog/sinks/ringbuffer_sink.h b/include/spdlog/sinks/ringbuffer_sink.h index 90d02764e..677f76dd1 100644 --- a/include/spdlog/sinks/ringbuffer_sink.h +++ b/include/spdlog/sinks/ringbuffer_sink.h @@ -7,10 +7,10 @@ #include #include +#include "../details/async_log_msg.h" #include "../details/circular_q.h" -#include "../details/log_msg_buffer.h" #include "../details/null_mutex.h" -#include "./base_sink.h" +#include "base_sink.h" namespace spdlog { namespace sinks { @@ -26,7 +26,7 @@ class ringbuffer_sink final : public base_sink { explicit ringbuffer_sink(size_t n_items) : q_{n_items} {} - void drain_raw(std::function callback) { + void drain_raw(std::function callback) { std::lock_guard lock(base_sink::mutex_); while (!q_.empty()) { callback(q_.front()); @@ -46,11 +46,11 @@ class ringbuffer_sink final : public base_sink { } protected: - void sink_it_(const details::log_msg &msg) override { q_.push_back(details::log_msg_buffer{msg}); } + void sink_it_(const details::log_msg &msg) override { q_.push_back(details::async_log_msg{details::async_log_msg::type::log, msg}); } void flush_() override {} private: - details::circular_q q_; + details::circular_q q_; }; using ringbuffer_sink_mt = ringbuffer_sink; diff --git a/include/spdlog/sinks/rotating_file_sink.h b/include/spdlog/sinks/rotating_file_sink.h index ec441168f..ca6f52cca 100644 --- a/include/spdlog/sinks/rotating_file_sink.h +++ b/include/spdlog/sinks/rotating_file_sink.h @@ -8,14 +8,12 @@ #include "../details/file_helper.h" #include "../details/null_mutex.h" -#include "../details/synchronous_factory.h" #include "./base_sink.h" +// Rotating file sink based on size + namespace spdlog { namespace sinks { -// -// Rotating file sink based on size -// template class rotating_file_sink final : public base_sink { public: @@ -56,30 +54,4 @@ using rotating_file_sink_mt = rotating_file_sink; using rotating_file_sink_st = rotating_file_sink; } // namespace sinks - -// -// factory functions -// - -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 file_event_handlers &event_handlers = {}) { - return Factory::template create(logger_name, filename, max_file_size, max_files, rotate_on_open, - event_handlers); -} - -template -inline std::shared_ptr rotating_logger_st(const std::string &logger_name, - const filename_t &filename, - size_t max_file_size, - size_t max_files, - bool rotate_on_open = false, - const file_event_handlers &event_handlers = {}) { - return Factory::template create(logger_name, filename, max_file_size, max_files, rotate_on_open, - event_handlers); -} } // namespace spdlog diff --git a/include/spdlog/sinks/sink.h b/include/spdlog/sinks/sink.h index b6a4c5c1c..cce832998 100644 --- a/include/spdlog/sinks/sink.h +++ b/include/spdlog/sinks/sink.h @@ -16,9 +16,9 @@ class SPDLOG_API sink { virtual void set_pattern(const std::string &pattern) = 0; virtual void set_formatter(std::unique_ptr sink_formatter) = 0; - void set_level(level level); - level log_level() const; - bool should_log(level msg_level) const; + void set_level(level level) { level_.store(level, std::memory_order_relaxed); } + level log_level() const { return level_.load(std::memory_order_relaxed);} + bool should_log(level msg_level) const {return msg_level >= level_.load(std::memory_order_relaxed);} protected: // sink log level - default is all diff --git a/include/spdlog/sinks/stdout_color_sinks.h b/include/spdlog/sinks/stdout_color_sinks.h index 73d5a470e..7025c4cc7 100644 --- a/include/spdlog/sinks/stdout_color_sinks.h +++ b/include/spdlog/sinks/stdout_color_sinks.h @@ -9,9 +9,6 @@ #include "./ansicolor_sink.h" #endif -#include "../async.h" -#include "../details/synchronous_factory.h" - namespace spdlog { namespace sinks { #ifdef _WIN32 @@ -25,19 +22,6 @@ using stdout_color_sink_st = ansicolor_stdout_sink_st; using stderr_color_sink_mt = ansicolor_stderr_sink_mt; using stderr_color_sink_st = ansicolor_stderr_sink_st; #endif -} // namespace sinks - -// logger factory functions -template -std::shared_ptr stdout_color_mt(const std::string &logger_name, color_mode mode = color_mode::automatic); - -template -std::shared_ptr stdout_color_st(const std::string &logger_name, color_mode mode = color_mode::automatic); - -template -std::shared_ptr stderr_color_mt(const std::string &logger_name, color_mode mode = color_mode::automatic); - -template -std::shared_ptr stderr_color_st(const std::string &logger_name, color_mode mode = color_mode::automatic); +} // namespace sinks } // namespace spdlog diff --git a/include/spdlog/sinks/stdout_sinks.h b/include/spdlog/sinks/stdout_sinks.h index aeb0b0abb..638473e0a 100644 --- a/include/spdlog/sinks/stdout_sinks.h +++ b/include/spdlog/sinks/stdout_sinks.h @@ -4,9 +4,9 @@ #pragma once #include +#include #include "../details/null_mutex.h" -#include "../details/synchronous_factory.h" #include "./base_sink.h" #include "./sink.h" @@ -58,18 +58,4 @@ using stderr_sink_mt = stderr_sink; using stderr_sink_st = stderr_sink; } // namespace sinks - -// factory methods -template -std::shared_ptr stdout_logger_mt(const std::string &logger_name); - -template -std::shared_ptr stdout_logger_st(const std::string &logger_name); - -template -std::shared_ptr stderr_logger_mt(const std::string &logger_name); - -template -std::shared_ptr stderr_logger_st(const std::string &logger_name); - } // namespace spdlog diff --git a/include/spdlog/sinks/syslog_sink.h b/include/spdlog/sinks/syslog_sink.h index 65f74bfab..52a486e19 100644 --- a/include/spdlog/sinks/syslog_sink.h +++ b/include/spdlog/sinks/syslog_sink.h @@ -4,12 +4,12 @@ #pragma once #include -#include #include #include #include #include +#include namespace spdlog { namespace sinks { @@ -19,7 +19,7 @@ namespace sinks { template class syslog_sink final : public base_sink { public: - syslog_sink(std::string ident, int syslog_option, int syslog_facility, bool enable_formatting) + syslog_sink(std::string ident = "", int syslog_option = 0, int syslog_facility = LOG_USER, bool enable_formatting=false) : enable_formatting_{enable_formatting}, syslog_levels_{{/* spdlog::level::trace */ LOG_DEBUG, /* spdlog::level::debug */ LOG_DEBUG, @@ -79,26 +79,6 @@ class syslog_sink final : public base_sink { using syslog_sink_mt = syslog_sink; using syslog_sink_st = syslog_sink; -} // namespace sinks - -// Create and register a syslog logger -template -inline std::shared_ptr syslog_logger_mt(const std::string &logger_name, - const std::string &syslog_ident = "", - int syslog_option = 0, - int syslog_facility = LOG_USER, - bool enable_formatting = false) { - return Factory::template create(logger_name, syslog_ident, syslog_option, syslog_facility, - enable_formatting); -} -template -inline std::shared_ptr syslog_logger_st(const std::string &logger_name, - const std::string &syslog_ident = "", - int syslog_option = 0, - int syslog_facility = LOG_USER, - bool enable_formatting = false) { - return Factory::template create(logger_name, syslog_ident, syslog_option, syslog_facility, - enable_formatting); -} +} // namespace sinks } // namespace spdlog diff --git a/include/spdlog/sinks/systemd_sink.h b/include/spdlog/sinks/systemd_sink.h index 9390d0ded..0ec81e281 100644 --- a/include/spdlog/sinks/systemd_sink.h +++ b/include/spdlog/sinks/systemd_sink.h @@ -7,7 +7,6 @@ #include "../details/null_mutex.h" #include "../details/os.h" -#include "../details/synchronous_factory.h" #include "./base_sink.h" #ifndef SD_JOURNAL_SUPPRESS_LOCATION #define SD_JOURNAL_SUPPRESS_LOCATION @@ -90,20 +89,6 @@ class systemd_sink : public base_sink { using systemd_sink_mt = systemd_sink; using systemd_sink_st = systemd_sink; -} // namespace sinks -// Create and register a syslog logger -template -inline std::shared_ptr systemd_logger_mt(const std::string &logger_name, - const std::string &ident = "", - bool enable_formatting = false) { - return Factory::template create(logger_name, ident, enable_formatting); -} - -template -inline std::shared_ptr systemd_logger_st(const std::string &logger_name, - const std::string &ident = "", - bool enable_formatting = false) { - return Factory::template create(logger_name, ident, enable_formatting); -} +} // namespace sinks } // namespace spdlog diff --git a/include/spdlog/sinks/tcp_sink.h b/include/spdlog/sinks/tcp_sink.h index b2fb251a6..be7bcc3be 100644 --- a/include/spdlog/sinks/tcp_sink.h +++ b/include/spdlog/sinks/tcp_sink.h @@ -69,7 +69,7 @@ class tcp_sink final : public spdlog::sinks::base_sink { }; using tcp_sink_mt = tcp_sink; -using tcp_sink_st = tcp_sink; +using tcp_sink_st = tcp_sink; } // namespace sinks } // namespace spdlog diff --git a/include/spdlog/sinks/udp_sink.h b/include/spdlog/sinks/udp_sink.h index 282f682f8..0f0951daa 100644 --- a/include/spdlog/sinks/udp_sink.h +++ b/include/spdlog/sinks/udp_sink.h @@ -33,7 +33,7 @@ struct udp_sink_config { }; template -class udp_sink final : public spdlog::sinks::base_sink { +class udp_sink final : public base_sink { public: // host can be hostname or ip address explicit udp_sink(udp_sink_config sink_config) @@ -53,16 +53,7 @@ class udp_sink final : public spdlog::sinks::base_sink { }; using udp_sink_mt = udp_sink; -using udp_sink_st = udp_sink; +using udp_sink_st = udp_sink; } // namespace sinks - -// -// factory functions -// -template -inline std::shared_ptr udp_logger_mt(const std::string &logger_name, sinks::udp_sink_config skin_config) { - return Factory::template create(logger_name, skin_config); -} - } // namespace spdlog diff --git a/include/spdlog/sinks/wincolor_sink.h b/include/spdlog/sinks/wincolor_sink.h index 1f6c09744..e1416f2c6 100644 --- a/include/spdlog/sinks/wincolor_sink.h +++ b/include/spdlog/sinks/wincolor_sink.h @@ -66,8 +66,8 @@ class wincolor_stderr_sink : public wincolor_sink { using wincolor_stdout_sink_mt = wincolor_stdout_sink; using wincolor_stdout_sink_st = wincolor_stdout_sink; - using wincolor_stderr_sink_mt = wincolor_stderr_sink; using wincolor_stderr_sink_st = wincolor_stderr_sink; + } // namespace sinks } // namespace spdlog diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h index eb6ba4b2d..45527eb49 100644 --- a/include/spdlog/spdlog.h +++ b/include/spdlog/spdlog.h @@ -14,24 +14,16 @@ #include #include "./common.h" -#include "./details/context.h" -#include "./details/synchronous_factory.h" #include "./logger.h" namespace spdlog { -using default_factory = synchronous_factory; - -SPDLOG_API void set_context(std::shared_ptr context); -SPDLOG_API std::shared_ptr context(); -SPDLOG_API const std::shared_ptr &context_ref(); - // Create a logger with a templated sink type // Example: // spdlog::create("logger_name", "dailylog_filename", 11, 59); template std::shared_ptr create(std::string logger_name, SinkArgs &&...sink_args) { - return default_factory::create(std::move(logger_name), std::forward(sink_args)...); + return std::make_shared(std::move(logger_name), std::make_shared(std::forward(sink_args)...)); } // Set formatter of the global logger. Each sink in each logger will get a clone of this object diff --git a/include/spdlog/stopwatch.h b/include/spdlog/stopwatch.h index cdd9b8b48..c08797ed1 100644 --- a/include/spdlog/stopwatch.h +++ b/include/spdlog/stopwatch.h @@ -5,7 +5,7 @@ #include -#include "fmt/fmt.h" +#include "fmt/base.h" // Stopwatch support for spdlog (using std::chrono::steady_clock). // Displays elapsed seconds since construction as double. diff --git a/src/async_logger.cpp b/src/async_logger.cpp deleted file mode 100644 index d891bb771..000000000 --- a/src/async_logger.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#include "spdlog/async_logger.h" - -#include -#include - -#include "spdlog/details/thread_pool.h" -#include "spdlog/sinks/sink.h" - -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) {} - -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 -void spdlog::async_logger::sink_it_(const details::log_msg &msg) { - try { - if (auto pool_ptr = thread_pool_.lock()) { - pool_ptr->post_log(shared_from_this(), msg, overflow_policy_); - } else { - throw_spdlog_ex("async log: thread pool doesn't exist anymore"); - } - } - SPDLOG_LOGGER_CATCH(msg.source) -} - -// send flush request to the thread pool -void spdlog::async_logger::flush_() { - try { - if (auto pool_ptr = thread_pool_.lock()) { - pool_ptr->post_flush(shared_from_this(), overflow_policy_); - } else { - throw_spdlog_ex("async flush: thread pool doesn't exist anymore"); - } - } - SPDLOG_LOGGER_CATCH(source_loc()) -} - -// -// backend functions - called from the thread pool to do the actual job -// -void spdlog::async_logger::backend_sink_it_(const details::log_msg &msg) { - for (auto &sink : sinks_) { - if (sink->should_log(msg.log_level)) { - try { - sink->log(msg); - } - SPDLOG_LOGGER_CATCH(msg.source) - } - } - - if (should_flush_(msg)) { - backend_flush_(); - } -} - -void spdlog::async_logger::backend_flush_() { - for (auto &sink : sinks_) { - try { - sink->flush(); - } - SPDLOG_LOGGER_CATCH(source_loc()) - } -} - -std::shared_ptr spdlog::async_logger::clone(std::string new_name) { - auto cloned = std::make_shared(*this); - cloned->name_ = std::move(new_name); - return cloned; -} diff --git a/src/details/async_log_msg.cpp b/src/details/async_log_msg.cpp new file mode 100644 index 000000000..62c95cabe --- /dev/null +++ b/src/details/async_log_msg.cpp @@ -0,0 +1,61 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#include "spdlog/details/async_log_msg.h" + +namespace spdlog { +namespace details { + + +async_log_msg::async_log_msg(const type type) + : msg_type_{type} {} + +// copy logger name and payload to buffer so can be used asynchronously +// note: source location pointers are copied without allocation since they +// are compiler generated const chars* (__FILE__, __LINE__, __FUNCTION__) +// if you pass custom strings to source location, make sure they outlive the async_log_msg +async_log_msg::async_log_msg(const type type, const log_msg &orig_msg) + : log_msg{orig_msg}, msg_type_(type) { + buffer_.append(logger_name); + buffer_.append(payload); + update_string_views(); +} + +async_log_msg::async_log_msg(const async_log_msg &other) + : log_msg{other}, msg_type_{other.msg_type_} { + buffer_.append(logger_name); + buffer_.append(payload); + update_string_views(); +} + +async_log_msg::async_log_msg(async_log_msg &&other) noexcept + : log_msg{other}, msg_type_{other.msg_type_}, buffer_{std::move(other.buffer_)} { + update_string_views(); +} + +async_log_msg &async_log_msg::operator=(const async_log_msg &other) { + if (this == &other) return *this; + log_msg::operator=(other); + msg_type_ = other.msg_type_; + buffer_.clear(); + buffer_.append(other.buffer_.data(), other.buffer_.data() + other.buffer_.size()); + update_string_views(); + return *this; +} + +async_log_msg &async_log_msg::operator=(async_log_msg &&other) noexcept { + if (this == &other) return *this; + log_msg::operator=(other); + msg_type_ = other.msg_type_; + buffer_ = std::move(other.buffer_); + update_string_views(); + return *this; +} + +void async_log_msg::update_string_views() { + logger_name = string_view_t{buffer_.data(), logger_name.size()}; + payload = string_view_t{buffer_.data() + logger_name.size(), payload.size()}; +} + +} // namespace details +} // namespace spdlog diff --git a/src/details/context.cpp b/src/details/context.cpp deleted file mode 100644 index 580a5e219..000000000 --- a/src/details/context.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#include "spdlog/details/context.h" - -#include "spdlog/logger.h" - -#ifndef SPDLOG_DISABLE_GLOBAL_LOGGER - #include "spdlog/sinks/stdout_color_sinks.h" -#endif // SPDLOG_DISABLE_GLOBAL_LOGGER - -#include - -namespace spdlog { -namespace details { - -context::context(std::unique_ptr global_logger) { global_logger_ = std::move(global_logger); } - -std::shared_ptr context::global_logger() { return global_logger_; } - -// Return raw ptr to the global 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_global_logger(). -// e.g do not call set_global_logger() from one thread while calling spdlog::info() from another. -logger *context::global_logger_raw() const noexcept { return global_logger_.get(); } - -// set global logger -void context::set_logger(std::shared_ptr new_global_logger) { global_logger_ = std::move(new_global_logger); } - -void context::set_tp(std::shared_ptr tp) { - std::lock_guard lock(tp_mutex_); - tp_ = std::move(tp); -} - -std::shared_ptr context::get_tp() { - std::lock_guard lock(tp_mutex_); - return tp_; -} - -// clean all resources and threads started by the registry -void context::shutdown() { - std::lock_guard lock(tp_mutex_); - tp_.reset(); -} - -std::recursive_mutex &context::tp_mutex() { return tp_mutex_; } - -} // namespace details -} // namespace spdlog diff --git a/src/details/err_helper.cpp b/src/details/err_helper.cpp new file mode 100644 index 000000000..153cf9883 --- /dev/null +++ b/src/details/err_helper.cpp @@ -0,0 +1,40 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#include "iostream" +#include "spdlog/details/err_helper.h" +#include "spdlog/details/os.h" + +namespace spdlog { +namespace details { + +// Prints error to stderr with source location (if available). A stderr sink is not used because reaching +// this point might indicate a problem with the logging system itself so we use fputs() directly. +void err_helper::handle_ex(const std::string &origin, const source_loc &loc, const std::exception &ex) const { + if (custom_err_handler_) { + custom_err_handler_(ex.what()); + return; + } + const auto tm_time = os::localtime(); + char date_buf[32]; + std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time); + std::string msg; + if (loc.empty()) { + msg = fmt_lib::format("[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, origin, ex.what()); + } else { + msg = fmt_lib::format("[*** LOG ERROR ***] [{}({})] [{}] [{}] {}\n", loc.filename, loc.line, date_buf, origin, + ex.what()); + } + std::fputs(msg.c_str(), stderr); +} + +void err_helper::handle_unknown_ex(const std::string &origin, const source_loc &loc) const { + handle_ex(origin, loc, std::runtime_error("unknown exception")); +} + +void err_helper::set_err_handler(err_handler handler) { + custom_err_handler_ = std::move(handler); +} + +} // namespace details +} // namespace spdlog diff --git a/src/details/log_msg_buffer.cpp b/src/details/log_msg_buffer.cpp deleted file mode 100644 index aadde4ffc..000000000 --- a/src/details/log_msg_buffer.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#include "spdlog/details/log_msg_buffer.h" - -namespace spdlog { -namespace details { - -// copy logger name and payload to buffer so can be used asynchronously -// note: source location pointers are copied without allocation since they -// are compiler generated const chars* (__FILE__, __LINE__, __FUNCTION__) -// if you pass custom strings to source location, make sure they outlive the log_msg_buffer -log_msg_buffer::log_msg_buffer(const log_msg &orig_msg) - : log_msg{orig_msg} { - buffer.append(logger_name); - buffer.append(payload); - update_string_views(); -} - -log_msg_buffer::log_msg_buffer(const log_msg_buffer &other) - : log_msg{other} { - buffer.append(logger_name); - buffer.append(payload); - update_string_views(); -} - -log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) noexcept - : log_msg{other}, - buffer{std::move(other.buffer)} { - update_string_views(); -} - -log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &other) { - log_msg::operator=(other); - buffer.clear(); - buffer.append(other.buffer.data(), other.buffer.data() + other.buffer.size()); - update_string_views(); - return *this; -} - -log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other) noexcept { - log_msg::operator=(other); - buffer = std::move(other.buffer); - update_string_views(); - return *this; -} - -void log_msg_buffer::update_string_views() { - logger_name = string_view_t{buffer.data(), logger_name.size()}; - payload = string_view_t{buffer.data() + logger_name.size(), payload.size()}; -} - -} // namespace details -} // namespace spdlog diff --git a/src/details/thread_pool.cpp b/src/details/thread_pool.cpp deleted file mode 100644 index 584241f42..000000000 --- a/src/details/thread_pool.cpp +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#include "spdlog/details/thread_pool.h" - -#include - -#include "spdlog/common.h" - -namespace spdlog { -namespace details { - -thread_pool::thread_pool(size_t q_max_items, - size_t threads_n, - std::function on_thread_start, - std::function on_thread_stop) - : 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([this, on_thread_start, on_thread_stop] { - on_thread_start(); - this->thread_pool::worker_loop_(); - on_thread_stop(); - }); - } -} - -thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std::function on_thread_start) - : thread_pool(q_max_items, threads_n, on_thread_start, [] {}) {} - -thread_pool::thread_pool(size_t q_max_items, size_t threads_n) - : thread_pool(q_max_items, threads_n, [] {}, [] {}) {} - -// message all threads to terminate gracefully join them -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 thread_pool::post_log(async_logger_ptr &&worker_ptr, const 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 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 thread_pool::overrun_counter() { return q_.overrun_counter(); } - -void thread_pool::reset_overrun_counter() { q_.reset_overrun_counter(); } - -size_t thread_pool::discard_counter() { return q_.discard_counter(); } - -void thread_pool::reset_discard_counter() { q_.reset_discard_counter(); } - -size_t thread_pool::queue_size() { return q_.size(); } - -void 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 if (overflow_policy == async_overflow_policy::overrun_oldest) { - q_.enqueue_nowait(std::move(new_msg)); - } else { - assert(overflow_policy == async_overflow_policy::discard_new); - q_.enqueue_if_have_room(std::move(new_msg)); - } -} - -void 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 thread_pool::process_next_msg_() { - async_msg incoming_async_msg; - q_.dequeue(incoming_async_msg); - - switch (incoming_async_msg.msg_type) { - case async_msg_type::log: { - incoming_async_msg.worker_ptr->backend_sink_it_(incoming_async_msg); - return true; - } - case async_msg_type::flush: { - incoming_async_msg.worker_ptr->backend_flush_(); - return true; - } - - case async_msg_type::terminate: { - return false; - } - - default: { - assert(false); - } - } - - return true; -} - -} // namespace details -} // namespace spdlog diff --git a/src/logger.cpp b/src/logger.cpp index 600efdc1a..ee8fef21a 100644 --- a/src/logger.cpp +++ b/src/logger.cpp @@ -3,9 +3,6 @@ #include "spdlog/logger.h" -#include -#include - #include "spdlog/pattern_formatter.h" #include "spdlog/sinks/sink.h" @@ -17,14 +14,14 @@ logger::logger(const logger &other) noexcept sinks_(other.sinks_), level_(other.level_.load(std::memory_order_relaxed)), flush_level_(other.flush_level_.load(std::memory_order_relaxed)), - custom_err_handler_(other.custom_err_handler_) {} + err_helper_(other.err_helper_) {} logger::logger(logger &&other) noexcept : name_(std::move(other.name_)), sinks_(std::move(other.sinks_)), level_(other.level_.load(std::memory_order_relaxed)), flush_level_(other.flush_level_.load(std::memory_order_relaxed)), - custom_err_handler_(std::move(other.custom_err_handler_)) {} + err_helper_(std::move(other.err_helper_)) {} void logger::set_level(level level) { level_.store(level); } @@ -62,8 +59,8 @@ const std::vector &logger::sinks() const { return sinks_; } std::vector &logger::sinks() { return sinks_; } -// error handler -void logger::set_error_handler(err_handler handler) { custom_err_handler_ = std::move(handler); } +// custom error handler +void logger::set_error_handler(err_handler handler) { err_helper_.set_err_handler(std::move(handler)); } // create new logger with same sinks and configuration. std::shared_ptr logger::clone(std::string logger_name) { @@ -77,30 +74,17 @@ void logger::flush_() { for (auto &sink : sinks_) { try { sink->flush(); + } catch (const std::exception &ex) { + err_helper_.handle_ex(name_, source_loc{}, ex); + } catch (...) { + err_helper_.handle_unknown_ex(name_, source_loc{}); } - SPDLOG_LOGGER_CATCH(source_loc()) } } bool logger::should_flush_(const details::log_msg &msg) const { - auto flush_level = flush_level_.load(std::memory_order_relaxed); + const auto flush_level = flush_level_.load(std::memory_order_relaxed); return (msg.log_level >= flush_level) && (msg.log_level != level::off); } -void logger::err_handler_(const std::string &msg) { - if (custom_err_handler_) { - custom_err_handler_(msg); - } else { - using std::chrono::system_clock; - auto now = system_clock::now(); - auto tm_time = details::os::localtime(system_clock::to_time_t(now)); - char date_buf[64]; - std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time); -#if defined(USING_R) && defined(R_R_H) // if in R environment - REprintf("[*** LOG ERROR ***] [%s] [%s] %s\n", date_buf, name().c_str(), msg.c_str()); -#else - std::fprintf(stderr, "[*** LOG ERROR ***] [%s] [%s] %s\n", date_buf, name().c_str(), msg.c_str()); -#endif - } -} } // namespace spdlog diff --git a/src/pattern_formatter.cpp b/src/pattern_formatter.cpp index 90dcb73bf..d5248133c 100644 --- a/src/pattern_formatter.cpp +++ b/src/pattern_formatter.cpp @@ -16,7 +16,6 @@ #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" namespace spdlog { diff --git a/src/sinks/ansicolor_sink.cpp b/src/sinks/ansicolor_sink.cpp index d0bce4816..c4d02b646 100644 --- a/src/sinks/ansicolor_sink.cpp +++ b/src/sinks/ansicolor_sink.cpp @@ -5,7 +5,6 @@ #include -#include "spdlog/details/null_mutex.h" #include "spdlog/details/os.h" #include "spdlog/pattern_formatter.h" @@ -110,12 +109,13 @@ template ansicolor_stderr_sink::ansicolor_stderr_sink(color_mode mode) : ansicolor_sink(stderr, mode) {} + } // namespace sinks } // namespace spdlog // template instantiations +#include "spdlog/details/null_mutex.h" template class SPDLOG_API spdlog::sinks::ansicolor_stdout_sink; template class SPDLOG_API spdlog::sinks::ansicolor_stdout_sink; - template class SPDLOG_API spdlog::sinks::ansicolor_stderr_sink; template class SPDLOG_API spdlog::sinks::ansicolor_stderr_sink; diff --git a/src/sinks/async_sink.cpp b/src/sinks/async_sink.cpp new file mode 100644 index 000000000..6a7f6f991 --- /dev/null +++ b/src/sinks/async_sink.cpp @@ -0,0 +1,130 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#include "spdlog/sinks/async_sink.h" + +#include +#include + +#include "spdlog/common.h" +#include "spdlog/details/mpmc_blocking_q.h" +#include "spdlog/pattern_formatter.h" +#include "spdlog/spdlog.h" + +namespace spdlog { +namespace sinks { + +async_sink::async_sink(config async_config) + : config_(std::move(async_config)) { + if (config_.queue_size == 0 || config_.queue_size > max_queue_size) { + throw spdlog_ex("async_sink: invalid queue size"); + } + q_ = std::make_unique(config_.queue_size); + worker_thread_ = std::thread([this] { + if (config_.on_thread_start) config_.on_thread_start(); + this->backend_loop_(); + if (config_.on_thread_stop) config_.on_thread_stop(); + }); +} + +async_sink::~async_sink() { + try { + q_->enqueue(async_log_msg(async_log_msg::type::terminate)); + worker_thread_.join(); + } catch (...) { + printf("Exception in ~async_sink()\n"); + } +} + +void async_sink::log(const details::log_msg &msg) { send_message_(async_log_msg::type::log, msg); } + +void async_sink::flush() { send_message_(async_log_msg::type::flush, details::log_msg()); } + +void async_sink::set_pattern(const std::string &pattern) { set_formatter(std::make_unique(pattern)); } + +void async_sink::set_formatter(std::unique_ptr formatter) { + const auto &sinks = config_.sinks; + for (auto it = sinks.begin(); it != sinks.end(); ++it) { + if (std::next(it) == sinks.end()) { + // last element - we can move it. + (*it)->set_formatter(std::move(formatter)); + break; // to prevent clang-tidy warning + } + (*it)->set_formatter(formatter->clone()); + } +} + +size_t async_sink::get_overrun_counter() const { return q_->overrun_counter(); } + +void async_sink::reset_overrun_counter() const { q_->reset_overrun_counter(); } + +size_t async_sink::get_discard_counter() const { return q_->discard_counter(); } + +void async_sink::reset_discard_counter() const { q_->reset_discard_counter(); } + +const async_sink::config &async_sink::get_config() const { return config_; } + +// private methods +void async_sink::send_message_(async_log_msg::type msg_type, const details::log_msg &msg) const { + switch (config_.policy) { + case overflow_policy::block: + q_->enqueue(async_log_msg(msg_type, msg)); + break; + case overflow_policy::overrun_oldest: + q_->enqueue_nowait(async_log_msg(msg_type, msg)); + break; + case overflow_policy::discard_new: + q_->enqueue_if_have_room(async_log_msg(msg_type, msg)); + break; + default: + assert(false); + throw spdlog_ex("async_sink: invalid overflow policy"); + } +} + +void async_sink::backend_loop_() { + details::async_log_msg incoming_msg; + for (;;) { + q_->dequeue(incoming_msg); + switch (incoming_msg.message_type()) { + case async_log_msg::type::log: + backend_log_(incoming_msg); + break; + case async_log_msg::type::flush: + backend_flush_(); + break; + case async_log_msg::type::terminate: + return; + default: + assert(false); + } + } +} + +void async_sink::backend_log_(const details::log_msg &msg) { + for (const auto &sink : config_.sinks) { + if (sink->should_log(msg.log_level)) { + try { + sink->log(msg); + } catch (const std::exception &ex) { + err_helper_.handle_ex("async log", msg.source, ex); + } catch (...) { + err_helper_.handle_unknown_ex("async log", source_loc{}); + } + } + } +} + +void async_sink::backend_flush_() { + for (const auto &sink : config_.sinks) { + try { + sink->flush(); + } catch (const std::exception &ex) { + err_helper_.handle_ex("async flush", source_loc{}, ex); + } catch (...) { + err_helper_.handle_unknown_ex("async flush", source_loc{}); + } + } +} +} // namespace sinks +} // namespace spdlog diff --git a/src/sinks/base_sink.cpp b/src/sinks/base_sink.cpp index c5efae8fb..3ac28a46f 100644 --- a/src/sinks/base_sink.cpp +++ b/src/sinks/base_sink.cpp @@ -5,53 +5,58 @@ #include #include - #include "spdlog/common.h" -#include "spdlog/details/null_mutex.h" #include "spdlog/pattern_formatter.h" +namespace spdlog { +namespace sinks { + template -spdlog::sinks::base_sink::base_sink() +base_sink::base_sink() : formatter_{std::make_unique()} {} template -spdlog::sinks::base_sink::base_sink(std::unique_ptr formatter) +base_sink::base_sink(std::unique_ptr formatter) : formatter_{std::move(formatter)} {} template -void spdlog::sinks::base_sink::log(const details::log_msg &msg) { +void base_sink::log(const details::log_msg &msg) { std::lock_guard lock(mutex_); sink_it_(msg); } template -void spdlog::sinks::base_sink::flush() { +void base_sink::flush() { std::lock_guard lock(mutex_); flush_(); } template -void spdlog::sinks::base_sink::set_pattern(const std::string &pattern) { +void base_sink::set_pattern(const std::string &pattern) { std::lock_guard lock(mutex_); set_pattern_(pattern); } template -void spdlog::sinks::base_sink::set_formatter(std::unique_ptr sink_formatter) { +void base_sink::set_formatter(std::unique_ptr sink_formatter) { std::lock_guard lock(mutex_); set_formatter_(std::move(sink_formatter)); } template -void spdlog::sinks::base_sink::set_pattern_(const std::string &pattern) { - set_formatter_(std::make_unique(pattern)); +void base_sink::set_pattern_(const std::string &pattern) { + set_formatter_(std::make_unique(pattern)); } template -void spdlog::sinks::base_sink::set_formatter_(std::unique_ptr sink_formatter) { +void base_sink::set_formatter_(std::unique_ptr sink_formatter) { formatter_ = std::move(sink_formatter); } +} // namespace sinks +} // namespace spdlog + // template instantiations +#include "spdlog/details/null_mutex.h" template class SPDLOG_API spdlog::sinks::base_sink; template class SPDLOG_API spdlog::sinks::base_sink; diff --git a/src/sinks/basic_file_sink.cpp b/src/sinks/basic_file_sink.cpp index b05dae31e..a48d3a906 100644 --- a/src/sinks/basic_file_sink.cpp +++ b/src/sinks/basic_file_sink.cpp @@ -2,8 +2,8 @@ // Distributed under the MIT License (http://opensource.org/licenses/MIT) #include "spdlog/sinks/basic_file_sink.h" - #include "spdlog/common.h" +#include namespace spdlog { namespace sinks { @@ -34,6 +34,8 @@ void basic_file_sink::flush_() { } // namespace sinks } // namespace spdlog + // template instantiations +#include "spdlog/details/null_mutex.h" template class SPDLOG_API spdlog::sinks::basic_file_sink; template class SPDLOG_API spdlog::sinks::basic_file_sink; \ No newline at end of file diff --git a/src/sinks/rotating_file_sink.cpp b/src/sinks/rotating_file_sink.cpp index 776dbf0f0..b58115bda 100644 --- a/src/sinks/rotating_file_sink.cpp +++ b/src/sinks/rotating_file_sink.cpp @@ -140,5 +140,6 @@ bool rotating_file_sink::rename_file_(const filename_t &src_filename, con } // namespace spdlog // template instantiations +#include "spdlog/details/null_mutex.h" template class SPDLOG_API spdlog::sinks::rotating_file_sink; -template class SPDLOG_API spdlog::sinks::rotating_file_sink; +template class SPDLOG_API spdlog::sinks::rotating_file_sink; \ No newline at end of file diff --git a/src/sinks/sink.cpp b/src/sinks/sink.cpp deleted file mode 100644 index c0d8509e5..000000000 --- a/src/sinks/sink.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#include "spdlog/sinks/sink.h" - -#include "spdlog/common.h" - -bool spdlog::sinks::sink::should_log(spdlog::level msg_level) const { - return msg_level >= level_.load(std::memory_order_relaxed); -} - -void spdlog::sinks::sink::set_level(level level) { level_.store(level, std::memory_order_relaxed); } - -spdlog::level spdlog::sinks::sink::log_level() const { return level_.load(std::memory_order_relaxed); } diff --git a/src/sinks/stdout_color_sinks.cpp b/src/sinks/stdout_color_sinks.cpp deleted file mode 100644 index c01b326c2..000000000 --- a/src/sinks/stdout_color_sinks.cpp +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. -// Distributed under the MIT License (http://opensource.org/licenses/MIT) - -#include "spdlog/sinks/stdout_color_sinks.h" - -#include "spdlog/async.h" -#include "spdlog/common.h" -#include "spdlog/details/synchronous_factory.h" -#include "spdlog/logger.h" - -namespace spdlog { - -template -std::shared_ptr stdout_color_mt(const std::string &logger_name, color_mode mode) { - return Factory::template create(logger_name, mode); -} - -template -std::shared_ptr stdout_color_st(const std::string &logger_name, color_mode mode) { - return Factory::template create(logger_name, mode); -} - -template -std::shared_ptr stderr_color_mt(const std::string &logger_name, color_mode mode) { - return Factory::template create(logger_name, mode); -} - -template -std::shared_ptr stderr_color_st(const std::string &logger_name, color_mode mode) { - return Factory::template create(logger_name, mode); -} -} // namespace spdlog - -// template instantiations -template SPDLOG_API std::shared_ptr spdlog::stdout_color_mt( - const std::string &logger_name, color_mode mode); - -template SPDLOG_API std::shared_ptr spdlog::stdout_color_st( - const std::string &logger_name, color_mode mode); - -template SPDLOG_API std::shared_ptr spdlog::stderr_color_mt( - const std::string &logger_name, color_mode mode); - -template SPDLOG_API std::shared_ptr spdlog::stderr_color_st( - const std::string &logger_name, color_mode mode); - -template SPDLOG_API std::shared_ptr spdlog::stdout_color_mt(const std::string &logger_name, - color_mode mode); - -template SPDLOG_API std::shared_ptr spdlog::stdout_color_st(const std::string &logger_name, - color_mode mode); - -template SPDLOG_API std::shared_ptr spdlog::stderr_color_mt(const std::string &logger_name, - color_mode mode); - -template SPDLOG_API std::shared_ptr spdlog::stderr_color_st(const std::string &logger_name, - color_mode mode); diff --git a/src/sinks/stdout_sinks.cpp b/src/sinks/stdout_sinks.cpp index ad081d8fd..450edb583 100644 --- a/src/sinks/stdout_sinks.cpp +++ b/src/sinks/stdout_sinks.cpp @@ -4,6 +4,7 @@ #include "spdlog/sinks/stdout_sinks.h" #include +#include #include "spdlog/details/os.h" #include "spdlog/pattern_formatter.h" @@ -82,55 +83,11 @@ stderr_sink::stderr_sink() : stdout_sink_base(stderr) {} } // namespace sinks - -// factory methods -template -std::shared_ptr stdout_logger_mt(const std::string &logger_name) { - return Factory::template create(logger_name); -} - -template -std::shared_ptr stdout_logger_st(const std::string &logger_name) { - return Factory::template create(logger_name); -} - -template -std::shared_ptr stderr_logger_mt(const std::string &logger_name) { - return Factory::template create(logger_name); -} - -template -std::shared_ptr stderr_logger_st(const std::string &logger_name) { - return Factory::template create(logger_name); -} } // namespace spdlog -// template instantiations for stdout/stderr loggers -template class SPDLOG_API spdlog::sinks::stdout_sink_base; -template class SPDLOG_API spdlog::sinks::stdout_sink_base; +// template instantiations +#include "spdlog/details/null_mutex.h" template class SPDLOG_API spdlog::sinks::stdout_sink; template class SPDLOG_API spdlog::sinks::stdout_sink; template class SPDLOG_API spdlog::sinks::stderr_sink; -template class SPDLOG_API spdlog::sinks::stderr_sink; - -// template instantiations for stdout/stderr factory functions -#include "spdlog/async.h" -#include "spdlog/details/synchronous_factory.h" - -template SPDLOG_API std::shared_ptr spdlog::stdout_logger_mt( - const std::string &logger_name); -template SPDLOG_API std::shared_ptr spdlog::stdout_logger_st( - const std::string &logger_name); -template SPDLOG_API std::shared_ptr spdlog::stderr_logger_mt( - const std::string &logger_name); -template SPDLOG_API std::shared_ptr spdlog::stderr_logger_st( - const std::string &logger_name); - -template SPDLOG_API std::shared_ptr spdlog::stdout_logger_mt( - const std::string &logger_name); -template SPDLOG_API std::shared_ptr spdlog::stdout_logger_st( - const std::string &logger_name); -template SPDLOG_API std::shared_ptr spdlog::stderr_logger_mt( - const std::string &logger_name); -template SPDLOG_API std::shared_ptr spdlog::stderr_logger_st( - const std::string &logger_name); +template class SPDLOG_API spdlog::sinks::stderr_sink; \ No newline at end of file diff --git a/src/sinks/wincolor_sink.cpp b/src/sinks/wincolor_sink.cpp index f71dd1304..5fa8542ed 100644 --- a/src/sinks/wincolor_sink.cpp +++ b/src/sinks/wincolor_sink.cpp @@ -5,12 +5,10 @@ #include "spdlog/details/windows_include.h" #include #include -// clang-format on +// clang-format on #include "spdlog/sinks/wincolor_sink.h" - #include "spdlog/common.h" -#include "spdlog/details/null_mutex.h" namespace spdlog { namespace sinks { @@ -132,15 +130,14 @@ wincolor_stdout_sink::wincolor_stdout_sink(color_mode mode) template wincolor_stderr_sink::wincolor_stderr_sink(color_mode mode) : wincolor_sink(::GetStdHandle(STD_ERROR_HANDLE), mode) {} + + } // namespace sinks } // namespace spdlog // template instantiations -template class SPDLOG_API spdlog::sinks::wincolor_sink; -template class SPDLOG_API spdlog::sinks::wincolor_sink; - +#include "spdlog/details/null_mutex.h" template class SPDLOG_API spdlog::sinks::wincolor_stdout_sink; template class SPDLOG_API spdlog::sinks::wincolor_stdout_sink; - template class SPDLOG_API spdlog::sinks::wincolor_stderr_sink; template class SPDLOG_API spdlog::sinks::wincolor_stderr_sink; diff --git a/src/spdlog.cpp b/src/spdlog.cpp index f364fa3c1..667099ce5 100644 --- a/src/spdlog.cpp +++ b/src/spdlog.cpp @@ -13,27 +13,20 @@ namespace spdlog { -static std::shared_ptr s_context = + #ifndef SPDLOG_DISABLE_GLOBAL_LOGGER - std::make_unique(std::make_unique(std::string(), std::make_unique())); + static std::shared_ptr s_logger = std::make_shared("global", std::make_shared()); #else - std::make_unique(); // empty context + static std::short_ptr s_logger = nullptr; #endif -void set_context(std::shared_ptr context) { s_context = std::move(context); } - -std::shared_ptr context() { return s_context; } - -const std::shared_ptr &context_ref() { return s_context; } -std::shared_ptr global_logger() { return context_ref()->global_logger(); } +std::shared_ptr global_logger() { return s_logger; } -void set_global_logger(std::shared_ptr global_logger) { context()->set_logger(std::move(global_logger)); } +void set_global_logger(std::shared_ptr global_logger) { s_logger = std::move(global_logger); } logger *global_logger_raw() noexcept { - auto *rv = context_ref()->global_logger_raw(); - assert(rv != nullptr); - return rv; + return s_logger.get(); } void set_formatter(std::unique_ptr formatter) { global_logger()->set_formatter(std::move(formatter)); } @@ -52,6 +45,6 @@ void flush_on(level level) { global_logger()->flush_on(level); } void set_error_handler(void (*handler)(const std::string &msg)) { global_logger()->set_error_handler(handler); } -void shutdown() { s_context.reset(); } +void shutdown() { s_logger.reset(); } } // namespace spdlog diff --git a/tests/includes.h b/tests/includes.h index f45174b59..e2fb6aadd 100644 --- a/tests/includes.h +++ b/tests/includes.h @@ -25,7 +25,6 @@ #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG -#include "spdlog/async.h" #include "spdlog/details/fmt_helper.h" #include "spdlog/pattern_formatter.h" #include "spdlog/sinks/null_sink.h" diff --git a/tests/test_async.cpp b/tests/test_async.cpp index 8c8a7e667..90b163a54 100644 --- a/tests/test_async.cpp +++ b/tests/test_async.cpp @@ -1,86 +1,98 @@ +#include + #include "includes.h" -#include "spdlog/async.h" +#include "spdlog/sinks/async_sink.h" #include "spdlog/sinks/basic_file_sink.h" #include "test_sink.h" #define TEST_FILENAME "test_logs/async_test.log" +using spdlog::sinks::async_sink; +using spdlog::sinks::sink; +using spdlog::sinks::test_sink_mt; + +auto creat_async_logger(size_t queue_size, std::shared_ptr backend_sink) { + async_sink::config cfg; + cfg.queue_size = queue_size; + cfg.sinks.push_back(std::move(backend_sink)); + auto s = std::make_shared(cfg); + auto logger = std::make_shared("async_logger", s); + return std::make_tuple(logger, s); +} + TEST_CASE("basic async test ", "[async]") { - auto test_sink = std::make_shared(); + const auto test_sink = std::make_shared(); size_t overrun_counter = 0; - size_t queue_size = 128; size_t messages = 256; { - auto tp = std::make_shared(queue_size, 1); - auto logger = std::make_shared("as", test_sink, tp, spdlog::async_overflow_policy::block); + constexpr size_t queue_size = 16; + auto [logger, async_sink] = creat_async_logger(queue_size, test_sink); for (size_t i = 0; i < messages; i++) { logger->info("Hello message #{}", i); } logger->flush(); - overrun_counter = tp->overrun_counter(); + overrun_counter = async_sink->get_overrun_counter(); } + // logger and async_sink are destroyed here so the queue should be emptied REQUIRE(test_sink->msg_counter() == messages); REQUIRE(test_sink->flush_counter() == 1); REQUIRE(overrun_counter == 0); } TEST_CASE("discard policy ", "[async]") { - auto test_sink = std::make_shared(); + auto test_sink = std::make_shared(); test_sink->set_delay(std::chrono::milliseconds(1)); - size_t queue_size = 4; + async_sink::config config; + config.queue_size = 4; + config.policy = async_sink::overflow_policy::overrun_oldest; + config.sinks.push_back(test_sink); size_t messages = 1024; - - auto tp = std::make_shared(queue_size, 1); - auto logger = std::make_shared("as", test_sink, tp, spdlog::async_overflow_policy::overrun_oldest); + auto as = std::make_shared(config); + auto logger = std::make_shared("async_logger", as); + REQUIRE(as->get_discard_counter() == 0); + REQUIRE(as->get_overrun_counter() == 0); for (size_t i = 0; i < messages; i++) { logger->info("Hello message"); } REQUIRE(test_sink->msg_counter() < messages); - REQUIRE(tp->overrun_counter() > 0); + REQUIRE(as->get_overrun_counter() > 0); + as->reset_overrun_counter(); + REQUIRE(as->get_overrun_counter() == 0); } TEST_CASE("discard policy discard_new ", "[async]") { - auto test_sink = std::make_shared(); + auto test_sink = std::make_shared(); test_sink->set_delay(std::chrono::milliseconds(1)); - size_t queue_size = 4; + async_sink::config config; + config.queue_size = 4; + config.policy = async_sink::overflow_policy::discard_new; + config.sinks.push_back(test_sink); size_t messages = 1024; + auto as = std::make_shared(config); + auto logger = std::make_shared("async_logger", as); - auto tp = std::make_shared(queue_size, 1); - auto logger = std::make_shared("as", test_sink, tp, spdlog::async_overflow_policy::discard_new); - for (size_t i = 0; i < messages; i++) { - logger->info("Hello message"); - } - REQUIRE(test_sink->msg_counter() < messages); - REQUIRE(tp->discard_counter() > 0); -} - -TEST_CASE("discard policy using factory ", "[async]") { - 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(3)); + REQUIRE(as->get_config().policy == async_sink::overflow_policy::discard_new); + REQUIRE(as->get_discard_counter() == 0); + REQUIRE(as->get_overrun_counter() == 0); for (size_t i = 0; i < messages; i++) { logger->info("Hello message"); } - REQUIRE(test_sink->msg_counter() < messages); + REQUIRE(as->get_discard_counter() > 0); + as->reset_discard_counter(); + REQUIRE(as->get_discard_counter() == 0); } TEST_CASE("flush", "[async]") { - auto test_sink = std::make_shared(); - size_t queue_size = 256; + auto test_sink = std::make_shared(); size_t messages = 256; { - auto tp = std::make_shared(queue_size, 1); - auto logger = std::make_shared("as", test_sink, tp, spdlog::async_overflow_policy::block); + constexpr size_t queue_size = 256; + auto [logger, async_sink] = creat_async_logger(queue_size, test_sink); for (size_t i = 0; i < messages; i++) { logger->info("Hello message #{}", i); } - logger->flush(); } // std::this_thread::sleep_for(std::chrono::milliseconds(250)); @@ -88,18 +100,24 @@ TEST_CASE("flush", "[async]") { REQUIRE(test_sink->flush_counter() == 1); } -TEST_CASE("tp->wait_empty() ", "[async]") { - auto test_sink = std::make_shared(); +TEST_CASE("wait_dtor ", "[async]") { + auto test_sink = std::make_shared(); test_sink->set_delay(std::chrono::milliseconds(5)); + async_sink::config config; + config.sinks.push_back(test_sink); + config.queue_size = 4; + config.policy = async_sink::overflow_policy::block; size_t messages = 100; - - auto tp = std::make_shared(messages, 2); - auto logger = std::make_shared("as", test_sink, tp, spdlog::async_overflow_policy::block); - for (size_t i = 0; i < messages; i++) { - logger->info("Hello message #{}", i); + { + auto as = std::make_shared(config); + auto logger = std::make_shared("async_logger", as); + for (size_t i = 0; i < messages; i++) { + logger->info("Hello message #{}", i); + } + logger->flush(); + REQUIRE(as->get_overrun_counter() == 0); + REQUIRE(as->get_discard_counter() == 0); } - logger->flush(); - tp.reset(); REQUIRE(test_sink->msg_counter() == messages); REQUIRE(test_sink->flush_counter() == 1); @@ -107,18 +125,17 @@ TEST_CASE("tp->wait_empty() ", "[async]") { TEST_CASE("multi threads", "[async]") { auto test_sink = std::make_shared(); - size_t queue_size = 128; size_t messages = 256; size_t n_threads = 10; { - auto tp = std::make_shared(queue_size, 1); - auto logger = std::make_shared("as", test_sink, tp, spdlog::async_overflow_policy::block); + constexpr size_t queue_size = 128; + auto [logger, async_sink] = creat_async_logger(queue_size, test_sink); std::vector threads; for (size_t i = 0; i < n_threads; i++) { - threads.emplace_back([logger, messages] { - for (size_t j = 0; j < messages; j++) { - logger->info("Hello message #{}", j); + threads.emplace_back([l = logger, msgs = messages] { + for (size_t j = 0; j < msgs; j++) { + l->info("Hello message #{}", j); } }); logger->flush(); @@ -128,7 +145,6 @@ TEST_CASE("multi threads", "[async]") { t.join(); } } - REQUIRE(test_sink->msg_counter() == messages * n_threads); REQUIRE(test_sink->flush_counter() == n_threads); } @@ -136,45 +152,146 @@ TEST_CASE("multi threads", "[async]") { TEST_CASE("to_file", "[async]") { prepare_logdir(); size_t messages = 1024; - size_t tp_threads = 1; - spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME); { + spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME); auto file_sink = std::make_shared(filename, true); - auto tp = std::make_shared(messages, tp_threads); - auto logger = std::make_shared("as", std::move(file_sink), std::move(tp)); + auto [logger, async_sink] = creat_async_logger(messages, file_sink); for (size_t j = 0; j < messages; j++) { logger->info("Hello message #{}", j); } } - require_message_count(TEST_FILENAME, messages); auto contents = file_contents(TEST_FILENAME); using spdlog::details::os::default_eol; REQUIRE(ends_with(contents, spdlog::fmt_lib::format("Hello message #1023{}", default_eol))); } -TEST_CASE("to_file multi-workers", "[async]") { + +TEST_CASE("bad_ctor", "[async]") { + async_sink::config cfg; + cfg.queue_size = 0; + REQUIRE_THROWS_AS(std::make_shared(cfg), spdlog::spdlog_ex); +} + +TEST_CASE("bad_ctor2", "[async]") { + async_sink::config cfg; + cfg.queue_size = async_sink::max_queue_size + 1; + REQUIRE_THROWS_AS(std::make_shared(cfg), spdlog::spdlog_ex); +} + +TEST_CASE("start_stop_clbks", "[async]") { + bool start_called = false; + bool stop_called = false; + { + async_sink::config cfg; + cfg.on_thread_start = [&] { start_called = true; }; + cfg.on_thread_stop = [&] { stop_called = true; }; + auto sink = std::make_shared(cfg); + } + REQUIRE(start_called); + REQUIRE(stop_called); +} + +TEST_CASE("start_stop_clbks2", "[async]") { + bool start_called = false; + bool stop_called = false; + { + async_sink::config cfg; + cfg.on_thread_start = [&] { start_called = true; }; + auto sink = std::make_shared(cfg); + } + REQUIRE(start_called); + REQUIRE_FALSE(stop_called); +} + +TEST_CASE("start_stop_clbks3", "[async]") { + bool start_called = false; + bool stop_called = false; + { + async_sink::config cfg; + cfg.on_thread_start = nullptr; + cfg.on_thread_stop = [&] { stop_called = true; }; + auto sink = std::make_shared(cfg); + } + REQUIRE_FALSE(start_called); + REQUIRE(stop_called); +} + +TEST_CASE("start_stop_clbks4", "[async]") { + bool start_called = false; + bool stop_called = false; + { + async_sink::config cfg; + cfg.on_thread_start = [&] { start_called = true; }; + cfg.on_thread_stop = [&] { stop_called = true; }; + cfg.queue_size = 128; + auto sink = std::make_shared(cfg); + } + REQUIRE(start_called); + REQUIRE(stop_called); +} + +// should not start threads if queue size is invalid +TEST_CASE("start_stop_clbks5", "[async]") { + bool start_called = false; + bool stop_called = false; + { + async_sink::config cfg; + cfg.on_thread_start = [&] { start_called = true; }; + cfg.on_thread_stop = [&] { stop_called = true; }; + cfg.queue_size = 0; + REQUIRE_THROWS_AS(std::make_shared(cfg), spdlog::spdlog_ex); + } + REQUIRE_FALSE(start_called); + REQUIRE_FALSE(stop_called); +} + +TEST_CASE("multi-sinks", "[async]") { prepare_logdir(); - size_t messages = 1024 * 10; - size_t tp_threads = 10; - spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME); + auto test_sink1 = std::make_shared(); + auto test_sink2 = std::make_shared(); + auto test_sink3 = std::make_shared(); + size_t messages = 1024; { - auto file_sink = std::make_shared(filename, true); - auto tp = std::make_shared(messages, tp_threads); - auto logger = std::make_shared("as", std::move(file_sink), std::move(tp)); + async_sink::config cfg; + cfg.sinks.push_back(test_sink1); + cfg.sinks.push_back(test_sink2); + cfg.sinks.push_back(test_sink3); + auto as = std::make_shared(cfg); + spdlog::logger l("async_logger", as); for (size_t j = 0; j < messages; j++) { - logger->info("Hello message #{}", j); + l.info("Hello message #{}", j); } } - require_message_count(TEST_FILENAME, messages); + REQUIRE(test_sink1->msg_counter() == messages); + REQUIRE(test_sink2->msg_counter() == messages); + REQUIRE(test_sink3->msg_counter() == messages); } -TEST_CASE("bad_tp", "[async]") { - auto test_sink = std::make_shared(); - std::shared_ptr const empty_tp; - auto logger = std::make_shared("as", test_sink, empty_tp); - logger->info("Please throw an exception"); +TEST_CASE("level-off", "[async]") { + const auto test_sink = std::make_shared(); + test_sink->set_level(spdlog::level::critical); + { + constexpr size_t messages = 256; + constexpr size_t queue_size = 16; + auto [logger, async_sink] = creat_async_logger(queue_size, test_sink); + logger->flush_on(spdlog::level::critical); + for (size_t i = 0; i < messages; i++) { + logger->info("Hello message #{}", i); + } + } + // logger and async_sink are destroyed here so the queue should be emptied REQUIRE(test_sink->msg_counter() == 0); + REQUIRE(test_sink->flush_counter() == 0); +} + +TEST_CASE("backend_ex", "[async]") { + const auto test_sink = std::make_shared(); + test_sink->set_exception(std::runtime_error("test backend exception")); + constexpr size_t queue_size = 16; + auto [logger, async_sink] = creat_async_logger(queue_size, test_sink); + REQUIRE_NOTHROW(logger->info("Hello message")); + REQUIRE_NOTHROW(logger->flush()); } diff --git a/tests/test_bin_to_hex.cpp b/tests/test_bin_to_hex.cpp index e3f7e0049..5617013d2 100644 --- a/tests/test_bin_to_hex.cpp +++ b/tests/test_bin_to_hex.cpp @@ -1,5 +1,6 @@ + #include "includes.h" -#include "spdlog/fmt/bin_to_hex.h" +#include "spdlog/bin_to_hex.h" #include "spdlog/sinks/ostream_sink.h" #include "test_sink.h" diff --git a/tests/test_custom_callbacks.cpp b/tests/test_custom_callbacks.cpp index 5f5a9d07c..cfae2119c 100644 --- a/tests/test_custom_callbacks.cpp +++ b/tests/test_custom_callbacks.cpp @@ -3,7 +3,6 @@ * https://raw.githubusercontent.com/gabime/spdlog/v2.x/LICENSE */ #include "includes.h" -#include "spdlog/async.h" #include "spdlog/common.h" #include "spdlog/sinks/callback_sink.h" #include "test_sink.h" diff --git a/tests/test_errors.cpp b/tests/test_errors.cpp index e3914e5dd..aa7d0935c 100644 --- a/tests/test_errors.cpp +++ b/tests/test_errors.cpp @@ -7,94 +7,59 @@ #include "includes.h" #include "spdlog/sinks/basic_file_sink.h" -#define SIMPLE_LOG "test_logs/simple_log.txt" -#define SIMPLE_ASYNC_LOG "test_logs/simple_async_log.txt" +static spdlog::filename_t log_filename = SPDLOG_FILENAME_T("test_logs/simple_log.txt"); +static std::string log_err_msg = "Error during log"; +static std::string flush_err_msg = "Error during flush"; class failing_sink final : public spdlog::sinks::base_sink { protected: - void sink_it_(const spdlog::details::log_msg &) final { throw std::runtime_error("some error happened during log"); } - - void flush_() final { throw std::runtime_error("some error happened during flush"); } + void sink_it_(const spdlog::details::log_msg &) override { throw std::runtime_error(log_err_msg.c_str()); } + void flush_() override { throw std::runtime_error(flush_err_msg.c_str()); } }; struct custom_ex {}; + +using namespace spdlog::sinks; TEST_CASE("default_error_handler", "[errors]") { prepare_logdir(); - spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG); - auto logger = spdlog::basic_logger_mt("test-error", filename); + auto logger = spdlog::create("test-error", log_filename); logger->set_pattern("%v"); logger->info(SPDLOG_FMT_RUNTIME("Test message {} {}"), 1); logger->info("Test message {}", 2); logger->flush(); using spdlog::details::os::default_eol; - REQUIRE(file_contents(SIMPLE_LOG) == spdlog::fmt_lib::format("Test message 2{}", default_eol)); - REQUIRE(count_lines(SIMPLE_LOG) == 1); + REQUIRE(file_contents(log_filename) == spdlog::fmt_lib::format("Test message 2{}", default_eol)); + REQUIRE(count_lines(log_filename) == 1); } TEST_CASE("custom_error_handler", "[errors]") { prepare_logdir(); - spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG); - auto logger = spdlog::basic_logger_mt("test-error", filename); + auto logger = spdlog::create("test-error", log_filename); logger->flush_on(spdlog::level::info); - logger->set_error_handler([=](const std::string &) { throw custom_ex(); }); + logger->set_error_handler([=](const std::string & msg) { + REQUIRE(msg == "argument not found"); + throw custom_ex(); + }); logger->info("Good message #1"); REQUIRE_THROWS_AS(logger->info(SPDLOG_FMT_RUNTIME("Bad format msg {} {}"), "xxx"), custom_ex); logger->info("Good message #2"); - require_message_count(SIMPLE_LOG, 2); + require_message_count(log_filename, 2); } TEST_CASE("default_error_handler2", "[errors]") { auto logger = std::make_shared("failed_logger", std::make_shared()); - logger->set_error_handler([=](const std::string &) { throw custom_ex(); }); + logger->set_error_handler([=](const std::string &msg) { + REQUIRE(msg == log_err_msg); + throw custom_ex(); + }); REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex); } TEST_CASE("flush_error_handler", "[errors]") { auto logger = spdlog::create("failed_logger"); - logger->set_error_handler([=](const std::string &) { throw custom_ex(); }); + logger->set_error_handler([=](const std::string &msg) { + REQUIRE(msg == flush_err_msg); + throw custom_ex(); + }); REQUIRE_THROWS_AS(logger->flush(), custom_ex); } - -TEST_CASE("async_error_handler", "[errors]") { - prepare_logdir(); - std::string err_msg("log failed with some msg"); - - spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_ASYNC_LOG); - { - spdlog::init_thread_pool(128, 1); - auto logger = spdlog::create_async("logger", filename, true); - logger->set_error_handler([=](const std::string &) { - std::ofstream ofs("test_logs/custom_err.txt"); - if (!ofs) { - throw std::runtime_error("Failed open test_logs/custom_err.txt"); - } - ofs << err_msg; - }); - logger->info("Good message #1"); - logger->info(SPDLOG_FMT_RUNTIME("Bad format msg {} {}"), "xxx"); - logger->info("Good message #2"); - } - spdlog::init_thread_pool(128, 1); - require_message_count(SIMPLE_ASYNC_LOG, 2); - REQUIRE(file_contents("test_logs/custom_err.txt") == err_msg); -} - -// Make sure async error handler is executed -TEST_CASE("async_error_handler2", "[errors]") { - prepare_logdir(); - std::string err_msg("This is async handler error message"); - { - spdlog::details::os::create_dir(SPDLOG_FILENAME_T("test_logs")); - spdlog::init_thread_pool(128, 1); - auto logger = spdlog::create_async("failed_logger"); - logger->set_error_handler([=](const std::string &) { - std::ofstream ofs("test_logs/custom_err2.txt"); - if (!ofs) throw std::runtime_error("Failed open test_logs/custom_err2.txt"); - ofs << err_msg; - }); - logger->info("Hello failure"); - } - - spdlog::init_thread_pool(128, 1); - REQUIRE(file_contents("test_logs/custom_err2.txt") == err_msg); -} diff --git a/tests/test_file_helper.cpp b/tests/test_file_helper.cpp index 3b61ff2cf..53c7e83ad 100644 --- a/tests/test_file_helper.cpp +++ b/tests/test_file_helper.cpp @@ -136,7 +136,7 @@ TEST_CASE("file_event_handlers", "[file_helper]") { events.clear(); helper.close(); REQUIRE(events == std::vector{flags::before_close, flags::after_close}); - REQUIRE(file_contents(TEST_FILENAME) == "after_open\nbefore_close\n"); + REQUIRE(file_contents(SPDLOG_FILENAME_T(TEST_FILENAME)) == "after_open\nbefore_close\n"); helper.reopen(true); events.clear(); diff --git a/tests/test_file_logging.cpp b/tests/test_file_logging.cpp index 3ef4a3b4c..56c7650eb 100644 --- a/tests/test_file_logging.cpp +++ b/tests/test_file_logging.cpp @@ -9,10 +9,12 @@ #define SIMPLE_LOG "test_logs/simple_log" #define ROTATING_LOG "test_logs/rotating_log" +using namespace spdlog::sinks; + TEST_CASE("simple_file_logger", "[simple_logger]") { prepare_logdir(); spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG); - auto logger = spdlog::basic_logger_mt("logger", filename); + auto logger = spdlog::create("test-error", filename); logger->set_pattern("%v"); logger->info("Test message {}", 1); logger->info("Test message {}", 2); @@ -25,7 +27,7 @@ TEST_CASE("simple_file_logger", "[simple_logger]") { TEST_CASE("flush_on", "[flush_on]") { prepare_logdir(); spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG); - auto logger = spdlog::basic_logger_mt("test-error", filename); + auto logger = spdlog::create("test-error", filename); logger->set_pattern("%v"); logger->set_level(spdlog::level::trace); logger->flush_on(spdlog::level::info); @@ -43,8 +45,7 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]") { prepare_logdir(); size_t max_size = 1024 * 10; spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG); - auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 0); - + auto logger = spdlog::create("logger", basename, max_size, 0); for (int i = 0; i < 10; ++i) { logger->info("Test message {}", i); } @@ -57,16 +58,14 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]") { prepare_logdir(); size_t max_size = 1024 * 10; spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG); - { // make an initial logger to create the first output file - auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 2, true); + auto logger = spdlog::create("logger", basename, max_size, 2, true); for (int i = 0; i < 10; ++i) { logger->info("Test message {}", i); } } - - auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 2, true); + auto logger = spdlog::create("logger", basename, max_size, 2, true); for (int i = 0; i < 10; ++i) { logger->info("Test message {}", i); } @@ -89,7 +88,7 @@ TEST_CASE("rotating_file_logger3", "[rotating_logger]") { prepare_logdir(); size_t max_size = 0; spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG); - REQUIRE_THROWS_AS(spdlog::rotating_logger_mt("logger", basename, max_size, 0), spdlog::spdlog_ex); + REQUIRE_THROWS_AS(spdlog::create("logger", basename, max_size, 0), spdlog::spdlog_ex); } // test on-demand rotation of logs @@ -99,15 +98,11 @@ TEST_CASE("rotating_file_logger4", "[rotating_logger]") { spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG); auto sink = std::make_shared(basename, max_size, 2); auto logger = std::make_shared("rotating_sink_logger", sink); - logger->info("Test message - pre-rotation"); logger->flush(); - sink->rotate_now(); - logger->info("Test message - post-rotation"); logger->flush(); - REQUIRE(get_filesize(ROTATING_LOG) > 0); REQUIRE(get_filesize(ROTATING_LOG ".1") > 0); } diff --git a/tests/test_macros.cpp b/tests/test_macros.cpp index cffa998a9..47a0d41a5 100644 --- a/tests/test_macros.cpp +++ b/tests/test_macros.cpp @@ -15,7 +15,7 @@ TEST_CASE("debug and trace w/o format string", "[macros]") { prepare_logdir(); spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME); - auto logger = spdlog::basic_logger_mt("logger", filename); + auto logger = spdlog::create("logger", filename); logger->set_pattern("%v"); logger->set_level(spdlog::level::trace); @@ -44,7 +44,7 @@ TEST_CASE("disable param evaluation", "[macros]") { } TEST_CASE("pass logger pointer", "[macros]") { - auto logger = spdlog::null_logger_mt("refmacro"); + auto logger = spdlog::create("refmacro"); auto &ref = *logger; SPDLOG_LOGGER_TRACE(&ref, "Test message 1"); SPDLOG_LOGGER_DEBUG(&ref, "Test message 2"); diff --git a/tests/test_misc.cpp b/tests/test_misc.cpp index f172536bc..304ed9c4b 100644 --- a/tests/test_misc.cpp +++ b/tests/test_misc.cpp @@ -8,6 +8,7 @@ #include "includes.h" #include "spdlog/details/os.h" #include "spdlog/sinks/ostream_sink.h" +#include "spdlog/sinks/async_sink.h" #include "test_sink.h" template @@ -94,25 +95,25 @@ TEST_CASE("clone-logger", "[clone]") { TEST_CASE("clone async", "[clone]") { using spdlog::sinks::test_sink_mt; - spdlog::init_thread_pool(4, 1); auto test_sink = std::make_shared(); - auto logger = std::make_shared("orig", test_sink, spdlog::thread_pool()); - logger->set_pattern("%v"); - auto cloned = logger->clone("clone"); - - REQUIRE(cloned->name() == "clone"); - REQUIRE(logger->sinks() == cloned->sinks()); - REQUIRE(logger->log_level() == cloned->log_level()); - REQUIRE(logger->flush_level() == cloned->flush_level()); - - logger->info("Some message 1"); - cloned->info("Some message 2"); - - spdlog::details::os::sleep_for_millis(100); - + { + auto cfg = spdlog::sinks::async_sink::config(); + cfg.sinks.push_back(test_sink); + auto async_sink = spdlog::sinks::async_sink::with(); + auto logger = spdlog::create("orig", cfg); + logger->set_pattern("*** %v ***"); + auto cloned = logger->clone("clone"); + REQUIRE(cloned->name() == "clone"); + REQUIRE(logger->sinks() == cloned->sinks()); + REQUIRE(logger->log_level() == cloned->log_level()); + REQUIRE(logger->flush_level() == cloned->flush_level()); + + logger->info("Some message 1"); + cloned->info("Some message 2"); + } REQUIRE(test_sink->lines().size() == 2); - REQUIRE(test_sink->lines()[0] == "Some message 1"); - REQUIRE(test_sink->lines()[1] == "Some message 2"); + REQUIRE(test_sink->lines()[0] == "*** Some message 1 ***"); + REQUIRE(test_sink->lines()[1] == "*** Some message 2 ***"); } TEST_CASE("global logger API", "[global logger]") { diff --git a/tests/test_mpmc_q.cpp b/tests/test_mpmc_q.cpp index bc7a37d9c..022c12330 100644 --- a/tests/test_mpmc_q.cpp +++ b/tests/test_mpmc_q.cpp @@ -1,4 +1,5 @@ #include "includes.h" +#include "spdlog/details/mpmc_blocking_q.h" using std::chrono::milliseconds; using test_clock = std::chrono::high_resolution_clock; diff --git a/tests/test_ringbuffer_sink.cpp b/tests/test_ringbuffer_sink.cpp index dc486adcb..be6ac28e9 100644 --- a/tests/test_ringbuffer_sink.cpp +++ b/tests/test_ringbuffer_sink.cpp @@ -37,7 +37,7 @@ TEST_CASE("test_drain_raw", "[ringbuffer_sink]") { } int counter = 0; - sink->drain_raw([&](const spdlog::details::log_msg_buffer &buffer) { + sink->drain_raw([&](const spdlog::details::async_log_msg &buffer) { REQUIRE(buffer.payload.data() == std::to_string(counter + 1)); counter++; }); @@ -68,4 +68,4 @@ TEST_CASE("test_empty_size", "[ringbuffer_sink]") { sink->drain([&](std::string_view) { REQUIRE_FALSE(true); // should not be called since the sink size is 0 }); -} \ No newline at end of file +} diff --git a/tests/test_sink.h b/tests/test_sink.h index 2818b68d4..5bbfd434a 100644 --- a/tests/test_sink.h +++ b/tests/test_sink.h @@ -8,10 +8,10 @@ #include #include #include +#include #include "spdlog/details/null_mutex.h" #include "spdlog/details/os.h" -#include "spdlog/fmt/fmt.h" #include "spdlog/sinks/base_sink.h" namespace spdlog { @@ -37,6 +37,14 @@ class test_sink : public base_sink { delay_ = delay; } + void set_exception(const std::runtime_error& ex) { + exception_ptr_ = std::make_exception_ptr(ex); + } + + void clear_exception() { + exception_ptr_ = nullptr; + } + // return last output without the eol std::vector lines() { std::lock_guard lock(base_sink::mutex_); @@ -45,6 +53,9 @@ class test_sink : public base_sink { protected: void sink_it_(const details::log_msg &msg) override { + if (exception_ptr_) { + std::rethrow_exception(exception_ptr_); + } memory_buf_t formatted; base_sink::formatter_->format(msg, formatted); // save the line without the eol @@ -56,12 +67,18 @@ class test_sink : public base_sink { std::this_thread::sleep_for(delay_); } - void flush_() override { flush_counter_++; } + void flush_() override { + if (exception_ptr_) { + std::rethrow_exception(exception_ptr_); + } + flush_counter_++; + } size_t msg_counter_{0}; size_t flush_counter_{0}; std::chrono::milliseconds delay_{std::chrono::milliseconds::zero()}; std::vector lines_; + std::exception_ptr exception_ptr_; // will be thrown on next log or flush if not null }; using test_sink_mt = test_sink; diff --git a/tests/test_stdout_api.cpp b/tests/test_stdout_api.cpp index 4db6f2184..958352ed1 100644 --- a/tests/test_stdout_api.cpp +++ b/tests/test_stdout_api.cpp @@ -8,7 +8,7 @@ TEST_CASE("stdout_st", "[stdout]") { spdlog::set_pattern("%+"); - auto l = spdlog::stdout_logger_st("test"); + auto l = spdlog::create("test"); l->set_level(spdlog::level::trace); l->trace("Test stdout_st"); l->debug("Test stdout_st"); @@ -19,7 +19,7 @@ TEST_CASE("stdout_st", "[stdout]") { } TEST_CASE("stderr_st", "[stderr]") { - auto l = spdlog::stderr_logger_st("test"); + auto l = spdlog::create("test"); l->set_level(spdlog::level::trace); l->trace("Test stderr_st"); l->debug("Test stderr_st"); @@ -43,7 +43,7 @@ TEST_CASE("stderr_mt", "[stderr]") { // color loggers TEST_CASE("stdout_color_st", "[stdout]") { - auto l = spdlog::stdout_color_st("test"); + auto l = spdlog::create("test"); l->set_pattern("%+"); l->set_level(spdlog::level::trace); l->trace("Test stdout_color_st"); @@ -55,7 +55,7 @@ TEST_CASE("stdout_color_st", "[stdout]") { } TEST_CASE("stdout_color_mt", "[stdout]") { - auto l = spdlog::stdout_color_mt("test"); + auto l = spdlog::create("test"); l->set_pattern("%+"); l->set_level(spdlog::level::trace); l->trace("Test stdout_color_mt"); @@ -67,14 +67,14 @@ TEST_CASE("stdout_color_mt", "[stdout]") { } TEST_CASE("stderr_color_st", "[stderr]") { - auto l = spdlog::stderr_color_st("test"); + auto l = spdlog::create("test"); l->set_pattern("%+"); l->set_level(spdlog::level::debug); l->debug("Test stderr_color_st"); } TEST_CASE("stderr_color_mt", "[stderr]") { - auto l = spdlog::stderr_color_mt("test"); + auto l = spdlog::create("test"); l->set_pattern("%+"); l->info("Test stderr_color_mt"); l->warn("Test stderr_color_mt"); diff --git a/tests/test_time_point.cpp b/tests/test_time_point.cpp index b5e15a4ca..8b5791af8 100644 --- a/tests/test_time_point.cpp +++ b/tests/test_time_point.cpp @@ -1,5 +1,4 @@ #include "includes.h" -#include "spdlog/async.h" #include "test_sink.h" TEST_CASE("time_point1", "[time_point log_msg]") { diff --git a/tests/utils.cpp b/tests/utils.cpp index 5a4a4d190..56e04d084 100644 --- a/tests/utils.cpp +++ b/tests/utils.cpp @@ -4,7 +4,6 @@ #include #else #include - #include #endif void prepare_logdir() { @@ -18,7 +17,7 @@ void prepare_logdir() { #endif } -std::string file_contents(const std::string &filename) { +std::string file_contents(const std::filesystem::path &filename) { std::ifstream ifs(filename, std::ios_base::binary); if (!ifs) { throw std::runtime_error("Failed open file "); @@ -26,7 +25,7 @@ std::string file_contents(const std::string &filename) { return std::string((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); } -std::size_t count_lines(const spdlog::filename_t &filename) { +std::size_t count_lines(const std::filesystem::path &filename) { std::ifstream ifs(filename); if (!ifs) { throw std::runtime_error("Failed open file "); @@ -52,7 +51,7 @@ std::size_t get_filesize(const std::string &filename) { throw std::runtime_error("Failed open file "); } - return static_cast(ifs.tellg()); + return static_cast(ifs.tellg()); } // source: https://stackoverflow.com/a/2072890/192001 @@ -72,7 +71,7 @@ std::size_t count_files(const std::string &folder) { // Start iterating over the files in the folder directory. HANDLE hFind = ::FindFirstFileA((folder + "\\*").c_str(), &ffd); if (hFind != INVALID_HANDLE_VALUE) { - do // Managed to locate and create an handle to that folder. + do // Managed to locate and create a handle to that folder. { if (ffd.cFileName[0] != '.') counter++; } while (::FindNextFileA(hFind, &ffd) != 0); diff --git a/tests/utils.h b/tests/utils.h index af66a937d..949f8f55c 100644 --- a/tests/utils.h +++ b/tests/utils.h @@ -8,7 +8,7 @@ std::size_t count_files(const std::string &folder); void prepare_logdir(); -std::string file_contents(const std::string &filename); +std::string file_contents(const std::filesystem::path &filename); // std::size_t count_lines(const std::string &filename); std::size_t count_lines(const std::filesystem::path &filename); @@ -17,4 +17,4 @@ void require_message_count(const std::filesystem::path &filename, const std::siz std::size_t get_filesize(const std::string &filename); -bool ends_with(std::string const &value, std::string const &ending); \ No newline at end of file +bool ends_with(std::string const &value, std::string const &ending);