Compare commits

..

45 Commits

Author SHA1 Message Date
gabime
560df2878a updated version number to 0.17.0 2018-05-21 20:38:13 +03:00
gabime
dc328de72a added test_async to cmake and vcxproj 2018-05-21 16:40:31 +03:00
gabime
6704375be6 updated example visula studio solution 2018-05-21 15:11:28 +03:00
gabime
7a85acc31f removed logger_name field from async_msg 2018-05-20 19:35:01 +03:00
gabime
7cbf832cca comments 2018-05-20 18:30:42 +03:00
gabime
58b1c2f78d Merge branch 'remove-lockfree' of https://github.com/gabime/spdlog into remove-lockfree 2018-05-20 18:19:36 +03:00
gabime
b94b9a8341 format 2018-05-20 18:17:34 +03:00
gabime
6a93f98a1f async test 2018-05-20 18:17:34 +03:00
gabime
b70020d7c3 upadte stats 2018-05-20 18:17:34 +03:00
gabime
ade22934e3 updated bench to show elapsed too 2018-05-20 18:17:34 +03:00
gabime
ee2fd265fd added async tests 2018-05-20 18:17:34 +03:00
gabime
5b84f30b3a fixed flush interval in async helper 2018-05-20 18:15:33 +03:00
gabime
5c38e15dd2 replaced the lockfree queue with bounded, locked queue 2018-05-20 18:10:29 +03:00
gabime
0e86c4eed0 removed debug printf 2018-05-20 18:07:38 +03:00
gabime
e69aafc737 fixed flush interval in async helper 2018-05-20 18:05:37 +03:00
gabime
3dbba6895d replaced the lockfree queue with bounded, locked queue 2018-05-20 18:05:37 +03:00
gabime
8bd16feb36 format 2018-05-20 18:01:47 +03:00
gabime
8e429ae5d6 async test 2018-05-20 18:01:27 +03:00
gabime
e2bf00ada4 upadte stats 2018-05-20 17:50:12 +03:00
gabime
c3dd37c8c1 updated bench to show elapsed too 2018-05-20 17:41:22 +03:00
gabime
1838ad9b3d added async tests 2018-05-20 13:22:44 +03:00
gabime
7cc884fe34 async_log_helper minor fixes 2018-05-20 13:22:34 +03:00
gabime
61ab1af906 merge 2018-05-19 16:14:54 +03:00
gabime
911a2986ce fixed flush interval in async helper 2018-05-19 16:13:35 +03:00
gabime
8321449c44 replaced the lockfree queue with bounded, locked queue 2018-05-19 16:12:25 +03:00
gabime
314afa7dbb removed debug printf 2018-05-19 16:12:22 +03:00
gabime
f57fc1b2fa fixed flush interval in async helper 2018-05-19 16:06:57 +03:00
gabime
a1a71804f4 replaced the lockfree queue with bounded, locked queue 2018-05-19 14:41:53 +03:00
Gabi Melman
b1a58cd342 Dont wait for the queue to drain in async flush 2018-05-17 20:38:43 +03:00
Gabi Melman
efb1af9a73 Update async_logger_impl.h 2018-05-17 20:36:10 +03:00
Gabi Melman
71e93a4f2d Merge pull request #688 from Puasonych/master
Update step_logger
2018-04-27 21:55:44 +03:00
Puasonych
8f3c4218ed Merge remote-tracking branch 'gabime/master' 2018-04-20 16:20:17 +03:00
gabime
217ad75ebd Fixed deault error handler not to use sink but write directly to stderr 2018-04-20 13:02:21 +03:00
Gabi Melman
ce41991c51 Remove unneeded include 2018-04-20 12:59:51 +03:00
Puasonych
1136bd7ce2 Update step_logger
Fixed code duplication
2018-04-18 09:49:41 +03:00
Gabi Melman
805ab9854f Merge pull request #683 from Puasonych/master
Let's add a new type of logger 'step_logger' :)
2018-04-16 15:07:29 +03:00
Gabi Melman
d921a9649c Merge pull request #687 from jhasse/patch-1
Silence warning "value stored to rv is never read"
2018-04-16 14:38:07 +03:00
Puasonych
093edc2dc2 Update step_logger
Test release of a logger step_logger
2018-04-16 12:56:45 +03:00
Jan Niklas Hasse
1b2f6815bf Silence warning "value stored to rv is never read" 2018-04-16 10:58:50 +02:00
Puasonych
f17eb1bb56 Merge remote-tracking branch 'gabime/master' 2018-04-16 08:27:01 +03:00
Erik
be685337b1 Update file_sinks.h
Removed possible throw of an exception from the destructor
2018-04-12 10:01:02 +05:00
Erik
06580c8333 Update file_sinks.h 2018-04-11 22:15:50 +05:00
Puasonych
6f12242a55 Update .gitignore 2018-04-11 11:41:48 +03:00
Puasonych
11c99892d7 Add new logger: step_logger 2018-04-11 11:40:34 +03:00
Puasonych
31ce7ef3a5 Update .gitignore 2018-04-10 16:57:29 +03:00
219 changed files with 25547 additions and 12253 deletions

View File

@@ -1,19 +1,108 @@
--- ---
Language: Cpp Language: Cpp
BasedOnStyle: Google # BasedOnStyle: LLVM
AccessModifierOffset: -4 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 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 UseTab: Never
ColumnLimit: 130
AlignAfterOpenBracket: Align
BinPackParameters: false
AlignEscapedNewlines: Left
AlwaysBreakTemplateDeclarations: Yes
PackConstructorInitializers: Never
BreakConstructorInitializersBeforeComma: false
IndentPPDirectives: BeforeHash
SortIncludes: CaseInsensitive
... ...

View File

@@ -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
View File

@@ -1 +0,0 @@
* text=false

View File

@@ -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

View File

@@ -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

View File

@@ -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
View File

@@ -1,7 +1,4 @@
# Auto generated files # Auto generated files
include/spdlog/spdlog_config.h
[Dd]ebug/
[Rr]elease/
build/* build/*
*.slo *.slo
*.lo *.lo
@@ -37,16 +34,23 @@ build/*
# Codelite # Codelite
.codelite .codelite
# KDevelop
*.kdev4
# .orig files # .orig files
*.orig *.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 files
generated generated
version.rc
# Cmake # Cmake
CMakeCache.txt CMakeCache.txt
@@ -59,29 +63,9 @@ install_manifest.txt
/tests/tests.VC.db /tests/tests.VC.db
/tests/tests /tests/tests
/tests/logs/* /tests/logs/*
spdlogConfig.cmake
spdlogConfigVersion.cmake
compile_commands.json
# idea # idea
.idea/ .idea/
.cache/
.vscode/
cmake-build-*/
*.db
*.ipch
*.filters
*.db-wal
*.opendb
*.db-shm
*.vcxproj
*.tcl
*.user
*.sln
# macos # vscode
*.DS_store .vscode/
*.xcodeproj/
/.vs
/out/build
/CMakeSettings.json

