mirror of
https://github.com/gabime/spdlog.git
synced 2025-09-29 01:29:35 +08:00
Compare commits
50 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ce0424bb37 | ||
![]() |
1f9272eb7d | ||
![]() |
22122f3901 | ||
![]() |
1b0a1dda33 | ||
![]() |
88715d29e9 | ||
![]() |
22405cf9ae | ||
![]() |
27d8580131 | ||
![]() |
fbffd38030 | ||
![]() |
3a54caee36 | ||
![]() |
9db0ba648a | ||
![]() |
463e41f049 | ||
![]() |
ace82f7da6 | ||
![]() |
b93c0f8e8d | ||
![]() |
aec733b7a9 | ||
![]() |
eb660caa6c | ||
![]() |
af8440b248 | ||
![]() |
214e26e8b2 | ||
![]() |
35060923d9 | ||
![]() |
a8e7527d2d | ||
![]() |
2abfa1628b | ||
![]() |
d6389d696e | ||
![]() |
c563b62aea | ||
![]() |
e3f8349d0c | ||
![]() |
128a9fcc49 | ||
![]() |
43812ddaf1 | ||
![]() |
23b1c4c079 | ||
![]() |
b01f15cb26 | ||
![]() |
370dad3225 | ||
![]() |
418a39f6ce | ||
![]() |
177f2618fb | ||
![]() |
6a794b1dff | ||
![]() |
fbe626d828 | ||
![]() |
47fe6ef92a | ||
![]() |
b9f0243405 | ||
![]() |
873026a254 | ||
![]() |
e99e09eba7 | ||
![]() |
07be1b4767 | ||
![]() |
f00a6550fa | ||
![]() |
391eb198bf | ||
![]() |
3f4cfa72d1 | ||
![]() |
80f00797e3 | ||
![]() |
62bbd87bdb | ||
![]() |
8736ee28e0 | ||
![]() |
82358e8ebe | ||
![]() |
9673c1ba09 | ||
![]() |
5dce654473 | ||
![]() |
be942e0a2d | ||
![]() |
af7b061773 | ||
![]() |
bc2eed7913 | ||
![]() |
b46b6dcb00 |
@@ -1,23 +0,0 @@
|
||||
# Read the Docs configuration file
|
||||
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||
|
||||
# Required
|
||||
version: 2
|
||||
|
||||
# Set the OS, Python version, and other tools you might need
|
||||
build:
|
||||
os: ubuntu-24.04
|
||||
tools:
|
||||
python: "3.13"
|
||||
|
||||
# Build documentation with Mkdocs
|
||||
mkdocs:
|
||||
configuration: docs/mkdocs.yml
|
||||
|
||||
# Optionally, but recommended,
|
||||
# declare the Python requirements required to build your documentation
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
# python:
|
||||
# install:
|
||||
# - requirements: docs/requirements.txt
|
||||
|
104
CMakeLists.txt
104
CMakeLists.txt
@@ -1,45 +1,34 @@
|
||||
# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Start spdlog project
|
||||
# Copyright(c) 2019-present by spdlog authors.
|
||||
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
# ---------------------------------------------------------------------------------------
|
||||
cmake_minimum_required(VERSION 3.23)
|
||||
include(cmake/utils.cmake)
|
||||
include(cmake/ide.cmake)
|
||||
|
||||
spdlog_extract_version()
|
||||
|
||||
project(spdlog VERSION ${SPDLOG_VERSION} LANGUAGES CXX)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Set default build to release
|
||||
# ---------------------------------------------------------------------------------------
|
||||
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE)
|
||||
endif ()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Compiler config
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# c++ standard >=17 is required
|
||||
# C++ standard >=17 is required
|
||||
if (NOT DEFINED CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 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 ()
|
||||
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")
|
||||
set(CMAKE_CXX_EXTENSIONS ON)
|
||||
endif ()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Set SPDLOG_MASTER_PROJECT to ON if we are building spdlog
|
||||
# ---------------------------------------------------------------------------------------
|
||||
@@ -51,75 +40,53 @@ if (NOT DEFINED SPDLOG_MASTER_PROJECT)
|
||||
set(SPDLOG_MASTER_PROJECT OFF)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Options
|
||||
# ---------------------------------------------------------------------------------------
|
||||
option(SPDLOG_BUILD_ALL "Build all artifacts" OFF)
|
||||
|
||||
# build shared option
|
||||
option(SPDLOG_BUILD_SHARED "Build shared library" OFF)
|
||||
|
||||
# example options
|
||||
option(SPDLOG_BUILD_EXAMPLE "Build example" ${SPDLOG_MASTER_PROJECT})
|
||||
|
||||
# testing options
|
||||
option(SPDLOG_BUILD_TESTS "Build tests" OFF)
|
||||
|
||||
# bench options
|
||||
option(SPDLOG_BUILD_BENCH "Build benchmarks (Requires https://github.com/google/benchmark.git to be installed)" OFF)
|
||||
|
||||
# sanitizer options
|
||||
option(SPDLOG_SANITIZE_ADDRESS "Enable address sanitizer in tests" OFF)
|
||||
|
||||
# warning options
|
||||
option(SPDLOG_BUILD_WARNINGS "Enable compiler warnings" OFF)
|
||||
|
||||
# install options
|
||||
option(SPDLOG_SYSTEM_INCLUDES "Include as system headers (skip for clang-tidy)." OFF)
|
||||
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")
|
||||
option(SPDLOG_CLOCK_COARSE "Use CLOCK_REALTIME_COARSE instead of the regular clock," OFF)
|
||||
else ()
|
||||
set(SPDLOG_CLOCK_COARSE OFF CACHE BOOL "non supported option" FORCE)
|
||||
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)
|
||||
option(SPDLOG_DISABLE_GLOBAL_LOGGER "Disable global logger creation" OFF)
|
||||
option(SPDLOG_NO_TLS "Disable thread local storage" OFF)
|
||||
|
||||
# clang-tidy
|
||||
option(SPDLOG_TIDY "run clang-tidy" OFF)
|
||||
|
||||
if (SPDLOG_TIDY)
|
||||
set(CMAKE_CXX_CLANG_TIDY "clang-tidy")
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
message(STATUS "Enabled clang-tidy")
|
||||
endif ()
|
||||
|
||||
if (SPDLOG_BUILD_SHARED)
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
endif ()
|
||||
|
||||
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/$<CONFIG>)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/$<CONFIG>)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/$<CONFIG>)
|
||||
set(CMAKE_PDB_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/$<CONFIG>)
|
||||
|
||||
# make sure __cplusplus is defined
|
||||
add_compile_options(/Zc:__cplusplus)
|
||||
# enable parallel build for the solution
|
||||
add_compile_options(/MP)
|
||||
endif ()
|
||||
|
||||
message(STATUS "spdlog version: ${SPDLOG_VERSION}")
|
||||
message(STATUS "spdlog build type: " ${CMAKE_BUILD_TYPE})
|
||||
message(STATUS "spdlog build shared: " ${BUILD_SHARED_LIBS})
|
||||
message(STATUS "spdlog fmt external: " ${SPDLOG_FMT_EXTERNAL})
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Find {fmt} library
|
||||
# ---------------------------------------------------------------------------------------
|
||||
@@ -129,12 +96,10 @@ if (SPDLOG_FMT_EXTERNAL)
|
||||
else ()
|
||||
include(cmake/fmtlib.cmake)
|
||||
endif ()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Threads library is required
|
||||
# ---------------------------------------------------------------------------------------
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Library sources
|
||||
# ---------------------------------------------------------------------------------------
|
||||
@@ -182,7 +147,6 @@ set(SPDLOG_HEADERS
|
||||
"include/spdlog/sinks/tcp_sink.h"
|
||||
"include/spdlog/sinks/udp_sink.h"
|
||||
"include/spdlog/sinks/async_sink.h")
|
||||
|
||||
set(SPDLOG_SRCS
|
||||
"src/common.cpp"
|
||||
"src/logger.cpp"
|
||||
@@ -198,7 +162,6 @@ set(SPDLOG_SRCS
|
||||
"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"
|
||||
@@ -218,7 +181,6 @@ else ()
|
||||
"include/spdlog/details/udp_client_unix.h"
|
||||
"include/spdlog/sinks/ansicolor_sink.h")
|
||||
endif ()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Check if fwrite_unlocked/_fwrite_nolock is available
|
||||
# ---------------------------------------------------------------------------------------
|
||||
@@ -231,7 +193,6 @@ endif ()
|
||||
if (HAVE_FWRITE_UNLOCKED)
|
||||
set(SPDLOG_FWRITE_UNLOCKED 1)
|
||||
endif ()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# spdlog library
|
||||
# ---------------------------------------------------------------------------------------
|
||||
@@ -244,12 +205,12 @@ if (BUILD_SHARED_LIBS)
|
||||
target_compile_definitions(spdlog PUBLIC SPDLOG_SHARED_LIB)
|
||||
if (MSVC)
|
||||
# disable dlls related warnings on msvc
|
||||
target_compile_options(spdlog PUBLIC $<$<AND:$<CXX_COMPILER_ID:MSVC>,$<NOT:$<COMPILE_LANGUAGE:CUDA>>>:/wd4251
|
||||
/wd4275>)
|
||||
target_compile_options(spdlog PUBLIC $<$<AND:$<CXX_COMPILER_ID:MSVC>,$<NOT:$<COMPILE_LANGUAGE:CUDA>>>:/wd4251 /wd4275>)
|
||||
endif ()
|
||||
else ()
|
||||
add_library(spdlog STATIC)
|
||||
endif ()
|
||||
set_target_properties(spdlog PROPERTIES CXX_VISIBILITY_PRESET hidden VISIBILITY_INLINES_HIDDEN ON)
|
||||
add_library(spdlog::spdlog ALIAS spdlog)
|
||||
target_sources(spdlog PRIVATE ${SPDLOG_SRCS})
|
||||
target_sources(
|
||||
@@ -258,42 +219,35 @@ target_sources(
|
||||
TYPE HEADERS
|
||||
BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
FILES ${SPDLOG_HEADERS})
|
||||
|
||||
set(SPDLOG_INCLUDES_LEVEL "")
|
||||
if (SPDLOG_SYSTEM_INCLUDES)
|
||||
set(SPDLOG_INCLUDES_LEVEL "SYSTEM")
|
||||
endif ()
|
||||
|
||||
target_include_directories(spdlog ${SPDLOG_INCLUDES_LEVEL} PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
|
||||
|
||||
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})
|
||||
|
||||
set(SPDLOG_NAME spdlog-${SPDLOG_VERSION_MAJOR})
|
||||
set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX "-${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR}d")
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# set source groups for visual studio
|
||||
# Set prefix and source group for 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 ()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Add required libraries for Android CMake build
|
||||
# Android support
|
||||
# ---------------------------------------------------------------------------------------
|
||||
if (ANDROID)
|
||||
target_link_libraries(spdlog PUBLIC log)
|
||||
endif ()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# spdlog private defines according to the options
|
||||
# Private defines according to the options
|
||||
# ---------------------------------------------------------------------------------------
|
||||
foreach (SPDLOG_OPTION
|
||||
SPDLOG_CLOCK_COARSE
|
||||
@@ -306,42 +260,31 @@ foreach (SPDLOG_OPTION
|
||||
target_compile_definitions(spdlog PRIVATE ${SPDLOG_OPTION})
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Build binaries
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# examples
|
||||
if (SPDLOG_BUILD_EXAMPLE OR SPDLOG_BUILD_ALL)
|
||||
message(STATUS "Generating example(s)")
|
||||
add_subdirectory(example)
|
||||
spdlog_enable_warnings(example)
|
||||
endif ()
|
||||
|
||||
# tests
|
||||
if (SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_ALL)
|
||||
message(STATUS "Generating tests")
|
||||
enable_testing()
|
||||
add_subdirectory(tests)
|
||||
endif ()
|
||||
|
||||
# benchmarks
|
||||
if (SPDLOG_BUILD_BENCH OR SPDLOG_BUILD_ALL)
|
||||
message(STATUS "Generating benchmarks")
|
||||
add_subdirectory(bench)
|
||||
endif ()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# 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")
|
||||
set(config_targets_file "spdlogConfigTargets.cmake")
|
||||
set(version_config_file "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfigVersion.cmake")
|
||||
set(export_dest_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${SPDLOG_NAME}")
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Include files
|
||||
# ---------------------------------------------------------------------------------------
|
||||
set(installed_include_dir "${CMAKE_INSTALL_INCLUDEDIR}/${SPDLOG_NAME}")
|
||||
install(
|
||||
TARGETS spdlog
|
||||
EXPORT spdlogTargets
|
||||
@@ -351,20 +294,17 @@ if (SPDLOG_INSTALL)
|
||||
FILE_SET pub_headers
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${SPDLOG_NAME}")
|
||||
message(STATUS "Installing spdlog in ${CMAKE_INSTALL_LIBDIR}/${SPDLOG_NAME}")
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Install CMake config files
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Install CMake spdlogConfig.cmake, spdlogConfigVersion.cmake and spdlogTargets.cmake
|
||||
set(project_config_in "${CMAKE_CURRENT_LIST_DIR}/cmake/spdlogConfig.cmake.in")
|
||||
set(project_config_out "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfig.cmake")
|
||||
set(config_targets_file "spdlogConfigTargets.cmake")
|
||||
set(version_config_file "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfigVersion.cmake")
|
||||
set(export_dest_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${SPDLOG_NAME}")
|
||||
install(EXPORT spdlogTargets DESTINATION ${export_dest_dir} NAMESPACE spdlog:: FILE ${config_targets_file})
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
configure_package_config_file("${project_config_in}" "${project_config_out}" INSTALL_DESTINATION ${export_dest_dir})
|
||||
|
||||
write_basic_package_version_file("${version_config_file}" COMPATIBILITY SameMajorVersion)
|
||||
install(FILES "${project_config_out}" "${version_config_file}" DESTINATION "${export_dest_dir}")
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Support creation of installable packages
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# CPack
|
||||
include(cmake/spdlogCPack.cmake)
|
||||
endif ()
|
||||
|
@@ -6,14 +6,14 @@
|
||||
//
|
||||
// bench.cpp : spdlog benchmarks
|
||||
//
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <locale>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <locale>
|
||||
#include <algorithm>
|
||||
|
||||
#include "spdlog/sinks/async_sink.h"
|
||||
#include "spdlog/sinks/basic_file_sink.h"
|
||||
@@ -21,8 +21,7 @@
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
using namespace spdlog;
|
||||
using namespace spdlog::sinks;
|
||||
using spdlog::sinks::async_sink;
|
||||
|
||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
||||
|
||||
@@ -50,8 +49,8 @@ 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 howmany = 1'000'000;
|
||||
int queue_size = async_sink::default_queue_size;
|
||||
int threads = 10;
|
||||
int iters = 3;
|
||||
|
||||
@@ -66,20 +65,23 @@ int main(int argc, char *argv[]) {
|
||||
if (argc > 2) threads = atoi(argv[2]);
|
||||
if (argc > 3) {
|
||||
queue_size = atoi(argv[3]);
|
||||
if (queue_size > 500000) {
|
||||
spdlog::error("Max queue size allowed: 500,000");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (argc > 4) iters = atoi(argv[4]);
|
||||
// validate all argc values
|
||||
if (howmany < 1 || threads < 1 || queue_size < 1 || iters < 1) {
|
||||
if (howmany < 1 || threads < 1 || queue_size < 1 || iters < 1) {
|
||||
spdlog::error("Invalid input values");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
auto slot_size = sizeof(details::async_log_msg);
|
||||
constexpr int max_q_size = async_sink::max_queue_size;
|
||||
if(queue_size > max_q_size)
|
||||
{
|
||||
spdlog::error("Queue size too large. Max queue size is {:L}", max_q_size);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
auto slot_size = sizeof(spdlog::details::async_log_msg);
|
||||
spdlog::info("-------------------------------------------------");
|
||||
spdlog::info("Messages : {:L}", howmany);
|
||||
spdlog::info("Threads : {:L}", threads);
|
||||
@@ -99,11 +101,11 @@ int main(int argc, char *argv[]) {
|
||||
auto cfg = async_sink::config();
|
||||
cfg.queue_size = queue_size;
|
||||
cfg.sinks.push_back(std::move(file_sink));
|
||||
auto async_sink = std::make_shared<sinks::async_sink>(cfg);
|
||||
auto logger = std::make_shared<spdlog::logger>("async_logger", std::move(async_sink));
|
||||
auto sink = std::make_shared<async_sink>(cfg);
|
||||
auto logger = std::make_shared<spdlog::logger>("async_logger", std::move(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
|
||||
// verify_file(filename, howmany); // in separate scope to ensure logger is destroyed and all logs were written
|
||||
}
|
||||
spdlog::info("");
|
||||
spdlog::info("*********************************");
|
||||
@@ -117,8 +119,8 @@ int main(int argc, char *argv[]) {
|
||||
cfg.queue_size = queue_size;
|
||||
auto file_sink = std::make_shared<basic_file_sink_mt>(filename, true);
|
||||
cfg.sinks.push_back(std::move(file_sink));
|
||||
auto async_sink = std::make_shared<sinks::async_sink>(cfg);
|
||||
auto logger = std::make_shared<spdlog::logger>("async_logger", std::move(async_sink));
|
||||
auto sink = std::make_shared<async_sink>(cfg);
|
||||
auto logger = std::make_shared<spdlog::logger>("async_logger", std::move(sink));
|
||||
bench_mt(howmany, std::move(logger), threads);
|
||||
}
|
||||
spdlog::shutdown();
|
||||
|
@@ -124,7 +124,8 @@ int main(int argc, char *argv[]) {
|
||||
benchmark::RegisterBenchmark("basic_st", bench_logger, std::move(basic_st))->UseRealTime();
|
||||
|
||||
// rotating st
|
||||
auto rotating_st = spdlog::create<rotating_file_sink_st>("rotating_st", "latency_logs/rotating_st.log", file_size, rotating_files);
|
||||
auto rotating_st =
|
||||
spdlog::create<rotating_file_sink_st>("rotating_st", "latency_logs/rotating_st.log", file_size, rotating_files);
|
||||
benchmark::RegisterBenchmark("rotating_st", bench_logger, std::move(rotating_st))->UseRealTime();
|
||||
|
||||
// daily st
|
||||
@@ -142,7 +143,8 @@ int main(int argc, char *argv[]) {
|
||||
benchmark::RegisterBenchmark("basic_mt", bench_logger, std::move(basic_mt))->Threads(n_threads)->UseRealTime();
|
||||
|
||||
// rotating mt
|
||||
auto rotating_mt = spdlog::create<rotating_file_sink_mt>("rotating_mt", "latency_logs/rotating_mt.log", file_size, rotating_files);
|
||||
auto rotating_mt =
|
||||
spdlog::create<rotating_file_sink_mt>("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
|
||||
@@ -151,7 +153,8 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
using spdlog::sinks::async_sink;
|
||||
async_sink::config config;
|
||||
config.queue_size = 3 * 1024 * 1024;;
|
||||
config.queue_size = async_sink::default_queue_size;;
|
||||
|
||||
config.sinks.push_back(std::make_shared<null_sink_st>());
|
||||
config.policy = async_sink::overflow_policy::overrun_oldest;
|
||||
auto async_logger = std::make_shared<spdlog::logger>("async_logger", std::make_shared<async_sink>(config));
|
||||
|
@@ -3,8 +3,8 @@ include(FetchContent)
|
||||
FetchContent_Declare(
|
||||
fmt
|
||||
DOWNLOAD_EXTRACT_TIMESTAMP FALSE
|
||||
URL https://github.com/fmtlib/fmt/archive/refs/tags/11.0.2.tar.gz
|
||||
URL_HASH SHA256=6cb1e6d37bdcb756dbbe59be438790db409cdb4868c66e888d5df9f13f7c027f)
|
||||
URL https://github.com/fmtlib/fmt/archive/refs/tags/11.1.4.tar.gz
|
||||
URL_HASH SHA256=ac366b7b4c2e9f0dde63a59b3feb5ee59b67974b14ee5dc9ea8ad78aa2c1ee1e)
|
||||
|
||||
FetchContent_GetProperties(fmt)
|
||||
if(NOT fmt_POPULATED)
|
||||
|
463
docs/index.md
463
docs/index.md
@@ -1,463 +0,0 @@
|
||||
|
||||
# spdlog
|
||||
|
||||
|
||||
[](https://github.com/gabime/spdlog/actions/workflows/linux.yml)
|
||||
[](https://github.com/gabime/spdlog/actions/workflows/windows.yml)
|
||||
[](https://github.com/gabime/spdlog/actions/workflows/macos.yml)
|
||||
[](https://github.com/gabime/spdlog/releases/latest)
|
||||
|
||||
Fast C++ logging library
|
||||
|
||||
## Install
|
||||
```console
|
||||
$ git clone https://github.com/gabime/spdlog.git
|
||||
$ cd spdlog && mkdir build && cd build
|
||||
$ cmake .. && cmake --build .
|
||||
```
|
||||
|
||||
see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v2.x/example/CMakeLists.txt) on how to use.
|
||||
|
||||
## Platforms
|
||||
* Linux, FreeBSD, OpenBSD, Solaris, AIX
|
||||
* Windows (msvc, cygwin)
|
||||
* macOS
|
||||
* Android
|
||||
|
||||
## Package managers:
|
||||
* Debian: `sudo apt install libspdlog-dev`
|
||||
* Homebrew: `brew install spdlog`
|
||||
* MacPorts: `sudo port install spdlog`
|
||||
* FreeBSD: `pkg install spdlog`
|
||||
* Fedora: `dnf install spdlog`
|
||||
* Gentoo: `emerge dev-libs/spdlog`
|
||||
* Arch Linux: `pacman -S spdlog`
|
||||
* openSUSE: `sudo zypper in spdlog-devel`
|
||||
* vcpkg: `vcpkg install spdlog`
|
||||
* conan: `conan install --requires=spdlog/[*]`
|
||||
* conda: `conda install -c conda-forge spdlog`
|
||||
* build2: ```depends: spdlog ^1.8.2```
|
||||
|
||||
|
||||
## Features
|
||||
* Very fast (see [benchmarks](#benchmarks) below).
|
||||
* Headers only or compiled
|
||||
* Feature-rich formatting, using the excellent [fmt](https://github.com/fmtlib/fmt) library.
|
||||
* Asynchronous mode (optional)
|
||||
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
|
||||
* Multi/Single threaded loggers.
|
||||
* Various log targets:
|
||||
* Rotating log files.
|
||||
* Daily log files.
|
||||
* Console logging (colors supported).
|
||||
* syslog.
|
||||
* Windows event log.
|
||||
* Windows debugger (```OutputDebugString(..)```).
|
||||
* Log to Qt widgets ([example](#log-to-qt-with-nice-colors)).
|
||||
* Easily [extendable](https://github.com/gabime/spdlog/wiki/4.-Sinks#implementing-your-own-sink) with custom log targets.
|
||||
* Log filtering - log levels can be modified at runtime as well as compile time.
|
||||
* Support for loading log levels from argv or environment var.
|
||||
|
||||
## Usage samples
|
||||
|
||||
#### Basic usage
|
||||
```c++
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
spdlog::info("Welcome to spdlog!");
|
||||
spdlog::error("Some error message with arg: {}", 1);
|
||||
|
||||
spdlog::warn("Easy padding in numbers like {:08d}", 12);
|
||||
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||
spdlog::info("Support for floats {:03.2f}", 1.23456);
|
||||
spdlog::info("Positional args are {1} {0}..", "too", "supported");
|
||||
spdlog::info("{:<30}", "left aligned");
|
||||
|
||||
spdlog::set_level(spdlog::level::debug); // Set global log level to debug
|
||||
spdlog::debug("This message should be displayed..");
|
||||
|
||||
// change log pattern
|
||||
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
|
||||
|
||||
// Compile time log levels
|
||||
// define SPDLOG_ACTIVE_LEVEL to desired level
|
||||
SPDLOG_TRACE("Some trace message with param {}", 42);
|
||||
SPDLOG_DEBUG("Some debug message");
|
||||
}
|
||||
|
||||
```
|
||||
---
|
||||
#### Create stdout/stderr logger object
|
||||
```c++
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||
void stdout_example()
|
||||
{
|
||||
// create a color multi-threaded logger
|
||||
auto console = spdlog::stdout_color_mt("console");
|
||||
auto err_logger = spdlog::stderr_color_mt("stderr");
|
||||
spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
#### Basic file logger
|
||||
```c++
|
||||
#include "spdlog/sinks/basic_file_sink.h"
|
||||
void basic_logfile_example()
|
||||
{
|
||||
try
|
||||
{
|
||||
auto logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
|
||||
}
|
||||
catch (const spdlog::spdlog_ex &ex)
|
||||
{
|
||||
std::cout << "Log init failed: " << ex.what() << std::endl;
|
||||
}
|
||||
}
|
||||
```
|
||||
---
|
||||
#### Rotating files
|
||||
```c++
|
||||
#include "spdlog/sinks/rotating_file_sink.h"
|
||||
void rotating_example()
|
||||
{
|
||||
// Create a file rotating logger with 5 MB size max and 3 rotated files
|
||||
auto max_size = 1048576 * 5;
|
||||
auto max_files = 3;
|
||||
auto logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", max_size, max_files);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
#### Daily files
|
||||
```c++
|
||||
|
||||
#include "spdlog/sinks/daily_file_sink.h"
|
||||
void daily_example()
|
||||
{
|
||||
// Create a daily logger - a new file is created every day at 2:30 am
|
||||
auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
#### Periodic flush
|
||||
```c++
|
||||
// periodically flush all *registered* loggers every 3 seconds:
|
||||
// warning: only use if all your loggers are thread-safe ("_mt" loggers)
|
||||
spdlog::flush_every(std::chrono::seconds(3));
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
#### Stopwatch
|
||||
```c++
|
||||
// Stopwatch support for spdlog
|
||||
#include "spdlog/stopwatch.h"
|
||||
void stopwatch_example()
|
||||
{
|
||||
spdlog::stopwatch sw;
|
||||
spdlog::debug("Elapsed {}", sw);
|
||||
spdlog::debug("Elapsed {:.3}", sw);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
#### Log binary data in hex
|
||||
```c++
|
||||
// many types of std::container<char> types can be used.
|
||||
// ranges are supported too.
|
||||
// format flags:
|
||||
// {:X} - print in uppercase.
|
||||
// {:s} - don't separate each byte with space.
|
||||
// {:p} - don't print the position on each line start.
|
||||
// {:n} - don't split the output into lines.
|
||||
// {:a} - show ASCII if :n is not set.
|
||||
|
||||
#include "spdlog/fmt/bin_to_hex.h"
|
||||
|
||||
void binary_example()
|
||||
{
|
||||
auto console = spdlog::get("console");
|
||||
std::array<char, 80> buf;
|
||||
console->info("Binary example: {}", spdlog::to_hex(buf));
|
||||
console->info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10));
|
||||
// more examples:
|
||||
// logger->info("uppercase: {:X}", spdlog::to_hex(buf));
|
||||
// logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf));
|
||||
// logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf));
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
#### Logger with multi sinks - each with a different format and log level
|
||||
```c++
|
||||
|
||||
// create a logger with 2 targets, with different log levels and formats.
|
||||
// The console will show only warnings or errors, while the file will log all.
|
||||
void multi_sink_example()
|
||||
{
|
||||
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
||||
console_sink->set_level(spdlog::level::warn);
|
||||
console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v");
|
||||
|
||||
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/multisink.txt", true);
|
||||
file_sink->set_level(spdlog::level::trace);
|
||||
|
||||
spdlog::logger logger("multi_sink", {console_sink, file_sink});
|
||||
logger.set_level(spdlog::level::debug);
|
||||
logger.warn("this should appear in both console and file");
|
||||
logger.info("this message should not appear in the console, only in the file");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
#### User-defined callbacks about log events
|
||||
```c++
|
||||
|
||||
// create a logger with a lambda function callback, the callback will be called
|
||||
// each time something is logged to the logger
|
||||
void callback_example()
|
||||
{
|
||||
auto callback_sink = std::make_shared<spdlog::sinks::callback_sink_mt>([](const spdlog::details::log_msg &msg) {
|
||||
// for example you can be notified by sending an email to yourself
|
||||
});
|
||||
callback_sink->set_level(spdlog::level::err);
|
||||
|
||||
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
||||
spdlog::logger logger("custom_callback_logger", {console_sink, callback_sink});
|
||||
|
||||
logger.info("some info log");
|
||||
logger.error("critical issue"); // will notify you
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
#### Asynchronous logging
|
||||
```c++
|
||||
#include "spdlog/async.h"
|
||||
#include "spdlog/sinks/basic_file_sink.h"
|
||||
void async_example()
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
#### User-defined types
|
||||
```c++
|
||||
template<>
|
||||
struct fmt::formatter<my_type> : fmt::formatter<std::string>
|
||||
{
|
||||
auto format(my_type my, format_context &ctx) const -> decltype(ctx.out())
|
||||
{
|
||||
return format_to(ctx.out(), "[my_type i={}]", my.i);
|
||||
}
|
||||
};
|
||||
|
||||
void user_defined_example()
|
||||
{
|
||||
spdlog::info("user defined type: {}", my_type(14));
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
#### User-defined flags in the log pattern
|
||||
```c++
|
||||
// Log patterns can contain custom flags.
|
||||
// the following example will add new flag '%*' - which will be bound to a <my_formatter_flag> instance.
|
||||
#include "spdlog/pattern_formatter.h"
|
||||
class my_formatter_flag : public spdlog::custom_flag_formatter
|
||||
{
|
||||
public:
|
||||
void format(const spdlog::details::log_msg &, const std::tm &, spdlog::memory_buf_t &dest) override
|
||||
{
|
||||
std::string some_txt = "custom-flag";
|
||||
dest.append(some_txt.data(), some_txt.data() + some_txt.size());
|
||||
}
|
||||
|
||||
std::unique_ptr<custom_flag_formatter> clone() const override
|
||||
{
|
||||
return std::make_unique<my_formatter_flag>();
|
||||
}
|
||||
};
|
||||
|
||||
void custom_flags_example()
|
||||
{
|
||||
auto formatter = std::make_unique<spdlog::pattern_formatter>();
|
||||
formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v");
|
||||
spdlog::set_formatter(std::move(formatter));
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
#### Custom error handler
|
||||
```c++
|
||||
void err_handler_example()
|
||||
{
|
||||
// can be set globally or per logger(logger->set_error_handler(..))
|
||||
spdlog::set_error_handler([](const std::string &msg) { spdlog::get("console")->error("*** LOGGER ERROR ***: {}", msg); });
|
||||
spdlog::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
#### syslog
|
||||
```c++
|
||||
#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);
|
||||
syslog_logger->warn("This is warning that will end up in syslog.");
|
||||
}
|
||||
```
|
||||
---
|
||||
#### Android example
|
||||
```c++
|
||||
#include "spdlog/sinks/android_sink.h"
|
||||
void android_example()
|
||||
{
|
||||
std::string tag = "spdlog-android";
|
||||
auto android_logger = spdlog::android_logger_mt("android", tag);
|
||||
android_logger->critical("Use \"adb shell logcat\" to view this message.");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
#### Load log levels from the env variable or argv
|
||||
|
||||
```c++
|
||||
#include "spdlog/cfg/env.h"
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
spdlog::cfg::load_env_levels();
|
||||
// or from the command line:
|
||||
// ./example SPDLOG_LEVEL=info,mylogger=trace
|
||||
// #include "spdlog/cfg/argv.h" // for loading levels from argv
|
||||
// spdlog::cfg::load_argv_levels(argc, argv);
|
||||
}
|
||||
```
|
||||
So then you can:
|
||||
|
||||
```console
|
||||
$ export SPDLOG_LEVEL=info,mylogger=trace
|
||||
$ ./example
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
#### Log file open/close event handlers
|
||||
```c++
|
||||
// You can get callbacks from spdlog before/after a log file has been opened or closed.
|
||||
// This is useful for cleanup procedures or for adding something to the start/end of the log file.
|
||||
void file_events_example()
|
||||
{
|
||||
// pass the spdlog::file_event_handlers to file sinks for open/close log file notifications
|
||||
spdlog::file_event_handlers handlers;
|
||||
handlers.before_open = [](spdlog::filename_t filename) { spdlog::info("Before opening {}", filename); };
|
||||
handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("After opening\n", fstream); };
|
||||
handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("Before closing\n", fstream); };
|
||||
handlers.after_close = [](spdlog::filename_t filename) { spdlog::info("After closing {}", filename); };
|
||||
auto my_logger = spdlog::basic_logger_st("some_logger", "logs/events-sample.txt", true, handlers);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
#### Replace the Default Logger
|
||||
```c++
|
||||
void replace_global_logger_example()
|
||||
{
|
||||
auto new_logger = spdlog::basic_logger_mt("new_global_logger", "logs/new-default-log.txt", true);
|
||||
spdlog::set_global_logger(new_logger);
|
||||
spdlog::info("new logger log message");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
#### Log to Qt with nice colors
|
||||
```c++
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/sinks/qt_sinks.h"
|
||||
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
||||
{
|
||||
setMinimumSize(640, 480);
|
||||
auto log_widget = new QTextEdit(this);
|
||||
setCentralWidget(log_widget);
|
||||
int max_lines = 500; // keep the text widget to max 500 lines. remove old lines if needed.
|
||||
auto logger = spdlog::qt_color_logger_mt("qt_logger", log_widget, max_lines);
|
||||
logger->info("Some info message");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
## Benchmarks
|
||||
|
||||
Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v2.x/bench/bench.cpp) done in Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
|
||||
|
||||
#### Synchronous mode
|
||||
```
|
||||
[info] **************************************************************
|
||||
[info] Single thread, 1,000,000 iterations
|
||||
[info] **************************************************************
|
||||
[info] basic_st Elapsed: 0.17 secs 5,777,626/sec
|
||||
[info] rotating_st Elapsed: 0.18 secs 5,475,894/sec
|
||||
[info] daily_st Elapsed: 0.20 secs 5,062,659/sec
|
||||
[info] empty_logger Elapsed: 0.07 secs 14,127,300/sec
|
||||
[info] **************************************************************
|
||||
[info] C-string (400 bytes). Single thread, 1,000,000 iterations
|
||||
[info] **************************************************************
|
||||
[info] basic_st Elapsed: 0.41 secs 2,412,483/sec
|
||||
[info] rotating_st Elapsed: 0.72 secs 1,389,196/sec
|
||||
[info] daily_st Elapsed: 0.42 secs 2,393,298/sec
|
||||
[info] null_st Elapsed: 0.04 secs 27,446,957/sec
|
||||
[info] **************************************************************
|
||||
[info] 10 threads, competing over the same logger object, 1,000,000 iterations
|
||||
[info] **************************************************************
|
||||
[info] basic_mt Elapsed: 0.60 secs 1,659,613/sec
|
||||
[info] rotating_mt Elapsed: 0.62 secs 1,612,493/sec
|
||||
[info] daily_mt Elapsed: 0.61 secs 1,638,305/sec
|
||||
[info] null_mt Elapsed: 0.16 secs 6,272,758/sec
|
||||
```
|
||||
#### Asynchronous mode
|
||||
```
|
||||
[info] -------------------------------------------------
|
||||
[info] Messages : 1,000,000
|
||||
[info] Threads : 10
|
||||
[info] Queue : 8,192 slots
|
||||
[info] Queue memory : 8,192 x 272 = 2,176 KB
|
||||
[info] -------------------------------------------------
|
||||
[info]
|
||||
[info] *********************************
|
||||
[info] Queue Overflow Policy: block
|
||||
[info] *********************************
|
||||
[info] Elapsed: 1.70784 secs 585,535/sec
|
||||
[info] Elapsed: 1.69805 secs 588,910/sec
|
||||
[info] Elapsed: 1.7026 secs 587,337/sec
|
||||
[info]
|
||||
[info] *********************************
|
||||
[info] Queue Overflow Policy: overrun
|
||||
[info] *********************************
|
||||
[info] Elapsed: 0.372816 secs 2,682,285/sec
|
||||
[info] Elapsed: 0.379758 secs 2,633,255/sec
|
||||
[info] Elapsed: 0.373532 secs 2,677,147/sec
|
||||
|
||||
```
|
||||
|
||||
## Documentation
|
||||
Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages.
|
||||
|
||||
---
|
||||
|
||||
Thanks to [JetBrains](https://www.jetbrains.com/?from=spdlog) for donating product licenses to help develop **spdlog** <a href="https://www.jetbrains.com/?from=spdlog"><img src="logos/jetbrains-variant-4.svg" width="94" align="center" /></a>
|
@@ -1,8 +0,0 @@
|
||||
docs_dir: .
|
||||
site_name: spdlog
|
||||
nav:
|
||||
- Home: index.md
|
||||
theme:
|
||||
name: mkdocs
|
||||
color_mode: dark
|
||||
|
@@ -88,7 +88,7 @@ void stdout_logger_example() {
|
||||
// Create color multithreading logger.
|
||||
auto console = spdlog::create<stdout_color_sink_mt>("console");
|
||||
// or for stderr:
|
||||
//auto console = spdlog::create<stderr_color_sink_mt>("console");
|
||||
// auto console = spdlog::create<stderr_color_sink_mt>("console");
|
||||
}
|
||||
|
||||
#include "spdlog/sinks/basic_file_sink.h"
|
||||
|
@@ -51,7 +51,7 @@ public:
|
||||
// do not use begin() and end() to avoid collision with fmt/ranges
|
||||
It get_begin() const { return begin_; }
|
||||
It get_end() const { return end_; }
|
||||
size_t size_per_line() const { return size_per_line_; }
|
||||
[[nodiscard]] size_t size_per_line() const { return size_per_line_; }
|
||||
|
||||
private:
|
||||
It begin_, end_;
|
||||
@@ -90,7 +90,7 @@ inline details::dump_info<It> to_hex(const It range_begin, const It range_end, s
|
||||
|
||||
template <typename T>
|
||||
struct fmt::formatter<spdlog::details::dump_info<T>, char> {
|
||||
const char delimiter = ' ';
|
||||
char delimiter = ' ';
|
||||
bool put_newlines = true;
|
||||
bool put_delimiters = true;
|
||||
bool use_uppercase = false;
|
||||
@@ -138,14 +138,14 @@ struct fmt::formatter<spdlog::details::dump_info<T>, char> {
|
||||
auto inserter = ctx.out();
|
||||
int size_per_line = static_cast<int>(the_range.size_per_line());
|
||||
auto start_of_line = the_range.get_begin();
|
||||
for (auto i = the_range.get_begin(); i != the_range.get_end(); i++) {
|
||||
for (auto i = the_range.get_begin(); i != the_range.get_end(); ++i) {
|
||||
auto ch = static_cast<unsigned char>(*i);
|
||||
|
||||
if (put_newlines && (i == the_range.get_begin() || i - start_of_line >= size_per_line)) {
|
||||
if (show_ascii && i != the_range.get_begin()) {
|
||||
*inserter++ = delimiter;
|
||||
*inserter++ = delimiter;
|
||||
for (auto j = start_of_line; j < i; j++) {
|
||||
for (auto j = start_of_line; j < i; ++j) {
|
||||
auto pc = static_cast<unsigned char>(*j);
|
||||
*inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
|
||||
}
|
||||
@@ -181,7 +181,7 @@ struct fmt::formatter<spdlog::details::dump_info<T>, char> {
|
||||
}
|
||||
*inserter++ = delimiter;
|
||||
*inserter++ = delimiter;
|
||||
for (auto j = start_of_line; j != the_range.get_end(); j++) {
|
||||
for (auto j = start_of_line; j != the_range.get_end(); ++j) {
|
||||
auto pc = static_cast<unsigned char>(*j);
|
||||
*inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
|
||||
}
|
||||
|
@@ -6,31 +6,33 @@
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <cstdint>
|
||||
|
||||
#include "./source_loc.h"
|
||||
#include "fmt/base.h"
|
||||
#include "fmt/xchar.h"
|
||||
|
||||
#if defined(SPDLOG_SHARED_LIB)
|
||||
#if defined(_WIN32)
|
||||
#ifdef spdlog_EXPORTS
|
||||
#define SPDLOG_API __declspec(dllexport)
|
||||
#else // !spdlog_EXPORTS
|
||||
#define SPDLOG_API __declspec(dllimport)
|
||||
#endif
|
||||
#else // !defined(_WIN32)
|
||||
#define SPDLOG_API __attribute__((visibility("default")))
|
||||
// Define SPDLOG_API according to current build settings
|
||||
#ifndef SPDLOG_SHARED_LIB
|
||||
#define SPDLOG_API
|
||||
#elif defined(_WIN32)
|
||||
#ifdef spdlog_EXPORTS
|
||||
#define SPDLOG_API __declspec(dllexport) // Export symbols when building the library
|
||||
#else
|
||||
#define SPDLOG_API __declspec(dllimport) // Import symbols when using the library
|
||||
#endif
|
||||
#else // !defined(SPDLOG_SHARED_LIB)
|
||||
#elif (defined(__GNUC__) || defined(__clang__))
|
||||
#define SPDLOG_API __attribute__((visibility("default"))) // Export symbols for shared libraries
|
||||
#else
|
||||
#define SPDLOG_API
|
||||
#endif
|
||||
// End of SPDLOG_API definition
|
||||
|
||||
#define SPDLOG_FMT_RUNTIME(format_string) fmt::runtime(format_string)
|
||||
#define SPDLOG_FMT_STRING(format_string) FMT_STRING(format_string)
|
||||
|
@@ -4,29 +4,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#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 };
|
||||
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;
|
||||
[[nodiscard]] type message_type() const { return msg_type_; }
|
||||
|
||||
type message_type() const {return msg_type_;}
|
||||
private:
|
||||
type msg_type_{type::log};
|
||||
memory_buf_t buffer_;
|
||||
|
@@ -3,21 +3,29 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "spdlog/common.h"
|
||||
|
||||
// by default, prints the error to stderr, thread safe
|
||||
// by default, prints the error to stderr, at max rate of 1/sec thread safe
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
class SPDLOG_API err_helper {
|
||||
err_handler custom_err_handler_;
|
||||
std::chrono::steady_clock::time_point last_report_time_;
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
public:
|
||||
void handle_ex(const std::string& origin, const source_loc& loc, const std::exception& ex) const noexcept;
|
||||
void handle_unknown_ex(const std::string& origin, const source_loc& loc) const noexcept;
|
||||
err_helper() = default;
|
||||
~err_helper() = default;
|
||||
err_helper(const err_helper& other);
|
||||
err_helper(err_helper&& other) noexcept;
|
||||
void handle_ex(const std::string& origin, const source_loc& loc, const std::exception& ex) noexcept;
|
||||
void handle_unknown_ex(const std::string& origin, const source_loc& loc) noexcept;
|
||||
void set_err_handler(err_handler handler);
|
||||
};
|
||||
|
||||
|
||||
}} // namespace spdlog::details
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
@@ -14,12 +14,12 @@ namespace details {
|
||||
namespace fmt_helper {
|
||||
|
||||
inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest) {
|
||||
auto *buf_ptr = view.data();
|
||||
const auto *buf_ptr = view.data();
|
||||
dest.append(buf_ptr, buf_ptr + view.size());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void append_int(T n, memory_buf_t &dest) {
|
||||
void append_int(T n, memory_buf_t &dest) {
|
||||
fmt::format_int i(n);
|
||||
dest.append(i.data(), i.data() + i.size());
|
||||
}
|
||||
@@ -36,14 +36,14 @@ constexpr unsigned int count_digits_fallback(T n) {
|
||||
if (n < 100) return count + 1;
|
||||
if (n < 1000) return count + 2;
|
||||
if (n < 10000) return count + 3;
|
||||
n /= 10000u;
|
||||
n /= 10000U;
|
||||
count += 4;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline unsigned int count_digits(T n) {
|
||||
using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
|
||||
using count_type = std::conditional_t<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>;
|
||||
|
||||
return static_cast<unsigned int>(fmt::
|
||||
// fmt 7.0.0 renamed the internal namespace to detail.
|
||||
@@ -59,8 +59,8 @@ inline unsigned int count_digits(T n) {
|
||||
inline void pad2(int n, memory_buf_t &dest) {
|
||||
if (n >= 0 && n < 100) // 0-99
|
||||
{
|
||||
dest.push_back(static_cast<char>('0' + n / 10));
|
||||
dest.push_back(static_cast<char>('0' + n % 10));
|
||||
dest.push_back(static_cast<char>('0' + (n / 10)));
|
||||
dest.push_back(static_cast<char>('0' + (n % 10)));
|
||||
} else // unlikely, but just in case, let fmt deal with it
|
||||
{
|
||||
fmt_lib::format_to(std::back_inserter(dest), SPDLOG_FMT_STRING("{:02}"), n);
|
||||
@@ -69,18 +69,18 @@ inline void pad2(int n, memory_buf_t &dest) {
|
||||
|
||||
template <typename T>
|
||||
inline void pad_uint(T n, unsigned int width, memory_buf_t &dest) {
|
||||
static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
|
||||
for (auto digits = count_digits(n); digits < width; digits++) {
|
||||
static_assert(std::is_unsigned_v<T>, "pad_uint must get unsigned T");
|
||||
for (auto digits = count_digits(n); digits < width; ++digits) {
|
||||
dest.push_back('0');
|
||||
}
|
||||
append_int(n, dest);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void pad3(T n, memory_buf_t &dest) {
|
||||
static_assert(std::is_unsigned<T>::value, "pad3 must get unsigned T");
|
||||
void pad3(T n, memory_buf_t &dest) {
|
||||
static_assert(std::is_unsigned_v<T>, "pad3 must get unsigned T");
|
||||
if (n < 1000) {
|
||||
dest.push_back(static_cast<char>(n / 100 + '0'));
|
||||
dest.push_back(static_cast<char>((n / 100) + '0'));
|
||||
n = n % 100;
|
||||
dest.push_back(static_cast<char>((n / 10) + '0'));
|
||||
dest.push_back(static_cast<char>((n % 10) + '0'));
|
||||
@@ -90,12 +90,12 @@ inline void pad3(T n, memory_buf_t &dest) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void pad6(T n, memory_buf_t &dest) {
|
||||
void pad6(T n, memory_buf_t &dest) {
|
||||
pad_uint(n, 6, dest);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void pad9(T n, memory_buf_t &dest) {
|
||||
void pad9(T n, memory_buf_t &dest) {
|
||||
pad_uint(n, 9, dest);
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ inline void pad9(T n, memory_buf_t &dest) {
|
||||
// e.g.
|
||||
// fraction<std::milliseconds>(tp) -> will return the millis part of the second
|
||||
template <typename ToDuration>
|
||||
inline ToDuration time_fraction(log_clock::time_point tp) {
|
||||
ToDuration time_fraction(log_clock::time_point tp) {
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::seconds;
|
||||
auto duration = tp.time_since_epoch();
|
||||
|
@@ -47,20 +47,15 @@ public:
|
||||
}
|
||||
|
||||
void enqueue_if_have_room(T &&item) {
|
||||
bool pushed = false;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
if (!q_.full()) {
|
||||
q_.push_back(std::move(item));
|
||||
pushed = true;
|
||||
std::unique_lock lock(queue_mutex_);
|
||||
if (q_.full()) {
|
||||
++discard_counter_;
|
||||
return;
|
||||
}
|
||||
q_.push_back(std::move(item));
|
||||
}
|
||||
|
||||
if (pushed) {
|
||||
push_cv_.notify_one();
|
||||
} else {
|
||||
++discard_counter_;
|
||||
}
|
||||
push_cv_.notify_one();
|
||||
}
|
||||
|
||||
// dequeue with a timeout.
|
||||
|
@@ -7,7 +7,6 @@
|
||||
#include <utility>
|
||||
|
||||
// null, no cost dummy "mutex" and dummy "atomic" log level
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
struct null_mutex {
|
||||
|
@@ -42,7 +42,7 @@ public:
|
||||
logger(std::string name, sinks_init_list sinks)
|
||||
: logger(std::move(name), sinks.begin(), sinks.end()) {}
|
||||
|
||||
logger(const logger &other) ;
|
||||
logger(const logger &other);
|
||||
logger(logger &&other) noexcept;
|
||||
|
||||
~logger() = default;
|
||||
@@ -200,7 +200,6 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (should_flush(msg)) {
|
||||
flush_();
|
||||
}
|
||||
|
@@ -70,14 +70,16 @@ public:
|
||||
static constexpr std::string_view bold_on_red = "\033[1m\033[41m";
|
||||
|
||||
private:
|
||||
void sink_it_(const details::log_msg &msg) override;
|
||||
void flush_() override;
|
||||
FILE *target_file_;
|
||||
bool should_do_colors_;
|
||||
std::array<std::string, levels_count> colors_;
|
||||
void print_ccode_(const string_view_t color_code);
|
||||
void print_range_(const memory_buf_t &formatted, size_t start, size_t end);
|
||||
static std::string to_string_(const string_view_t sv);
|
||||
|
||||
void sink_it_(const details::log_msg &msg) override;
|
||||
void flush_() override;
|
||||
void set_color_mode_(color_mode mode);
|
||||
void print_ccode_(string_view_t color_code) const;
|
||||
void print_range_(const memory_buf_t &formatted, size_t start, size_t end) const;
|
||||
static std::string to_string_(string_view_t sv);
|
||||
};
|
||||
|
||||
template <typename Mutex>
|
||||
|
@@ -31,7 +31,8 @@ public:
|
||||
discard_new // Discard the log message if the queue is full
|
||||
};
|
||||
|
||||
enum { default_queue_size = 8192, max_queue_size = 10 * 1024 * 1024 };
|
||||
static constexpr size_t default_queue_size = 8192;
|
||||
static constexpr size_t max_queue_size = 250'000;
|
||||
|
||||
struct config {
|
||||
size_t queue_size = default_queue_size;
|
||||
@@ -43,8 +44,46 @@ public:
|
||||
};
|
||||
|
||||
explicit async_sink(config async_config);
|
||||
async_sink(const async_sink &) = delete;
|
||||
async_sink &operator=(const async_sink &) = delete;
|
||||
async_sink(async_sink &&) = delete;
|
||||
async_sink &operator=(async_sink &&) = delete;
|
||||
~async_sink() override;
|
||||
|
||||
// create an async_sink with one backend sink
|
||||
// sink interface implementation
|
||||
void log(const details::log_msg &msg) override;
|
||||
void set_pattern(const std::string &pattern) override;
|
||||
void set_formatter(std::unique_ptr<formatter> sink_formatter) override;
|
||||
// enqueue flush request to the worker thread and return immediately(default)
|
||||
// if you need to wait for the actual flush to finish, call wait_all() after flush() or destruct the sink
|
||||
void flush() override;
|
||||
|
||||
// non sink interface methods
|
||||
|
||||
// wait until all logs were processed up to timeout millis and return false if timeout was reached
|
||||
[[nodiscard]] bool wait_all(std::chrono::milliseconds timeout) const;
|
||||
|
||||
// wait until all logs were processed
|
||||
void wait_all() const;
|
||||
|
||||
// return the number of overrun messages (effective only if policy is overrun_oldest)
|
||||
[[nodiscard]] size_t get_overrun_counter() const;
|
||||
|
||||
// reset the overrun counter
|
||||
void reset_overrun_counter() const;
|
||||
|
||||
// return the number of discarded messages (effective only if policy is discard_new)
|
||||
[[nodiscard]] size_t get_discard_counter() const;
|
||||
|
||||
// reset the discard counter
|
||||
void reset_discard_counter() const;
|
||||
|
||||
// return the current async_sink configuration
|
||||
[[nodiscard]] const config &get_config() const;
|
||||
|
||||
// create an async_sink with one backend sink constructed with the given args.
|
||||
// example:
|
||||
// auto async_file = async_sink::with<spdlog::sinks::basic_file_sink_st>("mylog.txt");
|
||||
template <typename Sink, typename... SinkArgs>
|
||||
static std::shared_ptr<async_sink> with(SinkArgs &&...sink_args) {
|
||||
config cfg{};
|
||||
@@ -52,34 +91,20 @@ public:
|
||||
return std::make_shared<async_sink>(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<formatter> 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<async_log_msg>;
|
||||
|
||||
void send_message_(async_log_msg::type msg_type, const details::log_msg &msg) const;
|
||||
void enqueue_message_(details::async_log_msg &&msg) const;
|
||||
void backend_loop_();
|
||||
void backend_log_(const details::log_msg &msg) ;
|
||||
void backend_log_(const details::log_msg &msg);
|
||||
void backend_flush_();
|
||||
|
||||
config config_;
|
||||
std::unique_ptr<queue_t> q_;
|
||||
std::thread worker_thread_;
|
||||
details::err_helper err_helper_;
|
||||
std::atomic_bool terminate_worker_ = false;
|
||||
};
|
||||
|
||||
} // namespace sinks
|
||||
|
@@ -36,7 +36,7 @@ public:
|
||||
protected:
|
||||
// sink formatter
|
||||
std::unique_ptr<spdlog::formatter> formatter_;
|
||||
Mutex mutex_;
|
||||
mutable Mutex mutex_;
|
||||
|
||||
virtual void sink_it_(const details::log_msg &msg) = 0;
|
||||
virtual void flush_() = 0;
|
||||
|
@@ -16,11 +16,11 @@
|
||||
#include <mongocxx/client.hpp>
|
||||
#include <mongocxx/instance.hpp>
|
||||
#include <mongocxx/uri.hpp>
|
||||
|
||||
#include <mutex>
|
||||
#include "../details/null_mutex.h"
|
||||
|
||||
#include "../common.h"
|
||||
#include "../details/log_msg.h"
|
||||
#include "../details/null_mutex.h"
|
||||
#include "./base_sink.h"
|
||||
|
||||
namespace spdlog {
|
||||
|
@@ -4,6 +4,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "../details/null_mutex.h"
|
||||
#include "./base_sink.h"
|
||||
|
||||
|
@@ -46,7 +46,9 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
void sink_it_(const details::log_msg &msg) override { q_.push_back(details::async_log_msg{details::async_log_msg::type::log, 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:
|
||||
|
@@ -17,8 +17,8 @@ public:
|
||||
virtual void set_formatter(std::unique_ptr<spdlog::formatter> sink_formatter) = 0;
|
||||
|
||||
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);}
|
||||
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
|
||||
|
@@ -8,8 +8,8 @@
|
||||
#include <syslog.h>
|
||||
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
namespace spdlog {
|
||||
namespace sinks {
|
||||
@@ -19,7 +19,7 @@ namespace sinks {
|
||||
template <typename Mutex>
|
||||
class syslog_sink final : public base_sink<Mutex> {
|
||||
public:
|
||||
syslog_sink(std::string ident = "", int syslog_option = 0, int syslog_facility = LOG_USER, bool enable_formatting=false)
|
||||
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,
|
||||
|
@@ -6,7 +6,6 @@
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
|
||||
async_log_msg::async_log_msg(const type type)
|
||||
: msg_type_{type} {}
|
||||
|
||||
@@ -15,21 +14,25 @@ async_log_msg::async_log_msg(const type type)
|
||||
// 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) {
|
||||
: 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_} {
|
||||
: 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_)} {
|
||||
: log_msg{other},
|
||||
msg_type_{other.msg_type_},
|
||||
buffer_{std::move(other.buffer_)} {
|
||||
update_string_views();
|
||||
}
|
||||
|
||||
|
@@ -3,23 +3,42 @@
|
||||
|
||||
#include "spdlog/details/err_helper.h"
|
||||
|
||||
#include "iostream"
|
||||
#include "spdlog/details/os.h"
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
err_helper::err_helper(const err_helper &other) {
|
||||
std::lock_guard lock(other.mutex_);
|
||||
custom_err_handler_ = other.custom_err_handler_;
|
||||
last_report_time_ = other.last_report_time_;
|
||||
}
|
||||
|
||||
err_helper::err_helper(err_helper &&other) noexcept {
|
||||
custom_err_handler_ = std::move(other.custom_err_handler_);
|
||||
last_report_time_ = std::move(other.last_report_time_);
|
||||
}
|
||||
|
||||
// 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 noexcept {
|
||||
void err_helper::handle_ex(const std::string &origin, const source_loc &loc, const std::exception &ex) noexcept {
|
||||
std::lock_guard lock(mutex_);
|
||||
try {
|
||||
if (custom_err_handler_) {
|
||||
custom_err_handler_(ex.what());
|
||||
return;
|
||||
}
|
||||
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (now - last_report_time_ < std::chrono::seconds(1)) {
|
||||
return;
|
||||
}
|
||||
last_report_time_ = now;
|
||||
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);
|
||||
char date_buf[64];
|
||||
if (std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time) == 0) {
|
||||
std::snprintf(date_buf, sizeof(date_buf), "unknown time");
|
||||
}
|
||||
std::string msg;
|
||||
if (loc.empty()) {
|
||||
msg = fmt_lib::format("[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, origin, ex.what());
|
||||
@@ -29,17 +48,21 @@ void err_helper::handle_ex(const std::string &origin, const source_loc &loc, con
|
||||
}
|
||||
std::fputs(msg.c_str(), stderr);
|
||||
} catch (const std::exception &handler_ex) {
|
||||
std::fprintf(stderr, "[*** LOG ERROR ***] [%s] caught exception during error handler: %s\n", origin.c_str(), handler_ex.what());
|
||||
std::fprintf(stderr, "[*** LOG ERROR ***] [%s] exception during %s handler: %s\n", origin.c_str(), custom_err_handler_ ? "custom" : "default",
|
||||
handler_ex.what());
|
||||
} catch (...) { // catch all exceptions
|
||||
std::fprintf(stderr, "[*** LOG ERROR ***] [%s] caught unknown exception during error handler\n", origin.c_str());
|
||||
std::fprintf(stderr, "[*** LOG ERROR ***] [%s] unknown exception during %s handler\n", origin.c_str(), custom_err_handler_ ? "custom" : "default");
|
||||
}
|
||||
}
|
||||
|
||||
void err_helper::handle_unknown_ex(const std::string &origin, const source_loc &loc) const noexcept {
|
||||
void err_helper::handle_unknown_ex(const std::string &origin, const source_loc &loc) noexcept {
|
||||
handle_ex(origin, loc, std::runtime_error("unknown exception"));
|
||||
}
|
||||
|
||||
void err_helper::set_err_handler(err_handler handler) { custom_err_handler_ = std::move(handler); }
|
||||
void err_helper::set_err_handler(err_handler handler) {
|
||||
std::lock_guard lock(mutex_);
|
||||
custom_err_handler_ = std::move(handler);
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
@@ -129,8 +129,10 @@ size_t filesize(FILE *f) {
|
||||
|
||||
// Return utc offset in minutes or throw spdlog_ex on failure
|
||||
int utc_minutes_offset(const std::tm &tm) {
|
||||
#if defined(sun) || defined(__sun) || defined(_AIX) || (defined(__NEWLIB__) && !defined(__TM_GMTOFF)) || \
|
||||
(!defined(_BSD_SOURCE) && !defined(_GNU_SOURCE))
|
||||
#if defined(sun) || defined(__sun) || defined(_AIX) || \
|
||||
(defined(__NEWLIB__) && !defined(__TM_GMTOFF)) || \
|
||||
(!defined(__APPLE__) && !defined(_BSD_SOURCE) && !defined(_GNU_SOURCE) && \
|
||||
(!defined(_POSIX_VERSION) || (_POSIX_VERSION < 202405L)))
|
||||
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
|
||||
struct helper {
|
||||
static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(),
|
||||
|
@@ -60,6 +60,9 @@ public:
|
||||
pad_it(remaining_pad_);
|
||||
} else if (padinfo_.truncate_) {
|
||||
long new_size = static_cast<long>(dest_.size()) + remaining_pad_;
|
||||
if (new_size < 0) {
|
||||
new_size = 0;
|
||||
}
|
||||
dest_.resize(static_cast<size_t>(new_size));
|
||||
}
|
||||
}
|
||||
@@ -123,9 +126,8 @@ public:
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Date time pattern appenders
|
||||
// Date time pattern formatters
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const char *ampm(const tm &t) { return t.tm_hour >= 12 ? "PM" : "AM"; }
|
||||
|
||||
static int to12h(const tm &t) { return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour; }
|
||||
@@ -248,7 +250,7 @@ public:
|
||||
: flag_formatter(padinfo) {}
|
||||
|
||||
void format(const details::log_msg &, const std::tm &tm_time, memory_buf_t &dest) override {
|
||||
constexpr size_t field_size = 10;
|
||||
constexpr size_t field_size = 8;
|
||||
ScopedPadder p(field_size, padinfo_, dest);
|
||||
|
||||
fmt_helper::pad2(tm_time.tm_mon + 1, dest);
|
||||
@@ -742,12 +744,25 @@ private:
|
||||
|
||||
// Full info formatter
|
||||
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] [%s:%#] %v
|
||||
class full_formatter final : public flag_formatter {
|
||||
class default_format_formatter final : public flag_formatter {
|
||||
public:
|
||||
explicit full_formatter(padding_info padinfo)
|
||||
// clang-format off
|
||||
// aligned level names
|
||||
static constexpr std::string_view trace_str = "[trace] ";
|
||||
static constexpr std::string_view debug_str = "[debug] ";
|
||||
static constexpr std::string_view info_str = "[info ] ";
|
||||
static constexpr std::string_view warn_str = "[warn ] ";
|
||||
static constexpr std::string_view error_str = "[error] ";
|
||||
static constexpr std::string_view critical_str = "[crit ] ";
|
||||
static constexpr std::string_view off_str = "[off ] ";
|
||||
// clang-format on
|
||||
static constexpr std::array<std::string_view, levels_count> padded_levels{
|
||||
{trace_str, debug_str, info_str, warn_str, error_str, critical_str, off_str}};
|
||||
|
||||
explicit default_format_formatter(padding_info padinfo)
|
||||
: flag_formatter(padinfo) {}
|
||||
|
||||
void format(const details::log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) override {
|
||||
void format(const log_msg &msg, const std::tm &tm_time, memory_buf_t &dest) override {
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::milliseconds;
|
||||
using std::chrono::seconds;
|
||||
@@ -780,13 +795,13 @@ public:
|
||||
cache_timestamp_ = secs;
|
||||
}
|
||||
dest.append(cached_datetime_.begin(), cached_datetime_.end());
|
||||
|
||||
// append milliseconds
|
||||
auto millis = fmt_helper::time_fraction<milliseconds>(msg.time);
|
||||
fmt_helper::pad3(static_cast<uint32_t>(millis.count()), dest);
|
||||
dest.push_back(']');
|
||||
dest.push_back(' ');
|
||||
|
||||
// append logger name if exists
|
||||
// append aligned logger name if exists
|
||||
if (!msg.logger_name.empty()) {
|
||||
dest.push_back('[');
|
||||
fmt_helper::append_string_view(msg.logger_name, dest);
|
||||
@@ -794,14 +809,10 @@ public:
|
||||
dest.push_back(' ');
|
||||
}
|
||||
|
||||
dest.push_back('[');
|
||||
// wrap the level name with color
|
||||
msg.color_range_start = dest.size();
|
||||
// fmt_helper::append_string_view(level::to_c_str(msg.log_level), dest);
|
||||
fmt_helper::append_string_view(to_string_view(msg.log_level), dest);
|
||||
msg.color_range_end = dest.size();
|
||||
dest.push_back(']');
|
||||
dest.push_back(' ');
|
||||
// wrap the level name inside the [..] with color
|
||||
msg.color_range_start = dest.size() + 1; // +1 to start coloring after the '[';
|
||||
fmt_helper::append_string_view(padded_levels.at(static_cast<size_t>(msg.log_level)), dest);
|
||||
msg.color_range_end = dest.size() - 2; // -2 to end coloring before the "] "
|
||||
|
||||
// add source location if present
|
||||
if (!msg.source.empty()) {
|
||||
@@ -844,7 +855,7 @@ pattern_formatter::pattern_formatter(pattern_time_type time_type, std::string eo
|
||||
need_localtime_{true},
|
||||
cached_tm_{},
|
||||
last_log_secs_{} {
|
||||
formatters_.push_back(std::make_unique<details::full_formatter>(details::padding_info{}));
|
||||
formatters_.push_back(std::make_unique<details::default_format_formatter>(details::padding_info{}));
|
||||
}
|
||||
|
||||
std::unique_ptr<formatter> pattern_formatter::clone() const {
|
||||
@@ -906,7 +917,7 @@ void pattern_formatter::handle_flag_(char flag, details::padding_info padding) {
|
||||
// process built-in flags
|
||||
switch (flag) {
|
||||
case ('+'): // default formatter
|
||||
formatters_.push_back(std::make_unique<details::full_formatter>(padding));
|
||||
formatters_.push_back(std::make_unique<details::default_format_formatter>(padding));
|
||||
need_localtime_ = true;
|
||||
break;
|
||||
|
||||
|
@@ -12,11 +12,8 @@ namespace spdlog {
|
||||
namespace sinks {
|
||||
|
||||
template <typename Mutex>
|
||||
ansicolor_sink<Mutex>::ansicolor_sink(FILE *target_file, color_mode mode)
|
||||
: target_file_(target_file)
|
||||
|
||||
{
|
||||
set_color_mode(mode);
|
||||
ansicolor_sink<Mutex>::ansicolor_sink(FILE *target_file, color_mode mode) : target_file_(target_file) {
|
||||
set_color_mode_(mode);
|
||||
colors_.at(level_to_number(level::trace)) = to_string_(white);
|
||||
colors_.at(level_to_number(level::debug)) = to_string_(cyan);
|
||||
colors_.at(level_to_number(level::info)) = to_string_(green);
|
||||
@@ -34,31 +31,37 @@ void ansicolor_sink<Mutex>::set_color(level color_level, string_view_t color) {
|
||||
|
||||
template <typename Mutex>
|
||||
bool ansicolor_sink<Mutex>::should_color() const {
|
||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||
return should_do_colors_;
|
||||
}
|
||||
|
||||
template <typename Mutex>
|
||||
void ansicolor_sink<Mutex>::set_color_mode(color_mode mode) {
|
||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||
set_color_mode_(mode);
|
||||
}
|
||||
|
||||
template <typename Mutex>
|
||||
void ansicolor_sink<Mutex>::set_color_mode_(color_mode mode) {
|
||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||
switch (mode) {
|
||||
case color_mode::always:
|
||||
should_do_colors_ = true;
|
||||
return;
|
||||
return;
|
||||
case color_mode::automatic:
|
||||
should_do_colors_ = details::os::in_terminal(target_file_) && details::os::is_color_terminal();
|
||||
return;
|
||||
return;
|
||||
case color_mode::never:
|
||||
should_do_colors_ = false;
|
||||
return;
|
||||
return;
|
||||
default:
|
||||
should_do_colors_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Mutex>
|
||||
void ansicolor_sink<Mutex>::sink_it_(const details::log_msg &msg) {
|
||||
// Wrap the originally formatted message in color codes.
|
||||
// If color is not supported in the terminal, log as is instead.
|
||||
|
||||
msg.color_range_start = 0;
|
||||
msg.color_range_end = 0;
|
||||
memory_buf_t formatted;
|
||||
@@ -85,12 +88,12 @@ void ansicolor_sink<Mutex>::flush_() {
|
||||
}
|
||||
|
||||
template <typename Mutex>
|
||||
void ansicolor_sink<Mutex>::print_ccode_(const string_view_t color_code) {
|
||||
void ansicolor_sink<Mutex>::print_ccode_(const string_view_t color_code) const {
|
||||
details::os::fwrite_bytes(color_code.data(), color_code.size(), target_file_);
|
||||
}
|
||||
|
||||
template <typename Mutex>
|
||||
void ansicolor_sink<Mutex>::print_range_(const memory_buf_t &formatted, size_t start, size_t end) {
|
||||
void ansicolor_sink<Mutex>::print_range_(const memory_buf_t &formatted, size_t start, size_t end) const {
|
||||
details::os::fwrite_bytes(formatted.data() + start, end - start, target_file_);
|
||||
}
|
||||
|
||||
@@ -109,12 +112,13 @@ template <typename Mutex>
|
||||
ansicolor_stderr_sink<Mutex>::ansicolor_stderr_sink(color_mode mode)
|
||||
: ansicolor_sink<Mutex>(stderr, mode) {}
|
||||
|
||||
|
||||
} // namespace sinks
|
||||
} // namespace spdlog
|
||||
|
||||
// template instantiations
|
||||
#include "spdlog/details/null_mutex.h"
|
||||
template class SPDLOG_API spdlog::sinks::ansicolor_sink<std::mutex>;
|
||||
template class SPDLOG_API spdlog::sinks::ansicolor_sink<spdlog::details::null_mutex>;
|
||||
template class SPDLOG_API spdlog::sinks::ansicolor_stdout_sink<std::mutex>;
|
||||
template class SPDLOG_API spdlog::sinks::ansicolor_stdout_sink<spdlog::details::null_mutex>;
|
||||
template class SPDLOG_API spdlog::sinks::ansicolor_stderr_sink<std::mutex>;
|
||||
|
@@ -3,8 +3,10 @@
|
||||
|
||||
#include "spdlog/sinks/async_sink.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "spdlog/common.h"
|
||||
#include "spdlog/details/mpmc_blocking_q.h"
|
||||
@@ -19,6 +21,10 @@ async_sink::async_sink(config async_config)
|
||||
if (config_.queue_size == 0 || config_.queue_size > max_queue_size) {
|
||||
throw spdlog_ex("async_sink: invalid queue size");
|
||||
}
|
||||
if (config_.custom_err_handler) {
|
||||
err_helper_.set_err_handler(config_.custom_err_handler);
|
||||
}
|
||||
|
||||
q_ = std::make_unique<queue_t>(config_.queue_size);
|
||||
worker_thread_ = std::thread([this] {
|
||||
if (config_.on_thread_start) config_.on_thread_start();
|
||||
@@ -32,13 +38,16 @@ async_sink::~async_sink() {
|
||||
q_->enqueue(async_log_msg(async_log_msg::type::terminate));
|
||||
worker_thread_.join();
|
||||
} catch (...) {
|
||||
terminate_worker_ = true; // as last resort, stop the worker thread using terminate_worker_ flag.
|
||||
#ifndef NDEBUG
|
||||
printf("Exception in ~async_sink()\n");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void async_sink::log(const details::log_msg &msg) { send_message_(async_log_msg::type::log, msg); }
|
||||
void async_sink::log(const details::log_msg &msg) { enqueue_message_(async_log_msg(async_log_msg::type::log, msg)); }
|
||||
|
||||
void async_sink::flush() { send_message_(async_log_msg::type::flush, details::log_msg()); }
|
||||
void async_sink::flush() { enqueue_message_(details::async_log_msg(async_log_msg::type::flush)); }
|
||||
|
||||
void async_sink::set_pattern(const std::string &pattern) { set_formatter(std::make_unique<pattern_formatter>(pattern)); }
|
||||
|
||||
@@ -54,6 +63,25 @@ void async_sink::set_formatter(std::unique_ptr<formatter> formatter) {
|
||||
}
|
||||
}
|
||||
|
||||
bool async_sink::wait_all(const std::chrono::milliseconds timeout) const {
|
||||
using std::chrono::steady_clock;
|
||||
constexpr std::chrono::milliseconds sleep_duration(5);
|
||||
const auto start_time = steady_clock::now();
|
||||
while (q_->size() > 0) {
|
||||
auto elapsed = steady_clock::now() - start_time;
|
||||
if (elapsed > timeout) {
|
||||
return false;
|
||||
}
|
||||
std::this_thread::sleep_for(std::min(sleep_duration, timeout));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void async_sink::wait_all() const {
|
||||
while (!wait_all(std::chrono::milliseconds(10))) { /* empty */
|
||||
}
|
||||
}
|
||||
|
||||
size_t async_sink::get_overrun_counter() const { return q_->overrun_counter(); }
|
||||
|
||||
void async_sink::reset_overrun_counter() const { q_->reset_overrun_counter(); }
|
||||
@@ -65,16 +93,16 @@ 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 {
|
||||
void async_sink::enqueue_message_(details::async_log_msg &&msg) const {
|
||||
switch (config_.policy) {
|
||||
case overflow_policy::block:
|
||||
q_->enqueue(async_log_msg(msg_type, msg));
|
||||
q_->enqueue(std::move(msg));
|
||||
break;
|
||||
case overflow_policy::overrun_oldest:
|
||||
q_->enqueue_nowait(async_log_msg(msg_type, msg));
|
||||
q_->enqueue_nowait(std::move(msg));
|
||||
break;
|
||||
case overflow_policy::discard_new:
|
||||
q_->enqueue_if_have_room(async_log_msg(msg_type, msg));
|
||||
q_->enqueue_if_have_room(std::move(msg));
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
@@ -84,7 +112,7 @@ void async_sink::send_message_(async_log_msg::type msg_type, const details::log_
|
||||
|
||||
void async_sink::backend_loop_() {
|
||||
details::async_log_msg incoming_msg;
|
||||
for (;;) {
|
||||
while (!terminate_worker_) {
|
||||
q_->dequeue(incoming_msg);
|
||||
switch (incoming_msg.message_type()) {
|
||||
case async_log_msg::type::log:
|
||||
@@ -101,7 +129,7 @@ void async_sink::backend_loop_() {
|
||||
}
|
||||
}
|
||||
|
||||
void async_sink::backend_log_(const details::log_msg &msg) {
|
||||
void async_sink::backend_log_(const details::log_msg &msg) {
|
||||
for (const auto &sink : config_.sinks) {
|
||||
if (sink->should_log(msg.log_level)) {
|
||||
try {
|
||||
|
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "spdlog/common.h"
|
||||
#include "spdlog/pattern_formatter.h"
|
||||
|
||||
@@ -53,8 +54,8 @@ void base_sink<Mutex>::set_formatter_(std::unique_ptr<formatter> sink_formatter)
|
||||
formatter_ = std::move(sink_formatter);
|
||||
}
|
||||
|
||||
} // namespace sinks
|
||||
} // namespace spdlog
|
||||
} // namespace sinks
|
||||
} // namespace spdlog
|
||||
|
||||
// template instantiations
|
||||
#include "spdlog/details/null_mutex.h"
|
||||
|
@@ -2,9 +2,11 @@
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#include "spdlog/sinks/basic_file_sink.h"
|
||||
#include "spdlog/common.h"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "spdlog/common.h"
|
||||
|
||||
namespace spdlog {
|
||||
namespace sinks {
|
||||
|
||||
@@ -34,7 +36,6 @@ void basic_file_sink<Mutex>::flush_() {
|
||||
} // namespace sinks
|
||||
} // namespace spdlog
|
||||
|
||||
|
||||
// template instantiations
|
||||
#include "spdlog/details/null_mutex.h"
|
||||
template class SPDLOG_API spdlog::sinks::basic_file_sink<std::mutex>;
|
||||
|
@@ -142,4 +142,4 @@ bool rotating_file_sink<Mutex>::rename_file_(const filename_t &src_filename, con
|
||||
// template instantiations
|
||||
#include "spdlog/details/null_mutex.h"
|
||||
template class SPDLOG_API spdlog::sinks::rotating_file_sink<std::mutex>;
|
||||
template class SPDLOG_API spdlog::sinks::rotating_file_sink<spdlog::details::null_mutex>;
|
||||
template class SPDLOG_API spdlog::sinks::rotating_file_sink<spdlog::details::null_mutex>;
|
||||
|
@@ -8,6 +8,7 @@
|
||||
|
||||
// clang-format on
|
||||
#include "spdlog/sinks/wincolor_sink.h"
|
||||
|
||||
#include "spdlog/common.h"
|
||||
|
||||
namespace spdlog {
|
||||
@@ -131,7 +132,6 @@ template <typename Mutex>
|
||||
wincolor_stderr_sink<Mutex>::wincolor_stderr_sink(color_mode mode)
|
||||
: wincolor_sink<Mutex>(::GetStdHandle(STD_ERROR_HANDLE), mode) {}
|
||||
|
||||
|
||||
} // namespace sinks
|
||||
} // namespace spdlog
|
||||
|
||||
|
@@ -13,21 +13,17 @@
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
|
||||
#ifndef SPDLOG_DISABLE_GLOBAL_LOGGER
|
||||
static std::shared_ptr<logger> s_logger = std::make_shared<logger>("global", std::make_shared<sinks::stdout_color_sink_mt>());
|
||||
static std::shared_ptr<logger> s_logger = std::make_shared<logger>("", std::make_shared<sinks::stdout_color_sink_mt>());
|
||||
#else
|
||||
static std::short_ptr<logger> s_logger = nullptr;
|
||||
static std::short_ptr<logger> s_logger = nullptr;
|
||||
#endif
|
||||
|
||||
|
||||
std::shared_ptr<logger> global_logger() { return s_logger; }
|
||||
|
||||
void set_global_logger(std::shared_ptr<logger> global_logger) { s_logger = std::move(global_logger); }
|
||||
|
||||
logger *global_logger_raw() noexcept {
|
||||
return s_logger.get();
|
||||
}
|
||||
logger *global_logger_raw() noexcept { return s_logger.get(); }
|
||||
|
||||
void set_formatter(std::unique_ptr<formatter> formatter) { global_logger()->set_formatter(std::move(formatter)); }
|
||||
|
||||
|
@@ -10,6 +10,7 @@
|
||||
using spdlog::sinks::async_sink;
|
||||
using spdlog::sinks::sink;
|
||||
using spdlog::sinks::test_sink_mt;
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
auto creat_async_logger(size_t queue_size, std::shared_ptr<sink> backend_sink) {
|
||||
async_sink::config cfg;
|
||||
@@ -41,7 +42,7 @@ TEST_CASE("basic async test ", "[async]") {
|
||||
|
||||
TEST_CASE("discard policy ", "[async]") {
|
||||
auto test_sink = std::make_shared<test_sink_mt>();
|
||||
test_sink->set_delay(std::chrono::milliseconds(1));
|
||||
test_sink->set_delay(1ms);
|
||||
async_sink::config config;
|
||||
config.queue_size = 4;
|
||||
config.policy = async_sink::overflow_policy::overrun_oldest;
|
||||
@@ -62,7 +63,7 @@ TEST_CASE("discard policy ", "[async]") {
|
||||
|
||||
TEST_CASE("discard policy discard_new ", "[async]") {
|
||||
auto test_sink = std::make_shared<test_sink_mt>();
|
||||
test_sink->set_delay(std::chrono::milliseconds(1));
|
||||
test_sink->set_delay(1ms);
|
||||
async_sink::config config;
|
||||
config.queue_size = 4;
|
||||
config.policy = async_sink::overflow_policy::discard_new;
|
||||
@@ -71,7 +72,6 @@ TEST_CASE("discard policy discard_new ", "[async]") {
|
||||
auto as = std::make_shared<async_sink>(config);
|
||||
auto logger = std::make_shared<spdlog::logger>("async_logger", as);
|
||||
|
||||
|
||||
REQUIRE(as->get_config().policy == async_sink::overflow_policy::discard_new);
|
||||
REQUIRE(as->get_discard_counter() == 0);
|
||||
REQUIRE(as->get_overrun_counter() == 0);
|
||||
@@ -95,14 +95,13 @@ TEST_CASE("flush", "[async]") {
|
||||
}
|
||||
logger->flush();
|
||||
}
|
||||
// std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
REQUIRE(test_sink->msg_counter() == messages);
|
||||
REQUIRE(test_sink->flush_counter() == 1);
|
||||
}
|
||||
|
||||
TEST_CASE("wait_dtor ", "[async]") {
|
||||
auto test_sink = std::make_shared<test_sink_mt>();
|
||||
test_sink->set_delay(std::chrono::milliseconds(5));
|
||||
test_sink->set_delay(5ms);
|
||||
async_sink::config config;
|
||||
config.sinks.push_back(test_sink);
|
||||
config.queue_size = 4;
|
||||
@@ -167,7 +166,6 @@ TEST_CASE("to_file", "[async]") {
|
||||
REQUIRE(ends_with(contents, spdlog::fmt_lib::format("Hello message #1023{}", default_eol)));
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("bad_ctor", "[async]") {
|
||||
async_sink::config cfg;
|
||||
cfg.queue_size = 0;
|
||||
@@ -295,3 +293,63 @@ TEST_CASE("backend_ex", "[async]") {
|
||||
REQUIRE_NOTHROW(logger->info("Hello message"));
|
||||
REQUIRE_NOTHROW(logger->flush());
|
||||
}
|
||||
|
||||
// test async custom error handler. trigger it using a backend exception and make sure it's called
|
||||
TEST_CASE("custom_err_handler", "[async]") {
|
||||
bool error_called = false;
|
||||
auto test_sink = std::make_shared<test_sink_mt>();
|
||||
test_sink->set_exception(std::runtime_error("test backend exception"));
|
||||
async_sink::config config;
|
||||
config.sinks.push_back(std::move(test_sink));
|
||||
config.custom_err_handler = [&error_called](const std::string &) { error_called = true; };
|
||||
auto asink = std::make_shared<async_sink>(config);
|
||||
spdlog::logger("async_logger", std::move(asink)).info("Test");
|
||||
// lvalue logger so will be destructed here already so all messages were processed
|
||||
REQUIRE(error_called);
|
||||
}
|
||||
|
||||
// test wait_all
|
||||
TEST_CASE("wait_all", "[async]") {
|
||||
auto test_sink = std::make_shared<test_sink_mt>();
|
||||
auto delay = 10ms;
|
||||
test_sink->set_delay(delay);
|
||||
async_sink::config config;
|
||||
config.sinks.push_back(test_sink);
|
||||
size_t messages = 10;
|
||||
auto as = std::make_shared<async_sink>(config);
|
||||
auto logger = std::make_shared<spdlog::logger>("async_logger", as);
|
||||
for (size_t i = 0; i < messages; i++) {
|
||||
logger->info("Hello message");
|
||||
}
|
||||
REQUIRE_FALSE(as->wait_all(-10ms));
|
||||
REQUIRE_FALSE(as->wait_all(0ms));
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
REQUIRE_FALSE(as->wait_all(delay));
|
||||
|
||||
// should have waited approx 10ms before giving up
|
||||
auto elapsed = std::chrono::steady_clock::now() - start;
|
||||
REQUIRE(elapsed >= delay);
|
||||
REQUIRE(elapsed < delay * 6); // big tolerance, to pass tests in slow virtual machines
|
||||
// wait enough time for all messages to be processed
|
||||
REQUIRE(as->wait_all(messages * delay + 500ms));
|
||||
REQUIRE(as->wait_all(-10ms)); // no more messages
|
||||
REQUIRE(as->wait_all(0ms)); // no more messages
|
||||
REQUIRE(as->wait_all(10ms)); // no more messages
|
||||
}
|
||||
|
||||
// test wait_all without timeout
|
||||
TEST_CASE("wait_all2", "[async]") {
|
||||
auto test_sink = std::make_shared<test_sink_mt>();
|
||||
auto delay = 10ms;
|
||||
test_sink->set_delay(delay);
|
||||
async_sink::config config;
|
||||
config.sinks.push_back(test_sink);
|
||||
size_t messages = 10;
|
||||
auto as = std::make_shared<async_sink>(config);
|
||||
auto logger = std::make_shared<spdlog::logger>("async_logger", as);
|
||||
for (size_t i = 0; i < messages; i++) {
|
||||
logger->info("Hello message");
|
||||
}
|
||||
as->wait_all();
|
||||
REQUIRE(test_sink->msg_counter() == messages);
|
||||
}
|
||||
|
@@ -18,11 +18,10 @@ protected:
|
||||
};
|
||||
struct custom_ex {};
|
||||
|
||||
|
||||
using namespace spdlog::sinks;
|
||||
TEST_CASE("default_error_handler", "[errors]") {
|
||||
prepare_logdir();
|
||||
auto logger = spdlog::create<basic_file_sink_mt>("test-error", log_filename);
|
||||
auto logger = spdlog::create<basic_file_sink_mt>("test-bad-format", log_filename);
|
||||
logger->set_pattern("%v");
|
||||
logger->info(SPDLOG_FMT_RUNTIME("Test message {} {}"), 1);
|
||||
logger->info("Test message {}", 2);
|
||||
@@ -36,17 +35,43 @@ TEST_CASE("custom_error_handler", "[errors]") {
|
||||
prepare_logdir();
|
||||
auto logger = spdlog::create<basic_file_sink_mt>("test-format-error", log_filename);
|
||||
logger->flush_on(spdlog::level::info);
|
||||
logger->set_error_handler([=](const std::string & msg) {
|
||||
REQUIRE(msg == "argument not found");
|
||||
});
|
||||
logger->set_error_handler([=](const std::string &msg) { REQUIRE(msg == "argument not found"); });
|
||||
logger->info("Good message #1");
|
||||
REQUIRE_NOTHROW(logger->info(SPDLOG_FMT_RUNTIME("Bad format msg {} {}"), "xxx"));
|
||||
logger->info("Good message #2");
|
||||
require_message_count(log_filename, 2);
|
||||
}
|
||||
|
||||
TEST_CASE("default_error_handler2", "[errors]") {
|
||||
auto logger = std::make_shared<spdlog::logger>("test-failing-sink", std::make_shared<failing_sink>());
|
||||
TEST_CASE("throwing_sink", "[errors]") {
|
||||
auto logger = std::make_shared<spdlog::logger>("test-throwing-sink", std::make_shared<failing_sink>());
|
||||
REQUIRE_NOTHROW(logger->info("Some message"));
|
||||
}
|
||||
|
||||
TEST_CASE("throwing_flush", "[errors]") {
|
||||
auto logger = spdlog::create<failing_sink>("test-throwing-sink");
|
||||
REQUIRE_NOTHROW(logger->flush());
|
||||
}
|
||||
|
||||
TEST_CASE("throwing_error_handler", "[errors]") {
|
||||
auto logger = std::make_shared<spdlog::logger>("test-throwing-error-handler", std::make_shared<failing_sink>());
|
||||
logger->set_error_handler([=](const std::string &msg) {
|
||||
REQUIRE(msg == log_err_msg);
|
||||
throw std::runtime_error("test throw");
|
||||
});
|
||||
REQUIRE_NOTHROW(logger->info("Some message"));
|
||||
}
|
||||
|
||||
TEST_CASE("throwing_flush_error_handler", "[errors]") {
|
||||
auto logger = spdlog::create<failing_sink>("test-throwing-error-handler");
|
||||
logger->set_error_handler([=](const std::string &msg) {
|
||||
REQUIRE(msg == flush_err_msg);
|
||||
throw std::runtime_error("test throw");
|
||||
});
|
||||
REQUIRE_NOTHROW(logger->flush());
|
||||
}
|
||||
|
||||
TEST_CASE("unknown_ex_from_err_handler", "[errors]") {
|
||||
auto logger = std::make_shared<spdlog::logger>("test-throwing-error-handler", std::make_shared<failing_sink>());
|
||||
logger->set_error_handler([=](const std::string &msg) {
|
||||
REQUIRE(msg == log_err_msg);
|
||||
throw custom_ex();
|
||||
@@ -54,8 +79,8 @@ TEST_CASE("default_error_handler2", "[errors]") {
|
||||
REQUIRE_NOTHROW(logger->info("Some message"));
|
||||
}
|
||||
|
||||
TEST_CASE("flush_error_handler", "[errors]") {
|
||||
auto logger = spdlog::create<failing_sink>("test-failing-sink");
|
||||
TEST_CASE("unknown_ex_from_flush_err_handler", "[errors]") {
|
||||
auto logger = spdlog::create<failing_sink>("test-throwing-error-handler");
|
||||
logger->set_error_handler([=](const std::string &msg) {
|
||||
REQUIRE(msg == flush_err_msg);
|
||||
throw custom_ex();
|
||||
|
@@ -7,8 +7,8 @@
|
||||
|
||||
#include "includes.h"
|
||||
#include "spdlog/details/os.h"
|
||||
#include "spdlog/sinks/ostream_sink.h"
|
||||
#include "spdlog/sinks/async_sink.h"
|
||||
#include "spdlog/sinks/ostream_sink.h"
|
||||
#include "test_sink.h"
|
||||
|
||||
template <class T>
|
||||
@@ -74,23 +74,93 @@ TEST_CASE("to_level_enum", "[convert_to_level_enum]") {
|
||||
REQUIRE(spdlog::level_from_str("null") == spdlog::level::off);
|
||||
}
|
||||
|
||||
TEST_CASE("copy_ctor", "[copy_ctor]") {
|
||||
using spdlog::sinks::test_sink_mt;
|
||||
auto test_sink = std::make_shared<test_sink_mt>();
|
||||
auto logger = std::make_shared<spdlog::logger>("orig", test_sink);
|
||||
logger->set_pattern("%v");
|
||||
bool error_handled = false;
|
||||
logger->set_error_handler([&error_handled](const std::string&) { error_handled = true; });
|
||||
spdlog::logger copied = *logger;
|
||||
|
||||
REQUIRE(copied.name() == logger->name());
|
||||
REQUIRE(logger->sinks() == copied.sinks());
|
||||
REQUIRE(logger->log_level() == copied.log_level());
|
||||
REQUIRE(logger->flush_level() == copied.flush_level());
|
||||
|
||||
logger->info("Some message 1");
|
||||
copied.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");
|
||||
|
||||
// check that copied custom error handler was indeed copied
|
||||
test_sink->set_exception(std::runtime_error("Some error"));
|
||||
REQUIRE(error_handled == false);
|
||||
copied.error("Some error");
|
||||
REQUIRE(error_handled == true);
|
||||
}
|
||||
|
||||
TEST_CASE("move_ctor", "[move_ctor]") {
|
||||
auto log_level = spdlog::level::critical;
|
||||
auto flush_level = spdlog::level::warn;
|
||||
using spdlog::sinks::test_sink_mt;
|
||||
auto test_sink = std::make_shared<test_sink_mt>();
|
||||
auto logger = std::make_shared<spdlog::logger>("orig", test_sink);
|
||||
logger->flush_on(flush_level);
|
||||
logger->set_level(log_level);
|
||||
logger->set_pattern("%v");
|
||||
bool error_handled = false;
|
||||
logger->set_error_handler([&error_handled](const std::string&) { error_handled = true; });
|
||||
spdlog::logger moved = std::move(*logger);
|
||||
|
||||
REQUIRE(logger->name() == "");
|
||||
REQUIRE(logger->sinks().empty());
|
||||
REQUIRE(moved.name() == "orig");
|
||||
REQUIRE(moved.sinks()[0].get() == test_sink.get());
|
||||
REQUIRE(moved.log_level() == log_level);
|
||||
REQUIRE(moved.flush_level() == flush_level);
|
||||
|
||||
logger->critical("Some message 1");
|
||||
moved.critical("Some message 2");
|
||||
|
||||
REQUIRE(test_sink->lines().size() == 1);
|
||||
REQUIRE(test_sink->lines()[0] == "Some message 2");
|
||||
|
||||
// check that copied custom error handler was indeed copied
|
||||
test_sink->set_exception(std::runtime_error("Some error"));
|
||||
REQUIRE(error_handled == false);
|
||||
moved.critical("Some error");
|
||||
REQUIRE(error_handled == true);
|
||||
}
|
||||
|
||||
TEST_CASE("clone-logger", "[clone]") {
|
||||
using spdlog::sinks::test_sink_mt;
|
||||
auto test_sink = std::make_shared<test_sink_mt>();
|
||||
auto logger = std::make_shared<spdlog::logger>("orig", test_sink);
|
||||
logger->set_pattern("%v");
|
||||
bool error_handled = false;
|
||||
logger->set_error_handler([&error_handled](const std::string&) { error_handled = true; });
|
||||
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");
|
||||
|
||||
// check that cloned custom error handler was indeed cloned
|
||||
test_sink->set_exception(std::runtime_error("Some error"));
|
||||
REQUIRE(error_handled == false);
|
||||
cloned->error("Some error");
|
||||
REQUIRE(error_handled == true);
|
||||
}
|
||||
|
||||
TEST_CASE("clone async", "[clone]") {
|
||||
|
@@ -2,6 +2,8 @@
|
||||
#include "spdlog/sinks/ostream_sink.h"
|
||||
#include "test_sink.h"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
using spdlog::memory_buf_t;
|
||||
|
||||
// log to str and return it
|
||||
@@ -18,6 +20,21 @@ static std::string log_to_str(const std::string &msg, const Args &...args) {
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
// log to str and return it with time
|
||||
template <typename... Args>
|
||||
static std::string log_to_str_with_time(spdlog::log_clock::time_point log_time, const std::string &msg, const Args &...args) {
|
||||
std::ostringstream oss;
|
||||
auto oss_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(oss);
|
||||
spdlog::logger oss_logger("pattern_tester", oss_sink);
|
||||
oss_logger.set_level(spdlog::level::info);
|
||||
|
||||
oss_logger.set_formatter(
|
||||
std::unique_ptr<spdlog::formatter>(new spdlog::pattern_formatter(args...)));
|
||||
|
||||
oss_logger.log(log_time, {}, spdlog::level::info, msg);
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
TEST_CASE("custom eol", "[pattern_formatter]") {
|
||||
std::string msg = "Hello custom eol test";
|
||||
std::string eol = ";)";
|
||||
@@ -52,6 +69,15 @@ TEST_CASE("date MM/DD/YY ", "[pattern_formatter]") {
|
||||
REQUIRE(log_to_str("Some message", "%D %v", spdlog::pattern_time_type::local, "\n") == oss.str());
|
||||
}
|
||||
|
||||
TEST_CASE("GMT offset ", "[pattern_formatter]") {
|
||||
using namespace std::chrono_literals;
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
const auto yesterday = now - 24h;
|
||||
|
||||
REQUIRE(log_to_str_with_time(yesterday, "Some message", "%z", spdlog::pattern_time_type::utc, "\n") ==
|
||||
"+00:00\n");
|
||||
}
|
||||
|
||||
TEST_CASE("color range test1", "[pattern_formatter]") {
|
||||
auto formatter = std::make_shared<spdlog::pattern_formatter>("%^%v%$", spdlog::pattern_time_type::local, "\n");
|
||||
|
||||
|
@@ -6,9 +6,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <exception>
|
||||
|
||||
#include "spdlog/details/null_mutex.h"
|
||||
#include "spdlog/details/os.h"
|
||||
@@ -37,13 +37,9 @@ public:
|
||||
delay_ = delay;
|
||||
}
|
||||
|
||||
void set_exception(const std::runtime_error& ex) {
|
||||
exception_ptr_ = std::make_exception_ptr(ex);
|
||||
}
|
||||
void set_exception(const std::runtime_error& ex) { exception_ptr_ = std::make_exception_ptr(ex); }
|
||||
|
||||
void clear_exception() {
|
||||
exception_ptr_ = nullptr;
|
||||
}
|
||||
void clear_exception() { exception_ptr_ = nullptr; }
|
||||
|
||||
// return last output without the eol
|
||||
std::vector<std::string> lines() {
|
||||
@@ -52,7 +48,7 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
void sink_it_(const details::log_msg &msg) override {
|
||||
void sink_it_(const details::log_msg& msg) override {
|
||||
if (exception_ptr_) {
|
||||
std::rethrow_exception(exception_ptr_);
|
||||
}
|
||||
@@ -78,7 +74,7 @@ protected:
|
||||
size_t flush_counter_{0};
|
||||
std::chrono::milliseconds delay_{std::chrono::milliseconds::zero()};
|
||||
std::vector<std::string> lines_;
|
||||
std::exception_ptr exception_ptr_; // will be thrown on next log or flush if not null
|
||||
std::exception_ptr exception_ptr_; // will be thrown on next log or flush if not null
|
||||
};
|
||||
|
||||
using test_sink_mt = test_sink<std::mutex>;
|
||||
|
Reference in New Issue
Block a user