mirror of
https://github.com/gabime/spdlog.git
synced 2025-09-29 01:29:35 +08:00
Compare commits
45 Commits
gabime/vis
...
v0.17.0
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 |
113
.clang-format
113
.clang-format
@@ -1,19 +1,108 @@
|
||||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
# BasedOnStyle: LLVM
|
||||
AccessModifierOffset: -4
|
||||
Standard: c++17
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: true
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: Empty
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AlwaysBreakTemplateDeclarations: true
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BraceWrapping:
|
||||
AfterClass: true
|
||||
AfterControlStatement: true
|
||||
AfterEnum: true
|
||||
AfterFunction: true
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: true
|
||||
AfterStruct: true
|
||||
AfterUnion: true
|
||||
BeforeCatch: true
|
||||
BeforeElse: true
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Custom
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: true
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 140
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IndentCaseLabels: false
|
||||
IndentWidth: 4
|
||||
TabWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: true
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerAlignment: Right
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterTemplateKeyword: false
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Cpp11
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
ColumnLimit: 130
|
||||
AlignAfterOpenBracket: Align
|
||||
BinPackParameters: false
|
||||
AlignEscapedNewlines: Left
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
PackConstructorInitializers: Never
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
IndentPPDirectives: BeforeHash
|
||||
SortIncludes: CaseInsensitive
|
||||
...
|
||||
|
||||
|
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,
|
||||
-readability-identifier-length,
|
||||
-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,
|
||||
-modernize-concat-nested-namespaces,
|
||||
-cppcoreguidelines-owning-memory
|
||||
'
|
||||
|
||||
|
||||
WarningsAsErrors: ''
|
||||
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
|
61
.github/workflows/linux.yml
vendored
61
.github/workflows/linux.yml
vendored
@@ -1,61 +0,0 @@
|
||||
name: linux
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
# -----------------------------------------------------------------------
|
||||
# Linux build matrix
|
||||
# -----------------------------------------------------------------------
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
shell: bash
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- { compiler: gcc, version: 9, build_type: Release, cppstd: 17 }
|
||||
- { compiler: gcc, version: 11, build_type: Debug, cppstd: 20 }
|
||||
- { compiler: gcc, version: 12, build_type: Release, cppstd: 20 }
|
||||
- { compiler: gcc, version: 12, build_type: Debug, cppstd: 20, asan: ON }
|
||||
- { compiler: clang, version: 12, build_type: Debug, cppstd: 17 }
|
||||
- { compiler: clang, version: 15, build_type: Debug, cppstd: 20, tsan: ON }
|
||||
container:
|
||||
image: ${{ matrix.config.compiler == 'clang' && 'teeks99/clang-ubuntu' || matrix.config.compiler }}:${{ matrix.config.version }}
|
||||
name: "${{ matrix.config.compiler}} ${{ matrix.config.version }} (C++${{ matrix.config.cppstd }} ${{ matrix.config.build_type }} ${{ matrix.config.asan == 'ON' && 'ASAN' || '' }}${{ matrix.config.tsan == 'ON' && 'TSAN' || '' }})"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup
|
||||
run: |
|
||||
apt-get update
|
||||
apt-get install -y curl git pkg-config libsystemd-dev
|
||||
CMAKE_VERSION="3.24.2"
|
||||
curl -sSL https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-x86_64.sh -o install-cmake.sh
|
||||
chmod +x install-cmake.sh
|
||||
./install-cmake.sh --prefix=/usr/local --skip-license
|
||||
- name: Setup Compiler
|
||||
if: matrix.config.compiler == 'clang'
|
||||
run: |
|
||||
scripts/ci_setup_clang.sh "${{ matrix.config.version }}"
|
||||
echo "CXXFLAGS=-stdlib=libc++" >> $GITHUB_ENV
|
||||
echo "CC=clang-${{ matrix.config.version }}" >> $GITHUB_ENV
|
||||
echo "CXX=clang++-${{ matrix.config.version }}" >> $GITHUB_ENV
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir -p build && cd build
|
||||
cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \
|
||||
-DCMAKE_CXX_STANDARD=${{ matrix.config.cppstd }} \
|
||||
-DSPDLOG_BUILD_EXAMPLE=${{ matrix.config.examples || 'ON' }} \
|
||||
-DSPDLOG_BUILD_WARNINGS=ON \
|
||||
-DSPDLOG_BUILD_BENCH=OFF \
|
||||
-DSPDLOG_BUILD_TESTS=ON \
|
||||
-DSPDLOG_SANITIZE_ADDRESS=${{ matrix.config.asan || 'OFF' }} \
|
||||
-DSPDLOG_SANITIZE_ADDRESS=${{ matrix.config.asan || 'OFF' }} \
|
||||
-DSPDLOG_SANITIZE_THREAD=${{ matrix.config.tsan || 'OFF' }}
|
||||
make -j 4
|
||||
ctest -j 4 --output-on-failure
|
26
.github/workflows/macos.yml
vendored
26
.github/workflows/macos.yml
vendored
@@ -1,26 +0,0 @@
|
||||
name: macos
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: macOS-latest
|
||||
name: "macOS Clang (C++17, Release)"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Build
|
||||
run: |
|
||||
mkdir -p build && cd build
|
||||
cmake .. \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_CXX_STANDARD=17 \
|
||||
-DSPDLOG_BUILD_EXAMPLE=ON \
|
||||
-DSPDLOG_BUILD_WARNINGS=ON \
|
||||
-DSPDLOG_BUILD_BENCH=OFF \
|
||||
-DSPDLOG_BUILD_TESTS=ON \
|
||||
-DSPDLOG_SANITIZE_ADDRESS=OFF
|
||||
make -j 4
|
||||
ctest -j 4 --output-on-failure
|
113
.github/workflows/windows.yml
vendored
113
.github/workflows/windows.yml
vendored
@@ -1,113 +0,0 @@
|
||||
name: windows
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: windows-latest
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
config:
|
||||
- GENERATOR: "Visual Studio 17 2022"
|
||||
BUILD_TYPE: Release
|
||||
BUILD_SHARED: 'ON'
|
||||
FATAL_ERRORS: 'ON'
|
||||
BUILD_EXAMPLE: 'OFF'
|
||||
CXX_STANDARD: 20
|
||||
- GENERATOR: "Visual Studio 17 2022"
|
||||
BUILD_TYPE: Release
|
||||
BUILD_SHARED: 'ON'
|
||||
FATAL_ERRORS: 'ON'
|
||||
BUILD_EXAMPLE: 'OFF'
|
||||
CXX_STANDARD: 17
|
||||
- GENERATOR: "Visual Studio 17 2022"
|
||||
BUILD_TYPE: Release
|
||||
BUILD_SHARED: 'OFF'
|
||||
FATAL_ERRORS: 'ON'
|
||||
BUILD_EXAMPLE: 'ON'
|
||||
CXX_STANDARD: 17
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: CMake ${{ matrix.config.GENERATOR }} CXX=${{matrix.config.CXX_STANDARD}}
|
||||
shell: pwsh
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -G "${{ matrix.config.GENERATOR }}" -A x64 `
|
||||
-D CMAKE_BUILD_TYPE=${{ matrix.config.BUILD_TYPE }} `
|
||||
-D BUILD_SHARED_LIBS=${{ matrix.config.BUILD_SHARED }} `
|
||||
-D SPDLOG_BUILD_EXAMPLE=${{ matrix.config.BUILD_EXAMPLE }} `
|
||||
-D SPDLOG_BUILD_TESTS=ON `
|
||||
-D SPDLOG_BUILD_WARNINGS=${{ matrix.config.FATAL_ERRORS }} `
|
||||
-D CMAKE_CXX_STANDARD=${{ matrix.config.CXX_STANDARD }} ..
|
||||
|
||||
- name: Build
|
||||
shell: pwsh
|
||||
run: |
|
||||
cd build
|
||||
cmake --build . --parallel --config ${{ matrix.config.BUILD_TYPE }}
|
||||
|
||||
- name: Run Tests
|
||||
shell: pwsh
|
||||
env:
|
||||
PATH: ${{ env.PATH }};${{ github.workspace }}\build\_deps\catch2-build\src\${{ matrix.config.BUILD_TYPE }};${{ github.workspace }}\build\bin\${{ matrix.config.BUILD_TYPE }}
|
||||
run: ${{ github.workspace }}\build\bin\${{ matrix.config.BUILD_TYPE }}\spdlog-utests.exe
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# MSVC 2019 build matrix
|
||||
# -----------------------------------------------------------------------
|
||||
build_2019:
|
||||
runs-on: windows-2019
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
config:
|
||||
- GENERATOR: "Visual Studio 16 2019"
|
||||
BUILD_TYPE: Release
|
||||
BUILD_SHARED: 'ON'
|
||||
FATAL_ERRORS: 'ON'
|
||||
BUILD_EXAMPLE: 'ON'
|
||||
CXX_STANDARD: 17
|
||||
- GENERATOR: "Visual Studio 16 2019"
|
||||
BUILD_TYPE: Release
|
||||
BUILD_SHARED: 'ON'
|
||||
FATAL_ERRORS: 'ON'
|
||||
BUILD_EXAMPLE: 'ON'
|
||||
CXX_STANDARD: 20
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: CMake ${{ matrix.config.GENERATOR }} CXX=${{matrix.config.CXX_STANDARD}}
|
||||
shell: pwsh
|
||||
run: |
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -G "${{ matrix.config.GENERATOR }}" -A x64 `
|
||||
-D CMAKE_BUILD_TYPE=${{ matrix.config.BUILD_TYPE }} `
|
||||
-D BUILD_SHARED_LIBS=${{ matrix.config.BUILD_SHARED }} `
|
||||
-D SPDLOG_BUILD_EXAMPLE=${{ matrix.config.BUILD_EXAMPLE }} `
|
||||
-D SPDLOG_BUILD_TESTS=ON `
|
||||
-D SPDLOG_BUILD_WARNINGS=${{ matrix.config.FATAL_ERRORS }} `
|
||||
-D CMAKE_CXX_STANDARD=${{ matrix.config.CXX_STANDARD }} ..
|
||||
|
||||
- name: Build
|
||||
shell: pwsh
|
||||
run: |
|
||||
cd build
|
||||
cmake --build . --parallel --config ${{ matrix.config.BUILD_TYPE }}
|
||||
|
||||
- name: Run Tests
|
||||
shell: pwsh
|
||||
env:
|
||||
PATH: ${{ env.PATH }};${{ github.workspace }}\build\_deps\catch2-build\src\${{ matrix.config.BUILD_TYPE }};${{ github.workspace }}\build\bin\${{ matrix.config.BUILD_TYPE }}
|
||||
run: ${{ github.workspace }}\build\bin\${{ matrix.config.BUILD_TYPE }}\spdlog-utests.exe
|
42
.gitignore
vendored
42
.gitignore
vendored
@@ -1,7 +1,4 @@
|
||||
# Auto generated files
|
||||
include/spdlog/spdlog_config.h
|
||||
[Dd]ebug/
|
||||
[Rr]elease/
|
||||
build/*
|
||||
*.slo
|
||||
*.lo
|
||||
@@ -37,16 +34,23 @@ build/*
|
||||
# Codelite
|
||||
.codelite
|
||||
|
||||
# KDevelop
|
||||
*.kdev4
|
||||
|
||||
# .orig files
|
||||
*.orig
|
||||
|
||||
# example files
|
||||
example/*
|
||||
!example/example.cpp
|
||||
!example/bench.cpp
|
||||
!example/utils.h
|
||||
!example/Makefile*
|
||||
!example/example.sln
|
||||
!example/example.vcxproj
|
||||
!example/CMakeLists.txt
|
||||
!example/multisink.cpp
|
||||
!example/jni
|
||||
|
||||
# generated files
|
||||
generated
|
||||
version.rc
|
||||
|
||||
# Cmake
|
||||
CMakeCache.txt
|
||||
@@ -59,29 +63,9 @@ install_manifest.txt
|
||||
/tests/tests.VC.db
|
||||
/tests/tests
|
||||
/tests/logs/*
|
||||
spdlogConfig.cmake
|
||||
spdlogConfigVersion.cmake
|
||||
compile_commands.json
|
||||
|
||||
# idea
|
||||
.idea/
|
||||
.cache/
|
||||
.vscode/
|
||||
cmake-build-*/
|
||||
*.db
|
||||
*.ipch
|
||||
*.filters
|
||||
*.db-wal
|
||||
*.opendb
|
||||
*.db-shm
|
||||
*.vcxproj
|
||||
*.tcl
|
||||
*.user
|
||||
*.sln
|
||||
|
||||
# macos
|
||||
*.DS_store
|
||||
*.xcodeproj/
|
||||
/.vs
|
||||
/out/build
|
||||
/CMakeSettings.json
|
||||
# vscode
|
||||
.vscode/
|
91
.travis.yml
Normal file
91
.travis.yml
Normal file
@@ -0,0 +1,91 @@
|
||||
# Adapted from various sources, including:
|
||||
# - Louis Dionne's Hana: https://github.com/ldionne/hana
|
||||
# - Paul Fultz II's FIT: https://github.com/pfultz2/Fit
|
||||
# - Eric Niebler's range-v3: https://github.com/ericniebler/range-v3
|
||||
language: cpp
|
||||
|
||||
# Test matrix:
|
||||
# - Build matrix per compiler: C++11/C++14 + Debug/Release
|
||||
# - Optionally: AddressSanitizer (ASAN)
|
||||
# - Valgrind: all release builds are also tested with valgrind
|
||||
# - clang 3.4, 3.5, 3.6, trunk
|
||||
# - Note: 3.4 and trunk are tested with/without ASAN,
|
||||
# the rest is only tested with ASAN=On.
|
||||
# - gcc 4.9, 5.0
|
||||
#
|
||||
matrix:
|
||||
include:
|
||||
|
||||
# Test gcc-4.8: C++11, Build=Debug/Release, ASAN=Off
|
||||
- env: GCC_VERSION=4.8 BUILD_TYPE=Debug CPP=11 ASAN=Off LIBCXX=Off
|
||||
os: linux
|
||||
addons: &gcc48
|
||||
apt:
|
||||
packages:
|
||||
- g++-4.8
|
||||
- valgrind
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
- env: GCC_VERSION=4.8 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=Off
|
||||
os: linux
|
||||
addons: *gcc48
|
||||
|
||||
# Test gcc-4.9: C++11, Build=Debug/Release, ASAN=Off
|
||||
- env: GCC_VERSION=4.9 BUILD_TYPE=Debug CPP=11 ASAN=Off LIBCXX=Off
|
||||
os: linux
|
||||
addons: &gcc49
|
||||
apt:
|
||||
packages:
|
||||
- g++-4.9
|
||||
- valgrind
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
|
||||
- env: GCC_VERSION=4.9 BUILD_TYPE=Release CPP=11 ASAN=Off LIBCXX=Off
|
||||
os: linux
|
||||
addons: *gcc49
|
||||
|
||||
# Install dependencies
|
||||
before_install:
|
||||
- export CHECKOUT_PATH=`pwd`;
|
||||
- if [ -n "$GCC_VERSION" ]; then export CXX="g++-${GCC_VERSION}" CC="gcc-${GCC_VERSION}"; fi
|
||||
- if [ -n "$CLANG_VERSION" ]; then export CXX="clang++-${CLANG_VERSION}" CC="clang-${CLANG_VERSION}"; fi
|
||||
- if [ "$CLANG_VERSION" == "3.4" ]; then export CXX="/usr/local/clang-3.4/bin/clang++" CC="/usr/local/clang-3.4/bin/clang"; fi
|
||||
- which $CXX
|
||||
- which $CC
|
||||
- which valgrind
|
||||
- if [ -n "$CLANG_VERSION" ]; then sudo CXX=$CXX CC=$CC ./tests/install_libcxx.sh; fi
|
||||
|
||||
install:
|
||||
- cd $CHECKOUT_PATH
|
||||
|
||||
# Workaround for valgrind bug: https://bugs.kde.org/show_bug.cgi?id=326469.
|
||||
# It is fixed in valgrind 3.10 so this won't be necessary if someone
|
||||
# replaces the current valgrind (3.7) with valgrind-3.10
|
||||
- sed -i 's/march=native/msse4.2/' example/Makefile
|
||||
|
||||
- if [ ! -d build ]; then mkdir build; fi
|
||||
- export CXX_FLAGS="-I${CHECKOUT_PATH}/include"
|
||||
- export CXX_LINKER_FLAGS=""
|
||||
- if [ -z "$BUILD_TYPE" ]; then export BUILD_TYPE=Release; fi
|
||||
- if [ "$ASAN" == "On"]; then export CXX_FLAGS="${CXX_FLAGS} -fsanitize=address,undefined,integer -fno-omit-frame-pointer -fno-sanitize=unsigned-integer-overflow"; fi
|
||||
- if [ -n "$CLANG_VERSION" ]; then CXX_FLAGS="${CXX_FLAGS} -D__extern_always_inline=inline"; fi
|
||||
- if [ "$LIBCXX" == "On" ]; then CXX_FLAGS="${CXX_FLAGS} -stdlib=libc++ -I/usr/include/c++/v1/"; fi
|
||||
- if [ "$LIBCXX" == "On" ]; then CXX_LINKER_FLAGS="${CXX_FLAGS} -L/usr/lib/ -lc++"; fi
|
||||
- CXX_FLAGS="${CXX_FLAGS} -std=c++${CPP}"
|
||||
|
||||
# Build examples
|
||||
- cd example
|
||||
- if [ "$BUILD_TYPE" == "Release" ]; then make rebuild CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example; fi
|
||||
- if [ "$BUILD_TYPE" == "Debug" ]; then make rebuild debug CXXFLAGS="${CXX_FLAGS} ${CXX_LINKER_FLAGS}" VERBOSE=1; export BIN=example-debug; fi
|
||||
|
||||
|
||||
script:
|
||||
- ./"${BIN}"
|
||||
- valgrind --trace-children=yes --leak-check=full ./"${BIN}"
|
||||
- cd $CHECKOUT_PATH/tests; make rebuild; ./tests
|
||||
- cd $CHECKOUT_PATH/tests; STYLE=printf make rebuild; ./tests
|
||||
|
||||
notifications:
|
||||
email: false
|
445
CMakeLists.txt
445
CMakeLists.txt
@@ -1,374 +1,115 @@
|
||||
# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Start spdlog project
|
||||
# ---------------------------------------------------------------------------------------
|
||||
include(cmake/utils.cmake)
|
||||
include(cmake/ide.cmake)
|
||||
|
||||
spdlog_extract_version()
|
||||
|
||||
project(spdlog VERSION ${SPDLOG_VERSION} LANGUAGES CXX)
|
||||
#
|
||||
# Copyright(c) 2015 Ruslan Baratov.
|
||||
# Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(spdlog VERSION 0.17.0 LANGUAGES CXX)
|
||||
include(CTest)
|
||||
include(CMakeDependentOption)
|
||||
include(GNUInstallDirs)
|
||||
|
||||
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Set default build to release
|
||||
# ---------------------------------------------------------------------------------------
|
||||
if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
|
||||
set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE)
|
||||
endif ()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Compiler config
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# c++ standard >=17 is required
|
||||
if (NOT DEFINED CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
elseif (CMAKE_CXX_STANDARD LESS 17)
|
||||
message(FATAL_ERROR "Minimum supported CMAKE_CXX_STANDARD is 17, but it is set to ${CMAKE_CXX_STANDARD}")
|
||||
endif ()
|
||||
#---------------------------------------------------------------------------------------
|
||||
# compiler config
|
||||
#---------------------------------------------------------------------------------------
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
|
||||
set(CMAKE_CXX_FLAGS "-Wall -O3 ${CMAKE_CXX_FLAGS}")
|
||||
endif()
|
||||
|
||||
if (CMAKE_SYSTEM_NAME MATCHES "CYGWIN" OR CMAKE_SYSTEM_NAME MATCHES "MSYS" OR CMAKE_SYSTEM_NAME MATCHES "MINGW")
|
||||
set(CMAKE_CXX_EXTENSIONS ON)
|
||||
endif ()
|
||||
#---------------------------------------------------------------------------------------
|
||||
# spdlog target
|
||||
#---------------------------------------------------------------------------------------
|
||||
add_library(spdlog INTERFACE)
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# 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_EXAMPLES "Build examples" OFF)
|
||||
cmake_dependent_option(SPDLOG_BUILD_TESTING
|
||||
"Build spdlog tests" ON
|
||||
"BUILD_TESTING" OFF
|
||||
)
|
||||
|
||||
option(SPDLOG_BUILD_ALL "Build all artifacts" OFF)
|
||||
|
||||
# build shared option
|
||||
option(SPDLOG_BUILD_SHARED "Build shared library" OFF)
|
||||
|
||||
# example options
|
||||
option(SPDLOG_BUILD_EXAMPLE "Build example" ${SPDLOG_MASTER_PROJECT})
|
||||
|
||||
# testing options
|
||||
option(SPDLOG_BUILD_TESTS "Build tests" OFF)
|
||||
|
||||
# bench options
|
||||
option(SPDLOG_BUILD_BENCH "Build benchmarks (Requires https://github.com/google/benchmark.git to be installed)" OFF)
|
||||
|
||||
# sanitizer options
|
||||
option(SPDLOG_SANITIZE_ADDRESS "Enable address sanitizer in tests" OFF)
|
||||
|
||||
# warning options
|
||||
option(SPDLOG_BUILD_WARNINGS "Enable compiler warnings" OFF)
|
||||
|
||||
# install options
|
||||
option(SPDLOG_SYSTEM_INCLUDES "Include as system headers (skip for clang-tidy)." OFF)
|
||||
option(SPDLOG_INSTALL "Generate the install target" ${SPDLOG_MASTER_PROJECT})
|
||||
option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of of fetching from gitub." OFF)
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
|
||||
option(SPDLOG_CLOCK_COARSE "Use CLOCK_REALTIME_COARSE instead of the regular clock," OFF)
|
||||
else ()
|
||||
set(SPDLOG_CLOCK_COARSE OFF CACHE BOOL "non supported option" FORCE)
|
||||
endif ()
|
||||
|
||||
option(SPDLOG_PREVENT_CHILD_FD "Prevent from child processes to inherit log file descriptors" OFF)
|
||||
option(SPDLOG_NO_THREAD_ID "prevent spdlog from querying the thread id on each log call if thread id is not needed" OFF)
|
||||
option(SPDLOG_DISABLE_GLOBAL_LOGGER "Disable global logger creation" OFF)
|
||||
option(SPDLOG_NO_TLS "Disable thread local storage" OFF)
|
||||
|
||||
# clang-tidy
|
||||
option(SPDLOG_TIDY "run clang-tidy" OFF)
|
||||
|
||||
if (SPDLOG_TIDY)
|
||||
set(CMAKE_CXX_CLANG_TIDY "clang-tidy")
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
message(STATUS "Enabled clang-tidy")
|
||||
endif ()
|
||||
|
||||
if (SPDLOG_BUILD_SHARED)
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
|
||||
endif ()
|
||||
|
||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
|
||||
# place dlls and libs and executables in the same directory
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/$<CONFIG>)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/$<CONFIG>)
|
||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/$<CONFIG>)
|
||||
set(CMAKE_PDB_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/$<CONFIG>)
|
||||
|
||||
# make sure __cplusplus is defined
|
||||
add_compile_options(/Zc:__cplusplus)
|
||||
# enable parallel build for the solution
|
||||
add_compile_options(/MP)
|
||||
endif ()
|
||||
|
||||
message(STATUS "spdlog version: ${SPDLOG_VERSION}")
|
||||
message(STATUS "spdlog build type: " ${CMAKE_BUILD_TYPE})
|
||||
message(STATUS "spdlog build shared: " ${BUILD_SHARED_LIBS})
|
||||
message(STATUS "spdlog fmt external: " ${SPDLOG_FMT_EXTERNAL})
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Find {fmt} library
|
||||
# ---------------------------------------------------------------------------------------
|
||||
if (SPDLOG_FMT_EXTERNAL)
|
||||
find_package(fmt REQUIRED)
|
||||
message(STATUS "Using external fmt lib version: ${fmt_VERSION}")
|
||||
else ()
|
||||
include(cmake/fmtlib.cmake)
|
||||
endif ()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Threads library is required
|
||||
# ---------------------------------------------------------------------------------------
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Library sources
|
||||
# ---------------------------------------------------------------------------------------
|
||||
set(SPDLOG_HEADERS
|
||||
"include/spdlog/common.h"
|
||||
"include/spdlog/formatter.h"
|
||||
"include/spdlog/fwd.h"
|
||||
"include/spdlog/logger.h"
|
||||
"include/spdlog/pattern_formatter.h"
|
||||
"include/spdlog/source_loc.h"
|
||||
"include/spdlog/spdlog.h"
|
||||
"include/spdlog/stopwatch.h"
|
||||
"include/spdlog/version.h"
|
||||
"include/spdlog/details/circular_q.h"
|
||||
"include/spdlog/details/file_helper.h"
|
||||
"include/spdlog/details/fmt_helper.h"
|
||||
"include/spdlog/details/log_msg.h"
|
||||
"include/spdlog/details/async_log_msg.h"
|
||||
"include/spdlog/details/mpmc_blocking_q.h"
|
||||
"include/spdlog/details/null_mutex.h"
|
||||
"include/spdlog/details/os.h"
|
||||
"include/spdlog/details/err_helper.h"
|
||||
"include/spdlog/bin_to_hex.h"
|
||||
"include/spdlog/sinks/android_sink.h"
|
||||
"include/spdlog/sinks/base_sink.h"
|
||||
"include/spdlog/sinks/basic_file_sink.h"
|
||||
"include/spdlog/sinks/callback_sink.h"
|
||||
"include/spdlog/sinks/daily_file_sink.h"
|
||||
"include/spdlog/sinks/dist_sink.h"
|
||||
"include/spdlog/sinks/dup_filter_sink.h"
|
||||
"include/spdlog/sinks/hourly_file_sink.h"
|
||||
"include/spdlog/sinks/kafka_sink.h"
|
||||
"include/spdlog/sinks/mongo_sink.h"
|
||||
"include/spdlog/sinks/msvc_sink.h"
|
||||
"include/spdlog/sinks/null_sink.h"
|
||||
"include/spdlog/sinks/ostream_sink.h"
|
||||
"include/spdlog/sinks/qt_sinks.h"
|
||||
"include/spdlog/sinks/ringbuffer_sink.h"
|
||||
"include/spdlog/sinks/rotating_file_sink.h"
|
||||
"include/spdlog/sinks/sink.h"
|
||||
"include/spdlog/sinks/stdout_color_sinks.h"
|
||||
"include/spdlog/sinks/stdout_sinks.h"
|
||||
"include/spdlog/sinks/syslog_sink.h"
|
||||
"include/spdlog/sinks/systemd_sink.h"
|
||||
"include/spdlog/sinks/tcp_sink.h"
|
||||
"include/spdlog/sinks/udp_sink.h"
|
||||
"include/spdlog/sinks/async_sink.h")
|
||||
|
||||
set(SPDLOG_SRCS
|
||||
"src/common.cpp"
|
||||
"src/logger.cpp"
|
||||
"src/pattern_formatter.cpp"
|
||||
"src/spdlog.cpp"
|
||||
"src/details/file_helper.cpp"
|
||||
"src/details/os_filesystem.cpp"
|
||||
"src/details/log_msg.cpp"
|
||||
"src/details/async_log_msg.cpp"
|
||||
"src/details/err_helper.cpp"
|
||||
"src/sinks/base_sink.cpp"
|
||||
"src/sinks/basic_file_sink.cpp"
|
||||
"src/sinks/rotating_file_sink.cpp"
|
||||
"src/sinks/stdout_sinks.cpp"
|
||||
"src/sinks/async_sink.cpp")
|
||||
|
||||
if (WIN32)
|
||||
list(APPEND SPDLOG_SRCS
|
||||
"src/details/os_windows.cpp"
|
||||
"src/sinks/wincolor_sink.cpp")
|
||||
list(APPEND SPDLOG_HEADERS
|
||||
"include/spdlog/sinks/wincolor_sink.h"
|
||||
"include/spdlog/details/tcp_client_windows.h"
|
||||
"include/spdlog/details/udp_client_windows.h"
|
||||
"include/spdlog/details/windows_include.h"
|
||||
"include/spdlog/sinks/win_eventlog_sink.h")
|
||||
else ()
|
||||
list(APPEND SPDLOG_SRCS
|
||||
"src/details/os_unix.cpp"
|
||||
"src/sinks/ansicolor_sink.cpp")
|
||||
list(APPEND SPDLOG_HEADERS
|
||||
"include/spdlog/details/tcp_client_unix.h"
|
||||
"include/spdlog/details/udp_client_unix.h"
|
||||
"include/spdlog/sinks/ansicolor_sink.h")
|
||||
endif ()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Check if fwrite_unlocked/_fwrite_nolock is available
|
||||
# ---------------------------------------------------------------------------------------
|
||||
include(CheckSymbolExists)
|
||||
if (WIN32)
|
||||
check_symbol_exists(_fwrite_nolock "stdio.h" HAVE_FWRITE_UNLOCKED)
|
||||
else ()
|
||||
check_symbol_exists(fwrite_unlocked "stdio.h" HAVE_FWRITE_UNLOCKED)
|
||||
endif ()
|
||||
if (HAVE_FWRITE_UNLOCKED)
|
||||
set(SPDLOG_FWRITE_UNLOCKED 1)
|
||||
endif ()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# spdlog library
|
||||
# ---------------------------------------------------------------------------------------
|
||||
if (BUILD_SHARED_LIBS)
|
||||
if (WIN32)
|
||||
set(VERSION_RC ${CMAKE_CURRENT_BINARY_DIR}/version.rc)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY)
|
||||
endif ()
|
||||
add_library(spdlog SHARED ${VERSION_RC})
|
||||
target_compile_definitions(spdlog PUBLIC SPDLOG_SHARED_LIB)
|
||||
set_target_properties(spdlog PROPERTIES
|
||||
CXX_VISIBILITY_PRESET hidden
|
||||
VISIBILITY_INLINES_HIDDEN ON)
|
||||
|
||||
if (MSVC)
|
||||
# disable dlls related warnings on msvc
|
||||
target_compile_options(spdlog PUBLIC $<$<AND:$<CXX_COMPILER_ID:MSVC>,$<NOT:$<COMPILE_LANGUAGE:CUDA>>>:/wd4251
|
||||
/wd4275>)
|
||||
endif ()
|
||||
else ()
|
||||
add_library(spdlog STATIC)
|
||||
endif ()
|
||||
add_library(spdlog::spdlog ALIAS spdlog)
|
||||
target_sources(spdlog PRIVATE ${SPDLOG_SRCS})
|
||||
target_sources(
|
||||
target_include_directories(
|
||||
spdlog
|
||||
PUBLIC FILE_SET pub_headers
|
||||
TYPE HEADERS
|
||||
BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||
FILES ${SPDLOG_HEADERS})
|
||||
INTERFACE
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
|
||||
)
|
||||
|
||||
set(SPDLOG_INCLUDES_LEVEL "")
|
||||
if (SPDLOG_SYSTEM_INCLUDES)
|
||||
set(SPDLOG_INCLUDES_LEVEL "SYSTEM")
|
||||
endif ()
|
||||
set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
|
||||
target_include_directories(spdlog ${SPDLOG_INCLUDES_LEVEL} PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
|
||||
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
|
||||
|
||||
target_link_libraries(spdlog PUBLIC Threads::Threads)
|
||||
target_link_libraries(spdlog PUBLIC fmt::fmt)
|
||||
spdlog_enable_warnings(spdlog)
|
||||
set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION
|
||||
${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR})
|
||||
|
||||
set(SPDLOG_NAME spdlog-${SPDLOG_VERSION_MAJOR})
|
||||
set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX "-${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR}d")
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# set source groups for visual studio
|
||||
# ---------------------------------------------------------------------------------------
|
||||
if (CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/include PREFIX include FILES ${SPDLOG_HEADERS})
|
||||
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR}/src PREFIX sources FILES ${SPDLOG_SRCS})
|
||||
source_group(sources FILES ${VERSION_RC})
|
||||
endif ()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Add required libraries for Android CMake build
|
||||
# ---------------------------------------------------------------------------------------
|
||||
if (ANDROID)
|
||||
target_link_libraries(spdlog PUBLIC log)
|
||||
endif ()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# spdlog private defines according to the options
|
||||
# ---------------------------------------------------------------------------------------
|
||||
foreach (SPDLOG_OPTION
|
||||
SPDLOG_CLOCK_COARSE
|
||||
SPDLOG_PREVENT_CHILD_FD
|
||||
SPDLOG_NO_THREAD_ID
|
||||
SPDLOG_DISABLE_GLOBAL_LOGGER
|
||||
SPDLOG_NO_TLS
|
||||
SPDLOG_FWRITE_UNLOCKED)
|
||||
if (${SPDLOG_OPTION})
|
||||
target_compile_definitions(spdlog PRIVATE ${SPDLOG_OPTION})
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Build binaries
|
||||
# ---------------------------------------------------------------------------------------
|
||||
if (SPDLOG_BUILD_EXAMPLE OR SPDLOG_BUILD_ALL)
|
||||
message(STATUS "Generating example(s)")
|
||||
if(SPDLOG_BUILD_EXAMPLES)
|
||||
add_subdirectory(example)
|
||||
spdlog_enable_warnings(example)
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
if (SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_ALL)
|
||||
message(STATUS "Generating tests")
|
||||
enable_testing()
|
||||
if(SPDLOG_BUILD_TESTING)
|
||||
add_subdirectory(tests)
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
if (SPDLOG_BUILD_BENCH OR SPDLOG_BUILD_ALL)
|
||||
message(STATUS "Generating benchmarks")
|
||||
add_subdirectory(bench)
|
||||
endif ()
|
||||
#---------------------------------------------------------------------------------------
|
||||
# Install/export targets and files
|
||||
#---------------------------------------------------------------------------------------
|
||||
# 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}::")
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Install
|
||||
# ---------------------------------------------------------------------------------------
|
||||
if (SPDLOG_INSTALL)
|
||||
message(STATUS "Generating install")
|
||||
set(project_config_in "${CMAKE_CURRENT_LIST_DIR}/cmake/spdlogConfig.cmake.in")
|
||||
set(project_config_out "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfig.cmake")
|
||||
set(config_targets_file "spdlogConfigTargets.cmake")
|
||||
set(version_config_file "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfigVersion.cmake")
|
||||
set(export_dest_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${SPDLOG_NAME}")
|
||||
# generate package version file
|
||||
include(CMakePackageConfigHelpers)
|
||||
write_basic_package_version_file(
|
||||
"${version_config}" COMPATIBILITY SameMajorVersion
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Include files
|
||||
# ---------------------------------------------------------------------------------------
|
||||
set(installed_include_dir "${CMAKE_INSTALL_INCLUDEDIR}/${SPDLOG_NAME}")
|
||||
install(
|
||||
# configure pkg config file
|
||||
configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY)
|
||||
|
||||
# install targets
|
||||
install(
|
||||
TARGETS spdlog
|
||||
EXPORT spdlogTargets
|
||||
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/${SPDLOG_NAME}"
|
||||
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/${SPDLOG_NAME}"
|
||||
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/${SPDLOG_NAME}"
|
||||
FILE_SET pub_headers
|
||||
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${SPDLOG_NAME}")
|
||||
message(STATUS "Installing spdlog in ${CMAKE_INSTALL_LIBDIR}/${SPDLOG_NAME}")
|
||||
EXPORT "${targets_export_name}"
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Install CMake config files
|
||||
# ---------------------------------------------------------------------------------------
|
||||
install(EXPORT spdlogTargets DESTINATION ${export_dest_dir} NAMESPACE spdlog:: FILE ${config_targets_file})
|
||||
# install headers
|
||||
install(
|
||||
DIRECTORY "${HEADER_BASE}/${PROJECT_NAME}"
|
||||
DESTINATION "${include_install_dir}"
|
||||
)
|
||||
|
||||
include(CMakePackageConfigHelpers)
|
||||
configure_package_config_file("${project_config_in}" "${project_config_out}" INSTALL_DESTINATION ${export_dest_dir})
|
||||
# install project version file
|
||||
install(
|
||||
FILES "${version_config}"
|
||||
DESTINATION "${config_install_dir}"
|
||||
)
|
||||
|
||||
write_basic_package_version_file("${version_config_file}" COMPATIBILITY SameMajorVersion)
|
||||
install(FILES "${project_config_out}" "${version_config_file}" DESTINATION "${export_dest_dir}")
|
||||
# install pkg config file
|
||||
install(
|
||||
FILES "${pkg_config}"
|
||||
DESTINATION "${pkgconfig_install_dir}"
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# Support creation of installable packages
|
||||
# ---------------------------------------------------------------------------------------
|
||||
include(cmake/spdlogCPack.cmake)
|
||||
endif ()
|
||||
# install project config file
|
||||
install(
|
||||
EXPORT "${targets_export_name}"
|
||||
NAMESPACE "${namespace}"
|
||||
DESTINATION "${config_install_dir}"
|
||||
FILE ${project_config}
|
||||
)
|
||||
|
||||
# export build directory config file
|
||||
export(
|
||||
EXPORT ${targets_export_name}
|
||||
NAMESPACE "${namespace}"
|
||||
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})
|
||||
|
9
INSTALL
9
INSTALL
@@ -1,12 +1,13 @@
|
||||
CMake:
|
||||
add_executable(example example.cpp)
|
||||
target_link_libraries(example spdlog::spdlog)
|
||||
spdlog is header only library.
|
||||
Just copy the files to your build tree and use a C++11 compiler
|
||||
|
||||
Tested on:
|
||||
gcc 4.8.1 and above
|
||||
clang 3.5
|
||||
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
|
||||
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://raw.githubusercontent.com/fmtlib/fmt/master/LICENSE
|
||||
|
||||
|
575
README.md
575
README.md
@@ -1,462 +1,221 @@
|
||||
# spdlog
|
||||
|
||||
Very fast, header only, C++ logging library. [](https://travis-ci.org/gabime/spdlog) [](https://ci.appveyor.com/project/gabime/spdlog)
|
||||
|
||||
[](https://github.com/gabime/spdlog/actions/workflows/linux.yml)
|
||||
[](https://github.com/gabime/spdlog/actions/workflows/windows.yml)
|
||||
[](https://github.com/gabime/spdlog/actions/workflows/macos.yml)
|
||||
[](https://github.com/gabime/spdlog/releases/latest)
|
||||
|
||||
Fast C++ logging library
|
||||
|
||||
## Install
|
||||
```console
|
||||
$ git clone https://github.com/gabime/spdlog.git
|
||||
$ cd spdlog && mkdir build && cd build
|
||||
$ cmake .. && cmake --build .
|
||||
```
|
||||
#### Just copy the headers:
|
||||
|
||||
* Copy the source [folder](https://github.com/gabime/spdlog/tree/master/include/spdlog) to your build tree and use a C++11 compiler.
|
||||
|
||||
#### Or use your favorite package manager:
|
||||
|
||||
* 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`
|
||||
|
||||
see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v2.x/example/CMakeLists.txt) on how to use.
|
||||
|
||||
## Platforms
|
||||
* Linux, FreeBSD, OpenBSD, Solaris, AIX
|
||||
* Windows (msvc, cygwin)
|
||||
* macOS
|
||||
* Android
|
||||
|
||||
## Package managers:
|
||||
* Debian: `sudo apt install libspdlog-dev`
|
||||
* Homebrew: `brew install spdlog`
|
||||
* MacPorts: `sudo port install spdlog`
|
||||
* FreeBSD: `pkg install spdlog`
|
||||
* Fedora: `dnf install spdlog`
|
||||
* Gentoo: `emerge dev-libs/spdlog`
|
||||
* Arch Linux: `pacman -S spdlog`
|
||||
* openSUSE: `sudo zypper in spdlog-devel`
|
||||
* vcpkg: `vcpkg install spdlog`
|
||||
* conan: `conan install --requires=spdlog/[*]`
|
||||
* conda: `conda install -c conda-forge spdlog`
|
||||
* build2: ```depends: spdlog ^1.8.2```
|
||||
|
||||
* Linux, FreeBSD, Solaris
|
||||
* Windows (vc 2013+, cygwin)
|
||||
* Mac OSX (clang 3.5+)
|
||||
* Android
|
||||
|
||||
## Features
|
||||
* Very fast (see [benchmarks](#benchmarks) below).
|
||||
* Headers only or compiled
|
||||
* Feature-rich formatting, using the excellent [fmt](https://github.com/fmtlib/fmt) library.
|
||||
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
|
||||
* Headers only, just copy and use.
|
||||
* Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library.
|
||||
* Optional printf syntax support.
|
||||
* Asynchronous mode (optional)
|
||||
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
|
||||
* Conditional Logging
|
||||
* Multi/Single threaded loggers.
|
||||
* Various log targets:
|
||||
* Rotating log files.
|
||||
* Daily log files.
|
||||
* Console logging (colors supported).
|
||||
* syslog.
|
||||
* Windows event log.
|
||||
* Windows debugger (```OutputDebugString(..)```).
|
||||
* Log to Qt widgets ([example](#log-to-qt-with-nice-colors)).
|
||||
* Easily [extendable](https://github.com/gabime/spdlog/wiki/4.-Sinks#implementing-your-own-sink) with custom log targets.
|
||||
* Log filtering - log levels can be modified at runtime as well as compile time.
|
||||
* Support for loading log levels from argv or environment var.
|
||||
* Windows debugger (```OutputDebugString(..)```)
|
||||
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
|
||||
* Severity based filtering - threshold levels can be modified in runtime as well as in compile time.
|
||||
|
||||
## 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++
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
int main()
|
||||
{
|
||||
spdlog::info("Welcome to spdlog!");
|
||||
spdlog::error("Some error message with arg: {}", 1);
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
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");
|
||||
void async_example();
|
||||
void syslog_example();
|
||||
void user_defined_example();
|
||||
void err_handler_example();
|
||||
|
||||
spdlog::set_level(spdlog::level::debug); // Set global log level to debug
|
||||
spdlog::debug("This message should be displayed..");
|
||||
|
||||
// change log pattern
|
||||
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
|
||||
|
||||
// Compile time log levels
|
||||
// define SPDLOG_ACTIVE_LEVEL to desired level
|
||||
SPDLOG_TRACE("Some trace message with param {}", 42);
|
||||
SPDLOG_DEBUG("Some debug message");
|
||||
}
|
||||
|
||||
```
|
||||
---
|
||||
#### Create stdout/stderr logger object
|
||||
```c++
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||
void stdout_example()
|
||||
{
|
||||
// create a color multi-threaded logger
|
||||
auto console = spdlog::stdout_color_mt("console");
|
||||
auto err_logger = spdlog::stderr_color_mt("stderr");
|
||||
spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
#### Basic file logger
|
||||
```c++
|
||||
#include "spdlog/sinks/basic_file_sink.h"
|
||||
void basic_logfile_example()
|
||||
namespace spd = spdlog;
|
||||
int main(int, char*[])
|
||||
{
|
||||
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;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
```
|
||||
---
|
||||
#### Rotating files
|
||||
```c++
|
||||
#include "spdlog/sinks/rotating_file_sink.h"
|
||||
void rotating_example()
|
||||
{
|
||||
// Create a file rotating logger with 5 MB size max and 3 rotated files
|
||||
auto max_size = 1048576 * 5;
|
||||
auto max_files = 3;
|
||||
auto logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", max_size, max_files);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
#### Daily files
|
||||
```c++
|
||||
|
||||
#include "spdlog/sinks/daily_file_sink.h"
|
||||
void daily_example()
|
||||
{
|
||||
// Create a daily logger - a new file is created every day at 2:30 am
|
||||
auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
#### Periodic flush
|
||||
```c++
|
||||
// periodically flush all *registered* loggers every 3 seconds:
|
||||
// warning: only use if all your loggers are thread-safe ("_mt" loggers)
|
||||
spdlog::flush_every(std::chrono::seconds(3));
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
#### Stopwatch
|
||||
```c++
|
||||
// Stopwatch support for spdlog
|
||||
#include "spdlog/stopwatch.h"
|
||||
void stopwatch_example()
|
||||
{
|
||||
spdlog::stopwatch sw;
|
||||
spdlog::debug("Elapsed {}", sw);
|
||||
spdlog::debug("Elapsed {:.3}", sw);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
#### Log binary data in hex
|
||||
```c++
|
||||
// many types of std::container<char> types can be used.
|
||||
// ranges are supported too.
|
||||
// format flags:
|
||||
// {:X} - print in uppercase.
|
||||
// {:s} - don't separate each byte with space.
|
||||
// {:p} - don't print the position on each line start.
|
||||
// {:n} - don't split the output into lines.
|
||||
// {:a} - show ASCII if :n is not set.
|
||||
|
||||
#include "spdlog/fmt/bin_to_hex.h"
|
||||
|
||||
void binary_example()
|
||||
{
|
||||
auto console = spdlog::get("console");
|
||||
std::array<char, 80> buf;
|
||||
console->info("Binary example: {}", spdlog::to_hex(buf));
|
||||
console->info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10));
|
||||
// more examples:
|
||||
// logger->info("uppercase: {:X}", spdlog::to_hex(buf));
|
||||
// logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf));
|
||||
// logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf));
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
#### Logger with multi sinks - each with a different format and log level
|
||||
```c++
|
||||
|
||||
// create a logger with 2 targets, with different log levels and formats.
|
||||
// The console will show only warnings or errors, while the file will log all.
|
||||
void multi_sink_example()
|
||||
{
|
||||
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
||||
console_sink->set_level(spdlog::level::warn);
|
||||
console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v");
|
||||
|
||||
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/multisink.txt", true);
|
||||
file_sink->set_level(spdlog::level::trace);
|
||||
|
||||
spdlog::logger logger("multi_sink", {console_sink, file_sink});
|
||||
logger.set_level(spdlog::level::debug);
|
||||
logger.warn("this should appear in both console and file");
|
||||
logger.info("this message should not appear in the console, only in the file");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
#### User-defined callbacks about log events
|
||||
```c++
|
||||
|
||||
// create a logger with a lambda function callback, the callback will be called
|
||||
// each time something is logged to the logger
|
||||
void callback_example()
|
||||
{
|
||||
auto callback_sink = std::make_shared<spdlog::sinks::callback_sink_mt>([](const spdlog::details::log_msg &msg) {
|
||||
// for example you can be notified by sending an email to yourself
|
||||
});
|
||||
callback_sink->set_level(spdlog::level::err);
|
||||
|
||||
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
|
||||
spdlog::logger logger("custom_callback_logger", {console_sink, callback_sink});
|
||||
|
||||
logger.info("some info log");
|
||||
logger.error("critical issue"); // will notify you
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
#### Asynchronous logging
|
||||
```c++
|
||||
#include "spdlog/async.h"
|
||||
#include "spdlog/sinks/basic_file_sink.h"
|
||||
void async_example()
|
||||
{
|
||||
// TODO
|
||||
size_t q_size = 4096;
|
||||
spd::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);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
#### User-defined types
|
||||
```c++
|
||||
template<>
|
||||
struct fmt::formatter<my_type> : fmt::formatter<std::string>
|
||||
{
|
||||
auto format(my_type my, format_context &ctx) const -> decltype(ctx.out())
|
||||
{
|
||||
return format_to(ctx.out(), "[my_type i={}]", my.i);
|
||||
}
|
||||
};
|
||||
|
||||
void user_defined_example()
|
||||
{
|
||||
spdlog::info("user defined type: {}", my_type(14));
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
#### User-defined flags in the log pattern
|
||||
```c++
|
||||
// Log patterns can contain custom flags.
|
||||
// the following example will add new flag '%*' - which will be bound to a <my_formatter_flag> instance.
|
||||
#include "spdlog/pattern_formatter.h"
|
||||
class my_formatter_flag : public spdlog::custom_flag_formatter
|
||||
{
|
||||
public:
|
||||
void format(const spdlog::details::log_msg &, const std::tm &, spdlog::memory_buf_t &dest) override
|
||||
{
|
||||
std::string some_txt = "custom-flag";
|
||||
dest.append(some_txt.data(), some_txt.data() + some_txt.size());
|
||||
}
|
||||
|
||||
std::unique_ptr<custom_flag_formatter> clone() const override
|
||||
{
|
||||
return std::make_unique<my_formatter_flag>();
|
||||
}
|
||||
};
|
||||
|
||||
void custom_flags_example()
|
||||
{
|
||||
auto formatter = std::make_unique<spdlog::pattern_formatter>();
|
||||
formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v");
|
||||
spdlog::set_formatter(std::move(formatter));
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
#### Custom error handler
|
||||
```c++
|
||||
void err_handler_example()
|
||||
{
|
||||
// can be set globally or per logger(logger->set_error_handler(..))
|
||||
spdlog::set_error_handler([](const std::string &msg) { spdlog::get("console")->error("*** LOGGER ERROR ***: {}", msg); });
|
||||
spdlog::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
---
|
||||
#### syslog
|
||||
```c++
|
||||
#include "spdlog/sinks/syslog_sink.h"
|
||||
//syslog example
|
||||
void syslog_example()
|
||||
{
|
||||
#ifdef SPDLOG_ENABLE_SYSLOG
|
||||
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.");
|
||||
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
|
||||
```c++
|
||||
#include "spdlog/sinks/android_sink.h"
|
||||
void android_example()
|
||||
|
||||
// user defined types logging by implementing operator<<
|
||||
struct my_type
|
||||
{
|
||||
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.");
|
||||
}
|
||||
```
|
||||
int i;
|
||||
template<typename OStream>
|
||||
friend OStream& operator<<(OStream& os, const my_type &c)
|
||||
{
|
||||
return os << "[my_type i="<<c.i << "]";
|
||||
}
|
||||
};
|
||||
|
||||
---
|
||||
#### Load log levels from the env variable or argv
|
||||
|
||||
```c++
|
||||
#include "spdlog/cfg/env.h"
|
||||
int main (int argc, char *argv[])
|
||||
#include <spdlog/fmt/ostr.h> // must be included
|
||||
void user_defined_example()
|
||||
{
|
||||
spdlog::cfg::load_env_levels();
|
||||
// or from the command line:
|
||||
// ./example SPDLOG_LEVEL=info,mylogger=trace
|
||||
// #include "spdlog/cfg/argv.h" // for loading levels from argv
|
||||
// spdlog::cfg::load_argv_levels(argc, argv);
|
||||
spd::get("console")->info("user defined type: {}", my_type { 14 });
|
||||
}
|
||||
```
|
||||
So then you can:
|
||||
|
||||
```console
|
||||
$ export SPDLOG_LEVEL=info,mylogger=trace
|
||||
$ ./example
|
||||
```
|
||||
|
||||
|
||||
---
|
||||
#### Log file open/close event handlers
|
||||
```c++
|
||||
// You can get callbacks from spdlog before/after a log file has been opened or closed.
|
||||
// This is useful for cleanup procedures or for adding something to the start/end of the log file.
|
||||
void file_events_example()
|
||||
//
|
||||
//custom error handler
|
||||
//
|
||||
void err_handler_example()
|
||||
{
|
||||
// pass the spdlog::file_event_handlers to file sinks for open/close log file notifications
|
||||
spdlog::file_event_handlers handlers;
|
||||
handlers.before_open = [](spdlog::filename_t filename) { spdlog::info("Before opening {}", filename); };
|
||||
handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("After opening\n", fstream); };
|
||||
handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("Before closing\n", fstream); };
|
||||
handlers.after_close = [](spdlog::filename_t filename) { spdlog::info("After closing {}", filename); };
|
||||
auto my_logger = spdlog::basic_logger_st("some_logger", "logs/events-sample.txt", true, handlers);
|
||||
spd::set_error_handler([](const std::string& msg) {
|
||||
std::cerr << "my err handler: " << msg << std::endl;
|
||||
});
|
||||
// (or logger->set_error_handler(..) to set for specific logger)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
#### Replace the Default Logger
|
||||
```c++
|
||||
void replace_global_logger_example()
|
||||
{
|
||||
auto new_logger = spdlog::basic_logger_mt("new_global_logger", "logs/new-default-log.txt", true);
|
||||
spdlog::set_global_logger(new_logger);
|
||||
spdlog::info("new logger log message");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
#### Log to Qt with nice colors
|
||||
```c++
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/sinks/qt_sinks.h"
|
||||
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
|
||||
{
|
||||
setMinimumSize(640, 480);
|
||||
auto log_widget = new QTextEdit(this);
|
||||
setCentralWidget(log_widget);
|
||||
int max_lines = 500; // keep the text widget to max 500 lines. remove old lines if needed.
|
||||
auto logger = spdlog::qt_color_logger_mt("qt_logger", log_widget, max_lines);
|
||||
logger->info("Some info message");
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
## Benchmarks
|
||||
|
||||
Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v2.x/bench/bench.cpp) done in Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
|
||||
|
||||
#### Synchronous mode
|
||||
```
|
||||
[info] **************************************************************
|
||||
[info] Single thread, 1,000,000 iterations
|
||||
[info] **************************************************************
|
||||
[info] basic_st Elapsed: 0.17 secs 5,777,626/sec
|
||||
[info] rotating_st Elapsed: 0.18 secs 5,475,894/sec
|
||||
[info] daily_st Elapsed: 0.20 secs 5,062,659/sec
|
||||
[info] empty_logger Elapsed: 0.07 secs 14,127,300/sec
|
||||
[info] **************************************************************
|
||||
[info] C-string (400 bytes). Single thread, 1,000,000 iterations
|
||||
[info] **************************************************************
|
||||
[info] basic_st Elapsed: 0.41 secs 2,412,483/sec
|
||||
[info] rotating_st Elapsed: 0.72 secs 1,389,196/sec
|
||||
[info] daily_st Elapsed: 0.42 secs 2,393,298/sec
|
||||
[info] null_st Elapsed: 0.04 secs 27,446,957/sec
|
||||
[info] **************************************************************
|
||||
[info] 10 threads, competing over the same logger object, 1,000,000 iterations
|
||||
[info] **************************************************************
|
||||
[info] basic_mt Elapsed: 0.60 secs 1,659,613/sec
|
||||
[info] rotating_mt Elapsed: 0.62 secs 1,612,493/sec
|
||||
[info] daily_mt Elapsed: 0.61 secs 1,638,305/sec
|
||||
[info] null_mt Elapsed: 0.16 secs 6,272,758/sec
|
||||
```
|
||||
#### Asynchronous mode
|
||||
```
|
||||
[info] -------------------------------------------------
|
||||
[info] Messages : 1,000,000
|
||||
[info] Threads : 10
|
||||
[info] Queue : 8,192 slots
|
||||
[info] Queue memory : 8,192 x 272 = 2,176 KB
|
||||
[info] -------------------------------------------------
|
||||
[info]
|
||||
[info] *********************************
|
||||
[info] Queue Overflow Policy: block
|
||||
[info] *********************************
|
||||
[info] Elapsed: 1.70784 secs 585,535/sec
|
||||
[info] Elapsed: 1.69805 secs 588,910/sec
|
||||
[info] Elapsed: 1.7026 secs 587,337/sec
|
||||
[info]
|
||||
[info] *********************************
|
||||
[info] Queue Overflow Policy: overrun
|
||||
[info] *********************************
|
||||
[info] Elapsed: 0.372816 secs 2,682,285/sec
|
||||
[info] Elapsed: 0.379758 secs 2,633,255/sec
|
||||
[info] Elapsed: 0.373532 secs 2,677,147/sec
|
||||
|
||||
```
|
||||
|
||||
## Documentation
|
||||
Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages.
|
||||
|
||||
---
|
||||
|
||||
Thanks to [JetBrains](https://www.jetbrains.com/?from=spdlog) for donating product licenses to help develop **spdlog** <a href="https://www.jetbrains.com/?from=spdlog"><img src="logos/jetbrains-variant-4.svg" width="94" align="center" /></a>
|
||||
|
32
appveyor.yml
Normal file
32
appveyor.yml
Normal file
@@ -0,0 +1,32 @@
|
||||
version: 1.0.{build}
|
||||
image: Visual Studio 2015
|
||||
environment:
|
||||
matrix:
|
||||
- GENERATOR: '"MinGW Makefiles"'
|
||||
BUILD_TYPE: Debug
|
||||
- GENERATOR: '"MinGW Makefiles"'
|
||||
BUILD_TYPE: Release
|
||||
- GENERATOR: '"Visual Studio 14 2015"'
|
||||
BUILD_TYPE: Debug
|
||||
- GENERATOR: '"Visual Studio 14 2015"'
|
||||
BUILD_TYPE: Release
|
||||
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
||||
BUILD_TYPE: Debug
|
||||
- GENERATOR: '"Visual Studio 14 2015 Win64"'
|
||||
BUILD_TYPE: Release
|
||||
build_script:
|
||||
- cmd: >-
|
||||
set
|
||||
|
||||
mkdir build
|
||||
|
||||
cd build
|
||||
|
||||
set PATH=%PATH:C:\Program Files\Git\usr\bin;=%
|
||||
|
||||
set PATH=C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin;%PATH%
|
||||
|
||||
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE%
|
||||
|
||||
cmake --build . --config %BUILD_TYPE%
|
||||
test: off
|
@@ -1,39 +0,0 @@
|
||||
# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
cmake_minimum_required(VERSION 3.14)
|
||||
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}")
|
||||
# User can fetch googlebenchmark
|
||||
message(STATUS "Downloading GoogleBenchmark")
|
||||
include(FetchContent)
|
||||
|
||||
# disable tests
|
||||
set(BENCHMARK_ENABLE_TESTING OFF CACHE INTERNAL "")
|
||||
# Do not build and run googlebenchmark tests
|
||||
FetchContent_Declare(googlebenchmark GIT_REPOSITORY https://github.com/google/benchmark.git GIT_TAG v1.6.0)
|
||||
FetchContent_MakeAvailable(googlebenchmark)
|
||||
set_target_properties(benchmark PROPERTIES FOLDER "third-party")
|
||||
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,160 +0,0 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
//
|
||||
// bench.cpp : spdlog benchmarks
|
||||
//
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <locale>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "spdlog/sinks/async_sink.h"
|
||||
#include "spdlog/sinks/basic_file_sink.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono;
|
||||
using namespace spdlog;
|
||||
using namespace spdlog::sinks;
|
||||
|
||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
||||
|
||||
int count_lines(const char *filename) {
|
||||
std::ifstream ifs(filename);
|
||||
return std::count(std::istreambuf_iterator(ifs), std::istreambuf_iterator<char>(), '\n');
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
using namespace spdlog::sinks;
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// setlocale to show thousands separators
|
||||
std::locale::global(std::locale("en_US.UTF-8"));
|
||||
int howmany = 1000000;
|
||||
int queue_size = std::min(howmany + 2, 8192);
|
||||
int threads = 10;
|
||||
int iters = 3;
|
||||
|
||||
try {
|
||||
spdlog::set_pattern("[%^%l%$] %v");
|
||||
if (argc > 1 && (std::string(argv[1]) == "-h" || std::string(argv[1]) == "--help")) {
|
||||
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]);
|
||||
// validate all argc values
|
||||
if (howmany < 1 || threads < 1 || queue_size < 1 || iters < 1) {
|
||||
spdlog::error("Invalid input values");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
auto slot_size = sizeof(details::async_log_msg);
|
||||
spdlog::info("-------------------------------------------------");
|
||||
spdlog::info("Messages : {:L}", howmany);
|
||||
spdlog::info("Threads : {:L}", threads);
|
||||
spdlog::info("Queue : {:L} slots", queue_size);
|
||||
spdlog::info("Queue memory : {:L} x {:L} = {:L} KB ", queue_size, slot_size, (queue_size * slot_size) / 1024);
|
||||
spdlog::info("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 file_sink = std::make_shared<basic_file_sink_mt>(filename, true);
|
||||
auto cfg = async_sink::config();
|
||||
cfg.queue_size = queue_size;
|
||||
cfg.sinks.push_back(std::move(file_sink));
|
||||
auto async_sink = std::make_shared<sinks::async_sink>(cfg);
|
||||
auto logger = std::make_shared<spdlog::logger>("async_logger", std::move(async_sink));
|
||||
bench_mt(howmany, std::move(logger), threads);
|
||||
}
|
||||
// verify_file(filename, howmany); // in separate scope to ensure logger is destroyed and all logs were written
|
||||
}
|
||||
spdlog::info("");
|
||||
spdlog::info("*********************************");
|
||||
spdlog::info("Queue Overflow Policy: overrun");
|
||||
spdlog::info("*********************************");
|
||||
// do same test but discard the oldest if queue is full instead of blocking
|
||||
filename = "logs/basic_async-overrun.log";
|
||||
for (int i = 0; i < iters; i++) {
|
||||
async_sink::config cfg;
|
||||
cfg.policy = async_sink::overflow_policy::overrun_oldest;
|
||||
cfg.queue_size = queue_size;
|
||||
auto file_sink = std::make_shared<basic_file_sink_mt>(filename, true);
|
||||
cfg.sinks.push_back(std::move(file_sink));
|
||||
auto async_sink = std::make_shared<sinks::async_sink>(cfg);
|
||||
auto logger = std::make_shared<spdlog::logger>("async_logger", std::move(async_sink));
|
||||
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<std::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("Elapsed: {} secs\t {:L}/sec", delta_d, static_cast<int>(howmany / delta_d));
|
||||
}
|
201
bench/bench.cpp
201
bench/bench.cpp
@@ -1,201 +0,0 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
//
|
||||
// bench.cpp : spdlog benchmarks
|
||||
//
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdlib> // EXIT_FAILURE
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "spdlog/sinks/basic_file_sink.h"
|
||||
#include "spdlog/sinks/daily_file_sink.h"
|
||||
#include "spdlog/sinks/null_sink.h"
|
||||
#include "spdlog/sinks/rotating_file_sink.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "utils.h"
|
||||
|
||||
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;
|
||||
|
||||
using namespace spdlog::sinks;
|
||||
void bench_threaded_logging(size_t threads, int iters) {
|
||||
spdlog::info("**************************************************************");
|
||||
spdlog::info(
|
||||
spdlog::fmt_lib::format(std::locale("en_US.UTF-8"), "Multi threaded: {:L} threads, {:L} messages", threads, iters));
|
||||
spdlog::info("**************************************************************");
|
||||
|
||||
auto basic_mt = spdlog::create<basic_file_sink_mt>("basic_mt", "logs/basic_mt.log", true);
|
||||
bench_mt(iters, std::move(basic_mt), threads);
|
||||
|
||||
spdlog::info("");
|
||||
auto rotating_mt = spdlog::create<rotating_file_sink_mt>("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files);
|
||||
bench_mt(iters, std::move(rotating_mt), threads);
|
||||
|
||||
spdlog::info("");
|
||||
auto daily_mt = spdlog::create<daily_file_sink_mt>("daily_mt", "logs/daily_mt.log", 0, 1);
|
||||
bench_mt(iters, std::move(daily_mt), threads);
|
||||
|
||||
spdlog::info("");
|
||||
auto empty_logger = std::make_shared<spdlog::logger>("level-off");
|
||||
empty_logger->set_level(spdlog::level::off);
|
||||
bench(iters, empty_logger);
|
||||
}
|
||||
|
||||
void bench_single_threaded(int iters) {
|
||||
spdlog::info("**************************************************************");
|
||||
spdlog::info(spdlog::fmt_lib::format(std::locale("en_US.UTF-8"), "Single threaded: {} messages", iters));
|
||||
spdlog::info("**************************************************************");
|
||||
|
||||
auto basic_st = spdlog::create<basic_file_sink_st>("basic_st", "logs/basic_st.log", true);
|
||||
bench(iters, std::move(basic_st));
|
||||
|
||||
spdlog::info("");
|
||||
auto rotating_st = spdlog::create<rotating_file_sink_st>("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
|
||||
bench(iters, std::move(rotating_st));
|
||||
|
||||
spdlog::info("");
|
||||
auto daily_st = spdlog::create<daily_file_sink_st>("daily_st", "logs/daily_st.log", 0, 1);
|
||||
bench(iters, std::move(daily_st));
|
||||
|
||||
spdlog::info("");
|
||||
auto empty_logger = std::make_shared<spdlog::logger>("level-off");
|
||||
empty_logger->set_level(spdlog::level::off);
|
||||
bench(iters, empty_logger);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
spdlog::global_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(spdlog::fmt_lib::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(spdlog::fmt_lib::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(),
|
||||
delta_d, static_cast<size_t>(howmany / delta_d)));
|
||||
}
|
||||
|
||||
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(spdlog::fmt_lib::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(),
|
||||
delta_d, static_cast<size_t>(howmany / delta_d)));
|
||||
}
|
||||
|
||||
/*
|
||||
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::global_logger();
|
||||
spdlog::set_global_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_global_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::global_logger();
|
||||
spdlog::set_global_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_global_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,68 +0,0 @@
|
||||
//
|
||||
// Copyright(c) 2018 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#include "benchmark/benchmark.h"
|
||||
#include "spdlog/pattern_formatter.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
void bench_formatter(benchmark::State &state, std::string pattern) {
|
||||
auto formatter = std::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,165 +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/sinks/async_sink.h"
|
||||
#include "spdlog/sinks/basic_file_sink.h"
|
||||
#include "spdlog/sinks/daily_file_sink.h"
|
||||
#include "spdlog/sinks/null_sink.h"
|
||||
#include "spdlog/sinks/rotating_file_sink.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
using namespace spdlog::sinks;
|
||||
void bench_c_string(benchmark::State &state, std::shared_ptr<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_global_logger(benchmark::State &state, std::shared_ptr<spdlog::logger> logger) {
|
||||
spdlog::set_global_logger(std::move(logger));
|
||||
int i = 0;
|
||||
for (auto _ : state) {
|
||||
spdlog::info("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++);
|
||||
}
|
||||
}
|
||||
|
||||
void bench_disabled_macro_global_logger(benchmark::State &state, std::shared_ptr<spdlog::logger> logger) {
|
||||
spdlog::set_global_logger(std::move(logger));
|
||||
int i = 0;
|
||||
benchmark::DoNotOptimize(i); // prevent unused warnings
|
||||
benchmark::DoNotOptimize(logger); // prevent unused warnings
|
||||
for (auto _ : state) {
|
||||
SPDLOG_DEBUG("Hello logger: msg number {}...............", i++);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __linux__
|
||||
|
||||
void bench_dev_null() {
|
||||
auto dev_null_st = spdlog::create<basic_file_sink_st>("/dev/null_st", "/dev/null");
|
||||
benchmark::RegisterBenchmark("/dev/null_st", bench_logger, std::move(dev_null_st))->UseRealTime();
|
||||
|
||||
auto dev_null_mt = spdlog::create<basic_file_sink_mt>("/dev/null_mt", "/dev/null");
|
||||
benchmark::RegisterBenchmark("/dev/null_mt", bench_logger, std::move(dev_null_mt))->UseRealTime();
|
||||
}
|
||||
|
||||
#endif // __linux__
|
||||
|
||||
// test spdlog::get() performance
|
||||
// for this test we create multiple null loggers and then call spdlog::get() on one of them multiple times
|
||||
// create multiple null loggers and return name of the one to test
|
||||
static std::string prepare_null_loggers() {
|
||||
const std::string some_logger_name = "Some logger name";
|
||||
const int null_logger_count = 9;
|
||||
for (int i = 0; i < null_logger_count; i++) {
|
||||
spdlog::create<null_sink_mt>(some_logger_name + std::to_string(i));
|
||||
}
|
||||
return some_logger_name + std::to_string(null_logger_count / 2);
|
||||
}
|
||||
|
||||
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-compile-time (global logger)", bench_disabled_macro_global_logger, disabled_logger);
|
||||
benchmark::RegisterBenchmark("disabled-at-runtime", bench_logger, disabled_logger);
|
||||
benchmark::RegisterBenchmark("disabled-at-runtime (global logger)", bench_global_logger, 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_st (global logger)", bench_global_logger, null_logger_st);
|
||||
|
||||
#ifdef __linux__
|
||||
bench_dev_null();
|
||||
#endif // __linux__
|
||||
|
||||
if (full_bench) {
|
||||
// basic_st
|
||||
auto basic_st = spdlog::create<basic_file_sink_st>("basic_st", "latency_logs/basic_st.log", true);
|
||||
benchmark::RegisterBenchmark("basic_st", bench_logger, std::move(basic_st))->UseRealTime();
|
||||
|
||||
// rotating st
|
||||
auto rotating_st =
|
||||
spdlog::create<rotating_file_sink_st>("rotating_st", "latency_logs/rotating_st.log", file_size, rotating_files);
|
||||
benchmark::RegisterBenchmark("rotating_st", bench_logger, std::move(rotating_st))->UseRealTime();
|
||||
|
||||
// daily st
|
||||
auto daily_st = spdlog::create<daily_file_sink_st>("daily_st", "latency_logs/daily_st.log", 0, 1);
|
||||
benchmark::RegisterBenchmark("daily_st", bench_logger, std::move(daily_st))->UseRealTime();
|
||||
|
||||
//
|
||||
// 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::create<basic_file_sink_mt>("basic_mt", "latency_logs/basic_mt.log", true);
|
||||
benchmark::RegisterBenchmark("basic_mt", bench_logger, std::move(basic_mt))->Threads(n_threads)->UseRealTime();
|
||||
|
||||
// rotating mt
|
||||
auto rotating_mt =
|
||||
spdlog::create<rotating_file_sink_mt>("rotating_mt", "latency_logs/rotating_mt.log", file_size, rotating_files);
|
||||
benchmark::RegisterBenchmark("rotating_mt", bench_logger, std::move(rotating_mt))->Threads(n_threads)->UseRealTime();
|
||||
|
||||
// daily mt
|
||||
auto daily_mt = spdlog::create<daily_file_sink_mt>("daily_mt", "latency_logs/daily_mt.log", 0, 1);
|
||||
benchmark::RegisterBenchmark("daily_mt", bench_logger, std::move(daily_mt))->Threads(n_threads)->UseRealTime();
|
||||
}
|
||||
using spdlog::sinks::async_sink;
|
||||
async_sink::config config;
|
||||
config.queue_size = 3 * 1024 * 1024;
|
||||
;
|
||||
config.sinks.push_back(std::make_shared<null_sink_st>());
|
||||
config.policy = async_sink::overflow_policy::overrun_oldest;
|
||||
auto async_logger = std::make_shared<spdlog::logger>("async_logger", std::make_shared<async_sink>(config));
|
||||
benchmark::RegisterBenchmark("async_logger", bench_logger, async_logger)->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;
|
||||
}
|
@@ -11,8 +11,9 @@
|
||||
|
||||
namespace utils {
|
||||
|
||||
template <typename T>
|
||||
inline std::string format(const T &value) {
|
||||
template<typename T>
|
||||
inline std::string format(const T &value)
|
||||
{
|
||||
static std::locale loc("");
|
||||
std::stringstream ss;
|
||||
ss.imbue(loc);
|
||||
@@ -20,8 +21,9 @@ inline std::string format(const T &value) {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::string format(const double &value) {
|
||||
template<>
|
||||
inline std::string format(const double &value)
|
||||
{
|
||||
static std::locale loc("");
|
||||
std::stringstream ss;
|
||||
ss.imbue(loc);
|
||||
|
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,15 +0,0 @@
|
||||
include(FetchContent)
|
||||
|
||||
FetchContent_Declare(
|
||||
fmt
|
||||
DOWNLOAD_EXTRACT_TIMESTAMP FALSE
|
||||
URL https://github.com/fmtlib/fmt/archive/refs/tags/11.1.2.tar.gz
|
||||
URL_HASH SHA256=d8773cf062cc806d4dd4df658111f15ba7a2c9c65db5084d2491696828b1eb97)
|
||||
|
||||
FetchContent_GetProperties(fmt)
|
||||
if(NOT fmt_POPULATED)
|
||||
# We do not require os features of fmt
|
||||
set(FMT_OS OFF CACHE BOOL "Disable FMT_OS" FORCE)
|
||||
FetchContent_MakeAvailable(fmt)
|
||||
set_target_properties(fmt PROPERTIES FOLDER "third-party")
|
||||
endif()
|
@@ -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})
|
6
cmake/spdlog.pc.in
Normal file
6
cmake/spdlog.pc.in
Normal file
@@ -0,0 +1,6 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
includedir=${prefix}/include
|
||||
|
||||
Name: @PROJECT_NAME@
|
||||
Description: Super fast C++ logging library.
|
||||
Version: @PROJECT_VERSION@
|
@@ -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 "Fast C++ logging library.")
|
||||
set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "FastC++ 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)
|
||||
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include(CMakeFindDependencyMacro)
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
find_dependency(fmt 11 CONFIG)
|
||||
|
||||
set(config_targets_file @config_targets_file@)
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/${config_targets_file}")
|
||||
|
||||
check_required_components(spdlog)
|
@@ -1,73 +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
|
||||
-Werror
|
||||
-Wfatal-errors>
|
||||
$<$<CXX_COMPILER_ID:MSVC>:${MSVC_OPTIONS}>)
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Enable address sanitizer (gcc/clang only)
|
||||
function(spdlog_enable_addr_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()
|
||||
|
||||
# Enable thread sanitizer (gcc/clang only)
|
||||
function(spdlog_enable_thread_sanitizer target_name)
|
||||
if(NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
message(FATAL_ERROR "Sanitizer supported only for gcc/clang")
|
||||
endif()
|
||||
message(STATUS "Thread sanitizer enabled")
|
||||
target_compile_options(${target_name} PRIVATE -fsanitize=thread)
|
||||
target_compile_options(${target_name} PRIVATE -fno-omit-frame-pointer)
|
||||
target_link_libraries(${target_name} PRIVATE -fsanitize=thread -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,12 +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.14)
|
||||
project(spdlog_example CXX)
|
||||
cmake_minimum_required(VERSION 3.1)
|
||||
project(SpdlogExamples CXX)
|
||||
|
||||
if(NOT TARGET spdlog)
|
||||
if(TARGET spdlog)
|
||||
# Part of the main project
|
||||
add_library(spdlog::spdlog ALIAS spdlog)
|
||||
else()
|
||||
# Stand-alone build
|
||||
find_package(spdlog 2 REQUIRED)
|
||||
find_package(spdlog CONFIG REQUIRED)
|
||||
endif()
|
||||
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
add_executable(example example.cpp)
|
||||
target_link_libraries(example PRIVATE spdlog::spdlog $<$<BOOL:${MINGW}>:ws2_32>)
|
||||
target_link_libraries(example spdlog::spdlog Threads::Threads)
|
||||
|
||||
add_executable(benchmark bench.cpp)
|
||||
target_link_libraries(benchmark spdlog::spdlog Threads::Threads)
|
||||
|
||||
add_executable(multisink multisink.cpp)
|
||||
target_link_libraries(multisink spdlog::spdlog Threads::Threads)
|
||||
|
||||
enable_testing()
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
|
||||
add_test(NAME RunExample COMMAND example)
|
||||
add_test(NAME RunBenchmark COMMAND benchmark)
|
||||
|
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,298 +1,162 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
//
|
||||
//
|
||||
// spdlog usage example
|
||||
//
|
||||
//
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
|
||||
void stdout_logger_example();
|
||||
void basic_example();
|
||||
void rotating_example();
|
||||
void daily_example();
|
||||
void callback_example();
|
||||
void async_example();
|
||||
void binary_example();
|
||||
void vector_example();
|
||||
void stopwatch_example();
|
||||
void trace_example();
|
||||
void multi_sink_example();
|
||||
void user_defined_example();
|
||||
void err_handler_example();
|
||||
void syslog_example();
|
||||
void udp_example();
|
||||
void custom_flags_example();
|
||||
void file_events_example();
|
||||
void replace_global_logger_example();
|
||||
#define SPDLOG_TRACE_ON
|
||||
#define SPDLOG_DEBUG_ON
|
||||
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/version.h"
|
||||
|
||||
using namespace spdlog::sinks;
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
int main(int, char *[]) {
|
||||
spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH);
|
||||
spdlog::warn("Easy padding in numbers like {:08d}", 12);
|
||||
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");
|
||||
void async_example();
|
||||
void syslog_example();
|
||||
void android_example();
|
||||
void user_defined_example();
|
||||
void err_handler_example();
|
||||
|
||||
// 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..");
|
||||
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);
|
||||
|
||||
// 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);
|
||||
// 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");
|
||||
|
||||
try {
|
||||
stdout_logger_example();
|
||||
basic_example();
|
||||
rotating_example();
|
||||
daily_example();
|
||||
callback_example();
|
||||
async_example();
|
||||
binary_example();
|
||||
vector_example();
|
||||
multi_sink_example();
|
||||
user_defined_example();
|
||||
err_handler_example();
|
||||
trace_example();
|
||||
stopwatch_example();
|
||||
udp_example();
|
||||
custom_flags_example();
|
||||
file_events_example();
|
||||
replace_global_logger_example();
|
||||
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
|
||||
|
||||
// Release all spdlog resources
|
||||
// This is optional (only mandatory if using windows + async log).
|
||||
spdlog::shutdown();
|
||||
// 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);
|
||||
}
|
||||
|
||||
// Exceptions will only be thrown upon failed logger or sink construction (not during logging).
|
||||
catch (const spdlog::spdlog_ex &ex) {
|
||||
std::printf("Log initialization failed: %s\n", ex.what());
|
||||
// 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();
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||
// or #include "spdlog/sinks/stdout_sinks.h" if no colors needed.
|
||||
void stdout_logger_example() {
|
||||
// Create color multithreading logger.
|
||||
auto console = spdlog::create<stdout_color_sink_mt>("console");
|
||||
// or for stderr:
|
||||
// auto console = spdlog::create<stderr_color_sink_mt>("console");
|
||||
}
|
||||
|
||||
#include "spdlog/sinks/basic_file_sink.h"
|
||||
void basic_example() {
|
||||
// Create basic file logger (not rotated).
|
||||
auto my_logger = spdlog::create<basic_file_sink_mt>("file_logger", "logs/basic-log.txt", true);
|
||||
}
|
||||
|
||||
#include "spdlog/sinks/rotating_file_sink.h"
|
||||
void rotating_example() {
|
||||
// Create a file rotating logger with 5mb size max and 3 rotated files.
|
||||
auto rotating_logger = spdlog::create<rotating_file_sink_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::create<daily_file_format_sink_mt>("daily_logger", "logs/daily.txt", 2, 30);
|
||||
}
|
||||
|
||||
#include "spdlog/sinks/callback_sink.h"
|
||||
void callback_example() {
|
||||
// Create the logger
|
||||
auto logger = spdlog::create<callback_sink_mt>("custom_callback_logger", [](const spdlog::details::log_msg & /*msg*/) {
|
||||
// do what you need to do with msg
|
||||
});
|
||||
}
|
||||
|
||||
#include "spdlog/sinks/async_sink.h"
|
||||
void async_example() {
|
||||
using spdlog::sinks::async_sink;
|
||||
auto sink = async_sink::with<basic_file_sink_mt>("logs/async_log.txt", true);
|
||||
auto logger = std::make_shared<spdlog::logger>("async_logger", sink);
|
||||
for (int i = 1; i < 101; ++i) {
|
||||
logger->info("Async message #{}", i);
|
||||
void async_example()
|
||||
{
|
||||
size_t q_size = 4096;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// Log binary data as hex.
|
||||
// Many types of std::container<char> types can be used.
|
||||
// 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/bin_to_hex.h"
|
||||
void binary_example() {
|
||||
std::vector<char> buf;
|
||||
for (int i = 0; i < 80; i++) {
|
||||
buf.push_back(static_cast<char>(i & 0xff));
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
// Log a vector of numbers
|
||||
#include "fmt/ranges.h"
|
||||
void vector_example() {
|
||||
std::vector<int> vec = {1, 2, 3};
|
||||
spdlog::info("Vector example: {}", vec);
|
||||
}
|
||||
|
||||
// Compile time log levels.
|
||||
// define SPDLOG_ACTIVE_LEVEL to required level (e.g. SPDLOG_LEVEL_TRACE)
|
||||
void trace_example() {
|
||||
// trace from global logger
|
||||
SPDLOG_TRACE("Some trace message.. {} ,{}", 1, 3.23);
|
||||
// debug from global logger
|
||||
SPDLOG_DEBUG("Some debug message.. {} ,{}", 1, 3.23);
|
||||
}
|
||||
|
||||
// stopwatch example
|
||||
#include <thread>
|
||||
|
||||
#include "spdlog/stopwatch.h"
|
||||
void stopwatch_example() {
|
||||
spdlog::stopwatch sw;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(123));
|
||||
spdlog::info("Stopwatch: {} seconds", sw);
|
||||
}
|
||||
|
||||
#include "spdlog/sinks/udp_sink.h"
|
||||
void udp_example() {
|
||||
udp_sink_config cfg("127.0.0.1", 11091);
|
||||
auto my_logger = spdlog::create<udp_sink_mt>("udplog", cfg);
|
||||
my_logger->set_level(spdlog::level::debug);
|
||||
my_logger->info("hello world");
|
||||
}
|
||||
|
||||
// A logger with multiple sinks (stdout and file) - each with a different format and log level.
|
||||
void multi_sink_example() {
|
||||
auto console_sink = std::make_shared<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<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
|
||||
struct my_type {
|
||||
int i = 0;
|
||||
explicit my_type(int i)
|
||||
: i(i) {}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct fmt::formatter<my_type> : fmt::formatter<std::string> {
|
||||
auto format(my_type my, format_context &ctx) const -> decltype(ctx.out()) {
|
||||
return fmt::format_to(ctx.out(), "[my_type i={}]", my.i);
|
||||
}
|
||||
};
|
||||
|
||||
void user_defined_example() { spdlog::info("user defined type: {}", my_type(14)); }
|
||||
|
||||
// Custom error handler. Will be triggered on log failure.
|
||||
void err_handler_example() {
|
||||
// can be set globally 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()); });
|
||||
}
|
||||
|
||||
// syslog example (linux/osx/freebsd)
|
||||
#ifndef _WIN32
|
||||
#include "spdlog/sinks/syslog_sink.h"
|
||||
void syslog_example() {
|
||||
void syslog_example()
|
||||
{
|
||||
#ifdef SPDLOG_ENABLE_SYSLOG
|
||||
std::string ident = "spdlog-example";
|
||||
auto syslog_logger = spdlog::create<syslog_sink_mt>("syslog", ident, LOG_PID);
|
||||
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.
|
||||
// Android example
|
||||
void 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);
|
||||
auto android_logger = spd::android_logger("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());
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::unique_ptr<custom_flag_formatter> clone() const override {
|
||||
return std::make_unique<my_formatter_flag>();
|
||||
// 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 << "]";
|
||||
}
|
||||
};
|
||||
|
||||
void custom_flags_example() {
|
||||
auto formatter = std::make_unique<spdlog::pattern_formatter>();
|
||||
formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v");
|
||||
// set the new formatter using spdlog::set_formatter(formatter) or
|
||||
// logger->set_formatter(formatter) spdlog::set_formatter(std::move(formatter));
|
||||
#include "spdlog/fmt/ostr.h" // must be included
|
||||
void user_defined_example()
|
||||
{
|
||||
spd::get("console")->info("user defined type: {}", my_type{14});
|
||||
}
|
||||
|
||||
void file_events_example() {
|
||||
// pass the spdlog::file_event_handlers to file sinks for open/close log file notifications
|
||||
spdlog::file_event_handlers handlers;
|
||||
handlers.before_open = [](spdlog::filename_t) { spdlog::trace("Before opening logfile"); };
|
||||
handlers.after_open = [](spdlog::filename_t, std::FILE *fstream) {
|
||||
spdlog::trace("After opening logfile");
|
||||
fputs("After opening\n", fstream);
|
||||
};
|
||||
handlers.before_close = [](spdlog::filename_t, std::FILE *fstream) {
|
||||
spdlog::trace("Before closing logfile");
|
||||
fputs("Before closing\n", fstream);
|
||||
};
|
||||
handlers.after_close = [](spdlog::filename_t) { spdlog::trace("After closing logfile"); };
|
||||
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/events-sample.txt", true, handlers);
|
||||
spdlog::logger my_logger("some_logger", file_sink);
|
||||
my_logger.trace("Some log line");
|
||||
}
|
||||
|
||||
void replace_global_logger_example() {
|
||||
// store the old logger so we don't break other examples.
|
||||
auto old_logger = spdlog::global_logger();
|
||||
|
||||
auto new_logger = spdlog::create<basic_file_sink_mt>("new_global_logger", "logs/new-default-log.txt", true);
|
||||
spdlog::set_global_logger(new_logger);
|
||||
spdlog::set_level(spdlog::level::info);
|
||||
spdlog::debug("This message should not be displayed!");
|
||||
spdlog::set_level(spdlog::level::trace);
|
||||
spdlog::debug("This message should be displayed..");
|
||||
spdlog::set_global_logger(old_logger);
|
||||
//
|
||||
// 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);
|
||||
}
|
||||
|
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
|
||||
|
||||
|
72
include/spdlog/async_logger.h
Normal file
72
include/spdlog/async_logger.h
Normal file
@@ -0,0 +1,72 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
// Very fast asynchronous logger (millions of logs per second on an average desktop)
|
||||
// Uses pre allocated lockfree queue for maximum throughput even under large number of threads.
|
||||
// Creates a single back thread to pop messages from the queue and log them.
|
||||
//
|
||||
// Upon each log write the logger:
|
||||
// 1. Checks if its log level is enough to log the message
|
||||
// 2. Push a new copy of the message to a queue (or block the caller until space is available in the queue)
|
||||
// 3. will throw spdlog_ex upon log exceptions
|
||||
// Upon destruction, logs all remaining messages in the queue before destructing..
|
||||
|
||||
#include "common.h"
|
||||
#include "logger.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
namespace details {
|
||||
class async_log_helper;
|
||||
}
|
||||
|
||||
class async_logger SPDLOG_FINAL : public logger
|
||||
{
|
||||
public:
|
||||
template<class It>
|
||||
async_logger(const std::string &logger_name, const It &begin, const It &end, size_t queue_size,
|
||||
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(const std::string &logger_name, sinks_init_list sinks, size_t queue_size,
|
||||
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(const std::string &logger_name, sink_ptr single_sink, size_t queue_size,
|
||||
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);
|
||||
|
||||
// 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:
|
||||
void _sink_it(details::log_msg &msg) override;
|
||||
void _set_formatter(spdlog::formatter_ptr msg_formatter) override;
|
||||
void _set_pattern(const std::string &pattern, pattern_time_type pattern_time) override;
|
||||
|
||||
private:
|
||||
std::unique_ptr<details::async_log_helper> _async_log_helper;
|
||||
};
|
||||
} // namespace spdlog
|
||||
|
||||
#include "details/async_logger_impl.h"
|
@@ -1,204 +0,0 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cctype>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<version>)
|
||||
#include <version>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if __cpp_lib_span >= 202002L
|
||||
#include <span>
|
||||
#endif
|
||||
|
||||
//
|
||||
// Support for logging binary data as hex
|
||||
// format flags, any combination of the following:
|
||||
// {: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
|
||||
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// std::vector<char> v(200, 0x0b);
|
||||
// logger->info("Some buffer {}", spdlog::to_hex(v));
|
||||
// char buf[128];
|
||||
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));
|
||||
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf), 16));
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
template <typename It>
|
||||
class dump_info {
|
||||
public:
|
||||
dump_info(It range_begin, It range_end, size_t size_per_line)
|
||||
: begin_(range_begin),
|
||||
end_(range_end),
|
||||
size_per_line_(size_per_line) {}
|
||||
|
||||
// do not use begin() and end() to avoid collision with fmt/ranges
|
||||
It get_begin() const { return begin_; }
|
||||
It get_end() const { return end_; }
|
||||
[[nodiscard]] size_t size_per_line() const { return size_per_line_; }
|
||||
|
||||
private:
|
||||
It begin_, end_;
|
||||
size_t size_per_line_;
|
||||
};
|
||||
} // namespace details
|
||||
|
||||
// create a dump_info that wraps the given container
|
||||
template <typename Container>
|
||||
inline details::dump_info<typename Container::const_iterator> to_hex(const Container &container, size_t size_per_line = 32) {
|
||||
static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
|
||||
using Iter = typename Container::const_iterator;
|
||||
return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
|
||||
}
|
||||
|
||||
#if __cpp_lib_span >= 202002L
|
||||
|
||||
template <typename Value, size_t Extent>
|
||||
inline details::dump_info<typename std::span<Value, Extent>::iterator> to_hex(const std::span<Value, Extent> &container,
|
||||
size_t size_per_line = 32) {
|
||||
using Container = std::span<Value, Extent>;
|
||||
static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
|
||||
using Iter = typename Container::iterator;
|
||||
return details::dump_info<Iter>(std::begin(container), std::end(container), size_per_line);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
// create dump_info from ranges
|
||||
template <typename It>
|
||||
inline details::dump_info<It> to_hex(const It range_begin, const It range_end, size_t size_per_line = 32) {
|
||||
return details::dump_info<It>(range_begin, range_end, size_per_line);
|
||||
}
|
||||
|
||||
} // namespace spdlog
|
||||
|
||||
template <typename T>
|
||||
struct fmt::formatter<spdlog::details::dump_info<T>, char> {
|
||||
char delimiter = ' ';
|
||||
bool put_newlines = true;
|
||||
bool put_delimiters = true;
|
||||
bool use_uppercase = false;
|
||||
bool put_positions = true; // position on start of each line
|
||||
bool show_ascii = false;
|
||||
|
||||
// parse the format string flags
|
||||
template <typename ParseContext>
|
||||
constexpr auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
|
||||
auto it = ctx.begin();
|
||||
while (it != ctx.end() && *it != '}') {
|
||||
switch (*it) {
|
||||
case 'X':
|
||||
use_uppercase = true;
|
||||
break;
|
||||
case 's':
|
||||
put_delimiters = false;
|
||||
break;
|
||||
case 'p':
|
||||
put_positions = false;
|
||||
break;
|
||||
case 'n':
|
||||
put_newlines = false;
|
||||
show_ascii = false;
|
||||
break;
|
||||
case 'a':
|
||||
if (put_newlines) {
|
||||
show_ascii = true;
|
||||
}
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
|
||||
++it;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
// format the given bytes range as hex
|
||||
template <typename FormatContext, typename Container>
|
||||
auto format(const spdlog::details::dump_info<Container> &the_range, FormatContext &ctx) const -> decltype(ctx.out()) {
|
||||
constexpr const char *hex_upper = "0123456789ABCDEF";
|
||||
constexpr const char *hex_lower = "0123456789abcdef";
|
||||
const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
|
||||
auto inserter = ctx.out();
|
||||
int size_per_line = static_cast<int>(the_range.size_per_line());
|
||||
auto start_of_line = the_range.get_begin();
|
||||
for (auto i = the_range.get_begin(); i != the_range.get_end(); ++i) {
|
||||
auto ch = static_cast<unsigned char>(*i);
|
||||
|
||||
if (put_newlines && (i == the_range.get_begin() || i - start_of_line >= size_per_line)) {
|
||||
if (show_ascii && i != the_range.get_begin()) {
|
||||
*inserter++ = delimiter;
|
||||
*inserter++ = delimiter;
|
||||
for (auto j = start_of_line; j < i; ++j) {
|
||||
auto pc = static_cast<unsigned char>(*j);
|
||||
*inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
|
||||
}
|
||||
}
|
||||
|
||||
put_newline(inserter, static_cast<size_t>(i - the_range.get_begin()));
|
||||
|
||||
// put first byte without delimiter in front of it
|
||||
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
|
||||
*inserter++ = hex_chars[ch & 0x0f];
|
||||
start_of_line = i;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (put_delimiters && i != the_range.get_begin()) {
|
||||
*inserter++ = delimiter;
|
||||
}
|
||||
|
||||
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
|
||||
*inserter++ = hex_chars[ch & 0x0f];
|
||||
}
|
||||
if (show_ascii) // add ascii to last line
|
||||
{
|
||||
if (the_range.get_end() - the_range.get_begin() > size_per_line) {
|
||||
auto blank_num = size_per_line - (the_range.get_end() - start_of_line);
|
||||
while (blank_num-- > 0) {
|
||||
*inserter++ = delimiter;
|
||||
*inserter++ = delimiter;
|
||||
if (put_delimiters) {
|
||||
*inserter++ = delimiter;
|
||||
}
|
||||
}
|
||||
}
|
||||
*inserter++ = delimiter;
|
||||
*inserter++ = delimiter;
|
||||
for (auto j = start_of_line; j != the_range.get_end(); ++j) {
|
||||
auto pc = static_cast<unsigned char>(*j);
|
||||
*inserter++ = std::isprint(pc) ? static_cast<char>(*j) : '.';
|
||||
}
|
||||
}
|
||||
return inserter;
|
||||
}
|
||||
|
||||
// put newline(and position header)
|
||||
template <typename It>
|
||||
void put_newline(It inserter, std::size_t pos) const {
|
||||
#ifdef _WIN32
|
||||
*inserter++ = '\r';
|
||||
#endif
|
||||
*inserter++ = '\n';
|
||||
|
||||
if (put_positions) {
|
||||
spdlog::fmt_lib::format_to(inserter, SPDLOG_FMT_STRING("{:04X}: "), pos);
|
||||
}
|
||||
}
|
||||
}; // namespace fmt
|
@@ -1,44 +1,56 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#define SPDLOG_VERSION "0.17.0"
|
||||
|
||||
#include "tweakme.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "./source_loc.h"
|
||||
#include "fmt/base.h"
|
||||
#include "fmt/xchar.h"
|
||||
|
||||
#if defined(SPDLOG_SHARED_LIB)
|
||||
#if defined(_WIN32)
|
||||
#ifdef spdlog_EXPORTS
|
||||
#define SPDLOG_API __declspec(dllexport)
|
||||
#else // !spdlog_EXPORTS
|
||||
#define SPDLOG_API __declspec(dllimport)
|
||||
#endif
|
||||
#else // !defined(_WIN32)
|
||||
#define SPDLOG_API __attribute__((visibility("default")))
|
||||
#endif
|
||||
#else // !defined(SPDLOG_SHARED_LIB)
|
||||
#define SPDLOG_API
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
#include <codecvt>
|
||||
#include <locale>
|
||||
#endif
|
||||
|
||||
#define SPDLOG_FMT_RUNTIME(format_string) fmt::runtime(format_string)
|
||||
#define SPDLOG_FMT_STRING(format_string) FMT_STRING(format_string)
|
||||
#include "details/null_mutex.h"
|
||||
|
||||
#ifndef SPDLOG_FUNCTION
|
||||
#define SPDLOG_FUNCTION static_cast<const char *>(__FUNCTION__)
|
||||
// visual studio upto 2013 does not support noexcept nor constexpr
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
#define SPDLOG_NOEXCEPT throw()
|
||||
#define SPDLOG_CONSTEXPR
|
||||
#else
|
||||
#define SPDLOG_NOEXCEPT noexcept
|
||||
#define SPDLOG_CONSTEXPR constexpr
|
||||
#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__)
|
||||
#define SPDLOG_DEPRECATED __attribute__((deprecated))
|
||||
#elif defined(_MSC_VER)
|
||||
#define SPDLOG_DEPRECATED __declspec(deprecated)
|
||||
#else
|
||||
#define SPDLOG_DEPRECATED
|
||||
#endif
|
||||
|
||||
#include "fmt/fmt.h"
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
class formatter;
|
||||
@@ -50,70 +62,80 @@ class sink;
|
||||
using log_clock = std::chrono::system_clock;
|
||||
using sink_ptr = std::shared_ptr<sinks::sink>;
|
||||
using sinks_init_list = std::initializer_list<sink_ptr>;
|
||||
using err_handler = std::function<void(const std::string &err_msg)>;
|
||||
using string_view_t = std::basic_string_view<char>;
|
||||
using wstring_view_t = std::basic_string_view<wchar_t>;
|
||||
|
||||
namespace fmt_lib = fmt;
|
||||
using memory_buf_t = fmt::basic_memory_buffer<char, 250>;
|
||||
using wmemory_buf_t = fmt::basic_memory_buffer<wchar_t, 250>;
|
||||
|
||||
template <typename... Args>
|
||||
using format_string_t = fmt::format_string<Args...>;
|
||||
|
||||
#define SPDLOG_LEVEL_TRACE 0
|
||||
#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
|
||||
using formatter_ptr = std::shared_ptr<spdlog::formatter>;
|
||||
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
|
||||
using level_t = details::null_atomic_int;
|
||||
#else
|
||||
using level_t = std::atomic<int>;
|
||||
#endif
|
||||
|
||||
using log_err_handler = std::function<void(const std::string &err_msg)>;
|
||||
|
||||
// Log level enum
|
||||
enum class level : std::uint8_t {
|
||||
trace = SPDLOG_LEVEL_TRACE,
|
||||
debug = SPDLOG_LEVEL_DEBUG,
|
||||
info = SPDLOG_LEVEL_INFO,
|
||||
warn = SPDLOG_LEVEL_WARN,
|
||||
err = SPDLOG_LEVEL_ERROR,
|
||||
critical = SPDLOG_LEVEL_CRITICAL,
|
||||
off = SPDLOG_LEVEL_OFF,
|
||||
n_levels = SPDLOG_LEVEL_OFF + 1
|
||||
namespace level {
|
||||
enum level_enum
|
||||
{
|
||||
trace = 0,
|
||||
debug = 1,
|
||||
info = 2,
|
||||
warn = 3,
|
||||
err = 4,
|
||||
critical = 5,
|
||||
off = 6
|
||||
};
|
||||
|
||||
using atomic_level_t = std::atomic<level>;
|
||||
#if !defined(SPDLOG_LEVEL_NAMES)
|
||||
#define SPDLOG_LEVEL_NAMES \
|
||||
{ \
|
||||
"trace", "debug", "info", "warning", "error", "critical", "off" \
|
||||
}
|
||||
#endif
|
||||
static const char *level_names[] SPDLOG_LEVEL_NAMES;
|
||||
|
||||
[[nodiscard]] constexpr size_t level_to_number(level lvl) noexcept { return static_cast<size_t>(lvl); }
|
||||
static const char *short_level_names[]{"T", "D", "I", "W", "E", "C", "O"};
|
||||
|
||||
constexpr auto levels_count = level_to_number(level::n_levels);
|
||||
constexpr std::array<std::string_view, levels_count> level_string_views{"trace", "debug", "info", "warning",
|
||||
"error", "critical", "off"};
|
||||
constexpr std::array<std::string_view, levels_count> short_level_names{"T", "D", "I", "W", "E", "C", "O"};
|
||||
|
||||
[[nodiscard]] constexpr std::string_view to_string_view(spdlog::level lvl) noexcept {
|
||||
return level_string_views.at(level_to_number(lvl));
|
||||
inline const char *to_str(spdlog::level::level_enum l)
|
||||
{
|
||||
return level_names[l];
|
||||
}
|
||||
|
||||
[[nodiscard]] constexpr std::string_view to_short_string_view(spdlog::level lvl) noexcept {
|
||||
return short_level_names.at(level_to_number(lvl));
|
||||
inline const char *to_short_str(spdlog::level::level_enum l)
|
||||
{
|
||||
return short_level_names[l];
|
||||
}
|
||||
inline spdlog::level::level_enum from_str(const std::string &name)
|
||||
{
|
||||
static std::unordered_map<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;
|
||||
}
|
||||
|
||||
[[nodiscard]] SPDLOG_API spdlog::level level_from_str(const std::string &name) noexcept;
|
||||
using level_hasher = std::hash<int>;
|
||||
} // namespace level
|
||||
|
||||
//
|
||||
// Color mode used by sinks with color support.
|
||||
// Async overflow policy - block by default.
|
||||
//
|
||||
enum class color_mode { always, automatic, never };
|
||||
enum class async_overflow_policy
|
||||
{
|
||||
block_retry, // Block / yield / sleep until message can be enqueued
|
||||
discard_log_msg // Discard the message it enqueue fails
|
||||
};
|
||||
|
||||
//
|
||||
// Pattern time - specific time getting to use for pattern_formatter.
|
||||
// local time by default
|
||||
//
|
||||
enum class pattern_time_type {
|
||||
enum class pattern_time_type
|
||||
{
|
||||
local, // log localtime
|
||||
utc // log utc
|
||||
};
|
||||
@@ -121,19 +143,40 @@ enum class pattern_time_type {
|
||||
//
|
||||
// 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:
|
||||
explicit spdlog_ex(std::string msg);
|
||||
explicit spdlog_ex(std::string msg)
|
||||
: _msg(std::move(msg))
|
||||
{
|
||||
}
|
||||
|
||||
spdlog_ex(const std::string &msg, int last_errno);
|
||||
spdlog_ex(const std::string &msg, int last_errno)
|
||||
{
|
||||
_msg = msg + ": " + details::os::errno_str(last_errno);
|
||||
}
|
||||
|
||||
[[nodiscard]] const char *what() const noexcept override;
|
||||
const char *what() const SPDLOG_NOEXCEPT override
|
||||
{
|
||||
return _msg.c_str();
|
||||
}
|
||||
|
||||
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)
|
||||
//
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
using filename_t = std::wstring;
|
||||
#else
|
||||
using filename_t = std::string;
|
||||
#endif
|
||||
|
||||
} // namespace spdlog
|
||||
|
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();
|
||||
}
|
@@ -1,35 +0,0 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "./log_msg.h"
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
// Extend log_msg with internal buffer to store its payload.
|
||||
// This is needed since log_msg holds string_views that points to stack data.
|
||||
class SPDLOG_API async_log_msg : public log_msg {
|
||||
public:
|
||||
enum class type : std::uint8_t { log, flush, terminate };
|
||||
async_log_msg() = default;
|
||||
explicit async_log_msg(type type);
|
||||
async_log_msg(type type, const log_msg &orig_msg);
|
||||
~async_log_msg() = default;
|
||||
async_log_msg(const async_log_msg &other);
|
||||
async_log_msg(async_log_msg &&other) noexcept;
|
||||
async_log_msg &operator=(const async_log_msg &other);
|
||||
async_log_msg &operator=(async_log_msg &&other) noexcept;
|
||||
[[nodiscard]] type message_type() const { return msg_type_; }
|
||||
|
||||
private:
|
||||
type msg_type_{type::log};
|
||||
memory_buf_t buffer_;
|
||||
void update_string_views();
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
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,119 +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 <cassert>
|
||||
#include <vector>
|
||||
|
||||
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) noexcept { copy_moveable(std::move(other)); }
|
||||
|
||||
circular_q &operator=(circular_q &&other) 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 const reference to the front item.
|
||||
// If there are no elements in the container, the behavior is undefined.
|
||||
[[nodiscard]] const T &front() const { return v_[head_]; }
|
||||
|
||||
// Return reference to the front item.
|
||||
// If there are no elements in the container, the behavior is undefined.
|
||||
[[nodiscard]] T &front() { return v_[head_]; }
|
||||
|
||||
// Return number of elements actually stored
|
||||
[[nodiscard]] 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 &operator[](size_t idx) const {
|
||||
assert(idx < size());
|
||||
assert(max_items_ > 0);
|
||||
return v_[(head_ + idx) % max_items_];
|
||||
}
|
||||
|
||||
// Pop item from front if not empty.
|
||||
void pop_front() {
|
||||
if (!empty()) {
|
||||
head_ = (head_ + 1) % max_items_;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] bool empty() const { return tail_ == head_; }
|
||||
|
||||
[[nodiscard]] bool full() const {
|
||||
// head is ahead of the tail by 1
|
||||
if (max_items_ > 0) {
|
||||
return ((tail_ + 1) % max_items_) == head_;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t overrun_counter() const { return overrun_counter_; }
|
||||
|
||||
void reset_overrun_counter() { overrun_counter_ = 0; }
|
||||
|
||||
private:
|
||||
// copy from other&& and reset it to disabled state
|
||||
void copy_moveable(circular_q &&other) 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,31 +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 <exception>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "spdlog/common.h"
|
||||
|
||||
// by default, prints the error to stderr, at max rate of 1/sec thread safe
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
class SPDLOG_API err_helper {
|
||||
err_handler custom_err_handler_;
|
||||
std::chrono::steady_clock::time_point last_report_time_;
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
public:
|
||||
err_helper() = default;
|
||||
~err_helper() = default;
|
||||
err_helper(const err_helper& other);
|
||||
err_helper(err_helper&& other) noexcept;
|
||||
void handle_ex(const std::string& origin, const source_loc& loc, const std::exception& ex) noexcept;
|
||||
void handle_unknown_ex(const std::string& origin, const source_loc& loc) noexcept;
|
||||
void set_err_handler(err_handler handler);
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
@@ -1,44 +1,151 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <tuple>
|
||||
// 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 "../common.h"
|
||||
#include "../file_event_handlers.h"
|
||||
#include "../details/log_msg.h"
|
||||
#include "../details/os.h"
|
||||
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
// Helper class for file sinks.
|
||||
// When failing to open a file, retry several times(5) with a delay interval(10 ms).
|
||||
// Throw spdlog_ex exception on errors.
|
||||
class file_helper
|
||||
{
|
||||
|
||||
class SPDLOG_API file_helper {
|
||||
public:
|
||||
file_helper() = default;
|
||||
explicit file_helper(file_event_handlers event_handlers);
|
||||
const int open_tries = 5;
|
||||
const int open_interval = 10;
|
||||
|
||||
explicit file_helper() = default;
|
||||
|
||||
file_helper(const file_helper &) = delete;
|
||||
file_helper &operator=(const file_helper &) = delete;
|
||||
~file_helper();
|
||||
|
||||
void open(const filename_t &fname, bool truncate = false);
|
||||
void reopen(bool truncate);
|
||||
void flush() const;
|
||||
void sync() const;
|
||||
void close();
|
||||
void write(const memory_buf_t &buf) const;
|
||||
size_t size() const;
|
||||
const filename_t &filename() const;
|
||||
~file_helper()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void open(const filename_t &fname, bool truncate = false)
|
||||
{
|
||||
close();
|
||||
auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
|
||||
_filename = fname;
|
||||
for (int tries = 0; tries < open_tries; ++tries)
|
||||
{
|
||||
if (!os::fopen_s(&_fd, fname, mode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
details::os::sleep_for_millis(open_interval);
|
||||
}
|
||||
|
||||
throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno);
|
||||
}
|
||||
|
||||
void reopen(bool truncate)
|
||||
{
|
||||
if (_filename.empty())
|
||||
{
|
||||
throw spdlog_ex("Failed re opening file - was not opened before");
|
||||
}
|
||||
open(_filename, truncate);
|
||||
}
|
||||
|
||||
void flush()
|
||||
{
|
||||
std::fflush(_fd);
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
if (_fd != nullptr)
|
||||
{
|
||||
std::fclose(_fd);
|
||||
_fd = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void write(const 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:
|
||||
//
|
||||
// "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")
|
||||
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:
|
||||
const int open_tries_ = 5;
|
||||
const unsigned int open_interval_ = 10;
|
||||
std::FILE *fd_{nullptr};
|
||||
filename_t filename_;
|
||||
file_event_handlers event_handlers_;
|
||||
FILE *_fd{nullptr};
|
||||
filename_t _filename;
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
@@ -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 <iterator>
|
||||
#include <type_traits>
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
// Some fmt helpers to efficiently format and pad ints and strings
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
namespace fmt_helper {
|
||||
|
||||
inline void append_string_view(spdlog::string_view_t view, memory_buf_t &dest) {
|
||||
const auto *buf_ptr = view.data();
|
||||
dest.append(buf_ptr, buf_ptr + view.size());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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>
|
||||
constexpr unsigned int count_digits_fallback(T n) {
|
||||
// taken from fmt: https://github.com/fmtlib/fmt/blob/8.0.1/include/fmt/format.h#L899-L912
|
||||
unsigned int count = 1;
|
||||
for (;;) {
|
||||
// Integer division is slow so do it for a group of four digits instead
|
||||
// of for every digit. The idea comes from the talk by Alexandrescu
|
||||
// "Three Optimization Tips for C++". See speed-test for a comparison.
|
||||
if (n < 10) return count;
|
||||
if (n < 100) return count + 1;
|
||||
if (n < 1000) return count + 2;
|
||||
if (n < 10000) return count + 3;
|
||||
n /= 10000U;
|
||||
count += 4;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline unsigned int count_digits(T n) {
|
||||
using count_type = std::conditional_t<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>;
|
||||
|
||||
return static_cast<unsigned int>(fmt::
|
||||
// fmt 7.0.0 renamed the internal namespace to detail.
|
||||
// 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_lib::format_to(std::back_inserter(dest), SPDLOG_FMT_STRING("{:02}"), n);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void pad_uint(T n, unsigned int width, memory_buf_t &dest) {
|
||||
static_assert(std::is_unsigned_v<T>, "pad_uint must get unsigned T");
|
||||
for (auto digits = count_digits(n); digits < width; ++digits) {
|
||||
dest.push_back('0');
|
||||
}
|
||||
append_int(n, dest);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void pad3(T n, memory_buf_t &dest) {
|
||||
static_assert(std::is_unsigned_v<T>, "pad3 must get unsigned T");
|
||||
if (n < 1000) {
|
||||
dest.push_back(static_cast<char>((n / 100) + '0'));
|
||||
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>
|
||||
void pad6(T n, memory_buf_t &dest) {
|
||||
pad_uint(n, 6, dest);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
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>
|
||||
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,33 +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)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../common.h"
|
||||
#include "../details/os.h"
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
struct SPDLOG_API log_msg {
|
||||
struct log_msg
|
||||
{
|
||||
log_msg() = default;
|
||||
log_msg(log_clock::time_point log_time, const source_loc &loc, string_view_t logger_name, level lvl, string_view_t msg);
|
||||
log_msg(const source_loc &loc, string_view_t logger_name, level lvl, string_view_t msg);
|
||||
log_msg(string_view_t logger_name, level lvl, string_view_t msg);
|
||||
log_msg(const log_msg &other) = default;
|
||||
log_msg &operator=(const log_msg &other) = default;
|
||||
log_msg(const std::string *loggers_name, level::level_enum lvl)
|
||||
: logger_name(loggers_name)
|
||||
, level(lvl)
|
||||
{
|
||||
#ifndef SPDLOG_NO_DATETIME
|
||||
time = os::now();
|
||||
#endif
|
||||
|
||||
string_view_t logger_name;
|
||||
level log_level{level::off};
|
||||
#ifndef SPDLOG_NO_THREAD_ID
|
||||
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;
|
||||
size_t thread_id{0};
|
||||
|
||||
// wrapping the formatted text with color (updated by pattern_formatter).
|
||||
mutable size_t color_range_start{0};
|
||||
mutable size_t color_range_end{0};
|
||||
|
||||
source_loc source;
|
||||
string_view_t payload;
|
||||
size_t thread_id;
|
||||
fmt::MemoryWriter raw;
|
||||
fmt::MemoryWriter formatted;
|
||||
size_t msg_id{0};
|
||||
// wrap this range with color codes
|
||||
size_t color_range_start{0};
|
||||
size_t color_range_end{0};
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
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,177 +1,84 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// multi producer-multi consumer blocking queue.
|
||||
// enqueue(..) - will block until room found to put the new message.
|
||||
// enqueue_nowait(..) - will return immediately with false if no room left in
|
||||
// the queue.
|
||||
// dequeue_for(..) - will block until the queue is not empty or timeout have
|
||||
// passed.
|
||||
//
|
||||
// Copyright(c) 2018 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
// 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 <atomic>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
#include "./circular_q.h"
|
||||
#include <queue>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
template <typename T>
|
||||
class mpmc_blocking_queue {
|
||||
template<typename T>
|
||||
class mpmc_bounded_queue
|
||||
{
|
||||
public:
|
||||
using item_type = T;
|
||||
explicit mpmc_blocking_queue(size_t max_items)
|
||||
: q_(max_items) {}
|
||||
explicit mpmc_bounded_queue(size_t max_items)
|
||||
: max_items_(max_items)
|
||||
{
|
||||
}
|
||||
|
||||
#ifndef __MINGW32__
|
||||
// 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_);
|
||||
pop_cv_.wait(lock, [this] { return !this->q_.full(); });
|
||||
q_.push_back(std::move(item));
|
||||
pop_cv_.wait(lock, [this] { return this->q_.size() < this->max_items_; });
|
||||
q_.push(std::move(item));
|
||||
}
|
||||
push_cv_.notify_one();
|
||||
}
|
||||
|
||||
// enqueue immediately. overrun the oldest message in the queue if no room left.
|
||||
void enqueue_nowait(T &&item) {
|
||||
// try to enqueue and return immdeialty false if no room left
|
||||
bool enqueue_nowait(T &&item)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
q_.push_back(std::move(item));
|
||||
}
|
||||
push_cv_.notify_one();
|
||||
}
|
||||
|
||||
void enqueue_if_have_room(T &&item) {
|
||||
bool pushed = false;
|
||||
if (q_.size() == this->max_items_)
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
if (!q_.full()) {
|
||||
q_.push_back(std::move(item));
|
||||
pushed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (pushed) {
|
||||
push_cv_.notify_one();
|
||||
} else {
|
||||
++discard_counter_;
|
||||
}
|
||||
}
|
||||
|
||||
// dequeue with a timeout.
|
||||
// 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;
|
||||
}
|
||||
q_.push(std::forward<T>(item));
|
||||
}
|
||||
push_cv_.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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_.size() > 0; }))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
popped_item = std::move(q_.front());
|
||||
q_.pop_front();
|
||||
q_.pop();
|
||||
}
|
||||
pop_cv_.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
// blocking dequeue without a timeout.
|
||||
void dequeue(T &popped_item) {
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
push_cv_.wait(lock, [this] { return !this->q_.empty(); });
|
||||
popped_item = std::move(q_.front());
|
||||
q_.pop_front();
|
||||
}
|
||||
pop_cv_.notify_one();
|
||||
}
|
||||
|
||||
#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();
|
||||
}
|
||||
|
||||
void enqueue_if_have_room(T &&item) {
|
||||
bool pushed = false;
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
if (!q_.full()) {
|
||||
q_.push_back(std::move(item));
|
||||
pushed = true;
|
||||
}
|
||||
|
||||
if (pushed) {
|
||||
push_cv_.notify_one();
|
||||
} else {
|
||||
++discard_counter_;
|
||||
}
|
||||
}
|
||||
|
||||
// dequeue with a timeout.
|
||||
// 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;
|
||||
}
|
||||
popped_item = std::move(q_.front());
|
||||
q_.pop_front();
|
||||
pop_cv_.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
// blocking dequeue without a timeout.
|
||||
void dequeue(T &popped_item) {
|
||||
std::unique_lock<std::mutex> lock(queue_mutex_);
|
||||
push_cv_.wait(lock, [this] { return !this->q_.empty(); });
|
||||
popped_item = std::move(q_.front());
|
||||
q_.pop_front();
|
||||
pop_cv_.notify_one();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
size_t overrun_counter() {
|
||||
std::lock_guard<std::mutex> lock(queue_mutex_);
|
||||
return q_.overrun_counter();
|
||||
}
|
||||
|
||||
size_t discard_counter() { return discard_counter_.load(std::memory_order_relaxed); }
|
||||
|
||||
size_t size() {
|
||||
std::lock_guard<std::mutex> lock(queue_mutex_);
|
||||
return q_.size();
|
||||
}
|
||||
|
||||
void reset_overrun_counter() {
|
||||
std::lock_guard<std::mutex> lock(queue_mutex_);
|
||||
q_.reset_overrun_counter();
|
||||
}
|
||||
|
||||
void reset_discard_counter() { discard_counter_.store(0, std::memory_order_relaxed); }
|
||||
|
||||
private:
|
||||
size_t max_items_;
|
||||
std::mutex queue_mutex_;
|
||||
std::condition_variable push_cv_;
|
||||
std::condition_variable pop_cv_;
|
||||
spdlog::details::circular_q<T> q_;
|
||||
std::atomic<size_t> discard_counter_{0};
|
||||
|
||||
std::queue<T> q_;
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
@@ -1,35 +1,43 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
// null, no cost dummy "mutex" and dummy "atomic" int
|
||||
|
||||
// null, no cost dummy "mutex" and dummy "atomic" log level
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
struct null_mutex {
|
||||
void lock() const {}
|
||||
void unlock() const {}
|
||||
struct null_mutex
|
||||
{
|
||||
void lock() {}
|
||||
void unlock() {}
|
||||
bool try_lock()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct null_atomic {
|
||||
T value;
|
||||
struct null_atomic_int
|
||||
{
|
||||
int value;
|
||||
null_atomic_int() = default;
|
||||
|
||||
null_atomic() = default;
|
||||
explicit null_atomic_int(int val)
|
||||
: value(val)
|
||||
{
|
||||
}
|
||||
|
||||
explicit constexpr null_atomic(T new_value)
|
||||
: value(new_value) {}
|
||||
int load(std::memory_order) const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
[[nodiscard]] T load(std::memory_order = std::memory_order_seq_cst) const { return value; }
|
||||
|
||||
void store(T new_value, std::memory_order = std::memory_order_seq_cst) { value = new_value; }
|
||||
|
||||
T exchange(T new_value, std::memory_order = std::memory_order_seq_cst) {
|
||||
std::swap(new_value, value);
|
||||
return new_value; // return value before the call
|
||||
void store(int val)
|
||||
{
|
||||
value = val;
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -1,128 +1,479 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include <ctime> // std::time_t
|
||||
#include <tuple>
|
||||
|
||||
#include "../common.h"
|
||||
#include "../filename_t.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <thread>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX // prevent windows redefining min/max
|
||||
#endif
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <io.h> // _get_osfhandle and _isatty support
|
||||
#include <process.h> // _get_pid support
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <share.h>
|
||||
#endif
|
||||
|
||||
#else // unix
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
|
||||
|
||||
#elif __FreeBSD__
|
||||
#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
|
||||
#endif
|
||||
|
||||
#endif // unix
|
||||
|
||||
#ifndef __has_feature // Clang - feature checking macros.
|
||||
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
|
||||
#endif
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
namespace os {
|
||||
|
||||
SPDLOG_API spdlog::log_clock::time_point now() noexcept;
|
||||
inline spdlog::log_clock::time_point now()
|
||||
{
|
||||
|
||||
SPDLOG_API std::tm localtime(const std::time_t &time_tt) noexcept;
|
||||
#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
|
||||
timespec ts;
|
||||
::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
|
||||
return std::chrono::time_point<log_clock, typename log_clock::duration>(
|
||||
std::chrono::duration_cast<typename log_clock::duration>(std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
|
||||
|
||||
SPDLOG_API std::tm localtime() noexcept;
|
||||
|
||||
SPDLOG_API std::tm gmtime(const std::time_t &time_tt) noexcept;
|
||||
|
||||
SPDLOG_API std::tm gmtime() noexcept;
|
||||
|
||||
// eol definition and folder separator for the current os
|
||||
#ifdef _WIN32
|
||||
constexpr static const char *default_eol = "\r\n";
|
||||
#else
|
||||
constexpr static const char *default_eol = "\n";
|
||||
return log_clock::now();
|
||||
#endif
|
||||
}
|
||||
inline std::tm localtime(const std::time_t &time_tt)
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
std::tm tm;
|
||||
localtime_s(&tm, &time_tt);
|
||||
#else
|
||||
std::tm tm;
|
||||
localtime_r(&time_tt, &tm);
|
||||
#endif
|
||||
return tm;
|
||||
}
|
||||
|
||||
inline std::tm localtime()
|
||||
{
|
||||
std::time_t now_t = time(nullptr);
|
||||
return localtime(now_t);
|
||||
}
|
||||
|
||||
inline std::tm gmtime(const std::time_t &time_tt)
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
std::tm tm;
|
||||
gmtime_s(&tm, &time_tt);
|
||||
#else
|
||||
std::tm tm;
|
||||
gmtime_r(&time_tt, &tm);
|
||||
#endif
|
||||
return tm;
|
||||
}
|
||||
|
||||
inline std::tm gmtime()
|
||||
{
|
||||
std::time_t now_t = time(nullptr);
|
||||
return gmtime(now_t);
|
||||
}
|
||||
inline bool operator==(const std::tm &tm1, const std::tm &tm2)
|
||||
{
|
||||
return (tm1.tm_sec == tm2.tm_sec && tm1.tm_min == tm2.tm_min && tm1.tm_hour == tm2.tm_hour && tm1.tm_mday == tm2.tm_mday &&
|
||||
tm1.tm_mon == tm2.tm_mon && tm1.tm_year == tm2.tm_year && tm1.tm_isdst == tm2.tm_isdst);
|
||||
}
|
||||
|
||||
inline bool operator!=(const std::tm &tm1, const std::tm &tm2)
|
||||
{
|
||||
return !(tm1 == tm2);
|
||||
}
|
||||
|
||||
// eol definition
|
||||
#if !defined(SPDLOG_EOL)
|
||||
#ifdef _WIN32
|
||||
#define SPDLOG_EOL "\r\n"
|
||||
#else
|
||||
#define SPDLOG_EOL "\n"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL;
|
||||
|
||||
// folder separator
|
||||
#ifdef _WIN32
|
||||
SPDLOG_CONSTEXPR static const char folder_sep = '\\';
|
||||
#else
|
||||
SPDLOG_CONSTEXPR static const char folder_sep = '/';
|
||||
#endif
|
||||
|
||||
inline void prevent_child_fd(FILE *f)
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
#if !defined(__cplusplus_winrt)
|
||||
auto file_handle = (HANDLE)_get_osfhandle(_fileno(f));
|
||||
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
|
||||
throw spdlog_ex("SetHandleInformation failed", errno);
|
||||
#endif
|
||||
#else
|
||||
auto fd = fileno(f);
|
||||
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
|
||||
{
|
||||
throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// fopen_s on non windows for writing
|
||||
SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode);
|
||||
inline bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||
*fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
|
||||
#else
|
||||
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
|
||||
#endif
|
||||
#else // unix
|
||||
*fp = fopen((filename.c_str()), mode.c_str());
|
||||
#endif
|
||||
|
||||
#ifdef SPDLOG_PREVENT_CHILD_FD
|
||||
if (*fp != nullptr)
|
||||
{
|
||||
prevent_child_fd(*fp);
|
||||
}
|
||||
#endif
|
||||
return *fp == nullptr;
|
||||
}
|
||||
|
||||
inline int remove(const filename_t &filename)
|
||||
{
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
return _wremove(filename.c_str());
|
||||
#else
|
||||
return std::remove(filename.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int rename(const filename_t &filename1, const filename_t &filename2)
|
||||
{
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
return _wrename(filename1.c_str(), filename2.c_str());
|
||||
#else
|
||||
return std::rename(filename1.c_str(), filename2.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return if file exists
|
||||
inline bool file_exists(const filename_t &filename)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||
auto attribs = GetFileAttributesW(filename.c_str());
|
||||
#else
|
||||
auto attribs = GetFileAttributesA(filename.c_str());
|
||||
#endif
|
||||
return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
|
||||
#else // common linux/unix all have the stat system call
|
||||
struct stat buffer;
|
||||
return (stat(filename.c_str(), &buffer) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return file size according to open FILE* object
|
||||
SPDLOG_API size_t filesize(FILE *f);
|
||||
inline size_t filesize(FILE *f)
|
||||
{
|
||||
if (f == nullptr)
|
||||
{
|
||||
throw spdlog_ex("Failed getting file size. fd is null");
|
||||
}
|
||||
#if defined(_WIN32) && !defined(__CYGWIN__)
|
||||
int fd = _fileno(f);
|
||||
#if _WIN64 // 64 bits
|
||||
struct _stat64 st;
|
||||
if (_fstat64(fd, &st) == 0)
|
||||
{
|
||||
return st.st_size;
|
||||
}
|
||||
|
||||
#else // windows 32 bits
|
||||
long ret = _filelength(fd);
|
||||
if (ret >= 0)
|
||||
{
|
||||
return static_cast<size_t>(ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
#else // unix
|
||||
int fd = fileno(f);
|
||||
// 64 bits(but not in osx or cygwin, where fstat64 is deprecated)
|
||||
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__)) && !defined(__CYGWIN__)
|
||||
struct stat64 st;
|
||||
if (fstat64(fd, &st) == 0)
|
||||
{
|
||||
return static_cast<size_t>(st.st_size);
|
||||
}
|
||||
#else // unix 32 bits or cygwin
|
||||
struct stat st;
|
||||
|
||||
if (fstat(fd, &st) == 0)
|
||||
{
|
||||
return static_cast<size_t>(st.st_size);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
throw spdlog_ex("Failed getting file size from fd", errno);
|
||||
}
|
||||
|
||||
// Return utc offset in minutes or throw spdlog_ex on failure
|
||||
SPDLOG_API int utc_minutes_offset(const std::tm &tm = details::os::localtime());
|
||||
inline int utc_minutes_offset(const std::tm &tm = details::os::localtime())
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
#if _WIN32_WINNT < _WIN32_WINNT_WS08
|
||||
TIME_ZONE_INFORMATION tzinfo;
|
||||
auto rv = GetTimeZoneInformation(&tzinfo);
|
||||
#else
|
||||
DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
|
||||
auto rv = GetDynamicTimeZoneInformation(&tzinfo);
|
||||
#endif
|
||||
if (rv == TIME_ZONE_ID_INVALID)
|
||||
throw spdlog::spdlog_ex("Failed getting timezone info. ", errno);
|
||||
|
||||
int offset = -tzinfo.Bias;
|
||||
if (tm.tm_isdst)
|
||||
{
|
||||
offset -= tzinfo.DaylightBias;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset -= tzinfo.StandardBias;
|
||||
}
|
||||
return offset;
|
||||
#else
|
||||
|
||||
#if defined(sun) || defined(__sun)
|
||||
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
|
||||
struct helper
|
||||
{
|
||||
static long int calculate_gmt_offset(const std::tm &localtm = details::os::localtime(), const std::tm &gmtm = details::os::gmtime())
|
||||
{
|
||||
int local_year = localtm.tm_year + (1900 - 1);
|
||||
int gmt_year = gmtm.tm_year + (1900 - 1);
|
||||
|
||||
long int days = (
|
||||
// difference in day of year
|
||||
localtm.tm_yday -
|
||||
gmtm.tm_yday
|
||||
|
||||
// + intervening leap days
|
||||
+ ((local_year >> 2) - (gmt_year >> 2)) - (local_year / 100 - gmt_year / 100) +
|
||||
((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
|
||||
|
||||
// + difference in years * 365 */
|
||||
+ (long int)(local_year - gmt_year) * 365);
|
||||
|
||||
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
|
||||
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
|
||||
long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
|
||||
|
||||
return secs;
|
||||
}
|
||||
};
|
||||
|
||||
auto offset_seconds = helper::calculate_gmt_offset(tm);
|
||||
#else
|
||||
auto offset_seconds = tm.tm_gmtoff;
|
||||
#endif
|
||||
|
||||
return static_cast<int>(offset_seconds / 60);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return current thread id as size_t
|
||||
// It exists because the std::this_thread::get_id() is much slower(especially
|
||||
// under VS 2013)
|
||||
SPDLOG_API size_t _thread_id() noexcept;
|
||||
// It exists because the std::this_thread::get_id() is much slower(especially under VS 2013)
|
||||
inline size_t _thread_id()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return static_cast<size_t>(::GetCurrentThreadId());
|
||||
#elif __linux__
|
||||
#if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
|
||||
#define SYS_gettid __NR_gettid
|
||||
#endif
|
||||
return static_cast<size_t>(syscall(SYS_gettid));
|
||||
#elif __FreeBSD__
|
||||
long tid;
|
||||
thr_self(&tid);
|
||||
return static_cast<size_t>(tid);
|
||||
#elif __APPLE__
|
||||
uint64_t tid;
|
||||
pthread_threadid_np(nullptr, &tid);
|
||||
return static_cast<size_t>(tid);
|
||||
#else // Default to standard C++11 (other Unix)
|
||||
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return current thread id as size_t (from thread local storage)
|
||||
SPDLOG_API size_t thread_id() noexcept;
|
||||
inline size_t thread_id()
|
||||
{
|
||||
#if defined(SPDLOG_DISABLE_TID_CACHING) || (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt) || \
|
||||
(defined(__clang__) && !__has_feature(cxx_thread_local))
|
||||
return _thread_id();
|
||||
#else // cache thread id in tls
|
||||
static thread_local const size_t tid = _thread_id();
|
||||
return tid;
|
||||
#endif
|
||||
}
|
||||
|
||||
// This is avoid msvc issue in sleep_for that happens if the clock changes.
|
||||
// See https://github.com/gabime/spdlog/issues/609
|
||||
SPDLOG_API void sleep_for_millis(unsigned int milliseconds) noexcept;
|
||||
inline void sleep_for_millis(int milliseconds)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
::Sleep(milliseconds);
|
||||
#else
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(milliseconds));
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return pid
|
||||
SPDLOG_API int pid() noexcept;
|
||||
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
#define SPDLOG_FILENAME_T(s) L##s
|
||||
inline std::string filename_to_str(const filename_t &filename)
|
||||
{
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c;
|
||||
return c.to_bytes(filename);
|
||||
}
|
||||
#else
|
||||
#define SPDLOG_FILENAME_T(s) s
|
||||
inline std::string filename_to_str(const filename_t &filename)
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline std::string errno_to_string(char[256], char *res)
|
||||
{
|
||||
return std::string(res);
|
||||
}
|
||||
|
||||
inline std::string errno_to_string(char buf[256], int res)
|
||||
{
|
||||
if (res == 0)
|
||||
{
|
||||
return std::string(buf);
|
||||
}
|
||||
return "Unknown error";
|
||||
}
|
||||
|
||||
// Return errno string (thread safe)
|
||||
inline std::string errno_str(int err_num)
|
||||
{
|
||||
char buf[256];
|
||||
SPDLOG_CONSTEXPR auto buf_size = sizeof(buf);
|
||||
|
||||
#ifdef _WIN32
|
||||
if (strerror_s(buf, buf_size, err_num) == 0)
|
||||
{
|
||||
return std::string(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Unknown error";
|
||||
}
|
||||
|
||||
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \
|
||||
((_POSIX_C_SOURCE >= 200112L) && !defined(_GNU_SOURCE)) // posix version
|
||||
|
||||
if (strerror_r(err_num, buf, buf_size) == 0)
|
||||
{
|
||||
return std::string(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Unknown error";
|
||||
}
|
||||
|
||||
#else // gnu version (might not use the given buf, so its retval pointer must be used)
|
||||
auto err = strerror_r(err_num, buf, buf_size); // let compiler choose type
|
||||
return errno_to_string(buf, err); // use overloading to select correct stringify function
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int pid()
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
return static_cast<int>(::GetCurrentProcessId());
|
||||
#else
|
||||
return static_cast<int>(::getpid());
|
||||
#endif
|
||||
}
|
||||
|
||||
// Determine if the terminal supports colors
|
||||
// Source: https://github.com/agauniyal/rang/
|
||||
SPDLOG_API bool is_color_terminal() noexcept;
|
||||
inline bool is_color_terminal()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return true;
|
||||
#else
|
||||
static constexpr const char *Terms[] = {
|
||||
"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"};
|
||||
|
||||
// Determine if the terminal attached
|
||||
// Source: https://github.com/agauniyal/rang/
|
||||
SPDLOG_API bool in_terminal(FILE *file) noexcept;
|
||||
const char *env_p = std::getenv("TERM");
|
||||
if (env_p == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#if (defined _WIN32)
|
||||
SPDLOG_API void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target);
|
||||
SPDLOG_API void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target);
|
||||
static const bool result =
|
||||
std::any_of(std::begin(Terms), std::end(Terms), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
// non thread safe, cross platform getenv/getenv_s
|
||||
// return empty string if field not found
|
||||
SPDLOG_API std::string getenv(const char *field);
|
||||
|
||||
// Do fsync by FILE objectpointer.
|
||||
// Return true on success.
|
||||
SPDLOG_API bool fsync(FILE *fp);
|
||||
|
||||
// Do non-locking fwrite if possible by the os or use the regular locking fwrite
|
||||
// Return true on success.
|
||||
SPDLOG_API bool fwrite_bytes(const void *ptr, const size_t n_bytes, FILE *fp);
|
||||
|
||||
//
|
||||
// std::filesystem wrapper functions
|
||||
//
|
||||
|
||||
// Return directory name from given path or empty string
|
||||
// "abc/file" => "abc"
|
||||
// "abc/" => "abc"
|
||||
// "abc" => ""
|
||||
SPDLOG_API filename_t dir_name(const filename_t &path);
|
||||
|
||||
// Create a dir from the given path.
|
||||
// Return true if succeeded or if this dir already exists.
|
||||
SPDLOG_API bool create_dir(const filename_t &path);
|
||||
|
||||
// Remove filename. return true on success
|
||||
SPDLOG_API bool remove(const filename_t &filename);
|
||||
|
||||
// Remove file if exists. return 0 on success
|
||||
// Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread)
|
||||
SPDLOG_API bool remove_if_exists(const filename_t &filename);
|
||||
|
||||
// Rename file. return true on success
|
||||
SPDLOG_API bool rename(const filename_t &filename1, const filename_t &filename2) noexcept;
|
||||
|
||||
// Return if file exists.
|
||||
SPDLOG_API bool path_exists(const filename_t &filename) noexcept;
|
||||
|
||||
// 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_API std::tuple<filename_t, filename_t> split_by_extension(const filename_t &fname);
|
||||
|
||||
// Try tp convert filename to string. Return "??" if failed
|
||||
SPDLOG_API std::string filename_to_str(const filename_t &filename);
|
||||
// Detrmine if the terminal attached
|
||||
// Source: https://github.com/agauniyal/rang/
|
||||
inline bool in_terminal(FILE *file)
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
return _isatty(_fileno(file)) != 0;
|
||||
#else
|
||||
return isatty(fileno(file)) != 0;
|
||||
#endif
|
||||
}
|
||||
} // namespace os
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
|
707
include/spdlog/details/pattern_formatter_impl.h
Normal file
707
include/spdlog/details/pattern_formatter_impl.h
Normal file
@@ -0,0 +1,707 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../details/log_msg.h"
|
||||
#include "../details/os.h"
|
||||
#include "../fmt/fmt.h"
|
||||
#include "../formatter.h"
|
||||
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
class flag_formatter
|
||||
{
|
||||
public:
|
||||
virtual ~flag_formatter() = default;
|
||||
virtual void format(details::log_msg &msg, const std::tm &tm_time) = 0;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// name & level pattern appenders
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
class name_formatter : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &) override
|
||||
{
|
||||
msg.formatted << *msg.logger_name;
|
||||
}
|
||||
};
|
||||
|
||||
// log level appender
|
||||
class level_formatter : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &) override
|
||||
{
|
||||
msg.formatted << level::to_str(msg.level);
|
||||
}
|
||||
};
|
||||
|
||||
// short log level appender
|
||||
class short_level_formatter : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &) override
|
||||
{
|
||||
msg.formatted << level::to_short_str(msg.level);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Date time pattern appenders
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const char *ampm(const tm &t)
|
||||
{
|
||||
return t.tm_hour >= 12 ? "PM" : "AM";
|
||||
}
|
||||
|
||||
static int to12h(const tm &t)
|
||||
{
|
||||
return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour;
|
||||
}
|
||||
|
||||
// Abbreviated weekday name
|
||||
static const std::string days[]{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
|
||||
class a_formatter : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
msg.formatted << days[tm_time.tm_wday];
|
||||
}
|
||||
};
|
||||
|
||||
// Full weekday name
|
||||
static const std::string full_days[]{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
|
||||
class A_formatter : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
msg.formatted << full_days[tm_time.tm_wday];
|
||||
}
|
||||
};
|
||||
|
||||
// Abbreviated month
|
||||
static const std::string months[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec"};
|
||||
class b_formatter : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
msg.formatted << months[tm_time.tm_mon];
|
||||
}
|
||||
};
|
||||
|
||||
// Full month name
|
||||
static const std::string full_months[]{
|
||||
"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"};
|
||||
class B_formatter : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
msg.formatted << full_months[tm_time.tm_mon];
|
||||
}
|
||||
};
|
||||
|
||||
// write 2 ints separated by sep with padding of 2
|
||||
static fmt::MemoryWriter &pad_n_join(fmt::MemoryWriter &w, int v1, int v2, char sep)
|
||||
{
|
||||
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0');
|
||||
return w;
|
||||
}
|
||||
|
||||
// write 3 ints separated by sep with padding of 2
|
||||
static fmt::MemoryWriter &pad_n_join(fmt::MemoryWriter &w, int v1, int v2, int v3, char sep)
|
||||
{
|
||||
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0');
|
||||
return w;
|
||||
}
|
||||
|
||||
// Date and time representation (Thu Aug 23 15:35:46 2014)
|
||||
class c_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
msg.formatted << days[tm_time.tm_wday] << ' ' << months[tm_time.tm_mon] << ' ' << tm_time.tm_mday << ' ';
|
||||
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << tm_time.tm_year + 1900;
|
||||
}
|
||||
};
|
||||
|
||||
// year - 2 digit
|
||||
class C_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
msg.formatted << fmt::pad(tm_time.tm_year % 100, 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
|
||||
class D_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
pad_n_join(msg.formatted, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_year % 100, '/');
|
||||
}
|
||||
};
|
||||
|
||||
// year - 4 digit
|
||||
class Y_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
msg.formatted << tm_time.tm_year + 1900;
|
||||
}
|
||||
};
|
||||
|
||||
// month 1-12
|
||||
class m_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
msg.formatted << fmt::pad(tm_time.tm_mon + 1, 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// day of month 1-31
|
||||
class d_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
msg.formatted << fmt::pad(tm_time.tm_mday, 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// hours in 24 format 0-23
|
||||
class H_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
msg.formatted << fmt::pad(tm_time.tm_hour, 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// hours in 12 format 1-12
|
||||
class I_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
msg.formatted << fmt::pad(to12h(tm_time), 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// minutes 0-59
|
||||
class M_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
msg.formatted << fmt::pad(tm_time.tm_min, 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// seconds 0-59
|
||||
class S_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
msg.formatted << fmt::pad(tm_time.tm_sec, 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// milliseconds
|
||||
class e_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &) override
|
||||
{
|
||||
auto duration = msg.time.time_since_epoch();
|
||||
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
|
||||
msg.formatted << fmt::pad(static_cast<int>(millis), 3, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// microseconds
|
||||
class f_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &) override
|
||||
{
|
||||
auto duration = msg.time.time_since_epoch();
|
||||
auto micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count() % 1000000;
|
||||
msg.formatted << fmt::pad(static_cast<int>(micros), 6, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// nanoseconds
|
||||
class F_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &) override
|
||||
{
|
||||
auto duration = msg.time.time_since_epoch();
|
||||
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % 1000000000;
|
||||
msg.formatted << fmt::pad(static_cast<int>(ns), 9, '0');
|
||||
}
|
||||
};
|
||||
|
||||
class E_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &) override
|
||||
{
|
||||
auto duration = msg.time.time_since_epoch();
|
||||
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
|
||||
msg.formatted << seconds;
|
||||
}
|
||||
};
|
||||
|
||||
// AM/PM
|
||||
class p_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
msg.formatted << ampm(tm_time);
|
||||
}
|
||||
};
|
||||
|
||||
// 12 hour clock 02:55:02 pm
|
||||
class r_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
pad_n_join(msg.formatted, to12h(tm_time), tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << ampm(tm_time);
|
||||
}
|
||||
};
|
||||
|
||||
// 24-hour HH:MM time, equivalent to %H:%M
|
||||
class R_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, ':');
|
||||
}
|
||||
};
|
||||
|
||||
// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
|
||||
class T_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':');
|
||||
}
|
||||
};
|
||||
|
||||
// ISO 8601 offset from UTC in timezone (+-HH:MM)
|
||||
class z_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
public:
|
||||
const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
|
||||
|
||||
z_formatter() = default;
|
||||
z_formatter(const z_formatter &) = delete;
|
||||
z_formatter &operator=(const z_formatter &) = delete;
|
||||
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
#ifdef _WIN32
|
||||
int total_minutes = get_cached_offset(msg, tm_time);
|
||||
#else
|
||||
// No need to chache under gcc,
|
||||
// it is very fast (already stored in tm.tm_gmtoff)
|
||||
int total_minutes = os::utc_minutes_offset(tm_time);
|
||||
#endif
|
||||
bool is_negative = total_minutes < 0;
|
||||
char sign;
|
||||
if (is_negative)
|
||||
{
|
||||
total_minutes = -total_minutes;
|
||||
sign = '-';
|
||||
}
|
||||
else
|
||||
{
|
||||
sign = '+';
|
||||
}
|
||||
|
||||
int h = total_minutes / 60;
|
||||
int m = total_minutes % 60;
|
||||
msg.formatted << sign;
|
||||
pad_n_join(msg.formatted, h, m, ':');
|
||||
}
|
||||
|
||||
private:
|
||||
log_clock::time_point _last_update{std::chrono::seconds(0)};
|
||||
int _offset_minutes{0};
|
||||
std::mutex _mutex;
|
||||
|
||||
int get_cached_offset(const log_msg &msg, const std::tm &tm_time)
|
||||
{
|
||||
std::lock_guard<std::mutex> l(_mutex);
|
||||
if (msg.time - _last_update >= cache_refresh)
|
||||
{
|
||||
_offset_minutes = os::utc_minutes_offset(tm_time);
|
||||
_last_update = msg.time;
|
||||
}
|
||||
return _offset_minutes;
|
||||
}
|
||||
};
|
||||
|
||||
// Thread id
|
||||
class t_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &) override
|
||||
{
|
||||
msg.formatted << msg.thread_id;
|
||||
}
|
||||
};
|
||||
|
||||
// Current pid
|
||||
class pid_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &) override
|
||||
{
|
||||
msg.formatted << details::os::pid();
|
||||
}
|
||||
};
|
||||
|
||||
// message counter formatter
|
||||
class i_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &) override
|
||||
{
|
||||
msg.formatted << fmt::pad(msg.msg_id, 6, '0');
|
||||
}
|
||||
};
|
||||
|
||||
class v_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &) override
|
||||
{
|
||||
msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
|
||||
}
|
||||
};
|
||||
|
||||
class ch_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
public:
|
||||
explicit ch_formatter(char ch)
|
||||
: _ch(ch)
|
||||
{
|
||||
}
|
||||
void format(details::log_msg &msg, const std::tm &) override
|
||||
{
|
||||
msg.formatted << _ch;
|
||||
}
|
||||
|
||||
private:
|
||||
char _ch;
|
||||
};
|
||||
|
||||
// aggregate user chars to display as is
|
||||
class aggregate_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
public:
|
||||
aggregate_formatter() = default;
|
||||
|
||||
void add_ch(char ch)
|
||||
{
|
||||
_str += ch;
|
||||
}
|
||||
void format(details::log_msg &msg, const std::tm &) override
|
||||
{
|
||||
msg.formatted << _str;
|
||||
}
|
||||
|
||||
private:
|
||||
std::string _str;
|
||||
};
|
||||
|
||||
// mark the color range. expect it to be in the form of "%^colored text%$"
|
||||
class color_start_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &) override
|
||||
{
|
||||
msg.color_range_start = msg.formatted.size();
|
||||
}
|
||||
};
|
||||
class color_stop_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &) override
|
||||
{
|
||||
msg.color_range_end = msg.formatted.size();
|
||||
}
|
||||
};
|
||||
|
||||
// Full info formatter
|
||||
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
|
||||
class full_formatter SPDLOG_FINAL : public flag_formatter
|
||||
{
|
||||
void format(details::log_msg &msg, const std::tm &tm_time) override
|
||||
{
|
||||
#ifndef SPDLOG_NO_DATETIME
|
||||
auto duration = msg.time.time_since_epoch();
|
||||
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
|
||||
|
||||
/* Slower version(while still very fast - about 3.2 million lines/sec under 10 threads),
|
||||
msg.formatted.write("[{:d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}.{:03d}] [{}] [{}] {} ",
|
||||
tm_time.tm_year + 1900,
|
||||
tm_time.tm_mon + 1,
|
||||
tm_time.tm_mday,
|
||||
tm_time.tm_hour,
|
||||
tm_time.tm_min,
|
||||
tm_time.tm_sec,
|
||||
static_cast<int>(millis),
|
||||
msg.logger_name,
|
||||
level::to_str(msg.level),
|
||||
msg.raw.str());*/
|
||||
|
||||
// Faster (albeit uglier) way to format the line (5.6 million lines/sec under 10 threads)
|
||||
msg.formatted << '[' << static_cast<unsigned int>(tm_time.tm_year + 1900) << '-'
|
||||
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_mon + 1), 2, '0') << '-'
|
||||
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_mday), 2, '0') << ' '
|
||||
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_hour), 2, '0') << ':'
|
||||
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_min), 2, '0') << ':'
|
||||
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_sec), 2, '0') << '.'
|
||||
<< fmt::pad(static_cast<unsigned int>(millis), 3, '0') << "] ";
|
||||
|
||||
// no datetime needed
|
||||
#else
|
||||
(void)tm_time;
|
||||
#endif
|
||||
|
||||
#ifndef SPDLOG_NO_NAME
|
||||
msg.formatted << '[' << *msg.logger_name << "] ";
|
||||
#endif
|
||||
|
||||
msg.formatted << '[';
|
||||
// wrap the level name with color
|
||||
msg.color_range_start = msg.formatted.size();
|
||||
msg.formatted << level::to_str(msg.level);
|
||||
msg.color_range_end = msg.formatted.size();
|
||||
msg.formatted << "] " << fmt::StringRef(msg.raw.data(), msg.raw.size());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// pattern_formatter inline impl
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
inline spdlog::pattern_formatter::pattern_formatter(const std::string &pattern, pattern_time_type pattern_time, std::string eol)
|
||||
: _eol(std::move(eol))
|
||||
, _pattern_time(pattern_time)
|
||||
{
|
||||
compile_pattern(pattern);
|
||||
}
|
||||
|
||||
inline void spdlog::pattern_formatter::compile_pattern(const std::string &pattern)
|
||||
{
|
||||
auto end = pattern.end();
|
||||
std::unique_ptr<details::aggregate_formatter> user_chars;
|
||||
for (auto it = pattern.begin(); it != end; ++it)
|
||||
{
|
||||
if (*it == '%')
|
||||
{
|
||||
if (user_chars) // append user chars found so far
|
||||
{
|
||||
_formatters.push_back(std::move(user_chars));
|
||||
}
|
||||
// if(
|
||||
if (++it != end)
|
||||
{
|
||||
handle_flag(*it);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else // chars not following the % sign should be displayed as is
|
||||
{
|
||||
if (!user_chars)
|
||||
{
|
||||
user_chars = std::unique_ptr<details::aggregate_formatter>(new details::aggregate_formatter());
|
||||
}
|
||||
user_chars->add_ch(*it);
|
||||
}
|
||||
}
|
||||
if (user_chars) // append raw chars found so far
|
||||
{
|
||||
_formatters.push_back(std::move(user_chars));
|
||||
}
|
||||
}
|
||||
inline void spdlog::pattern_formatter::handle_flag(char flag)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
// logger name
|
||||
case 'n':
|
||||
_formatters.emplace_back(new details::name_formatter());
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
_formatters.emplace_back(new details::level_formatter());
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
_formatters.emplace_back(new details::short_level_formatter());
|
||||
break;
|
||||
|
||||
case ('t'):
|
||||
_formatters.emplace_back(new details::t_formatter());
|
||||
break;
|
||||
|
||||
case ('v'):
|
||||
_formatters.emplace_back(new details::v_formatter());
|
||||
break;
|
||||
|
||||
case ('a'):
|
||||
_formatters.emplace_back(new details::a_formatter());
|
||||
break;
|
||||
|
||||
case ('A'):
|
||||
_formatters.emplace_back(new details::A_formatter());
|
||||
break;
|
||||
|
||||
case ('b'):
|
||||
case ('h'):
|
||||
_formatters.emplace_back(new details::b_formatter());
|
||||
break;
|
||||
|
||||
case ('B'):
|
||||
_formatters.emplace_back(new details::B_formatter());
|
||||
break;
|
||||
case ('c'):
|
||||
_formatters.emplace_back(new details::c_formatter());
|
||||
break;
|
||||
|
||||
case ('C'):
|
||||
_formatters.emplace_back(new details::C_formatter());
|
||||
break;
|
||||
|
||||
case ('Y'):
|
||||
_formatters.emplace_back(new details::Y_formatter());
|
||||
break;
|
||||
|
||||
case ('D'):
|
||||
case ('x'):
|
||||
|
||||
_formatters.emplace_back(new details::D_formatter());
|
||||
break;
|
||||
|
||||
case ('m'):
|
||||
_formatters.emplace_back(new details::m_formatter());
|
||||
break;
|
||||
|
||||
case ('d'):
|
||||
_formatters.emplace_back(new details::d_formatter());
|
||||
break;
|
||||
|
||||
case ('H'):
|
||||
_formatters.emplace_back(new details::H_formatter());
|
||||
break;
|
||||
|
||||
case ('I'):
|
||||
_formatters.emplace_back(new details::I_formatter());
|
||||
break;
|
||||
|
||||
case ('M'):
|
||||
_formatters.emplace_back(new details::M_formatter());
|
||||
break;
|
||||
|
||||
case ('S'):
|
||||
_formatters.emplace_back(new details::S_formatter());
|
||||
break;
|
||||
|
||||
case ('e'):
|
||||
_formatters.emplace_back(new details::e_formatter());
|
||||
break;
|
||||
|
||||
case ('f'):
|
||||
_formatters.emplace_back(new details::f_formatter());
|
||||
break;
|
||||
case ('F'):
|
||||
_formatters.emplace_back(new details::F_formatter());
|
||||
break;
|
||||
|
||||
case ('E'):
|
||||
_formatters.emplace_back(new details::E_formatter());
|
||||
break;
|
||||
|
||||
case ('p'):
|
||||
_formatters.emplace_back(new details::p_formatter());
|
||||
break;
|
||||
|
||||
case ('r'):
|
||||
_formatters.emplace_back(new details::r_formatter());
|
||||
break;
|
||||
|
||||
case ('R'):
|
||||
_formatters.emplace_back(new details::R_formatter());
|
||||
break;
|
||||
|
||||
case ('T'):
|
||||
case ('X'):
|
||||
_formatters.emplace_back(new details::T_formatter());
|
||||
break;
|
||||
|
||||
case ('z'):
|
||||
_formatters.emplace_back(new details::z_formatter());
|
||||
break;
|
||||
|
||||
case ('+'):
|
||||
_formatters.emplace_back(new details::full_formatter());
|
||||
break;
|
||||
|
||||
case ('P'):
|
||||
_formatters.emplace_back(new details::pid_formatter());
|
||||
break;
|
||||
|
||||
case ('i'):
|
||||
_formatters.emplace_back(new details::i_formatter());
|
||||
break;
|
||||
|
||||
case ('^'):
|
||||
_formatters.emplace_back(new details::color_start_formatter());
|
||||
break;
|
||||
|
||||
case ('$'):
|
||||
_formatters.emplace_back(new details::color_stop_formatter());
|
||||
break;
|
||||
|
||||
default: // Unknown flag appears as is
|
||||
_formatters.emplace_back(new details::ch_formatter('%'));
|
||||
_formatters.emplace_back(new details::ch_formatter(flag));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::tm spdlog::pattern_formatter::get_time(details::log_msg &msg)
|
||||
{
|
||||
if (_pattern_time == pattern_time_type::local)
|
||||
{
|
||||
return details::os::localtime(log_clock::to_time_t(msg.time));
|
||||
}
|
||||
return details::os::gmtime(log_clock::to_time_t(msg.time));
|
||||
}
|
||||
|
||||
inline void spdlog::pattern_formatter::format(details::log_msg &msg)
|
||||
{
|
||||
|
||||
#ifndef SPDLOG_NO_DATETIME
|
||||
auto tm_time = get_time(msg);
|
||||
#else
|
||||
std::tm tm_time;
|
||||
#endif
|
||||
for (auto &f : _formatters)
|
||||
{
|
||||
f->format(msg, tm_time);
|
||||
}
|
||||
// write eol
|
||||
msg.formatted << _eol;
|
||||
}
|
263
include/spdlog/details/registry.h
Normal file
263
include/spdlog/details/registry.h
Normal file
@@ -0,0 +1,263 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
// Loggers registy of unique name->logger pointer
|
||||
// An attempt to create a logger with an already existing name will be ignored
|
||||
// If user requests a non existing logger, nullptr will be returned
|
||||
// This class is thread safe
|
||||
|
||||
#include "../async_logger.h"
|
||||
#include "../common.h"
|
||||
#include "../details/null_mutex.h"
|
||||
#include "../logger.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
template<class Mutex>
|
||||
class registry_t
|
||||
{
|
||||
public:
|
||||
registry_t<Mutex>(const registry_t<Mutex> &) = delete;
|
||||
registry_t<Mutex> &operator=(const registry_t<Mutex> &) = delete;
|
||||
|
||||
void register_logger(std::shared_ptr<logger> logger)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
auto logger_name = logger->name();
|
||||
throw_if_exists(logger_name);
|
||||
_loggers[logger_name] = logger;
|
||||
}
|
||||
|
||||
std::shared_ptr<logger> get(const std::string &logger_name)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
auto found = _loggers.find(logger_name);
|
||||
return found == _loggers.end() ? nullptr : found->second;
|
||||
}
|
||||
|
||||
template<class It>
|
||||
std::shared_ptr<logger> create(const std::string &logger_name, const It &sinks_begin, const It &sinks_end)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
throw_if_exists(logger_name);
|
||||
std::shared_ptr<logger> new_logger;
|
||||
if (_async_mode)
|
||||
{
|
||||
new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy,
|
||||
_worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb);
|
||||
}
|
||||
else
|
||||
{
|
||||
new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end);
|
||||
}
|
||||
|
||||
if (_formatter)
|
||||
{
|
||||
new_logger->set_formatter(_formatter);
|
||||
}
|
||||
|
||||
if (_err_handler)
|
||||
{
|
||||
new_logger->set_error_handler(_err_handler);
|
||||
}
|
||||
|
||||
new_logger->set_level(_level);
|
||||
new_logger->flush_on(_flush_level);
|
||||
|
||||
// Add to registry
|
||||
_loggers[logger_name] = new_logger;
|
||||
return new_logger;
|
||||
}
|
||||
|
||||
template<class It>
|
||||
std::shared_ptr<async_logger> create_async(const std::string &logger_name, 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, const It &sinks_begin,
|
||||
const It &sinks_end)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
throw_if_exists(logger_name);
|
||||
auto new_logger = std::make_shared<async_logger>(
|
||||
logger_name, sinks_begin, sinks_end, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb);
|
||||
|
||||
if (_formatter)
|
||||
{
|
||||
new_logger->set_formatter(_formatter);
|
||||
}
|
||||
|
||||
if (_err_handler)
|
||||
{
|
||||
new_logger->set_error_handler(_err_handler);
|
||||
}
|
||||
|
||||
new_logger->set_level(_level);
|
||||
new_logger->flush_on(_flush_level);
|
||||
|
||||
// Add to registry
|
||||
_loggers[logger_name] = new_logger;
|
||||
return new_logger;
|
||||
}
|
||||
|
||||
void apply_all(std::function<void(std::shared_ptr<logger>)> fun)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
for (auto &l : _loggers)
|
||||
{
|
||||
fun(l.second);
|
||||
}
|
||||
}
|
||||
|
||||
void drop(const std::string &logger_name)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
_loggers.erase(logger_name);
|
||||
}
|
||||
|
||||
void drop_all()
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
_loggers.clear();
|
||||
}
|
||||
|
||||
std::shared_ptr<logger> create(const std::string &logger_name, sinks_init_list sinks)
|
||||
{
|
||||
return create(logger_name, sinks.begin(), sinks.end());
|
||||
}
|
||||
|
||||
std::shared_ptr<logger> create(const std::string &logger_name, sink_ptr sink)
|
||||
{
|
||||
return create(logger_name, {sink});
|
||||
}
|
||||
|
||||
std::shared_ptr<async_logger> create_async(const std::string &logger_name, 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, sinks_init_list sinks)
|
||||
{
|
||||
return create_async(
|
||||
logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks.begin(), sinks.end());
|
||||
}
|
||||
|
||||
std::shared_ptr<async_logger> create_async(const std::string &logger_name, 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, sink_ptr sink)
|
||||
{
|
||||
return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, {sink});
|
||||
}
|
||||
|
||||
void formatter(formatter_ptr f)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
_formatter = f;
|
||||
for (auto &l : _loggers)
|
||||
{
|
||||
l.second->set_formatter(_formatter);
|
||||
}
|
||||
}
|
||||
|
||||
void set_pattern(const std::string &pattern)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
_formatter = std::make_shared<pattern_formatter>(pattern);
|
||||
for (auto &l : _loggers)
|
||||
{
|
||||
l.second->set_formatter(_formatter);
|
||||
}
|
||||
}
|
||||
|
||||
void set_level(level::level_enum log_level)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
for (auto &l : _loggers)
|
||||
{
|
||||
l.second->set_level(log_level);
|
||||
}
|
||||
_level = log_level;
|
||||
}
|
||||
|
||||
void flush_on(level::level_enum log_level)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
for (auto &l : _loggers)
|
||||
{
|
||||
l.second->flush_on(log_level);
|
||||
}
|
||||
_flush_level = log_level;
|
||||
}
|
||||
|
||||
void set_error_handler(log_err_handler handler)
|
||||
{
|
||||
for (auto &l : _loggers)
|
||||
{
|
||||
l.second->set_error_handler(handler);
|
||||
}
|
||||
_err_handler = handler;
|
||||
}
|
||||
|
||||
void set_async_mode(size_t q_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)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
_async_mode = true;
|
||||
_async_q_size = q_size;
|
||||
_overflow_policy = overflow_policy;
|
||||
_worker_warmup_cb = worker_warmup_cb;
|
||||
_flush_interval_ms = flush_interval_ms;
|
||||
_worker_teardown_cb = worker_teardown_cb;
|
||||
}
|
||||
|
||||
void set_sync_mode()
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
_async_mode = false;
|
||||
}
|
||||
|
||||
static registry_t<Mutex> &instance()
|
||||
{
|
||||
static registry_t<Mutex> s_instance;
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
private:
|
||||
registry_t<Mutex>() = default;
|
||||
|
||||
void throw_if_exists(const std::string &logger_name)
|
||||
{
|
||||
if (_loggers.find(logger_name) != _loggers.end())
|
||||
{
|
||||
throw spdlog_ex("logger with name '" + logger_name + "' already exists");
|
||||
}
|
||||
}
|
||||
|
||||
Mutex _mutex;
|
||||
std::unordered_map<std::string, std::shared_ptr<logger>> _loggers;
|
||||
formatter_ptr _formatter;
|
||||
level::level_enum _level = level::info;
|
||||
level::level_enum _flush_level = level::off;
|
||||
log_err_handler _err_handler;
|
||||
bool _async_mode = false;
|
||||
size_t _async_q_size = 0;
|
||||
async_overflow_policy _overflow_policy = async_overflow_policy::block_retry;
|
||||
std::function<void()> _worker_warmup_cb;
|
||||
std::chrono::milliseconds _flush_interval_ms{std::chrono::milliseconds::zero()};
|
||||
std::function<void()> _worker_teardown_cb;
|
||||
};
|
||||
|
||||
#ifdef SPDLOG_NO_REGISTRY_MUTEX
|
||||
using registry = registry_t<spdlog::details::null_mutex>;
|
||||
#else
|
||||
using registry = registry_t<std::mutex>;
|
||||
#endif
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
278
include/spdlog/details/spdlog_impl.h
Normal file
278
include/spdlog/details/spdlog_impl.h
Normal file
@@ -0,0 +1,278 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// Global registry functions
|
||||
//
|
||||
#include "../details/registry.h"
|
||||
#include "../sinks/file_sinks.h"
|
||||
#include "../sinks/stdout_sinks.h"
|
||||
#include "../spdlog.h"
|
||||
#ifdef SPDLOG_ENABLE_SYSLOG
|
||||
#include "../sinks/syslog_sink.h"
|
||||
#endif
|
||||
|
||||
#if defined _WIN32 && !defined(__cplusplus_winrt)
|
||||
#include "../sinks/wincolor_sink.h"
|
||||
#else
|
||||
#include "../sinks/ansicolor_sink.h"
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "../sinks/android_sink.h"
|
||||
#endif
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
inline void spdlog::register_logger(std::shared_ptr<logger> logger)
|
||||
{
|
||||
return details::registry::instance().register_logger(std::move(logger));
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::get(const std::string &name)
|
||||
{
|
||||
return details::registry::instance().get(name);
|
||||
}
|
||||
|
||||
inline void spdlog::drop(const std::string &name)
|
||||
{
|
||||
details::registry::instance().drop(name);
|
||||
}
|
||||
|
||||
// Create multi/single threaded simple file logger
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate)
|
||||
{
|
||||
return create<spdlog::sinks::simple_file_sink_mt>(logger_name, filename, truncate);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate)
|
||||
{
|
||||
return create<spdlog::sinks::simple_file_sink_st>(logger_name, filename, truncate);
|
||||
}
|
||||
|
||||
// Create multi/single threaded rotating file logger
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(
|
||||
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
|
||||
{
|
||||
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(
|
||||
const std::string &logger_name, const filename_t &filename, size_t max_file_size, size_t max_files)
|
||||
{
|
||||
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
|
||||
}
|
||||
|
||||
// Create file logger which creates new file at midnight):
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(
|
||||
const std::string &logger_name, const filename_t &filename, int hour, int minute)
|
||||
{
|
||||
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, hour, minute);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(
|
||||
const std::string &logger_name, const filename_t &filename, int hour, int minute)
|
||||
{
|
||||
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, hour, minute);
|
||||
}
|
||||
|
||||
//
|
||||
// stdout/stderr loggers
|
||||
//
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string &logger_name)
|
||||
{
|
||||
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance());
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string &logger_name)
|
||||
{
|
||||
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance());
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string &logger_name)
|
||||
{
|
||||
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance());
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string &logger_name)
|
||||
{
|
||||
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance());
|
||||
}
|
||||
|
||||
//
|
||||
// stdout/stderr color loggers
|
||||
//
|
||||
#if defined _WIN32 && !defined(__cplusplus_winrt)
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string &logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string &logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_st>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string &logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_mt>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string &logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_st>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
#else // ansi terminal colors
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string &logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string &logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_st>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string &logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_mt>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string &logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_st>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SPDLOG_ENABLE_SYSLOG
|
||||
// Create syslog logger
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(
|
||||
const std::string &logger_name, const std::string &syslog_ident, int syslog_option, int syslog_facility)
|
||||
{
|
||||
return create<spdlog::sinks::syslog_sink>(logger_name, syslog_ident, syslog_option, syslog_facility);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::android_logger(const std::string &logger_name, const std::string &tag)
|
||||
{
|
||||
return create<spdlog::sinks::android_sink>(logger_name, tag);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Create and register a logger a single sink
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string &logger_name, const spdlog::sink_ptr &sink)
|
||||
{
|
||||
return details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
// Create logger with multiple sinks
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string &logger_name, spdlog::sinks_init_list sinks)
|
||||
{
|
||||
return details::registry::instance().create(logger_name, sinks);
|
||||
}
|
||||
|
||||
template<typename Sink, typename... Args>
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string &logger_name, Args... args)
|
||||
{
|
||||
sink_ptr sink = std::make_shared<Sink>(args...);
|
||||
return details::registry::instance().create(logger_name, {sink});
|
||||
}
|
||||
|
||||
template<class It>
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string &logger_name, const It &sinks_begin, const It &sinks_end)
|
||||
{
|
||||
return details::registry::instance().create(logger_name, sinks_begin, sinks_end);
|
||||
}
|
||||
|
||||
// Create and register an async logger with a single sink
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string &logger_name, const sink_ptr &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)
|
||||
{
|
||||
return details::registry::instance().create_async(
|
||||
logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sink);
|
||||
}
|
||||
|
||||
// Create and register an async logger with multiple sinks
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string &logger_name, sinks_init_list sinks, 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)
|
||||
{
|
||||
return details::registry::instance().create_async(
|
||||
logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks);
|
||||
}
|
||||
|
||||
template<class It>
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string &logger_name, const It &sinks_begin, const It &sinks_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)
|
||||
{
|
||||
return details::registry::instance().create_async(
|
||||
logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks_begin, sinks_end);
|
||||
}
|
||||
|
||||
inline void spdlog::set_formatter(spdlog::formatter_ptr f)
|
||||
{
|
||||
details::registry::instance().formatter(std::move(f));
|
||||
}
|
||||
|
||||
inline void spdlog::set_pattern(const std::string &format_string)
|
||||
{
|
||||
return details::registry::instance().set_pattern(format_string);
|
||||
}
|
||||
|
||||
inline void spdlog::set_level(level::level_enum log_level)
|
||||
{
|
||||
return details::registry::instance().set_level(log_level);
|
||||
}
|
||||
|
||||
inline void spdlog::flush_on(level::level_enum log_level)
|
||||
{
|
||||
return details::registry::instance().flush_on(log_level);
|
||||
}
|
||||
|
||||
inline void spdlog::set_error_handler(log_err_handler handler)
|
||||
{
|
||||
return details::registry::instance().set_error_handler(std::move(handler));
|
||||
}
|
||||
|
||||
inline void spdlog::set_async_mode(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)
|
||||
{
|
||||
details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb);
|
||||
}
|
||||
|
||||
inline void spdlog::set_sync_mode()
|
||||
{
|
||||
details::registry::instance().set_sync_mode();
|
||||
}
|
||||
|
||||
inline void spdlog::apply_all(std::function<void(std::shared_ptr<logger>)> fun)
|
||||
{
|
||||
details::registry::instance().apply_all(std::move(fun));
|
||||
}
|
||||
|
||||
inline void spdlog::drop_all()
|
||||
{
|
||||
details::registry::instance().drop_all();
|
||||
}
|
@@ -1,125 +0,0 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
#error include tcp_client_windows.h instead
|
||||
#endif
|
||||
|
||||
// tcp client helper
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../common.h"
|
||||
#include "./os.h"
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
class tcp_client_unix {
|
||||
int socket_ = -1;
|
||||
|
||||
public:
|
||||
bool is_connected() const { return socket_ != -1; }
|
||||
|
||||
void close() {
|
||||
if (is_connected()) {
|
||||
::close(socket_);
|
||||
socket_ = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int fd() const { return socket_; }
|
||||
|
||||
~tcp_client_unix() { close(); }
|
||||
|
||||
// try to connect or throw on failure
|
||||
void connect(const std::string &host, int port) {
|
||||
close();
|
||||
struct addrinfo hints {};
|
||||
memset(&hints, 0, sizeof(struct addrinfo));
|
||||
hints.ai_family = AF_UNSPEC; // To work with IPv4, IPv6, and so on
|
||||
hints.ai_socktype = SOCK_STREAM; // TCP
|
||||
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
|
||||
hints.ai_protocol = 0;
|
||||
|
||||
auto port_str = std::to_string(port);
|
||||
struct addrinfo *addrinfo_result;
|
||||
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
|
||||
if (rv != 0) {
|
||||
throw_spdlog_ex(fmt_lib::format("::getaddrinfo failed: {}", gai_strerror(rv)));
|
||||
}
|
||||
|
||||
// Try each address until we successfully connect(2).
|
||||
int last_errno = 0;
|
||||
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next) {
|
||||
#if defined(SOCK_CLOEXEC)
|
||||
constexpr int flags = SOCK_CLOEXEC;
|
||||
#else
|
||||
constexpr int flags = 0;
|
||||
#endif
|
||||
socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol);
|
||||
if (socket_ == -1) {
|
||||
last_errno = errno;
|
||||
continue;
|
||||
}
|
||||
rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen);
|
||||
if (rv == 0) {
|
||||
break;
|
||||
}
|
||||
last_errno = errno;
|
||||
::close(socket_);
|
||||
socket_ = -1;
|
||||
}
|
||||
::freeaddrinfo(addrinfo_result);
|
||||
if (socket_ == -1) {
|
||||
throw_spdlog_ex("::connect failed", last_errno);
|
||||
}
|
||||
|
||||
// set TCP_NODELAY
|
||||
int enable_flag = 1;
|
||||
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
|
||||
|
||||
// prevent sigpipe on systems where MSG_NOSIGNAL is not available
|
||||
#if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
|
||||
::setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
|
||||
#endif
|
||||
|
||||
#if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL)
|
||||
#error "tcp_sink would raise SIGPIPE since neither SO_NOSIGPIPE nor MSG_NOSIGNAL are available"
|
||||
#endif
|
||||
}
|
||||
|
||||
// Send exactly n_bytes of the given data.
|
||||
// On error close the connection and throw.
|
||||
void send(const char *data, size_t n_bytes) {
|
||||
size_t bytes_sent = 0;
|
||||
while (bytes_sent < n_bytes) {
|
||||
#if defined(MSG_NOSIGNAL)
|
||||
const int send_flags = MSG_NOSIGNAL;
|
||||
#else
|
||||
const int send_flags = 0;
|
||||
#endif
|
||||
auto write_result = ::send(socket_, data + bytes_sent, n_bytes - bytes_sent, send_flags);
|
||||
if (write_result < 0) {
|
||||
close();
|
||||
throw_spdlog_ex("write(2) failed", errno);
|
||||
}
|
||||
|
||||
if (write_result == 0) // (probably should not happen but in any case..)
|
||||
{
|
||||
break;
|
||||
}
|
||||
bytes_sent += static_cast<size_t>(write_result);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
@@ -1,133 +0,0 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
// tcp client helper
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <windows.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../common.h"
|
||||
#include "./os.h"
|
||||
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
#pragma comment(lib, "Mswsock.lib")
|
||||
#pragma comment(lib, "AdvApi32.lib")
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
class tcp_client_unix {
|
||||
SOCKET socket_ = INVALID_SOCKET;
|
||||
|
||||
static void init_winsock_() {
|
||||
WSADATA wsaData;
|
||||
auto rv = WSAStartup(MAKEWORD(2, 2), &wsaData);
|
||||
if (rv != 0) {
|
||||
throw_winsock_error_("WSAStartup failed", ::WSAGetLastError());
|
||||
}
|
||||
}
|
||||
|
||||
static void throw_winsock_error_(const std::string &msg, int last_error) {
|
||||
char buf[512];
|
||||
::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, last_error,
|
||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, (sizeof(buf) / sizeof(char)), NULL);
|
||||
|
||||
throw_spdlog_ex(fmt_lib::format("tcp_sink - {}: {}", msg, buf));
|
||||
}
|
||||
|
||||
public:
|
||||
tcp_client_unix() { init_winsock_(); }
|
||||
|
||||
~tcp_client_unix() {
|
||||
close();
|
||||
::WSACleanup();
|
||||
}
|
||||
|
||||
bool is_connected() const { return socket_ != INVALID_SOCKET; }
|
||||
|
||||
void close() {
|
||||
::closesocket(socket_);
|
||||
socket_ = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
SOCKET fd() const { return socket_; }
|
||||
|
||||
// try to connect or throw on failure
|
||||
void connect(const std::string &host, int port) {
|
||||
if (is_connected()) {
|
||||
close();
|
||||
}
|
||||
struct addrinfo hints {};
|
||||
ZeroMemory(&hints, sizeof(hints));
|
||||
|
||||
hints.ai_family = AF_UNSPEC; // To work with IPv4, IPv6, and so on
|
||||
hints.ai_socktype = SOCK_STREAM; // TCP
|
||||
hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value
|
||||
hints.ai_protocol = 0;
|
||||
|
||||
auto port_str = std::to_string(port);
|
||||
struct addrinfo *addrinfo_result;
|
||||
auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result);
|
||||
int last_error = 0;
|
||||
if (rv != 0) {
|
||||
last_error = ::WSAGetLastError();
|
||||
WSACleanup();
|
||||
throw_winsock_error_("getaddrinfo failed", last_error);
|
||||
}
|
||||
|
||||
// Try each address until we successfully connect(2).
|
||||
|
||||
for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next) {
|
||||
socket_ = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
|
||||
if (socket_ == INVALID_SOCKET) {
|
||||
last_error = ::WSAGetLastError();
|
||||
WSACleanup();
|
||||
continue;
|
||||
}
|
||||
if (::connect(socket_, rp->ai_addr, (int)rp->ai_addrlen) == 0) {
|
||||
break;
|
||||
} else {
|
||||
last_error = ::WSAGetLastError();
|
||||
close();
|
||||
}
|
||||
}
|
||||
::freeaddrinfo(addrinfo_result);
|
||||
if (socket_ == INVALID_SOCKET) {
|
||||
WSACleanup();
|
||||
throw_winsock_error_("connect failed", last_error);
|
||||
}
|
||||
|
||||
// set TCP_NODELAY
|
||||
int enable_flag = 1;
|
||||
::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&enable_flag), sizeof(enable_flag));
|
||||
}
|
||||
|
||||
// Send exactly n_bytes of the given data.
|
||||
// On error close the connection and throw.
|
||||
void send(const char *data, size_t n_bytes) {
|
||||
size_t bytes_sent = 0;
|
||||
while (bytes_sent < n_bytes) {
|
||||
const int send_flags = 0;
|
||||
auto write_result = ::send(socket_, data + bytes_sent, (int)(n_bytes - bytes_sent), send_flags);
|
||||
if (write_result == SOCKET_ERROR) {
|
||||
int last_error = ::WSAGetLastError();
|
||||
close();
|
||||
throw_winsock_error_("send failed", last_error);
|
||||
}
|
||||
|
||||
if (write_result == 0) // (probably should not happen but in any case..)
|
||||
{
|
||||
break;
|
||||
}
|
||||
bytes_sent += static_cast<size_t>(write_result);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // namespace details
|
||||
} // 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