mirror of
https://github.com/gabime/spdlog.git
synced 2025-09-29 01:29:35 +08:00
Compare commits
45 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
560df2878a | ||
![]() |
dc328de72a | ||
![]() |
6704375be6 | ||
![]() |
7a85acc31f | ||
![]() |
7cbf832cca | ||
![]() |
58b1c2f78d | ||
![]() |
b94b9a8341 | ||
![]() |
6a93f98a1f | ||
![]() |
b70020d7c3 | ||
![]() |
ade22934e3 | ||
![]() |
ee2fd265fd | ||
![]() |
5b84f30b3a | ||
![]() |
5c38e15dd2 | ||
![]() |
0e86c4eed0 | ||
![]() |
e69aafc737 | ||
![]() |
3dbba6895d | ||
![]() |
8bd16feb36 | ||
![]() |
8e429ae5d6 | ||
![]() |
e2bf00ada4 | ||
![]() |
c3dd37c8c1 | ||
![]() |
1838ad9b3d | ||
![]() |
7cc884fe34 | ||
![]() |
61ab1af906 | ||
![]() |
911a2986ce | ||
![]() |
8321449c44 | ||
![]() |
314afa7dbb | ||
![]() |
f57fc1b2fa | ||
![]() |
a1a71804f4 | ||
![]() |
b1a58cd342 | ||
![]() |
efb1af9a73 | ||
![]() |
71e93a4f2d | ||
![]() |
8f3c4218ed | ||
![]() |
217ad75ebd | ||
![]() |
ce41991c51 | ||
![]() |
1136bd7ce2 | ||
![]() |
805ab9854f | ||
![]() |
d921a9649c | ||
![]() |
093edc2dc2 | ||
![]() |
1b2f6815bf | ||
![]() |
f17eb1bb56 | ||
![]() |
be685337b1 | ||
![]() |
06580c8333 | ||
![]() |
6f12242a55 | ||
![]() |
11c99892d7 | ||
![]() |
31ce7ef3a5 |
@@ -32,9 +32,9 @@ BraceWrapping:
|
|||||||
BeforeCatch: true
|
BeforeCatch: true
|
||||||
BeforeElse: true
|
BeforeElse: true
|
||||||
IndentBraces: false
|
IndentBraces: false
|
||||||
SplitEmptyFunction: false
|
SplitEmptyFunction: true
|
||||||
SplitEmptyRecord: false
|
SplitEmptyRecord: true
|
||||||
SplitEmptyNamespace: false
|
SplitEmptyNamespace: true
|
||||||
BreakBeforeBinaryOperators: None
|
BreakBeforeBinaryOperators: None
|
||||||
BreakBeforeBraces: Custom
|
BreakBeforeBraces: Custom
|
||||||
BreakBeforeInheritanceComma: false
|
BreakBeforeInheritanceComma: false
|
||||||
@@ -88,7 +88,7 @@ PenaltyExcessCharacter: 1000000
|
|||||||
PenaltyReturnTypeOnItsOwnLine: 60
|
PenaltyReturnTypeOnItsOwnLine: 60
|
||||||
PointerAlignment: Right
|
PointerAlignment: Right
|
||||||
ReflowComments: true
|
ReflowComments: true
|
||||||
SortIncludes: false
|
SortIncludes: true
|
||||||
SortUsingDeclarations: true
|
SortUsingDeclarations: true
|
||||||
SpaceAfterCStyleCast: false
|
SpaceAfterCStyleCast: false
|
||||||
SpaceAfterTemplateKeyword: false
|
SpaceAfterTemplateKeyword: false
|
||||||
|
54
.clang-tidy
54
.clang-tidy
@@ -1,54 +0,0 @@
|
|||||||
Checks: 'cppcoreguidelines-*,
|
|
||||||
performance-*,
|
|
||||||
modernize-*,
|
|
||||||
google-*,
|
|
||||||
misc-*,
|
|
||||||
cert-*,
|
|
||||||
readability-*,
|
|
||||||
clang-analyzer-*,
|
|
||||||
-performance-unnecessary-value-param,
|
|
||||||
-modernize-use-trailing-return-type,
|
|
||||||
-google-runtime-references,
|
|
||||||
-misc-non-private-member-variables-in-classes,
|
|
||||||
-readability-braces-around-statements,
|
|
||||||
-google-readability-braces-around-statements,
|
|
||||||
-cppcoreguidelines-avoid-magic-numbers,
|
|
||||||
-readability-magic-numbers,
|
|
||||||
-readability-magic-numbers,
|
|
||||||
-cppcoreguidelines-pro-type-vararg,
|
|
||||||
-cppcoreguidelines-pro-bounds-pointer-arithmetic,
|
|
||||||
-cppcoreguidelines-avoid-c-arrays,
|
|
||||||
-modernize-avoid-c-arrays,
|
|
||||||
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
|
|
||||||
-readability-named-parameter,
|
|
||||||
-cert-env33-c
|
|
||||||
'
|
|
||||||
|
|
||||||
|
|
||||||
WarningsAsErrors: ''
|
|
||||||
HeaderFilterRegex: '*spdlog/[^f].*'
|
|
||||||
AnalyzeTemporaryDtors: false
|
|
||||||
FormatStyle: none
|
|
||||||
|
|
||||||
CheckOptions:
|
|
||||||
- key: google-readability-braces-around-statements.ShortStatementLines
|
|
||||||
value: '1'
|
|
||||||
- key: google-readability-function-size.StatementThreshold
|
|
||||||
value: '800'
|
|
||||||
- key: google-readability-namespace-comments.ShortNamespaceLines
|
|
||||||
value: '10'
|
|
||||||
- key: google-readability-namespace-comments.SpacesBeforeComments
|
|
||||||
value: '2'
|
|
||||||
- key: modernize-loop-convert.MaxCopySize
|
|
||||||
value: '16'
|
|
||||||
- key: modernize-loop-convert.MinConfidence
|
|
||||||
value: reasonable
|
|
||||||
- key: modernize-loop-convert.NamingStyle
|
|
||||||
value: CamelCase
|
|
||||||
- key: modernize-pass-by-value.IncludeStyle
|
|
||||||
value: llvm
|
|
||||||
- key: modernize-replace-auto-ptr.IncludeStyle
|
|
||||||
value: llvm
|
|
||||||
- key: modernize-use-nullptr.NullMacros
|
|
||||||
value: 'NULL'
|
|
||||||
|
|
1
.gitattributes
vendored
1
.gitattributes
vendored
@@ -1 +0,0 @@
|
|||||||
* text=false
|
|
18
.gitignore
vendored
18
.gitignore
vendored
@@ -34,9 +34,6 @@ build/*
|
|||||||
# Codelite
|
# Codelite
|
||||||
.codelite
|
.codelite
|
||||||
|
|
||||||
# KDevelop
|
|
||||||
*.kdev4
|
|
||||||
|
|
||||||
# .orig files
|
# .orig files
|
||||||
*.orig
|
*.orig
|
||||||
|
|
||||||
@@ -49,7 +46,6 @@ example/*
|
|||||||
!example/example.sln
|
!example/example.sln
|
||||||
!example/example.vcxproj
|
!example/example.vcxproj
|
||||||
!example/CMakeLists.txt
|
!example/CMakeLists.txt
|
||||||
!example/meson.build
|
|
||||||
!example/multisink.cpp
|
!example/multisink.cpp
|
||||||
!example/jni
|
!example/jni
|
||||||
|
|
||||||
@@ -70,14 +66,6 @@ install_manifest.txt
|
|||||||
|
|
||||||
# idea
|
# idea
|
||||||
.idea/
|
.idea/
|
||||||
cmake-build-*/
|
|
||||||
*.db
|
# vscode
|
||||||
*.ipch
|
.vscode/
|
||||||
*.filters
|
|
||||||
*.db-wal
|
|
||||||
*.opendb
|
|
||||||
*.db-shm
|
|
||||||
*.vcxproj
|
|
||||||
*.tcl
|
|
||||||
*.user
|
|
||||||
*.sln
|
|
203
.travis.yml
203
.travis.yml
@@ -1,112 +1,91 @@
|
|||||||
# Adapted from various sources, including:
|
# Adapted from various sources, including:
|
||||||
# - Louis Dionne's Hana: https://github.com/ldionne/hana
|
# - Louis Dionne's Hana: https://github.com/ldionne/hana
|
||||||
# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit
|
# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit
|
||||||
# - Eric Niebler's range-v3: https://github.com/ericniebler/range-v3
|
# - Eric Niebler's range-v3: https://github.com/ericniebler/range-v3
|
||||||
sudo: required
|
language: cpp
|
||||||
language: cpp
|
|
||||||
|
# Test matrix:
|
||||||
# gcc 4.8
|
# - Build matrix per compiler: C++11/C++14 + Debug/Release
|
||||||
addons: &gcc48
|
# - Optionally: AddressSanitizer (ASAN)
|
||||||
apt:
|
# - Valgrind: all release builds are also tested with valgrind
|
||||||
packages:
|
# - clang 3.4, 3.5, 3.6, trunk
|
||||||
- g++-4.8
|
# - Note: 3.4 and trunk are tested with/without ASAN,
|
||||||
sources:
|
# the rest is only tested with ASAN=On.
|
||||||
- ubuntu-toolchain-r-test
|
# - gcc 4.9, 5.0
|
||||||
|
#
|
||||||
# gcc 7.0
|
matrix:
|
||||||
addons: &gcc7
|
include:
|
||||||
apt:
|
|
||||||
packages:
|
# Test gcc-4.8: C++11, Build=Debug/Release, ASAN=Off
|
||||||
- g++-7
|
- env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11 ASAN=Off LIBCXX=Off
|
||||||
sources:
|
os: linux
|
||||||
- ubuntu-toolchain-r-test
|
addons: &gcc48
|
||||||
|
apt:
|
||||||
# Clang 3.5
|
packages:
|
||||||
addons: &clang35
|
- g++-4.8
|
||||||
apt:
|
- valgrind
|
||||||
packages:
|
sources:
|
||||||
- clang-3.5
|
- ubuntu-toolchain-r-test
|
||||||
sources:
|
|
||||||
- ubuntu-toolchain-r-test
|
- env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=Off
|
||||||
- llvm-toolchain-precise-3.5
|
os: linux
|
||||||
|
addons: *gcc48
|
||||||
|
|
||||||
addons: &clang10
|
# Test gcc-4.9: C++11, Build=Debug/Release, ASAN=Off
|
||||||
apt:
|
- env: GCC_VERSION=4.9 BUILD_TYPE=Debug CPP=11 ASAN=Off LIBCXX=Off
|
||||||
packages:
|
os: linux
|
||||||
- clang-10
|
addons: &gcc49
|
||||||
- lldb-10
|
apt:
|
||||||
- lld-10
|
packages:
|
||||||
sources:
|
- g++-4.9
|
||||||
- sourceline: "deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main"
|
- valgrind
|
||||||
key_url: "https://apt.llvm.org/llvm-snapshot.gpg.key"
|
sources:
|
||||||
|
- ubuntu-toolchain-r-test
|
||||||
|
|
||||||
matrix:
|
- env: GCC_VERSION=4.9 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=Off
|
||||||
include:
|
os: linux
|
||||||
# Test gcc-4.8: C++11, Build=Debug/Release
|
addons: *gcc49
|
||||||
- env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11
|
|
||||||
os: linux
|
# Install dependencies
|
||||||
addons: *gcc48
|
before_install:
|
||||||
|
- export CHECKOUT_PATH=`pwd`;
|
||||||
- env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11
|
- if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi
|
||||||
os: linux
|
- if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi
|
||||||
addons: *gcc48
|
- if [ "$CLANG_VERSION" == "3.4" ]; then export CXX="/usr/local/clang-3.4/bin/clang++" CC="/usr/local/clang-3.4/bin/clang"; fi
|
||||||
|
- which $CXX
|
||||||
- env: GCC_VERSION=7 BUILD_TYPE=Release CPP=11
|
- which $CC
|
||||||
os: linux
|
- which valgrind
|
||||||
addons: *gcc7
|
- if [ -n "$CLANG_VERSION" ]; then sudo CXX=$CXX CC=$CC ./tests/install_libcxx.sh; fi
|
||||||
|
|
||||||
# Test clang-3.5: C++11, Build=Debug/Release
|
install:
|
||||||
- env: CLANG_VERSION=3.5 BUILD_TYPE=Debug CPP=11
|
- cd $CHECKOUT_PATH
|
||||||
os: linux
|
|
||||||
addons: *clang35
|
# Workaround for valgrind bug: https://bugs.kde.org/show_bug.cgi?id=326469.
|
||||||
|
# It is fixed in valgrind 3.10 so this won't be necessary if someone
|
||||||
- env: CLANG_VERSION=3.5 BUILD_TYPE=Release CPP=11
|
# replaces the current valgrind (3.7) with valgrind-3.10
|
||||||
os: linux
|
- sed -i 's/march=native/msse4.2/' example/Makefile
|
||||||
addons: *clang35
|
|
||||||
|
- if [ ! -d build ]; then mkdir build; fi
|
||||||
# osx
|
- export CXX_FLAGS="-I${CHECKOUT_PATH}/include"
|
||||||
- env: BUILD_TYPE=Release CPP=11 ASAN=Off TSAN=Off
|
- export CXX_LINKER_FLAGS=""
|
||||||
os: osx
|
- if [ -z "$BUILD_TYPE" ]; then export BUILD_TYPE=Release; fi
|
||||||
|
- if [ "$ASAN" == "On"]; then export CXX_FLAGS="${CXX_FLAGS} -fsanitize=address,undefined,integer -fno-omit-frame-pointer -fno-sanitize=unsigned-integer-overflow"; fi
|
||||||
# Test clang-10.0: C++11, Build=Debug/Release
|
- if [ -n "$CLANG_VERSION" ]; then CXX_FLAGS="${CXX_FLAGS} -D__extern_always_inline=inline"; fi
|
||||||
- env: CLANG_VERSION=10 BUILD_TYPE=Debug CPP=11
|
- if [ "$LIBCXX" == "On" ]; then CXX_FLAGS="${CXX_FLAGS} -stdlib=libc++ -I/usr/include/c++/v1/"; fi
|
||||||
os: linux
|
- if [ "$LIBCXX" == "On" ]; then CXX_LINKER_FLAGS="${CXX_FLAGS} -L/usr/lib/ -lc++"; fi
|
||||||
dist: bionic
|
- CXX_FLAGS="${CXX_FLAGS} -std=c++${CPP}"
|
||||||
addons: *clang10
|
|
||||||
|
# Build examples
|
||||||
- env: CLANG_VERSION=10 BUILD_TYPE=Release CPP=11 ASAN=On
|
- cd example
|
||||||
os: linux
|
- if [ "$BUILD_TYPE" == "Release" ]; then make rebuild CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example; fi
|
||||||
dist: bionic
|
- if [ "$BUILD_TYPE" == "Debug" ]; then make rebuild debug CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example-debug; fi
|
||||||
addons: *clang10
|
|
||||||
|
|
||||||
|
script:
|
||||||
before_script:
|
- ./"${BIN}"
|
||||||
- if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi
|
- valgrind --trace-children=yes --leak-check=full ./"${BIN}"
|
||||||
- if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi
|
- cd $CHECKOUT_PATH/tests; make rebuild; ./tests
|
||||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then export CXX="clang++" CC="clang"; fi
|
- cd $CHECKOUT_PATH/tests; STYLE=printf make rebuild; ./tests
|
||||||
- which $CXX
|
|
||||||
- which $CC
|
notifications:
|
||||||
- $CXX --version
|
email: false
|
||||||
- cmake --version
|
|
||||||
|
|
||||||
script:
|
|
||||||
- cd ${TRAVIS_BUILD_DIR}
|
|
||||||
- mkdir -p build && cd build
|
|
||||||
- |
|
|
||||||
cmake .. \
|
|
||||||
--warn-uninitialized \
|
|
||||||
-DCMAKE_BUILD_TYPE=$BUILD_TYPE \
|
|
||||||
-DCMAKE_CXX_STANDARD=$CPP \
|
|
||||||
-DSPDLOG_BUILD_EXAMPLE=ON \
|
|
||||||
-DSPDLOG_BUILD_EXAMPLE_HO=ON \
|
|
||||||
-DSPDLOG_BUILD_WARNINGS=ON \
|
|
||||||
-DSPDLOG_BUILD_BENCH=OFF \
|
|
||||||
-DSPDLOG_BUILD_TESTS=ON \
|
|
||||||
-DSPDLOG_BUILD_TESTS_HO=OFF \
|
|
||||||
-DSPDLOG_SANITIZE_ADDRESS=$ASAN
|
|
||||||
|
|
||||||
- make VERBOSE=1 -j2
|
|
||||||
- ctest -j2 --output-on-failure
|
|
||||||
|
|
||||||
|
367
CMakeLists.txt
367
CMakeLists.txt
@@ -1,300 +1,115 @@
|
|||||||
# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
#
|
||||||
|
# Copyright(c) 2015 Ruslan Baratov.
|
||||||
cmake_minimum_required(VERSION 3.10)
|
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
#
|
||||||
# ---------------------------------------------------------------------------------------
|
|
||||||
# Start spdlog project
|
|
||||||
# ---------------------------------------------------------------------------------------
|
|
||||||
include(cmake/utils.cmake)
|
|
||||||
include(cmake/ide.cmake)
|
|
||||||
|
|
||||||
spdlog_extract_version()
|
|
||||||
|
|
||||||
project(spdlog VERSION ${SPDLOG_VERSION} LANGUAGES CXX)
|
|
||||||
message(STATUS "Build spdlog: ${SPDLOG_VERSION}")
|
|
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.1)
|
||||||
|
project(spdlog VERSION 0.17.0 LANGUAGES CXX)
|
||||||
|
include(CTest)
|
||||||
|
include(CMakeDependentOption)
|
||||||
include(GNUInstallDirs)
|
include(GNUInstallDirs)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------------
|
||||||
# Set default build to release
|
# compiler config
|
||||||
# ---------------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------------
|
||||||
if(NOT CMAKE_BUILD_TYPE)
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||||
|
set(CMAKE_CXX_FLAGS "-Wall -O3 ${CMAKE_CXX_FLAGS}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------------
|
||||||
# Compiler config
|
# spdlog target
|
||||||
# ---------------------------------------------------------------------------------------
|
#---------------------------------------------------------------------------------------
|
||||||
if(NOT CMAKE_CXX_STANDARD)
|
add_library(spdlog INTERFACE)
|
||||||
set(CMAKE_CXX_STANDARD 11)
|
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# make sure __cplusplus is defined when using msvc
|
option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF)
|
||||||
if(MSVC)
|
cmake_dependent_option(SPDLOG_BUILD_TESTING
|
||||||
string(APPEND CMAKE_CXX_FLAGS " /Zc:__cplusplus")
|
"Build spdlog tests" ON
|
||||||
endif()
|
"BUILD_TESTING" OFF
|
||||||
|
)
|
||||||
|
|
||||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
target_include_directories(
|
||||||
|
spdlog
|
||||||
|
INTERFACE
|
||||||
|
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
||||||
|
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||||
|
)
|
||||||
|
|
||||||
if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN" OR CMAKE_SYSTEM_NAME MATCHES "MSYS")
|
set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||||
set(CMAKE_CXX_EXTENSIONS ON)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
if(SPDLOG_BUILD_EXAMPLES)
|
||||||
# Set SPDLOG_MASTER_PROJECT to ON if we are building spdlog
|
|
||||||
# ---------------------------------------------------------------------------------------
|
|
||||||
# Check if spdlog is being used directly or via add_subdirectory, but allow overriding
|
|
||||||
if(NOT DEFINED SPDLOG_MASTER_PROJECT)
|
|
||||||
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
|
|
||||||
set(SPDLOG_MASTER_PROJECT ON)
|
|
||||||
else()
|
|
||||||
set(SPDLOG_MASTER_PROJECT OFF)
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
option(SPDLOG_BUILD_ALL "Build all artifacts" OFF)
|
|
||||||
|
|
||||||
# build shared option
|
|
||||||
option(SPDLOG_BUILD_SHARED "Build shared library" OFF)
|
|
||||||
|
|
||||||
# precompiled headers option
|
|
||||||
option(SPDLOG_ENABLE_PCH "Build static or shared library using precompiled header to speed up compilation time" OFF)
|
|
||||||
|
|
||||||
# example options
|
|
||||||
option(SPDLOG_BUILD_EXAMPLE "Build example" ${SPDLOG_MASTER_PROJECT})
|
|
||||||
option(SPDLOG_BUILD_EXAMPLE_HO "Build header only example" OFF)
|
|
||||||
|
|
||||||
# testing options
|
|
||||||
option(SPDLOG_BUILD_TESTS "Build tests" OFF)
|
|
||||||
option(SPDLOG_BUILD_TESTS_HO "Build tests using the header only version" 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_INSTALL "Generate the install target" ${SPDLOG_MASTER_PROJECT})
|
|
||||||
option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF)
|
|
||||||
option(SPDLOG_FMT_EXTERNAL_HO "Use external fmt header-only library instead of bundled" OFF)
|
|
||||||
option(SPDLOG_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF)
|
|
||||||
|
|
||||||
if(SPDLOG_FMT_EXTERNAL AND SPDLOG_FMT_EXTERNAL_HO)
|
|
||||||
message(FATAL_ERROR "SPDLOG_FMT_EXTERNAL and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# misc tweakme options
|
|
||||||
if(WIN32)
|
|
||||||
option(SPDLOG_WCHAR_SUPPORT "Support wchar api" OFF)
|
|
||||||
option(SPDLOG_WCHAR_FILENAMES "Support wchar filenames" OFF)
|
|
||||||
endif()
|
|
||||||
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
|
||||||
option(SPDLOG_CLOCK_COARSE
|
|
||||||
"Use the much faster (but much less accurate) CLOCK_REALTIME_COARSE instead of the regular clock," OFF)
|
|
||||||
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_NO_TLS "prevent spdlog from using thread local storage" OFF)
|
|
||||||
option(
|
|
||||||
SPDLOG_NO_ATOMIC_LEVELS
|
|
||||||
"prevent spdlog from using of std::atomic log levels (use only if your code never modifies log levels concurrently"
|
|
||||||
OFF)
|
|
||||||
option(SPDLOG_DISABLE_DEFAULT_LOGGER "Disable default logger creation" OFF)
|
|
||||||
|
|
||||||
# clang-tidy
|
|
||||||
if(${CMAKE_VERSION} VERSION_GREATER "3.5")
|
|
||||||
option(SPDLOG_TIDY "run clang-tidy" OFF)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(SPDLOG_TIDY)
|
|
||||||
set(CMAKE_CXX_CLANG_TIDY "clang-tidy")
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
|
||||||
message(STATUS "Enabled clang-tidy")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
|
||||||
message(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
|
|
||||||
# ---------------------------------------------------------------------------------------
|
|
||||||
# Static/Shared library (shared not supported in windows yet)
|
|
||||||
# ---------------------------------------------------------------------------------------
|
|
||||||
set(SPDLOG_SRCS src/spdlog.cpp src/stdout_sinks.cpp src/color_sinks.cpp src/file_sinks.cpp src/async.cpp src/cfg.cpp)
|
|
||||||
|
|
||||||
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
|
||||||
list(APPEND SPDLOG_SRCS src/fmt.cpp)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(SPDLOG_BUILD_SHARED OR BUILD_SHARED_LIBS)
|
|
||||||
if(WIN32)
|
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY)
|
|
||||||
list(APPEND SPDLOG_SRCS ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
|
||||||
endif()
|
|
||||||
add_library(spdlog SHARED ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
|
|
||||||
target_compile_definitions(spdlog PUBLIC SPDLOG_SHARED_LIB)
|
|
||||||
if(MSVC)
|
|
||||||
target_compile_options(spdlog PUBLIC
|
|
||||||
$<$<AND:$<CXX_COMPILER_ID:MSVC>,$<NOT:$<COMPILE_LANGUAGE:CUDA>>>:/wd4251 /wd4275>)
|
|
||||||
endif()
|
|
||||||
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
|
||||||
target_compile_definitions(spdlog PRIVATE FMT_EXPORT PUBLIC FMT_SHARED)
|
|
||||||
endif()
|
|
||||||
else()
|
|
||||||
add_library(spdlog STATIC ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_library(spdlog::spdlog ALIAS spdlog)
|
|
||||||
|
|
||||||
target_compile_definitions(spdlog PUBLIC SPDLOG_COMPILED_LIB)
|
|
||||||
target_include_directories(spdlog PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
|
||||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
|
|
||||||
target_link_libraries(spdlog PUBLIC Threads::Threads)
|
|
||||||
spdlog_enable_warnings(spdlog)
|
|
||||||
|
|
||||||
set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION ${SPDLOG_VERSION_MAJOR})
|
|
||||||
set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX d)
|
|
||||||
|
|
||||||
if(COMMAND target_precompile_headers AND SPDLOG_ENABLE_PCH)
|
|
||||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/pch.h.in ${PROJECT_BINARY_DIR}/spdlog_pch.h @ONLY)
|
|
||||||
target_precompile_headers(spdlog PRIVATE ${PROJECT_BINARY_DIR}/spdlog_pch.h)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
|
||||||
# Header only version
|
|
||||||
# ---------------------------------------------------------------------------------------
|
|
||||||
add_library(spdlog_header_only INTERFACE)
|
|
||||||
add_library(spdlog::spdlog_header_only ALIAS spdlog_header_only)
|
|
||||||
|
|
||||||
target_include_directories(spdlog_header_only INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
|
||||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
|
|
||||||
target_link_libraries(spdlog_header_only INTERFACE Threads::Threads)
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
|
||||||
# Use fmt package if using external fmt
|
|
||||||
# ---------------------------------------------------------------------------------------
|
|
||||||
if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO)
|
|
||||||
if(NOT TARGET fmt::fmt)
|
|
||||||
find_package(fmt 5.3.0 CONFIG REQUIRED)
|
|
||||||
endif()
|
|
||||||
target_compile_definitions(spdlog PUBLIC SPDLOG_FMT_EXTERNAL)
|
|
||||||
target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FMT_EXTERNAL)
|
|
||||||
|
|
||||||
# use external fmt-header-nly
|
|
||||||
if(SPDLOG_FMT_EXTERNAL_HO)
|
|
||||||
target_link_libraries(spdlog PUBLIC fmt::fmt-header-only)
|
|
||||||
target_link_libraries(spdlog_header_only INTERFACE fmt::fmt-header-only)
|
|
||||||
else() # use external compile fmt
|
|
||||||
target_link_libraries(spdlog PUBLIC fmt::fmt)
|
|
||||||
target_link_libraries(spdlog_header_only INTERFACE fmt::fmt)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(PKG_CONFIG_REQUIRES fmt) # add dependency to pkg-config
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
|
||||||
# Misc definitions according to tweak options
|
|
||||||
# ---------------------------------------------------------------------------------------
|
|
||||||
set(SPDLOG_WCHAR_TO_UTF8_SUPPORT ${SPDLOG_WCHAR_SUPPORT})
|
|
||||||
foreach(
|
|
||||||
SPDLOG_OPTION
|
|
||||||
SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
|
||||||
SPDLOG_WCHAR_FILENAMES
|
|
||||||
SPDLOG_NO_EXCEPTIONS
|
|
||||||
SPDLOG_CLOCK_COARSE
|
|
||||||
SPDLOG_PREVENT_CHILD_FD
|
|
||||||
SPDLOG_NO_THREAD_ID
|
|
||||||
SPDLOG_NO_TLS
|
|
||||||
SPDLOG_NO_ATOMIC_LEVELS
|
|
||||||
SPDLOG_DISABLE_DEFAULT_LOGGER)
|
|
||||||
if(${SPDLOG_OPTION})
|
|
||||||
target_compile_definitions(spdlog PUBLIC ${SPDLOG_OPTION})
|
|
||||||
target_compile_definitions(spdlog_header_only INTERFACE ${SPDLOG_OPTION})
|
|
||||||
endif()
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
if(SPDLOG_NO_EXCEPTIONS AND NOT MSVC)
|
|
||||||
target_compile_options(spdlog PRIVATE -fno-exceptions)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
|
||||||
# Build binaries
|
|
||||||
# ---------------------------------------------------------------------------------------
|
|
||||||
if(SPDLOG_BUILD_EXAMPLE OR SPDLOG_BUILD_EXAMPLE_HO OR SPDLOG_BUILD_ALL)
|
|
||||||
message(STATUS "Generating example(s)")
|
|
||||||
add_subdirectory(example)
|
add_subdirectory(example)
|
||||||
spdlog_enable_warnings(example)
|
|
||||||
if(SPDLOG_BUILD_EXAMPLE_HO)
|
|
||||||
spdlog_enable_warnings(example_header_only)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_TESTS_HO OR SPDLOG_BUILD_ALL)
|
if(SPDLOG_BUILD_TESTING)
|
||||||
message(STATUS "Generating tests")
|
|
||||||
enable_testing()
|
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(SPDLOG_BUILD_BENCH OR SPDLOG_BUILD_ALL)
|
#---------------------------------------------------------------------------------------
|
||||||
message(STATUS "Generating benchmarks")
|
# Install/export targets and files
|
||||||
add_subdirectory(bench)
|
#---------------------------------------------------------------------------------------
|
||||||
endif()
|
# set files and directories
|
||||||
|
set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
|
||||||
|
set(include_install_dir "${CMAKE_INSTALL_INCLUDEDIR}")
|
||||||
|
set(pkgconfig_install_dir "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
||||||
|
set(version_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
|
||||||
|
set(project_config "${PROJECT_NAME}Config.cmake")
|
||||||
|
set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc")
|
||||||
|
set(targets_export_name "${PROJECT_NAME}Targets")
|
||||||
|
set(namespace "${PROJECT_NAME}::")
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
# generate package version file
|
||||||
# Install
|
include(CMakePackageConfigHelpers)
|
||||||
# ---------------------------------------------------------------------------------------
|
write_basic_package_version_file(
|
||||||
if(SPDLOG_INSTALL)
|
"${version_config}" COMPATIBILITY SameMajorVersion
|
||||||
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")
|
|
||||||
set(pkgconfig_install_dir "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
|
||||||
set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc")
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
# configure pkg config file
|
||||||
# Include files
|
configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY)
|
||||||
# ---------------------------------------------------------------------------------------
|
|
||||||
install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" PATTERN "fmt/bundled" EXCLUDE)
|
|
||||||
install(
|
|
||||||
TARGETS spdlog spdlog_header_only
|
|
||||||
EXPORT spdlog
|
|
||||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
|
||||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
|
||||||
|
|
||||||
if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
|
# install targets
|
||||||
install(DIRECTORY include/${PROJECT_NAME}/fmt/bundled/
|
install(
|
||||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/fmt/bundled/")
|
TARGETS spdlog
|
||||||
endif()
|
EXPORT "${targets_export_name}"
|
||||||
|
)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
# install headers
|
||||||
# Install pkg-config file
|
install(
|
||||||
# ---------------------------------------------------------------------------------------
|
DIRECTORY "${HEADER_BASE}/${PROJECT_NAME}"
|
||||||
get_target_property(PKG_CONFIG_DEFINES spdlog INTERFACE_COMPILE_DEFINITIONS)
|
DESTINATION "${include_install_dir}"
|
||||||
string(REPLACE ";" " -D" PKG_CONFIG_DEFINES "${PKG_CONFIG_DEFINES}")
|
)
|
||||||
string(CONCAT PKG_CONFIG_DEFINES "-D" "${PKG_CONFIG_DEFINES}")
|
|
||||||
configure_file("cmake/${PROJECT_NAME}.pc.in" "${pkg_config}" @ONLY)
|
|
||||||
install(FILES "${pkg_config}" DESTINATION "${pkgconfig_install_dir}")
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
# install project version file
|
||||||
# Install CMake config files
|
install(
|
||||||
# ---------------------------------------------------------------------------------------
|
FILES "${version_config}"
|
||||||
install(EXPORT spdlog DESTINATION ${export_dest_dir} NAMESPACE spdlog:: FILE ${config_targets_file})
|
DESTINATION "${config_install_dir}"
|
||||||
|
)
|
||||||
|
|
||||||
include(CMakePackageConfigHelpers)
|
# install pkg config file
|
||||||
configure_file("${project_config_in}" "${project_config_out}" @ONLY)
|
install(
|
||||||
|
FILES "${pkg_config}"
|
||||||
|
DESTINATION "${pkgconfig_install_dir}"
|
||||||
|
)
|
||||||
|
|
||||||
write_basic_package_version_file("${version_config_file}" COMPATIBILITY SameMajorVersion)
|
# install project config file
|
||||||
install(FILES "${project_config_out}" "${version_config_file}" DESTINATION "${export_dest_dir}")
|
install(
|
||||||
|
EXPORT "${targets_export_name}"
|
||||||
|
NAMESPACE "${namespace}"
|
||||||
|
DESTINATION "${config_install_dir}"
|
||||||
|
FILE ${project_config}
|
||||||
|
)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
# export build directory config file
|
||||||
# Support creation of installable packages
|
export(
|
||||||
# ---------------------------------------------------------------------------------------
|
EXPORT ${targets_export_name}
|
||||||
include(cmake/spdlogCPack.cmake)
|
NAMESPACE "${namespace}"
|
||||||
endif()
|
FILE ${project_config}
|
||||||
|
)
|
||||||
|
|
||||||
|
# register project in CMake user registry
|
||||||
|
export(PACKAGE ${PROJECT_NAME})
|
||||||
|
|
||||||
|
file(GLOB_RECURSE spdlog_include_SRCS "${HEADER_BASE}/*.h")
|
||||||
|
add_custom_target(spdlog_headers_for_ide SOURCES ${spdlog_include_SRCS})
|
||||||
|
21
INSTALL
21
INSTALL
@@ -1,24 +1,13 @@
|
|||||||
Header only version:
|
spdlog is header only library.
|
||||||
==================================================================
|
Just copy the files to your build tree and use a C++11 compiler
|
||||||
Just copy the files to your build tree and use a C++11 compiler.
|
|
||||||
Or use CMake:
|
|
||||||
add_executable(example_header_only example.cpp)
|
|
||||||
target_link_libraries(example_header_only spdlog::spdlog_header_only)
|
|
||||||
|
|
||||||
|
|
||||||
Compiled library version:
|
|
||||||
==================================================================
|
|
||||||
CMake:
|
|
||||||
add_executable(example example.cpp)
|
|
||||||
target_link_libraries(example spdlog::spdlog)
|
|
||||||
|
|
||||||
Or copy src/spdlog.cpp to your build tree and pass the -DSPDLOG_COMPILED_LIB to the compiler.
|
|
||||||
|
|
||||||
Tested on:
|
Tested on:
|
||||||
gcc 4.8.1 and above
|
gcc 4.8.1 and above
|
||||||
clang 3.5
|
clang 3.5
|
||||||
Visual Studio 2013
|
Visual Studio 2013
|
||||||
|
|
||||||
|
gcc 4.8 flags: --std==c++11 -pthread -O3 -flto -Wl,--no-as-needed
|
||||||
|
gcc 4.9 flags: --std=c++11 -pthread -O3 -flto
|
||||||
|
|
||||||
|
|
||||||
|
see the makefile in the example folder
|
||||||
|
4
LICENSE
4
LICENSE
@@ -20,7 +20,3 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-- NOTE: Third party dependency used by this software --
|
|
||||||
This software depends on the fmt lib (MIT License),
|
|
||||||
and users must comply to its license: https://github.com/fmtlib/fmt/blob/master/LICENSE.rst
|
|
||||||
|
|
||||||
|
541
README.md
541
README.md
@@ -1,436 +1,221 @@
|
|||||||
# spdlog
|
# spdlog
|
||||||
|
|
||||||
Very fast, header-only/compiled, C++ logging library. [](https://travis-ci.org/gabime/spdlog) [](https://ci.appveyor.com/project/gabime/spdlog) [](https://github.com/gabime/spdlog/releases/latest)
|
Very fast, header only, C++ logging library. [](https://travis-ci.org/gabime/spdlog) [](https://ci.appveyor.com/project/gabime/spdlog)
|
||||||
|
|
||||||
## Install
|
|
||||||
#### Header only version
|
|
||||||
Copy the source [folder](https://github.com/gabime/spdlog/tree/v1.x/include/spdlog) to your build tree and use a C++11 compiler.
|
|
||||||
|
|
||||||
#### Static lib version (recommended - much faster compile times)
|
## Install
|
||||||
```console
|
#### Just copy the headers:
|
||||||
$ git clone https://github.com/gabime/spdlog.git
|
|
||||||
$ cd spdlog && mkdir build && cd build
|
* Copy the source [folder](https://github.com/gabime/spdlog/tree/master/include/spdlog) to your build tree and use a C++11 compiler.
|
||||||
$ cmake .. && make -j
|
|
||||||
```
|
#### Or use your favorite package manager:
|
||||||
|
|
||||||
see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v1.x/example/CMakeLists.txt) on how to use.
|
* Ubuntu: `apt-get install libspdlog-dev`
|
||||||
|
* Homebrew: `brew install spdlog`
|
||||||
|
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
|
||||||
|
* Fedora: `yum install spdlog`
|
||||||
|
* Gentoo: `emerge dev-libs/spdlog`
|
||||||
|
* Arch Linux: `yaourt -S spdlog-git`
|
||||||
|
* vcpkg: `vcpkg install spdlog`
|
||||||
|
|
||||||
|
|
||||||
## Platforms
|
## Platforms
|
||||||
* Linux, FreeBSD, OpenBSD, Solaris, AIX
|
* Linux, FreeBSD, Solaris
|
||||||
* Windows (msvc 2013+, cygwin)
|
* Windows (vc 2013+, cygwin)
|
||||||
* macOS (clang 3.5+)
|
* Mac OSX (clang 3.5+)
|
||||||
* Android
|
* Android
|
||||||
|
|
||||||
## Package managers:
|
|
||||||
* Homebrew: `brew install spdlog`
|
|
||||||
* MacPorts: `sudo port install spdlog`
|
|
||||||
* FreeBSD: `cd /usr/ports/devel/spdlog/ && make install clean`
|
|
||||||
* Fedora: `dnf install spdlog`
|
|
||||||
* Gentoo: `emerge dev-libs/spdlog`
|
|
||||||
* Arch Linux: `pacman -S spdlog`
|
|
||||||
* vcpkg: `vcpkg install spdlog`
|
|
||||||
* conan: `spdlog/[>=1.4.1]`
|
|
||||||
* conda: `conda install -c conda-forge spdlog`
|
|
||||||
* build2: ```depends: spdlog ^1.8.2```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
* Very fast (see [benchmarks](#benchmarks) below).
|
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
|
||||||
* Headers only or compiled
|
* Headers only, just copy and use.
|
||||||
* Feature rich formatting, using the excellent [fmt](https://github.com/fmtlib/fmt) library.
|
* Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library.
|
||||||
|
* Optional printf syntax support.
|
||||||
* Asynchronous mode (optional)
|
* Asynchronous mode (optional)
|
||||||
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
|
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
|
||||||
|
* Conditional Logging
|
||||||
* Multi/Single threaded loggers.
|
* Multi/Single threaded loggers.
|
||||||
* Various log targets:
|
* Various log targets:
|
||||||
* Rotating log files.
|
* Rotating log files.
|
||||||
* Daily log files.
|
* Daily log files.
|
||||||
* Console logging (colors supported).
|
* Console logging (colors supported).
|
||||||
* syslog.
|
* syslog.
|
||||||
* Windows event log.
|
* Windows debugger (```OutputDebugString(..)```)
|
||||||
* Windows debugger (```OutputDebugString(..)```).
|
|
||||||
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
|
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
|
||||||
* Log filtering - log levels can be modified in runtime as well as in compile time.
|
* Severity based filtering - threshold levels can be modified in runtime as well as in compile time.
|
||||||
* Support for loading log levels from argv or from environment var.
|
|
||||||
* [Backtrace](#backtrace-support) support - store debug messages in a ring buffer and display later on demand.
|
|
||||||
|
|
||||||
## Usage samples
|
|
||||||
|
|
||||||
#### Basic usage
|
|
||||||
|
|
||||||
|
## Benchmarks
|
||||||
|
|
||||||
|
Below are some [benchmarks](bench) comparing popular log libraries under Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
|
||||||
|
|
||||||
|
#### Synchronous mode
|
||||||
|
Time needed to log 1,000,000 lines in synchronous mode (in seconds, the best of 3 runs):
|
||||||
|
|
||||||
|
|threads|boost log 1.54|glog |easylogging |spdlog|
|
||||||
|
|-------|:-------:|:-----:|----------:|------:|
|
||||||
|
|1| 4.169s |1.066s |0.975s |0.392s|
|
||||||
|
|10| 6.180s |3.032s |2.857s |0.773s|
|
||||||
|
|100| 5.981s |1.139s |4.512s |0.587s|
|
||||||
|
|
||||||
|
|
||||||
|
#### Asynchronous mode
|
||||||
|
Time needed to log 1,000,000 lines in asynchronous mode, i.e. the time it takes to put them in the async queue (in seconds, the best of 3 runs):
|
||||||
|
|
||||||
|
|threads|g2log <sup>async logger</sup> |spdlog <sup>async mode</sup>|
|
||||||
|
|:-------|:-----:|-------------------------:|
|
||||||
|
|1| 1.850s |0.39s |
|
||||||
|
|10| 0.943s |0.416s|
|
||||||
|
|100| 0.959s |0.413s|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Usage Example
|
||||||
```c++
|
```c++
|
||||||
|
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
int main()
|
#include <iostream>
|
||||||
{
|
#include <memory>
|
||||||
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");
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
void async_example();
|
||||||
---
|
void syslog_example();
|
||||||
#### Create stdout/stderr logger object
|
void user_defined_example();
|
||||||
```c++
|
void err_handler_example();
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
|
||||||
void stdout_example()
|
|
||||||
{
|
|
||||||
// create 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)");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
namespace spd = spdlog;
|
||||||
#### Basic file logger
|
int main(int, char*[])
|
||||||
```c++
|
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
|
||||||
void basic_logfile_example()
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
auto logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
|
// Console logger with color
|
||||||
|
auto console = spd::stdout_color_mt("console");
|
||||||
|
console->info("Welcome to spdlog!");
|
||||||
|
console->error("Some error message with arg{}..", 1);
|
||||||
|
|
||||||
|
// Formatting examples
|
||||||
|
console->warn("Easy padding in numbers like {:08d}", 12);
|
||||||
|
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||||
|
console->info("Support for floats {:03.2f}", 1.23456);
|
||||||
|
console->info("Positional args are {1} {0}..", "too", "supported");
|
||||||
|
console->info("{:<30}", "left aligned");
|
||||||
|
|
||||||
|
// Use global registry to retrieve loggers
|
||||||
|
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
|
||||||
|
|
||||||
|
// Create basic file logger (not rotated)
|
||||||
|
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic.txt");
|
||||||
|
my_logger->info("Some log message");
|
||||||
|
|
||||||
|
// Create a file rotating logger with 5mb size max and 3 rotated files
|
||||||
|
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/mylogfile.txt", 1048576 * 5, 3);
|
||||||
|
for (int i = 0; i < 10; ++i)
|
||||||
|
rotating_logger->info("{} * {} equals {:>10}", i, i, i*i);
|
||||||
|
|
||||||
|
// Create a daily logger - a new file is created every day on 2:30am
|
||||||
|
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
||||||
|
// trigger flush if the log severity is error or higher
|
||||||
|
daily_logger->flush_on(spd::level::err);
|
||||||
|
daily_logger->info(123.44);
|
||||||
|
|
||||||
|
// Customize msg format for all messages
|
||||||
|
spd::set_pattern("[%^+++%$] [%H:%M:%S %z] [thread %t] %v");
|
||||||
|
console->info("This an info message with custom format (and custom color range between the '%^' and '%$')");
|
||||||
|
console->error("This an error message with custom format (and custom color range between the '%^' and '%$')");
|
||||||
|
|
||||||
|
// Runtime log levels
|
||||||
|
spd::set_level(spd::level::info); //Set global log level to info
|
||||||
|
console->debug("This message should not be displayed!");
|
||||||
|
console->set_level(spd::level::debug); // Set specific logger's log level
|
||||||
|
console->debug("This message should be displayed..");
|
||||||
|
|
||||||
|
// Compile time log levels
|
||||||
|
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
|
||||||
|
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
|
||||||
|
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
|
||||||
|
|
||||||
|
// Asynchronous logging is very fast..
|
||||||
|
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
|
||||||
|
async_example();
|
||||||
|
|
||||||
|
// syslog example. linux/osx only
|
||||||
|
syslog_example();
|
||||||
|
|
||||||
|
// android example. compile with NDK
|
||||||
|
android_example();
|
||||||
|
|
||||||
|
// Log user-defined types example
|
||||||
|
user_defined_example();
|
||||||
|
|
||||||
|
// Change default log error handler
|
||||||
|
err_handler_example();
|
||||||
|
|
||||||
|
// Apply a function on all registered loggers
|
||||||
|
spd::apply_all([&](std::shared_ptr<spd::logger> l)
|
||||||
|
{
|
||||||
|
l->info("End of example.");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Release and close all loggers
|
||||||
|
spd::drop_all();
|
||||||
}
|
}
|
||||||
catch (const spdlog::spdlog_ex &ex)
|
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
|
||||||
|
catch (const spd::spdlog_ex& ex)
|
||||||
{
|
{
|
||||||
std::cout << "Log init failed: " << ex.what() << std::endl;
|
std::cout << "Log init failed: " << ex.what() << std::endl;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
|
||||||
---
|
|
||||||
#### Rotating files
|
|
||||||
```c++
|
|
||||||
#include "spdlog/sinks/rotating_file_sink.h"
|
|
||||||
void rotating_example()
|
|
||||||
{
|
|
||||||
// Create a file rotating logger with 5mb 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 on 2:30am
|
|
||||||
auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
#### Backtrace support
|
|
||||||
```c++
|
|
||||||
// Loggers can store in a ring buffer all messages (including debug/trace) and display later on demand.
|
|
||||||
// When needed, call dump_backtrace() to see them
|
|
||||||
|
|
||||||
spdlog::enable_backtrace(32); // Store the latest 32 messages in a buffer. Older messages will be dropped.
|
|
||||||
// or my_logger->enable_backtrace(32)..
|
|
||||||
for(int i = 0; i < 100; i++)
|
|
||||||
{
|
|
||||||
spdlog::debug("Backtrace message {}", i); // not logged yet..
|
|
||||||
}
|
|
||||||
// e.g. if some error happened:
|
|
||||||
spdlog::dump_backtrace(); // log them now! show the last 32 messages
|
|
||||||
|
|
||||||
// or my_logger->dump_backtrace(32)..
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
#### 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 to 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 different format and log level
|
|
||||||
```c++
|
|
||||||
|
|
||||||
// create 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");
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
#### Asynchronous logging
|
|
||||||
```c++
|
|
||||||
#include "spdlog/async.h"
|
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
|
||||||
void async_example()
|
void async_example()
|
||||||
{
|
{
|
||||||
// default thread pool settings can be modified *before* creating the async logger:
|
size_t q_size = 4096;
|
||||||
// spdlog::init_thread_pool(8192, 1); // queue with 8k items and 1 backing thread.
|
spd::set_async_mode(q_size);
|
||||||
auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
|
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
|
||||||
// alternatively:
|
for (int i = 0; i < 100; ++i)
|
||||||
// auto async_file = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");
|
async_file->info("Async message #{}", i);
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
//syslog example
|
||||||
|
void syslog_example()
|
||||||
---
|
|
||||||
#### Asynchronous logger with multi sinks
|
|
||||||
```c++
|
|
||||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
|
||||||
#include "spdlog/sinks/rotating_file_sink.h"
|
|
||||||
|
|
||||||
void multi_sink_example2()
|
|
||||||
{
|
{
|
||||||
spdlog::init_thread_pool(8192, 1);
|
#ifdef SPDLOG_ENABLE_SYSLOG
|
||||||
auto stdout_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt >();
|
std::string ident = "spdlog-example";
|
||||||
auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("mylog.txt", 1024*1024*10, 3);
|
auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID);
|
||||||
std::vector<spdlog::sink_ptr> sinks {stdout_sink, rotating_sink};
|
syslog_logger->warn("This is warning that will end up in syslog..");
|
||||||
auto logger = std::make_shared<spdlog::async_logger>("loggername", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block);
|
#endif
|
||||||
spdlog::register_logger(logger);
|
|
||||||
}
|
}
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
#### User defined types
|
|
||||||
```c++
|
|
||||||
// user defined types logging by implementing operator<<
|
// user defined types logging by implementing operator<<
|
||||||
#include "spdlog/fmt/ostr.h" // must be included
|
|
||||||
struct my_type
|
struct my_type
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
template<typename OStream>
|
template<typename OStream>
|
||||||
friend OStream &operator<<(OStream &os, const my_type &c)
|
friend OStream& operator<<(OStream& os, const my_type &c)
|
||||||
{
|
{
|
||||||
return os << "[my_type i=" << c.i << "]";
|
return os << "[my_type i="<<c.i << "]";
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#include <spdlog/fmt/ostr.h> // must be included
|
||||||
void user_defined_example()
|
void user_defined_example()
|
||||||
{
|
{
|
||||||
spdlog::get("console")->info("user defined type: {}", my_type{14});
|
spd::get("console")->info("user defined type: {}", my_type { 14 });
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
//
|
||||||
|
//custom error handler
|
||||||
---
|
//
|
||||||
#### 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 spdlog::details::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()
|
void err_handler_example()
|
||||||
{
|
{
|
||||||
// can be set globally or per logger(logger->set_error_handler(..))
|
spd::set_error_handler([](const std::string& msg) {
|
||||||
spdlog::set_error_handler([](const std::string &msg) { spdlog::get("console")->error("*** LOGGER ERROR ***: {}", msg); });
|
std::cerr << "my err handler: " << msg << std::endl;
|
||||||
spdlog::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
|
});
|
||||||
|
// (or logger->set_error_handler(..) to set for specific logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
|
||||||
#### 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 env variable or from argv
|
|
||||||
|
|
||||||
```c++
|
|
||||||
#include "spdlog/cfg/env.h"
|
|
||||||
int main (int argc, char *argv[])
|
|
||||||
{
|
|
||||||
spdlog::cfg::load_env_levels();
|
|
||||||
// or from 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
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
## Benchmarks
|
|
||||||
|
|
||||||
Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.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
|
||||||
Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages.
|
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>
|
|
||||||
|
|
||||||
|
|
||||||
|
83
appveyor.yml
83
appveyor.yml
@@ -1,71 +1,32 @@
|
|||||||
version: 1.0.{build}
|
version: 1.0.{build}
|
||||||
image: Visual Studio 2017
|
image: Visual Studio 2015
|
||||||
environment:
|
environment:
|
||||||
matrix:
|
matrix:
|
||||||
- GENERATOR: '"Visual Studio 14 2015"'
|
- GENERATOR: '"MinGW Makefiles"'
|
||||||
BUILD_TYPE: Debug
|
BUILD_TYPE: Debug
|
||||||
BUILD_SHARED: 'OFF'
|
- GENERATOR: '"MinGW Makefiles"'
|
||||||
WCHAR: 'OFF'
|
BUILD_TYPE: Release
|
||||||
WCHAR_FILES: 'OFF'
|
- GENERATOR: '"Visual Studio 14 2015"'
|
||||||
BUILD_EXAMPLE: 'ON'
|
BUILD_TYPE: Debug
|
||||||
- GENERATOR: '"Visual Studio 14 2015"'
|
- GENERATOR: '"Visual Studio 14 2015"'
|
||||||
BUILD_TYPE: Release
|
BUILD_TYPE: Release
|
||||||
BUILD_SHARED: 'OFF'
|
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
||||||
WCHAR: 'ON'
|
BUILD_TYPE: Debug
|
||||||
WCHAR_FILES: 'OFF'
|
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
||||||
BUILD_EXAMPLE: 'ON'
|
BUILD_TYPE: Release
|
||||||
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
|
||||||
BUILD_TYPE: Debug
|
|
||||||
BUILD_SHARED: 'OFF'
|
|
||||||
WCHAR: 'ON'
|
|
||||||
WCHAR_FILES: 'OFF'
|
|
||||||
BUILD_EXAMPLE: 'ON'
|
|
||||||
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
|
||||||
BUILD_TYPE: Release
|
|
||||||
BUILD_SHARED: 'OFF'
|
|
||||||
WCHAR: 'ON'
|
|
||||||
WCHAR_FILES: 'OFF'
|
|
||||||
BUILD_EXAMPLE: 'ON'
|
|
||||||
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
|
||||||
BUILD_TYPE: Debug
|
|
||||||
BUILD_SHARED: 'OFF'
|
|
||||||
WCHAR: 'ON'
|
|
||||||
WCHAR_FILES: 'OFF'
|
|
||||||
BUILD_EXAMPLE: 'ON'
|
|
||||||
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
|
||||||
BUILD_TYPE: Release
|
|
||||||
BUILD_SHARED: 'OFF'
|
|
||||||
WCHAR: 'OFF'
|
|
||||||
WCHAR_FILES: 'OFF'
|
|
||||||
BUILD_EXAMPLE: 'ON'
|
|
||||||
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
|
||||||
BUILD_TYPE: Release
|
|
||||||
BUILD_SHARED: 'ON'
|
|
||||||
WCHAR: 'OFF'
|
|
||||||
WCHAR_FILES: 'OFF'
|
|
||||||
BUILD_EXAMPLE: 'ON'
|
|
||||||
- GENERATOR: '"Visual Studio 15 2017 Win64"'
|
|
||||||
BUILD_TYPE: Release
|
|
||||||
BUILD_SHARED: 'ON'
|
|
||||||
WCHAR: 'ON'
|
|
||||||
WCHAR_FILES: 'ON'
|
|
||||||
BUILD_EXAMPLE: 'OFF'
|
|
||||||
build_script:
|
build_script:
|
||||||
- cmd: >-
|
- cmd: >-
|
||||||
set
|
set
|
||||||
|
|
||||||
mkdir build
|
mkdir build
|
||||||
|
|
||||||
cd build
|
cd build
|
||||||
|
|
||||||
set PATH=%PATH%;C:\Program Files\Git\usr\bin
|
set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
|
||||||
|
|
||||||
cmake -G %GENERATOR% -D CMAKE_BUILD_TYPE=%BUILD_TYPE% -D BUILD_SHARED_LIBS=%BUILD_SHARED% -D SPDLOG_WCHAR_SUPPORT=%WCHAR% -D SPDLOG_WCHAR_FILENAMES=%WCHAR_FILES% -D SPDLOG_BUILD_EXAMPLE=%BUILD_EXAMPLE% -D SPDLOG_BUILD_EXAMPLE_HO=%BUILD_EXAMPLE% -D SPDLOG_BUILD_TESTS=ON -D SPDLOG_BUILD_TESTS_HO=OFF -D SPDLOG_BUILD_WARNINGS=ON ..
|
set PATH=C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin;%PATH%
|
||||||
|
|
||||||
cmake --build . --config %BUILD_TYPE%
|
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE%
|
||||||
|
|
||||||
before_test:
|
cmake --build . --config %BUILD_TYPE%
|
||||||
- set PATH=%PATH%;C:\projects\spdlog\build\%BUILD_TYPE%
|
test: off
|
||||||
|
|
||||||
test_script:
|
|
||||||
- C:\projects\spdlog\build\tests\%BUILD_TYPE%\spdlog-utests.exe
|
|
||||||
|
@@ -1,42 +0,0 @@
|
|||||||
# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.10)
|
|
||||||
project(spdlog_bench CXX)
|
|
||||||
|
|
||||||
if(NOT TARGET spdlog)
|
|
||||||
# Stand-alone build
|
|
||||||
find_package(spdlog CONFIG REQUIRED)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
|
||||||
find_package(benchmark CONFIG)
|
|
||||||
if (NOT benchmark_FOUND)
|
|
||||||
message(STATUS "Using CMake Version ${CMAKE_VERSION}")
|
|
||||||
if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.11.0")
|
|
||||||
# User can fetch googlebenchmark
|
|
||||||
message(STATUS "Downloading GoogleBenchmark")
|
|
||||||
include(FetchContent)
|
|
||||||
set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE INTERNAL "")
|
|
||||||
# Do not build and run googlebenchmark tests
|
|
||||||
FetchContent_Declare(googlebenchmark
|
|
||||||
GIT_REPOSITORY https://github.com/google/benchmark.git
|
|
||||||
GIT_TAG v1.5.2)
|
|
||||||
|
|
||||||
FetchContent_MakeAvailable(googlebenchmark)
|
|
||||||
else()
|
|
||||||
message(FATAL_ERROR "GoogleBenchmark is missing. Use CMake >= 3.11 or download it")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_executable(bench bench.cpp)
|
|
||||||
spdlog_enable_warnings(bench)
|
|
||||||
target_link_libraries(bench PRIVATE spdlog::spdlog)
|
|
||||||
|
|
||||||
add_executable(async_bench async_bench.cpp)
|
|
||||||
target_link_libraries(async_bench PRIVATE spdlog::spdlog)
|
|
||||||
|
|
||||||
add_executable(latency latency.cpp)
|
|
||||||
target_link_libraries(latency PRIVATE benchmark::benchmark spdlog::spdlog)
|
|
||||||
|
|
||||||
add_executable(formatter-bench formatter-bench.cpp)
|
|
||||||
target_link_libraries(formatter-bench PRIVATE benchmark::benchmark spdlog::spdlog)
|
|
97
bench/Makefile
Normal file
97
bench/Makefile
Normal file
@@ -0,0 +1,97 @@
|
|||||||
|
CXX ?= g++
|
||||||
|
CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -I../include
|
||||||
|
CXX_RELEASE_FLAGS = -O3 -flto -DNDEBUG
|
||||||
|
|
||||||
|
|
||||||
|
# g2log-async
|
||||||
|
binaries=spdlog-bench spdlog-bench-mt spdlog-async spdlog-null-async \
|
||||||
|
boost-bench boost-bench-mt \
|
||||||
|
glog-bench glog-bench-mt \
|
||||||
|
g3log-async \
|
||||||
|
p7-bench p7-bench-mt \
|
||||||
|
log4cpp-bench log4cpp-bench-mt \
|
||||||
|
log4cplus-bench log4cplus-bench-mt \
|
||||||
|
easylogging-bench easylogging-bench-mt easylogging-bench-async \
|
||||||
|
plog-bench plog-bench-mt
|
||||||
|
|
||||||
|
all: $(binaries)
|
||||||
|
|
||||||
|
spdlog-bench: spdlog-bench.cpp
|
||||||
|
$(CXX) spdlog-bench.cpp -o spdlog-bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
spdlog-bench-mt: spdlog-bench-mt.cpp
|
||||||
|
$(CXX) spdlog-bench-mt.cpp -o spdlog-bench-mt $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
spdlog-async: spdlog-async.cpp
|
||||||
|
$(CXX) spdlog-async.cpp -o spdlog-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
spdlog-null-async: spdlog-null-async.cpp
|
||||||
|
$(CXX) spdlog-null-async.cpp -o spdlog-null-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
BOOST_FLAGS = -DBOOST_LOG_DYN_LINK -I$(HOME)/include -I/usr/include -L$(HOME)/lib -lboost_log_setup -lboost_log -lboost_filesystem -lboost_system -lboost_thread -lboost_regex -lboost_date_time -lboost_chrono
|
||||||
|
|
||||||
|
boost-bench: boost-bench.cpp
|
||||||
|
$(CXX) boost-bench.cpp -o boost-bench $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
boost-bench-mt: boost-bench-mt.cpp
|
||||||
|
$(CXX) boost-bench-mt.cpp -o boost-bench-mt $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
GLOG_FLAGS = -I$(HOME)/include -L$(HOME)/lib -lglog
|
||||||
|
glog-bench: glog-bench.cpp
|
||||||
|
$(CXX) glog-bench.cpp -o glog-bench $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
glog-bench-mt: glog-bench-mt.cpp
|
||||||
|
$(CXX) glog-bench-mt.cpp -o glog-bench-mt $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
G2LOG_FLAGS = -I$(HOME)/include -L$(HOME)/lib -llib_g2logger
|
||||||
|
g2log-async: g2log-async.cpp
|
||||||
|
$(CXX) g2log-async.cpp -o g2log-async $(CXXFLAGS) $(G2LOG_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
G3LOG_FLAGS = -I$(HOME)/include -L$(HOME)/lib -lg3logger
|
||||||
|
g3log-async: g3log-async.cpp
|
||||||
|
$(CXX) g3log-async.cpp -o g3log-async $(CXXFLAGS) $(G3LOG_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
P7_FLAGS = -I$(HOME)/P7/Headers -I$(HOME)/include -L$(HOME)/lib -lP7
|
||||||
|
p7-bench: p7-bench.cpp
|
||||||
|
$(CXX) p7-bench.cpp -o p7-bench $(CXXFLAGS) $(P7_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
p7-bench-mt: p7-bench-mt.cpp
|
||||||
|
$(CXX) p7-bench-mt.cpp -o p7-bench-mt $(CXXFLAGS) $(P7_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
LOG4CPP_FLAGS = -I$(HOME)/include -L$(HOME)/lib -llog4cpp
|
||||||
|
log4cpp-bench: log4cpp-bench.cpp
|
||||||
|
$(CXX) log4cpp-bench.cpp -o log4cpp-bench $(CXXFLAGS) $(LOG4CPP_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
log4cpp-bench-mt: log4cpp-bench-mt.cpp
|
||||||
|
$(CXX) log4cpp-bench-mt.cpp -o log4cpp-bench-mt $(CXXFLAGS) $(LOG4CPP_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
LOG4CPLUS_FLAGS = -I$(HOME)/include -L$(HOME)/lib -llog4cplus
|
||||||
|
log4cplus-bench: log4cplus-bench.cpp
|
||||||
|
$(CXX) log4cplus-bench.cpp -o log4cplus-bench $(CXXFLAGS) $(LOG4CPLUS_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
log4cplus-bench-mt: log4cplus-bench-mt.cpp
|
||||||
|
$(CXX) log4cplus-bench-mt.cpp -o log4cplus-bench-mt $(CXXFLAGS) $(LOG4CPLUS_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
EASYL_FLAGS = -I$(HOME)/easyloggingpp/src
|
||||||
|
easylogging-bench: easylogging-bench.cpp
|
||||||
|
$(CXX) easylogging-bench.cpp -o easylogging-bench $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
easylogging-bench-mt: easylogging-bench-mt.cpp
|
||||||
|
$(CXX) easylogging-bench-mt.cpp -o easylogging-bench-mt $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
easylogging-bench-async: easylogging-bench-async.cpp
|
||||||
|
$(CXX) easylogging-bench-async.cpp -o easylogging-bench-async $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
PLOG_FLAGS = -I$(HOME)/include
|
||||||
|
plog-bench: plog-bench.cpp
|
||||||
|
$(CXX) plog-bench.cpp -o plog-bench $(CXXFLAGS) $(PLOG_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
plog-bench-mt: plog-bench-mt.cpp
|
||||||
|
$(CXX) plog-bench-mt.cpp -o plog-bench-mt $(CXXFLAGS) $(PLOG_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o logs/* $(binaries)
|
||||||
|
|
||||||
|
rebuild: clean all
|
57
bench/Makefile.mingw
Normal file
57
bench/Makefile.mingw
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
CXX ?= g++
|
||||||
|
CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
|
||||||
|
CXX_RELEASE_FLAGS = -O3 -flto
|
||||||
|
|
||||||
|
|
||||||
|
binaries=spdlog-bench spdlog-bench-mt spdlog-async boost-bench boost-bench-mt glog-bench glog-bench-mt g2log-async easylogging-bench easylogging-bench-mt
|
||||||
|
|
||||||
|
all: $(binaries)
|
||||||
|
|
||||||
|
spdlog-bench: spdlog-bench.cpp
|
||||||
|
$(CXX) spdlog-bench.cpp -o spdlog-bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
spdlog-bench-mt: spdlog-bench-mt.cpp
|
||||||
|
$(CXX) spdlog-bench-mt.cpp -o spdlog-bench-mt $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
spdlog-async: spdlog-async.cpp
|
||||||
|
$(CXX) spdlog-async.cpp -o spdlog-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
BOOST_FLAGS = -DBOOST_LOG_DYN_LINK -I/home/gabi/devel/boost_1_56_0/ -L/home/gabi/devel/boost_1_56_0/stage/lib -lboost_log -lboost_log_setup -lboost_filesystem -lboost_system -lboost_thread -lboost_regex -lboost_date_time -lboost_chrono
|
||||||
|
|
||||||
|
boost-bench: boost-bench.cpp
|
||||||
|
$(CXX) boost-bench.cpp -o boost-bench $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
boost-bench-mt: boost-bench-mt.cpp
|
||||||
|
$(CXX) boost-bench-mt.cpp -o boost-bench-mt $(CXXFLAGS) $(BOOST_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
GLOG_FLAGS = -lglog
|
||||||
|
glog-bench: glog-bench.cpp
|
||||||
|
$(CXX) glog-bench.cpp -o glog-bench $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
glog-bench-mt: glog-bench-mt.cpp
|
||||||
|
$(CXX) glog-bench-mt.cpp -o glog-bench-mt $(CXXFLAGS) $(GLOG_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
G2LOG_FLAGS = -I/home/gabi/devel/g2log/g2log/src -L/home/gabi/devel/g2log/g2log -llib_g2logger
|
||||||
|
g2log-async: g2log-async.cpp
|
||||||
|
$(CXX) g2log-async.cpp -o g2log-async $(CXXFLAGS) $(G2LOG_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
EASYL_FLAGS = -I../../easylogging/src/
|
||||||
|
easylogging-bench: easylogging-bench.cpp
|
||||||
|
$(CXX) easylogging-bench.cpp -o easylogging-bench $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
easylogging-bench-mt: easylogging-bench-mt.cpp
|
||||||
|
$(CXX) easylogging-bench-mt.cpp -o easylogging-bench-mt $(CXXFLAGS) $(EASYL_FLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o logs/* $(binaries)
|
||||||
|
|
||||||
|
|
||||||
|
rebuild: clean all
|
||||||
|
|
||||||
|
|
||||||
|
|
14
bench/README.md
Normal file
14
bench/README.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# loggers
|
||||||
|
|
||||||
|
Name | License | Lang. | Year | Platform | Compiler | Dependence | URL
|
||||||
|
--- | --- | --- | --- | --- | --- | --- | ---
|
||||||
|
Pantheios | BSD | C++ | 2017 | Windows, Linux, MacOSX | VC++, GCC(3.2+), Intel, Borland, Comeau, Digital Mars, Metrowerks | STLSoft | http://www.pantheios.org/ <br> http://blog.pantheios.org/ <br> https://github.com/synesissoftware/Pantheios <br> http://www.pantheios.org/performance.html
|
||||||
|
Glog | 3-clause BSD| C++| 2017 | Windows, Linux, MacOSX | VC++, GCC, Clang, intel| Google gflags | https://github.com/google/glog
|
||||||
|
G3log | Public Domain | C++11 | 2018 | Windows, Linux, MacOSX, iPhoneOS | VC++, GCC, Clang, MinGW | None | https://github.com/KjellKod/g3log <br> https://github.com/KjellKod/g3sinks <br> https://kjellkod.wordpress.com/2014/08/16/presenting-g3log-the-next-version-of-the-next-generation-of-loggers/ <br> https://kjellkod.wordpress.com/2015/06/30/the-worlds-fastest-logger-vs-g3log/
|
||||||
|
P7 | LGPL | C++, C, C#, Python | 2017 | Windows, Linux | VC++, GCC, Clang, MinGW | None | http://baical.net/p7.html
|
||||||
|
log4cpp | LGPL | C++ | 2017 | Windows, Linux, MacOSX | VC++, GCC, Sun CC, OpenVMS | Boost | http://log4cpp.sourceforge.net/
|
||||||
|
log4cplus | 2-clause BSD or Apache 2 | C++ | 2017 | Windows, Linux, MacOSX, Android | VC++, GCC, Clang | Boost | https://github.com/log4cplus/log4cplus <br> https://sourceforge.net/p/log4cplus/wiki/Home/
|
||||||
|
Easylogging | MIT | C++11 | 2018 | Windows, Linux, MacOSX, iPhoneOS, Android | VC++, GCC, Clang, Intel, MinGW | None | https://github.com/muflihun/easyloggingpp
|
||||||
|
Spdlog | MIT | C++11 | 2018 | Windows, Linux, MacOSX, iPhoneOS, Android(logcat) | VC++, GCC, Clang, MinGW | None, Headers only library | https://github.com/gabime/spdlog <br> https://github.com/fmtlib/fmt
|
||||||
|
Boost.Log v2 | Boost | C++ | 2016 | Windows, Linux, MacOSX, iPhoneOS, Android | VC++, GCC, Clang | Boost | https://github.com/boostorg/log <br> http://www.boost.org/doc/libs/1_66_0/libs/log/doc/html/index.html
|
||||||
|
plog | MPL 2.0 | C++ | 2017 | Windows, Linux, MacOSX, iPhoneOS, Android | gcc, clang, msvc, mingw, mingw-w64, icc, c++builder | None, Headers only library | https://github.com/SergiusTheBest/plog
|
@@ -1,185 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
//
|
|
||||||
// bench.cpp : spdlog benchmarks
|
|
||||||
//
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#include "spdlog/async.h"
|
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
|
||||||
|
|
||||||
#ifdef SPDLOG_FMT_EXTERNAL
|
|
||||||
#include <fmt/locale.h>
|
|
||||||
#else
|
|
||||||
#include "spdlog/fmt/bundled/locale.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "utils.h"
|
|
||||||
#include <atomic>
|
|
||||||
#include <iostream>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
using namespace std::chrono;
|
|
||||||
using namespace spdlog;
|
|
||||||
using namespace spdlog::sinks;
|
|
||||||
using namespace utils;
|
|
||||||
|
|
||||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable : 4996) // disable fopen warning under msvc
|
|
||||||
#endif // _MSC_VER
|
|
||||||
|
|
||||||
int count_lines(const char *filename)
|
|
||||||
{
|
|
||||||
int counter = 0;
|
|
||||||
auto *infile = fopen(filename, "r");
|
|
||||||
int ch;
|
|
||||||
while (EOF != (ch = getc(infile)))
|
|
||||||
{
|
|
||||||
if ('\n' == ch)
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
fclose(infile);
|
|
||||||
|
|
||||||
return counter;
|
|
||||||
}
|
|
||||||
|
|
||||||
void verify_file(const char *filename, int expected_count)
|
|
||||||
{
|
|
||||||
spdlog::info("Verifying {} to contain {} line..", filename, expected_count);
|
|
||||||
auto count = count_lines(filename);
|
|
||||||
if (count != expected_count)
|
|
||||||
{
|
|
||||||
spdlog::error("Test failed. {} has {} lines instead of {}", filename, count, expected_count);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
spdlog::info("Line count OK ({})\n", count);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
|
|
||||||
int howmany = 1000000;
|
|
||||||
int queue_size = std::min(howmany + 2, 8192);
|
|
||||||
int threads = 10;
|
|
||||||
int iters = 3;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
spdlog::set_pattern("[%^%l%$] %v");
|
|
||||||
if (argc == 1)
|
|
||||||
{
|
|
||||||
spdlog::info("Usage: {} <message_count> <threads> <q_size> <iterations>", argv[0]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (argc > 1)
|
|
||||||
howmany = atoi(argv[1]);
|
|
||||||
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]);
|
|
||||||
|
|
||||||
auto slot_size = sizeof(spdlog::details::async_msg);
|
|
||||||
spdlog::info("-------------------------------------------------");
|
|
||||||
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Messages : {:L}", howmany));
|
|
||||||
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Threads : {:L}", threads));
|
|
||||||
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Queue : {:L} slots", queue_size));
|
|
||||||
spdlog::info(fmt::format(
|
|
||||||
std::locale("en_US.UTF-8"), "Queue memory : {:L} x {:L} = {:L} KB ", queue_size, slot_size, (queue_size * slot_size) / 1024));
|
|
||||||
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Total iters : {:L}", iters));
|
|
||||||
spdlog::info("-------------------------------------------------");
|
|
||||||
|
|
||||||
const char *filename = "logs/basic_async.log";
|
|
||||||
spdlog::info("");
|
|
||||||
spdlog::info("*********************************");
|
|
||||||
spdlog::info("Queue Overflow Policy: block");
|
|
||||||
spdlog::info("*********************************");
|
|
||||||
for (int i = 0; i < iters; i++)
|
|
||||||
{
|
|
||||||
auto tp = std::make_shared<details::thread_pool>(queue_size, 1);
|
|
||||||
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
|
|
||||||
auto logger = std::make_shared<async_logger>("async_logger", std::move(file_sink), std::move(tp), async_overflow_policy::block);
|
|
||||||
bench_mt(howmany, std::move(logger), threads);
|
|
||||||
// verify_file(filename, howmany);
|
|
||||||
}
|
|
||||||
|
|
||||||
spdlog::info("");
|
|
||||||
spdlog::info("*********************************");
|
|
||||||
spdlog::info("Queue Overflow Policy: overrun");
|
|
||||||
spdlog::info("*********************************");
|
|
||||||
// do same test but discard oldest if queue is full instead of blocking
|
|
||||||
filename = "logs/basic_async-overrun.log";
|
|
||||||
for (int i = 0; i < iters; i++)
|
|
||||||
{
|
|
||||||
auto tp = std::make_shared<details::thread_pool>(queue_size, 1);
|
|
||||||
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
|
|
||||||
auto logger =
|
|
||||||
std::make_shared<async_logger>("async_logger", std::move(file_sink), std::move(tp), async_overflow_policy::overrun_oldest);
|
|
||||||
bench_mt(howmany, std::move(logger), threads);
|
|
||||||
}
|
|
||||||
spdlog::shutdown();
|
|
||||||
}
|
|
||||||
catch (std::exception &ex)
|
|
||||||
{
|
|
||||||
std::cerr << "Error: " << ex.what() << std::endl;
|
|
||||||
perror("Last error");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void thread_fun(std::shared_ptr<spdlog::logger> logger, int howmany)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < howmany; i++)
|
|
||||||
{
|
|
||||||
logger->info("Hello logger: msg number {}", i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> logger, int thread_count)
|
|
||||||
{
|
|
||||||
using std::chrono::high_resolution_clock;
|
|
||||||
vector<thread> threads;
|
|
||||||
auto start = high_resolution_clock::now();
|
|
||||||
|
|
||||||
int msgs_per_thread = howmany / thread_count;
|
|
||||||
int msgs_per_thread_mod = howmany % thread_count;
|
|
||||||
for (int t = 0; t < thread_count; ++t)
|
|
||||||
{
|
|
||||||
if (t == 0 && msgs_per_thread_mod)
|
|
||||||
threads.push_back(std::thread(thread_fun, logger, msgs_per_thread + msgs_per_thread_mod));
|
|
||||||
else
|
|
||||||
threads.push_back(std::thread(thread_fun, logger, msgs_per_thread));
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &t : threads)
|
|
||||||
{
|
|
||||||
t.join();
|
|
||||||
};
|
|
||||||
|
|
||||||
auto delta = high_resolution_clock::now() - start;
|
|
||||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
|
||||||
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Elapsed: {} secs\t {:L}/sec", delta_d, int(howmany / delta_d)));
|
|
||||||
}
|
|
246
bench/bench.cpp
246
bench/bench.cpp
@@ -1,246 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
//
|
|
||||||
// bench.cpp : spdlog benchmarks
|
|
||||||
//
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
|
||||||
#include "spdlog/sinks/daily_file_sink.h"
|
|
||||||
#include "spdlog/sinks/null_sink.h"
|
|
||||||
#include "spdlog/sinks/rotating_file_sink.h"
|
|
||||||
|
|
||||||
#ifdef SPDLOG_FMT_EXTERNAL
|
|
||||||
#include <fmt/locale.h>
|
|
||||||
#else
|
|
||||||
#include "spdlog/fmt/bundled/locale.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "utils.h"
|
|
||||||
#include <atomic>
|
|
||||||
#include <cstdlib> // EXIT_FAILURE
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
void bench(int howmany, std::shared_ptr<spdlog::logger> log);
|
|
||||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, size_t thread_count);
|
|
||||||
|
|
||||||
// void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log);
|
|
||||||
// void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log);
|
|
||||||
|
|
||||||
static const size_t file_size = 30 * 1024 * 1024;
|
|
||||||
static const size_t rotating_files = 5;
|
|
||||||
static const int max_threads = 1000;
|
|
||||||
|
|
||||||
void bench_threaded_logging(size_t threads, int iters)
|
|
||||||
{
|
|
||||||
spdlog::info("**************************************************************");
|
|
||||||
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Multi threaded: {:L} threads, {:L} messages", threads, iters));
|
|
||||||
spdlog::info("**************************************************************");
|
|
||||||
|
|
||||||
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
|
|
||||||
bench_mt(iters, std::move(basic_mt), threads);
|
|
||||||
auto basic_mt_tracing = spdlog::basic_logger_mt("basic_mt/backtrace-on", "logs/basic_mt.log", true);
|
|
||||||
basic_mt_tracing->enable_backtrace(32);
|
|
||||||
bench_mt(iters, std::move(basic_mt_tracing), threads);
|
|
||||||
|
|
||||||
spdlog::info("");
|
|
||||||
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files);
|
|
||||||
bench_mt(iters, std::move(rotating_mt), threads);
|
|
||||||
auto rotating_mt_tracing = spdlog::rotating_logger_mt("rotating_mt/backtrace-on", "logs/rotating_mt.log", file_size, rotating_files);
|
|
||||||
rotating_mt_tracing->enable_backtrace(32);
|
|
||||||
bench_mt(iters, std::move(rotating_mt_tracing), threads);
|
|
||||||
|
|
||||||
spdlog::info("");
|
|
||||||
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
|
|
||||||
bench_mt(iters, std::move(daily_mt), threads);
|
|
||||||
auto daily_mt_tracing = spdlog::daily_logger_mt("daily_mt/backtrace-on", "logs/daily_mt.log");
|
|
||||||
daily_mt_tracing->enable_backtrace(32);
|
|
||||||
bench_mt(iters, std::move(daily_mt_tracing), threads);
|
|
||||||
|
|
||||||
spdlog::info("");
|
|
||||||
auto empty_logger = std::make_shared<spdlog::logger>("level-off");
|
|
||||||
empty_logger->set_level(spdlog::level::off);
|
|
||||||
bench(iters, empty_logger);
|
|
||||||
auto empty_logger_tracing = std::make_shared<spdlog::logger>("level-off/backtrace-on");
|
|
||||||
empty_logger_tracing->set_level(spdlog::level::off);
|
|
||||||
empty_logger_tracing->enable_backtrace(32);
|
|
||||||
bench(iters, empty_logger_tracing);
|
|
||||||
}
|
|
||||||
|
|
||||||
void bench_single_threaded(int iters)
|
|
||||||
{
|
|
||||||
spdlog::info("**************************************************************");
|
|
||||||
spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Single threaded: {} messages", iters));
|
|
||||||
spdlog::info("**************************************************************");
|
|
||||||
|
|
||||||
auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true);
|
|
||||||
bench(iters, std::move(basic_st));
|
|
||||||
|
|
||||||
auto basic_st_tracing = spdlog::basic_logger_st("basic_st/backtrace-on", "logs/basic_st.log", true);
|
|
||||||
bench(iters, std::move(basic_st_tracing));
|
|
||||||
|
|
||||||
spdlog::info("");
|
|
||||||
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
|
|
||||||
bench(iters, std::move(rotating_st));
|
|
||||||
auto rotating_st_tracing = spdlog::rotating_logger_st("rotating_st/backtrace-on", "logs/rotating_st.log", file_size, rotating_files);
|
|
||||||
rotating_st_tracing->enable_backtrace(32);
|
|
||||||
bench(iters, std::move(rotating_st_tracing));
|
|
||||||
|
|
||||||
spdlog::info("");
|
|
||||||
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
|
|
||||||
bench(iters, std::move(daily_st));
|
|
||||||
auto daily_st_tracing = spdlog::daily_logger_st("daily_st/backtrace-on", "logs/daily_st.log");
|
|
||||||
daily_st_tracing->enable_backtrace(32);
|
|
||||||
bench(iters, std::move(daily_st_tracing));
|
|
||||||
|
|
||||||
spdlog::info("");
|
|
||||||
auto empty_logger = std::make_shared<spdlog::logger>("level-off");
|
|
||||||
empty_logger->set_level(spdlog::level::off);
|
|
||||||
bench(iters, empty_logger);
|
|
||||||
|
|
||||||
auto empty_logger_tracing = std::make_shared<spdlog::logger>("level-off/backtrace-on");
|
|
||||||
empty_logger_tracing->set_level(spdlog::level::off);
|
|
||||||
empty_logger_tracing->enable_backtrace(32);
|
|
||||||
bench(iters, empty_logger_tracing);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
spdlog::set_automatic_registration(false);
|
|
||||||
spdlog::default_logger()->set_pattern("[%^%l%$] %v");
|
|
||||||
int iters = 250000;
|
|
||||||
size_t threads = 4;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
if (argc > 1)
|
|
||||||
{
|
|
||||||
iters = std::stoi(argv[1]);
|
|
||||||
}
|
|
||||||
if (argc > 2)
|
|
||||||
{
|
|
||||||
threads = std::stoul(argv[2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (threads > max_threads)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(fmt::format("Number of threads exceeds maximum({}})", max_threads));
|
|
||||||
}
|
|
||||||
|
|
||||||
bench_single_threaded(iters);
|
|
||||||
bench_threaded_logging(1, iters);
|
|
||||||
bench_threaded_logging(threads, iters);
|
|
||||||
}
|
|
||||||
catch (std::exception &ex)
|
|
||||||
{
|
|
||||||
spdlog::error(ex.what());
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
return EXIT_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
void bench(int howmany, std::shared_ptr<spdlog::logger> log)
|
|
||||||
{
|
|
||||||
using std::chrono::duration;
|
|
||||||
using std::chrono::duration_cast;
|
|
||||||
using std::chrono::high_resolution_clock;
|
|
||||||
|
|
||||||
auto start = high_resolution_clock::now();
|
|
||||||
for (auto i = 0; i < howmany; ++i)
|
|
||||||
{
|
|
||||||
log->info("Hello logger: msg number {}", i);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto delta = high_resolution_clock::now() - start;
|
|
||||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
|
||||||
|
|
||||||
spdlog::info(
|
|
||||||
fmt::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d)));
|
|
||||||
spdlog::drop(log->name());
|
|
||||||
}
|
|
||||||
|
|
||||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, size_t thread_count)
|
|
||||||
{
|
|
||||||
using std::chrono::duration;
|
|
||||||
using std::chrono::duration_cast;
|
|
||||||
using std::chrono::high_resolution_clock;
|
|
||||||
|
|
||||||
std::vector<std::thread> threads;
|
|
||||||
threads.reserve(thread_count);
|
|
||||||
auto start = high_resolution_clock::now();
|
|
||||||
for (size_t t = 0; t < thread_count; ++t)
|
|
||||||
{
|
|
||||||
threads.emplace_back([&]() {
|
|
||||||
for (int j = 0; j < howmany / static_cast<int>(thread_count); j++)
|
|
||||||
{
|
|
||||||
log->info("Hello logger: msg number {}", j);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &t : threads)
|
|
||||||
{
|
|
||||||
t.join();
|
|
||||||
};
|
|
||||||
|
|
||||||
auto delta = high_resolution_clock::now() - start;
|
|
||||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
|
||||||
spdlog::info(
|
|
||||||
fmt::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d)));
|
|
||||||
spdlog::drop(log->name());
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log)
|
|
||||||
{
|
|
||||||
using std::chrono::high_resolution_clock;
|
|
||||||
using std::chrono::duration;
|
|
||||||
using std::chrono::duration_cast;
|
|
||||||
|
|
||||||
auto orig_default = spdlog::default_logger();
|
|
||||||
spdlog::set_default_logger(log);
|
|
||||||
auto start = high_resolution_clock::now();
|
|
||||||
for (auto i = 0; i < howmany; ++i)
|
|
||||||
{
|
|
||||||
spdlog::info("Hello logger: msg number {}", i);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto delta = high_resolution_clock::now() - start;
|
|
||||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
|
||||||
spdlog::drop(log->name());
|
|
||||||
spdlog::set_default_logger(std::move(orig_default));
|
|
||||||
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16}/sec", log->name(), delta_d, int(howmany / delta_d));
|
|
||||||
}
|
|
||||||
|
|
||||||
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
|
|
||||||
{
|
|
||||||
using std::chrono::high_resolution_clock;
|
|
||||||
using std::chrono::duration;
|
|
||||||
using std::chrono::duration_cast;
|
|
||||||
|
|
||||||
const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus "
|
|
||||||
"lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus volutpat mi, eu consequat sem "
|
|
||||||
"libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam non dapibus eros. Donec fringilla dui sed "
|
|
||||||
"augue pretium, nec scelerisque est maximus. Nullam convallis, sem nec blandit maximus, nisi turpis ornare "
|
|
||||||
"nisl, sit amet volutpat neque massa eu odio. Maecenas malesuada quam ex, posuere congue nibh turpis duis.";
|
|
||||||
|
|
||||||
auto orig_default = spdlog::default_logger();
|
|
||||||
spdlog::set_default_logger(log);
|
|
||||||
auto start = high_resolution_clock::now();
|
|
||||||
for (auto i = 0; i < howmany; ++i)
|
|
||||||
{
|
|
||||||
spdlog::log(spdlog::level::info, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto delta = high_resolution_clock::now() - start;
|
|
||||||
auto delta_d = duration_cast<duration<double>>(delta).count();
|
|
||||||
spdlog::drop(log->name());
|
|
||||||
spdlog::set_default_logger(std::move(orig_default));
|
|
||||||
spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16}/sec", log->name(), delta_d, int(howmany / delta_d));
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
86
bench/boost-bench-mt.cpp
Normal file
86
bench/boost-bench-mt.cpp
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/log/core.hpp>
|
||||||
|
#include <boost/log/expressions.hpp>
|
||||||
|
#include <boost/log/sinks/text_file_backend.hpp>
|
||||||
|
#include <boost/log/sources/record_ostream.hpp>
|
||||||
|
#include <boost/log/sources/severity_logger.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||||
|
#include <boost/log/utility/setup/file.hpp>
|
||||||
|
|
||||||
|
namespace logging = boost::log;
|
||||||
|
namespace src = boost::log::sources;
|
||||||
|
namespace sinks = boost::log::sinks;
|
||||||
|
namespace keywords = boost::log::keywords;
|
||||||
|
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
logging::add_file_log(keywords::file_name = "logs/boost-bench-mt_%N.log", /*< file name pattern >*/
|
||||||
|
keywords::auto_flush = false, keywords::format = "[%TimeStamp%]: %Message%");
|
||||||
|
|
||||||
|
logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::info);
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
|
||||||
|
int thread_count = 10;
|
||||||
|
if (argc > 1)
|
||||||
|
thread_count = atoi(argv[1]);
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
init();
|
||||||
|
logging::add_common_attributes();
|
||||||
|
|
||||||
|
using namespace logging::trivial;
|
||||||
|
|
||||||
|
src::severity_logger_mt<severity_level> lg;
|
||||||
|
|
||||||
|
std::atomic<int> msg_counter{0};
|
||||||
|
vector<thread> threads;
|
||||||
|
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int t = 0; t < thread_count; ++t)
|
||||||
|
{
|
||||||
|
threads.push_back(std::thread([&]() {
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int counter = ++msg_counter;
|
||||||
|
if (counter > howmany)
|
||||||
|
break;
|
||||||
|
BOOST_LOG_SEV(lg, info) << "boost message #" << counter << ": This is some text for your pleasure";
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
std::cout << "Total: " << howmany << std::endl;
|
||||||
|
std::cout << "Threads: " << thread_count << std::endl;
|
||||||
|
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
56
bench/boost-bench.cpp
Normal file
56
bench/boost-bench.cpp
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <boost/log/core.hpp>
|
||||||
|
#include <boost/log/expressions.hpp>
|
||||||
|
#include <boost/log/sinks/text_file_backend.hpp>
|
||||||
|
#include <boost/log/sources/record_ostream.hpp>
|
||||||
|
#include <boost/log/sources/severity_logger.hpp>
|
||||||
|
#include <boost/log/trivial.hpp>
|
||||||
|
#include <boost/log/utility/setup/common_attributes.hpp>
|
||||||
|
#include <boost/log/utility/setup/file.hpp>
|
||||||
|
|
||||||
|
namespace logging = boost::log;
|
||||||
|
namespace src = boost::log::sources;
|
||||||
|
namespace sinks = boost::log::sinks;
|
||||||
|
namespace keywords = boost::log::keywords;
|
||||||
|
|
||||||
|
void init()
|
||||||
|
{
|
||||||
|
logging::add_file_log(keywords::file_name = "logs/boost-bench_%N.log", /*< file name pattern >*/
|
||||||
|
keywords::auto_flush = false, keywords::format = "[%TimeStamp%]: %Message%");
|
||||||
|
|
||||||
|
logging::core::get()->set_filter(logging::trivial::severity >= logging::trivial::info);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int, char *[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
init();
|
||||||
|
logging::add_common_attributes();
|
||||||
|
|
||||||
|
using namespace logging::trivial;
|
||||||
|
src::severity_logger_mt<severity_level> lg;
|
||||||
|
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int i = 0; i < howmany; ++i)
|
||||||
|
BOOST_LOG_SEV(lg, info) << "boost message #" << i << ": This is some text for your pleasure";
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
std::cout << "Total: " << howmany << std::endl;
|
||||||
|
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
10
bench/easyl-async.conf
Normal file
10
bench/easyl-async.conf
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
* GLOBAL:
|
||||||
|
FORMAT = "[%datetime]: %levshort %thread %msg"
|
||||||
|
FILENAME = ./logs/easylogging-async.log
|
||||||
|
ENABLED = true
|
||||||
|
TO_FILE = true
|
||||||
|
TO_STANDARD_OUTPUT = false
|
||||||
|
MILLISECONDS_WIDTH = 3
|
||||||
|
PERFORMANCE_TRACKING = false
|
||||||
|
MAX_LOG_FILE_SIZE = 10485760
|
||||||
|
Log_Flush_Threshold = 10485760
|
10
bench/easyl-mt.conf
Normal file
10
bench/easyl-mt.conf
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
* GLOBAL:
|
||||||
|
FORMAT = "[%datetime]: %levshort %thread %msg"
|
||||||
|
FILENAME = ./logs/easylogging-mt.log
|
||||||
|
ENABLED = true
|
||||||
|
TO_FILE = true
|
||||||
|
TO_STANDARD_OUTPUT = false
|
||||||
|
MILLISECONDS_WIDTH = 3
|
||||||
|
PERFORMANCE_TRACKING = false
|
||||||
|
MAX_LOG_FILE_SIZE = 10485760
|
||||||
|
Log_Flush_Threshold = 10485760
|
10
bench/easyl.conf
Normal file
10
bench/easyl.conf
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
* GLOBAL:
|
||||||
|
FORMAT = "[%datetime]: %levshort %msg"
|
||||||
|
FILENAME = ./logs/easylogging.log
|
||||||
|
ENABLED = true
|
||||||
|
TO_FILE = true
|
||||||
|
TO_STANDARD_OUTPUT = false
|
||||||
|
MILLISECONDS_WIDTH = 3
|
||||||
|
PERFORMANCE_TRACKING = false
|
||||||
|
MAX_LOG_FILE_SIZE = 10485760
|
||||||
|
Log_Flush_Threshold = 10485760
|
67
bench/easylogging-bench-async.cpp
Normal file
67
bench/easylogging-bench-async.cpp
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define ELPP_THREAD_SAFE
|
||||||
|
#define ELPP_EXPERIMENTAL_ASYNC
|
||||||
|
#include "easylogging++.cc"
|
||||||
|
#include "easylogging++.h"
|
||||||
|
INITIALIZE_EASYLOGGINGPP
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
|
||||||
|
int thread_count = 10;
|
||||||
|
if (argc > 1)
|
||||||
|
thread_count = atoi(argv[1]);
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
// Load configuration from file
|
||||||
|
el::Configurations conf("easyl-async.conf");
|
||||||
|
el::Loggers::reconfigureLogger("default", conf);
|
||||||
|
|
||||||
|
std::atomic<int> msg_counter{0};
|
||||||
|
vector<thread> threads;
|
||||||
|
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int t = 0; t < thread_count; ++t)
|
||||||
|
{
|
||||||
|
threads.push_back(std::thread([&]() {
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int counter = ++msg_counter;
|
||||||
|
if (counter > howmany)
|
||||||
|
break;
|
||||||
|
LOG(INFO) << "easylog message #" << counter << ": This is some text for your pleasure";
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
std::cout << "Total: " << howmany << std::endl;
|
||||||
|
std::cout << "Threads: " << thread_count << std::endl;
|
||||||
|
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
66
bench/easylogging-bench-mt.cpp
Normal file
66
bench/easylogging-bench-mt.cpp
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#define ELPP_THREAD_SAFE
|
||||||
|
#include "easylogging++.cc"
|
||||||
|
#include "easylogging++.h"
|
||||||
|
INITIALIZE_EASYLOGGINGPP
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
|
||||||
|
int thread_count = 10;
|
||||||
|
if (argc > 1)
|
||||||
|
thread_count = atoi(argv[1]);
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
// Load configuration from file
|
||||||
|
el::Configurations conf("easyl-mt.conf");
|
||||||
|
el::Loggers::reconfigureLogger("default", conf);
|
||||||
|
|
||||||
|
std::atomic<int> msg_counter{0};
|
||||||
|
vector<thread> threads;
|
||||||
|
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int t = 0; t < thread_count; ++t)
|
||||||
|
{
|
||||||
|
threads.push_back(std::thread([&]() {
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int counter = ++msg_counter;
|
||||||
|
if (counter > howmany)
|
||||||
|
break;
|
||||||
|
LOG(INFO) << "easylog message #" << counter << ": This is some text for your pleasure";
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
std::cout << "Total: " << howmany << std::endl;
|
||||||
|
std::cout << "Threads: " << thread_count << std::endl;
|
||||||
|
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
39
bench/easylogging-bench.cpp
Normal file
39
bench/easylogging-bench.cpp
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "easylogging++.cc"
|
||||||
|
#include "easylogging++.h"
|
||||||
|
INITIALIZE_EASYLOGGINGPP
|
||||||
|
|
||||||
|
int main(int, char *[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
// Load configuration from file
|
||||||
|
el::Configurations conf("easyl.conf");
|
||||||
|
el::Loggers::reconfigureLogger("default", conf);
|
||||||
|
|
||||||
|
el::Logger *defaultLogger = el::Loggers::getLogger("default");
|
||||||
|
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int i = 0; i < howmany; ++i)
|
||||||
|
LOG(INFO) << "easylog message #" << i << ": This is some text for your pleasure";
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
std::cout << "Total: " << howmany << std::endl;
|
||||||
|
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@@ -1,80 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2018 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "benchmark/benchmark.h"
|
|
||||||
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#include "spdlog/pattern_formatter.h"
|
|
||||||
|
|
||||||
void bench_formatter(benchmark::State &state, std::string pattern)
|
|
||||||
{
|
|
||||||
auto formatter = spdlog::details::make_unique<spdlog::pattern_formatter>(pattern);
|
|
||||||
spdlog::memory_buf_t dest;
|
|
||||||
std::string logger_name = "logger-name";
|
|
||||||
const char *text = "Hello. This is some message with length of 80 ";
|
|
||||||
|
|
||||||
spdlog::source_loc source_loc{"a/b/c/d/myfile.cpp", 123, "some_func()"};
|
|
||||||
spdlog::details::log_msg msg(source_loc, logger_name, spdlog::level::info, text);
|
|
||||||
|
|
||||||
for (auto _ : state)
|
|
||||||
{
|
|
||||||
dest.clear();
|
|
||||||
formatter->format(msg, dest);
|
|
||||||
benchmark::DoNotOptimize(dest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void bench_formatters()
|
|
||||||
{
|
|
||||||
// basic patterns(single flag)
|
|
||||||
std::string all_flags = "+vtPnlLaAbBcCYDmdHIMSefFprRTXzEisg@luioO%";
|
|
||||||
std::vector<std::string> basic_patterns;
|
|
||||||
for (auto &flag : all_flags)
|
|
||||||
{
|
|
||||||
auto pattern = std::string("%") + flag;
|
|
||||||
benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern);
|
|
||||||
|
|
||||||
// pattern = std::string("%16") + flag;
|
|
||||||
// benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern);
|
|
||||||
//
|
|
||||||
// // bench center padding
|
|
||||||
// pattern = std::string("%=16") + flag;
|
|
||||||
// benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
// complex patterns
|
|
||||||
std::vector<std::string> patterns = {
|
|
||||||
"[%D %X] [%l] [%n] %v",
|
|
||||||
"[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] %v",
|
|
||||||
"[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] [%t] %v",
|
|
||||||
};
|
|
||||||
for (auto &pattern : patterns)
|
|
||||||
{
|
|
||||||
benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern)->Iterations(2500000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
|
|
||||||
spdlog::set_pattern("[%^%l%$] %v");
|
|
||||||
if (argc != 2)
|
|
||||||
{
|
|
||||||
spdlog::error("Usage: {} <pattern> (or \"all\" to bench all)", argv[0]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string pattern = argv[1];
|
|
||||||
if (pattern == "all")
|
|
||||||
{
|
|
||||||
bench_formatters();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern);
|
|
||||||
}
|
|
||||||
benchmark::Initialize(&argc, argv);
|
|
||||||
benchmark::RunSpecifiedBenchmarks();
|
|
||||||
}
|
|
61
bench/g2log-async.cpp
Normal file
61
bench/g2log-async.cpp
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "g2log.h"
|
||||||
|
#include "g2logworker.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
template<typename T>
|
||||||
|
std::string format(const T &value);
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
int thread_count = 10;
|
||||||
|
|
||||||
|
if (argc > 1)
|
||||||
|
thread_count = atoi(argv[1]);
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
g2LogWorker g2log(argv[0], "logs");
|
||||||
|
g2::initializeLogging(&g2log);
|
||||||
|
|
||||||
|
std::atomic<int> msg_counter{0};
|
||||||
|
vector<thread> threads;
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int t = 0; t < thread_count; ++t)
|
||||||
|
{
|
||||||
|
threads.push_back(std::thread([&]() {
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int counter = ++msg_counter;
|
||||||
|
if (counter > howmany)
|
||||||
|
break;
|
||||||
|
LOG(INFO) << "g2log message #" << counter << ": This is some text for your pleasure";
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
cout << "Total: " << howmany << std::endl;
|
||||||
|
cout << "Threads: " << thread_count << std::endl;
|
||||||
|
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||||
|
}
|
63
bench/g3log-async.cpp
Normal file
63
bench/g3log-async.cpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "g3log/g3log.hpp"
|
||||||
|
#include "g3log/logworker.hpp"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
template<typename T>
|
||||||
|
std::string format(const T &value);
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
int thread_count = 10;
|
||||||
|
|
||||||
|
if (argc > 1)
|
||||||
|
thread_count = atoi(argv[1]);
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
auto worker = g3::LogWorker::createLogWorker();
|
||||||
|
auto handle = worker->addDefaultLogger(argv[0], "logs");
|
||||||
|
g3::initializeLogging(worker.get());
|
||||||
|
|
||||||
|
std::atomic<int> msg_counter{0};
|
||||||
|
vector<thread> threads;
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int t = 0; t < thread_count; ++t)
|
||||||
|
{
|
||||||
|
threads.push_back(std::thread([&]() {
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int counter = ++msg_counter;
|
||||||
|
if (counter > howmany)
|
||||||
|
break;
|
||||||
|
LOG(INFO) << "g3log message #" << counter << ": This is some text for your pleasure";
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
cout << "Total: " << howmany << std::endl;
|
||||||
|
cout << "Threads: " << thread_count << std::endl;
|
||||||
|
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||||
|
}
|
63
bench/glog-bench-mt.cpp
Normal file
63
bench/glog-bench-mt.cpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
|
||||||
|
int thread_count = 10;
|
||||||
|
if (argc > 1)
|
||||||
|
thread_count = atoi(argv[1]);
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
FLAGS_logtostderr = 0;
|
||||||
|
FLAGS_log_dir = "logs";
|
||||||
|
google::InitGoogleLogging(argv[0]);
|
||||||
|
|
||||||
|
std::atomic<int> msg_counter{0};
|
||||||
|
vector<thread> threads;
|
||||||
|
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int t = 0; t < thread_count; ++t)
|
||||||
|
{
|
||||||
|
threads.push_back(std::thread([&]() {
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int counter = ++msg_counter;
|
||||||
|
if (counter > howmany)
|
||||||
|
break;
|
||||||
|
LOG(INFO) << "glog message #" << counter << ": This is some text for your pleasure";
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
std::cout << "Total: " << howmany << std::endl;
|
||||||
|
std::cout << "Threads: " << thread_count << std::endl;
|
||||||
|
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
34
bench/glog-bench.cpp
Normal file
34
bench/glog-bench.cpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "glog/logging.h"
|
||||||
|
|
||||||
|
int main(int, char *argv[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
FLAGS_logtostderr = 0;
|
||||||
|
FLAGS_log_dir = "logs";
|
||||||
|
google::InitGoogleLogging(argv[0]);
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int i = 0; i < howmany; ++i)
|
||||||
|
LOG(INFO) << "glog message #" << i << ": This is some text for your pleasure";
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
std::cout << "Total: " << howmany << std::endl;
|
||||||
|
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
@@ -1,177 +0,0 @@
|
|||||||
//
|
|
||||||
// Copyright(c) 2018 Gabi Melman.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
//
|
|
||||||
|
|
||||||
//
|
|
||||||
// latency.cpp : spdlog latency benchmarks
|
|
||||||
//
|
|
||||||
|
|
||||||
#include "benchmark/benchmark.h"
|
|
||||||
|
|
||||||
#include "spdlog/spdlog.h"
|
|
||||||
#include "spdlog/async.h"
|
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
|
||||||
#include "spdlog/sinks/daily_file_sink.h"
|
|
||||||
#include "spdlog/sinks/null_sink.h"
|
|
||||||
#include "spdlog/sinks/rotating_file_sink.h"
|
|
||||||
|
|
||||||
void bench_c_string(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
|
|
||||||
{
|
|
||||||
const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus "
|
|
||||||
"lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus volutpat mi, eu consequat sem "
|
|
||||||
"libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam non dapibus eros. Donec fringilla dui sed "
|
|
||||||
"augue pretium, nec scelerisque est maximus. Nullam convallis, sem nec blandit maximus, nisi turpis ornare "
|
|
||||||
"nisl, sit amet volutpat neque massa eu odio. Maecenas malesuada quam ex, posuere congue nibh turpis duis.";
|
|
||||||
|
|
||||||
for (auto _ : state)
|
|
||||||
{
|
|
||||||
logger->info(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void bench_logger(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
for (auto _ : state)
|
|
||||||
{
|
|
||||||
logger->info("Hello logger: msg number {}...............", ++i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void bench_logger_fmt_string(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
for (auto _ : state)
|
|
||||||
{
|
|
||||||
logger->info(FMT_STRING("Hello logger: msg number {}..............."), ++i);
|
|
||||||
;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void bench_disabled_macro(benchmark::State &state, std::shared_ptr<spdlog::logger> logger)
|
|
||||||
{
|
|
||||||
int i = 0;
|
|
||||||
benchmark::DoNotOptimize(i); // prevent unused warnings
|
|
||||||
benchmark::DoNotOptimize(logger); // prevent unused warnings
|
|
||||||
for (auto _ : state)
|
|
||||||
{
|
|
||||||
SPDLOG_LOGGER_DEBUG(logger, "Hello logger: msg number {}...............", i++);
|
|
||||||
SPDLOG_DEBUG("Hello logger: msg number {}...............", i++);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef __linux__
|
|
||||||
void bench_dev_null()
|
|
||||||
{
|
|
||||||
auto dev_null_st = spdlog::basic_logger_st("/dev/null_st", "/dev/null");
|
|
||||||
benchmark::RegisterBenchmark("/dev/null_st", bench_logger, std::move(dev_null_st))->UseRealTime();
|
|
||||||
spdlog::drop("/dev/null_st");
|
|
||||||
|
|
||||||
auto dev_null_mt = spdlog::basic_logger_mt("/dev/null_mt", "/dev/null");
|
|
||||||
benchmark::RegisterBenchmark("/dev/null_mt", bench_logger, std::move(dev_null_mt))->UseRealTime();
|
|
||||||
spdlog::drop("/dev/null_mt");
|
|
||||||
}
|
|
||||||
#endif // __linux__
|
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
using spdlog::sinks::null_sink_mt;
|
|
||||||
using spdlog::sinks::null_sink_st;
|
|
||||||
|
|
||||||
size_t file_size = 30 * 1024 * 1024;
|
|
||||||
size_t rotating_files = 5;
|
|
||||||
int n_threads = benchmark::CPUInfo::Get().num_cpus;
|
|
||||||
|
|
||||||
auto full_bench = argc > 1 && std::string(argv[1]) == "full";
|
|
||||||
|
|
||||||
// disabled loggers
|
|
||||||
auto disabled_logger = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>());
|
|
||||||
disabled_logger->set_level(spdlog::level::off);
|
|
||||||
benchmark::RegisterBenchmark("disabled-at-compile-time", bench_disabled_macro, disabled_logger);
|
|
||||||
benchmark::RegisterBenchmark("disabled-at-runtime", bench_logger, disabled_logger);
|
|
||||||
// with backtrace of 64
|
|
||||||
auto tracing_disabled_logger = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>());
|
|
||||||
tracing_disabled_logger->enable_backtrace(64);
|
|
||||||
benchmark::RegisterBenchmark("disabled-at-runtime/backtrace", bench_logger, tracing_disabled_logger);
|
|
||||||
|
|
||||||
auto null_logger_st = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_st>());
|
|
||||||
benchmark::RegisterBenchmark("null_sink_st (500_bytes c_str)", bench_c_string, std::move(null_logger_st));
|
|
||||||
benchmark::RegisterBenchmark("null_sink_st", bench_logger, null_logger_st);
|
|
||||||
benchmark::RegisterBenchmark("null_sink_fmt_string", bench_logger_fmt_string, null_logger_st);
|
|
||||||
// with backtrace of 64
|
|
||||||
auto tracing_null_logger_st = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_st>());
|
|
||||||
tracing_null_logger_st->enable_backtrace(64);
|
|
||||||
benchmark::RegisterBenchmark("null_sink_st/backtrace", bench_logger, tracing_null_logger_st);
|
|
||||||
|
|
||||||
#ifdef __linux
|
|
||||||
bench_dev_null();
|
|
||||||
#endif // __linux__
|
|
||||||
|
|
||||||
if (full_bench)
|
|
||||||
{
|
|
||||||
// basic_st
|
|
||||||
auto basic_st = spdlog::basic_logger_st("basic_st", "latency_logs/basic_st.log", true);
|
|
||||||
benchmark::RegisterBenchmark("basic_st", bench_logger, std::move(basic_st))->UseRealTime();
|
|
||||||
spdlog::drop("basic_st");
|
|
||||||
// with backtrace of 64
|
|
||||||
auto tracing_basic_st = spdlog::basic_logger_st("tracing_basic_st", "latency_logs/tracing_basic_st.log", true);
|
|
||||||
tracing_basic_st->enable_backtrace(64);
|
|
||||||
benchmark::RegisterBenchmark("basic_st/backtrace", bench_logger, std::move(tracing_basic_st))->UseRealTime();
|
|
||||||
spdlog::drop("tracing_basic_st");
|
|
||||||
|
|
||||||
// rotating st
|
|
||||||
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "latency_logs/rotating_st.log", file_size, rotating_files);
|
|
||||||
benchmark::RegisterBenchmark("rotating_st", bench_logger, std::move(rotating_st))->UseRealTime();
|
|
||||||
spdlog::drop("rotating_st");
|
|
||||||
// with backtrace of 64
|
|
||||||
auto tracing_rotating_st =
|
|
||||||
spdlog::rotating_logger_st("tracing_rotating_st", "latency_logs/tracing_rotating_st.log", file_size, rotating_files);
|
|
||||||
benchmark::RegisterBenchmark("rotating_st/backtrace", bench_logger, std::move(tracing_rotating_st))->UseRealTime();
|
|
||||||
spdlog::drop("tracing_rotating_st");
|
|
||||||
|
|
||||||
// daily st
|
|
||||||
auto daily_st = spdlog::daily_logger_mt("daily_st", "latency_logs/daily_st.log");
|
|
||||||
benchmark::RegisterBenchmark("daily_st", bench_logger, std::move(daily_st))->UseRealTime();
|
|
||||||
spdlog::drop("daily_st");
|
|
||||||
auto tracing_daily_st = spdlog::daily_logger_mt("tracing_daily_st", "latency_logs/daily_st.log");
|
|
||||||
benchmark::RegisterBenchmark("daily_st/backtrace", bench_logger, std::move(tracing_daily_st))->UseRealTime();
|
|
||||||
spdlog::drop("tracing_daily_st");
|
|
||||||
|
|
||||||
//
|
|
||||||
// Multi threaded bench, 10 loggers using same logger concurrently
|
|
||||||
//
|
|
||||||
auto null_logger_mt = std::make_shared<spdlog::logger>("bench", std::make_shared<null_sink_mt>());
|
|
||||||
benchmark::RegisterBenchmark("null_sink_mt", bench_logger, null_logger_mt)->Threads(n_threads)->UseRealTime();
|
|
||||||
|
|
||||||
// basic_mt
|
|
||||||
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "latency_logs/basic_mt.log", true);
|
|
||||||
benchmark::RegisterBenchmark("basic_mt", bench_logger, std::move(basic_mt))->Threads(n_threads)->UseRealTime();
|
|
||||||
spdlog::drop("basic_mt");
|
|
||||||
|
|
||||||
// rotating mt
|
|
||||||
auto rotating_mt = spdlog::rotating_logger_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();
|
|
||||||
spdlog::drop("rotating_mt");
|
|
||||||
|
|
||||||
// daily mt
|
|
||||||
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "latency_logs/daily_mt.log");
|
|
||||||
benchmark::RegisterBenchmark("daily_mt", bench_logger, std::move(daily_mt))->Threads(n_threads)->UseRealTime();
|
|
||||||
spdlog::drop("daily_mt");
|
|
||||||
}
|
|
||||||
|
|
||||||
// async
|
|
||||||
auto queue_size = 1024 * 1024 * 3;
|
|
||||||
auto tp = std::make_shared<spdlog::details::thread_pool>(queue_size, 1);
|
|
||||||
auto async_logger = std::make_shared<spdlog::async_logger>(
|
|
||||||
"async_logger", std::make_shared<null_sink_mt>(), std::move(tp), spdlog::async_overflow_policy::overrun_oldest);
|
|
||||||
benchmark::RegisterBenchmark("async_logger", bench_logger, async_logger)->Threads(n_threads)->UseRealTime();
|
|
||||||
|
|
||||||
auto async_logger_tracing = std::make_shared<spdlog::async_logger>(
|
|
||||||
"async_logger_tracing", std::make_shared<null_sink_mt>(), std::move(tp), spdlog::async_overflow_policy::overrun_oldest);
|
|
||||||
async_logger_tracing->enable_backtrace(32);
|
|
||||||
benchmark::RegisterBenchmark("async_logger/tracing", bench_logger, async_logger_tracing)->Threads(n_threads)->UseRealTime();
|
|
||||||
|
|
||||||
benchmark::Initialize(&argc, argv);
|
|
||||||
benchmark::RunSpecifiedBenchmarks();
|
|
||||||
}
|
|
32
bench/latency/Makefile
Normal file
32
bench/latency/Makefile
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
CXX ?= g++
|
||||||
|
CXXFLAGS = -march=native -Wall -std=c++11 -pthread
|
||||||
|
CXX_RELEASE_FLAGS = -O2 -DNDEBUG
|
||||||
|
|
||||||
|
|
||||||
|
binaries=spdlog-latency g3log-latency g3log-crush
|
||||||
|
|
||||||
|
all: $(binaries)
|
||||||
|
|
||||||
|
spdlog-latency: spdlog-latency.cpp
|
||||||
|
$(CXX) spdlog-latency.cpp -o spdlog-latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -I../../include
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
g3log-latency: g3log-latency.cpp
|
||||||
|
$(CXX) g3log-latency.cpp -o g3log-latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -I../../../g3log/src -L. -lg3logger
|
||||||
|
|
||||||
|
|
||||||
|
g3log-crush: g3log-crush.cpp
|
||||||
|
$(CXX) g3log-crush.cpp -o g3log-crush $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -I../../../g3log/src -L. -lg3logger
|
||||||
|
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *.log $(binaries)
|
||||||
|
|
||||||
|
|
||||||
|
rebuild: clean all
|
||||||
|
|
||||||
|
|
||||||
|
|
13
bench/latency/compare.sh
Executable file
13
bench/latency/compare.sh
Executable file
@@ -0,0 +1,13 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "running spdlog and g3log tests 10 time with ${1:-10} threads each (total 1,000,000 entries).."
|
||||||
|
rm -f *.log
|
||||||
|
for i in {1..10}
|
||||||
|
|
||||||
|
do
|
||||||
|
echo
|
||||||
|
sleep 0.5
|
||||||
|
./spdlog-latency ${1:-10} 2>/dev/null || exit
|
||||||
|
sleep 0.5
|
||||||
|
./g3log-latency ${1:-10} 2>/dev/null || exit
|
||||||
|
|
||||||
|
done
|
34
bench/latency/g3log-crush.cpp
Normal file
34
bench/latency/g3log-crush.cpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <g3log/g3log.hpp>
|
||||||
|
#include <g3log/logworker.hpp>
|
||||||
|
|
||||||
|
void CrusherLoop()
|
||||||
|
{
|
||||||
|
size_t counter = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
LOGF(INFO, "Some text to crush you machine. thread:");
|
||||||
|
if (++counter % 1000000 == 0)
|
||||||
|
{
|
||||||
|
std::cout << "Wrote " << counter << " entries" << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
std::cout << "WARNING: This test will exaust all your machine memory and will crush it!" << std::endl;
|
||||||
|
std::cout << "Are you sure you want to continue ? " << std::endl;
|
||||||
|
char c;
|
||||||
|
std::cin >> c;
|
||||||
|
if (toupper(c) != 'Y')
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
auto worker = g3::LogWorker::createLogWorker();
|
||||||
|
auto handle = worker->addDefaultLogger(argv[0], "g3log.txt");
|
||||||
|
g3::initializeLogging(worker.get());
|
||||||
|
CrusherLoop();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
118
bench/latency/g3log-latency.cpp
Normal file
118
bench/latency/g3log-latency.cpp
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
#include "utils.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <fstream>
|
||||||
|
#include <functional>
|
||||||
|
#include <g3log/g3log.hpp>
|
||||||
|
#include <g3log/logworker.hpp>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <numeric>
|
||||||
|
#include <sstream>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const uint64_t g_iterations = 1000000;
|
||||||
|
|
||||||
|
std::atomic<size_t> g_counter = {0};
|
||||||
|
|
||||||
|
void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t> &result)
|
||||||
|
{
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
const size_t value_now = ++g_counter;
|
||||||
|
if (value_now > g_iterations)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto start_time = std::chrono::high_resolution_clock::now();
|
||||||
|
LOGF(INFO, "Some text to log for thread: %ld", id);
|
||||||
|
auto stop_time = std::chrono::high_resolution_clock::now();
|
||||||
|
uint64_t time_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time - start_time).count();
|
||||||
|
result.push_back(time_us);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintResults(const std::map<size_t, std::vector<uint64_t>> &threads_result, size_t total_us)
|
||||||
|
{
|
||||||
|
|
||||||
|
std::vector<uint64_t> all_measurements;
|
||||||
|
all_measurements.reserve(g_iterations);
|
||||||
|
for (auto &t_result : threads_result)
|
||||||
|
{
|
||||||
|
all_measurements.insert(all_measurements.end(), t_result.second.begin(), t_result.second.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// calc worst latenct
|
||||||
|
auto worst = *std::max_element(all_measurements.begin(), all_measurements.end());
|
||||||
|
|
||||||
|
// calc avg
|
||||||
|
auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus<uint64_t>());
|
||||||
|
auto avg = double(total) / all_measurements.size();
|
||||||
|
|
||||||
|
std::cout << "[g3log] worst: " << std::setw(10) << std::right << worst << "\tAvg: " << avg << "\tTotal: " << utils::format(total_us)
|
||||||
|
<< " us" << std::endl;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// The purpose of this test is NOT to see how fast
|
||||||
|
// each thread can possibly write. It is to see what
|
||||||
|
// the worst latency is for writing a log entry
|
||||||
|
//
|
||||||
|
// In the test 1 million log entries will be written
|
||||||
|
// an atomic counter is used to give each thread what
|
||||||
|
// it is to write next. The overhead of atomic
|
||||||
|
// synchronization between the threads are not counted in the worst case latency
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
size_t number_of_threads{0};
|
||||||
|
if (argc == 2)
|
||||||
|
{
|
||||||
|
number_of_threads = atoi(argv[1]);
|
||||||
|
}
|
||||||
|
if (argc != 2 || number_of_threads == 0)
|
||||||
|
{
|
||||||
|
std::cerr << "USAGE is: " << argv[0] << " number_threads" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::thread> threads(number_of_threads);
|
||||||
|
std::map<size_t, std::vector<uint64_t>> threads_result;
|
||||||
|
|
||||||
|
for (size_t idx = 0; idx < number_of_threads; ++idx)
|
||||||
|
{
|
||||||
|
// reserve to 1 million for all the result
|
||||||
|
// it's a test so let's not care about the wasted space
|
||||||
|
threads_result[idx].reserve(g_iterations);
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string g_path = "./";
|
||||||
|
const std::string g_prefix_log_name = "g3log-performance-";
|
||||||
|
const std::string g_measurement_dump = g_path + g_prefix_log_name + "_RESULT.txt";
|
||||||
|
|
||||||
|
auto worker = g3::LogWorker::createLogWorker();
|
||||||
|
auto handle = worker->addDefaultLogger(argv[0], "g3log.txt");
|
||||||
|
g3::initializeLogging(worker.get());
|
||||||
|
|
||||||
|
auto start_time_application_total = std::chrono::high_resolution_clock::now();
|
||||||
|
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
|
||||||
|
{
|
||||||
|
threads[idx] = std::thread(MeasurePeakDuringLogWrites, idx, std::ref(threads_result[idx]));
|
||||||
|
}
|
||||||
|
for (size_t idx = 0; idx < number_of_threads; ++idx)
|
||||||
|
{
|
||||||
|
threads[idx].join();
|
||||||
|
}
|
||||||
|
auto stop_time_application_total = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
uint64_t total_time_in_us =
|
||||||
|
std::chrono::duration_cast<std::chrono::microseconds>(stop_time_application_total - start_time_application_total).count();
|
||||||
|
PrintResults(threads_result, total_time_in_us);
|
||||||
|
return 0;
|
||||||
|
}
|
119
bench/latency/spdlog-latency.cpp
Normal file
119
bench/latency/spdlog-latency.cpp
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
|
||||||
|
#include "utils.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
#include <numeric>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
namespace spd = spdlog;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
const uint64_t g_iterations = 1000000;
|
||||||
|
|
||||||
|
std::atomic<size_t> g_counter = {0};
|
||||||
|
|
||||||
|
void MeasurePeakDuringLogWrites(const size_t id, std::vector<uint64_t> &result)
|
||||||
|
{
|
||||||
|
auto logger = spd::get("file_logger");
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
const size_t value_now = ++g_counter;
|
||||||
|
if (value_now > g_iterations)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto start_time = std::chrono::high_resolution_clock::now();
|
||||||
|
logger->info("Some text to log for thread: [somemore text...............................] {}", id);
|
||||||
|
auto stop_time = std::chrono::high_resolution_clock::now();
|
||||||
|
uint64_t time_us = std::chrono::duration_cast<std::chrono::microseconds>(stop_time - start_time).count();
|
||||||
|
result.push_back(time_us);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PrintResults(const std::map<size_t, std::vector<uint64_t>> &threads_result, size_t total_us)
|
||||||
|
{
|
||||||
|
|
||||||
|
std::vector<uint64_t> all_measurements;
|
||||||
|
all_measurements.reserve(g_iterations);
|
||||||
|
for (auto &t_result : threads_result)
|
||||||
|
{
|
||||||
|
all_measurements.insert(all_measurements.end(), t_result.second.begin(), t_result.second.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// calc worst latenct
|
||||||
|
auto worst = *std::max_element(all_measurements.begin(), all_measurements.end());
|
||||||
|
|
||||||
|
// calc avg
|
||||||
|
auto total = accumulate(begin(all_measurements), end(all_measurements), 0, std::plus<uint64_t>());
|
||||||
|
auto avg = double(total) / all_measurements.size();
|
||||||
|
|
||||||
|
std::cout << "[spdlog] worst: " << std::setw(10) << std::right << worst << "\tAvg: " << avg << "\tTotal: " << utils::format(total_us)
|
||||||
|
<< " us" << std::endl;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// The purpose of this test is NOT to see how fast
|
||||||
|
// each thread can possibly write. It is to see what
|
||||||
|
// the worst latency is for writing a log entry
|
||||||
|
//
|
||||||
|
// In the test 1 million log entries will be written
|
||||||
|
// an atomic counter is used to give each thread what
|
||||||
|
// it is to write next. The overhead of atomic
|
||||||
|
// synchronization between the threads are not counted in the worst case latency
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
size_t number_of_threads{0};
|
||||||
|
if (argc == 2)
|
||||||
|
{
|
||||||
|
number_of_threads = atoi(argv[1]);
|
||||||
|
}
|
||||||
|
if (argc != 2 || number_of_threads == 0)
|
||||||
|
{
|
||||||
|
std::cerr << "usage: " << argv[0] << " number_threads" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::thread> threads(number_of_threads);
|
||||||
|
std::map<size_t, std::vector<uint64_t>> threads_result;
|
||||||
|
|
||||||
|
for (size_t idx = 0; idx < number_of_threads; ++idx)
|
||||||
|
{
|
||||||
|
// reserve to 1 million for all the result
|
||||||
|
// it's a test so let's not care about the wasted space
|
||||||
|
threads_result[idx].reserve(g_iterations);
|
||||||
|
}
|
||||||
|
|
||||||
|
int queue_size = 1048576; // 2 ^ 20
|
||||||
|
spdlog::set_async_mode(queue_size);
|
||||||
|
auto logger = spdlog::create<spd::sinks::simple_file_sink_mt>("file_logger", "spdlog.log", true);
|
||||||
|
|
||||||
|
// force flush on every call to compare with g3log
|
||||||
|
auto s = (spd::sinks::simple_file_sink_mt *)logger->sinks()[0].get();
|
||||||
|
s->set_force_flush(true);
|
||||||
|
|
||||||
|
auto start_time_application_total = std::chrono::high_resolution_clock::now();
|
||||||
|
for (uint64_t idx = 0; idx < number_of_threads; ++idx)
|
||||||
|
{
|
||||||
|
threads[idx] = std::thread(MeasurePeakDuringLogWrites, idx, std::ref(threads_result[idx]));
|
||||||
|
}
|
||||||
|
for (size_t idx = 0; idx < number_of_threads; ++idx)
|
||||||
|
{
|
||||||
|
threads[idx].join();
|
||||||
|
}
|
||||||
|
auto stop_time_application_total = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
uint64_t total_time_in_us =
|
||||||
|
std::chrono::duration_cast<std::chrono::microseconds>(stop_time_application_total - start_time_application_total).count();
|
||||||
|
|
||||||
|
PrintResults(threads_result, total_time_in_us);
|
||||||
|
return 0;
|
||||||
|
}
|
34
bench/latency/utils.h
Normal file
34
bench/latency/utils.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
#include <locale>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace utils {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline std::string format(const T &value)
|
||||||
|
{
|
||||||
|
static std::locale loc("");
|
||||||
|
std::stringstream ss;
|
||||||
|
ss.imbue(loc);
|
||||||
|
ss << value;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline std::string format(const double &value)
|
||||||
|
{
|
||||||
|
static std::locale loc("");
|
||||||
|
std::stringstream ss;
|
||||||
|
ss.imbue(loc);
|
||||||
|
ss << std::fixed << std::setprecision(1) << value;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace utils
|
78
bench/log4cplus-bench-mt.cpp
Normal file
78
bench/log4cplus-bench-mt.cpp
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "log4cplus/fileappender.h"
|
||||||
|
#include "log4cplus/helpers/loglog.h"
|
||||||
|
#include "log4cplus/helpers/property.h"
|
||||||
|
#include "log4cplus/layout.h"
|
||||||
|
#include "log4cplus/logger.h"
|
||||||
|
#include "log4cplus/loggingmacros.h"
|
||||||
|
#include "log4cplus/ndc.h"
|
||||||
|
|
||||||
|
using namespace log4cplus;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
|
||||||
|
int thread_count = 10;
|
||||||
|
if (argc > 1)
|
||||||
|
thread_count = std::atoi(argv[1]);
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
log4cplus::initialize();
|
||||||
|
SharedFileAppenderPtr append(new FileAppender(LOG4CPLUS_TEXT("logs/log4cplus-bench-mt.log"), std::ios_base::trunc, true, true));
|
||||||
|
append->setName(LOG4CPLUS_TEXT("File"));
|
||||||
|
|
||||||
|
log4cplus::tstring pattern = LOG4CPLUS_TEXT("%d{%Y-%m-%d %H:%M:%S.%Q}: %p - %m %n");
|
||||||
|
append->setLayout(std::auto_ptr<Layout>(new PatternLayout(pattern)));
|
||||||
|
append->getloc();
|
||||||
|
Logger::getRoot().addAppender(SharedAppenderPtr(append.get()));
|
||||||
|
|
||||||
|
Logger root = Logger::getRoot();
|
||||||
|
|
||||||
|
std::atomic<int> msg_counter{0};
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int t = 0; t < thread_count; ++t)
|
||||||
|
{
|
||||||
|
threads.push_back(std::thread([&]() {
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int counter = ++msg_counter;
|
||||||
|
if (counter > howmany)
|
||||||
|
break;
|
||||||
|
LOG4CPLUS_INFO(root, "log4cplus message #" << counter << ": This is some text for your pleasure");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
std::cout << "Total: " << howmany << std::endl;
|
||||||
|
std::cout << "Threads: " << thread_count << std::endl;
|
||||||
|
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||||
|
|
||||||
|
log4cplus::Logger::shutdown();
|
||||||
|
return 0;
|
||||||
|
}
|
52
bench/log4cplus-bench.cpp
Normal file
52
bench/log4cplus-bench.cpp
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "log4cplus/fileappender.h"
|
||||||
|
#include "log4cplus/helpers/loglog.h"
|
||||||
|
#include "log4cplus/helpers/property.h"
|
||||||
|
#include "log4cplus/layout.h"
|
||||||
|
#include "log4cplus/logger.h"
|
||||||
|
#include "log4cplus/loggingmacros.h"
|
||||||
|
#include "log4cplus/ndc.h"
|
||||||
|
|
||||||
|
using namespace log4cplus;
|
||||||
|
|
||||||
|
int main(int, char *[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
log4cplus::initialize();
|
||||||
|
SharedFileAppenderPtr append(new FileAppender(LOG4CPLUS_TEXT("logs/log4cplus-bench.log"), std::ios_base::trunc, true, true));
|
||||||
|
append->setName(LOG4CPLUS_TEXT("File"));
|
||||||
|
|
||||||
|
log4cplus::tstring pattern = LOG4CPLUS_TEXT("%d{%Y-%m-%d %H:%M:%S.%Q}: %p - %m %n");
|
||||||
|
append->setLayout(std::auto_ptr<Layout>(new PatternLayout(pattern)));
|
||||||
|
append->getloc();
|
||||||
|
Logger::getRoot().addAppender(SharedAppenderPtr(append.get()));
|
||||||
|
|
||||||
|
Logger root = Logger::getRoot();
|
||||||
|
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int i = 0; i < howmany; ++i)
|
||||||
|
LOG4CPLUS_INFO(root, "log4cplus message #" << i << ": This is some text for your pleasure");
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
std::cout << "Total: " << howmany << std::endl;
|
||||||
|
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||||
|
|
||||||
|
log4cplus::Logger::shutdown();
|
||||||
|
return 0;
|
||||||
|
}
|
74
bench/log4cpp-bench-mt.cpp
Normal file
74
bench/log4cpp-bench-mt.cpp
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "log4cpp/Appender.hh"
|
||||||
|
#include "log4cpp/BasicLayout.hh"
|
||||||
|
#include "log4cpp/Category.hh"
|
||||||
|
#include "log4cpp/FileAppender.hh"
|
||||||
|
#include "log4cpp/Layout.hh"
|
||||||
|
#include "log4cpp/PatternLayout.hh"
|
||||||
|
#include "log4cpp/Priority.hh"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
|
||||||
|
int thread_count = 10;
|
||||||
|
if (argc > 1)
|
||||||
|
thread_count = std::atoi(argv[1]);
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
log4cpp::Appender *appender = new log4cpp::FileAppender("default", "logs/log4cpp-bench-mt.log");
|
||||||
|
log4cpp::PatternLayout *layout = new log4cpp::PatternLayout();
|
||||||
|
layout->setConversionPattern("%d{%Y-%m-%d %H:%M:%S.%l}: %p - %m %n");
|
||||||
|
appender->setLayout(layout);
|
||||||
|
|
||||||
|
log4cpp::Category &root = log4cpp::Category::getRoot();
|
||||||
|
root.addAppender(appender);
|
||||||
|
root.setPriority(log4cpp::Priority::INFO);
|
||||||
|
|
||||||
|
std::atomic<int> msg_counter{0};
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int t = 0; t < thread_count; ++t)
|
||||||
|
{
|
||||||
|
threads.push_back(std::thread([&]() {
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int counter = ++msg_counter;
|
||||||
|
if (counter > howmany)
|
||||||
|
break;
|
||||||
|
root << log4cpp::Priority::INFO << "log4cpp message #" << counter << ": This is some text for your pleasure";
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
std::cout << "Total: " << howmany << std::endl;
|
||||||
|
std::cout << "Threads: " << thread_count << std::endl;
|
||||||
|
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||||
|
|
||||||
|
root.shutdown();
|
||||||
|
return 0;
|
||||||
|
}
|
48
bench/log4cpp-bench.cpp
Normal file
48
bench/log4cpp-bench.cpp
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "log4cpp/Appender.hh"
|
||||||
|
#include "log4cpp/BasicLayout.hh"
|
||||||
|
#include "log4cpp/Category.hh"
|
||||||
|
#include "log4cpp/FileAppender.hh"
|
||||||
|
#include "log4cpp/Layout.hh"
|
||||||
|
#include "log4cpp/PatternLayout.hh"
|
||||||
|
#include "log4cpp/Priority.hh"
|
||||||
|
|
||||||
|
int main(int, char *[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
log4cpp::Appender *appender = new log4cpp::FileAppender("default", "logs/log4cpp-bench.log");
|
||||||
|
log4cpp::PatternLayout *layout = new log4cpp::PatternLayout();
|
||||||
|
layout->setConversionPattern("%d{%Y-%m-%d %H:%M:%S.%l}: %p - %m %n");
|
||||||
|
appender->setLayout(layout);
|
||||||
|
|
||||||
|
log4cpp::Category &root = log4cpp::Category::getRoot();
|
||||||
|
root.addAppender(appender);
|
||||||
|
root.setPriority(log4cpp::Priority::INFO);
|
||||||
|
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int i = 0; i < howmany; ++i)
|
||||||
|
root << log4cpp::Priority::INFO << "log4cpp message #" << i << ": This is some text for your pleasure";
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
std::cout << "Total: " << howmany << std::endl;
|
||||||
|
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||||
|
|
||||||
|
root.shutdown();
|
||||||
|
return 0;
|
||||||
|
}
|
4
bench/logs/.gitignore
vendored
Normal file
4
bench/logs/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Ignore everything in this directory
|
||||||
|
*
|
||||||
|
# Except this file
|
||||||
|
!.gitignore
|
19
bench/mem
Executable file
19
bench/mem
Executable file
@@ -0,0 +1,19 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
if [ $# -lt 1 ]; then
|
||||||
|
echo "usage: $0 <program>"
|
||||||
|
fi
|
||||||
|
|
||||||
|
PROG=$1
|
||||||
|
|
||||||
|
if [ ! -x "$PROG" ]; then
|
||||||
|
echo $PROG not found or not executable.
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
$* &
|
||||||
|
PID=$!
|
||||||
|
|
||||||
|
while `kill -0 $PID 2>/dev/null`; do
|
||||||
|
ps -eo size,pid,user,pcpu,command --sort -size | awk '{ line=1 ; hr=$1/1024 ; printf("%13.2f Mb ",hr); } { for ( x=4 ; x<=NF ; x++ ) { printf("%s ",$x) } print "" }' | grep -v grep | grep -v $0 | grep $PROG
|
||||||
|
done
|
94
bench/p7-bench-mt.cpp
Normal file
94
bench/p7-bench-mt.cpp
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "P7_Trace.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
|
||||||
|
int thread_count = 10;
|
||||||
|
if (argc > 1)
|
||||||
|
thread_count = std::atoi(argv[1]);
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
IP7_Trace::hModule module = NULL;
|
||||||
|
|
||||||
|
// create P7 client object
|
||||||
|
std::unique_ptr<IP7_Client, std::function<void(IP7_Client *)>> client(
|
||||||
|
P7_Create_Client(TM("/P7.Pool=1024 /P7.Sink=FileTxt /P7.Dir=logs/p7-bench-mt")), [&](IP7_Client *ptr) {
|
||||||
|
if (ptr)
|
||||||
|
ptr->Release();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!client)
|
||||||
|
{
|
||||||
|
std::cout << "Can't create IP7_Client" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create P7 trace object 1
|
||||||
|
std::unique_ptr<IP7_Trace, std::function<void(IP7_Trace *)>> trace(
|
||||||
|
P7_Create_Trace(client.get(), TM("Trace channel 1")), [&](IP7_Trace *ptr) {
|
||||||
|
if (ptr)
|
||||||
|
ptr->Release();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!trace)
|
||||||
|
{
|
||||||
|
std::cout << "Can't create IP7_Trace" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace->Register_Thread(TM("Application"), 0);
|
||||||
|
trace->Register_Module(TM("Main"), &module);
|
||||||
|
|
||||||
|
std::atomic<int> msg_counter{0};
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int t = 0; t < thread_count; ++t)
|
||||||
|
{
|
||||||
|
threads.push_back(std::thread([&]() {
|
||||||
|
trace->Register_Thread(TM("Application"), t + 1);
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int counter = ++msg_counter;
|
||||||
|
if (counter > howmany)
|
||||||
|
break;
|
||||||
|
trace->P7_INFO(module, TM("p7 message #%d: This is some text for your pleasure"), counter);
|
||||||
|
}
|
||||||
|
trace->Register_Thread(TM("Application"), t + 1);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
std::cout << "Total: " << howmany << std::endl;
|
||||||
|
std::cout << "Threads: " << thread_count << std::endl;
|
||||||
|
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||||
|
|
||||||
|
trace->Unregister_Thread(0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
66
bench/p7-bench.cpp
Normal file
66
bench/p7-bench.cpp
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "P7_Trace.h"
|
||||||
|
|
||||||
|
int main(int, char *[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
IP7_Trace::hModule module = NULL;
|
||||||
|
|
||||||
|
// create P7 client object
|
||||||
|
std::unique_ptr<IP7_Client, std::function<void(IP7_Client *)>> client(
|
||||||
|
P7_Create_Client(TM("/P7.Pool=1024 /P7.Sink=FileTxt /P7.Dir=logs/p7-bench")), [&](IP7_Client *ptr) {
|
||||||
|
if (ptr)
|
||||||
|
ptr->Release();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!client)
|
||||||
|
{
|
||||||
|
std::cout << "Can't create IP7_Client" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create P7 trace object 1
|
||||||
|
std::unique_ptr<IP7_Trace, std::function<void(IP7_Trace *)>> trace(
|
||||||
|
P7_Create_Trace(client.get(), TM("Trace channel 1")), [&](IP7_Trace *ptr) {
|
||||||
|
if (ptr)
|
||||||
|
ptr->Release();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!trace)
|
||||||
|
{
|
||||||
|
std::cout << "Can't create IP7_Trace" << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace->Register_Thread(TM("Application"), 0);
|
||||||
|
trace->Register_Module(TM("Main"), &module);
|
||||||
|
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int i = 0; i < howmany; ++i)
|
||||||
|
trace->P7_INFO(module, TM("p7 message #%d: This is some text for your pleasure"), i);
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
std::cout << "Total: " << howmany << std::endl;
|
||||||
|
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||||
|
|
||||||
|
trace->Unregister_Thread(0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
61
bench/plog-bench-mt.cpp
Normal file
61
bench/plog-bench-mt.cpp
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "plog/Log.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
|
||||||
|
int thread_count = 10;
|
||||||
|
if (argc > 1)
|
||||||
|
thread_count = atoi(argv[1]);
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
plog::init(plog::debug, "logs/plog-bench-mt.log");
|
||||||
|
|
||||||
|
std::atomic<int> msg_counter{0};
|
||||||
|
vector<thread> threads;
|
||||||
|
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int t = 0; t < thread_count; ++t)
|
||||||
|
{
|
||||||
|
threads.push_back(std::thread([&]() {
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int counter = ++msg_counter;
|
||||||
|
if (counter > howmany)
|
||||||
|
break;
|
||||||
|
LOG_INFO << "plog message #" << counter << ": This is some text for your pleasure";
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
std::cout << "Total: " << howmany << std::endl;
|
||||||
|
std::cout << "Threads: " << thread_count << std::endl;
|
||||||
|
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
34
bench/plog-bench.cpp
Normal file
34
bench/plog-bench.cpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
#include "plog/Log.h"
|
||||||
|
|
||||||
|
int main(int, char *[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
plog::init(plog::debug, "logs/plog-bench.log");
|
||||||
|
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int i = 0; i < howmany; ++i)
|
||||||
|
LOG_INFO << "plog message #" << i << ": This is some text for your pleasure";
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
std::cout << "Total: " << howmany << std::endl;
|
||||||
|
std::cout << "Delta = " << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << rate << "/sec" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
76
bench/spdlog-async.cpp
Normal file
76
bench/spdlog-async.cpp
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
|
||||||
|
int thread_count = 10;
|
||||||
|
if (argc > 1)
|
||||||
|
thread_count = std::atoi(argv[1]);
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
spdlog::set_async_mode(1000000);
|
||||||
|
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("file_logger", "logs/spdlog-bench-async.log", true);
|
||||||
|
logger->set_pattern("[%Y-%m-%d %T.%F]: %L %t %v");
|
||||||
|
|
||||||
|
std::cout << "To stop, press <Enter>" << std::endl;
|
||||||
|
std::atomic<bool> run{true};
|
||||||
|
std::thread stoper(std::thread([&run]() {
|
||||||
|
std::cin.get();
|
||||||
|
run = false;
|
||||||
|
}));
|
||||||
|
|
||||||
|
while (run)
|
||||||
|
{
|
||||||
|
std::atomic<int> msg_counter{0};
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int t = 0; t < thread_count; ++t)
|
||||||
|
{
|
||||||
|
threads.push_back(std::thread([&]() {
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int counter = ++msg_counter;
|
||||||
|
if (counter > howmany)
|
||||||
|
break;
|
||||||
|
logger->info("spdlog message #{}: This is some text for your pleasure", counter);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
std::cout << "Total: " << howmany << std::endl;
|
||||||
|
std::cout << "Threads: " << thread_count << std::endl;
|
||||||
|
std::cout << "Delta = " << std::fixed << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << std::fixed << rate << "/sec" << std::endl;
|
||||||
|
} // while
|
||||||
|
|
||||||
|
stoper.join();
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
63
bench/spdlog-bench-mt.cpp
Normal file
63
bench/spdlog-bench-mt.cpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
|
||||||
|
int thread_count = 10;
|
||||||
|
if (argc > 1)
|
||||||
|
thread_count = std::atoi(argv[1]);
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("file_logger", "logs/spdlog-bench-mt.log", false);
|
||||||
|
logger->set_pattern("[%Y-%m-%d %T.%F]: %L %t %v");
|
||||||
|
|
||||||
|
std::atomic<int> msg_counter{0};
|
||||||
|
std::vector<thread> threads;
|
||||||
|
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int t = 0; t < thread_count; ++t)
|
||||||
|
{
|
||||||
|
threads.push_back(std::thread([&]() {
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int counter = ++msg_counter;
|
||||||
|
if (counter > howmany)
|
||||||
|
break;
|
||||||
|
logger->info("spdlog message #{}: This is some text for your pleasure", counter);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
std::cout << "Total: " << howmany << std::endl;
|
||||||
|
std::cout << "Threads: " << thread_count << std::endl;
|
||||||
|
std::cout << "Delta = " << std::fixed << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << std::fixed << rate << "/sec" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
34
bench/spdlog-bench.cpp
Normal file
34
bench/spdlog-bench.cpp
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
int main(int, char *[])
|
||||||
|
{
|
||||||
|
using namespace std::chrono;
|
||||||
|
using clock = steady_clock;
|
||||||
|
|
||||||
|
int howmany = 1000000;
|
||||||
|
|
||||||
|
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_st>("file_logger", "logs/spdlog-bench.log", false);
|
||||||
|
logger->set_pattern("[%Y-%m-%d %T.%F]: %L %v");
|
||||||
|
|
||||||
|
auto start = clock::now();
|
||||||
|
for (int i = 0; i < howmany; ++i)
|
||||||
|
logger->info("spdlog message #{} : This is some text for your pleasure", i);
|
||||||
|
|
||||||
|
duration<float> delta = clock::now() - start;
|
||||||
|
float deltaf = delta.count();
|
||||||
|
auto rate = howmany / deltaf;
|
||||||
|
|
||||||
|
std::cout << "Total: " << howmany << std::endl;
|
||||||
|
std::cout << "Delta = " << std::fixed << deltaf << " seconds" << std::endl;
|
||||||
|
std::cout << "Rate = " << std::fixed << rate << "/sec" << std::endl;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
103
bench/spdlog-null-async.cpp
Normal file
103
bench/spdlog-null-async.cpp
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
// bench.cpp : spdlog benchmarks
|
||||||
|
//
|
||||||
|
#include "spdlog/async_logger.h"
|
||||||
|
#include "spdlog/sinks/null_sink.h"
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstdlib> // EXIT_FAILURE
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace std::chrono;
|
||||||
|
using namespace spdlog;
|
||||||
|
using namespace spdlog::sinks;
|
||||||
|
using namespace utils;
|
||||||
|
|
||||||
|
size_t bench_as(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
|
||||||
|
int queue_size = 1048576;
|
||||||
|
int howmany = 1000000;
|
||||||
|
int threads = 10;
|
||||||
|
int iters = 10;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
if (argc > 1)
|
||||||
|
howmany = atoi(argv[1]);
|
||||||
|
if (argc > 2)
|
||||||
|
threads = atoi(argv[2]);
|
||||||
|
if (argc > 3)
|
||||||
|
queue_size = atoi(argv[3]);
|
||||||
|
|
||||||
|
cout << "\n*******************************************************************************\n";
|
||||||
|
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " messages " << endl;
|
||||||
|
cout << "*******************************************************************************\n";
|
||||||
|
|
||||||
|
spdlog::set_async_mode(queue_size);
|
||||||
|
|
||||||
|
size_t total_rate = 0;
|
||||||
|
|
||||||
|
for (int i = 0; i < iters; ++i)
|
||||||
|
{
|
||||||
|
// auto as = spdlog::daily_logger_st("as", "logs/daily_async");
|
||||||
|
auto as = spdlog::create<null_sink_st>("async(null-sink)");
|
||||||
|
total_rate += bench_as(howmany, as, threads);
|
||||||
|
spdlog::drop("async(null-sink)");
|
||||||
|
}
|
||||||
|
std::cout << endl;
|
||||||
|
std::cout << "Avg rate: " << format(total_rate / iters) << "/sec" << std::endl;
|
||||||
|
}
|
||||||
|
catch (std::exception &ex)
|
||||||
|
{
|
||||||
|
std::cerr << "Error: " << ex.what() << std::endl;
|
||||||
|
perror("Last error");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
// return rate/sec
|
||||||
|
size_t bench_as(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
|
||||||
|
{
|
||||||
|
cout << log->name() << "...\t\t" << flush;
|
||||||
|
std::atomic<int> msg_counter{0};
|
||||||
|
vector<thread> threads;
|
||||||
|
auto start = system_clock::now();
|
||||||
|
for (int t = 0; t < thread_count; ++t)
|
||||||
|
{
|
||||||
|
threads.push_back(std::thread([&]() {
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
int counter = ++msg_counter;
|
||||||
|
if (counter > howmany)
|
||||||
|
break;
|
||||||
|
log->info("Hello logger: msg number {}", counter);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto delta = system_clock::now() - start;
|
||||||
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
|
auto per_sec = size_t(howmany / delta_d);
|
||||||
|
cout << format(per_sec) << "/sec" << endl;
|
||||||
|
return per_sec;
|
||||||
|
}
|
24
cmake/Config.cmake.in
Normal file
24
cmake/Config.cmake.in
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# *************************************************************************/
|
||||||
|
# * Copyright (c) 2015 Ruslan Baratov. */
|
||||||
|
# * */
|
||||||
|
# * Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
# * a copy of this software and associated documentation files (the */
|
||||||
|
# * "Software"), to deal in the Software without restriction, including */
|
||||||
|
# * without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
# * distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
# * permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
# * the following conditions: */
|
||||||
|
# * */
|
||||||
|
# * The above copyright notice and this permission notice shall be */
|
||||||
|
# * included in all copies or substantial portions of the Software. */
|
||||||
|
# * */
|
||||||
|
# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||||
|
# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
# *************************************************************************/
|
||||||
|
|
||||||
|
include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
|
@@ -1,18 +0,0 @@
|
|||||||
# ---------------------------------------------------------------------------------------
|
|
||||||
# IDE support for headers
|
|
||||||
# ---------------------------------------------------------------------------------------
|
|
||||||
set(SPDLOG_HEADERS_DIR "${CMAKE_CURRENT_LIST_DIR}/../include")
|
|
||||||
|
|
||||||
file(GLOB SPDLOG_TOP_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/*.h")
|
|
||||||
file(GLOB SPDLOG_DETAILS_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/details/*.h")
|
|
||||||
file(GLOB SPDLOG_SINKS_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/sinks/*.h")
|
|
||||||
file(GLOB SPDLOG_FMT_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/fmt/*.h")
|
|
||||||
file(GLOB SPDLOG_FMT_BUNDELED_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/fmt/bundled/*.h")
|
|
||||||
set(SPDLOG_ALL_HEADERS ${SPDLOG_TOP_HEADERS} ${SPDLOG_DETAILS_HEADERS} ${SPDLOG_SINKS_HEADERS} ${SPDLOG_FMT_HEADERS}
|
|
||||||
${SPDLOG_FMT_BUNDELED_HEADERS})
|
|
||||||
|
|
||||||
source_group("Header Files\\spdlog" FILES ${SPDLOG_TOP_HEADERS})
|
|
||||||
source_group("Header Files\\spdlog\\details" FILES ${SPDLOG_DETAILS_HEADERS})
|
|
||||||
source_group("Header Files\\spdlog\\sinks" FILES ${SPDLOG_SINKS_HEADERS})
|
|
||||||
source_group("Header Files\\spdlog\\fmt" FILES ${SPDLOG_FMT_HEADERS})
|
|
||||||
source_group("Header Files\\spdlog\\fmt\\bundled\\" FILES ${SPDLOG_FMT_BUNDELED_HEADERS})
|
|
258
cmake/pch.h.in
258
cmake/pch.h.in
@@ -1,258 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// details/pattern_formatter-inl.h
|
|
||||||
// fmt/bin_to_hex.h
|
|
||||||
// fmt/bundled/format-inl.h
|
|
||||||
#include <cctype>
|
|
||||||
|
|
||||||
// details/file_helper-inl.h
|
|
||||||
// details/os-inl.h
|
|
||||||
// fmt/bundled/core.h
|
|
||||||
// fmt/bundled/posix.h
|
|
||||||
// logger-inl.h
|
|
||||||
// sinks/daily_file_sink.h
|
|
||||||
// sinks/stdout_sinks.h
|
|
||||||
#include <cstdio>
|
|
||||||
|
|
||||||
// details/os-inl.h
|
|
||||||
// fmt/bundled/posix.h
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
// details/os-inl.h
|
|
||||||
// details/pattern_formatter-inl.h
|
|
||||||
// fmt/bundled/core.h
|
|
||||||
// fmt/bundled/format-inl.h
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
// details/os-inl.h
|
|
||||||
// details/os.h
|
|
||||||
// details/pattern_formatter-inl.h
|
|
||||||
// details/pattern_formatter.h
|
|
||||||
// fmt/bundled/chrono.h
|
|
||||||
// sinks/daily_file_sink.h
|
|
||||||
// sinks/rotating_file_sink-inl.h
|
|
||||||
#include <ctime>
|
|
||||||
|
|
||||||
// fmt/bundled/format-inl.h
|
|
||||||
#include <climits>
|
|
||||||
|
|
||||||
// fmt/bundled/format-inl.h
|
|
||||||
#include <cwchar>
|
|
||||||
|
|
||||||
// fmt/bundled/format-inl.h
|
|
||||||
// fmt/bundled/format.h
|
|
||||||
#include <cmath>
|
|
||||||
|
|
||||||
// fmt/bundled/format-inl.h
|
|
||||||
#include <cstdarg>
|
|
||||||
|
|
||||||
// details/file_helper-inl.h
|
|
||||||
// fmt/bundled/format.h
|
|
||||||
// fmt/bundled/posix.h
|
|
||||||
// sinks/rotating_file_sink-inl.h
|
|
||||||
#include <cerrno>
|
|
||||||
|
|
||||||
// details/circular_q.h
|
|
||||||
// details/thread_pool-inl.h
|
|
||||||
// fmt/bundled/format-inl.h
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
// async_logger-inl.h
|
|
||||||
// cfg/helpers-inl.h
|
|
||||||
// log_levels.h
|
|
||||||
// common.h
|
|
||||||
// details/file_helper-inl.h
|
|
||||||
// details/log_msg.h
|
|
||||||
// details/os-inl.h
|
|
||||||
// details/pattern_formatter-inl.h
|
|
||||||
// details/pattern_formatter.h
|
|
||||||
// details/registry-inl.h
|
|
||||||
// details/registry.h
|
|
||||||
// details/tcp_client-windows.h
|
|
||||||
// details/tcp_client.h
|
|
||||||
// fmt/bundled/core.h
|
|
||||||
// sinks/android_sink.h
|
|
||||||
// sinks/ansicolor_sink.h
|
|
||||||
// sinks/basic_file_sink.h
|
|
||||||
// sinks/daily_file_sink.h
|
|
||||||
// sinks/dup_filter_sink.h
|
|
||||||
// sinks/msvc_sink.h
|
|
||||||
// sinks/ringbuffer_sink.h
|
|
||||||
// sinks/rotating_file_sink-inl.h
|
|
||||||
// sinks/rotating_file_sink.h
|
|
||||||
// sinks/syslog_sink.h
|
|
||||||
// sinks/tcp_sink.h
|
|
||||||
// sinks/win_eventlog_sink.h
|
|
||||||
// sinks/wincolor_sink.h
|
|
||||||
// spdlog.h:
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// cfg/helpers-inl.h
|
|
||||||
// fmt/bundled/chrono.h
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
// fmt/bundled/ostream.h
|
|
||||||
// sinks/ostream_sink.h
|
|
||||||
#include <ostream>
|
|
||||||
|
|
||||||
// cfg/log_levels.h
|
|
||||||
// details/registry-inl.h
|
|
||||||
// details/registry.h
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
// details/circular_q.h
|
|
||||||
// details/pattern_formatter-inl.h
|
|
||||||
// details/pattern_formatter.h
|
|
||||||
// details/thread_pool.h
|
|
||||||
// fmt/bundled/compile.h
|
|
||||||
// logger.h
|
|
||||||
// sinks/dist_sink.h
|
|
||||||
// sinks/ringbuffer_sink.h
|
|
||||||
// sinks/win_eventlog_sink.h
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
// details/os-inl.h
|
|
||||||
// details/pattern_formatter-inl.h
|
|
||||||
// sinks/ansicolor_sink.h
|
|
||||||
// sinks/syslog_sink.h
|
|
||||||
// sinks/systemd_sink.h
|
|
||||||
// sinks/wincolor_sink.h
|
|
||||||
#include <array>
|
|
||||||
|
|
||||||
// details/file_helper-inl.h
|
|
||||||
// details/file_helper.h
|
|
||||||
// sinks/rotating_file_sink-inl.h
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
// details/os-inl.h
|
|
||||||
// fmt/bundled/format.h
|
|
||||||
// fmt/bundled/printf.h
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
// common.h
|
|
||||||
// details/backtracer.h
|
|
||||||
// details/null_mutex.h
|
|
||||||
#include <atomic>
|
|
||||||
|
|
||||||
// common.h
|
|
||||||
// details/backtracer.h
|
|
||||||
// details/null_mutex.h
|
|
||||||
#include <locale>
|
|
||||||
|
|
||||||
// common.h
|
|
||||||
#include <initializer_list>
|
|
||||||
|
|
||||||
// common.h
|
|
||||||
#include <exception>
|
|
||||||
|
|
||||||
// common.h
|
|
||||||
// details/fmt_helper.h
|
|
||||||
// fmt/bundled/core.h
|
|
||||||
// fmt/bundled/ranges.h
|
|
||||||
#include <type_traits>
|
|
||||||
|
|
||||||
// cfg/helpers-inl.h
|
|
||||||
// details/null_mutex.h
|
|
||||||
// details/pattern_formatter-inl.h
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
// async.h
|
|
||||||
// async_logger-inl.h
|
|
||||||
// common.h
|
|
||||||
// details/pattern_formatter-inl.h
|
|
||||||
// details/pattern_formatter.h
|
|
||||||
// details/registry-inl.h
|
|
||||||
// details/registry.h
|
|
||||||
// details/thread_pool.h
|
|
||||||
// fmt/bundled/format.h
|
|
||||||
// sinks/ansicolor_sink.h
|
|
||||||
// sinks/base_sink-inl.h
|
|
||||||
// sinks/dist_sink.h
|
|
||||||
// sinks/stdout_sinks-inl.h
|
|
||||||
// sinks/wincolor_sink.h
|
|
||||||
// spdlog.h
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
// async.h
|
|
||||||
// common.h
|
|
||||||
// details/backtracer.h
|
|
||||||
// details/periodic_worker.h
|
|
||||||
// details/registry-inl.h
|
|
||||||
// details/registry.h
|
|
||||||
// details/thread_pool.h
|
|
||||||
// sinks/tcp_sink.h
|
|
||||||
// spdlog.h
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
// details/mpmc_blocking_q.h
|
|
||||||
// details/periodic_worker.h
|
|
||||||
#include <condition_variable>
|
|
||||||
|
|
||||||
// details/os-inl.h
|
|
||||||
// fmt/bundled/format.h
|
|
||||||
// fmt/bundled/printf.h
|
|
||||||
// sinks/dist_sink.h
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
// common.h
|
|
||||||
// details/file_helper-inl.h
|
|
||||||
// details/fmt_helper.h
|
|
||||||
// details/os-inl.h
|
|
||||||
// details/pattern_formatter-inl.h
|
|
||||||
// details/pattern_formatter.h
|
|
||||||
// details/periodic_worker.h
|
|
||||||
// details/registry-inl.h
|
|
||||||
// details/registry.h
|
|
||||||
// details/thread_pool.h
|
|
||||||
// fmt/bundled/chrono.h
|
|
||||||
// sinks/android_sink.h
|
|
||||||
// sinks/daily_file_sink.h
|
|
||||||
// sinks/dup_filter_sink.h
|
|
||||||
// sinks/rotating_file_sink-inl.h
|
|
||||||
// sinks/rotating_file_sink.h
|
|
||||||
// sinks/tcp_sink.h
|
|
||||||
// spdlog.h
|
|
||||||
#include <chrono>
|
|
||||||
|
|
||||||
// details/file_helper-inl.h
|
|
||||||
// details/os-inl.h
|
|
||||||
// details/pattern_formatter-inl.h
|
|
||||||
// details/periodic_worker.h
|
|
||||||
// details/thread_pool.h
|
|
||||||
// sinks/android_sink.h
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
// async.h
|
|
||||||
// details/backtracer.h
|
|
||||||
// details/console_globals.h
|
|
||||||
// details/mpmc_blocking_q.h
|
|
||||||
// details/pattern_formatter-inl.h
|
|
||||||
// details/periodic_worker.h
|
|
||||||
// details/registry.h
|
|
||||||
// sinks/android_sink.h
|
|
||||||
// sinks/ansicolor_sink.h
|
|
||||||
// sinks/basic_file_sink.h
|
|
||||||
// sinks/daily_file_sink.h
|
|
||||||
// sinks/dist_sink.h
|
|
||||||
// sinks/dup_filter_sink.h
|
|
||||||
// sinks/msvc_sink.h
|
|
||||||
// sinks/null_sink.h
|
|
||||||
// sinks/ostream_sink.h
|
|
||||||
// sinks/ringbuffer_sink.h
|
|
||||||
// sinks/rotating_file_sink-inl.h
|
|
||||||
// sinks/rotating_file_sink.h
|
|
||||||
// sinks/tcp_sink.h
|
|
||||||
// sinks/win_eventlog_sink.h
|
|
||||||
// sinks/wincolor_sink.h
|
|
||||||
//
|
|
||||||
// color_sinks.cpp
|
|
||||||
// file_sinks.cpp
|
|
||||||
// spdlog.cpp
|
|
||||||
// stdout_sinks.cpp
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
// spdlog
|
|
||||||
#include <spdlog/common.h>
|
|
@@ -1,13 +1,6 @@
|
|||||||
prefix=@CMAKE_INSTALL_PREFIX@
|
prefix=@CMAKE_INSTALL_PREFIX@
|
||||||
exec_prefix=${prefix}
|
includedir=${prefix}/include
|
||||||
includedir=${prefix}/include
|
|
||||||
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
|
Name: @PROJECT_NAME@
|
||||||
|
Description: Super fast C++ logging library.
|
||||||
Name: lib@PROJECT_NAME@
|
Version: @PROJECT_VERSION@
|
||||||
Description: Fast C++ logging library.
|
|
||||||
URL: https://github.com/gabime/@PROJECT_NAME@
|
|
||||||
Version: @SPDLOG_VERSION@
|
|
||||||
CFlags: -I${includedir} @PKG_CONFIG_DEFINES@
|
|
||||||
Libs: -L${libdir} -lspdlog -pthread
|
|
||||||
Requires: @PKG_CONFIG_REQUIRES@
|
|
||||||
|
|
||||||
|
@@ -1,60 +0,0 @@
|
|||||||
set(CPACK_GENERATOR "TGZ;ZIP" CACHE STRING "Semicolon separated list of generators")
|
|
||||||
|
|
||||||
set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0)
|
|
||||||
set(CPACK_INSTALL_CMAKE_PROJECTS "${CMAKE_BINARY_DIR}" "${PROJECT_NAME}" ALL .)
|
|
||||||
|
|
||||||
set(CPACK_PROJECT_URL "https://github.com/gabime/spdlog")
|
|
||||||
set(CPACK_PACKAGE_VENDOR "Gabi Melman")
|
|
||||||
set(CPACK_PACKAGE_CONTACT "Gabi Melman <gmelman1@gmail.com>")
|
|
||||||
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Fast C++ logging library")
|
|
||||||
set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR})
|
|
||||||
set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR})
|
|
||||||
set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH})
|
|
||||||
set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
|
|
||||||
if(PROJECT_VERSION_TWEAK)
|
|
||||||
set(CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION}.${PROJECT_VERSION_TWEAK})
|
|
||||||
endif()
|
|
||||||
set(CPACK_PACKAGE_RELOCATABLE ON CACHE BOOL "Build relocatable package")
|
|
||||||
|
|
||||||
set(CPACK_RPM_PACKAGE_LICENSE "MIT")
|
|
||||||
set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries")
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_SECTION "libs")
|
|
||||||
set(CPACK_RPM_PACKAGE_URL ${CPACK_PROJECT_URL})
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_HOMEPAGE ${CPACK_PROJECT_URL})
|
|
||||||
set(CPACK_RPM_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.")
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.")
|
|
||||||
|
|
||||||
if(CPACK_PACKAGE_NAME)
|
|
||||||
set(CPACK_RPM_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
|
|
||||||
set(CPACK_DEBIAN_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}")
|
|
||||||
else()
|
|
||||||
set(CPACK_RPM_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}")
|
|
||||||
set(CPACK_DEBIAN_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}")
|
|
||||||
set(CPACK_RPM_PACKAGE_NAME "${PROJECT_NAME}")
|
|
||||||
set(CPACK_DEBIAN_PACKAGE_NAME "${PROJECT_NAME}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CPACK_RPM_PACKAGE_RELEASE)
|
|
||||||
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}-${CPACK_RPM_PACKAGE_RELEASE}")
|
|
||||||
endif()
|
|
||||||
if(CPACK_DEBIAN_PACKAGE_RELEASE)
|
|
||||||
set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}-${CPACK_DEBIAN_PACKAGE_RELEASE}")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if(CPACK_RPM_PACKAGE_ARCHITECTURE)
|
|
||||||
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.${CPACK_RPM_PACKAGE_ARCHITECTURE}")
|
|
||||||
endif()
|
|
||||||
if(CPACK_DEBIAN_PACKAGE_ARCHITECTURE)
|
|
||||||
set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
|
|
||||||
endif()
|
|
||||||
set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.rpm")
|
|
||||||
set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.deb")
|
|
||||||
|
|
||||||
if(NOT CPACK_PACKAGE_RELOCATABLE)
|
|
||||||
# Depend on pkgconfig rpm to create the system pkgconfig folder
|
|
||||||
set(CPACK_RPM_PACKAGE_REQUIRES pkgconfig)
|
|
||||||
set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION
|
|
||||||
"${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/pkgconfig")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
include(CPack)
|
|
@@ -1,15 +0,0 @@
|
|||||||
# Copyright(c) 2019 spdlog authors
|
|
||||||
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
find_package(Threads REQUIRED)
|
|
||||||
|
|
||||||
set(SPDLOG_FMT_EXTERNAL @SPDLOG_FMT_EXTERNAL@)
|
|
||||||
set(config_targets_file @config_targets_file@)
|
|
||||||
|
|
||||||
if(SPDLOG_FMT_EXTERNAL)
|
|
||||||
include(CMakeFindDependencyMacro)
|
|
||||||
find_dependency(fmt CONFIG)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
|
|
||||||
include("${CMAKE_CURRENT_LIST_DIR}/${config_targets_file}")
|
|
@@ -1,62 +0,0 @@
|
|||||||
# Get spdlog version from include/spdlog/version.h and put it in SPDLOG_VERSION
|
|
||||||
function(spdlog_extract_version)
|
|
||||||
file(READ "${CMAKE_CURRENT_LIST_DIR}/include/spdlog/version.h" file_contents)
|
|
||||||
string(REGEX MATCH "SPDLOG_VER_MAJOR ([0-9]+)" _ "${file_contents}")
|
|
||||||
if(NOT CMAKE_MATCH_COUNT EQUAL 1)
|
|
||||||
message(FATAL_ERROR "Could not extract major version number from spdlog/version.h")
|
|
||||||
endif()
|
|
||||||
set(ver_major ${CMAKE_MATCH_1})
|
|
||||||
|
|
||||||
string(REGEX MATCH "SPDLOG_VER_MINOR ([0-9]+)" _ "${file_contents}")
|
|
||||||
if(NOT CMAKE_MATCH_COUNT EQUAL 1)
|
|
||||||
message(FATAL_ERROR "Could not extract minor version number from spdlog/version.h")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
set(ver_minor ${CMAKE_MATCH_1})
|
|
||||||
string(REGEX MATCH "SPDLOG_VER_PATCH ([0-9]+)" _ "${file_contents}")
|
|
||||||
if(NOT CMAKE_MATCH_COUNT EQUAL 1)
|
|
||||||
message(FATAL_ERROR "Could not extract patch version number from spdlog/version.h")
|
|
||||||
endif()
|
|
||||||
set(ver_patch ${CMAKE_MATCH_1})
|
|
||||||
|
|
||||||
set(SPDLOG_VERSION_MAJOR ${ver_major} PARENT_SCOPE)
|
|
||||||
set(SPDLOG_VERSION_MINOR ${ver_minor} PARENT_SCOPE)
|
|
||||||
set(SPDLOG_VERSION_PATCH ${ver_patch} PARENT_SCOPE)
|
|
||||||
set(SPDLOG_VERSION "${ver_major}.${ver_minor}.${ver_patch}" PARENT_SCOPE)
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
# Turn on warnings on the given target
|
|
||||||
function(spdlog_enable_warnings target_name)
|
|
||||||
if(SPDLOG_BUILD_WARNINGS)
|
|
||||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
|
||||||
list(APPEND MSVC_OPTIONS "/W3")
|
|
||||||
if(MSVC_VERSION GREATER 1900) # Allow non fatal security warnings for msvc 2015
|
|
||||||
list(APPEND MSVC_OPTIONS "/WX")
|
|
||||||
endif()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
target_compile_options(
|
|
||||||
${target_name}
|
|
||||||
PRIVATE $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:GNU>>:
|
|
||||||
-Wall
|
|
||||||
-Wextra
|
|
||||||
-Wconversion
|
|
||||||
-pedantic
|
|
||||||
-Wfatal-errors>
|
|
||||||
$<$<CXX_COMPILER_ID:MSVC>:${MSVC_OPTIONS}>)
|
|
||||||
endif()
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
# Enable address sanitizer (gcc/clang only)
|
|
||||||
function(spdlog_enable_sanitizer target_name)
|
|
||||||
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
|
||||||
message(FATAL_ERROR "Sanitizer supported only for gcc/clang")
|
|
||||||
endif()
|
|
||||||
message(STATUS "Address sanitizer enabled")
|
|
||||||
target_compile_options(${target_name} PRIVATE -fsanitize=address,undefined)
|
|
||||||
target_compile_options(${target_name} PRIVATE -fno-sanitize=signed-integer-overflow)
|
|
||||||
target_compile_options(${target_name} PRIVATE -fno-sanitize-recover=all)
|
|
||||||
target_compile_options(${target_name} PRIVATE -fno-omit-frame-pointer)
|
|
||||||
target_link_libraries(${target_name} PRIVATE -fsanitize=address,undefined -fuse-ld=gold)
|
|
||||||
endfunction()
|
|
||||||
|
|
@@ -1,42 +0,0 @@
|
|||||||
#define APSTUDIO_READONLY_SYMBOLS
|
|
||||||
#include <windows.h>
|
|
||||||
#undef APSTUDIO_READONLY_SYMBOLS
|
|
||||||
|
|
||||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
|
||||||
|
|
||||||
|
|
||||||
VS_VERSION_INFO VERSIONINFO
|
|
||||||
FILEVERSION @SPDLOG_VERSION_MAJOR@,@SPDLOG_VERSION_MINOR@,@SPDLOG_VERSION_PATCH@,0
|
|
||||||
PRODUCTVERSION @SPDLOG_VERSION_MAJOR@,@SPDLOG_VERSION_MINOR@,@SPDLOG_VERSION_PATCH@,0
|
|
||||||
FILEFLAGSMASK 0x3fL
|
|
||||||
#ifdef _DEBUG
|
|
||||||
FILEFLAGS 0x1L
|
|
||||||
#else
|
|
||||||
FILEFLAGS 0x0L
|
|
||||||
#endif
|
|
||||||
FILEOS 0x40004L
|
|
||||||
FILETYPE 0x2L
|
|
||||||
FILESUBTYPE 0x0L
|
|
||||||
BEGIN
|
|
||||||
BLOCK "StringFileInfo"
|
|
||||||
BEGIN
|
|
||||||
BLOCK "040904b0"
|
|
||||||
BEGIN
|
|
||||||
VALUE "FileDescription", "spdlog dll\0"
|
|
||||||
VALUE "FileVersion", "@SPDLOG_VERSION@.0\0"
|
|
||||||
VALUE "InternalName", "spdlog.dll\0"
|
|
||||||
VALUE "LegalCopyright", "Copyright (C) spdlog\0"
|
|
||||||
VALUE "ProductName", "spdlog\0"
|
|
||||||
VALUE "ProductVersion", "@SPDLOG_VERSION@.0\0"
|
|
||||||
END
|
|
||||||
END
|
|
||||||
BLOCK "VarFileInfo"
|
|
||||||
BEGIN
|
|
||||||
VALUE "Translation", 0x409, 1200
|
|
||||||
END
|
|
||||||
END
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@@ -1,23 +1,49 @@
|
|||||||
# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
# *************************************************************************/
|
||||||
|
# * Copyright (c) 2015 Ruslan Baratov. */
|
||||||
|
# * */
|
||||||
|
# * Permission is hereby granted, free of charge, to any person obtaining */
|
||||||
|
# * a copy of this software and associated documentation files (the */
|
||||||
|
# * "Software"), to deal in the Software without restriction, including */
|
||||||
|
# * without limitation the rights to use, copy, modify, merge, publish, */
|
||||||
|
# * distribute, sublicense, and/or sell copies of the Software, and to */
|
||||||
|
# * permit persons to whom the Software is furnished to do so, subject to */
|
||||||
|
# * the following conditions: */
|
||||||
|
# * */
|
||||||
|
# * The above copyright notice and this permission notice shall be */
|
||||||
|
# * included in all copies or substantial portions of the Software. */
|
||||||
|
# * */
|
||||||
|
# * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||||
|
# * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||||
|
# * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||||
|
# * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||||
|
# * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||||
|
# * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||||
|
# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
|
# *************************************************************************/
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.1)
|
||||||
project(spdlog_examples CXX)
|
project(SpdlogExamples CXX)
|
||||||
|
|
||||||
if(NOT TARGET spdlog)
|
if(TARGET spdlog)
|
||||||
# Stand-alone build
|
# Part of the main project
|
||||||
find_package(spdlog REQUIRED)
|
add_library(spdlog::spdlog ALIAS spdlog)
|
||||||
|
else()
|
||||||
|
# Stand-alone build
|
||||||
|
find_package(spdlog CONFIG REQUIRED)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
find_package(Threads REQUIRED)
|
||||||
# Example of using pre-compiled library
|
|
||||||
# ---------------------------------------------------------------------------------------
|
|
||||||
add_executable(example example.cpp)
|
add_executable(example example.cpp)
|
||||||
target_link_libraries(example PRIVATE spdlog::spdlog)
|
target_link_libraries(example spdlog::spdlog Threads::Threads)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------------------
|
add_executable(benchmark bench.cpp)
|
||||||
# Example of using header-only library
|
target_link_libraries(benchmark spdlog::spdlog Threads::Threads)
|
||||||
# ---------------------------------------------------------------------------------------
|
|
||||||
if(SPDLOG_BUILD_EXAMPLE_HO)
|
add_executable(multisink multisink.cpp)
|
||||||
add_executable(example_header_only example.cpp)
|
target_link_libraries(multisink spdlog::spdlog Threads::Threads)
|
||||||
target_link_libraries(example_header_only PRIVATE spdlog::spdlog_header_only)
|
|
||||||
endif()
|
enable_testing()
|
||||||
|
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
|
||||||
|
add_test(NAME RunExample COMMAND example)
|
||||||
|
add_test(NAME RunBenchmark COMMAND benchmark)
|
||||||
|
29
example/Makefile
Normal file
29
example/Makefile
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
CXX ?= g++
|
||||||
|
CXXFLAGS =
|
||||||
|
CXX_FLAGS = -Wall -Wshadow -Wextra -pedantic -std=c++11 -pthread -I../include
|
||||||
|
CXX_RELEASE_FLAGS = -O3 -march=native
|
||||||
|
CXX_DEBUG_FLAGS= -g
|
||||||
|
|
||||||
|
|
||||||
|
all: example bench
|
||||||
|
debug: example-debug bench-debug
|
||||||
|
|
||||||
|
example: example.cpp
|
||||||
|
$(CXX) example.cpp -o example $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS)
|
||||||
|
|
||||||
|
bench: bench.cpp
|
||||||
|
$(CXX) bench.cpp -o bench $(CXX_FLAGS) $(CXX_RELEASE_FLAGS) $(CXXFLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
example-debug: example.cpp
|
||||||
|
$(CXX) example.cpp -o example-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS)
|
||||||
|
|
||||||
|
bench-debug: bench.cpp
|
||||||
|
$(CXX) bench.cpp -o bench-debug $(CXX_FLAGS) $(CXX_DEBUG_FLAGS) $(CXXFLAGS)
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o logs/*.txt example example-debug bench bench-debug
|
||||||
|
|
||||||
|
|
||||||
|
rebuild: clean all
|
||||||
|
rebuild-debug: clean debug
|
32
example/Makefile.clang
Normal file
32
example/Makefile.clang
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
CXX = clang++
|
||||||
|
CXXFLAGS = -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -I../include
|
||||||
|
CXX_RELEASE_FLAGS = -O2
|
||||||
|
CXX_DEBUG_FLAGS= -g
|
||||||
|
|
||||||
|
|
||||||
|
all: example bench
|
||||||
|
debug: example-debug bench-debug
|
||||||
|
|
||||||
|
example: example.cpp
|
||||||
|
$(CXX) example.cpp -o example-clang $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
bench: bench.cpp
|
||||||
|
$(CXX) bench.cpp -o bench-clang $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
example-debug: example.cpp
|
||||||
|
$(CXX) example.cpp -o example-clang-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
|
||||||
|
|
||||||
|
bench-debug: bench.cpp
|
||||||
|
$(CXX) bench.cpp -o bench-clang-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o logs/*.txt example-clang example-clang-debug bench-clang bench-clang-debug
|
||||||
|
|
||||||
|
|
||||||
|
rebuild: clean all
|
||||||
|
rebuild-debug: clean debug
|
||||||
|
|
||||||
|
|
32
example/Makefile.mingw
Normal file
32
example/Makefile.mingw
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
CXX ?= g++
|
||||||
|
CXXFLAGS = -D_WIN32_WINNT=0x600 -march=native -Wall -Wextra -Wshadow -pedantic -std=gnu++0x -pthread -Wl,--no-as-needed -I../include
|
||||||
|
CXX_RELEASE_FLAGS = -O3
|
||||||
|
CXX_DEBUG_FLAGS= -g
|
||||||
|
|
||||||
|
|
||||||
|
all: example bench
|
||||||
|
debug: example-debug bench-debug
|
||||||
|
|
||||||
|
example: example.cpp
|
||||||
|
$(CXX) example.cpp -o example $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
bench: bench.cpp
|
||||||
|
$(CXX) bench.cpp -o bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
example-debug: example.cpp
|
||||||
|
$(CXX) example.cpp -o example-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
|
||||||
|
|
||||||
|
bench-debug: bench.cpp
|
||||||
|
$(CXX) bench.cpp -o bench-debug $(CXXFLAGS) $(CXX_DEBUG_FLAGS)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o logs/*.txt example example-debug bench bench-debug
|
||||||
|
|
||||||
|
|
||||||
|
rebuild: clean all
|
||||||
|
rebuild-debug: clean debug
|
||||||
|
|
||||||
|
|
134
example/bench.cpp
Normal file
134
example/bench.cpp
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
//
|
||||||
|
// bench.cpp : spdlog benchmarks
|
||||||
|
//
|
||||||
|
#include "spdlog/async_logger.h"
|
||||||
|
#include "spdlog/sinks/file_sinks.h"
|
||||||
|
#include "spdlog/sinks/null_sink.h"
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
#include "utils.h"
|
||||||
|
#include <atomic>
|
||||||
|
#include <cstdlib> // EXIT_FAILURE
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace std::chrono;
|
||||||
|
using namespace spdlog;
|
||||||
|
using namespace spdlog::sinks;
|
||||||
|
using namespace utils;
|
||||||
|
|
||||||
|
void bench(int howmany, std::shared_ptr<spdlog::logger> log);
|
||||||
|
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
|
||||||
|
int queue_size = 1048576;
|
||||||
|
int howmany = 1000000;
|
||||||
|
int threads = 10;
|
||||||
|
int file_size = 30 * 1024 * 1024;
|
||||||
|
int rotating_files = 5;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
|
||||||
|
if (argc > 1)
|
||||||
|
howmany = atoi(argv[1]);
|
||||||
|
if (argc > 2)
|
||||||
|
threads = atoi(argv[2]);
|
||||||
|
if (argc > 3)
|
||||||
|
queue_size = atoi(argv[3]);
|
||||||
|
|
||||||
|
cout << "*******************************************************************************\n";
|
||||||
|
cout << "Single thread, " << format(howmany) << " iterations" << endl;
|
||||||
|
cout << "*******************************************************************************\n";
|
||||||
|
|
||||||
|
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
|
||||||
|
bench(howmany, rotating_st);
|
||||||
|
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
|
||||||
|
bench(howmany, daily_st);
|
||||||
|
bench(howmany, spdlog::create<null_sink_st>("null_st"));
|
||||||
|
|
||||||
|
cout << "\n*******************************************************************************\n";
|
||||||
|
cout << threads << " threads sharing same logger, " << format(howmany) << " iterations" << endl;
|
||||||
|
cout << "*******************************************************************************\n";
|
||||||
|
|
||||||
|
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files);
|
||||||
|
bench_mt(howmany, rotating_mt, threads);
|
||||||
|
|
||||||
|
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
|
||||||
|
bench_mt(howmany, daily_mt, threads);
|
||||||
|
bench(howmany, spdlog::create<null_sink_st>("null_mt"));
|
||||||
|
|
||||||
|
cout << "\n*******************************************************************************\n";
|
||||||
|
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations " << endl;
|
||||||
|
cout << "*******************************************************************************\n";
|
||||||
|
|
||||||
|
spdlog::set_async_mode(queue_size);
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; ++i)
|
||||||
|
{
|
||||||
|
auto as = spdlog::daily_logger_st("as", "logs/daily_async.log");
|
||||||
|
bench_mt(howmany, as, threads);
|
||||||
|
spdlog::drop("as");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception &ex)
|
||||||
|
{
|
||||||
|
std::cerr << "Error: " << ex.what() << std::endl;
|
||||||
|
perror("Last error");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||||
|
{
|
||||||
|
cout << log->name() << "...\t\t" << flush;
|
||||||
|
auto start = system_clock::now();
|
||||||
|
for (auto i = 0; i < howmany; ++i)
|
||||||
|
{
|
||||||
|
log->info("Hello logger: msg number {}", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto delta = system_clock::now() - start;
|
||||||
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
|
cout << "Elapsed: " << delta_d << "\t" << format(int(howmany / delta_d)) << "/sec" << endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
|
||||||
|
{
|
||||||
|
|
||||||
|
cout << log->name() << "...\t\t" << flush;
|
||||||
|
std::atomic<int> msg_counter{0};
|
||||||
|
vector<thread> threads;
|
||||||
|
auto start = system_clock::now();
|
||||||
|
for (int t = 0; t < thread_count; ++t)
|
||||||
|
{
|
||||||
|
threads.push_back(std::thread([&]() {
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
int counter = ++msg_counter;
|
||||||
|
if (counter > howmany)
|
||||||
|
break;
|
||||||
|
log->info("Hello logger: msg number {}", counter);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &t : threads)
|
||||||
|
{
|
||||||
|
t.join();
|
||||||
|
};
|
||||||
|
|
||||||
|
auto delta = system_clock::now() - start;
|
||||||
|
auto delta_d = duration_cast<duration<double>>(delta).count();
|
||||||
|
cout << "Elapsed: " << delta_d << "\t" << format(int(howmany / delta_d)) << "/sec" << endl;
|
||||||
|
}
|
@@ -1,227 +1,140 @@
|
|||||||
//
|
//
|
||||||
// Copyright(c) 2015 Gabi Melman.
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
//
|
||||||
// spdlog usage example
|
// spdlog usage example
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
#include <cstdio>
|
#define SPDLOG_TRACE_ON
|
||||||
|
#define SPDLOG_DEBUG_ON
|
||||||
void load_levels_example();
|
|
||||||
void stdout_logger_example();
|
|
||||||
void basic_example();
|
|
||||||
void rotating_example();
|
|
||||||
void daily_example();
|
|
||||||
void async_example();
|
|
||||||
void binary_example();
|
|
||||||
void stopwatch_example();
|
|
||||||
void trace_example();
|
|
||||||
void multi_sink_example();
|
|
||||||
void user_defined_example();
|
|
||||||
void err_handler_example();
|
|
||||||
void syslog_example();
|
|
||||||
void custom_flags_example();
|
|
||||||
|
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
#include "spdlog/cfg/env.h" // support for loading levels from the environment variable
|
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
void async_example();
|
||||||
|
void syslog_example();
|
||||||
|
void android_example();
|
||||||
|
void user_defined_example();
|
||||||
|
void err_handler_example();
|
||||||
|
|
||||||
|
namespace spd = spdlog;
|
||||||
int main(int, char *[])
|
int main(int, char *[])
|
||||||
{
|
{
|
||||||
// Log levels can be loaded from argv/env using "SPDLOG_LEVEL"
|
|
||||||
load_levels_example();
|
|
||||||
|
|
||||||
spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH);
|
|
||||||
|
|
||||||
spdlog::warn("Easy padding in numbers like {:08d}", 12);
|
|
||||||
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
|
||||||
spdlog::info("Support for floats {:03.2f}", 1.23456);
|
|
||||||
spdlog::info("Positional args are {1} {0}..", "too", "supported");
|
|
||||||
spdlog::info("{:>8} aligned, {:<8} aligned", "right", "left");
|
|
||||||
|
|
||||||
// Runtime log levels
|
|
||||||
spdlog::set_level(spdlog::level::info); // Set global log level to info
|
|
||||||
spdlog::debug("This message should not be displayed!");
|
|
||||||
spdlog::set_level(spdlog::level::trace); // Set specific logger's log level
|
|
||||||
spdlog::debug("This message should be displayed..");
|
|
||||||
|
|
||||||
// Customize msg format for all loggers
|
|
||||||
spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [thread %t] %v");
|
|
||||||
spdlog::info("This an info message with custom format");
|
|
||||||
spdlog::set_pattern("%+"); // back to default format
|
|
||||||
spdlog::set_level(spdlog::level::info);
|
|
||||||
|
|
||||||
// Backtrace support
|
|
||||||
// Loggers can store in a ring buffer all messages (including debug/trace) for later inspection.
|
|
||||||
// When needed, call dump_backtrace() to see what happened:
|
|
||||||
spdlog::enable_backtrace(10); // create ring buffer with capacity of 10 messages
|
|
||||||
for (int i = 0; i < 100; i++)
|
|
||||||
{
|
|
||||||
spdlog::debug("Backtrace message {}", i); // not logged..
|
|
||||||
}
|
|
||||||
// e.g. if some error happened:
|
|
||||||
spdlog::dump_backtrace(); // log them now!
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
stdout_logger_example();
|
// Console logger with color
|
||||||
basic_example();
|
auto console = spd::stdout_color_mt("console");
|
||||||
rotating_example();
|
console->info("Welcome to spdlog!");
|
||||||
daily_example();
|
console->error("Some error message with arg{}..", 1);
|
||||||
|
|
||||||
|
// Formatting examples
|
||||||
|
console->warn("Easy padding in numbers like {:08d}", 12);
|
||||||
|
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||||
|
console->info("Support for floats {:03.2f}", 1.23456);
|
||||||
|
console->info("Positional args are {1} {0}..", "too", "supported");
|
||||||
|
console->info("{:<30}", "left aligned");
|
||||||
|
|
||||||
|
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
|
||||||
|
|
||||||
|
// Create basic file logger (not rotated)
|
||||||
|
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt");
|
||||||
|
my_logger->info("Some log message");
|
||||||
|
|
||||||
|
// Create a file rotating logger with 5mb size max and 3 rotated files
|
||||||
|
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
|
||||||
|
for (int i = 0; i < 10; ++i)
|
||||||
|
{
|
||||||
|
rotating_logger->info("{} * {} equals {:>10}", i, i, i * i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a daily logger - a new file is created every day on 2:30am
|
||||||
|
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
||||||
|
// trigger flush if the log severity is error or higher
|
||||||
|
daily_logger->flush_on(spd::level::err);
|
||||||
|
daily_logger->info(123.44);
|
||||||
|
|
||||||
|
// Customize msg format for all messages
|
||||||
|
spd::set_pattern("[%^+++%$] [%H:%M:%S %z] [thread %t] %v");
|
||||||
|
console->info("This an info message with custom format");
|
||||||
|
console->error("This an error message with custom format");
|
||||||
|
|
||||||
|
// Runtime log levels
|
||||||
|
spd::set_level(spd::level::info); // Set global log level to info
|
||||||
|
console->debug("This message should not be displayed!");
|
||||||
|
console->set_level(spd::level::debug); // Set specific logger's log level
|
||||||
|
console->debug("This message should be displayed..");
|
||||||
|
|
||||||
|
// Compile time log levels
|
||||||
|
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
|
||||||
|
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
|
||||||
|
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
|
||||||
|
|
||||||
|
// Asynchronous logging is very fast..
|
||||||
|
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
|
||||||
async_example();
|
async_example();
|
||||||
binary_example();
|
|
||||||
multi_sink_example();
|
// syslog example. linux/osx only
|
||||||
|
syslog_example();
|
||||||
|
|
||||||
|
// android example. compile with NDK
|
||||||
|
android_example();
|
||||||
|
|
||||||
|
// Log user-defined types example
|
||||||
user_defined_example();
|
user_defined_example();
|
||||||
|
|
||||||
|
// Change default log error handler
|
||||||
err_handler_example();
|
err_handler_example();
|
||||||
trace_example();
|
|
||||||
stopwatch_example();
|
|
||||||
custom_flags_example();
|
|
||||||
|
|
||||||
// Flush all *registered* loggers using a worker thread every 3 seconds.
|
// Apply a function on all registered loggers
|
||||||
// note: registered loggers *must* be thread safe for this to work correctly!
|
spd::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); });
|
||||||
spdlog::flush_every(std::chrono::seconds(3));
|
|
||||||
|
|
||||||
// Apply some function on all registered loggers
|
// Release and close all loggers
|
||||||
spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); });
|
spdlog::drop_all();
|
||||||
|
|
||||||
// Release all spdlog resources, and drop all loggers in the registry.
|
|
||||||
// This is optional (only mandatory if using windows + async log).
|
|
||||||
spdlog::shutdown();
|
|
||||||
}
|
}
|
||||||
|
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
|
||||||
// Exceptions will only be thrown upon failed logger or sink construction (not during logging).
|
catch (const spd::spdlog_ex &ex)
|
||||||
catch (const spdlog::spdlog_ex &ex)
|
|
||||||
{
|
{
|
||||||
std::printf("Log initialization failed: %s\n", ex.what());
|
std::cout << "Log init failed: " << ex.what() << std::endl;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
|
||||||
// or #include "spdlog/sinks/stdout_sinks.h" if no colors needed.
|
|
||||||
void stdout_logger_example()
|
|
||||||
{
|
|
||||||
// Create color multi threaded logger.
|
|
||||||
auto console = spdlog::stdout_color_mt("console");
|
|
||||||
// or for stderr:
|
|
||||||
// auto console = spdlog::stderr_color_mt("error-logger");
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
|
||||||
void basic_example()
|
|
||||||
{
|
|
||||||
// Create basic file logger (not rotated).
|
|
||||||
auto my_logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt");
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "spdlog/sinks/rotating_file_sink.h"
|
|
||||||
void rotating_example()
|
|
||||||
{
|
|
||||||
// Create a file rotating logger with 5mb size max and 3 rotated files.
|
|
||||||
auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "spdlog/sinks/daily_file_sink.h"
|
|
||||||
void daily_example()
|
|
||||||
{
|
|
||||||
// Create a daily logger - a new file is created every day on 2:30am.
|
|
||||||
auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "spdlog/cfg/env.h"
|
|
||||||
void load_levels_example()
|
|
||||||
{
|
|
||||||
// Set the log level to "info" and mylogger to to "trace":
|
|
||||||
// SPDLOG_LEVEL=info,mylogger=trace && ./example
|
|
||||||
spdlog::cfg::load_env_levels();
|
|
||||||
// or from command line:
|
|
||||||
// ./example SPDLOG_LEVEL=info,mylogger=trace
|
|
||||||
// #include "spdlog/cfg/argv.h" // for loading levels from argv
|
|
||||||
// spdlog::cfg::load_argv_levels(args, argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "spdlog/async.h"
|
|
||||||
void async_example()
|
void async_example()
|
||||||
{
|
{
|
||||||
// Default thread pool settings can be modified *before* creating the async logger:
|
size_t q_size = 4096;
|
||||||
// spdlog::init_thread_pool(32768, 1); // queue with max 32k items 1 backing thread.
|
spdlog::set_async_mode(q_size);
|
||||||
auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
|
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
|
||||||
// alternatively:
|
for (int i = 0; i < 100; ++i)
|
||||||
// auto async_file = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger", "logs/async_log.txt");
|
|
||||||
|
|
||||||
for (int i = 1; i < 101; ++i)
|
|
||||||
{
|
{
|
||||||
async_file->info("Async message #{}", i);
|
async_file->info("Async message #{}", i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log binary data as hex.
|
// syslog example (linux/osx/freebsd)
|
||||||
// Many types of std::container<char> types can be used.
|
void syslog_example()
|
||||||
// Iterator 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 to lines.
|
|
||||||
|
|
||||||
#include "spdlog/fmt/bin_to_hex.h"
|
|
||||||
void binary_example()
|
|
||||||
{
|
{
|
||||||
std::vector<char> buf(80);
|
#ifdef SPDLOG_ENABLE_SYSLOG
|
||||||
for (int i = 0; i < 80; i++)
|
std::string ident = "spdlog-example";
|
||||||
{
|
auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID);
|
||||||
buf.push_back(static_cast<char>(i & 0xff));
|
syslog_logger->warn("This is warning that will end up in syslog.");
|
||||||
}
|
#endif
|
||||||
spdlog::info("Binary example: {}", spdlog::to_hex(buf));
|
|
||||||
spdlog::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->info("hexdump style: {:a}", spdlog::to_hex(buf));
|
|
||||||
// logger->info("hexdump style, 20 chars per line {:a}", spdlog::to_hex(buf, 20));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile time log levels.
|
// Android example
|
||||||
// define SPDLOG_ACTIVE_LEVEL to required level (e.g. SPDLOG_LEVEL_TRACE)
|
void android_example()
|
||||||
void trace_example()
|
|
||||||
{
|
{
|
||||||
// trace from default logger
|
#if defined(__ANDROID__)
|
||||||
SPDLOG_TRACE("Some trace message.. {} ,{}", 1, 3.23);
|
std::string tag = "spdlog-android";
|
||||||
// debug from default logger
|
auto android_logger = spd::android_logger("android", tag);
|
||||||
SPDLOG_DEBUG("Some debug message.. {} ,{}", 1, 3.23);
|
android_logger->critical("Use \"adb shell logcat\" to view this message.");
|
||||||
|
#endif
|
||||||
// trace from logger object
|
|
||||||
auto logger = spdlog::get("file_logger");
|
|
||||||
SPDLOG_LOGGER_TRACE(logger, "another trace message");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// stopwatch example
|
// user defined types logging by implementing operator<<
|
||||||
#include "spdlog/stopwatch.h"
|
|
||||||
#include <thread>
|
|
||||||
void stopwatch_example()
|
|
||||||
{
|
|
||||||
spdlog::stopwatch sw;
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(123));
|
|
||||||
spdlog::info("Stopwatch: {} seconds", sw);
|
|
||||||
}
|
|
||||||
|
|
||||||
// A logger with multiple sinks (stdout and file) - each with a different format and log level.
|
|
||||||
void multi_sink_example()
|
|
||||||
{
|
|
||||||
auto console_sink = std::make_shared<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 types logging by implementing operator<<
|
|
||||||
#include "spdlog/fmt/ostr.h" // must be included
|
|
||||||
struct my_type
|
struct my_type
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
@@ -232,63 +145,18 @@ struct my_type
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#include "spdlog/fmt/ostr.h" // must be included
|
||||||
void user_defined_example()
|
void user_defined_example()
|
||||||
{
|
{
|
||||||
spdlog::info("user defined type: {}", my_type{14});
|
spd::get("console")->info("user defined type: {}", my_type{14});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Custom error handler. Will be triggered on log failure.
|
//
|
||||||
|
// custom error handler
|
||||||
|
//
|
||||||
void err_handler_example()
|
void err_handler_example()
|
||||||
{
|
{
|
||||||
// can be set globally or per logger(logger->set_error_handler(..))
|
// can be set globaly or per logger(logger->set_error_handler(..))
|
||||||
spdlog::set_error_handler([](const std::string &msg) { printf("*** Custom log error handler: %s ***\n", msg.c_str()); });
|
spdlog::set_error_handler([](const std::string &msg) { std::cerr << "my err handler: " << msg << std::endl; });
|
||||||
}
|
spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
|
||||||
|
|
||||||
// syslog example (linux/osx/freebsd)
|
|
||||||
#ifndef _WIN32
|
|
||||||
#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.");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Android example.
|
|
||||||
#if defined(__ANDROID__)
|
|
||||||
#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.");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Log patterns can contain custom flags.
|
|
||||||
// this will add custom 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 spdlog::details::make_unique<my_formatter_flag>();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void custom_flags_example()
|
|
||||||
{
|
|
||||||
|
|
||||||
using spdlog::details::make_unique; // for pre c++14
|
|
||||||
auto formatter = make_unique<spdlog::pattern_formatter>();
|
|
||||||
formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v");
|
|
||||||
spdlog::set_formatter(std::move(formatter));
|
|
||||||
}
|
}
|
||||||
|
110
example/example.sln
Normal file
110
example/example.sln
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio 15
|
||||||
|
VisualStudioVersion = 15.0.27428.2037
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "example", "example.vcxproj", "{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "spdlog", "spdlog", "{319A0767-E66D-4DD0-8BEF-E29E891BA836}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
..\include\spdlog\async_logger.h = ..\include\spdlog\async_logger.h
|
||||||
|
..\include\spdlog\common.h = ..\include\spdlog\common.h
|
||||||
|
..\include\spdlog\formatter.h = ..\include\spdlog\formatter.h
|
||||||
|
..\include\spdlog\logger.h = ..\include\spdlog\logger.h
|
||||||
|
..\include\spdlog\spdlog.h = ..\include\spdlog\spdlog.h
|
||||||
|
..\include\spdlog\tweakme.h = ..\include\spdlog\tweakme.h
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "contrib", "contrib", "{F1B153FB-7638-4F2B-B6FF-F56859D381F9}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
..\include\spdlog\contrib\README.md = ..\include\spdlog\contrib\README.md
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sinks", "sinks", "{9B3669F7-6D70-4699-9067-451E9298BA53}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
..\include\spdlog\contrib\sinks\.gitignore = ..\include\spdlog\contrib\sinks\.gitignore
|
||||||
|
..\include\spdlog\contrib\sinks\step_file_sink.h = ..\include\spdlog\contrib\sinks\step_file_sink.h
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "details", "details", "{579FDBF1-8FCD-4F1D-99F1-540F2EFF95E0}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
..\include\spdlog\details\async_log_helper.h = ..\include\spdlog\details\async_log_helper.h
|
||||||
|
..\include\spdlog\details\async_logger_impl.h = ..\include\spdlog\details\async_logger_impl.h
|
||||||
|
..\include\spdlog\details\file_helper.h = ..\include\spdlog\details\file_helper.h
|
||||||
|
..\include\spdlog\details\log_msg.h = ..\include\spdlog\details\log_msg.h
|
||||||
|
..\include\spdlog\details\logger_impl.h = ..\include\spdlog\details\logger_impl.h
|
||||||
|
..\include\spdlog\details\mpmc_blocking_q.h = ..\include\spdlog\details\mpmc_blocking_q.h
|
||||||
|
..\include\spdlog\details\null_mutex.h = ..\include\spdlog\details\null_mutex.h
|
||||||
|
..\include\spdlog\details\os.h = ..\include\spdlog\details\os.h
|
||||||
|
..\include\spdlog\details\pattern_formatter_impl.h = ..\include\spdlog\details\pattern_formatter_impl.h
|
||||||
|
..\include\spdlog\details\registry.h = ..\include\spdlog\details\registry.h
|
||||||
|
..\include\spdlog\details\spdlog_impl.h = ..\include\spdlog\details\spdlog_impl.h
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "fmt", "fmt", "{2034E575-9375-4AE7-B667-AF4A359F1483}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
..\include\spdlog\fmt\fmt.h = ..\include\spdlog\fmt\fmt.h
|
||||||
|
..\include\spdlog\fmt\ostr.h = ..\include\spdlog\fmt\ostr.h
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "bundled", "bundled", "{16763E99-3CC7-4C46-8F79-259356FF0B37}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
..\include\spdlog\fmt\bundled\format.cc = ..\include\spdlog\fmt\bundled\format.cc
|
||||||
|
..\include\spdlog\fmt\bundled\format.h = ..\include\spdlog\fmt\bundled\format.h
|
||||||
|
..\include\spdlog\fmt\bundled\LICENSE.rst = ..\include\spdlog\fmt\bundled\LICENSE.rst
|
||||||
|
..\include\spdlog\fmt\bundled\ostream.cc = ..\include\spdlog\fmt\bundled\ostream.cc
|
||||||
|
..\include\spdlog\fmt\bundled\ostream.h = ..\include\spdlog\fmt\bundled\ostream.h
|
||||||
|
..\include\spdlog\fmt\bundled\posix.cc = ..\include\spdlog\fmt\bundled\posix.cc
|
||||||
|
..\include\spdlog\fmt\bundled\posix.h = ..\include\spdlog\fmt\bundled\posix.h
|
||||||
|
..\include\spdlog\fmt\bundled\printf.cc = ..\include\spdlog\fmt\bundled\printf.cc
|
||||||
|
..\include\spdlog\fmt\bundled\printf.h = ..\include\spdlog\fmt\bundled\printf.h
|
||||||
|
..\include\spdlog\fmt\bundled\time.h = ..\include\spdlog\fmt\bundled\time.h
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sinks", "sinks", "{13310FA4-52E7-46BA-B071-B72B1D8E44D9}"
|
||||||
|
ProjectSection(SolutionItems) = preProject
|
||||||
|
..\include\spdlog\sinks\android_sink.h = ..\include\spdlog\sinks\android_sink.h
|
||||||
|
..\include\spdlog\sinks\ansicolor_sink.h = ..\include\spdlog\sinks\ansicolor_sink.h
|
||||||
|
..\include\spdlog\sinks\base_sink.h = ..\include\spdlog\sinks\base_sink.h
|
||||||
|
..\include\spdlog\sinks\dist_sink.h = ..\include\spdlog\sinks\dist_sink.h
|
||||||
|
..\include\spdlog\sinks\file_sinks.h = ..\include\spdlog\sinks\file_sinks.h
|
||||||
|
..\include\spdlog\sinks\msvc_sink.h = ..\include\spdlog\sinks\msvc_sink.h
|
||||||
|
..\include\spdlog\sinks\null_sink.h = ..\include\spdlog\sinks\null_sink.h
|
||||||
|
..\include\spdlog\sinks\ostream_sink.h = ..\include\spdlog\sinks\ostream_sink.h
|
||||||
|
..\include\spdlog\sinks\sink.h = ..\include\spdlog\sinks\sink.h
|
||||||
|
..\include\spdlog\sinks\stdout_sinks.h = ..\include\spdlog\sinks\stdout_sinks.h
|
||||||
|
..\include\spdlog\sinks\syslog_sink.h = ..\include\spdlog\sinks\syslog_sink.h
|
||||||
|
..\include\spdlog\sinks\wincolor_sink.h = ..\include\spdlog\sinks\wincolor_sink.h
|
||||||
|
..\include\spdlog\sinks\windebug_sink.h = ..\include\spdlog\sinks\windebug_sink.h
|
||||||
|
EndProjectSection
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Win32 = Debug|Win32
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Release|Win32 = Release|Win32
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||||
|
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|Win32.Build.0 = Debug|Win32
|
||||||
|
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Debug|x64.ActiveCfg = Debug|Win32
|
||||||
|
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.ActiveCfg = Release|Win32
|
||||||
|
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|Win32.Build.0 = Release|Win32
|
||||||
|
{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}.Release|x64.ActiveCfg = Release|Win32
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(NestedProjects) = preSolution
|
||||||
|
{F1B153FB-7638-4F2B-B6FF-F56859D381F9} = {319A0767-E66D-4DD0-8BEF-E29E891BA836}
|
||||||
|
{9B3669F7-6D70-4699-9067-451E9298BA53} = {F1B153FB-7638-4F2B-B6FF-F56859D381F9}
|
||||||
|
{579FDBF1-8FCD-4F1D-99F1-540F2EFF95E0} = {319A0767-E66D-4DD0-8BEF-E29E891BA836}
|
||||||
|
{2034E575-9375-4AE7-B667-AF4A359F1483} = {319A0767-E66D-4DD0-8BEF-E29E891BA836}
|
||||||
|
{16763E99-3CC7-4C46-8F79-259356FF0B37} = {2034E575-9375-4AE7-B667-AF4A359F1483}
|
||||||
|
{13310FA4-52E7-46BA-B071-B72B1D8E44D9} = {319A0767-E66D-4DD0-8BEF-E29E891BA836}
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
|
SolutionGuid = {8C12721F-513C-4922-B2F8-B9F2403412B4}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
93
example/example.vcxproj
Normal file
93
example/example.vcxproj
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<ItemGroup Label="ProjectConfigurations">
|
||||||
|
<ProjectConfiguration Include="Debug|Win32">
|
||||||
|
<Configuration>Debug</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
<ProjectConfiguration Include="Release|Win32">
|
||||||
|
<Configuration>Release</Configuration>
|
||||||
|
<Platform>Win32</Platform>
|
||||||
|
</ProjectConfiguration>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ClCompile Include="example.cpp" />
|
||||||
|
</ItemGroup>
|
||||||
|
<PropertyGroup Label="Globals">
|
||||||
|
<ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid>
|
||||||
|
<Keyword>Win32Proj</Keyword>
|
||||||
|
<RootNamespace>.</RootNamespace>
|
||||||
|
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>true</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v141</PlatformToolset>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||||
|
<ConfigurationType>Application</ConfigurationType>
|
||||||
|
<UseDebugLibraries>false</UseDebugLibraries>
|
||||||
|
<PlatformToolset>v141</PlatformToolset>
|
||||||
|
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||||
|
<CharacterSet>Unicode</CharacterSet>
|
||||||
|
</PropertyGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||||
|
<ImportGroup Label="ExtensionSettings">
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||||
|
</ImportGroup>
|
||||||
|
<PropertyGroup Label="UserMacros" />
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<LinkIncremental>true</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<LinkIncremental>false</LinkIncremental>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<Optimization>Disabled</Optimization>
|
||||||
|
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<PrecompiledHeaderFile />
|
||||||
|
<PrecompiledHeaderOutputFile />
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||||
|
<ClCompile>
|
||||||
|
<WarningLevel>Level3</WarningLevel>
|
||||||
|
<PrecompiledHeader>
|
||||||
|
</PrecompiledHeader>
|
||||||
|
<Optimization>MaxSpeed</Optimization>
|
||||||
|
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||||
|
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||||
|
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||||
|
<AdditionalIncludeDirectories>..\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||||
|
<PrecompiledHeaderFile />
|
||||||
|
<PrecompiledHeaderOutputFile />
|
||||||
|
</ClCompile>
|
||||||
|
<Link>
|
||||||
|
<SubSystem>Console</SubSystem>
|
||||||
|
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||||
|
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||||
|
<OptimizeReferences>true</OptimizeReferences>
|
||||||
|
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||||
|
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
|
||||||
|
</Link>
|
||||||
|
</ItemDefinitionGroup>
|
||||||
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
</ImportGroup>
|
||||||
|
</Project>
|
15
example/jni/Android.mk
Normal file
15
example/jni/Android.mk
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# Setup a project
|
||||||
|
LOCAL_PATH := $(call my-dir)
|
||||||
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
|
LOCAL_MODULE := example
|
||||||
|
LOCAL_SRC_FILES := example.cpp
|
||||||
|
LOCAL_CPPFLAGS += -Wall -Wshadow -Wextra -pedantic -std=c++11 -fPIE -pie
|
||||||
|
LOCAL_LDFLAGS += -fPIE -pie
|
||||||
|
|
||||||
|
# Add exception support and set path for spdlog's headers
|
||||||
|
LOCAL_CPPFLAGS += -fexceptions -I../include
|
||||||
|
# Use android's log library
|
||||||
|
LOCAL_LDFLAGS += -llog
|
||||||
|
|
||||||
|
include $(BUILD_EXECUTABLE)
|
2
example/jni/Application.mk
Normal file
2
example/jni/Application.mk
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Exceptions are used in spdlog. Link to an exception-ready C++ runtime.
|
||||||
|
APP_STL = gnustl_static
|
157
example/jni/example.cpp
Normal file
157
example/jni/example.cpp
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// spdlog usage example
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#define SPDLOG_TRACE_ON
|
||||||
|
#define SPDLOG_DEBUG_ON
|
||||||
|
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
void async_example();
|
||||||
|
void syslog_example();
|
||||||
|
void android_example();
|
||||||
|
void user_defined_example();
|
||||||
|
void err_handler_example();
|
||||||
|
|
||||||
|
namespace spd = spdlog;
|
||||||
|
int main(int, char *[])
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Console logger with color
|
||||||
|
auto console = spd::stdout_color_mt("console");
|
||||||
|
console->info("Welcome to spdlog!");
|
||||||
|
console->error("Some error message with arg{}..", 1);
|
||||||
|
|
||||||
|
// Formatting examples
|
||||||
|
console->warn("Easy padding in numbers like {:08d}", 12);
|
||||||
|
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
||||||
|
console->info("Support for floats {:03.2f}", 1.23456);
|
||||||
|
console->info("Positional args are {1} {0}..", "too", "supported");
|
||||||
|
console->info("{:<30}", "left aligned");
|
||||||
|
|
||||||
|
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
|
||||||
|
|
||||||
|
// Create basic file logger (not rotated)
|
||||||
|
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt");
|
||||||
|
my_logger->info("Some log message");
|
||||||
|
|
||||||
|
// Create a file rotating logger with 5mb size max and 3 rotated files
|
||||||
|
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
|
||||||
|
for (int i = 0; i < 10; ++i)
|
||||||
|
rotating_logger->info("{} * {} equals {:>10}", i, i, i * i);
|
||||||
|
|
||||||
|
// Create a daily logger - a new file is created every day on 2:30am
|
||||||
|
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
||||||
|
// trigger flush if the log severity is error or higher
|
||||||
|
daily_logger->flush_on(spd::level::err);
|
||||||
|
daily_logger->info(123.44);
|
||||||
|
|
||||||
|
// Customize msg format for all messages
|
||||||
|
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
|
||||||
|
rotating_logger->info("This is another message with custom format");
|
||||||
|
|
||||||
|
// Runtime log levels
|
||||||
|
spd::set_level(spd::level::info); // Set global log level to info
|
||||||
|
console->debug("This message should not be displayed!");
|
||||||
|
console->set_level(spd::level::debug); // Set specific logger's log level
|
||||||
|
console->debug("This message should be displayed..");
|
||||||
|
|
||||||
|
// Compile time log levels
|
||||||
|
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
|
||||||
|
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
|
||||||
|
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
|
||||||
|
|
||||||
|
// Asynchronous logging is very fast..
|
||||||
|
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
|
||||||
|
async_example();
|
||||||
|
|
||||||
|
// syslog example. linux/osx only
|
||||||
|
syslog_example();
|
||||||
|
|
||||||
|
// android example. compile with NDK
|
||||||
|
android_example();
|
||||||
|
|
||||||
|
// Log user-defined types example
|
||||||
|
user_defined_example();
|
||||||
|
|
||||||
|
// Change default log error handler
|
||||||
|
err_handler_example();
|
||||||
|
|
||||||
|
// Apply a function on all registered loggers
|
||||||
|
spd::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); });
|
||||||
|
|
||||||
|
// Release and close all loggers
|
||||||
|
spdlog::drop_all();
|
||||||
|
}
|
||||||
|
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
|
||||||
|
catch (const spd::spdlog_ex &ex)
|
||||||
|
{
|
||||||
|
std::cout << "Log init failed: " << ex.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void async_example()
|
||||||
|
{
|
||||||
|
size_t q_size = 4096; // queue size must be power of 2
|
||||||
|
spdlog::set_async_mode(q_size);
|
||||||
|
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
|
||||||
|
for (int i = 0; i < 100; ++i)
|
||||||
|
async_file->info("Async message #{}", i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// syslog example (linux/osx/freebsd)
|
||||||
|
void syslog_example()
|
||||||
|
{
|
||||||
|
#ifdef SPDLOG_ENABLE_SYSLOG
|
||||||
|
std::string ident = "spdlog-example";
|
||||||
|
auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID);
|
||||||
|
syslog_logger->warn("This is warning that will end up in syslog.");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// Android example
|
||||||
|
void android_example()
|
||||||
|
{
|
||||||
|
#if defined(__ANDROID__)
|
||||||
|
std::string tag = "spdlog-android";
|
||||||
|
auto android_logger = spd::android_logger("android", tag);
|
||||||
|
android_logger->critical("Use \"adb shell logcat\" to view this message.");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// user defined types logging by implementing operator<<
|
||||||
|
struct my_type
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
template<typename OStream>
|
||||||
|
friend OStream &operator<<(OStream &os, const my_type &c)
|
||||||
|
{
|
||||||
|
return os << "[my_type i=" << c.i << "]";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#include "spdlog/fmt/ostr.h" // must be included
|
||||||
|
void user_defined_example()
|
||||||
|
{
|
||||||
|
spd::get("console")->info("user defined type: {}", my_type{14});
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// custom error handler
|
||||||
|
//
|
||||||
|
void err_handler_example()
|
||||||
|
{
|
||||||
|
// can be set globaly or per logger(logger->set_error_handler(..))
|
||||||
|
spdlog::set_error_handler([](const std::string &msg) { std::cerr << "my err handler: " << msg << std::endl; });
|
||||||
|
spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
|
||||||
|
}
|
1
example/logs/.gitignore
vendored
Normal file
1
example/logs/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*.txt
|
46
example/multisink.cpp
Normal file
46
example/multisink.cpp
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace spd = spdlog;
|
||||||
|
int main(int, char *[])
|
||||||
|
{
|
||||||
|
bool enable_debug = true;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// This other example use a single logger with multiple sinks.
|
||||||
|
// This means that the same log_msg is forwarded to multiple sinks;
|
||||||
|
// Each sink can have it's own log level and a message will be logged.
|
||||||
|
std::vector<spdlog::sink_ptr> sinks;
|
||||||
|
sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_mt>());
|
||||||
|
sinks.push_back(std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_regular_file.txt"));
|
||||||
|
sinks.push_back(std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_debug_file.txt"));
|
||||||
|
|
||||||
|
spdlog::logger console_multisink("multisink", sinks.begin(), sinks.end());
|
||||||
|
console_multisink.set_level(spdlog::level::warn);
|
||||||
|
|
||||||
|
sinks[0]->set_level(spdlog::level::trace); // console. Allow everything. Default value
|
||||||
|
sinks[1]->set_level(spdlog::level::trace); // regular file. Allow everything. Default value
|
||||||
|
sinks[2]->set_level(spdlog::level::off); // regular file. Ignore everything.
|
||||||
|
|
||||||
|
console_multisink.warn("warn: will print only on console and regular file");
|
||||||
|
|
||||||
|
if (enable_debug)
|
||||||
|
{
|
||||||
|
console_multisink.set_level(spdlog::level::debug); // level of the logger
|
||||||
|
sinks[1]->set_level(spdlog::level::debug); // regular file
|
||||||
|
sinks[2]->set_level(spdlog::level::debug); // debug file
|
||||||
|
}
|
||||||
|
console_multisink.debug("Debug: you should see this on console and both files");
|
||||||
|
|
||||||
|
// Release and close all loggers
|
||||||
|
spdlog::drop_all();
|
||||||
|
}
|
||||||
|
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
|
||||||
|
catch (const spd::spdlog_ex &ex)
|
||||||
|
{
|
||||||
|
std::cout << "Log init failed: " << ex.what() << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
34
example/utils.h
Normal file
34
example/utils.h
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iomanip>
|
||||||
|
#include <locale>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace utils {
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline std::string format(const T &value)
|
||||||
|
{
|
||||||
|
static std::locale loc("");
|
||||||
|
std::stringstream ss;
|
||||||
|
ss.imbue(loc);
|
||||||
|
ss << value;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline std::string format(const double &value)
|
||||||
|
{
|
||||||
|
static std::locale loc("");
|
||||||
|
std::stringstream ss;
|
||||||
|
ss.imbue(loc);
|
||||||
|
ss << std::fixed << std::setprecision(1) << value;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace utils
|
9
format.sh
Executable file
9
format.sh
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo -n "Running dos2unix "
|
||||||
|
find . -name "*\.h" -o -name "*\.cpp"|xargs -I {} sh -c "dos2unix '{}' 2>/dev/null; echo -n '.'"
|
||||||
|
echo
|
||||||
|
echo -n "Running clang-format "
|
||||||
|
find . -name "*\.h" -o -name "*\.cpp"|xargs -I {} sh -c "clang-format -i {}; echo -n '.'"
|
||||||
|
echo
|
||||||
|
|
||||||
|
|
@@ -1,93 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
//
|
|
||||||
// Async logging using global thread pool
|
|
||||||
// All loggers created here share same global thread pool.
|
|
||||||
// Each log message is pushed to a queue along with a shared pointer to the
|
|
||||||
// logger.
|
|
||||||
// If a logger deleted while having pending messages in the queue, it's actual
|
|
||||||
// destruction will defer
|
|
||||||
// until all its messages are processed by the thread pool.
|
|
||||||
// This is because each message in the queue holds a shared_ptr to the
|
|
||||||
// originating logger.
|
|
||||||
|
|
||||||
#include <spdlog/async_logger.h>
|
|
||||||
#include <spdlog/details/registry.h>
|
|
||||||
#include <spdlog/details/thread_pool.h>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <mutex>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
namespace details {
|
|
||||||
static const size_t default_async_q_size = 8192;
|
|
||||||
}
|
|
||||||
|
|
||||||
// async logger factory - creates async loggers backed with thread pool.
|
|
||||||
// if a global thread pool doesn't already exist, create it with default queue
|
|
||||||
// size of 8192 items and single thread.
|
|
||||||
template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
|
|
||||||
struct async_factory_impl
|
|
||||||
{
|
|
||||||
template<typename Sink, typename... SinkArgs>
|
|
||||||
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&...args)
|
|
||||||
{
|
|
||||||
auto ®istry_inst = details::registry::instance();
|
|
||||||
|
|
||||||
// create global thread pool if not already exists..
|
|
||||||
|
|
||||||
auto &mutex = registry_inst.tp_mutex();
|
|
||||||
std::lock_guard<std::recursive_mutex> tp_lock(mutex);
|
|
||||||
auto tp = registry_inst.get_tp();
|
|
||||||
if (tp == nullptr)
|
|
||||||
{
|
|
||||||
tp = std::make_shared<details::thread_pool>(details::default_async_q_size, 1);
|
|
||||||
registry_inst.set_tp(tp);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
|
||||||
auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy);
|
|
||||||
registry_inst.initialize_logger(new_logger);
|
|
||||||
return new_logger;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
using async_factory = async_factory_impl<async_overflow_policy::block>;
|
|
||||||
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
|
|
||||||
|
|
||||||
template<typename Sink, typename... SinkArgs>
|
|
||||||
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&...sink_args)
|
|
||||||
{
|
|
||||||
return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename Sink, typename... SinkArgs>
|
|
||||||
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&...sink_args)
|
|
||||||
{
|
|
||||||
return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
// set global thread pool.
|
|
||||||
inline void init_thread_pool(size_t q_size, size_t thread_count, std::function<void()> on_thread_start)
|
|
||||||
{
|
|
||||||
auto tp = std::make_shared<details::thread_pool>(q_size, thread_count, on_thread_start);
|
|
||||||
details::registry::instance().set_tp(std::move(tp));
|
|
||||||
}
|
|
||||||
|
|
||||||
// set global thread pool.
|
|
||||||
inline void init_thread_pool(size_t q_size, size_t thread_count)
|
|
||||||
{
|
|
||||||
init_thread_pool(q_size, thread_count, [] {});
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the global thread pool.
|
|
||||||
inline std::shared_ptr<spdlog::details::thread_pool> thread_pool()
|
|
||||||
{
|
|
||||||
return details::registry::instance().get_tp();
|
|
||||||
}
|
|
||||||
} // namespace spdlog
|
|
@@ -1,92 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/async_logger.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/sinks/sink.h>
|
|
||||||
#include <spdlog/details/thread_pool.h>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
SPDLOG_INLINE spdlog::async_logger::async_logger(
|
|
||||||
std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
|
||||||
: async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), std::move(tp), overflow_policy)
|
|
||||||
{}
|
|
||||||
|
|
||||||
SPDLOG_INLINE spdlog::async_logger::async_logger(
|
|
||||||
std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
|
|
||||||
: async_logger(std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy)
|
|
||||||
{}
|
|
||||||
|
|
||||||
// send the log message to the thread pool
|
|
||||||
SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg)
|
|
||||||
{
|
|
||||||
if (auto pool_ptr = thread_pool_.lock())
|
|
||||||
{
|
|
||||||
pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw_spdlog_ex("async log: thread pool doesn't exist anymore");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// send flush request to the thread pool
|
|
||||||
SPDLOG_INLINE void spdlog::async_logger::flush_()
|
|
||||||
{
|
|
||||||
if (auto pool_ptr = thread_pool_.lock())
|
|
||||||
{
|
|
||||||
pool_ptr->post_flush(shared_from_this(), overflow_policy_);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw_spdlog_ex("async flush: thread pool doesn't exist anymore");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// backend functions - called from the thread pool to do the actual job
|
|
||||||
//
|
|
||||||
SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg &msg)
|
|
||||||
{
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
if (sink->should_log(msg.level))
|
|
||||||
{
|
|
||||||
SPDLOG_TRY
|
|
||||||
{
|
|
||||||
sink->log(msg);
|
|
||||||
}
|
|
||||||
SPDLOG_LOGGER_CATCH()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (should_flush_(msg))
|
|
||||||
{
|
|
||||||
backend_flush_();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void spdlog::async_logger::backend_flush_()
|
|
||||||
{
|
|
||||||
for (auto &sink : sinks_)
|
|
||||||
{
|
|
||||||
SPDLOG_TRY
|
|
||||||
{
|
|
||||||
sink->flush();
|
|
||||||
}
|
|
||||||
SPDLOG_LOGGER_CATCH()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE std::shared_ptr<spdlog::logger> spdlog::async_logger::clone(std::string new_name)
|
|
||||||
{
|
|
||||||
auto cloned = std::make_shared<spdlog::async_logger>(*this);
|
|
||||||
cloned->name_ = std::move(new_name);
|
|
||||||
return cloned;
|
|
||||||
}
|
|
@@ -1,68 +1,72 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Fast asynchronous logger.
|
// Very fast asynchronous logger (millions of logs per second on an average desktop)
|
||||||
// Uses pre allocated queue.
|
// Uses pre allocated lockfree queue for maximum throughput even under large number of threads.
|
||||||
// Creates a single back thread to pop messages from the queue and log them.
|
// Creates a single back thread to pop messages from the queue and log them.
|
||||||
//
|
//
|
||||||
// Upon each log write the logger:
|
// Upon each log write the logger:
|
||||||
// 1. Checks if its log level is enough to log the message
|
// 1. Checks if its log level is enough to log the message
|
||||||
// 2. Push a new copy of the message to a queue (or block the caller until
|
// 2. Push a new copy of the message to a queue (or block the caller until space is available in the queue)
|
||||||
// space is available in the queue)
|
// 3. will throw spdlog_ex upon log exceptions
|
||||||
// Upon destruction, logs all remaining messages in the queue before
|
// Upon destruction, logs all remaining messages in the queue before destructing..
|
||||||
// destructing..
|
|
||||||
|
|
||||||
#include <spdlog/logger.h>
|
#include "common.h"
|
||||||
|
#include "logger.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
|
|
||||||
// Async overflow policy - block by default.
|
|
||||||
enum class async_overflow_policy
|
|
||||||
{
|
|
||||||
block, // Block until message can be enqueued
|
|
||||||
overrun_oldest // Discard oldest message in the queue if full when trying to
|
|
||||||
// add new item.
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace details {
|
namespace details {
|
||||||
class thread_pool;
|
class async_log_helper;
|
||||||
}
|
}
|
||||||
|
|
||||||
class SPDLOG_API async_logger final : public std::enable_shared_from_this<async_logger>, public logger
|
class async_logger SPDLOG_FINAL : public logger
|
||||||
{
|
{
|
||||||
friend class details::thread_pool;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template<typename It>
|
template<class It>
|
||||||
async_logger(std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp,
|
async_logger(const std::string &logger_name, const It &begin, const It &end, size_t queue_size,
|
||||||
async_overflow_policy overflow_policy = async_overflow_policy::block)
|
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
|
||||||
: logger(std::move(logger_name), begin, end)
|
const std::function<void()> &worker_warmup_cb = nullptr,
|
||||||
, thread_pool_(std::move(tp))
|
const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(),
|
||||||
, overflow_policy_(overflow_policy)
|
const std::function<void()> &worker_teardown_cb = nullptr);
|
||||||
{}
|
|
||||||
|
|
||||||
async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp,
|
async_logger(const std::string &logger_name, sinks_init_list sinks, size_t queue_size,
|
||||||
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
|
||||||
|
const std::function<void()> &worker_warmup_cb = nullptr,
|
||||||
|
const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(),
|
||||||
|
const std::function<void()> &worker_teardown_cb = nullptr);
|
||||||
|
|
||||||
async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp,
|
async_logger(const std::string &logger_name, sink_ptr single_sink, size_t queue_size,
|
||||||
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
|
||||||
|
const std::function<void()> &worker_warmup_cb = nullptr,
|
||||||
|
const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(),
|
||||||
|
const std::function<void()> &worker_teardown_cb = nullptr);
|
||||||
|
|
||||||
std::shared_ptr<logger> clone(std::string new_name) override;
|
// Wait for the queue to be empty, and flush synchronously
|
||||||
|
// Warning: this can potentially last forever as we wait it to complete
|
||||||
|
void flush() override;
|
||||||
|
|
||||||
|
// Error handler
|
||||||
|
void set_error_handler(log_err_handler) override;
|
||||||
|
log_err_handler error_handler() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void sink_it_(const details::log_msg &msg) override;
|
void _sink_it(details::log_msg &msg) override;
|
||||||
void flush_() override;
|
void _set_formatter(spdlog::formatter_ptr msg_formatter) override;
|
||||||
void backend_sink_it_(const details::log_msg &incoming_log_msg);
|
void _set_pattern(const std::string &pattern, pattern_time_type pattern_time) override;
|
||||||
void backend_flush_();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::weak_ptr<details::thread_pool> thread_pool_;
|
std::unique_ptr<details::async_log_helper> _async_log_helper;
|
||||||
async_overflow_policy overflow_policy_;
|
|
||||||
};
|
};
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
#include "details/async_logger_impl.h"
|
||||||
#include "async_logger-inl.h"
|
|
||||||
#endif
|
|
||||||
|
@@ -1,44 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <spdlog/cfg/helpers.h>
|
|
||||||
#include <spdlog/details/registry.h>
|
|
||||||
|
|
||||||
//
|
|
||||||
// Init log levels using each argv entry that starts with "SPDLOG_LEVEL="
|
|
||||||
//
|
|
||||||
// set all loggers to debug level:
|
|
||||||
// example.exe "SPDLOG_LEVEL=debug"
|
|
||||||
|
|
||||||
// set logger1 to trace level
|
|
||||||
// example.exe "SPDLOG_LEVEL=logger1=trace"
|
|
||||||
|
|
||||||
// turn off all logging except for logger1 and logger2:
|
|
||||||
// example.exe "SPDLOG_LEVEL=off,logger1=debug,logger2=info"
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace cfg {
|
|
||||||
|
|
||||||
// search for SPDLOG_LEVEL= in the args and use it to init the levels
|
|
||||||
inline void load_argv_levels(int argc, const char **argv)
|
|
||||||
{
|
|
||||||
const std::string spdlog_level_prefix = "SPDLOG_LEVEL=";
|
|
||||||
for (int i = 1; i < argc; i++)
|
|
||||||
{
|
|
||||||
std::string arg = argv[i];
|
|
||||||
if (arg.find(spdlog_level_prefix) == 0)
|
|
||||||
{
|
|
||||||
auto levels_string = arg.substr(spdlog_level_prefix.size());
|
|
||||||
helpers::load_levels(levels_string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void load_argv_levels(int argc, char **argv)
|
|
||||||
{
|
|
||||||
load_argv_levels(argc, const_cast<const char **>(argv));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace cfg
|
|
||||||
} // namespace spdlog
|
|
@@ -1,38 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
#include <spdlog/cfg/helpers.h>
|
|
||||||
#include <spdlog/details/registry.h>
|
|
||||||
#include <spdlog/details/os.h>
|
|
||||||
|
|
||||||
//
|
|
||||||
// Init levels and patterns from env variables SPDLOG_LEVEL
|
|
||||||
// Inspired from Rust's "env_logger" crate (https://crates.io/crates/env_logger).
|
|
||||||
// Note - fallback to "info" level on unrecognized levels
|
|
||||||
//
|
|
||||||
// Examples:
|
|
||||||
//
|
|
||||||
// set global level to debug:
|
|
||||||
// export SPDLOG_LEVEL=debug
|
|
||||||
//
|
|
||||||
// turn off all logging except for logger1:
|
|
||||||
// export SPDLOG_LEVEL="*=off,logger1=debug"
|
|
||||||
//
|
|
||||||
|
|
||||||
// turn off all logging except for logger1 and logger2:
|
|
||||||
// export SPDLOG_LEVEL="off,logger1=debug,logger2=info"
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace cfg {
|
|
||||||
inline void load_env_levels()
|
|
||||||
{
|
|
||||||
auto env_val = details::os::getenv("SPDLOG_LEVEL");
|
|
||||||
if (!env_val.empty())
|
|
||||||
{
|
|
||||||
helpers::load_levels(env_val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace cfg
|
|
||||||
} // namespace spdlog
|
|
@@ -1,120 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/cfg/helpers.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/spdlog.h>
|
|
||||||
#include <spdlog/details/os.h>
|
|
||||||
#include <spdlog/details/registry.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <string>
|
|
||||||
#include <utility>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace cfg {
|
|
||||||
namespace helpers {
|
|
||||||
|
|
||||||
// inplace convert to lowercase
|
|
||||||
inline std::string &to_lower_(std::string &str)
|
|
||||||
{
|
|
||||||
std::transform(
|
|
||||||
str.begin(), str.end(), str.begin(), [](char ch) { return static_cast<char>((ch >= 'A' && ch <= 'Z') ? ch + ('a' - 'A') : ch); });
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
// inplace trim spaces
|
|
||||||
inline std::string &trim_(std::string &str)
|
|
||||||
{
|
|
||||||
const char *spaces = " \n\r\t";
|
|
||||||
str.erase(str.find_last_not_of(spaces) + 1);
|
|
||||||
str.erase(0, str.find_first_not_of(spaces));
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
|
|
||||||
// return (name,value) trimmed pair from given "name=value" string.
|
|
||||||
// return empty string on missing parts
|
|
||||||
// "key=val" => ("key", "val")
|
|
||||||
// " key = val " => ("key", "val")
|
|
||||||
// "key=" => ("key", "")
|
|
||||||
// "val" => ("", "val")
|
|
||||||
|
|
||||||
inline std::pair<std::string, std::string> extract_kv_(char sep, const std::string &str)
|
|
||||||
{
|
|
||||||
auto n = str.find(sep);
|
|
||||||
std::string k, v;
|
|
||||||
if (n == std::string::npos)
|
|
||||||
{
|
|
||||||
v = str;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
k = str.substr(0, n);
|
|
||||||
v = str.substr(n + 1);
|
|
||||||
}
|
|
||||||
return std::make_pair(trim_(k), trim_(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
// return vector of key/value pairs from sequence of "K1=V1,K2=V2,.."
|
|
||||||
// "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...}
|
|
||||||
inline std::unordered_map<std::string, std::string> extract_key_vals_(const std::string &str)
|
|
||||||
{
|
|
||||||
std::string token;
|
|
||||||
std::istringstream token_stream(str);
|
|
||||||
std::unordered_map<std::string, std::string> rv{};
|
|
||||||
while (std::getline(token_stream, token, ','))
|
|
||||||
{
|
|
||||||
if (token.empty())
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
auto kv = extract_kv_('=', token);
|
|
||||||
rv[kv.first] = kv.second;
|
|
||||||
}
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void load_levels(const std::string &input)
|
|
||||||
{
|
|
||||||
if (input.empty() || input.size() > 512)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto key_vals = extract_key_vals_(input);
|
|
||||||
std::unordered_map<std::string, level::level_enum> levels;
|
|
||||||
level::level_enum global_level = level::info;
|
|
||||||
bool global_level_found = false;
|
|
||||||
|
|
||||||
for (auto &name_level : key_vals)
|
|
||||||
{
|
|
||||||
auto &logger_name = name_level.first;
|
|
||||||
auto level_name = to_lower_(name_level.second);
|
|
||||||
auto level = level::from_str(level_name);
|
|
||||||
// ignore unrecognized level names
|
|
||||||
if (level == level::off && level_name != "off")
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (logger_name.empty()) // no logger name indicate global level
|
|
||||||
{
|
|
||||||
global_level_found = true;
|
|
||||||
global_level = level;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
levels[logger_name] = level;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
details::registry::instance().set_levels(std::move(levels), global_level_found ? &global_level : nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace helpers
|
|
||||||
} // namespace cfg
|
|
||||||
} // namespace spdlog
|
|
@@ -1,29 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace cfg {
|
|
||||||
namespace helpers {
|
|
||||||
//
|
|
||||||
// Init levels from given string
|
|
||||||
//
|
|
||||||
// Examples:
|
|
||||||
//
|
|
||||||
// set global level to debug: "debug"
|
|
||||||
// turn off all logging except for logger1: "off,logger1=debug"
|
|
||||||
// turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info"
|
|
||||||
//
|
|
||||||
SPDLOG_API void load_levels(const std::string &txt);
|
|
||||||
} // namespace helpers
|
|
||||||
|
|
||||||
} // namespace cfg
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "helpers-inl.h"
|
|
||||||
#endif // SPDLOG_HEADER_ONLY
|
|
@@ -1,80 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace level {
|
|
||||||
|
|
||||||
#if __cplusplus >= 201703L
|
|
||||||
constexpr
|
|
||||||
#endif
|
|
||||||
static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
|
|
||||||
|
|
||||||
static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES;
|
|
||||||
|
|
||||||
SPDLOG_INLINE const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
return level_string_views[l];
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
return short_level_names[l];
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
int level = 0;
|
|
||||||
for (const auto &level_str : level_string_views)
|
|
||||||
{
|
|
||||||
if (level_str == name)
|
|
||||||
{
|
|
||||||
return static_cast<level::level_enum>(level);
|
|
||||||
}
|
|
||||||
level++;
|
|
||||||
}
|
|
||||||
// check also for "warn" and "err" before giving up..
|
|
||||||
if (name == "warn")
|
|
||||||
{
|
|
||||||
return level::warn;
|
|
||||||
}
|
|
||||||
if (name == "err")
|
|
||||||
{
|
|
||||||
return level::err;
|
|
||||||
}
|
|
||||||
return level::off;
|
|
||||||
}
|
|
||||||
} // namespace level
|
|
||||||
|
|
||||||
SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg)
|
|
||||||
: msg_(std::move(msg))
|
|
||||||
{}
|
|
||||||
|
|
||||||
SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno)
|
|
||||||
{
|
|
||||||
memory_buf_t outbuf;
|
|
||||||
fmt::format_system_error(outbuf, last_errno, msg);
|
|
||||||
msg_ = fmt::to_string(outbuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
return msg_.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno)
|
|
||||||
{
|
|
||||||
SPDLOG_THROW(spdlog_ex(msg, last_errno));
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void throw_spdlog_ex(std::string msg)
|
|
||||||
{
|
|
||||||
SPDLOG_THROW(spdlog_ex(std::move(msg)));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace spdlog
|
|
@@ -1,49 +1,46 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <spdlog/tweakme.h>
|
#define SPDLOG_VERSION "0.17.0"
|
||||||
#include <spdlog/details/null_mutex.h>
|
|
||||||
|
#include "tweakme.h"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <exception>
|
||||||
|
#include <functional>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <exception>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <unordered_map>
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
#ifdef SPDLOG_COMPILED_LIB
|
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
#undef SPDLOG_HEADER_ONLY
|
#include <codecvt>
|
||||||
#if defined(_WIN32) && defined(SPDLOG_SHARED_LIB)
|
#include <locale>
|
||||||
#ifdef spdlog_EXPORTS
|
|
||||||
#define SPDLOG_API __declspec(dllexport)
|
|
||||||
#else
|
|
||||||
#define SPDLOG_API __declspec(dllimport)
|
|
||||||
#endif
|
#endif
|
||||||
#else // !defined(_WIN32) || !defined(SPDLOG_SHARED_LIB)
|
|
||||||
#define SPDLOG_API
|
|
||||||
#endif
|
|
||||||
#define SPDLOG_INLINE
|
|
||||||
#else // !defined(SPDLOG_COMPILED_LIB)
|
|
||||||
#define SPDLOG_API
|
|
||||||
#define SPDLOG_HEADER_ONLY
|
|
||||||
#define SPDLOG_INLINE inline
|
|
||||||
#endif // #ifdef SPDLOG_COMPILED_LIB
|
|
||||||
|
|
||||||
#include <spdlog/fmt/fmt.h>
|
#include "details/null_mutex.h"
|
||||||
|
|
||||||
// visual studio upto 2013 does not support noexcept nor constexpr
|
// visual studio upto 2013 does not support noexcept nor constexpr
|
||||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||||
#define SPDLOG_NOEXCEPT _NOEXCEPT
|
#define SPDLOG_NOEXCEPT throw()
|
||||||
#define SPDLOG_CONSTEXPR
|
#define SPDLOG_CONSTEXPR
|
||||||
#else
|
#else
|
||||||
#define SPDLOG_NOEXCEPT noexcept
|
#define SPDLOG_NOEXCEPT noexcept
|
||||||
#define SPDLOG_CONSTEXPR constexpr
|
#define SPDLOG_CONSTEXPR constexpr
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// final keyword support. On by default. See tweakme.h
|
||||||
|
#if defined(SPDLOG_NO_FINAL)
|
||||||
|
#define SPDLOG_FINAL
|
||||||
|
#else
|
||||||
|
#define SPDLOG_FINAL final
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(__GNUC__) || defined(__clang__)
|
#if defined(__GNUC__) || defined(__clang__)
|
||||||
#define SPDLOG_DEPRECATED __attribute__((deprecated))
|
#define SPDLOG_DEPRECATED __attribute__((deprecated))
|
||||||
#elif defined(_MSC_VER)
|
#elif defined(_MSC_VER)
|
||||||
@@ -52,31 +49,7 @@
|
|||||||
#define SPDLOG_DEPRECATED
|
#define SPDLOG_DEPRECATED
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// disable thread local on msvc 2013
|
#include "fmt/fmt.h"
|
||||||
#ifndef SPDLOG_NO_TLS
|
|
||||||
#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
|
|
||||||
#define SPDLOG_NO_TLS 1
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef SPDLOG_FUNCTION
|
|
||||||
#define SPDLOG_FUNCTION static_cast<const char *>(__FUNCTION__)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef SPDLOG_NO_EXCEPTIONS
|
|
||||||
#define SPDLOG_TRY
|
|
||||||
#define SPDLOG_THROW(ex) \
|
|
||||||
do \
|
|
||||||
{ \
|
|
||||||
printf("spdlog fatal error: %s\n", ex.what()); \
|
|
||||||
std::abort(); \
|
|
||||||
} while (0)
|
|
||||||
#define SPDLOG_CATCH_ALL()
|
|
||||||
#else
|
|
||||||
#define SPDLOG_TRY try
|
|
||||||
#define SPDLOG_THROW(ex) throw(ex)
|
|
||||||
#define SPDLOG_CATCH_ALL() catch (...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
|
|
||||||
@@ -86,69 +59,29 @@ namespace sinks {
|
|||||||
class sink;
|
class sink;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
|
||||||
using filename_t = std::wstring;
|
|
||||||
// allow macro expansion to occur in SPDLOG_FILENAME_T
|
|
||||||
#define SPDLOG_FILENAME_T_INNER(s) L##s
|
|
||||||
#define SPDLOG_FILENAME_T(s) SPDLOG_FILENAME_T_INNER(s)
|
|
||||||
#else
|
|
||||||
using filename_t = std::string;
|
|
||||||
#define SPDLOG_FILENAME_T(s) s
|
|
||||||
#endif
|
|
||||||
|
|
||||||
using log_clock = std::chrono::system_clock;
|
using log_clock = std::chrono::system_clock;
|
||||||
using sink_ptr = std::shared_ptr<sinks::sink>;
|
using sink_ptr = std::shared_ptr<sinks::sink>;
|
||||||
using sinks_init_list = std::initializer_list<sink_ptr>;
|
using sinks_init_list = std::initializer_list<sink_ptr>;
|
||||||
using err_handler = std::function<void(const std::string &err_msg)>;
|
using formatter_ptr = std::shared_ptr<spdlog::formatter>;
|
||||||
using string_view_t = fmt::basic_string_view<char>;
|
|
||||||
using wstring_view_t = fmt::basic_string_view<wchar_t>;
|
|
||||||
using memory_buf_t = fmt::basic_memory_buffer<char, 250>;
|
|
||||||
using wmemory_buf_t = fmt::basic_memory_buffer<wchar_t, 250>;
|
|
||||||
|
|
||||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
|
||||||
#ifndef _WIN32
|
|
||||||
#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
|
|
||||||
#else
|
|
||||||
template<typename T>
|
|
||||||
struct is_convertible_to_wstring_view : std::is_convertible<T, wstring_view_t>
|
|
||||||
{};
|
|
||||||
#endif // _WIN32
|
|
||||||
#else
|
|
||||||
template<typename>
|
|
||||||
struct is_convertible_to_wstring_view : std::false_type
|
|
||||||
{};
|
|
||||||
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
|
||||||
|
|
||||||
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
|
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
|
||||||
using level_t = details::null_atomic_int;
|
using level_t = details::null_atomic_int;
|
||||||
#else
|
#else
|
||||||
using level_t = std::atomic<int>;
|
using level_t = std::atomic<int>;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define SPDLOG_LEVEL_TRACE 0
|
using log_err_handler = std::function<void(const std::string &err_msg)>;
|
||||||
#define SPDLOG_LEVEL_DEBUG 1
|
|
||||||
#define SPDLOG_LEVEL_INFO 2
|
|
||||||
#define SPDLOG_LEVEL_WARN 3
|
|
||||||
#define SPDLOG_LEVEL_ERROR 4
|
|
||||||
#define SPDLOG_LEVEL_CRITICAL 5
|
|
||||||
#define SPDLOG_LEVEL_OFF 6
|
|
||||||
|
|
||||||
#if !defined(SPDLOG_ACTIVE_LEVEL)
|
|
||||||
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Log level enum
|
// Log level enum
|
||||||
namespace level {
|
namespace level {
|
||||||
enum level_enum
|
enum level_enum
|
||||||
{
|
{
|
||||||
trace = SPDLOG_LEVEL_TRACE,
|
trace = 0,
|
||||||
debug = SPDLOG_LEVEL_DEBUG,
|
debug = 1,
|
||||||
info = SPDLOG_LEVEL_INFO,
|
info = 2,
|
||||||
warn = SPDLOG_LEVEL_WARN,
|
warn = 3,
|
||||||
err = SPDLOG_LEVEL_ERROR,
|
err = 4,
|
||||||
critical = SPDLOG_LEVEL_CRITICAL,
|
critical = 5,
|
||||||
off = SPDLOG_LEVEL_OFF,
|
off = 6
|
||||||
n_levels
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#if !defined(SPDLOG_LEVEL_NAMES)
|
#if !defined(SPDLOG_LEVEL_NAMES)
|
||||||
@@ -157,29 +90,44 @@ enum level_enum
|
|||||||
"trace", "debug", "info", "warning", "error", "critical", "off" \
|
"trace", "debug", "info", "warning", "error", "critical", "off" \
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
static const char *level_names[] SPDLOG_LEVEL_NAMES;
|
||||||
|
|
||||||
#if !defined(SPDLOG_SHORT_LEVEL_NAMES)
|
static const char *short_level_names[]{"T", "D", "I", "W", "E", "C", "O"};
|
||||||
|
|
||||||
#define SPDLOG_SHORT_LEVEL_NAMES \
|
inline const char *to_str(spdlog::level::level_enum l)
|
||||||
{ \
|
{
|
||||||
"T", "D", "I", "W", "E", "C", "O" \
|
return level_names[l];
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
SPDLOG_API const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
inline const char *to_short_str(spdlog::level::level_enum l)
|
||||||
SPDLOG_API const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT;
|
{
|
||||||
SPDLOG_API spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT;
|
return short_level_names[l];
|
||||||
|
}
|
||||||
|
inline spdlog::level::level_enum from_str(const std::string &name)
|
||||||
|
{
|
||||||
|
static std::unordered_map<std::string, level_enum> name_to_level = // map string->level
|
||||||
|
{{level_names[0], level::trace}, // trace
|
||||||
|
{level_names[1], level::debug}, // debug
|
||||||
|
{level_names[2], level::info}, // info
|
||||||
|
{level_names[3], level::warn}, // warn
|
||||||
|
{level_names[4], level::err}, // err
|
||||||
|
{level_names[5], level::critical}, // critical
|
||||||
|
{level_names[6], level::off}}; // off
|
||||||
|
|
||||||
|
auto lvl_it = name_to_level.find(name);
|
||||||
|
return lvl_it != name_to_level.end() ? lvl_it->second : level::off;
|
||||||
|
}
|
||||||
|
|
||||||
|
using level_hasher = std::hash<int>;
|
||||||
} // namespace level
|
} // namespace level
|
||||||
|
|
||||||
//
|
//
|
||||||
// Color mode used by sinks with color support.
|
// Async overflow policy - block by default.
|
||||||
//
|
//
|
||||||
enum class color_mode
|
enum class async_overflow_policy
|
||||||
{
|
{
|
||||||
always,
|
block_retry, // Block / yield / sleep until message can be enqueued
|
||||||
automatic,
|
discard_log_msg // Discard the message it enqueue fails
|
||||||
never
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -195,54 +143,40 @@ enum class pattern_time_type
|
|||||||
//
|
//
|
||||||
// Log exception
|
// Log exception
|
||||||
//
|
//
|
||||||
class SPDLOG_API spdlog_ex : public std::exception
|
namespace details {
|
||||||
|
namespace os {
|
||||||
|
std::string errno_str(int err_num);
|
||||||
|
}
|
||||||
|
} // namespace details
|
||||||
|
class spdlog_ex : public std::exception
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit spdlog_ex(std::string msg);
|
explicit spdlog_ex(std::string msg)
|
||||||
spdlog_ex(const std::string &msg, int last_errno);
|
: _msg(std::move(msg))
|
||||||
const char *what() const SPDLOG_NOEXCEPT override;
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
spdlog_ex(const std::string &msg, int last_errno)
|
||||||
|
{
|
||||||
|
_msg = msg + ": " + details::os::errno_str(last_errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *what() const SPDLOG_NOEXCEPT override
|
||||||
|
{
|
||||||
|
return _msg.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string msg_;
|
std::string _msg;
|
||||||
};
|
};
|
||||||
|
|
||||||
[[noreturn]] SPDLOG_API void throw_spdlog_ex(const std::string &msg, int last_errno);
|
//
|
||||||
[[noreturn]] SPDLOG_API void throw_spdlog_ex(std::string msg);
|
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
|
||||||
|
//
|
||||||
struct source_loc
|
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||||
{
|
using filename_t = std::wstring;
|
||||||
SPDLOG_CONSTEXPR source_loc() = default;
|
|
||||||
SPDLOG_CONSTEXPR source_loc(const char *filename_in, int line_in, const char *funcname_in)
|
|
||||||
: filename{filename_in}
|
|
||||||
, line{line_in}
|
|
||||||
, funcname{funcname_in}
|
|
||||||
{}
|
|
||||||
|
|
||||||
SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
return line == 0;
|
|
||||||
}
|
|
||||||
const char *filename{nullptr};
|
|
||||||
int line{0};
|
|
||||||
const char *funcname{nullptr};
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace details {
|
|
||||||
// make_unique support for pre c++14
|
|
||||||
|
|
||||||
#if __cplusplus >= 201402L // C++14 and beyond
|
|
||||||
using std::make_unique;
|
|
||||||
#else
|
#else
|
||||||
template<typename T, typename... Args>
|
using filename_t = std::string;
|
||||||
std::unique_ptr<T> make_unique(Args &&...args)
|
|
||||||
{
|
|
||||||
static_assert(!std::is_array<T>::value, "arrays not supported");
|
|
||||||
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
} // namespace spdlog
|
||||||
#include "common-inl.h"
|
|
||||||
#endif
|
|
||||||
|
1
include/spdlog/contrib/README.md
Normal file
1
include/spdlog/contrib/README.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Please put here your contribs. Popular contribs will be moved to main tree after stablization
|
1
include/spdlog/contrib/sinks/.gitignore
vendored
Normal file
1
include/spdlog/contrib/sinks/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
|
163
include/spdlog/contrib/sinks/step_file_sink.h
Normal file
163
include/spdlog/contrib/sinks/step_file_sink.h
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../../details/file_helper.h"
|
||||||
|
#include "../../details/null_mutex.h"
|
||||||
|
#include "../../fmt/fmt.h"
|
||||||
|
#include "../../sinks/base_sink.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <ctime>
|
||||||
|
#include <mutex>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// Example for spdlog.h
|
||||||
|
//
|
||||||
|
// Create a file logger which creates new files with a specified time step and fixed file size:
|
||||||
|
//
|
||||||
|
// std::shared_ptr<logger> step_logger_mt(const std::string &logger_name, const filename_t &filename, unsigned seconds = 60, const
|
||||||
|
// filename_t &tmp_ext = ".tmp", unsigned max_file_size = std::numeric_limits<unsigned>::max()); std::shared_ptr<logger>
|
||||||
|
// step_logger_st(const std::string &logger_name, const filename_t &filename, unsigned seconds = 60, const filename_t &tmp_ext = ".tmp",
|
||||||
|
// unsigned max_file_size = std::numeric_limits<unsigned>::max());
|
||||||
|
|
||||||
|
// Example for spdlog_impl.h
|
||||||
|
// Create a file logger that creates new files with a specified increment
|
||||||
|
// inline std::shared_ptr<spdlog::logger> spdlog::step_logger_mt(
|
||||||
|
// const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, const filename_t &tmp_ext, unsigned max_file_size)
|
||||||
|
// {
|
||||||
|
// return create<spdlog::sinks::step_file_sink_mt>(logger_name, filename_fmt, seconds, tmp_ext, max_file_size);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// inline std::shared_ptr<spdlog::logger> spdlog::step_logger_st(
|
||||||
|
// const std::string &logger_name, const filename_t &filename_fmt, unsigned seconds, const filename_t &tmp_ext, unsigned max_file_size)
|
||||||
|
// {
|
||||||
|
// return create<spdlog::sinks::step_file_sink_st>(logger_name, filename_fmt, seconds, tmp_ext, max_file_size);
|
||||||
|
// }
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Default generator of step log file names.
|
||||||
|
*/
|
||||||
|
struct default_step_file_name_calculator
|
||||||
|
{
|
||||||
|
// Create filename for the form filename_YYYY-MM-DD_hh-mm-ss.ext
|
||||||
|
static std::tuple<filename_t, filename_t> calc_filename(const filename_t &filename, const filename_t &tmp_ext)
|
||||||
|
{
|
||||||
|
std::tm tm = spdlog::details::os::localtime();
|
||||||
|
filename_t basename, ext;
|
||||||
|
std::tie(basename, ext) = details::file_helper::split_by_extenstion(filename);
|
||||||
|
std::conditional<std::is_same<filename_t::value_type, char>::value, fmt::MemoryWriter, fmt::WMemoryWriter>::type w;
|
||||||
|
w.write(SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}_{:02d}-{:02d}-{:02d}{}"), basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
||||||
|
tm.tm_hour, tm.tm_min, tm.tm_sec, tmp_ext);
|
||||||
|
return std::make_tuple(w.str(), ext);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rotating file sink based on size and a specified time step
|
||||||
|
*/
|
||||||
|
template<class Mutex, class FileNameCalc = default_step_file_name_calculator>
|
||||||
|
class step_file_sink SPDLOG_FINAL : public base_sink<Mutex>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
step_file_sink(filename_t base_filename, unsigned step_seconds, filename_t tmp_ext, unsigned max_size)
|
||||||
|
: _base_filename(std::move(base_filename))
|
||||||
|
, _tmp_ext(std::move(tmp_ext))
|
||||||
|
, _step_seconds(step_seconds)
|
||||||
|
, _max_size(max_size)
|
||||||
|
{
|
||||||
|
if (step_seconds == 0)
|
||||||
|
{
|
||||||
|
throw spdlog_ex("step_file_sink: Invalid time step in ctor");
|
||||||
|
}
|
||||||
|
if (max_size == 0)
|
||||||
|
{
|
||||||
|
throw spdlog_ex("step_file_sink: Invalid max log size in ctor");
|
||||||
|
}
|
||||||
|
|
||||||
|
_tp = _next_tp();
|
||||||
|
std::tie(_current_filename, _ext) = FileNameCalc::calc_filename(_base_filename, _tmp_ext);
|
||||||
|
|
||||||
|
if (_tmp_ext == _ext)
|
||||||
|
{
|
||||||
|
throw spdlog_ex("step_file_sink: The temporary extension matches the specified in ctor");
|
||||||
|
}
|
||||||
|
|
||||||
|
_file_helper.open(_current_filename);
|
||||||
|
_current_size = _file_helper.size(); // expensive. called only once
|
||||||
|
}
|
||||||
|
|
||||||
|
~step_file_sink()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
close_current_file();
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void _sink_it(const details::log_msg &msg) override
|
||||||
|
{
|
||||||
|
_current_size += msg.formatted.size();
|
||||||
|
if (std::chrono::system_clock::now() >= _tp || _current_size > _max_size)
|
||||||
|
{
|
||||||
|
close_current_file();
|
||||||
|
|
||||||
|
std::tie(_current_filename, std::ignore) = FileNameCalc::calc_filename(_base_filename, _tmp_ext);
|
||||||
|
_file_helper.open(_current_filename);
|
||||||
|
_tp = _next_tp();
|
||||||
|
_current_size = msg.formatted.size();
|
||||||
|
}
|
||||||
|
_file_helper.write(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _flush() override
|
||||||
|
{
|
||||||
|
_file_helper.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::chrono::system_clock::time_point _next_tp()
|
||||||
|
{
|
||||||
|
return std::chrono::system_clock::now() + _step_seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
void close_current_file()
|
||||||
|
{
|
||||||
|
using details::os::filename_to_str;
|
||||||
|
|
||||||
|
filename_t src = _current_filename, target;
|
||||||
|
std::tie(target, std::ignore) = details::file_helper::split_by_extenstion(src);
|
||||||
|
target += _ext;
|
||||||
|
|
||||||
|
if (details::file_helper::file_exists(src) && details::os::rename(src, target) != 0)
|
||||||
|
{
|
||||||
|
throw spdlog_ex("step_file_sink: failed renaming " + filename_to_str(src) + " to " + filename_to_str(target), errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filename_t _base_filename;
|
||||||
|
const filename_t _tmp_ext;
|
||||||
|
const std::chrono::seconds _step_seconds;
|
||||||
|
const unsigned _max_size;
|
||||||
|
|
||||||
|
std::chrono::system_clock::time_point _tp;
|
||||||
|
filename_t _current_filename;
|
||||||
|
filename_t _ext;
|
||||||
|
unsigned _current_size;
|
||||||
|
|
||||||
|
details::file_helper _file_helper;
|
||||||
|
};
|
||||||
|
|
||||||
|
using step_file_sink_mt = step_file_sink<std::mutex>;
|
||||||
|
using step_file_sink_st = step_file_sink<details::null_mutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
342
include/spdlog/details/async_log_helper.h
Normal file
342
include/spdlog/details/async_log_helper.h
Normal file
@@ -0,0 +1,342 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
// async log helper :
|
||||||
|
// Process logs asynchronously using a back thread.
|
||||||
|
//
|
||||||
|
// If the internal queue of log messages reaches its max size,
|
||||||
|
// then the client call will block until there is more room.
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../common.h"
|
||||||
|
#include "../details/log_msg.h"
|
||||||
|
#include "../details/mpmc_blocking_q.h"
|
||||||
|
#include "../details/os.h"
|
||||||
|
#include "../formatter.h"
|
||||||
|
#include "../sinks/sink.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <exception>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
class async_log_helper
|
||||||
|
{
|
||||||
|
// Async msg to move to/from the queue
|
||||||
|
// Movable only. should never be copied
|
||||||
|
enum class async_msg_type
|
||||||
|
{
|
||||||
|
log,
|
||||||
|
flush,
|
||||||
|
terminate
|
||||||
|
};
|
||||||
|
|
||||||
|
struct async_msg
|
||||||
|
{
|
||||||
|
level::level_enum level;
|
||||||
|
log_clock::time_point time;
|
||||||
|
size_t thread_id;
|
||||||
|
std::string txt;
|
||||||
|
async_msg_type msg_type;
|
||||||
|
size_t msg_id;
|
||||||
|
|
||||||
|
async_msg() = default;
|
||||||
|
~async_msg() = default;
|
||||||
|
|
||||||
|
explicit async_msg(async_msg_type m_type)
|
||||||
|
: level(level::info)
|
||||||
|
, thread_id(0)
|
||||||
|
, msg_type(m_type)
|
||||||
|
, msg_id(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
async_msg(async_msg &&other) = default;
|
||||||
|
async_msg &operator=(async_msg &&other) = default;
|
||||||
|
|
||||||
|
// never copy or assign. should only be moved..
|
||||||
|
async_msg(const async_msg &) = delete;
|
||||||
|
async_msg &operator=(const async_msg &other) = delete;
|
||||||
|
|
||||||
|
// construct from log_msg
|
||||||
|
explicit async_msg(const details::log_msg &m)
|
||||||
|
: level(m.level)
|
||||||
|
, time(m.time)
|
||||||
|
, thread_id(m.thread_id)
|
||||||
|
, txt(m.raw.data(), m.raw.size())
|
||||||
|
, msg_type(async_msg_type::log)
|
||||||
|
, msg_id(m.msg_id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy into log_msg
|
||||||
|
void fill_log_msg(log_msg &msg, std::string* logger_name)
|
||||||
|
{
|
||||||
|
msg.logger_name = logger_name;
|
||||||
|
msg.level = level;
|
||||||
|
msg.time = time;
|
||||||
|
msg.thread_id = thread_id;
|
||||||
|
msg.raw.clear();
|
||||||
|
msg.raw << txt;
|
||||||
|
msg.msg_id = msg_id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
using item_type = async_msg;
|
||||||
|
using q_type = details::mpmc_bounded_queue<item_type>;
|
||||||
|
|
||||||
|
using clock = std::chrono::steady_clock;
|
||||||
|
|
||||||
|
async_log_helper(std::string logger_name,
|
||||||
|
formatter_ptr formatter,
|
||||||
|
std::vector<sink_ptr> sinks,
|
||||||
|
size_t queue_size,
|
||||||
|
const log_err_handler err_handler,
|
||||||
|
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
|
||||||
|
std::function<void()> worker_warmup_cb = nullptr,
|
||||||
|
const std::chrono::milliseconds &flush_interval_ms = std::chrono::milliseconds::zero(),
|
||||||
|
std::function<void()> worker_teardown_cb = nullptr);
|
||||||
|
|
||||||
|
void log(const details::log_msg &msg);
|
||||||
|
|
||||||
|
// stop logging and join the back thread
|
||||||
|
~async_log_helper();
|
||||||
|
|
||||||
|
async_log_helper(const async_log_helper &) = delete;
|
||||||
|
async_log_helper &operator=(const async_log_helper &) = delete;
|
||||||
|
|
||||||
|
void set_formatter(formatter_ptr msg_formatter);
|
||||||
|
|
||||||
|
void flush();
|
||||||
|
|
||||||
|
void set_error_handler(spdlog::log_err_handler err_handler);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string _logger_name;
|
||||||
|
formatter_ptr _formatter;
|
||||||
|
std::vector<std::shared_ptr<sinks::sink>> _sinks;
|
||||||
|
|
||||||
|
// queue of messages to log
|
||||||
|
q_type _q;
|
||||||
|
|
||||||
|
log_err_handler _err_handler;
|
||||||
|
|
||||||
|
std::chrono::time_point<log_clock> _last_flush;
|
||||||
|
|
||||||
|
// overflow policy
|
||||||
|
const async_overflow_policy _overflow_policy;
|
||||||
|
|
||||||
|
// worker thread warmup callback - one can set thread priority, affinity, etc
|
||||||
|
const std::function<void()> _worker_warmup_cb;
|
||||||
|
|
||||||
|
// auto periodic sink flush parameter
|
||||||
|
const std::chrono::milliseconds _flush_interval_ms;
|
||||||
|
|
||||||
|
// worker thread teardown callback
|
||||||
|
const std::function<void()> _worker_teardown_cb;
|
||||||
|
|
||||||
|
std::mutex null_mutex_;
|
||||||
|
// null_mutex null_mutex_;
|
||||||
|
std::condition_variable_any not_empty_cv_;
|
||||||
|
std::condition_variable_any not_full_cv_;
|
||||||
|
|
||||||
|
// worker thread
|
||||||
|
std::thread _worker_thread;
|
||||||
|
|
||||||
|
void enqueue_msg(async_msg &&new_msg, async_overflow_policy policy);
|
||||||
|
|
||||||
|
// worker thread main loop
|
||||||
|
void worker_loop();
|
||||||
|
|
||||||
|
// dequeue next message from the queue and process it.
|
||||||
|
// return false if termination of the queue is required
|
||||||
|
bool process_next_msg();
|
||||||
|
|
||||||
|
void handle_flush_interval();
|
||||||
|
|
||||||
|
void flush_sinks();
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
// async_sink class implementation
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
inline spdlog::details::async_log_helper::async_log_helper(std::string logger_name,
|
||||||
|
formatter_ptr formatter,
|
||||||
|
std::vector<sink_ptr> sinks,
|
||||||
|
size_t queue_size,
|
||||||
|
log_err_handler err_handler,
|
||||||
|
const async_overflow_policy overflow_policy,
|
||||||
|
std::function<void()> worker_warmup_cb,
|
||||||
|
const std::chrono::milliseconds &flush_interval_ms,
|
||||||
|
std::function<void()> worker_teardown_cb)
|
||||||
|
: _logger_name(std::move(logger_name))
|
||||||
|
, _formatter(std::move(formatter))
|
||||||
|
, _sinks(std::move(sinks))
|
||||||
|
, _q(queue_size)
|
||||||
|
, _err_handler(std::move(err_handler))
|
||||||
|
, _last_flush(os::now())
|
||||||
|
, _overflow_policy(overflow_policy)
|
||||||
|
, _worker_warmup_cb(std::move(worker_warmup_cb))
|
||||||
|
, _flush_interval_ms(flush_interval_ms)
|
||||||
|
, _worker_teardown_cb(std::move(worker_teardown_cb))
|
||||||
|
{
|
||||||
|
_worker_thread = std::thread(&async_log_helper::worker_loop, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// send to the worker thread terminate message, and join it.
|
||||||
|
inline spdlog::details::async_log_helper::~async_log_helper()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
enqueue_msg(async_msg(async_msg_type::terminate), async_overflow_policy::block_retry);
|
||||||
|
_worker_thread.join();
|
||||||
|
}
|
||||||
|
catch (...) // don't crash in destructor
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to push and block until succeeded (if the policy is not to discard when the queue is full)
|
||||||
|
inline void spdlog::details::async_log_helper::log(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
enqueue_msg(async_msg(msg), _overflow_policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::details::async_log_helper::enqueue_msg(details::async_log_helper::async_msg &&new_msg, async_overflow_policy policy)
|
||||||
|
{
|
||||||
|
|
||||||
|
// block until succeeded pushing to the queue
|
||||||
|
if (policy == async_overflow_policy::block_retry)
|
||||||
|
{
|
||||||
|
_q.enqueue(std::move(new_msg));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_q.enqueue_nowait(std::move(new_msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// optionally wait for the queue be empty and request flush from the sinks
|
||||||
|
inline void spdlog::details::async_log_helper::flush()
|
||||||
|
{
|
||||||
|
enqueue_msg(async_msg(async_msg_type::flush), _overflow_policy);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::details::async_log_helper::worker_loop()
|
||||||
|
{
|
||||||
|
if (_worker_warmup_cb)
|
||||||
|
{
|
||||||
|
_worker_warmup_cb();
|
||||||
|
}
|
||||||
|
auto active = true;
|
||||||
|
while (active)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
active = process_next_msg();
|
||||||
|
}
|
||||||
|
catch (const std::exception &ex)
|
||||||
|
{
|
||||||
|
_err_handler(ex.what());
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
_err_handler("Unknown exeption in async logger worker loop.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_worker_teardown_cb)
|
||||||
|
{
|
||||||
|
_worker_teardown_cb();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// process next message in the queue
|
||||||
|
// return true if this thread should still be active (while no terminate msg was received)
|
||||||
|
inline bool spdlog::details::async_log_helper::process_next_msg()
|
||||||
|
{
|
||||||
|
async_msg incoming_async_msg;
|
||||||
|
bool dequeued = _q.dequeue_for(incoming_async_msg, std::chrono::seconds(2));
|
||||||
|
if (!dequeued)
|
||||||
|
{
|
||||||
|
handle_flush_interval();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (incoming_async_msg.msg_type)
|
||||||
|
{
|
||||||
|
case async_msg_type::flush:
|
||||||
|
flush_sinks();
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case async_msg_type::terminate:
|
||||||
|
flush_sinks();
|
||||||
|
return false;
|
||||||
|
|
||||||
|
default:
|
||||||
|
log_msg incoming_log_msg;
|
||||||
|
incoming_async_msg.fill_log_msg(incoming_log_msg, &_logger_name);
|
||||||
|
_formatter->format(incoming_log_msg);
|
||||||
|
for (auto &s : _sinks)
|
||||||
|
{
|
||||||
|
if (s->should_log(incoming_log_msg.level))
|
||||||
|
{
|
||||||
|
s->log(incoming_log_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handle_flush_interval();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
assert(false);
|
||||||
|
return true; // should not be reached
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter)
|
||||||
|
{
|
||||||
|
_formatter = std::move(msg_formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler)
|
||||||
|
{
|
||||||
|
_err_handler = std::move(err_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush all sinks if _flush_interval_ms has expired.
|
||||||
|
inline void spdlog::details::async_log_helper::handle_flush_interval()
|
||||||
|
{
|
||||||
|
if (_flush_interval_ms == std::chrono::milliseconds::zero())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto delta = details::os::now() - _last_flush;
|
||||||
|
;
|
||||||
|
if (delta >= _flush_interval_ms)
|
||||||
|
{
|
||||||
|
flush_sinks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// flush all sinks if _flush_interval_ms has expired. only called if queue is empty
|
||||||
|
inline void spdlog::details::async_log_helper::flush_sinks()
|
||||||
|
{
|
||||||
|
for (auto &s : _sinks)
|
||||||
|
{
|
||||||
|
s->flush();
|
||||||
|
}
|
||||||
|
_last_flush = os::now();
|
||||||
|
}
|
95
include/spdlog/details/async_logger_impl.h
Normal file
95
include/spdlog/details/async_logger_impl.h
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Async Logger implementation
|
||||||
|
// Use an async_sink (queue per logger) to perform the logging in a worker thread
|
||||||
|
|
||||||
|
#include "../async_logger.h"
|
||||||
|
#include "../details/async_log_helper.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
template<class It>
|
||||||
|
inline spdlog::async_logger::async_logger(const std::string &logger_name, const It &begin, const It &end, size_t queue_size,
|
||||||
|
const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb,
|
||||||
|
const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb)
|
||||||
|
: logger(logger_name, begin, end)
|
||||||
|
, _async_log_helper(new details::async_log_helper(
|
||||||
|
logger_name, _formatter, _sinks, queue_size, _err_handler, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline spdlog::async_logger::async_logger(const std::string &logger_name, sinks_init_list sinks_list, size_t queue_size,
|
||||||
|
const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb,
|
||||||
|
const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb)
|
||||||
|
: async_logger(logger_name, sinks_list.begin(), sinks_list.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms,
|
||||||
|
worker_teardown_cb)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline spdlog::async_logger::async_logger(const std::string &logger_name, sink_ptr single_sink, size_t queue_size,
|
||||||
|
const async_overflow_policy overflow_policy, const std::function<void()> &worker_warmup_cb,
|
||||||
|
const std::chrono::milliseconds &flush_interval_ms, const std::function<void()> &worker_teardown_cb)
|
||||||
|
: async_logger(
|
||||||
|
logger_name, {std::move(single_sink)}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::async_logger::flush()
|
||||||
|
{
|
||||||
|
_async_log_helper->flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error handler
|
||||||
|
inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_handler)
|
||||||
|
{
|
||||||
|
_err_handler = err_handler;
|
||||||
|
_async_log_helper->set_error_handler(err_handler);
|
||||||
|
}
|
||||||
|
inline spdlog::log_err_handler spdlog::async_logger::error_handler()
|
||||||
|
{
|
||||||
|
return _err_handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)
|
||||||
|
{
|
||||||
|
_formatter = msg_formatter;
|
||||||
|
_async_log_helper->set_formatter(_formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::async_logger::_set_pattern(const std::string &pattern, pattern_time_type pattern_time)
|
||||||
|
{
|
||||||
|
_formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
|
||||||
|
_async_log_helper->set_formatter(_formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::async_logger::_sink_it(details::log_msg &msg)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
|
||||||
|
_incr_msg_counter(msg);
|
||||||
|
#endif
|
||||||
|
_async_log_helper->log(msg);
|
||||||
|
if (_should_flush_on(msg))
|
||||||
|
{
|
||||||
|
_async_log_helper->flush(); // do async flush
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &ex)
|
||||||
|
{
|
||||||
|
_err_handler(ex.what());
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
_err_handler("Unknown exception in logger " + _name);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
@@ -1,69 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/details/backtracer.h>
|
|
||||||
#endif
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
SPDLOG_INLINE backtracer::backtracer(const backtracer &other)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(other.mutex_);
|
|
||||||
enabled_ = other.enabled();
|
|
||||||
messages_ = other.messages_;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(other.mutex_);
|
|
||||||
enabled_ = other.enabled();
|
|
||||||
messages_ = std::move(other.messages_);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
|
||||||
enabled_ = other.enabled();
|
|
||||||
messages_ = std::move(other.messages_);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void backtracer::enable(size_t size)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock{mutex_};
|
|
||||||
enabled_.store(true, std::memory_order_relaxed);
|
|
||||||
messages_ = circular_q<log_msg_buffer>{size};
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void backtracer::disable()
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock{mutex_};
|
|
||||||
enabled_.store(false, std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE bool backtracer::enabled() const
|
|
||||||
{
|
|
||||||
return enabled_.load(std::memory_order_relaxed);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void backtracer::push_back(const log_msg &msg)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock{mutex_};
|
|
||||||
messages_.push_back(log_msg_buffer{msg});
|
|
||||||
}
|
|
||||||
|
|
||||||
// pop all items in the q and apply the given fun on each of them.
|
|
||||||
SPDLOG_INLINE void backtracer::foreach_pop(std::function<void(const details::log_msg &)> fun)
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock{mutex_};
|
|
||||||
while (!messages_.empty())
|
|
||||||
{
|
|
||||||
auto &front_msg = messages_.front();
|
|
||||||
fun(front_msg);
|
|
||||||
messages_.pop_front();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@@ -1,45 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/details/log_msg_buffer.h>
|
|
||||||
#include <spdlog/details/circular_q.h>
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include <mutex>
|
|
||||||
#include <functional>
|
|
||||||
|
|
||||||
// Store log messages in circular buffer.
|
|
||||||
// Useful for storing debug data in case of error/warning happens.
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
class SPDLOG_API backtracer
|
|
||||||
{
|
|
||||||
mutable std::mutex mutex_;
|
|
||||||
std::atomic<bool> enabled_{false};
|
|
||||||
circular_q<log_msg_buffer> messages_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
backtracer() = default;
|
|
||||||
backtracer(const backtracer &other);
|
|
||||||
|
|
||||||
backtracer(backtracer &&other) SPDLOG_NOEXCEPT;
|
|
||||||
backtracer &operator=(backtracer other);
|
|
||||||
|
|
||||||
void enable(size_t size);
|
|
||||||
void disable();
|
|
||||||
bool enabled() const;
|
|
||||||
void push_back(const log_msg &msg);
|
|
||||||
|
|
||||||
// pop all items in the q and apply the given fun on each of them.
|
|
||||||
void foreach_pop(std::function<void(const details::log_msg &)> fun);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "backtracer-inl.h"
|
|
||||||
#endif
|
|
@@ -1,141 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
// circular q view of std::vector.
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
template<typename T>
|
|
||||||
class circular_q
|
|
||||||
{
|
|
||||||
size_t max_items_ = 0;
|
|
||||||
typename std::vector<T>::size_type head_ = 0;
|
|
||||||
typename std::vector<T>::size_type tail_ = 0;
|
|
||||||
size_t overrun_counter_ = 0;
|
|
||||||
std::vector<T> v_;
|
|
||||||
|
|
||||||
public:
|
|
||||||
using value_type = T;
|
|
||||||
|
|
||||||
// empty ctor - create a disabled queue with no elements allocated at all
|
|
||||||
circular_q() = default;
|
|
||||||
|
|
||||||
explicit circular_q(size_t max_items)
|
|
||||||
: max_items_(max_items + 1) // one item is reserved as marker for full q
|
|
||||||
, v_(max_items_)
|
|
||||||
{}
|
|
||||||
|
|
||||||
circular_q(const circular_q &) = default;
|
|
||||||
circular_q &operator=(const circular_q &) = default;
|
|
||||||
|
|
||||||
// move cannot be default,
|
|
||||||
// since we need to reset head_, tail_, etc to zero in the moved object
|
|
||||||
circular_q(circular_q &&other) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
copy_moveable(std::move(other));
|
|
||||||
}
|
|
||||||
|
|
||||||
circular_q &operator=(circular_q &&other) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
copy_moveable(std::move(other));
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// push back, overrun (oldest) item if no room left
|
|
||||||
void push_back(T &&item)
|
|
||||||
{
|
|
||||||
if (max_items_ > 0)
|
|
||||||
{
|
|
||||||
v_[tail_] = std::move(item);
|
|
||||||
tail_ = (tail_ + 1) % max_items_;
|
|
||||||
|
|
||||||
if (tail_ == head_) // overrun last item if full
|
|
||||||
{
|
|
||||||
head_ = (head_ + 1) % max_items_;
|
|
||||||
++overrun_counter_;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return reference to the front item.
|
|
||||||
// If there are no elements in the container, the behavior is undefined.
|
|
||||||
const T &front() const
|
|
||||||
{
|
|
||||||
return v_[head_];
|
|
||||||
}
|
|
||||||
|
|
||||||
T &front()
|
|
||||||
{
|
|
||||||
return v_[head_];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return number of elements actually stored
|
|
||||||
size_t size() const
|
|
||||||
{
|
|
||||||
if (tail_ >= head_)
|
|
||||||
{
|
|
||||||
return tail_ - head_;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return max_items_ - (head_ - tail_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return const reference to item by index.
|
|
||||||
// If index is out of range 0…size()-1, the behavior is undefined.
|
|
||||||
const T &at(size_t i) const
|
|
||||||
{
|
|
||||||
assert(i < size());
|
|
||||||
return v_[(head_ + i) % max_items_];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pop item from front.
|
|
||||||
// If there are no elements in the container, the behavior is undefined.
|
|
||||||
void pop_front()
|
|
||||||
{
|
|
||||||
head_ = (head_ + 1) % max_items_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool empty() const
|
|
||||||
{
|
|
||||||
return tail_ == head_;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool full() const
|
|
||||||
{
|
|
||||||
// head is ahead of the tail by 1
|
|
||||||
if (max_items_ > 0)
|
|
||||||
{
|
|
||||||
return ((tail_ + 1) % max_items_) == head_;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t overrun_counter() const
|
|
||||||
{
|
|
||||||
return overrun_counter_;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
// copy from other&& and reset it to disabled state
|
|
||||||
void copy_moveable(circular_q &&other) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
max_items_ = other.max_items_;
|
|
||||||
head_ = other.head_;
|
|
||||||
tail_ = other.tail_;
|
|
||||||
overrun_counter_ = other.overrun_counter_;
|
|
||||||
v_ = std::move(other.v_);
|
|
||||||
|
|
||||||
// put &&other in disabled, but valid state
|
|
||||||
other.max_items_ = 0;
|
|
||||||
other.head_ = other.tail_ = 0;
|
|
||||||
other.overrun_counter_ = 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@@ -1,32 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/details/null_mutex.h>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
struct console_mutex
|
|
||||||
{
|
|
||||||
using mutex_t = std::mutex;
|
|
||||||
static mutex_t &mutex()
|
|
||||||
{
|
|
||||||
static mutex_t s_mutex;
|
|
||||||
return s_mutex;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct console_nullmutex
|
|
||||||
{
|
|
||||||
using mutex_t = null_mutex;
|
|
||||||
static mutex_t &mutex()
|
|
||||||
{
|
|
||||||
static mutex_t s_mutex;
|
|
||||||
return s_mutex;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@@ -1,147 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/details/file_helper.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/details/os.h>
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
|
|
||||||
#include <cerrno>
|
|
||||||
#include <chrono>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <string>
|
|
||||||
#include <thread>
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
SPDLOG_INLINE file_helper::~file_helper()
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void file_helper::open(const filename_t &fname, bool truncate)
|
|
||||||
{
|
|
||||||
close();
|
|
||||||
filename_ = fname;
|
|
||||||
|
|
||||||
auto *mode = SPDLOG_FILENAME_T("ab");
|
|
||||||
auto *trunc_mode = SPDLOG_FILENAME_T("wb");
|
|
||||||
|
|
||||||
for (int tries = 0; tries < open_tries_; ++tries)
|
|
||||||
{
|
|
||||||
// create containing folder if not exists already.
|
|
||||||
os::create_dir(os::dir_name(fname));
|
|
||||||
if (truncate)
|
|
||||||
{
|
|
||||||
// Truncate by opening-and-closing a tmp file in "wb" mode, always
|
|
||||||
// opening the actual log-we-write-to in "ab" mode, since that
|
|
||||||
// interacts more politely with eternal processes that might
|
|
||||||
// rotate/truncate the file underneath us.
|
|
||||||
std::FILE *tmp;
|
|
||||||
if (os::fopen_s(&tmp, fname, trunc_mode))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
std::fclose(tmp);
|
|
||||||
}
|
|
||||||
if (!os::fopen_s(&fd_, fname, mode))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
details::os::sleep_for_millis(open_interval_);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw_spdlog_ex("Failed opening file " + os::filename_to_str(filename_) + " for writing", errno);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void file_helper::reopen(bool truncate)
|
|
||||||
{
|
|
||||||
if (filename_.empty())
|
|
||||||
{
|
|
||||||
throw_spdlog_ex("Failed re opening file - was not opened before");
|
|
||||||
}
|
|
||||||
this->open(filename_, truncate);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void file_helper::flush()
|
|
||||||
{
|
|
||||||
std::fflush(fd_);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void file_helper::close()
|
|
||||||
{
|
|
||||||
if (fd_ != nullptr)
|
|
||||||
{
|
|
||||||
std::fclose(fd_);
|
|
||||||
fd_ = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void file_helper::write(const memory_buf_t &buf)
|
|
||||||
{
|
|
||||||
size_t msg_size = buf.size();
|
|
||||||
auto data = buf.data();
|
|
||||||
if (std::fwrite(data, 1, msg_size, fd_) != msg_size)
|
|
||||||
{
|
|
||||||
throw_spdlog_ex("Failed writing to file " + os::filename_to_str(filename_), errno);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE size_t file_helper::size() const
|
|
||||||
{
|
|
||||||
if (fd_ == nullptr)
|
|
||||||
{
|
|
||||||
throw_spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(filename_));
|
|
||||||
}
|
|
||||||
return os::filesize(fd_);
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE const filename_t &file_helper::filename() const
|
|
||||||
{
|
|
||||||
return filename_;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// return file path and its extension:
|
|
||||||
//
|
|
||||||
// "mylog.txt" => ("mylog", ".txt")
|
|
||||||
// "mylog" => ("mylog", "")
|
|
||||||
// "mylog." => ("mylog.", "")
|
|
||||||
// "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt")
|
|
||||||
//
|
|
||||||
// the starting dot in filenames is ignored (hidden files):
|
|
||||||
//
|
|
||||||
// ".mylog" => (".mylog". "")
|
|
||||||
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
|
||||||
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
|
||||||
SPDLOG_INLINE std::tuple<filename_t, filename_t> file_helper::split_by_extension(const filename_t &fname)
|
|
||||||
{
|
|
||||||
auto ext_index = fname.rfind('.');
|
|
||||||
|
|
||||||
// no valid extension found - return whole path and empty string as
|
|
||||||
// extension
|
|
||||||
if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
|
|
||||||
{
|
|
||||||
return std::make_tuple(fname, filename_t());
|
|
||||||
}
|
|
||||||
|
|
||||||
// treat cases like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
|
|
||||||
auto folder_index = fname.find_last_of(details::os::folder_seps_filename);
|
|
||||||
if (folder_index != filename_t::npos && folder_index >= ext_index - 1)
|
|
||||||
{
|
|
||||||
return std::make_tuple(fname, filename_t());
|
|
||||||
}
|
|
||||||
|
|
||||||
// finally - return a valid base and extension tuple
|
|
||||||
return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@@ -1,34 +1,113 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
// Helper class for file sink
|
||||||
|
// When failing to open a file, retry several times(5) with small delay between the tries(10 ms)
|
||||||
|
// Throw spdlog_ex exception on errors
|
||||||
|
|
||||||
|
#include "../details/log_msg.h"
|
||||||
|
#include "../details/os.h"
|
||||||
|
|
||||||
|
#include <cerrno>
|
||||||
|
#include <chrono>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <string>
|
||||||
|
#include <thread>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace details {
|
namespace details {
|
||||||
|
|
||||||
// Helper class for file sinks.
|
class file_helper
|
||||||
// When failing to open a file, retry several times(5) with a delay interval(10 ms).
|
|
||||||
// Throw spdlog_ex exception on errors.
|
|
||||||
|
|
||||||
class SPDLOG_API file_helper
|
|
||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
const int open_tries = 5;
|
||||||
|
const int open_interval = 10;
|
||||||
|
|
||||||
explicit file_helper() = default;
|
explicit file_helper() = default;
|
||||||
|
|
||||||
file_helper(const file_helper &) = delete;
|
file_helper(const file_helper &) = delete;
|
||||||
file_helper &operator=(const file_helper &) = delete;
|
file_helper &operator=(const file_helper &) = delete;
|
||||||
~file_helper();
|
|
||||||
|
|
||||||
void open(const filename_t &fname, bool truncate = false);
|
~file_helper()
|
||||||
void reopen(bool truncate);
|
{
|
||||||
void flush();
|
close();
|
||||||
void close();
|
}
|
||||||
void write(const memory_buf_t &buf);
|
|
||||||
size_t size() const;
|
void open(const filename_t &fname, bool truncate = false)
|
||||||
const filename_t &filename() const;
|
{
|
||||||
|
close();
|
||||||
|
auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
|
||||||
|
_filename = fname;
|
||||||
|
for (int tries = 0; tries < open_tries; ++tries)
|
||||||
|
{
|
||||||
|
if (!os::fopen_s(&_fd, fname, mode))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
details::os::sleep_for_millis(open_interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
void reopen(bool truncate)
|
||||||
|
{
|
||||||
|
if (_filename.empty())
|
||||||
|
{
|
||||||
|
throw spdlog_ex("Failed re opening file - was not opened before");
|
||||||
|
}
|
||||||
|
open(_filename, truncate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void flush()
|
||||||
|
{
|
||||||
|
std::fflush(_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
void close()
|
||||||
|
{
|
||||||
|
if (_fd != nullptr)
|
||||||
|
{
|
||||||
|
std::fclose(_fd);
|
||||||
|
_fd = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void write(const log_msg &msg)
|
||||||
|
{
|
||||||
|
size_t msg_size = msg.formatted.size();
|
||||||
|
auto data = msg.formatted.data();
|
||||||
|
if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
|
||||||
|
{
|
||||||
|
throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
if (_fd == nullptr)
|
||||||
|
{
|
||||||
|
throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
|
||||||
|
}
|
||||||
|
return os::filesize(_fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
const filename_t &filename() const
|
||||||
|
{
|
||||||
|
return _filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool file_exists(const filename_t &fname)
|
||||||
|
{
|
||||||
|
return os::file_exists(fname);
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// return file path and its extension:
|
// return file path and its extension:
|
||||||
@@ -43,17 +122,30 @@ public:
|
|||||||
// ".mylog" => (".mylog". "")
|
// ".mylog" => (".mylog". "")
|
||||||
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
// "my_folder/.mylog" => ("my_folder/.mylog", "")
|
||||||
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
|
||||||
static std::tuple<filename_t, filename_t> split_by_extension(const filename_t &fname);
|
static std::tuple<filename_t, filename_t> split_by_extenstion(const spdlog::filename_t &fname)
|
||||||
|
{
|
||||||
|
auto ext_index = fname.rfind('.');
|
||||||
|
|
||||||
|
// no valid extension found - return whole path and empty string as extension
|
||||||
|
if (ext_index == filename_t::npos || ext_index == 0 || ext_index == fname.size() - 1)
|
||||||
|
{
|
||||||
|
return std::make_tuple(fname, spdlog::filename_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
// treat casese like "/etc/rc.d/somelogfile or "/abc/.hiddenfile"
|
||||||
|
auto folder_index = fname.rfind(details::os::folder_sep);
|
||||||
|
if (folder_index != fname.npos && folder_index >= ext_index - 1)
|
||||||
|
{
|
||||||
|
return std::make_tuple(fname, spdlog::filename_t());
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally - return a valid base and extension tuple
|
||||||
|
return std::make_tuple(fname.substr(0, ext_index), fname.substr(ext_index));
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const int open_tries_ = 5;
|
FILE *_fd{nullptr};
|
||||||
const int open_interval_ = 10;
|
filename_t _filename;
|
||||||
std::FILE *fd_{nullptr};
|
|
||||||
filename_t filename_;
|
|
||||||
};
|
};
|
||||||
} // namespace details
|
} // namespace details
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "file_helper-inl.h"
|
|
||||||
#endif
|
|
||||||
|
@@ -1,116 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <chrono>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <spdlog/fmt/fmt.h>
|
|
||||||
#include <spdlog/common.h>
|
|
||||||
|
|
||||||
// Some fmt helpers to efficiently format and pad ints and strings
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
namespace fmt_helper {
|
|
||||||
|
|
||||||
inline spdlog::string_view_t to_string_view(const memory_buf_t &buf) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
return spdlog::string_view_t{buf.data(), buf.size()};
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
fmt::format_int i(n);
|
|
||||||
dest.append(i.data(), i.data() + i.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
return static_cast<unsigned int>(fmt::
|
|
||||||
// fmt 7.0.0 renamed the internal namespace to detail.
|
|
||||||
// See: https://github.com/fmtlib/fmt/issues/1538
|
|
||||||
#if FMT_VERSION < 70000
|
|
||||||
internal
|
|
||||||
#else
|
|
||||||
detail
|
|
||||||
#endif
|
|
||||||
::count_digits(static_cast<count_type>(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));
|
|
||||||
}
|
|
||||||
else // unlikely, but just in case, let fmt deal with it
|
|
||||||
{
|
|
||||||
fmt::format_to(dest, "{:02}", n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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++)
|
|
||||||
{
|
|
||||||
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");
|
|
||||||
if (n < 1000)
|
|
||||||
{
|
|
||||||
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'));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
append_int(n, dest);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
inline 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)
|
|
||||||
{
|
|
||||||
pad_uint(n, 9, dest);
|
|
||||||
}
|
|
||||||
|
|
||||||
// return fraction of a second of the given time_point.
|
|
||||||
// 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)
|
|
||||||
{
|
|
||||||
using std::chrono::duration_cast;
|
|
||||||
using std::chrono::seconds;
|
|
||||||
auto duration = tp.time_since_epoch();
|
|
||||||
auto secs = duration_cast<seconds>(duration);
|
|
||||||
return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace fmt_helper
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@@ -1,37 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/details/log_msg.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <spdlog/details/os.h>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
SPDLOG_INLINE log_msg::log_msg(spdlog::log_clock::time_point log_time, spdlog::source_loc loc, string_view_t a_logger_name,
|
|
||||||
spdlog::level::level_enum lvl, spdlog::string_view_t msg)
|
|
||||||
: logger_name(a_logger_name)
|
|
||||||
, level(lvl)
|
|
||||||
, time(log_time)
|
|
||||||
#ifndef SPDLOG_NO_THREAD_ID
|
|
||||||
, thread_id(os::thread_id())
|
|
||||||
#endif
|
|
||||||
, source(loc)
|
|
||||||
, payload(msg)
|
|
||||||
{}
|
|
||||||
|
|
||||||
SPDLOG_INLINE log_msg::log_msg(
|
|
||||||
spdlog::source_loc loc, string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
|
|
||||||
: log_msg(os::now(), loc, a_logger_name, lvl, msg)
|
|
||||||
{}
|
|
||||||
|
|
||||||
SPDLOG_INLINE log_msg::log_msg(string_view_t a_logger_name, spdlog::level::level_enum lvl, spdlog::string_view_t msg)
|
|
||||||
: log_msg(os::now(), source_loc{}, a_logger_name, lvl, msg)
|
|
||||||
{}
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@@ -1,36 +1,48 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <spdlog/common.h>
|
#include "../common.h"
|
||||||
|
#include "../details/os.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace details {
|
namespace details {
|
||||||
struct SPDLOG_API log_msg
|
struct log_msg
|
||||||
{
|
{
|
||||||
log_msg() = default;
|
log_msg() = default;
|
||||||
log_msg(log_clock::time_point log_time, source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
log_msg(const std::string *loggers_name, level::level_enum lvl)
|
||||||
log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
: logger_name(loggers_name)
|
||||||
log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
, level(lvl)
|
||||||
log_msg(const log_msg &other) = default;
|
{
|
||||||
|
#ifndef SPDLOG_NO_DATETIME
|
||||||
|
time = os::now();
|
||||||
|
#endif
|
||||||
|
|
||||||
string_view_t logger_name;
|
#ifndef SPDLOG_NO_THREAD_ID
|
||||||
level::level_enum level{level::off};
|
thread_id = os::thread_id();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
log_msg(const log_msg &other) = delete;
|
||||||
|
log_msg &operator=(log_msg &&other) = delete;
|
||||||
|
log_msg(log_msg &&other) = delete;
|
||||||
|
|
||||||
|
const std::string *logger_name{nullptr};
|
||||||
|
level::level_enum level;
|
||||||
log_clock::time_point time;
|
log_clock::time_point time;
|
||||||
size_t thread_id{0};
|
size_t thread_id;
|
||||||
|
fmt::MemoryWriter raw;
|
||||||
// wrapping the formatted text with color (updated by pattern_formatter).
|
fmt::MemoryWriter formatted;
|
||||||
mutable size_t color_range_start{0};
|
size_t msg_id{0};
|
||||||
mutable size_t color_range_end{0};
|
// wrap this range with color codes
|
||||||
|
size_t color_range_start{0};
|
||||||
source_loc source;
|
size_t color_range_end{0};
|
||||||
string_view_t payload;
|
|
||||||
};
|
};
|
||||||
} // namespace details
|
} // namespace details
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "log_msg-inl.h"
|
|
||||||
#endif
|
|
||||||
|
@@ -1,60 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#ifndef SPDLOG_HEADER_ONLY
|
|
||||||
#include <spdlog/details/log_msg_buffer.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg &orig_msg)
|
|
||||||
: log_msg{orig_msg}
|
|
||||||
{
|
|
||||||
buffer.append(logger_name.begin(), logger_name.end());
|
|
||||||
buffer.append(payload.begin(), payload.end());
|
|
||||||
update_string_views();
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other)
|
|
||||||
: log_msg{other}
|
|
||||||
{
|
|
||||||
buffer.append(logger_name.begin(), logger_name.end());
|
|
||||||
buffer.append(payload.begin(), payload.end());
|
|
||||||
update_string_views();
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT
|
|
||||||
: log_msg{other}
|
|
||||||
, buffer{std::move(other.buffer)}
|
|
||||||
{
|
|
||||||
update_string_views();
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &other)
|
|
||||||
{
|
|
||||||
log_msg::operator=(other);
|
|
||||||
buffer.clear();
|
|
||||||
buffer.append(other.buffer.data(), other.buffer.data() + other.buffer.size());
|
|
||||||
update_string_views();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT
|
|
||||||
{
|
|
||||||
log_msg::operator=(other);
|
|
||||||
buffer = std::move(other.buffer);
|
|
||||||
update_string_views();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
SPDLOG_INLINE void log_msg_buffer::update_string_views()
|
|
||||||
{
|
|
||||||
logger_name = string_view_t{buffer.data(), logger_name.size()};
|
|
||||||
payload = string_view_t{buffer.data() + logger_name.size(), payload.size()};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
@@ -1,33 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <spdlog/details/log_msg.h>
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
// Extend log_msg with internal buffer to store its payload.
|
|
||||||
// This is needed since log_msg holds string_views that points to stack data.
|
|
||||||
|
|
||||||
class SPDLOG_API log_msg_buffer : public log_msg
|
|
||||||
{
|
|
||||||
memory_buf_t buffer;
|
|
||||||
void update_string_views();
|
|
||||||
|
|
||||||
public:
|
|
||||||
log_msg_buffer() = default;
|
|
||||||
explicit log_msg_buffer(const log_msg &orig_msg);
|
|
||||||
log_msg_buffer(const log_msg_buffer &other);
|
|
||||||
log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
|
|
||||||
log_msg_buffer &operator=(const log_msg_buffer &other);
|
|
||||||
log_msg_buffer &operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
||||||
|
|
||||||
#ifdef SPDLOG_HEADER_ONLY
|
|
||||||
#include "log_msg_buffer-inl.h"
|
|
||||||
#endif
|
|
368
include/spdlog/details/logger_impl.h
Normal file
368
include/spdlog/details/logger_impl.h
Normal file
@@ -0,0 +1,368 @@
|
|||||||
|
//
|
||||||
|
// Copyright(c) 2015 Gabi Melman.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
//
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../logger.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
// create logger with given name, sinks and the default pattern formatter
|
||||||
|
// all other ctors will call this one
|
||||||
|
template<class It>
|
||||||
|
inline spdlog::logger::logger(std::string logger_name, const It &begin, const It &end)
|
||||||
|
: _name(std::move(logger_name))
|
||||||
|
, _sinks(begin, end)
|
||||||
|
, _formatter(std::make_shared<pattern_formatter>("%+"))
|
||||||
|
, _level(level::info)
|
||||||
|
, _flush_level(level::off)
|
||||||
|
, _last_err_time(0)
|
||||||
|
, _msg_counter(1) // message counter will start from 1. 0-message id will be reserved for controll messages
|
||||||
|
{
|
||||||
|
_err_handler = [this](const std::string &msg) { this->_default_err_handler(msg); };
|
||||||
|
}
|
||||||
|
|
||||||
|
// ctor with sinks as init list
|
||||||
|
inline spdlog::logger::logger(const std::string &logger_name, sinks_init_list sinks_list)
|
||||||
|
: logger(logger_name, sinks_list.begin(), sinks_list.end())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// ctor with single sink
|
||||||
|
inline spdlog::logger::logger(const std::string &logger_name, spdlog::sink_ptr single_sink)
|
||||||
|
: logger(logger_name, {std::move(single_sink)})
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
inline spdlog::logger::~logger() = default;
|
||||||
|
|
||||||
|
inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter)
|
||||||
|
{
|
||||||
|
_set_formatter(std::move(msg_formatter));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::set_pattern(const std::string &pattern, pattern_time_type pattern_time)
|
||||||
|
{
|
||||||
|
_set_pattern(pattern, pattern_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
if (!should_log(lvl))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
details::log_msg log_msg(&_name, lvl);
|
||||||
|
|
||||||
|
#if defined(SPDLOG_FMT_PRINTF)
|
||||||
|
fmt::printf(log_msg.raw, fmt, args...);
|
||||||
|
#else
|
||||||
|
log_msg.raw.write(fmt, args...);
|
||||||
|
#endif
|
||||||
|
_sink_it(log_msg);
|
||||||
|
}
|
||||||
|
catch (const std::exception &ex)
|
||||||
|
{
|
||||||
|
_err_handler(ex.what());
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
_err_handler("Unknown exception in logger " + _name);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::log(level::level_enum lvl, const char *msg)
|
||||||
|
{
|
||||||
|
if (!should_log(lvl))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
details::log_msg log_msg(&_name, lvl);
|
||||||
|
log_msg.raw << msg;
|
||||||
|
_sink_it(log_msg);
|
||||||
|
}
|
||||||
|
catch (const std::exception &ex)
|
||||||
|
{
|
||||||
|
_err_handler(ex.what());
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
_err_handler("Unknown exception in logger " + _name);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
|
||||||
|
{
|
||||||
|
if (!should_log(lvl))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
details::log_msg log_msg(&_name, lvl);
|
||||||
|
log_msg.raw << msg;
|
||||||
|
_sink_it(log_msg);
|
||||||
|
}
|
||||||
|
catch (const std::exception &ex)
|
||||||
|
{
|
||||||
|
_err_handler(ex.what());
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
_err_handler("Unknown exception in logger " + _name);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Arg1, typename... Args>
|
||||||
|
inline void spdlog::logger::trace(const char *fmt, const Arg1 &arg1, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::trace, fmt, arg1, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Arg1, typename... Args>
|
||||||
|
inline void spdlog::logger::debug(const char *fmt, const Arg1 &arg1, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::debug, fmt, arg1, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Arg1, typename... Args>
|
||||||
|
inline void spdlog::logger::info(const char *fmt, const Arg1 &arg1, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::info, fmt, arg1, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Arg1, typename... Args>
|
||||||
|
inline void spdlog::logger::warn(const char *fmt, const Arg1 &arg1, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::warn, fmt, arg1, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Arg1, typename... Args>
|
||||||
|
inline void spdlog::logger::error(const char *fmt, const Arg1 &arg1, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::err, fmt, arg1, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Arg1, typename... Args>
|
||||||
|
inline void spdlog::logger::critical(const char *fmt, const Arg1 &arg1, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::critical, fmt, arg1, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void spdlog::logger::trace(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::trace, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void spdlog::logger::debug(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::debug, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void spdlog::logger::info(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::info, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void spdlog::logger::warn(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::warn, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void spdlog::logger::error(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::err, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline void spdlog::logger::critical(const T &msg)
|
||||||
|
{
|
||||||
|
log(level::critical, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
|
#include <codecvt>
|
||||||
|
#include <locale>
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *msg)
|
||||||
|
{
|
||||||
|
std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
|
||||||
|
|
||||||
|
log(lvl, conv.to_bytes(msg));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
fmt::WMemoryWriter wWriter;
|
||||||
|
|
||||||
|
wWriter.write(fmt, args...);
|
||||||
|
log(lvl, wWriter.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::trace(const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::trace, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::debug(const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::debug, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::info(const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::info, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::warn(const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::warn, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::error(const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::err, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename... Args>
|
||||||
|
inline void spdlog::logger::critical(const wchar_t *fmt, const Args &... args)
|
||||||
|
{
|
||||||
|
log(level::critical, fmt, args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||||
|
|
||||||
|
//
|
||||||
|
// name and level
|
||||||
|
//
|
||||||
|
inline const std::string &spdlog::logger::name() const
|
||||||
|
{
|
||||||
|
return _name;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::set_level(spdlog::level::level_enum log_level)
|
||||||
|
{
|
||||||
|
_level.store(log_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler)
|
||||||
|
{
|
||||||
|
_err_handler = std::move(err_handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline spdlog::log_err_handler spdlog::logger::error_handler()
|
||||||
|
{
|
||||||
|
return _err_handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::flush_on(level::level_enum log_level)
|
||||||
|
{
|
||||||
|
_flush_level.store(log_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline spdlog::level::level_enum spdlog::logger::level() const
|
||||||
|
{
|
||||||
|
return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const
|
||||||
|
{
|
||||||
|
return msg_level >= _level.load(std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// protected virtual called at end of each user log call (if enabled) by the line_logger
|
||||||
|
//
|
||||||
|
inline void spdlog::logger::_sink_it(details::log_msg &msg)
|
||||||
|
{
|
||||||
|
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
|
||||||
|
_incr_msg_counter(msg);
|
||||||
|
#endif
|
||||||
|
_formatter->format(msg);
|
||||||
|
for (auto &sink : _sinks)
|
||||||
|
{
|
||||||
|
if (sink->should_log(msg.level))
|
||||||
|
{
|
||||||
|
sink->log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_should_flush_on(msg))
|
||||||
|
{
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::_set_pattern(const std::string &pattern, pattern_time_type pattern_time)
|
||||||
|
{
|
||||||
|
_formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter)
|
||||||
|
{
|
||||||
|
_formatter = std::move(msg_formatter);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::flush()
|
||||||
|
{
|
||||||
|
for (auto &sink : _sinks)
|
||||||
|
{
|
||||||
|
sink->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::_default_err_handler(const std::string &msg)
|
||||||
|
{
|
||||||
|
auto now = time(nullptr);
|
||||||
|
if (now - _last_err_time < 60)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_last_err_time = now;
|
||||||
|
auto tm_time = details::os::localtime(now);
|
||||||
|
char date_buf[100];
|
||||||
|
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
|
||||||
|
fmt::print(stderr, "[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, name(), msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg)
|
||||||
|
{
|
||||||
|
const auto flush_level = _flush_level.load(std::memory_order_relaxed);
|
||||||
|
return (msg.level >= flush_level) && (msg.level != level::off);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void spdlog::logger::_incr_msg_counter(details::log_msg &msg)
|
||||||
|
{
|
||||||
|
msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const std::vector<spdlog::sink_ptr> &spdlog::logger::sinks() const
|
||||||
|
{
|
||||||
|
return _sinks;
|
||||||
|
}
|
@@ -1,126 +1,84 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// multi producer-multi consumer blocking queue.
|
//
|
||||||
// enqueue(..) - will block until room found to put the new message.
|
// Copyright(c) 2018 Gabi Melman.
|
||||||
// enqueue_nowait(..) - will return immediately with false if no room left in
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
// the queue.
|
//
|
||||||
// dequeue_for(..) - will block until the queue is not empty or timeout have
|
|
||||||
// passed.
|
|
||||||
|
|
||||||
#include <spdlog/details/circular_q.h>
|
// async log helper :
|
||||||
|
// multi producer-multi consumer blocking queue
|
||||||
|
// enqueue(..) - will block until room found to put the new message
|
||||||
|
// enqueue_nowait(..) - will return immediatly with false if no room left in the queue
|
||||||
|
// dequeue_for(..) - will block until the queue is not empty or timeout passed
|
||||||
|
|
||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace details {
|
namespace details {
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
class mpmc_blocking_queue
|
class mpmc_bounded_queue
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using item_type = T;
|
using item_type = T;
|
||||||
explicit mpmc_blocking_queue(size_t max_items)
|
explicit mpmc_bounded_queue(size_t max_items)
|
||||||
: q_(max_items)
|
: max_items_(max_items)
|
||||||
{}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef __MINGW32__
|
|
||||||
// try to enqueue and block if no room left
|
// try to enqueue and block if no room left
|
||||||
void enqueue(T &&item)
|
void enqueue(T &&item)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
|
pop_cv_.wait(lock, [this] { return this->q_.size() < this->max_items_; });
|
||||||
q_.push_back(std::move(item));
|
q_.push(std::move(item));
|
||||||
}
|
}
|
||||||
push_cv_.notify_one();
|
push_cv_.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
// enqueue immediately. overrun oldest message in the queue if no room left.
|
// try to enqueue and return immdeialty false if no room left
|
||||||
void enqueue_nowait(T &&item)
|
bool enqueue_nowait(T &&item)
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
q_.push_back(std::move(item));
|
if (q_.size() == this->max_items_)
|
||||||
}
|
|
||||||
push_cv_.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to dequeue item. if no item found. wait upto timeout and try again
|
|
||||||
// Return true, if succeeded dequeue item, false otherwise
|
|
||||||
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
popped_item = std::move(q_.front());
|
q_.push(std::forward<T>(item));
|
||||||
q_.pop_front();
|
|
||||||
}
|
}
|
||||||
pop_cv_.notify_one();
|
push_cv_.notify_one();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
|
||||||
// apparently mingw deadlocks if the mutex is released before cv.notify_one(),
|
|
||||||
// so release the mutex at the very end each function.
|
|
||||||
|
|
||||||
// try to enqueue and block if no room left
|
|
||||||
void enqueue(T &&item)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
|
|
||||||
q_.push_back(std::move(item));
|
|
||||||
push_cv_.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
// enqueue immediately. overrun oldest message in the queue if no room left.
|
|
||||||
void enqueue_nowait(T &&item)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
q_.push_back(std::move(item));
|
|
||||||
push_cv_.notify_one();
|
|
||||||
}
|
|
||||||
|
|
||||||
// try to dequeue item. if no item found. wait upto timeout and try again
|
// try to dequeue item. if no item found. wait upto timeout and try again
|
||||||
// Return true, if succeeded dequeue item, false otherwise
|
// Return true, if succeeded dequeue item, false otherwise
|
||||||
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
|
bool dequeue_for(T &popped_item, std::chrono::milliseconds wait_duration)
|
||||||
{
|
{
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
if (!push_cv_.wait_for(lock, wait_duration, [this] { return !this->q_.empty(); }))
|
|
||||||
{
|
{
|
||||||
return false;
|
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||||
|
if (!push_cv_.wait_for(lock, wait_duration, [this] { return this->q_.size() > 0; }))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
popped_item = std::move(q_.front());
|
||||||
|
q_.pop();
|
||||||
}
|
}
|
||||||
popped_item = std::move(q_.front());
|
|
||||||
q_.pop_front();
|
|
||||||
pop_cv_.notify_one();
|
pop_cv_.notify_one();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
size_t overrun_counter()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
return q_.overrun_counter();
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t size()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
|
||||||
return q_.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
size_t max_items_;
|
||||||
std::mutex queue_mutex_;
|
std::mutex queue_mutex_;
|
||||||
std::condition_variable push_cv_;
|
std::condition_variable push_cv_;
|
||||||
std::condition_variable pop_cv_;
|
std::condition_variable pop_cv_;
|
||||||
spdlog::details::circular_q<T> q_;
|
|
||||||
|
std::queue<T> q_;
|
||||||
};
|
};
|
||||||
} // namespace details
|
} // namespace details
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user