91
.travis.yml Normal file
View 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

View File

@@ -1,374 +1,115 @@
# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT) #
# Copyright(c) 2015 Ruslan Baratov.
cmake_minimum_required(VERSION 3.14) # Distributed under the MIT License (http://opensource.org/licenses/MIT)
#
# ---------------------------------------------------------------------------------------
# Start spdlog project
# ---------------------------------------------------------------------------------------
include(cmake/utils.cmake)
include(cmake/ide.cmake)
spdlog_extract_version()
project(spdlog VERSION ${SPDLOG_VERSION} LANGUAGES CXX)
cmake_minimum_required(VERSION 3.1)
project(spdlog VERSION 0.17.0 LANGUAGES CXX)
include(CTest)
include(CMakeDependentOption)
include(GNUInstallDirs) include(GNUInstallDirs)
set_property(GLOBAL PROPERTY USE_FOLDERS ON) #---------------------------------------------------------------------------------------
# compiler config
# --------------------------------------------------------------------------------------- #---------------------------------------------------------------------------------------
# Set default build to release set(CMAKE_CXX_STANDARD 11)
# ---------------------------------------------------------------------------------------
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 ()
set(CMAKE_CXX_STANDARD_REQUIRED ON) 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) # spdlog target
endif () #---------------------------------------------------------------------------------------
add_library(spdlog INTERFACE)
# --------------------------------------------------------------------------------------- option(SPDLOG_BUILD_EXAMPLES "Build examples" OFF)
# Set SPDLOG_MASTER_PROJECT to ON if we are building spdlog cmake_dependent_option(SPDLOG_BUILD_TESTING
# --------------------------------------------------------------------------------------- "Build spdlog tests" ON
# Check if spdlog is being used directly or via add_subdirectory, but allow overriding "BUILD_TESTING" OFF
if (NOT DEFINED SPDLOG_MASTER_PROJECT) )
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(SPDLOG_MASTER_PROJECT ON)
else ()
set(SPDLOG_MASTER_PROJECT OFF)
endif ()
endif ()
option(SPDLOG_BUILD_ALL "Build all artifacts" OFF) target_include_directories(
spdlog
INTERFACE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
# build shared option set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include")
option(SPDLOG_BUILD_SHARED "Build shared library" OFF)
# example options if(SPDLOG_BUILD_EXAMPLES)
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(
spdlog
PUBLIC FILE_SET pub_headers
TYPE HEADERS
BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/include
FILES ${SPDLOG_HEADERS})
set(SPDLOG_INCLUDES_LEVEL "")
if (SPDLOG_SYSTEM_INCLUDES)
set(SPDLOG_INCLUDES_LEVEL "SYSTEM")
endif ()
target_include_directories(spdlog ${SPDLOG_INCLUDES_LEVEL} PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
target_link_libraries(spdlog PUBLIC Threads::Threads)
target_link_libraries(spdlog PUBLIC fmt::fmt)
spdlog_enable_warnings(spdlog)
set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION
${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR})
set(SPDLOG_NAME spdlog-${SPDLOG_VERSION_MAJOR})
set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX "-${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR}d")
# ---------------------------------------------------------------------------------------
# set source groups for visual studio
# ---------------------------------------------------------------------------------------
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)")
add_subdirectory(example) add_subdirectory(example)
spdlog_enable_warnings(example) endif()
endif ()
if (SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_ALL) if(SPDLOG_BUILD_TESTING)
message(STATUS "Generating tests")
enable_testing()
add_subdirectory(tests) add_subdirectory(tests)
endif () endif()
if (SPDLOG_BUILD_BENCH OR SPDLOG_BUILD_ALL) #---------------------------------------------------------------------------------------
message(STATUS "Generating benchmarks") # Install/export targets and files
add_subdirectory(bench) #---------------------------------------------------------------------------------------
endif () # set files and directories
set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
set(include_install_dir "${CMAKE_INSTALL_INCLUDEDIR}")
set(pkgconfig_install_dir "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
set(version_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
set(project_config "${PROJECT_NAME}Config.cmake")
set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc")
set(targets_export_name "${PROJECT_NAME}Targets")
set(namespace "${PROJECT_NAME}::")
# --------------------------------------------------------------------------------------- # generate package version file
# Install include(CMakePackageConfigHelpers)
# --------------------------------------------------------------------------------------- write_basic_package_version_file(
if (SPDLOG_INSTALL) "${version_config}" COMPATIBILITY SameMajorVersion
message(STATUS "Generating install") )
set(project_config_in "${CMAKE_CURRENT_LIST_DIR}/cmake/spdlogConfig.cmake.in")
set(project_config_out "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfig.cmake")
set(config_targets_file "spdlogConfigTargets.cmake")
set(version_config_file "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfigVersion.cmake")
set(export_dest_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${SPDLOG_NAME}")
# --------------------------------------------------------------------------------------- # configure pkg config file
# Include files configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY)
# ---------------------------------------------------------------------------------------
set(installed_include_dir "${CMAKE_INSTALL_INCLUDEDIR}/${SPDLOG_NAME}")
install(
TARGETS spdlog
EXPORT spdlogTargets
LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}/${SPDLOG_NAME}"
ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}/${SPDLOG_NAME}"
RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}/${SPDLOG_NAME}"
FILE_SET pub_headers
DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${SPDLOG_NAME}")
message(STATUS "Installing spdlog in ${CMAKE_INSTALL_LIBDIR}/${SPDLOG_NAME}")
# --------------------------------------------------------------------------------------- # install targets
# Install CMake config files install(
# --------------------------------------------------------------------------------------- TARGETS spdlog
install(EXPORT spdlogTargets DESTINATION ${export_dest_dir} NAMESPACE spdlog:: FILE ${config_targets_file}) EXPORT "${targets_export_name}"
)
include(CMakePackageConfigHelpers) # install headers
configure_package_config_file("${project_config_in}" "${project_config_out}" INSTALL_DESTINATION ${export_dest_dir}) install(
DIRECTORY "${HEADER_BASE}/${PROJECT_NAME}"
DESTINATION "${include_install_dir}"
)
write_basic_package_version_file("${version_config_file}" COMPATIBILITY SameMajorVersion) # install project version file
install(FILES "${project_config_out}" "${version_config_file}" DESTINATION "${export_dest_dir}") install(
FILES "${version_config}"
DESTINATION "${config_install_dir}"
)
# --------------------------------------------------------------------------------------- # install pkg config file
# Support creation of installable packages install(
# --------------------------------------------------------------------------------------- FILES "${pkg_config}"
include(cmake/spdlogCPack.cmake) DESTINATION "${pkgconfig_install_dir}"
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})

View File

@@ -1,12 +1,13 @@
CMake: spdlog is header only library.
add_executable(example example.cpp) Just copy the files to your build tree and use a C++11 compiler
target_link_libraries(example spdlog::spdlog)
Tested on: Tested on:
gcc 4.8.1 and above gcc 4.8.1 and above
clang 3.5 clang 3.5
Visual Studio 2013 Visual Studio 2013
gcc 4.8 flags: --std==c++11 -pthread -O3 -flto -Wl,--no-as-needed
gcc 4.9 flags: --std=c++11 -pthread -O3 -flto
see the makefile in the example folder

View File

@@ -20,7 +20,3 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.
-- NOTE: Third party dependency used by this software --
This software depends on the fmt lib (MIT License),
and users must comply to its license: https://raw.githubusercontent.com/fmtlib/fmt/master/LICENSE

629
README.md
View File

@@ -1,462 +1,221 @@
# spdlog # spdlog
Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=master)](https://travis-ci.org/gabime/spdlog)&nbsp; [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true)](https://ci.appveyor.com/project/gabime/spdlog)
[![ci](https://github.com/gabime/spdlog/actions/workflows/linux.yml/badge.svg)](https://github.com/gabime/spdlog/actions/workflows/linux.yml)&nbsp;
[![ci](https://github.com/gabime/spdlog/actions/workflows/windows.yml/badge.svg)](https://github.com/gabime/spdlog/actions/workflows/windows.yml)&nbsp;
[![ci](https://github.com/gabime/spdlog/actions/workflows/macos.yml/badge.svg)](https://github.com/gabime/spdlog/actions/workflows/macos.yml)&nbsp;
[![Release](https://img.shields.io/github/release/gabime/spdlog.svg)](https://github.com/gabime/spdlog/releases/latest)
Fast C++ logging library
## Install ## Install
```console #### Just copy the headers:
$ git clone https://github.com/gabime/spdlog.git
$ cd spdlog && mkdir build && cd build
$ cmake .. && cmake --build .
```
see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v2.x/example/CMakeLists.txt) on how to use. * 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`
## Platforms ## Platforms
* Linux, FreeBSD, OpenBSD, Solaris, AIX * Linux, FreeBSD, Solaris
* Windows (msvc, cygwin) * Windows (vc 2013+, cygwin)
* macOS * Mac OSX (clang 3.5+)
* Android * Android
## Package managers:
* Debian: `sudo apt install libspdlog-dev`
* Homebrew: `brew install spdlog`
* MacPorts: `sudo port install spdlog`
* FreeBSD: `pkg install spdlog`
* Fedora: `dnf install spdlog`
* Gentoo: `emerge dev-libs/spdlog`
* Arch Linux: `pacman -S spdlog`
* openSUSE: `sudo zypper in spdlog-devel`
* vcpkg: `vcpkg install spdlog`
* conan: `conan install --requires=spdlog/[*]`
* conda: `conda install -c conda-forge spdlog`
* build2: ```depends: spdlog ^1.8.2```
## Features ## Features
* Very fast (see [benchmarks](#benchmarks) below). * Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
* Headers only or compiled * Headers only, just copy and use.
* Feature-rich formatting, using the excellent [fmt](https://github.com/fmtlib/fmt) library. * Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library.
* Optional printf syntax support.
* Asynchronous mode (optional) * Asynchronous mode (optional)
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting. * [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
* Conditional Logging
* Multi/Single threaded loggers. * Multi/Single threaded loggers.
* Various log targets: * Various log targets:
* Rotating log files. * Rotating log files.
* Daily log files. * Daily log files.
* Console logging (colors supported). * Console logging (colors supported).
* syslog. * syslog.
* Windows event log. * Windows debugger (```OutputDebugString(..)```)
* Windows debugger (```OutputDebugString(..)```). * Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
* Log to Qt widgets ([example](#log-to-qt-with-nice-colors)). * Severity based filtering - threshold levels can be modified in runtime as well as in compile time.
* Easily [extendable](https://github.com/gabime/spdlog/wiki/4.-Sinks#implementing-your-own-sink) with custom log targets.
* Log filtering - log levels can be modified at runtime as well as compile time.
* Support for loading log levels from argv or environment var.
## Usage samples
#### Basic usage
```c++
#include "spdlog/spdlog.h"
int main()
{
spdlog::info("Welcome to spdlog!");
spdlog::error("Some error message with arg: {}", 1);
spdlog::warn("Easy padding in numbers like {:08d}", 12);
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
spdlog::info("Support for floats {:03.2f}", 1.23456);
spdlog::info("Positional args are {1} {0}..", "too", "supported");
spdlog::info("{:<30}", "left aligned");
spdlog::set_level(spdlog::level::debug); // Set global log level to debug
spdlog::debug("This message should be displayed..");
// change log pattern
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
// Compile time log levels
// define SPDLOG_ACTIVE_LEVEL to desired level
SPDLOG_TRACE("Some trace message with param {}", 42);
SPDLOG_DEBUG("Some debug message");
}
```
---
#### Create stdout/stderr logger object
```c++
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
void stdout_example()
{
// create a color multi-threaded logger
auto console = spdlog::stdout_color_mt("console");
auto err_logger = spdlog::stderr_color_mt("stderr");
spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
}
```
---
#### Basic file logger
```c++
#include "spdlog/sinks/basic_file_sink.h"
void basic_logfile_example()
{
try
{
auto logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
}
catch (const spdlog::spdlog_ex &ex)
{
std::cout << "Log init failed: " << ex.what() << std::endl;
}
}
```
---
#### Rotating files
```c++
#include "spdlog/sinks/rotating_file_sink.h"
void rotating_example()
{
// Create a file rotating logger with 5 MB size max and 3 rotated files
auto max_size = 1048576 * 5;
auto max_files = 3;
auto logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", max_size, max_files);
}
```
---
#### Daily files
```c++
#include "spdlog/sinks/daily_file_sink.h"
void daily_example()
{
// Create a daily logger - a new file is created every day at 2:30 am
auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
}
```
---
#### Periodic flush
```c++
// periodically flush all *registered* loggers every 3 seconds:
// warning: only use if all your loggers are thread-safe ("_mt" loggers)
spdlog::flush_every(std::chrono::seconds(3));
```
---
#### Stopwatch
```c++
// Stopwatch support for spdlog
#include "spdlog/stopwatch.h"
void stopwatch_example()
{
spdlog::stopwatch sw;
spdlog::debug("Elapsed {}", sw);
spdlog::debug("Elapsed {:.3}", sw);
}
```
---
#### Log binary data in hex
```c++
// many types of std::container<char> types can be used.
// ranges are supported too.
// format flags:
// {:X} - print in uppercase.
// {:s} - don't separate each byte with space.
// {:p} - don't print the position on each line start.
// {:n} - don't split the output into lines.
// {:a} - show ASCII if :n is not set.
#include "spdlog/fmt/bin_to_hex.h"
void binary_example()
{
auto console = spdlog::get("console");
std::array<char, 80> buf;
console->info("Binary example: {}", spdlog::to_hex(buf));
console->info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10));
// more examples:
// logger->info("uppercase: {:X}", spdlog::to_hex(buf));
// logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf));
// logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf));
}
```
---
#### Logger with multi sinks - each with a different format and log level
```c++
// create a logger with 2 targets, with different log levels and formats.
// The console will show only warnings or errors, while the file will log all.
void multi_sink_example()
{
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
console_sink->set_level(spdlog::level::warn);
console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v");
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/multisink.txt", true);
file_sink->set_level(spdlog::level::trace);
spdlog::logger logger("multi_sink", {console_sink, file_sink});
logger.set_level(spdlog::level::debug);
logger.warn("this should appear in both console and file");
logger.info("this message should not appear in the console, only in the file");
}
```
---
#### User-defined callbacks about log events
```c++
// create a logger with a lambda function callback, the callback will be called
// each time something is logged to the logger
void callback_example()
{
auto callback_sink = std::make_shared<spdlog::sinks::callback_sink_mt>([](const spdlog::details::log_msg &msg) {
// for example you can be notified by sending an email to yourself
});
callback_sink->set_level(spdlog::level::err);
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
spdlog::logger logger("custom_callback_logger", {console_sink, callback_sink});
logger.info("some info log");
logger.error("critical issue"); // will notify you
}
```
---
#### Asynchronous logging
```c++
#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"
void async_example()
{
// TODO
}
```
---
#### User-defined types
```c++
template<>
struct fmt::formatter<my_type> : fmt::formatter<std::string>
{
auto format(my_type my, format_context &ctx) const -> decltype(ctx.out())
{
return format_to(ctx.out(), "[my_type i={}]", my.i);
}
};
void user_defined_example()
{
spdlog::info("user defined type: {}", my_type(14));
}
```
---
#### User-defined flags in the log pattern
```c++
// Log patterns can contain custom flags.
// the following example will add new flag '%*' - which will be bound to a <my_formatter_flag> instance.
#include "spdlog/pattern_formatter.h"
class my_formatter_flag : public spdlog::custom_flag_formatter
{
public:
void format(const spdlog::details::log_msg &, const std::tm &, spdlog::memory_buf_t &dest) override
{
std::string some_txt = "custom-flag";
dest.append(some_txt.data(), some_txt.data() + some_txt.size());
}
std::unique_ptr<custom_flag_formatter> clone() const override
{
return std::make_unique<my_formatter_flag>();
}
};
void custom_flags_example()
{
auto formatter = std::make_unique<spdlog::pattern_formatter>();
formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v");
spdlog::set_formatter(std::move(formatter));
}
```
---
#### Custom error handler
```c++
void err_handler_example()
{
// can be set globally or per logger(logger->set_error_handler(..))
spdlog::set_error_handler([](const std::string &msg) { spdlog::get("console")->error("*** LOGGER ERROR ***: {}", msg); });
spdlog::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
}
```
---
#### syslog
```c++
#include "spdlog/sinks/syslog_sink.h"
void syslog_example()
{
std::string ident = "spdlog-example";
auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID);
syslog_logger->warn("This is warning that will end up in syslog.");
}
```
---
#### Android example
```c++
#include "spdlog/sinks/android_sink.h"
void android_example()
{
std::string tag = "spdlog-android";
auto android_logger = spdlog::android_logger_mt("android", tag);
android_logger->critical("Use \"adb shell logcat\" to view this message.");
}
```
---
#### Load log levels from the env variable or argv
```c++
#include "spdlog/cfg/env.h"
int main (int argc, char *argv[])
{
spdlog::cfg::load_env_levels();
// or from the command line:
// ./example SPDLOG_LEVEL=info,mylogger=trace
// #include "spdlog/cfg/argv.h" // for loading levels from argv
// spdlog::cfg::load_argv_levels(argc, argv);
}
```
So then you can:
```console
$ export SPDLOG_LEVEL=info,mylogger=trace
$ ./example
```
---
#### Log file open/close event handlers
```c++
// You can get callbacks from spdlog before/after a log file has been opened or closed.
// This is useful for cleanup procedures or for adding something to the start/end of the log file.
void file_events_example()
{
// pass the spdlog::file_event_handlers to file sinks for open/close log file notifications
spdlog::file_event_handlers handlers;
handlers.before_open = [](spdlog::filename_t filename) { spdlog::info("Before opening {}", filename); };
handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("After opening\n", fstream); };
handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("Before closing\n", fstream); };
handlers.after_close = [](spdlog::filename_t filename) { spdlog::info("After closing {}", filename); };
auto my_logger = spdlog::basic_logger_st("some_logger", "logs/events-sample.txt", true, handlers);
}
```
---
#### Replace the Default Logger
```c++
void replace_global_logger_example()
{
auto new_logger = spdlog::basic_logger_mt("new_global_logger", "logs/new-default-log.txt", true);
spdlog::set_global_logger(new_logger);
spdlog::info("new logger log message");
}
```
---
#### Log to Qt with nice colors
```c++
#include "spdlog/spdlog.h"
#include "spdlog/sinks/qt_sinks.h"
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
setMinimumSize(640, 480);
auto log_widget = new QTextEdit(this);
setCentralWidget(log_widget);
int max_lines = 500; // keep the text widget to max 500 lines. remove old lines if needed.
auto logger = spdlog::qt_color_logger_mt("qt_logger", log_widget, max_lines);
logger->info("Some info message");
}
```
---
## Benchmarks ## 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 Below are some [benchmarks](bench) comparing popular log libraries under Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
#### Synchronous mode #### Synchronous mode
``` Time needed to log 1,000,000 lines in synchronous mode (in seconds, the best of 3 runs):
[info] **************************************************************
[info] Single thread, 1,000,000 iterations |threads|boost log 1.54|glog |easylogging |spdlog|
[info] ************************************************************** |-------|:-------:|:-----:|----------:|------:|
[info] basic_st Elapsed: 0.17 secs 5,777,626/sec |1| 4.169s |1.066s |0.975s |0.392s|
[info] rotating_st Elapsed: 0.18 secs 5,475,894/sec |10| 6.180s |3.032s |2.857s |0.773s|
[info] daily_st Elapsed: 0.20 secs 5,062,659/sec |100| 5.981s |1.139s |4.512s |0.587s|
[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 #### 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):
[info] -------------------------------------------------
[info] Messages : 1,000,000 |threads|g2log <sup>async logger</sup> |spdlog <sup>async mode</sup>|
[info] Threads : 10 |:-------|:-----:|-------------------------:|
[info] Queue : 8,192 slots |1| 1.850s |0.39s |
[info] Queue memory : 8,192 x 272 = 2,176 KB |10| 0.943s |0.416s|
[info] ------------------------------------------------- |100| 0.959s |0.413s|
[info]
[info] *********************************
[info] Queue Overflow Policy: block
[info] *********************************
[info] Elapsed: 1.70784 secs 585,535/sec ## Usage Example
[info] Elapsed: 1.69805 secs 588,910/sec ```c++
[info] Elapsed: 1.7026 secs 587,337/sec
[info] #include "spdlog/spdlog.h"
[info] *********************************
[info] Queue Overflow Policy: overrun #include <iostream>
[info] ********************************* #include <memory>
[info] Elapsed: 0.372816 secs 2,682,285/sec
[info] Elapsed: 0.379758 secs 2,633,255/sec void async_example();
[info] Elapsed: 0.373532 secs 2,677,147/sec void syslog_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");
// 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();
}
// 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;
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);
}
//syslog example
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
}
// 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()
{
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)
}
``` ```
## Documentation ## Documentation
Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages. Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages.
---
Thanks to [JetBrains](https://www.jetbrains.com/?from=spdlog) for donating product licenses to help develop **spdlog** <a href="https://www.jetbrains.com/?from=spdlog"><img src="logos/jetbrains-variant-4.svg" width="94" align="center" /></a>

32
appveyor.yml Normal file
View 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

View File

@@ -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
View 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
View 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
View 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

View File

@@ -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));
}

View File

@@ -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
View 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
View 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
View 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
View 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
View 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

View 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;
}

View 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;
}

View 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;
}

View File

@@ -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
View 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
View 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
View 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
View 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;
}

View File

@@ -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
View 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
View 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

View 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;
}

View 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;
}

View 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
View 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

View 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
View 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;
}

View 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
View 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
View File

@@ -0,0 +1,4 @@
# Ignore everything in this directory
*
# Except this file
!.gitignore

19
bench/mem Executable file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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;
}

View File

@@ -11,8 +11,9 @@
namespace utils { namespace utils {
template <typename T> template<typename T>
inline std::string format(const T &value) { inline std::string format(const T &value)
{
static std::locale loc(""); static std::locale loc("");
std::stringstream ss; std::stringstream ss;
ss.imbue(loc); ss.imbue(loc);
@@ -20,8 +21,9 @@ inline std::string format(const T &value) {
return ss.str(); return ss.str();
} }
template <> template<>
inline std::string format(const double &value) { inline std::string format(const double &value)
{
static std::locale loc(""); static std::locale loc("");
std::stringstream ss; std::stringstream ss;
ss.imbue(loc); ss.imbue(loc);
@@ -29,4 +31,4 @@ inline std::string format(const double &value) {
return ss.str(); return ss.str();
} }
} // namespace utils } // namespace utils

24
cmake/Config.cmake.in Normal file
View 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")

View File

@@ -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()

View File

@@ -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
View File

@@ -0,0 +1,6 @@
prefix=@CMAKE_INSTALL_PREFIX@
includedir=${prefix}/include
Name: @PROJECT_NAME@
Description: Super fast C++ logging library.
Version: @PROJECT_VERSION@

View File

@@ -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)

View File

@@ -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)

View File

@@ -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()

View File

@@ -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

View File

@@ -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) cmake_minimum_required(VERSION 3.1)
project(spdlog_example CXX) project(SpdlogExamples CXX)
if(NOT TARGET spdlog) if(TARGET spdlog)
# Stand-alone build # Part of the main project
find_package(spdlog 2 REQUIRED) add_library(spdlog::spdlog ALIAS spdlog)
else()
# Stand-alone build
find_package(spdlog CONFIG REQUIRED)
endif() endif()
find_package(Threads REQUIRED)
add_executable(example example.cpp) 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
View 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
View 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
View 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
View 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;
}

View File

@@ -1,298 +1,162 @@
// //
// Copyright(c) 2015 Gabi Melman. // Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
//
// spdlog usage example // spdlog usage example
//
//
#include <chrono> #define SPDLOG_TRACE_ON
#include <cstdio> #define SPDLOG_DEBUG_ON
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();
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include "spdlog/version.h"
using namespace spdlog::sinks; #include <iostream>
#include <memory>
int main(int, char *[]) { void async_example();
spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH); void syslog_example();
spdlog::warn("Easy padding in numbers like {:08d}", 12); void android_example();
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); void user_defined_example();
spdlog::info("Support for floats {:03.2f}", 1.23456); void err_handler_example();
spdlog::info("Positional args are {1} {0}..", "too", "supported");
spdlog::info("{:>8} aligned, {:<8} aligned", "right", "left");
// Runtime log levels namespace spd = spdlog;
spdlog::set_level(spdlog::level::info); // Set global log level to info int main(int, char *[])
spdlog::debug("This message should not be displayed!"); {
spdlog::set_level(spdlog::level::trace); // Set specific logger's log level try
spdlog::debug("This message should be displayed.."); {
// 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 // Formatting examples
spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [thread %t] %v"); console->warn("Easy padding in numbers like {:08d}", 12);
spdlog::info("This an info message with custom format"); console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
spdlog::set_pattern("%+"); // back to default format console->info("Support for floats {:03.2f}", 1.23456);
spdlog::set_level(spdlog::level::info); console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");
try { spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
stdout_logger_example();
basic_example(); // Create basic file logger (not rotated)
rotating_example(); auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt");
daily_example(); my_logger->info("Some log message");
callback_example();
// Create a file rotating logger with 5mb size max and 3 rotated files
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
for (int i = 0; i < 10; ++i)
{
rotating_logger->info("{} * {} equals {:>10}", i, i, i * i);
}
// Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
// trigger flush if the log severity is error or higher
daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44);
// Customize msg format for all messages
spd::set_pattern("[%^+++%$] [%H:%M:%S %z] [thread %t] %v");
console->info("This an info message with custom format");
console->error("This an error message with custom format");
// Runtime log levels
spd::set_level(spd::level::info); // Set global log level to info
console->debug("This message should not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->debug("This message should be displayed..");
// Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
// Asynchronous logging is very fast..
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
async_example(); async_example();
binary_example();
vector_example(); // syslog example. linux/osx only
multi_sink_example(); syslog_example();
// android example. compile with NDK
android_example();
// Log user-defined types example
user_defined_example(); user_defined_example();
// Change default log error handler
err_handler_example(); err_handler_example();
trace_example();
stopwatch_example();
udp_example();
custom_flags_example();
file_events_example();
replace_global_logger_example();
// Release all spdlog resources // Apply a function on all registered loggers
// This is optional (only mandatory if using windows + async log). spd::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); });
spdlog::shutdown();
// Release and close all loggers
spdlog::drop_all();
} }
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
// Exceptions will only be thrown upon failed logger or sink construction (not during logging). catch (const spd::spdlog_ex &ex)
catch (const spdlog::spdlog_ex &ex) { {
std::printf("Log initialization failed: %s\n", ex.what()); std::cout << "Log init failed: " << ex.what() << std::endl;
return 1; return 1;
} }
} }
#include "spdlog/sinks/stdout_color_sinks.h" void async_example()
// or #include "spdlog/sinks/stdout_sinks.h" if no colors needed. {
void stdout_logger_example() { size_t q_size = 4096;
// Create color multithreading logger. spdlog::set_async_mode(q_size);
auto console = spdlog::create<stdout_color_sink_mt>("console"); auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
// or for stderr: for (int i = 0; i < 100; ++i)
// auto console = spdlog::create<stderr_color_sink_mt>("console"); {
} async_file->info("Async message #{}", i);
#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);
} }
} }
// 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) // syslog example (linux/osx/freebsd)
#ifndef _WIN32 void syslog_example()
#include "spdlog/sinks/syslog_sink.h" {
void syslog_example() { #ifdef SPDLOG_ENABLE_SYSLOG
std::string ident = "spdlog-example"; 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."); syslog_logger->warn("This is warning that will end up in syslog.");
}
#endif #endif
}
// Android example. // Android example
void android_example()
{
#if defined(__ANDROID__) #if defined(__ANDROID__)
#include "spdlog/sinks/android_sink.h"
void android_example() {
std::string tag = "spdlog-android"; 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."); android_logger->critical("Use \"adb shell logcat\" to view this message.");
}
#endif #endif
}
// Log patterns can contain custom flags. // user defined types logging by implementing operator<<
// this will add custom flag '%*' which will be bound to a <my_formatter_flag> instance struct my_type
#include "spdlog/pattern_formatter.h" {
class my_formatter_flag : public spdlog::custom_flag_formatter { int i;
public: template<typename OStream>
void format(const spdlog::details::log_msg &, const std::tm &, spdlog::memory_buf_t &dest) override { friend OStream &operator<<(OStream &os, const my_type &c)
std::string some_txt = "custom-flag"; {
dest.append(some_txt.data(), some_txt.data() + some_txt.size()); return os << "[my_type i=" << c.i << "]";
}
[[nodiscard]]
std::unique_ptr<custom_flag_formatter> clone() const override {
return std::make_unique<my_formatter_flag>();
} }
}; };
void custom_flags_example() { #include "spdlog/fmt/ostr.h" // must be included
auto formatter = std::make_unique<spdlog::pattern_formatter>(); void user_defined_example()
formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v"); {
// set the new formatter using spdlog::set_formatter(formatter) or spd::get("console")->info("user defined type: {}", my_type{14});
// logger->set_formatter(formatter) spdlog::set_formatter(std::move(formatter));
} }
void file_events_example() { //
// pass the spdlog::file_event_handlers to file sinks for open/close log file notifications // custom error handler
spdlog::file_event_handlers handlers; //
handlers.before_open = [](spdlog::filename_t) { spdlog::trace("Before opening logfile"); }; void err_handler_example()
handlers.after_open = [](spdlog::filename_t, std::FILE *fstream) { {
spdlog::trace("After opening logfile"); // can be set globaly or per logger(logger->set_error_handler(..))
fputs("After opening\n", fstream); 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);
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);
} }

110
example/example.sln Normal file
View 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
View 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
View 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)

View 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
View 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
View File

@@ -0,0 +1 @@
*.txt

46
example/multisink.cpp Normal file
View 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
View 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
View 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

View 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"

View File

@@ -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

View File

@@ -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) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once #pragma once
#include <array> #define SPDLOG_VERSION "0.17.0"
#include "tweakme.h"
#include <atomic> #include <atomic>
#include <chrono> #include <chrono>
#include <cstdint>
#include <exception> #include <exception>
#include <functional> #include <functional>
#include <initializer_list> #include <initializer_list>
#include <memory> #include <memory>
#include <string> #include <string>
#include <string_view> #include <unordered_map>
#include "./source_loc.h" #if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
#include "fmt/base.h" #include <codecvt>
#include "fmt/xchar.h" #include <locale>
#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
#endif #endif
#define SPDLOG_FMT_RUNTIME(format_string) fmt::runtime(format_string) #include "details/null_mutex.h"
#define SPDLOG_FMT_STRING(format_string) FMT_STRING(format_string)
#ifndef SPDLOG_FUNCTION // visual studio upto 2013 does not support noexcept nor constexpr
#define SPDLOG_FUNCTION static_cast<const char *>(__FUNCTION__) #if defined(_MSC_VER) && (_MSC_VER < 1900)
#define SPDLOG_NOEXCEPT throw()
#define SPDLOG_CONSTEXPR
#else
#define SPDLOG_NOEXCEPT noexcept
#define SPDLOG_CONSTEXPR constexpr
#endif #endif
// final keyword support. On by default. See tweakme.h
#if defined(SPDLOG_NO_FINAL)
#define SPDLOG_FINAL
#else
#define SPDLOG_FINAL final
#endif
#if defined(__GNUC__) || defined(__clang__)
#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 { namespace spdlog {
class formatter; class formatter;
@@ -50,90 +62,121 @@ class sink;
using log_clock = std::chrono::system_clock; using log_clock = std::chrono::system_clock;
using sink_ptr = std::shared_ptr<sinks::sink>; using sink_ptr = std::shared_ptr<sinks::sink>;
using sinks_init_list = std::initializer_list<sink_ptr>; using sinks_init_list = std::initializer_list<sink_ptr>;
using err_handler = std::function<void(const std::string &err_msg)>; using formatter_ptr = std::shared_ptr<spdlog::formatter>;
using string_view_t = std::basic_string_view<char>; #if defined(SPDLOG_NO_ATOMIC_LEVELS)
using wstring_view_t = std::basic_string_view<wchar_t>; using level_t = details::null_atomic_int;
#else
namespace fmt_lib = fmt; using level_t = std::atomic<int>;
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
#endif #endif
using log_err_handler = std::function<void(const std::string &err_msg)>;
// Log level enum // Log level enum
enum class level : std::uint8_t { namespace level {
trace = SPDLOG_LEVEL_TRACE, enum level_enum
debug = SPDLOG_LEVEL_DEBUG, {
info = SPDLOG_LEVEL_INFO, trace = 0,
warn = SPDLOG_LEVEL_WARN, debug = 1,
err = SPDLOG_LEVEL_ERROR, info = 2,
critical = SPDLOG_LEVEL_CRITICAL, warn = 3,
off = SPDLOG_LEVEL_OFF, err = 4,
n_levels = SPDLOG_LEVEL_OFF + 1 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); inline const char *to_str(spdlog::level::level_enum l)
constexpr std::array<std::string_view, levels_count> level_string_views{"trace", "debug", "info", "warning", {
"error", "critical", "off"}; return level_names[l];
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));
} }
[[nodiscard]] constexpr std::string_view to_short_string_view(spdlog::level lvl) noexcept { inline const char *to_short_str(spdlog::level::level_enum l)
return short_level_names.at(level_to_number(lvl)); {
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. // Pattern time - specific time getting to use for pattern_formatter.
// local time by default // local time by default
// //
enum class pattern_time_type { enum class pattern_time_type
local, // log localtime {
utc // log utc local, // log localtime
utc // log utc
}; };
// //
// Log exception // Log exception
// //
class SPDLOG_API spdlog_ex : public std::exception { namespace details {
namespace os {
std::string errno_str(int err_num);
}
} // namespace details
class spdlog_ex : public std::exception
{
public: public:
explicit spdlog_ex(std::string msg); explicit spdlog_ex(std::string msg)
: _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: 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 } // namespace spdlog

View File

@@ -0,0 +1 @@
Please put here your contribs. Popular contribs will be moved to main tree after stablization

View File

@@ -0,0 +1 @@

View 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

View 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();
}

View File

@@ -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

View 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;
}
}

View File

@@ -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

View File

@@ -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

View File

@@ -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) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once #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 "../details/log_msg.h"
#include "../file_event_handlers.h" #include "../details/os.h"
#include <cerrno>
#include <chrono>
#include <cstdio>
#include <string>
#include <thread>
#include <tuple>
namespace spdlog { namespace spdlog {
namespace details { namespace details {
// Helper class for file sinks. class file_helper
// When failing to open a file, retry several times(5) with a delay interval(10 ms). {
// Throw spdlog_ex exception on errors.
class SPDLOG_API file_helper {
public: public:
file_helper() = default; const int open_tries = 5;
explicit file_helper(file_event_handlers event_handlers); const int open_interval = 10;
explicit file_helper() = default;
file_helper(const file_helper &) = delete; file_helper(const file_helper &) = delete;
file_helper &operator=(const file_helper &) = delete; file_helper &operator=(const file_helper &) = delete;
~file_helper();
void open(const filename_t &fname, bool truncate = false); ~file_helper()
void reopen(bool truncate); {
void flush() const; close();
void sync() const; }
void close();
void write(const memory_buf_t &buf) const; void open(const filename_t &fname, bool truncate = false)
size_t size() const; {
const filename_t &filename() const; close();
auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
_filename = fname;
for (int tries = 0; tries < open_tries; ++tries)
{
if (!os::fopen_s(&_fd, fname, mode))
{
return;
}
details::os::sleep_for_millis(open_interval);
}
throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno);
}
void reopen(bool truncate)
{
if (_filename.empty())
{
throw spdlog_ex("Failed re opening file - was not opened before");
}
open(_filename, truncate);
}
void flush()
{
std::fflush(_fd);
}
void close()
{
if (_fd != nullptr)
{
std::fclose(_fd);
_fd = nullptr;
}
}
void write(const log_msg &msg)
{
size_t msg_size = msg.formatted.size();
auto data = msg.formatted.data();
if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
{
throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
}
}
size_t size() const
{
if (_fd == nullptr)
{
throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
}
return os::filesize(_fd);
}
const filename_t &filename() const
{
return _filename;
}
static bool file_exists(const filename_t &fname)
{
return os::file_exists(fname);
}
//
// return file path and its extension:
//
// "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: private:
const int open_tries_ = 5; FILE *_fd{nullptr};
const unsigned int open_interval_ = 10; filename_t _filename;
std::FILE *fd_{nullptr};
filename_t filename_;
file_event_handlers event_handlers_;
}; };
} // namespace details } // namespace details
} // namespace spdlog } // namespace spdlog

View File

@@ -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

View File

@@ -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) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once #pragma once
#include <string>
#include "../common.h" #include "../common.h"
#include "../details/os.h"
#include <string>
#include <utility>
namespace spdlog { namespace spdlog {
namespace details { namespace details {
struct SPDLOG_API log_msg { struct log_msg
{
log_msg() = default; log_msg() = default;
log_msg(log_clock::time_point log_time, const source_loc &loc, string_view_t logger_name, level lvl, string_view_t msg); log_msg(const std::string *loggers_name, level::level_enum lvl)
log_msg(const source_loc &loc, string_view_t logger_name, level lvl, string_view_t msg); : logger_name(loggers_name)
log_msg(string_view_t logger_name, level lvl, string_view_t msg); , level(lvl)
log_msg(const log_msg &other) = default; {
log_msg &operator=(const log_msg &other) = default; #ifndef SPDLOG_NO_DATETIME
time = os::now();
#endif
string_view_t logger_name; #ifndef SPDLOG_NO_THREAD_ID
level log_level{level::off}; thread_id = os::thread_id();
#endif
}
log_msg(const log_msg &other) = delete;
log_msg &operator=(log_msg &&other) = delete;
log_msg(log_msg &&other) = delete;
const std::string *logger_name{nullptr};
level::level_enum level;
log_clock::time_point time; log_clock::time_point time;
size_t thread_id{0}; size_t thread_id;
fmt::MemoryWriter raw;
// wrapping the formatted text with color (updated by pattern_formatter). fmt::MemoryWriter formatted;
mutable size_t color_range_start{0}; size_t msg_id{0};
mutable size_t color_range_end{0}; // wrap this range with color codes
size_t color_range_start{0};
source_loc source; size_t color_range_end{0};
string_view_t payload;
}; };
} // namespace details } // namespace details
} // namespace spdlog } // namespace spdlog

View 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;
}

View File

@@ -1,177 +1,84 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
#pragma once #pragma once
// multi producer-multi consumer blocking queue. //
// enqueue(..) - will block until room found to put the new message. // Copyright(c) 2018 Gabi Melman.
// enqueue_nowait(..) - will return immediately with false if no room left in // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// the queue. //
// dequeue_for(..) - will block until the queue is not empty or timeout have
// passed. // 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 <condition_variable>
#include <mutex> #include <mutex>
#include <queue>
#include "./circular_q.h"
namespace spdlog { namespace spdlog {
namespace details { namespace details {
template <typename T> template<typename T>
class mpmc_blocking_queue { class mpmc_bounded_queue
{
public: public:
using item_type = T; using item_type = T;
explicit mpmc_blocking_queue(size_t max_items) explicit mpmc_bounded_queue(size_t max_items)
: q_(max_items) {} : max_items_(max_items)
{
}
#ifndef __MINGW32__
// try to enqueue and block if no room left // try to enqueue and block if no room left
void enqueue(T &&item) { void enqueue(T &&item)
{
{ {
std::unique_lock<std::mutex> lock(queue_mutex_); std::unique_lock<std::mutex> lock(queue_mutex_);
pop_cv_.wait(lock, [this] { return !this->q_.full(); }); pop_cv_.wait(lock, [this] { return this->q_.size() < this->max_items_; });
q_.push_back(std::move(item)); q_.push(std::move(item));
} }
push_cv_.notify_one(); push_cv_.notify_one();
} }
// enqueue immediately. overrun the oldest message in the queue if no room left. // try to enqueue and return immdeialty false if no room left
void enqueue_nowait(T &&item) { bool enqueue_nowait(T &&item)
{
{ {
std::unique_lock<std::mutex> lock(queue_mutex_); std::unique_lock<std::mutex> lock(queue_mutex_);
q_.push_back(std::move(item)); if (q_.size() == this->max_items_)
} {
push_cv_.notify_one();
}
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; return false;
} }
popped_item = std::move(q_.front()); q_.push(std::forward<T>(item));
q_.pop_front();
} }
pop_cv_.notify_one(); push_cv_.notify_one();
return true; return true;
} }
// blocking dequeue without a timeout. // try to dequeue item. if no item found. wait upto timeout and try again
void dequeue(T &popped_item) { // 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_); std::unique_lock<std::mutex> lock(queue_mutex_);
push_cv_.wait(lock, [this] { return !this->q_.empty(); }); if (!push_cv_.wait_for(lock, wait_duration, [this] { return this->q_.size() > 0; }))
{
return false;
}
popped_item = std::move(q_.front()); popped_item = std::move(q_.front());
q_.pop_front(); q_.pop();
} }
pop_cv_.notify_one(); 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; 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: private:
size_t max_items_;
std::mutex queue_mutex_; std::mutex queue_mutex_;
std::condition_variable push_cv_; std::condition_variable push_cv_;
std::condition_variable pop_cv_; std::condition_variable pop_cv_;
spdlog::details::circular_q<T> q_;
std::atomic<size_t> discard_counter_{0}; std::queue<T> q_;
}; };
} // namespace details } // namespace details
} // namespace spdlog } // namespace spdlog

View File

@@ -1,37 +1,45 @@
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. //
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once #pragma once
#include <atomic> #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 spdlog {
namespace details { namespace details {
struct null_mutex { struct null_mutex
void lock() const {} {
void unlock() const {} void lock() {}
}; void unlock() {}
bool try_lock()
template <typename T> {
struct null_atomic { return true;
T value;
null_atomic() = default;
explicit constexpr null_atomic(T new_value)
: value(new_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
} }
}; };
} // namespace details struct null_atomic_int
} // namespace spdlog {
int value;
null_atomic_int() = default;
explicit null_atomic_int(int val)
: value(val)
{
}
int load(std::memory_order) const
{
return value;
}
void store(int val)
{
value = val;
}
};
} // namespace details
} // namespace spdlog

View File

@@ -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) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once #pragma once
#include <ctime> // std::time_t
#include <tuple>
#include "../common.h" #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 spdlog {
namespace details { namespace details {
namespace os { 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 #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 #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 // 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 // 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 // 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 // Return current thread id as size_t
// It exists because the std::this_thread::get_id() is much slower(especially // It exists because the std::this_thread::get_id() is much slower(especially under VS 2013)
// under VS 2013) inline size_t _thread_id()
SPDLOG_API size_t _thread_id() noexcept; {
#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) // 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. // This is avoid msvc issue in sleep_for that happens if the clock changes.
// See https://github.com/gabime/spdlog/issues/609 // 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 // wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
SPDLOG_API int pid() noexcept; #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 // Determine if the terminal supports colors
// Source: https://github.com/agauniyal/rang/ // 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 const char *env_p = std::getenv("TERM");
// Source: https://github.com/agauniyal/rang/ if (env_p == nullptr)
SPDLOG_API bool in_terminal(FILE *file) noexcept; {
return false;
}
#if (defined _WIN32) static const bool result =
SPDLOG_API void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target); std::any_of(std::begin(Terms), std::end(Terms), [&](const char *term) { return std::strstr(env_p, term) != nullptr; });
SPDLOG_API void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target); return result;
#endif #endif
}
// non thread safe, cross platform getenv/getenv_s // Detrmine if the terminal attached
// return empty string if field not found // Source: https://github.com/agauniyal/rang/
SPDLOG_API std::string getenv(const char *field); inline bool in_terminal(FILE *file)
{
// Do fsync by FILE objectpointer. #ifdef _WIN32
// Return true on success. return _isatty(_fileno(file)) != 0;
SPDLOG_API bool fsync(FILE *fp); #else
return isatty(fileno(file)) != 0;
// Do non-locking fwrite if possible by the os or use the regular locking fwrite #endif
// Return true on success. }
SPDLOG_API bool fwrite_bytes(const void *ptr, const size_t n_bytes, FILE *fp); } // namespace os
} // namespace details
// } // namespace spdlog
// 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);
} // namespace os
} // namespace details
} // namespace spdlog

View 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;
}

View 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

View 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();
}

View File

@@ -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

View File

@@ -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