├── .clang-format ├── .clang-tidy ├── .git-blame-ignore-revs ├── .gitattributes ├── .github └── workflows │ ├── coverity_scan.yml │ ├── linux.yml │ ├── macos.yml │ └── windows.yml ├── .gitignore ├── CMakeLists.txt ├── INSTALL ├── LICENSE ├── README.md ├── appveyor.yml ├── bench ├── CMakeLists.txt ├── async_bench.cpp ├── bench.cpp ├── formatter-bench.cpp ├── latency.cpp └── utils.h ├── cmake ├── ide.cmake ├── pch.h.in ├── spdlog.pc.in ├── spdlogCPack.cmake ├── spdlogConfig.cmake.in ├── utils.cmake └── version.rc.in ├── example ├── CMakeLists.txt └── example.cpp ├── include └── spdlog │ ├── async.h │ ├── async_logger-inl.h │ ├── async_logger.h │ ├── cfg │ ├── argv.h │ ├── env.h │ ├── helpers-inl.h │ └── helpers.h │ ├── common-inl.h │ ├── common.h │ ├── details │ ├── backtracer-inl.h │ ├── backtracer.h │ ├── circular_q.h │ ├── console_globals.h │ ├── file_helper-inl.h │ ├── file_helper.h │ ├── fmt_helper.h │ ├── log_msg-inl.h │ ├── log_msg.h │ ├── log_msg_buffer-inl.h │ ├── log_msg_buffer.h │ ├── mpmc_blocking_q.h │ ├── null_mutex.h │ ├── os-inl.h │ ├── os.h │ ├── periodic_worker-inl.h │ ├── periodic_worker.h │ ├── registry-inl.h │ ├── registry.h │ ├── synchronous_factory.h │ ├── tcp_client-windows.h │ ├── tcp_client.h │ ├── thread_pool-inl.h │ ├── thread_pool.h │ ├── udp_client-windows.h │ ├── udp_client.h │ └── windows_include.h │ ├── fmt │ ├── bin_to_hex.h │ ├── bundled │ │ ├── args.h │ │ ├── base.h │ │ ├── chrono.h │ │ ├── color.h │ │ ├── compile.h │ │ ├── core.h │ │ ├── fmt.license.rst │ │ ├── format-inl.h │ │ ├── format.h │ │ ├── os.h │ │ ├── ostream.h │ │ ├── printf.h │ │ ├── ranges.h │ │ ├── std.h │ │ └── xchar.h │ ├── chrono.h │ ├── compile.h │ ├── fmt.h │ ├── ostr.h │ ├── ranges.h │ ├── std.h │ └── xchar.h │ ├── formatter.h │ ├── fwd.h │ ├── logger-inl.h │ ├── logger.h │ ├── mdc.h │ ├── pattern_formatter-inl.h │ ├── pattern_formatter.h │ ├── sinks │ ├── android_sink.h │ ├── ansicolor_sink-inl.h │ ├── ansicolor_sink.h │ ├── base_sink-inl.h │ ├── base_sink.h │ ├── basic_file_sink-inl.h │ ├── basic_file_sink.h │ ├── callback_sink.h │ ├── daily_file_sink.h │ ├── dist_sink.h │ ├── dup_filter_sink.h │ ├── hourly_file_sink.h │ ├── kafka_sink.h │ ├── mongo_sink.h │ ├── msvc_sink.h │ ├── null_sink.h │ ├── ostream_sink.h │ ├── qt_sinks.h │ ├── ringbuffer_sink.h │ ├── rotating_file_sink-inl.h │ ├── rotating_file_sink.h │ ├── sink-inl.h │ ├── sink.h │ ├── stdout_color_sinks-inl.h │ ├── stdout_color_sinks.h │ ├── stdout_sinks-inl.h │ ├── stdout_sinks.h │ ├── syslog_sink.h │ ├── systemd_sink.h │ ├── tcp_sink.h │ ├── udp_sink.h │ ├── win_eventlog_sink.h │ ├── wincolor_sink-inl.h │ └── wincolor_sink.h │ ├── spdlog-inl.h │ ├── spdlog.h │ ├── stopwatch.h │ ├── tweakme.h │ └── version.h ├── logos ├── jetbrains-variant-4.svg └── spdlog.png ├── scripts ├── ci_setup_clang.sh ├── extract_version.py └── format.sh ├── src ├── async.cpp ├── bundled_fmtlib_format.cpp ├── cfg.cpp ├── color_sinks.cpp ├── file_sinks.cpp ├── spdlog.cpp └── stdout_sinks.cpp └── tests ├── CMakeLists.txt ├── includes.h ├── main.cpp ├── test_async.cpp ├── test_backtrace.cpp ├── test_bin_to_hex.cpp ├── test_cfg.cpp ├── test_circular_q.cpp ├── test_create_dir.cpp ├── test_custom_callbacks.cpp ├── test_daily_logger.cpp ├── test_dup_filter.cpp ├── test_errors.cpp ├── test_eventlog.cpp ├── test_file_helper.cpp ├── test_file_logging.cpp ├── test_fmt_helper.cpp ├── test_macros.cpp ├── test_misc.cpp ├── test_mpmc_q.cpp ├── test_pattern_formatter.cpp ├── test_registry.cpp ├── test_sink.h ├── test_stdout_api.cpp ├── test_stopwatch.cpp ├── test_systemd.cpp ├── test_time_point.cpp ├── utils.cpp └── utils.h /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | BasedOnStyle: Google 4 | AccessModifierOffset: -4 5 | Standard: c++17 6 | IndentWidth: 4 7 | TabWidth: 4 8 | UseTab: Never 9 | ColumnLimit: 100 10 | AlignAfterOpenBracket: Align 11 | BinPackParameters: false 12 | AlignEscapedNewlines: Left 13 | AlwaysBreakTemplateDeclarations: Yes 14 | PackConstructorInitializers: Never 15 | BreakConstructorInitializersBeforeComma: false 16 | IndentPPDirectives: BeforeHash 17 | SortIncludes: Never 18 | ... 19 | 20 | -------------------------------------------------------------------------------- /.clang-tidy: -------------------------------------------------------------------------------- 1 | Checks: 'cppcoreguidelines-*, 2 | performance-*, 3 | modernize-*, 4 | google-*, 5 | misc-* 6 | cert-*, 7 | readability-*, 8 | clang-analyzer-*, 9 | -performance-unnecessary-value-param, 10 | -modernize-use-trailing-return-type, 11 | -google-runtime-references, 12 | -misc-non-private-member-variables-in-classes, 13 | -readability-braces-around-statements, 14 | -google-readability-braces-around-statements, 15 | -cppcoreguidelines-avoid-magic-numbers, 16 | -readability-magic-numbers, 17 | -readability-magic-numbers, 18 | -cppcoreguidelines-pro-type-vararg, 19 | -cppcoreguidelines-pro-bounds-pointer-arithmetic, 20 | -cppcoreguidelines-avoid-c-arrays, 21 | -modernize-avoid-c-arrays, 22 | -cppcoreguidelines-pro-bounds-array-to-pointer-decay, 23 | -readability-named-parameter, 24 | -cert-env33-c 25 | ' 26 | 27 | 28 | WarningsAsErrors: '' 29 | HeaderFilterRegex: '*spdlog/[^f].*' 30 | FormatStyle: none 31 | 32 | CheckOptions: 33 | - key: google-readability-braces-around-statements.ShortStatementLines 34 | value: '1' 35 | - key: google-readability-function-size.StatementThreshold 36 | value: '800' 37 | - key: google-readability-namespace-comments.ShortNamespaceLines 38 | value: '10' 39 | - key: google-readability-namespace-comments.SpacesBeforeComments 40 | value: '2' 41 | - key: modernize-loop-convert.MaxCopySize 42 | value: '16' 43 | - key: modernize-loop-convert.MinConfidence 44 | value: reasonable 45 | - key: modernize-loop-convert.NamingStyle 46 | value: CamelCase 47 | - key: modernize-pass-by-value.IncludeStyle 48 | value: llvm 49 | - key: modernize-replace-auto-ptr.IncludeStyle 50 | value: llvm 51 | - key: modernize-use-nullptr.NullMacros 52 | value: 'NULL' 53 | 54 | -------------------------------------------------------------------------------- /.git-blame-ignore-revs: -------------------------------------------------------------------------------- 1 | # clang-format 2 | 1a0bfc7a89f2d58e22605a4dc7e18a9a555b65aa 3 | 95c226e9c92928e20ccdac0d060e7241859e282b 4 | 9d52261185b5f2c454c381d626ec5c84d7b195f4 5 | 4b2a8219d5d1b40062d030441adde7d1fb0d4f84 6 | 0a53eafe18d983c7c8ba4cadd02d0cc7f7308f28 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=false 2 | -------------------------------------------------------------------------------- /.github/workflows/coverity_scan.yml: -------------------------------------------------------------------------------- 1 | name: coverity-linux 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | coverity_scan: 10 | runs-on: ubuntu-latest 11 | name: Coverity Scan 12 | steps: 13 | - uses: actions/checkout@v4 14 | 15 | - name: Install dependencies 16 | run: | 17 | sudo apt-get update 18 | sudo apt-get install -y curl build-essential cmake pkg-config libsystemd-dev 19 | 20 | - name: Download Coverity Tool 21 | run: | 22 | curl -s -L --output coverity_tool.tgz "https://scan.coverity.com/download/linux64?token=${{ secrets.COVERITY_TOKEN }}&project=gabime%2Fspdlog" 23 | mkdir coverity_tool 24 | tar -C coverity_tool --strip-components=1 -xf coverity_tool.tgz 25 | echo "$PWD/coverity_tool/bin" >> $GITHUB_PATH 26 | 27 | - name: Build with Coverity 28 | run: | 29 | mkdir build && cd build 30 | cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_CXX_STANDARD=17 31 | cd .. 32 | cov-build --dir cov-int make -C build -j4 33 | 34 | - name: Submit results to Coverity 35 | run: | 36 | tar czf cov-int.tgz cov-int 37 | response=$(curl --silent --show-error --fail \ 38 | --form email="${{ secrets.EMAIL }}" \ 39 | --form token="${{ secrets.COVERITY_TOKEN }}" \ 40 | --form file=@cov-int.tgz \ 41 | --form version="GitHub PR #${{ github.event.pull_request.number }}" \ 42 | --form description="CI run for PR" \ 43 | https://scan.coverity.com/builds?project=gabime%2Fspdlog) 44 | 45 | echo "$response" 46 | 47 | if echo "$response" | grep -qi "Build successfully submitted"; then 48 | echo "Coverity upload succeeded" 49 | else 50 | echo "Coverity upload failed or was rejected" 51 | exit 1 52 | fi 53 | -------------------------------------------------------------------------------- /.github/workflows/linux.yml: -------------------------------------------------------------------------------- 1 | name: linux 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | # ----------------------------------------------------------------------- 10 | # Linux build matrix 11 | # ----------------------------------------------------------------------- 12 | build: 13 | runs-on: ubuntu-latest 14 | defaults: 15 | run: 16 | shell: bash 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | config: 21 | - { compiler: gcc, version: 7, build_type: Release, cppstd: 11 } 22 | - { compiler: gcc, version: 9, build_type: Release, cppstd: 17 } 23 | - { compiler: gcc, version: 11, build_type: Debug, cppstd: 20 } 24 | - { compiler: gcc, version: 12, build_type: Release, cppstd: 20 } 25 | - { compiler: gcc, version: 12, build_type: Debug, cppstd: 20, asan: ON } 26 | - { compiler: clang, version: 12, build_type: Debug, cppstd: 17 } 27 | - { compiler: clang, version: 15, build_type: Release, cppstd: 20, tsan: ON } 28 | container: 29 | image: ${{ matrix.config.compiler == 'clang' && 'teeks99/clang-ubuntu' || matrix.config.compiler }}:${{ matrix.config.version }} 30 | 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' || '' }})" 31 | steps: 32 | - uses: actions/checkout@v4 33 | - name: Setup 34 | run: | 35 | apt-get update 36 | apt-get install -y curl git pkg-config libsystemd-dev 37 | CMAKE_VERSION="3.24.2" 38 | curl -sSL https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION}/cmake-${CMAKE_VERSION}-linux-x86_64.sh -o install-cmake.sh 39 | chmod +x install-cmake.sh 40 | ./install-cmake.sh --prefix=/usr/local --skip-license 41 | - name: Setup Compiler 42 | if: matrix.config.compiler == 'clang' 43 | run: | 44 | scripts/ci_setup_clang.sh "${{ matrix.config.version }}" 45 | echo "CXXFLAGS=-stdlib=libc++" >> $GITHUB_ENV 46 | echo "CC=clang-${{ matrix.config.version }}" >> $GITHUB_ENV 47 | echo "CXX=clang++-${{ matrix.config.version }}" >> $GITHUB_ENV 48 | - name: Build 49 | run: | 50 | mkdir -p build && cd build 51 | cmake .. \ 52 | -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \ 53 | -DCMAKE_CXX_STANDARD=${{ matrix.config.cppstd }} \ 54 | -DSPDLOG_BUILD_EXAMPLE=${{ matrix.config.examples || 'ON' }} \ 55 | -DSPDLOG_BUILD_EXAMPLE_HO=${{ matrix.config.examples || 'ON' }} \ 56 | -DSPDLOG_BUILD_WARNINGS=ON \ 57 | -DSPDLOG_BUILD_BENCH=OFF \ 58 | -DSPDLOG_BUILD_TESTS=ON \ 59 | -DSPDLOG_BUILD_TESTS_HO=OFF \ 60 | -DSPDLOG_SANITIZE_ADDRESS=${{ matrix.config.asan || 'OFF' }} \ 61 | -DSPDLOG_SANITIZE_THREAD=${{ matrix.config.tsan || 'OFF' }} 62 | make -j 4 63 | ctest -j 4 --output-on-failure 64 | 65 | # ----------------------------------------------------------------------- 66 | # OS X build matrix 67 | # ----------------------------------------------------------------------- 68 | build_osx: 69 | runs-on: macOS-latest 70 | name: "OS X Clang (C++11, Release)" 71 | steps: 72 | - uses: actions/checkout@v4 73 | - name: Build 74 | run: | 75 | mkdir -p build && cd build 76 | cmake .. \ 77 | -DCMAKE_BUILD_TYPE=Release \ 78 | -DCMAKE_CXX_STANDARD=11 \ 79 | -DSPDLOG_BUILD_EXAMPLE=ON \ 80 | -DSPDLOG_BUILD_EXAMPLE_HO=ON \ 81 | -DSPDLOG_BUILD_WARNINGS=ON \ 82 | -DSPDLOG_BUILD_BENCH=OFF \ 83 | -DSPDLOG_BUILD_TESTS=ON \ 84 | -DSPDLOG_BUILD_TESTS_HO=OFF \ 85 | -DSPDLOG_SANITIZE_ADDRESS=OFF 86 | make -j 4 87 | ctest -j 4 --output-on-failure 88 | -------------------------------------------------------------------------------- /.github/workflows/macos.yml: -------------------------------------------------------------------------------- 1 | name: macos 2 | 3 | on: [push, pull_request] 4 | 5 | permissions: 6 | contents: read 7 | 8 | jobs: 9 | build: 10 | runs-on: macOS-latest 11 | name: "macOS Clang (C++11, Release)" 12 | strategy: 13 | fail-fast: true 14 | matrix: 15 | config: 16 | - USE_STD_FORMAT: 'ON' 17 | BUILD_EXAMPLE: 'OFF' 18 | - USE_STD_FORMAT: 'OFF' 19 | BUILD_EXAMPLE: 'ON' 20 | 21 | steps: 22 | - uses: actions/checkout@v4 23 | - name: Build 24 | run: | 25 | mkdir -p build && cd build 26 | cmake .. \ 27 | -DCMAKE_BUILD_TYPE=Release \ 28 | -DCMAKE_CXX_STANDARD=11 \ 29 | -DSPDLOG_BUILD_EXAMPLE=${{ matrix.config.BUILD_EXAMPLE }} \ 30 | -DSPDLOG_BUILD_EXAMPLE_HO=${{ matrix.config.BUILD_EXAMPLE }} \ 31 | -DSPDLOG_BUILD_WARNINGS=ON \ 32 | -DSPDLOG_BUILD_BENCH=OFF \ 33 | -DSPDLOG_BUILD_TESTS=ON \ 34 | -DSPDLOG_BUILD_TESTS_HO=OFF \ 35 | -DSPDLOG_USE_STD_FORMAT=${{ matrix.config.USE_STD_FORMAT }} \ 36 | -DSPDLOG_SANITIZE_ADDRESS=OFF 37 | make -j 4 38 | ctest -j 4 --output-on-failure 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Auto generated files 2 | [Dd]ebug/ 3 | [Rr]elease/ 4 | build/* 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | *.suo 10 | *.tlog 11 | *.ilk 12 | *.log 13 | *.pdb 14 | *.idb 15 | *.iobj 16 | *.ipdb 17 | *.opensdf 18 | *.sdf 19 | 20 | # Compiled Dynamic libraries 21 | *.so 22 | *.dylib 23 | *.dll 24 | 25 | # Compiled Static libraries 26 | *.lai 27 | *.la 28 | *.a 29 | *.lib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | 36 | # Codelite 37 | .codelite 38 | 39 | # KDevelop 40 | *.kdev4 41 | 42 | # .orig files 43 | *.orig 44 | 45 | # example files 46 | example/* 47 | !example/example.cpp 48 | !example/bench.cpp 49 | !example/utils.h 50 | !example/Makefile* 51 | !example/example.sln 52 | !example/example.vcxproj 53 | !example/CMakeLists.txt 54 | !example/meson.build 55 | !example/multisink.cpp 56 | !example/jni 57 | 58 | # generated files 59 | generated 60 | version.rc 61 | 62 | # Cmake 63 | CMakeCache.txt 64 | CMakeFiles 65 | CMakeScripts 66 | Makefile 67 | cmake_install.cmake 68 | install_manifest.txt 69 | /tests/tests.VC.VC.opendb 70 | /tests/tests.VC.db 71 | /tests/tests 72 | /tests/logs/* 73 | spdlogConfig.cmake 74 | spdlogConfigVersion.cmake 75 | compile_commands.json 76 | 77 | # idea 78 | .idea/ 79 | .cache/ 80 | .vscode/ 81 | cmake-build-*/ 82 | *.db 83 | *.ipch 84 | *.filters 85 | *.db-wal 86 | *.opendb 87 | *.db-shm 88 | *.vcxproj 89 | *.tcl 90 | *.user 91 | *.sln 92 | 93 | # macos 94 | *.DS_store 95 | *.xcodeproj/ 96 | /.vs 97 | /out/build 98 | /CMakeSettings.json 99 | -------------------------------------------------------------------------------- /INSTALL: -------------------------------------------------------------------------------- 1 | Header Only Version 2 | ================================================================== 3 | Just copy the files to your build tree and use a C++11 compiler. 4 | Or use CMake: 5 | ``` 6 | add_executable(example_header_only example.cpp) 7 | target_link_libraries(example_header_only spdlog::spdlog_header_only) 8 | ``` 9 | 10 | Compiled Library Version 11 | ================================================================== 12 | CMake: 13 | ``` 14 | add_executable(example example.cpp) 15 | target_link_libraries(example spdlog::spdlog) 16 | ``` 17 | 18 | Or copy files src/*.cpp to your build tree and pass the -DSPDLOG_COMPILED_LIB to the compiler. 19 | 20 | Important Information for Compilation: 21 | ================================================================== 22 | * If you encounter compilation errors with gcc 4.8.x, please note that gcc 4.8.x does not fully support C++11. In such cases, consider upgrading your compiler or using a different version that fully supports C++11 standards 23 | 24 | Tested on: 25 | gcc 4.8.1 and above 26 | clang 3.5 27 | Visual Studio 2013 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Gabi Melman. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | -- NOTE: Third party dependency used by this software -- 24 | This software depends on the fmt lib (MIT License), 25 | and users must comply to its license: https://raw.githubusercontent.com/fmtlib/fmt/master/LICENSE 26 | 27 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.{build} 2 | image: Visual Studio 2017 3 | environment: 4 | matrix: 5 | - GENERATOR: '"Visual Studio 15 2017 Win64"' 6 | BUILD_TYPE: Debug 7 | BUILD_SHARED: 'OFF' 8 | FATAL_ERRORS: 'OFF' 9 | WCHAR: 'ON' 10 | WCHAR_FILES: 'OFF' 11 | BUILD_EXAMPLE: 'ON' 12 | USE_STD_FORMAT: 'OFF' 13 | CXX_STANDARD: 11 14 | - GENERATOR: '"Visual Studio 15 2017 Win64"' 15 | BUILD_TYPE: Release 16 | BUILD_SHARED: 'OFF' 17 | FATAL_ERRORS: 'OFF' 18 | WCHAR: 'OFF' 19 | WCHAR_FILES: 'OFF' 20 | BUILD_EXAMPLE: 'ON' 21 | USE_STD_FORMAT: 'OFF' 22 | CXX_STANDARD: 11 23 | - GENERATOR: '"Visual Studio 15 2017 Win64"' 24 | BUILD_TYPE: Release 25 | BUILD_SHARED: 'ON' 26 | FATAL_ERRORS: 'OFF' 27 | WCHAR: 'OFF' 28 | WCHAR_FILES: 'OFF' 29 | BUILD_EXAMPLE: 'ON' 30 | USE_STD_FORMAT: 'OFF' 31 | CXX_STANDARD: 11 32 | - GENERATOR: '"Visual Studio 15 2017 Win64"' 33 | BUILD_TYPE: Release 34 | BUILD_SHARED: 'ON' 35 | FATAL_ERRORS: 'OFF' 36 | WCHAR: 'ON' 37 | WCHAR_FILES: 'ON' 38 | BUILD_EXAMPLE: 'OFF' 39 | USE_STD_FORMAT: 'OFF' 40 | CXX_STANDARD: 11 41 | - GENERATOR: '"Visual Studio 16 2019" -A x64' 42 | BUILD_TYPE: Release 43 | BUILD_SHARED: 'ON' 44 | FATAL_ERRORS: 'ON' 45 | WCHAR: 'OFF' 46 | WCHAR_FILES: 'OFF' 47 | BUILD_EXAMPLE: 'OFF' 48 | USE_STD_FORMAT: 'OFF' 49 | CXX_STANDARD: 17 50 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 51 | - GENERATOR: '"Visual Studio 17 2022" -A x64' 52 | BUILD_TYPE: Release 53 | BUILD_SHARED: 'ON' 54 | FATAL_ERRORS: 'ON' 55 | WCHAR: 'OFF' 56 | WCHAR_FILES: 'OFF' 57 | BUILD_EXAMPLE: 'OFF' 58 | USE_STD_FORMAT: 'ON' 59 | CXX_STANDARD: 20 60 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 61 | - GENERATOR: '"Visual Studio 17 2022" -A x64' 62 | BUILD_TYPE: Release 63 | BUILD_SHARED: 'ON' 64 | FATAL_ERRORS: 'ON' 65 | WCHAR: 'ON' 66 | WCHAR_FILES: 'ON' 67 | BUILD_EXAMPLE: 'OFF' 68 | USE_STD_FORMAT: 'ON' 69 | CXX_STANDARD: 20 70 | APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 71 | build_script: 72 | - cmd: >- 73 | set 74 | 75 | mkdir build 76 | 77 | cd build 78 | 79 | set PATH=%PATH%;C:\Program Files\Git\usr\bin 80 | 81 | cmake -G %GENERATOR% -D CMAKE_BUILD_TYPE=%BUILD_TYPE% -D BUILD_SHARED_LIBS=%BUILD_SHARED% -D SPDLOG_WCHAR_SUPPORT=%WCHAR% -D SPDLOG_WCHAR_FILENAMES=%WCHAR_FILES% -D SPDLOG_BUILD_EXAMPLE=%BUILD_EXAMPLE% -D SPDLOG_BUILD_EXAMPLE_HO=%BUILD_EXAMPLE% -D SPDLOG_BUILD_TESTS=ON -D SPDLOG_BUILD_TESTS_HO=OFF -D SPDLOG_BUILD_WARNINGS=%FATAL_ERRORS% -D SPDLOG_USE_STD_FORMAT=%USE_STD_FORMAT% -D CMAKE_CXX_STANDARD=%CXX_STANDARD% .. 82 | 83 | cmake --build . --config %BUILD_TYPE% 84 | 85 | before_test: 86 | - set PATH=%PATH%;C:\projects\spdlog\build\_deps\catch2-build\src\%BUILD_TYPE%;C:\projects\spdlog\build\%BUILD_TYPE% 87 | 88 | test_script: 89 | - C:\projects\spdlog\build\tests\%BUILD_TYPE%\spdlog-utests.exe 90 | -------------------------------------------------------------------------------- /bench/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT) 2 | 3 | cmake_minimum_required(VERSION 3.11) 4 | project(spdlog_bench CXX) 5 | 6 | if(NOT TARGET spdlog) 7 | # Stand-alone build 8 | find_package(spdlog CONFIG REQUIRED) 9 | endif() 10 | 11 | find_package(Threads REQUIRED) 12 | find_package(benchmark CONFIG) 13 | if(NOT benchmark_FOUND) 14 | message(STATUS "Using CMake Version ${CMAKE_VERSION}") 15 | # User can fetch googlebenchmark 16 | message(STATUS "Downloading GoogleBenchmark") 17 | include(FetchContent) 18 | 19 | # disable tests 20 | set(BENCHMARK_ENABLE_TESTING OFF CACHE INTERNAL "") 21 | # Do not build and run googlebenchmark tests 22 | FetchContent_Declare(googlebenchmark GIT_REPOSITORY https://github.com/google/benchmark.git GIT_TAG v1.6.0) 23 | FetchContent_MakeAvailable(googlebenchmark) 24 | endif() 25 | 26 | add_executable(bench bench.cpp) 27 | spdlog_enable_warnings(bench) 28 | target_link_libraries(bench PRIVATE spdlog::spdlog) 29 | 30 | add_executable(async_bench async_bench.cpp) 31 | target_link_libraries(async_bench PRIVATE spdlog::spdlog) 32 | 33 | add_executable(latency latency.cpp) 34 | target_link_libraries(latency PRIVATE benchmark::benchmark spdlog::spdlog) 35 | 36 | add_executable(formatter-bench formatter-bench.cpp) 37 | target_link_libraries(formatter-bench PRIVATE benchmark::benchmark spdlog::spdlog) 38 | -------------------------------------------------------------------------------- /bench/formatter-bench.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2018 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #include "benchmark/benchmark.h" 7 | 8 | #include "spdlog/spdlog.h" 9 | #include "spdlog/pattern_formatter.h" 10 | 11 | void bench_formatter(benchmark::State &state, std::string pattern) { 12 | auto formatter = spdlog::details::make_unique(pattern); 13 | spdlog::memory_buf_t dest; 14 | std::string logger_name = "logger-name"; 15 | const char *text = 16 | "Hello. This is some message with length of 80 "; 17 | 18 | spdlog::source_loc source_loc{"a/b/c/d/myfile.cpp", 123, "some_func()"}; 19 | spdlog::details::log_msg msg(source_loc, logger_name, spdlog::level::info, text); 20 | 21 | for (auto _ : state) { 22 | dest.clear(); 23 | formatter->format(msg, dest); 24 | benchmark::DoNotOptimize(dest); 25 | } 26 | } 27 | 28 | void bench_formatters() { 29 | // basic patterns(single flag) 30 | std::string all_flags = "+vtPnlLaAbBcCYDmdHIMSefFprRTXzEisg@luioO%"; 31 | std::vector basic_patterns; 32 | for (auto &flag : all_flags) { 33 | auto pattern = std::string("%") + flag; 34 | benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern); 35 | 36 | // pattern = std::string("%16") + flag; 37 | // benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern); 38 | // 39 | // // bench center padding 40 | // pattern = std::string("%=16") + flag; 41 | // benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern); 42 | } 43 | 44 | // complex patterns 45 | std::vector patterns = { 46 | "[%D %X] [%l] [%n] %v", 47 | "[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] %v", 48 | "[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] [%t] %v", 49 | }; 50 | for (auto &pattern : patterns) { 51 | benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern) 52 | ->Iterations(2500000); 53 | } 54 | } 55 | 56 | int main(int argc, char *argv[]) { 57 | spdlog::set_pattern("[%^%l%$] %v"); 58 | if (argc != 2) { 59 | spdlog::error("Usage: {} (or \"all\" to bench all)", argv[0]); 60 | exit(1); 61 | } 62 | 63 | std::string pattern = argv[1]; 64 | if (pattern == "all") { 65 | bench_formatters(); 66 | } else { 67 | benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern); 68 | } 69 | benchmark::Initialize(&argc, argv); 70 | benchmark::RunSpecifiedBenchmarks(); 71 | } 72 | -------------------------------------------------------------------------------- /bench/utils.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2015 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | namespace utils { 13 | 14 | template 15 | inline std::string format(const T &value) { 16 | static std::locale loc(""); 17 | std::stringstream ss; 18 | ss.imbue(loc); 19 | ss << value; 20 | return ss.str(); 21 | } 22 | 23 | template <> 24 | inline std::string format(const double &value) { 25 | static std::locale loc(""); 26 | std::stringstream ss; 27 | ss.imbue(loc); 28 | ss << std::fixed << std::setprecision(1) << value; 29 | return ss.str(); 30 | } 31 | 32 | } // namespace utils 33 | -------------------------------------------------------------------------------- /cmake/ide.cmake: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------------------- 2 | # IDE support for headers 3 | # --------------------------------------------------------------------------------------- 4 | set(SPDLOG_HEADERS_DIR "${CMAKE_CURRENT_LIST_DIR}/../include") 5 | 6 | file(GLOB SPDLOG_TOP_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/*.h") 7 | file(GLOB SPDLOG_DETAILS_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/details/*.h") 8 | file(GLOB SPDLOG_SINKS_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/sinks/*.h") 9 | file(GLOB SPDLOG_FMT_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/fmt/*.h") 10 | file(GLOB SPDLOG_FMT_BUNDELED_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/fmt/bundled/*.h") 11 | set(SPDLOG_ALL_HEADERS ${SPDLOG_TOP_HEADERS} ${SPDLOG_DETAILS_HEADERS} ${SPDLOG_SINKS_HEADERS} ${SPDLOG_FMT_HEADERS} 12 | ${SPDLOG_FMT_BUNDELED_HEADERS}) 13 | 14 | source_group("Header Files\\spdlog" FILES ${SPDLOG_TOP_HEADERS}) 15 | source_group("Header Files\\spdlog\\details" FILES ${SPDLOG_DETAILS_HEADERS}) 16 | source_group("Header Files\\spdlog\\sinks" FILES ${SPDLOG_SINKS_HEADERS}) 17 | source_group("Header Files\\spdlog\\fmt" FILES ${SPDLOG_FMT_HEADERS}) 18 | source_group("Header Files\\spdlog\\fmt\\bundled\\" FILES ${SPDLOG_FMT_BUNDELED_HEADERS}) 19 | -------------------------------------------------------------------------------- /cmake/spdlog.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@CMAKE_INSTALL_PREFIX@ 2 | exec_prefix=${prefix} 3 | includedir=@PKG_CONFIG_INCLUDEDIR@ 4 | libdir=@PKG_CONFIG_LIBDIR@ 5 | 6 | Name: lib@PROJECT_NAME@ 7 | Description: Fast C++ logging library. 8 | URL: https://github.com/gabime/@PROJECT_NAME@ 9 | Version: @SPDLOG_VERSION@ 10 | CFlags: -I${includedir} @PKG_CONFIG_DEFINES@ 11 | Libs: -L${libdir} -lspdlog -pthread 12 | Requires: @PKG_CONFIG_REQUIRES@ 13 | 14 | -------------------------------------------------------------------------------- /cmake/spdlogCPack.cmake: -------------------------------------------------------------------------------- 1 | set(CPACK_GENERATOR "TGZ;ZIP" CACHE STRING "Semicolon separated list of generators") 2 | 3 | set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0) 4 | set(CPACK_INSTALL_CMAKE_PROJECTS "${CMAKE_BINARY_DIR}" "${PROJECT_NAME}" ALL .) 5 | 6 | set(CPACK_PROJECT_URL "https://github.com/gabime/spdlog") 7 | set(CPACK_PACKAGE_VENDOR "Gabi Melman") 8 | set(CPACK_PACKAGE_CONTACT "Gabi Melman ") 9 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Fast C++ logging library") 10 | set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) 11 | set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) 12 | set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) 13 | set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}) 14 | if(PROJECT_VERSION_TWEAK) 15 | set(CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION}.${PROJECT_VERSION_TWEAK}) 16 | endif() 17 | set(CPACK_PACKAGE_RELOCATABLE ON CACHE BOOL "Build relocatable package") 18 | 19 | set(CPACK_RPM_PACKAGE_LICENSE "MIT") 20 | set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries") 21 | set(CPACK_DEBIAN_PACKAGE_SECTION "libs") 22 | set(CPACK_RPM_PACKAGE_URL ${CPACK_PROJECT_URL}) 23 | set(CPACK_DEBIAN_PACKAGE_HOMEPAGE ${CPACK_PROJECT_URL}) 24 | set(CPACK_RPM_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.") 25 | set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.") 26 | 27 | if(CPACK_PACKAGE_NAME) 28 | set(CPACK_RPM_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") 29 | set(CPACK_DEBIAN_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") 30 | else() 31 | set(CPACK_RPM_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}") 32 | set(CPACK_DEBIAN_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}") 33 | set(CPACK_RPM_PACKAGE_NAME "${PROJECT_NAME}") 34 | set(CPACK_DEBIAN_PACKAGE_NAME "${PROJECT_NAME}") 35 | endif() 36 | 37 | if(CPACK_RPM_PACKAGE_RELEASE) 38 | set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}-${CPACK_RPM_PACKAGE_RELEASE}") 39 | endif() 40 | if(CPACK_DEBIAN_PACKAGE_RELEASE) 41 | set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}-${CPACK_DEBIAN_PACKAGE_RELEASE}") 42 | endif() 43 | 44 | if(CPACK_RPM_PACKAGE_ARCHITECTURE) 45 | set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.${CPACK_RPM_PACKAGE_ARCHITECTURE}") 46 | endif() 47 | if(CPACK_DEBIAN_PACKAGE_ARCHITECTURE) 48 | set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}") 49 | endif() 50 | set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.rpm") 51 | set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.deb") 52 | 53 | if(NOT CPACK_PACKAGE_RELOCATABLE) 54 | # Depend on pkgconfig rpm to create the system pkgconfig folder 55 | set(CPACK_RPM_PACKAGE_REQUIRES pkgconfig) 56 | set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION 57 | "${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/pkgconfig") 58 | endif() 59 | 60 | include(CPack) 61 | -------------------------------------------------------------------------------- /cmake/spdlogConfig.cmake.in: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2019 spdlog authors 2 | # Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | @PACKAGE_INIT@ 5 | 6 | find_package(Threads REQUIRED) 7 | 8 | set(SPDLOG_FMT_EXTERNAL @SPDLOG_FMT_EXTERNAL@) 9 | set(SPDLOG_FMT_EXTERNAL_HO @SPDLOG_FMT_EXTERNAL_HO@) 10 | set(config_targets_file @config_targets_file@) 11 | 12 | if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO) 13 | include(CMakeFindDependencyMacro) 14 | find_dependency(fmt CONFIG) 15 | endif() 16 | 17 | 18 | include("${CMAKE_CURRENT_LIST_DIR}/${config_targets_file}") 19 | 20 | check_required_components(spdlog) 21 | -------------------------------------------------------------------------------- /cmake/utils.cmake: -------------------------------------------------------------------------------- 1 | # Get spdlog version from include/spdlog/version.h and put it in SPDLOG_VERSION 2 | function(spdlog_extract_version) 3 | file(READ "${CMAKE_CURRENT_LIST_DIR}/include/spdlog/version.h" file_contents) 4 | string(REGEX MATCH "SPDLOG_VER_MAJOR ([0-9]+)" _ "${file_contents}") 5 | if(NOT CMAKE_MATCH_COUNT EQUAL 1) 6 | message(FATAL_ERROR "Could not extract major version number from spdlog/version.h") 7 | endif() 8 | set(ver_major ${CMAKE_MATCH_1}) 9 | 10 | string(REGEX MATCH "SPDLOG_VER_MINOR ([0-9]+)" _ "${file_contents}") 11 | if(NOT CMAKE_MATCH_COUNT EQUAL 1) 12 | message(FATAL_ERROR "Could not extract minor version number from spdlog/version.h") 13 | endif() 14 | 15 | set(ver_minor ${CMAKE_MATCH_1}) 16 | string(REGEX MATCH "SPDLOG_VER_PATCH ([0-9]+)" _ "${file_contents}") 17 | if(NOT CMAKE_MATCH_COUNT EQUAL 1) 18 | message(FATAL_ERROR "Could not extract patch version number from spdlog/version.h") 19 | endif() 20 | set(ver_patch ${CMAKE_MATCH_1}) 21 | 22 | set(SPDLOG_VERSION_MAJOR ${ver_major} PARENT_SCOPE) 23 | set(SPDLOG_VERSION_MINOR ${ver_minor} PARENT_SCOPE) 24 | set(SPDLOG_VERSION_PATCH ${ver_patch} PARENT_SCOPE) 25 | set(SPDLOG_VERSION "${ver_major}.${ver_minor}.${ver_patch}" PARENT_SCOPE) 26 | endfunction() 27 | 28 | # Turn on warnings on the given target 29 | function(spdlog_enable_warnings target_name) 30 | if(SPDLOG_BUILD_WARNINGS) 31 | if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") 32 | list(APPEND MSVC_OPTIONS "/W3") 33 | if(MSVC_VERSION GREATER 1900) # Allow non fatal security warnings for msvc 2015 34 | list(APPEND MSVC_OPTIONS "/WX") 35 | endif() 36 | endif() 37 | 38 | target_compile_options( 39 | ${target_name} 40 | PRIVATE $<$,$,$>: 41 | -Wall 42 | -Wextra 43 | -Wconversion 44 | -pedantic 45 | -Werror 46 | -Wfatal-errors> 47 | $<$:${MSVC_OPTIONS}>) 48 | endif() 49 | endfunction() 50 | 51 | # Enable address sanitizer (gcc/clang only) 52 | function(spdlog_enable_addr_sanitizer target_name) 53 | if(NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") 54 | message(FATAL_ERROR "Sanitizer supported only for gcc/clang") 55 | endif() 56 | message(STATUS "Address sanitizer enabled") 57 | target_compile_options(${target_name} PRIVATE -fsanitize=address,undefined) 58 | target_compile_options(${target_name} PRIVATE -fno-sanitize=signed-integer-overflow) 59 | target_compile_options(${target_name} PRIVATE -fno-sanitize-recover=all) 60 | target_compile_options(${target_name} PRIVATE -fno-omit-frame-pointer) 61 | target_link_libraries(${target_name} PRIVATE -fsanitize=address,undefined) 62 | endfunction() 63 | 64 | # Enable thread sanitizer (gcc/clang only) 65 | function(spdlog_enable_thread_sanitizer target_name) 66 | if(NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") 67 | message(FATAL_ERROR "Sanitizer supported only for gcc/clang") 68 | endif() 69 | message(STATUS "Thread sanitizer enabled") 70 | target_compile_options(${target_name} PRIVATE -fsanitize=thread) 71 | target_compile_options(${target_name} PRIVATE -fno-omit-frame-pointer) 72 | target_link_libraries(${target_name} PRIVATE -fsanitize=thread) 73 | endfunction() 74 | -------------------------------------------------------------------------------- /cmake/version.rc.in: -------------------------------------------------------------------------------- 1 | #define APSTUDIO_READONLY_SYMBOLS 2 | #include 3 | #undef APSTUDIO_READONLY_SYMBOLS 4 | 5 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 6 | 7 | 8 | VS_VERSION_INFO VERSIONINFO 9 | FILEVERSION @SPDLOG_VERSION_MAJOR@,@SPDLOG_VERSION_MINOR@,@SPDLOG_VERSION_PATCH@,0 10 | PRODUCTVERSION @SPDLOG_VERSION_MAJOR@,@SPDLOG_VERSION_MINOR@,@SPDLOG_VERSION_PATCH@,0 11 | FILEFLAGSMASK 0x3fL 12 | #ifdef _DEBUG 13 | FILEFLAGS 0x1L 14 | #else 15 | FILEFLAGS 0x0L 16 | #endif 17 | FILEOS 0x40004L 18 | FILETYPE 0x2L 19 | FILESUBTYPE 0x0L 20 | BEGIN 21 | BLOCK "StringFileInfo" 22 | BEGIN 23 | BLOCK "040904b0" 24 | BEGIN 25 | VALUE "FileDescription", "spdlog dll\0" 26 | VALUE "FileVersion", "@SPDLOG_VERSION@.0\0" 27 | VALUE "InternalName", "spdlog.dll\0" 28 | VALUE "LegalCopyright", "Copyright (C) spdlog\0" 29 | VALUE "ProductName", "spdlog\0" 30 | VALUE "ProductVersion", "@SPDLOG_VERSION@.0\0" 31 | END 32 | END 33 | BLOCK "VarFileInfo" 34 | BEGIN 35 | VALUE "Translation", 0x409, 1200 36 | END 37 | END 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT) 2 | 3 | cmake_minimum_required(VERSION 3.11) 4 | project(spdlog_examples CXX) 5 | 6 | if(NOT TARGET spdlog) 7 | # Stand-alone build 8 | find_package(spdlog REQUIRED) 9 | endif() 10 | 11 | # --------------------------------------------------------------------------------------- 12 | # Example of using pre-compiled library 13 | # --------------------------------------------------------------------------------------- 14 | add_executable(example example.cpp) 15 | target_link_libraries(example PRIVATE spdlog::spdlog $<$:ws2_32>) 16 | 17 | # --------------------------------------------------------------------------------------- 18 | # Example of using header-only library 19 | # --------------------------------------------------------------------------------------- 20 | if(SPDLOG_BUILD_EXAMPLE_HO) 21 | add_executable(example_header_only example.cpp) 22 | target_link_libraries(example_header_only PRIVATE spdlog::spdlog_header_only) 23 | endif() 24 | -------------------------------------------------------------------------------- /include/spdlog/async.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | // 7 | // Async logging using global thread pool 8 | // All loggers created here share same global thread pool. 9 | // Each log message is pushed to a queue along with a shared pointer to the 10 | // logger. 11 | // If a logger deleted while having pending messages in the queue, it's actual 12 | // destruction will defer 13 | // until all its messages are processed by the thread pool. 14 | // This is because each message in the queue holds a shared_ptr to the 15 | // originating logger. 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | namespace spdlog { 26 | 27 | namespace details { 28 | static const size_t default_async_q_size = 8192; 29 | } 30 | 31 | // async logger factory - creates async loggers backed with thread pool. 32 | // if a global thread pool doesn't already exist, create it with default queue 33 | // size of 8192 items and single thread. 34 | template 35 | struct async_factory_impl { 36 | template 37 | static std::shared_ptr create(std::string logger_name, SinkArgs &&...args) { 38 | auto ®istry_inst = details::registry::instance(); 39 | 40 | // create global thread pool if not already exists.. 41 | 42 | auto &mutex = registry_inst.tp_mutex(); 43 | std::lock_guard tp_lock(mutex); 44 | auto tp = registry_inst.get_tp(); 45 | if (tp == nullptr) { 46 | tp = std::make_shared(details::default_async_q_size, 1U); 47 | registry_inst.set_tp(tp); 48 | } 49 | 50 | auto sink = std::make_shared(std::forward(args)...); 51 | auto new_logger = std::make_shared(std::move(logger_name), std::move(sink), 52 | std::move(tp), OverflowPolicy); 53 | registry_inst.initialize_logger(new_logger); 54 | return new_logger; 55 | } 56 | }; 57 | 58 | using async_factory = async_factory_impl; 59 | using async_factory_nonblock = async_factory_impl; 60 | 61 | template 62 | inline std::shared_ptr create_async(std::string logger_name, 63 | SinkArgs &&...sink_args) { 64 | return async_factory::create(std::move(logger_name), 65 | std::forward(sink_args)...); 66 | } 67 | 68 | template 69 | inline std::shared_ptr create_async_nb(std::string logger_name, 70 | SinkArgs &&...sink_args) { 71 | return async_factory_nonblock::create(std::move(logger_name), 72 | std::forward(sink_args)...); 73 | } 74 | 75 | // set global thread pool. 76 | inline void init_thread_pool(size_t q_size, 77 | size_t thread_count, 78 | std::function on_thread_start, 79 | std::function on_thread_stop) { 80 | auto tp = std::make_shared(q_size, thread_count, on_thread_start, 81 | on_thread_stop); 82 | details::registry::instance().set_tp(std::move(tp)); 83 | } 84 | 85 | inline void init_thread_pool(size_t q_size, 86 | size_t thread_count, 87 | std::function on_thread_start) { 88 | init_thread_pool(q_size, thread_count, on_thread_start, [] {}); 89 | } 90 | 91 | inline void init_thread_pool(size_t q_size, size_t thread_count) { 92 | init_thread_pool(q_size, thread_count, [] {}, [] {}); 93 | } 94 | 95 | // get the global thread pool. 96 | inline std::shared_ptr thread_pool() { 97 | return details::registry::instance().get_tp(); 98 | } 99 | } // namespace spdlog 100 | -------------------------------------------------------------------------------- /include/spdlog/async_logger-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | SPDLOG_INLINE spdlog::async_logger::async_logger(std::string logger_name, 17 | sinks_init_list sinks_list, 18 | std::weak_ptr tp, 19 | async_overflow_policy overflow_policy) 20 | : async_logger(std::move(logger_name), 21 | sinks_list.begin(), 22 | sinks_list.end(), 23 | std::move(tp), 24 | overflow_policy) {} 25 | 26 | SPDLOG_INLINE spdlog::async_logger::async_logger(std::string logger_name, 27 | sink_ptr single_sink, 28 | std::weak_ptr tp, 29 | async_overflow_policy overflow_policy) 30 | : async_logger( 31 | std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy) {} 32 | 33 | // send the log message to the thread pool 34 | SPDLOG_INLINE void spdlog::async_logger::sink_it_(const details::log_msg &msg){ 35 | SPDLOG_TRY{if (auto pool_ptr = thread_pool_.lock()){ 36 | pool_ptr -> post_log(shared_from_this(), msg, overflow_policy_); 37 | } 38 | else { 39 | throw_spdlog_ex("async log: thread pool doesn't exist anymore"); 40 | } 41 | } 42 | SPDLOG_LOGGER_CATCH(msg.source) 43 | } 44 | 45 | // send flush request to the thread pool 46 | SPDLOG_INLINE void spdlog::async_logger::flush_(){ 47 | SPDLOG_TRY{if (auto pool_ptr = thread_pool_.lock()){ 48 | pool_ptr -> post_flush(shared_from_this(), overflow_policy_); 49 | } 50 | else { 51 | throw_spdlog_ex("async flush: thread pool doesn't exist anymore"); 52 | } 53 | } 54 | SPDLOG_LOGGER_CATCH(source_loc()) 55 | } 56 | 57 | // 58 | // backend functions - called from the thread pool to do the actual job 59 | // 60 | SPDLOG_INLINE void spdlog::async_logger::backend_sink_it_(const details::log_msg &msg) { 61 | for (auto &sink : sinks_) { 62 | if (sink->should_log(msg.level)) { 63 | SPDLOG_TRY { sink->log(msg); } 64 | SPDLOG_LOGGER_CATCH(msg.source) 65 | } 66 | } 67 | 68 | if (should_flush_(msg)) { 69 | backend_flush_(); 70 | } 71 | } 72 | 73 | SPDLOG_INLINE void spdlog::async_logger::backend_flush_() { 74 | for (auto &sink : sinks_) { 75 | SPDLOG_TRY { sink->flush(); } 76 | SPDLOG_LOGGER_CATCH(source_loc()) 77 | } 78 | } 79 | 80 | SPDLOG_INLINE std::shared_ptr spdlog::async_logger::clone(std::string new_name) { 81 | auto cloned = std::make_shared(*this); 82 | cloned->name_ = std::move(new_name); 83 | return cloned; 84 | } 85 | -------------------------------------------------------------------------------- /include/spdlog/async_logger.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | // Fast asynchronous logger. 7 | // Uses pre allocated queue. 8 | // Creates a single back thread to pop messages from the queue and log them. 9 | // 10 | // Upon each log write the logger: 11 | // 1. Checks if its log level is enough to log the message 12 | // 2. Push a new copy of the message to a queue (or block the caller until 13 | // space is available in the queue) 14 | // Upon destruction, logs all remaining messages in the queue before 15 | // destructing.. 16 | 17 | #include 18 | 19 | namespace spdlog { 20 | 21 | // Async overflow policy - block by default. 22 | enum class async_overflow_policy { 23 | block, // Block until message can be enqueued 24 | overrun_oldest, // Discard oldest message in the queue if full when trying to 25 | // add new item. 26 | discard_new // Discard new message if the queue is full when trying to add new item. 27 | }; 28 | 29 | namespace details { 30 | class thread_pool; 31 | } 32 | 33 | class SPDLOG_API async_logger final : public std::enable_shared_from_this, 34 | public logger { 35 | friend class details::thread_pool; 36 | 37 | public: 38 | template 39 | async_logger(std::string logger_name, 40 | It begin, 41 | It end, 42 | std::weak_ptr tp, 43 | async_overflow_policy overflow_policy = async_overflow_policy::block) 44 | : logger(std::move(logger_name), begin, end), 45 | thread_pool_(std::move(tp)), 46 | overflow_policy_(overflow_policy) {} 47 | 48 | async_logger(std::string logger_name, 49 | sinks_init_list sinks_list, 50 | std::weak_ptr tp, 51 | async_overflow_policy overflow_policy = async_overflow_policy::block); 52 | 53 | async_logger(std::string logger_name, 54 | sink_ptr single_sink, 55 | std::weak_ptr tp, 56 | async_overflow_policy overflow_policy = async_overflow_policy::block); 57 | 58 | std::shared_ptr clone(std::string new_name) override; 59 | 60 | protected: 61 | void sink_it_(const details::log_msg &msg) override; 62 | void flush_() override; 63 | void backend_sink_it_(const details::log_msg &incoming_log_msg); 64 | void backend_flush_(); 65 | 66 | private: 67 | std::weak_ptr thread_pool_; 68 | async_overflow_policy overflow_policy_; 69 | }; 70 | } // namespace spdlog 71 | 72 | #ifdef SPDLOG_HEADER_ONLY 73 | #include "async_logger-inl.h" 74 | #endif 75 | -------------------------------------------------------------------------------- /include/spdlog/cfg/argv.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | #include 6 | #include 7 | 8 | // 9 | // Init log levels using each argv entry that starts with "SPDLOG_LEVEL=" 10 | // 11 | // set all loggers to debug level: 12 | // example.exe "SPDLOG_LEVEL=debug" 13 | 14 | // set logger1 to trace level 15 | // example.exe "SPDLOG_LEVEL=logger1=trace" 16 | 17 | // turn off all logging except for logger1 and logger2: 18 | // example.exe "SPDLOG_LEVEL=off,logger1=debug,logger2=info" 19 | 20 | namespace spdlog { 21 | namespace cfg { 22 | 23 | // search for SPDLOG_LEVEL= in the args and use it to init the levels 24 | inline void load_argv_levels(int argc, const char **argv) { 25 | const std::string spdlog_level_prefix = "SPDLOG_LEVEL="; 26 | for (int i = 1; i < argc; i++) { 27 | std::string arg = argv[i]; 28 | if (arg.find(spdlog_level_prefix) == 0) { 29 | auto levels_string = arg.substr(spdlog_level_prefix.size()); 30 | helpers::load_levels(levels_string); 31 | } 32 | } 33 | } 34 | 35 | inline void load_argv_levels(int argc, char **argv) { 36 | load_argv_levels(argc, const_cast(argv)); 37 | } 38 | 39 | } // namespace cfg 40 | } // namespace spdlog 41 | -------------------------------------------------------------------------------- /include/spdlog/cfg/env.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | #include 6 | #include 7 | #include 8 | 9 | // 10 | // Init levels and patterns from env variables SPDLOG_LEVEL 11 | // Inspired from Rust's "env_logger" crate (https://crates.io/crates/env_logger). 12 | // Note - fallback to "info" level on unrecognized levels 13 | // 14 | // Examples: 15 | // 16 | // set global level to debug: 17 | // export SPDLOG_LEVEL=debug 18 | // 19 | // turn off all logging except for logger1: 20 | // export SPDLOG_LEVEL="*=off,logger1=debug" 21 | // 22 | 23 | // turn off all logging except for logger1 and logger2: 24 | // export SPDLOG_LEVEL="off,logger1=debug,logger2=info" 25 | 26 | namespace spdlog { 27 | namespace cfg { 28 | inline void load_env_levels(const char* var = "SPDLOG_LEVEL") { 29 | auto env_val = details::os::getenv(var); 30 | if (!env_val.empty()) { 31 | helpers::load_levels(env_val); 32 | } 33 | } 34 | 35 | } // namespace cfg 36 | } // namespace spdlog 37 | -------------------------------------------------------------------------------- /include/spdlog/cfg/helpers-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace spdlog { 19 | namespace cfg { 20 | namespace helpers { 21 | 22 | // inplace convert to lowercase 23 | inline std::string &to_lower_(std::string &str) { 24 | std::transform(str.begin(), str.end(), str.begin(), [](char ch) { 25 | return static_cast((ch >= 'A' && ch <= 'Z') ? ch + ('a' - 'A') : ch); 26 | }); 27 | return str; 28 | } 29 | 30 | // inplace trim spaces 31 | inline std::string &trim_(std::string &str) { 32 | const char *spaces = " \n\r\t"; 33 | str.erase(str.find_last_not_of(spaces) + 1); 34 | str.erase(0, str.find_first_not_of(spaces)); 35 | return str; 36 | } 37 | 38 | // return (name,value) trimmed pair from the given "name = value" string. 39 | // return empty string on missing parts 40 | // "key=val" => ("key", "val") 41 | // " key = val " => ("key", "val") 42 | // "key=" => ("key", "") 43 | // "val" => ("", "val") 44 | 45 | inline std::pair extract_kv_(char sep, const std::string &str) { 46 | auto n = str.find(sep); 47 | std::string k, v; 48 | if (n == std::string::npos) { 49 | v = str; 50 | } else { 51 | k = str.substr(0, n); 52 | v = str.substr(n + 1); 53 | } 54 | return std::make_pair(trim_(k), trim_(v)); 55 | } 56 | 57 | // return vector of key/value pairs from a sequence of "K1=V1,K2=V2,.." 58 | // "a=AAA,b=BBB,c=CCC,.." => {("a","AAA"),("b","BBB"),("c", "CCC"),...} 59 | inline std::unordered_map extract_key_vals_(const std::string &str) { 60 | std::string token; 61 | std::istringstream token_stream(str); 62 | std::unordered_map rv{}; 63 | while (std::getline(token_stream, token, ',')) { 64 | if (token.empty()) { 65 | continue; 66 | } 67 | auto kv = extract_kv_('=', token); 68 | rv[kv.first] = kv.second; 69 | } 70 | return rv; 71 | } 72 | 73 | SPDLOG_INLINE void load_levels(const std::string &input) { 74 | if (input.empty() || input.size() > 512) { 75 | return; 76 | } 77 | 78 | auto key_vals = extract_key_vals_(input); 79 | std::unordered_map levels; 80 | level::level_enum global_level = level::info; 81 | bool global_level_found = false; 82 | 83 | for (auto &name_level : key_vals) { 84 | const auto &logger_name = name_level.first; 85 | const auto &level_name = to_lower_(name_level.second); 86 | auto level = level::from_str(level_name); 87 | // ignore unrecognized level names 88 | if (level == level::off && level_name != "off") { 89 | continue; 90 | } 91 | if (logger_name.empty()) // no logger name indicates global level 92 | { 93 | global_level_found = true; 94 | global_level = level; 95 | } else { 96 | levels[logger_name] = level; 97 | } 98 | } 99 | 100 | details::registry::instance().set_levels(std::move(levels), 101 | global_level_found ? &global_level : nullptr); 102 | } 103 | 104 | } // namespace helpers 105 | } // namespace cfg 106 | } // namespace spdlog 107 | -------------------------------------------------------------------------------- /include/spdlog/cfg/helpers.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | namespace spdlog { 10 | namespace cfg { 11 | namespace helpers { 12 | // 13 | // Init levels from given string 14 | // 15 | // Examples: 16 | // 17 | // set global level to debug: "debug" 18 | // turn off all logging except for logger1: "off,logger1=debug" 19 | // turn off all logging except for logger1 and logger2: "off,logger1=debug,logger2=info" 20 | // 21 | SPDLOG_API void load_levels(const std::string &txt); 22 | } // namespace helpers 23 | 24 | } // namespace cfg 25 | } // namespace spdlog 26 | 27 | #ifdef SPDLOG_HEADER_ONLY 28 | #include "helpers-inl.h" 29 | #endif // SPDLOG_HEADER_ONLY 30 | -------------------------------------------------------------------------------- /include/spdlog/common-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | #include 11 | #include 12 | 13 | namespace spdlog { 14 | namespace level { 15 | 16 | #if __cplusplus >= 201703L 17 | constexpr 18 | #endif 19 | static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES; 20 | 21 | static const char *short_level_names[] SPDLOG_SHORT_LEVEL_NAMES; 22 | 23 | SPDLOG_INLINE const string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT { 24 | return level_string_views[l]; 25 | } 26 | 27 | SPDLOG_INLINE const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT { 28 | return short_level_names[l]; 29 | } 30 | 31 | SPDLOG_INLINE spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT { 32 | auto it = std::find(std::begin(level_string_views), std::end(level_string_views), name); 33 | if (it != std::end(level_string_views)) 34 | return static_cast(std::distance(std::begin(level_string_views), it)); 35 | 36 | // check also for "warn" and "err" before giving up.. 37 | if (name == "warn") { 38 | return level::warn; 39 | } 40 | if (name == "err") { 41 | return level::err; 42 | } 43 | return level::off; 44 | } 45 | } // namespace level 46 | 47 | SPDLOG_INLINE spdlog_ex::spdlog_ex(std::string msg) 48 | : msg_(std::move(msg)) {} 49 | 50 | SPDLOG_INLINE spdlog_ex::spdlog_ex(const std::string &msg, int last_errno) { 51 | #ifdef SPDLOG_USE_STD_FORMAT 52 | msg_ = std::system_error(std::error_code(last_errno, std::generic_category()), msg).what(); 53 | #else 54 | memory_buf_t outbuf; 55 | fmt::format_system_error(outbuf, last_errno, msg.c_str()); 56 | msg_ = fmt::to_string(outbuf); 57 | #endif 58 | } 59 | 60 | SPDLOG_INLINE const char *spdlog_ex::what() const SPDLOG_NOEXCEPT { return msg_.c_str(); } 61 | 62 | SPDLOG_INLINE void throw_spdlog_ex(const std::string &msg, int last_errno) { 63 | SPDLOG_THROW(spdlog_ex(msg, last_errno)); 64 | } 65 | 66 | SPDLOG_INLINE void throw_spdlog_ex(std::string msg) { SPDLOG_THROW(spdlog_ex(std::move(msg))); } 67 | 68 | } // namespace spdlog 69 | -------------------------------------------------------------------------------- /include/spdlog/details/backtracer-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | namespace spdlog { 10 | namespace details { 11 | SPDLOG_INLINE backtracer::backtracer(const backtracer &other) { 12 | std::lock_guard lock(other.mutex_); 13 | enabled_ = other.enabled(); 14 | messages_ = other.messages_; 15 | } 16 | 17 | SPDLOG_INLINE backtracer::backtracer(backtracer &&other) SPDLOG_NOEXCEPT { 18 | std::lock_guard lock(other.mutex_); 19 | enabled_ = other.enabled(); 20 | messages_ = std::move(other.messages_); 21 | } 22 | 23 | SPDLOG_INLINE backtracer &backtracer::operator=(backtracer other) { 24 | std::lock_guard lock(mutex_); 25 | enabled_ = other.enabled(); 26 | messages_ = std::move(other.messages_); 27 | return *this; 28 | } 29 | 30 | SPDLOG_INLINE void backtracer::enable(size_t size) { 31 | std::lock_guard lock{mutex_}; 32 | enabled_.store(true, std::memory_order_relaxed); 33 | messages_ = circular_q{size}; 34 | } 35 | 36 | SPDLOG_INLINE void backtracer::disable() { 37 | std::lock_guard lock{mutex_}; 38 | enabled_.store(false, std::memory_order_relaxed); 39 | } 40 | 41 | SPDLOG_INLINE bool backtracer::enabled() const { return enabled_.load(std::memory_order_relaxed); } 42 | 43 | SPDLOG_INLINE void backtracer::push_back(const log_msg &msg) { 44 | std::lock_guard lock{mutex_}; 45 | messages_.push_back(log_msg_buffer{msg}); 46 | } 47 | 48 | SPDLOG_INLINE bool backtracer::empty() const { 49 | std::lock_guard lock{mutex_}; 50 | return messages_.empty(); 51 | } 52 | 53 | // pop all items in the q and apply the given fun on each of them. 54 | SPDLOG_INLINE void backtracer::foreach_pop(std::function fun) { 55 | std::lock_guard lock{mutex_}; 56 | while (!messages_.empty()) { 57 | auto &front_msg = messages_.front(); 58 | fun(front_msg); 59 | messages_.pop_front(); 60 | } 61 | } 62 | } // namespace details 63 | } // namespace spdlog 64 | -------------------------------------------------------------------------------- /include/spdlog/details/backtracer.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | // Store log messages in circular buffer. 14 | // Useful for storing debug data in case of error/warning happens. 15 | 16 | namespace spdlog { 17 | namespace details { 18 | class SPDLOG_API backtracer { 19 | mutable std::mutex mutex_; 20 | std::atomic enabled_{false}; 21 | circular_q messages_; 22 | 23 | public: 24 | backtracer() = default; 25 | backtracer(const backtracer &other); 26 | 27 | backtracer(backtracer &&other) SPDLOG_NOEXCEPT; 28 | backtracer &operator=(backtracer other); 29 | 30 | void enable(size_t size); 31 | void disable(); 32 | bool enabled() const; 33 | void push_back(const log_msg &msg); 34 | bool empty() const; 35 | 36 | // pop all items in the q and apply the given fun on each of them. 37 | void foreach_pop(std::function fun); 38 | }; 39 | 40 | } // namespace details 41 | } // namespace spdlog 42 | 43 | #ifdef SPDLOG_HEADER_ONLY 44 | #include "backtracer-inl.h" 45 | #endif 46 | -------------------------------------------------------------------------------- /include/spdlog/details/circular_q.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | // circular q view of std::vector. 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include "spdlog/common.h" 11 | 12 | namespace spdlog { 13 | namespace details { 14 | template 15 | class circular_q { 16 | size_t max_items_ = 0; 17 | typename std::vector::size_type head_ = 0; 18 | typename std::vector::size_type tail_ = 0; 19 | size_t overrun_counter_ = 0; 20 | std::vector v_; 21 | 22 | public: 23 | using value_type = T; 24 | 25 | // empty ctor - create a disabled queue with no elements allocated at all 26 | circular_q() = default; 27 | 28 | explicit circular_q(size_t max_items) 29 | : max_items_(max_items + 1) // one item is reserved as marker for full q 30 | , 31 | v_(max_items_) {} 32 | 33 | circular_q(const circular_q &) = default; 34 | circular_q &operator=(const circular_q &) = default; 35 | 36 | // move cannot be default, 37 | // since we need to reset head_, tail_, etc to zero in the moved object 38 | circular_q(circular_q &&other) SPDLOG_NOEXCEPT { copy_moveable(std::move(other)); } 39 | 40 | circular_q &operator=(circular_q &&other) SPDLOG_NOEXCEPT { 41 | copy_moveable(std::move(other)); 42 | return *this; 43 | } 44 | 45 | // push back, overrun (oldest) item if no room left 46 | void push_back(T &&item) { 47 | if (max_items_ > 0) { 48 | v_[tail_] = std::move(item); 49 | tail_ = (tail_ + 1) % max_items_; 50 | 51 | if (tail_ == head_) // overrun last item if full 52 | { 53 | head_ = (head_ + 1) % max_items_; 54 | ++overrun_counter_; 55 | } 56 | } 57 | } 58 | 59 | // Return reference to the front item. 60 | // If there are no elements in the container, the behavior is undefined. 61 | const T &front() const { return v_[head_]; } 62 | 63 | T &front() { return v_[head_]; } 64 | 65 | // Return number of elements actually stored 66 | size_t size() const { 67 | if (tail_ >= head_) { 68 | return tail_ - head_; 69 | } else { 70 | return max_items_ - (head_ - tail_); 71 | } 72 | } 73 | 74 | // Return const reference to item by index. 75 | // If index is out of range 0…size()-1, the behavior is undefined. 76 | const T &at(size_t i) const { 77 | assert(i < size()); 78 | return v_[(head_ + i) % max_items_]; 79 | } 80 | 81 | // Pop item from front. 82 | // If there are no elements in the container, the behavior is undefined. 83 | void pop_front() { head_ = (head_ + 1) % max_items_; } 84 | 85 | bool empty() const { return tail_ == head_; } 86 | 87 | bool full() const { 88 | // head is ahead of the tail by 1 89 | if (max_items_ > 0) { 90 | return ((tail_ + 1) % max_items_) == head_; 91 | } 92 | return false; 93 | } 94 | 95 | size_t overrun_counter() const { return overrun_counter_; } 96 | 97 | void reset_overrun_counter() { overrun_counter_ = 0; } 98 | 99 | private: 100 | // copy from other&& and reset it to disabled state 101 | void copy_moveable(circular_q &&other) SPDLOG_NOEXCEPT { 102 | max_items_ = other.max_items_; 103 | head_ = other.head_; 104 | tail_ = other.tail_; 105 | overrun_counter_ = other.overrun_counter_; 106 | v_ = std::move(other.v_); 107 | 108 | // put &&other in disabled, but valid state 109 | other.max_items_ = 0; 110 | other.head_ = other.tail_ = 0; 111 | other.overrun_counter_ = 0; 112 | } 113 | }; 114 | } // namespace details 115 | } // namespace spdlog 116 | -------------------------------------------------------------------------------- /include/spdlog/details/console_globals.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | namespace spdlog { 10 | namespace details { 11 | 12 | struct console_mutex { 13 | using mutex_t = std::mutex; 14 | static mutex_t &mutex() { 15 | static mutex_t s_mutex; 16 | return s_mutex; 17 | } 18 | }; 19 | 20 | struct console_nullmutex { 21 | using mutex_t = null_mutex; 22 | static mutex_t &mutex() { 23 | static mutex_t s_mutex; 24 | return s_mutex; 25 | } 26 | }; 27 | } // namespace details 28 | } // namespace spdlog 29 | -------------------------------------------------------------------------------- /include/spdlog/details/file_helper.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | namespace spdlog { 10 | namespace details { 11 | 12 | // Helper class for file sinks. 13 | // When failing to open a file, retry several times(5) with a delay interval(10 ms). 14 | // Throw spdlog_ex exception on errors. 15 | 16 | class SPDLOG_API file_helper { 17 | public: 18 | file_helper() = default; 19 | explicit file_helper(const file_event_handlers &event_handlers); 20 | 21 | file_helper(const file_helper &) = delete; 22 | file_helper &operator=(const file_helper &) = delete; 23 | ~file_helper(); 24 | 25 | void open(const filename_t &fname, bool truncate = false); 26 | void reopen(bool truncate); 27 | void flush(); 28 | void sync(); 29 | void close(); 30 | void write(const memory_buf_t &buf); 31 | size_t size() const; 32 | const filename_t &filename() const; 33 | 34 | // 35 | // return file path and its extension: 36 | // 37 | // "mylog.txt" => ("mylog", ".txt") 38 | // "mylog" => ("mylog", "") 39 | // "mylog." => ("mylog.", "") 40 | // "/dir1/dir2/mylog.txt" => ("/dir1/dir2/mylog", ".txt") 41 | // 42 | // the starting dot in filenames is ignored (hidden files): 43 | // 44 | // ".mylog" => (".mylog". "") 45 | // "my_folder/.mylog" => ("my_folder/.mylog", "") 46 | // "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt") 47 | static std::tuple split_by_extension(const filename_t &fname); 48 | 49 | private: 50 | const int open_tries_ = 5; 51 | const unsigned int open_interval_ = 10; 52 | std::FILE *fd_{nullptr}; 53 | filename_t filename_; 54 | file_event_handlers event_handlers_; 55 | }; 56 | } // namespace details 57 | } // namespace spdlog 58 | 59 | #ifdef SPDLOG_HEADER_ONLY 60 | #include "file_helper-inl.h" 61 | #endif 62 | -------------------------------------------------------------------------------- /include/spdlog/details/log_msg-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | #include 11 | 12 | namespace spdlog { 13 | namespace details { 14 | 15 | SPDLOG_INLINE log_msg::log_msg(spdlog::log_clock::time_point log_time, 16 | spdlog::source_loc loc, 17 | string_view_t a_logger_name, 18 | spdlog::level::level_enum lvl, 19 | spdlog::string_view_t msg) 20 | : logger_name(a_logger_name), 21 | level(lvl), 22 | time(log_time) 23 | #ifndef SPDLOG_NO_THREAD_ID 24 | , 25 | thread_id(os::thread_id()) 26 | #endif 27 | , 28 | source(loc), 29 | payload(msg) { 30 | } 31 | 32 | SPDLOG_INLINE log_msg::log_msg(spdlog::source_loc loc, 33 | string_view_t a_logger_name, 34 | spdlog::level::level_enum lvl, 35 | spdlog::string_view_t msg) 36 | : log_msg(os::now(), loc, a_logger_name, lvl, msg) {} 37 | 38 | SPDLOG_INLINE log_msg::log_msg(string_view_t a_logger_name, 39 | spdlog::level::level_enum lvl, 40 | spdlog::string_view_t msg) 41 | : log_msg(os::now(), source_loc{}, a_logger_name, lvl, msg) {} 42 | 43 | } // namespace details 44 | } // namespace spdlog 45 | -------------------------------------------------------------------------------- /include/spdlog/details/log_msg.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | namespace spdlog { 10 | namespace details { 11 | struct SPDLOG_API log_msg { 12 | log_msg() = default; 13 | log_msg(log_clock::time_point log_time, 14 | source_loc loc, 15 | string_view_t logger_name, 16 | level::level_enum lvl, 17 | string_view_t msg); 18 | log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg); 19 | log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg); 20 | log_msg(const log_msg &other) = default; 21 | log_msg &operator=(const log_msg &other) = default; 22 | 23 | string_view_t logger_name; 24 | level::level_enum level{level::off}; 25 | log_clock::time_point time; 26 | size_t thread_id{0}; 27 | 28 | // wrapping the formatted text with color (updated by pattern_formatter). 29 | mutable size_t color_range_start{0}; 30 | mutable size_t color_range_end{0}; 31 | 32 | source_loc source; 33 | string_view_t payload; 34 | }; 35 | } // namespace details 36 | } // namespace spdlog 37 | 38 | #ifdef SPDLOG_HEADER_ONLY 39 | #include "log_msg-inl.h" 40 | #endif 41 | -------------------------------------------------------------------------------- /include/spdlog/details/log_msg_buffer-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | namespace spdlog { 11 | namespace details { 12 | 13 | SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg &orig_msg) 14 | : log_msg{orig_msg} { 15 | buffer.append(logger_name.begin(), logger_name.end()); 16 | buffer.append(payload.begin(), payload.end()); 17 | update_string_views(); 18 | } 19 | 20 | SPDLOG_INLINE log_msg_buffer::log_msg_buffer(const log_msg_buffer &other) 21 | : log_msg{other} { 22 | buffer.append(logger_name.begin(), logger_name.end()); 23 | buffer.append(payload.begin(), payload.end()); 24 | update_string_views(); 25 | } 26 | 27 | SPDLOG_INLINE log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT 28 | : log_msg{other}, 29 | buffer{std::move(other.buffer)} { 30 | update_string_views(); 31 | } 32 | 33 | SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &other) { 34 | log_msg::operator=(other); 35 | buffer.clear(); 36 | buffer.append(other.buffer.data(), other.buffer.data() + other.buffer.size()); 37 | update_string_views(); 38 | return *this; 39 | } 40 | 41 | SPDLOG_INLINE log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT { 42 | log_msg::operator=(other); 43 | buffer = std::move(other.buffer); 44 | update_string_views(); 45 | return *this; 46 | } 47 | 48 | SPDLOG_INLINE void log_msg_buffer::update_string_views() { 49 | logger_name = string_view_t{buffer.data(), logger_name.size()}; 50 | payload = string_view_t{buffer.data() + logger_name.size(), payload.size()}; 51 | } 52 | 53 | } // namespace details 54 | } // namespace spdlog 55 | -------------------------------------------------------------------------------- /include/spdlog/details/log_msg_buffer.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace spdlog { 9 | namespace details { 10 | 11 | // Extend log_msg with internal buffer to store its payload. 12 | // This is needed since log_msg holds string_views that points to stack data. 13 | 14 | class SPDLOG_API log_msg_buffer : public log_msg { 15 | memory_buf_t buffer; 16 | void update_string_views(); 17 | 18 | public: 19 | log_msg_buffer() = default; 20 | explicit log_msg_buffer(const log_msg &orig_msg); 21 | log_msg_buffer(const log_msg_buffer &other); 22 | log_msg_buffer(log_msg_buffer &&other) SPDLOG_NOEXCEPT; 23 | log_msg_buffer &operator=(const log_msg_buffer &other); 24 | log_msg_buffer &operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT; 25 | }; 26 | 27 | } // namespace details 28 | } // namespace spdlog 29 | 30 | #ifdef SPDLOG_HEADER_ONLY 31 | #include "log_msg_buffer-inl.h" 32 | #endif 33 | -------------------------------------------------------------------------------- /include/spdlog/details/null_mutex.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | // null, no cost dummy "mutex" and dummy "atomic" int 9 | 10 | namespace spdlog { 11 | namespace details { 12 | struct null_mutex { 13 | void lock() const {} 14 | void unlock() const {} 15 | }; 16 | 17 | struct null_atomic_int { 18 | int value; 19 | null_atomic_int() = default; 20 | 21 | explicit null_atomic_int(int new_value) 22 | : value(new_value) {} 23 | 24 | int load(std::memory_order = std::memory_order_relaxed) const { return value; } 25 | 26 | void store(int new_value, std::memory_order = std::memory_order_relaxed) { value = new_value; } 27 | 28 | int exchange(int new_value, std::memory_order = std::memory_order_relaxed) { 29 | std::swap(new_value, value); 30 | return new_value; // return value before the call 31 | } 32 | }; 33 | 34 | } // namespace details 35 | } // namespace spdlog 36 | -------------------------------------------------------------------------------- /include/spdlog/details/os.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include // std::time_t 7 | #include 8 | 9 | namespace spdlog { 10 | namespace details { 11 | namespace os { 12 | 13 | SPDLOG_API spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT; 14 | 15 | SPDLOG_API std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT; 16 | 17 | SPDLOG_API std::tm localtime() SPDLOG_NOEXCEPT; 18 | 19 | SPDLOG_API std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT; 20 | 21 | SPDLOG_API std::tm gmtime() SPDLOG_NOEXCEPT; 22 | 23 | // eol definition 24 | #if !defined(SPDLOG_EOL) 25 | #ifdef _WIN32 26 | #define SPDLOG_EOL "\r\n" 27 | #else 28 | #define SPDLOG_EOL "\n" 29 | #endif 30 | #endif 31 | 32 | SPDLOG_CONSTEXPR static const char *default_eol = SPDLOG_EOL; 33 | 34 | // folder separator 35 | #if !defined(SPDLOG_FOLDER_SEPS) 36 | #ifdef _WIN32 37 | #define SPDLOG_FOLDER_SEPS "\\/" 38 | #else 39 | #define SPDLOG_FOLDER_SEPS "/" 40 | #endif 41 | #endif 42 | 43 | SPDLOG_CONSTEXPR static const char folder_seps[] = SPDLOG_FOLDER_SEPS; 44 | SPDLOG_CONSTEXPR static const filename_t::value_type folder_seps_filename[] = 45 | SPDLOG_FILENAME_T(SPDLOG_FOLDER_SEPS); 46 | 47 | // fopen_s on non windows for writing 48 | SPDLOG_API bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mode); 49 | 50 | // Remove filename. return 0 on success 51 | SPDLOG_API int remove(const filename_t &filename) SPDLOG_NOEXCEPT; 52 | 53 | // Remove file if exists. return 0 on success 54 | // Note: Non atomic (might return failure to delete if concurrently deleted by other process/thread) 55 | SPDLOG_API int remove_if_exists(const filename_t &filename) SPDLOG_NOEXCEPT; 56 | 57 | SPDLOG_API int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT; 58 | 59 | // Return if file exists. 60 | SPDLOG_API bool path_exists(const filename_t &filename) SPDLOG_NOEXCEPT; 61 | 62 | // Return file size according to open FILE* object 63 | SPDLOG_API size_t filesize(FILE *f); 64 | 65 | // Return utc offset in minutes or throw spdlog_ex on failure 66 | SPDLOG_API int utc_minutes_offset(const std::tm &tm = details::os::localtime()); 67 | 68 | // Return current thread id as size_t 69 | // It exists because the std::this_thread::get_id() is much slower(especially 70 | // under VS 2013) 71 | SPDLOG_API size_t _thread_id() SPDLOG_NOEXCEPT; 72 | 73 | // Return current thread id as size_t (from thread local storage) 74 | SPDLOG_API size_t thread_id() SPDLOG_NOEXCEPT; 75 | 76 | // This is avoid msvc issue in sleep_for that happens if the clock changes. 77 | // See https://github.com/gabime/spdlog/issues/609 78 | SPDLOG_API void sleep_for_millis(unsigned int milliseconds) SPDLOG_NOEXCEPT; 79 | 80 | SPDLOG_API std::string filename_to_str(const filename_t &filename); 81 | 82 | SPDLOG_API int pid() SPDLOG_NOEXCEPT; 83 | 84 | // Determine if the terminal supports colors 85 | // Source: https://github.com/agauniyal/rang/ 86 | SPDLOG_API bool is_color_terminal() SPDLOG_NOEXCEPT; 87 | 88 | // Determine if the terminal attached 89 | // Source: https://github.com/agauniyal/rang/ 90 | SPDLOG_API bool in_terminal(FILE *file) SPDLOG_NOEXCEPT; 91 | 92 | #if (defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) || defined(SPDLOG_WCHAR_FILENAMES)) && defined(_WIN32) 93 | SPDLOG_API void wstr_to_utf8buf(wstring_view_t wstr, memory_buf_t &target); 94 | 95 | SPDLOG_API void utf8_to_wstrbuf(string_view_t str, wmemory_buf_t &target); 96 | #endif 97 | 98 | // Return directory name from given path or empty string 99 | // "abc/file" => "abc" 100 | // "abc/" => "abc" 101 | // "abc" => "" 102 | // "abc///" => "abc//" 103 | SPDLOG_API filename_t dir_name(const filename_t &path); 104 | 105 | // Create a dir from the given path. 106 | // Return true if succeeded or if this dir already exists. 107 | SPDLOG_API bool create_dir(const filename_t &path); 108 | 109 | // non thread safe, cross platform getenv/getenv_s 110 | // return empty string if field not found 111 | SPDLOG_API std::string getenv(const char *field); 112 | 113 | // Do fsync by FILE objectpointer. 114 | // Return true on success. 115 | SPDLOG_API bool fsync(FILE *fp); 116 | 117 | // Do non-locking fwrite if possible by the os or use the regular locking fwrite 118 | // Return true on success. 119 | SPDLOG_API bool fwrite_bytes(const void *ptr, const size_t n_bytes, FILE *fp); 120 | 121 | } // namespace os 122 | } // namespace details 123 | } // namespace spdlog 124 | 125 | #ifdef SPDLOG_HEADER_ONLY 126 | #include "os-inl.h" 127 | #endif 128 | -------------------------------------------------------------------------------- /include/spdlog/details/periodic_worker-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | namespace spdlog { 11 | namespace details { 12 | 13 | // stop the worker thread and join it 14 | SPDLOG_INLINE periodic_worker::~periodic_worker() { 15 | if (worker_thread_.joinable()) { 16 | { 17 | std::lock_guard lock(mutex_); 18 | active_ = false; 19 | } 20 | cv_.notify_one(); 21 | worker_thread_.join(); 22 | } 23 | } 24 | 25 | } // namespace details 26 | } // namespace spdlog 27 | -------------------------------------------------------------------------------- /include/spdlog/details/periodic_worker.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | // periodic worker thread - periodically executes the given callback function. 7 | // 8 | // RAII over the owned thread: 9 | // creates the thread on construction. 10 | // stops and joins the thread on destruction (if the thread is executing a callback, wait for it 11 | // to finish first). 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | namespace spdlog { 19 | namespace details { 20 | 21 | class SPDLOG_API periodic_worker { 22 | public: 23 | template 24 | periodic_worker(const std::function &callback_fun, 25 | std::chrono::duration interval) { 26 | active_ = (interval > std::chrono::duration::zero()); 27 | if (!active_) { 28 | return; 29 | } 30 | 31 | worker_thread_ = std::thread([this, callback_fun, interval]() { 32 | for (;;) { 33 | std::unique_lock lock(this->mutex_); 34 | if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; })) { 35 | return; // active_ == false, so exit this thread 36 | } 37 | callback_fun(); 38 | } 39 | }); 40 | } 41 | std::thread &get_thread() { return worker_thread_; } 42 | periodic_worker(const periodic_worker &) = delete; 43 | periodic_worker &operator=(const periodic_worker &) = delete; 44 | // stop the worker thread and join it 45 | ~periodic_worker(); 46 | 47 | private: 48 | bool active_; 49 | std::thread worker_thread_; 50 | std::mutex mutex_; 51 | std::condition_variable cv_; 52 | }; 53 | } // namespace details 54 | } // namespace spdlog 55 | 56 | #ifdef SPDLOG_HEADER_ONLY 57 | #include "periodic_worker-inl.h" 58 | #endif 59 | -------------------------------------------------------------------------------- /include/spdlog/details/synchronous_factory.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include "registry.h" 7 | 8 | namespace spdlog { 9 | 10 | // Default logger factory- creates synchronous loggers 11 | class logger; 12 | 13 | struct synchronous_factory { 14 | template 15 | static std::shared_ptr create(std::string logger_name, SinkArgs &&...args) { 16 | auto sink = std::make_shared(std::forward(args)...); 17 | auto new_logger = std::make_shared(std::move(logger_name), std::move(sink)); 18 | details::registry::instance().initialize_logger(new_logger); 19 | return new_logger; 20 | } 21 | }; 22 | } // namespace spdlog 23 | -------------------------------------------------------------------------------- /include/spdlog/details/tcp_client.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifdef _WIN32 7 | #error include tcp_client-windows.h instead 8 | #endif 9 | 10 | // tcp client helper 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | namespace spdlog { 24 | namespace details { 25 | class tcp_client { 26 | int socket_ = -1; 27 | 28 | public: 29 | bool is_connected() const { return socket_ != -1; } 30 | 31 | void close() { 32 | if (is_connected()) { 33 | ::close(socket_); 34 | socket_ = -1; 35 | } 36 | } 37 | 38 | int fd() const { return socket_; } 39 | 40 | ~tcp_client() { close(); } 41 | 42 | // try to connect or throw on failure 43 | void connect(const std::string &host, int port) { 44 | close(); 45 | struct addrinfo hints {}; 46 | memset(&hints, 0, sizeof(struct addrinfo)); 47 | hints.ai_family = AF_UNSPEC; // To work with IPv4, IPv6, and so on 48 | hints.ai_socktype = SOCK_STREAM; // TCP 49 | hints.ai_flags = AI_NUMERICSERV; // port passed as as numeric value 50 | hints.ai_protocol = 0; 51 | 52 | auto port_str = std::to_string(port); 53 | struct addrinfo *addrinfo_result; 54 | auto rv = ::getaddrinfo(host.c_str(), port_str.c_str(), &hints, &addrinfo_result); 55 | if (rv != 0) { 56 | throw_spdlog_ex(fmt_lib::format("::getaddrinfo failed: {}", gai_strerror(rv))); 57 | } 58 | 59 | // Try each address until we successfully connect(2). 60 | int last_errno = 0; 61 | for (auto *rp = addrinfo_result; rp != nullptr; rp = rp->ai_next) { 62 | #if defined(SOCK_CLOEXEC) 63 | const int flags = SOCK_CLOEXEC; 64 | #else 65 | const int flags = 0; 66 | #endif 67 | socket_ = ::socket(rp->ai_family, rp->ai_socktype | flags, rp->ai_protocol); 68 | if (socket_ == -1) { 69 | last_errno = errno; 70 | continue; 71 | } 72 | rv = ::connect(socket_, rp->ai_addr, rp->ai_addrlen); 73 | if (rv == 0) { 74 | break; 75 | } 76 | last_errno = errno; 77 | ::close(socket_); 78 | socket_ = -1; 79 | } 80 | ::freeaddrinfo(addrinfo_result); 81 | if (socket_ == -1) { 82 | throw_spdlog_ex("::connect failed", last_errno); 83 | } 84 | 85 | // set TCP_NODELAY 86 | int enable_flag = 1; 87 | ::setsockopt(socket_, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast(&enable_flag), 88 | sizeof(enable_flag)); 89 | 90 | // prevent sigpipe on systems where MSG_NOSIGNAL is not available 91 | #if defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL) 92 | ::setsockopt(socket_, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast(&enable_flag), 93 | sizeof(enable_flag)); 94 | #endif 95 | 96 | #if !defined(SO_NOSIGPIPE) && !defined(MSG_NOSIGNAL) 97 | #error "tcp_sink would raise SIGPIPE since neither SO_NOSIGPIPE nor MSG_NOSIGNAL are available" 98 | #endif 99 | } 100 | 101 | // Send exactly n_bytes of the given data. 102 | // On error close the connection and throw. 103 | void send(const char *data, size_t n_bytes) { 104 | size_t bytes_sent = 0; 105 | while (bytes_sent < n_bytes) { 106 | #if defined(MSG_NOSIGNAL) 107 | const int send_flags = MSG_NOSIGNAL; 108 | #else 109 | const int send_flags = 0; 110 | #endif 111 | auto write_result = 112 | ::send(socket_, data + bytes_sent, n_bytes - bytes_sent, send_flags); 113 | if (write_result < 0) { 114 | close(); 115 | throw_spdlog_ex("write(2) failed", errno); 116 | } 117 | 118 | if (write_result == 0) // (probably should not happen but in any case..) 119 | { 120 | break; 121 | } 122 | bytes_sent += static_cast(write_result); 123 | } 124 | } 125 | }; 126 | } // namespace details 127 | } // namespace spdlog 128 | -------------------------------------------------------------------------------- /include/spdlog/details/thread_pool.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | namespace spdlog { 17 | class async_logger; 18 | 19 | namespace details { 20 | 21 | using async_logger_ptr = std::shared_ptr; 22 | 23 | enum class async_msg_type { log, flush, terminate }; 24 | 25 | // Async msg to move to/from the queue 26 | // Movable only. should never be copied 27 | struct async_msg : log_msg_buffer { 28 | async_msg_type msg_type{async_msg_type::log}; 29 | async_logger_ptr worker_ptr; 30 | 31 | async_msg() = default; 32 | ~async_msg() = default; 33 | 34 | // should only be moved in or out of the queue.. 35 | async_msg(const async_msg &) = delete; 36 | 37 | // support for vs2013 move 38 | #if defined(_MSC_VER) && _MSC_VER <= 1800 39 | async_msg(async_msg &&other) 40 | : log_msg_buffer(std::move(other)), 41 | msg_type(other.msg_type), 42 | worker_ptr(std::move(other.worker_ptr)) {} 43 | 44 | async_msg &operator=(async_msg &&other) { 45 | *static_cast(this) = std::move(other); 46 | msg_type = other.msg_type; 47 | worker_ptr = std::move(other.worker_ptr); 48 | return *this; 49 | } 50 | #else // (_MSC_VER) && _MSC_VER <= 1800 51 | async_msg(async_msg &&) = default; 52 | async_msg &operator=(async_msg &&) = default; 53 | #endif 54 | 55 | // construct from log_msg with given type 56 | async_msg(async_logger_ptr &&worker, async_msg_type the_type, const details::log_msg &m) 57 | : log_msg_buffer{m}, 58 | msg_type{the_type}, 59 | worker_ptr{std::move(worker)} {} 60 | 61 | async_msg(async_logger_ptr &&worker, async_msg_type the_type) 62 | : log_msg_buffer{}, 63 | msg_type{the_type}, 64 | worker_ptr{std::move(worker)} {} 65 | 66 | explicit async_msg(async_msg_type the_type) 67 | : async_msg{nullptr, the_type} {} 68 | }; 69 | 70 | class SPDLOG_API thread_pool { 71 | public: 72 | using item_type = async_msg; 73 | using q_type = details::mpmc_blocking_queue; 74 | 75 | thread_pool(size_t q_max_items, 76 | size_t threads_n, 77 | std::function on_thread_start, 78 | std::function on_thread_stop); 79 | thread_pool(size_t q_max_items, size_t threads_n, std::function on_thread_start); 80 | thread_pool(size_t q_max_items, size_t threads_n); 81 | 82 | // message all threads to terminate gracefully and join them 83 | ~thread_pool(); 84 | 85 | thread_pool(const thread_pool &) = delete; 86 | thread_pool &operator=(thread_pool &&) = delete; 87 | 88 | void post_log(async_logger_ptr &&worker_ptr, 89 | const details::log_msg &msg, 90 | async_overflow_policy overflow_policy); 91 | void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy); 92 | size_t overrun_counter(); 93 | void reset_overrun_counter(); 94 | size_t discard_counter(); 95 | void reset_discard_counter(); 96 | size_t queue_size(); 97 | 98 | private: 99 | q_type q_; 100 | 101 | std::vector threads_; 102 | 103 | void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy); 104 | void worker_loop_(); 105 | 106 | // process next message in the queue 107 | // return true if this thread should still be active (while no terminate msg 108 | // was received) 109 | bool process_next_msg_(); 110 | }; 111 | 112 | } // namespace details 113 | } // namespace spdlog 114 | 115 | #ifdef SPDLOG_HEADER_ONLY 116 | #include "thread_pool-inl.h" 117 | #endif 118 | -------------------------------------------------------------------------------- /include/spdlog/details/udp_client-windows.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | // Helper RAII over winsock udp client socket. 7 | // Will throw on construction if socket creation failed. 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #if defined(_MSC_VER) 19 | #pragma comment(lib, "Ws2_32.lib") 20 | #pragma comment(lib, "Mswsock.lib") 21 | #pragma comment(lib, "AdvApi32.lib") 22 | #endif 23 | 24 | namespace spdlog { 25 | namespace details { 26 | class udp_client { 27 | static constexpr int TX_BUFFER_SIZE = 1024 * 10; 28 | SOCKET socket_ = INVALID_SOCKET; 29 | sockaddr_in addr_ = {}; 30 | 31 | static void init_winsock_() { 32 | WSADATA wsaData; 33 | auto rv = ::WSAStartup(MAKEWORD(2, 2), &wsaData); 34 | if (rv != 0) { 35 | throw_winsock_error_("WSAStartup failed", ::WSAGetLastError()); 36 | } 37 | } 38 | 39 | static void throw_winsock_error_(const std::string &msg, int last_error) { 40 | char buf[512]; 41 | ::FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, 42 | last_error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 43 | (sizeof(buf) / sizeof(char)), NULL); 44 | 45 | throw_spdlog_ex(fmt_lib::format("udp_sink - {}: {}", msg, buf)); 46 | } 47 | 48 | void cleanup_() { 49 | if (socket_ != INVALID_SOCKET) { 50 | ::closesocket(socket_); 51 | } 52 | socket_ = INVALID_SOCKET; 53 | ::WSACleanup(); 54 | } 55 | 56 | public: 57 | udp_client(const std::string &host, uint16_t port) { 58 | init_winsock_(); 59 | 60 | addr_.sin_family = PF_INET; 61 | addr_.sin_port = htons(port); 62 | addr_.sin_addr.s_addr = INADDR_ANY; 63 | if (InetPtonA(PF_INET, host.c_str(), &addr_.sin_addr.s_addr) != 1) { 64 | int last_error = ::WSAGetLastError(); 65 | ::WSACleanup(); 66 | throw_winsock_error_("error: Invalid address!", last_error); 67 | } 68 | 69 | socket_ = ::socket(PF_INET, SOCK_DGRAM, 0); 70 | if (socket_ == INVALID_SOCKET) { 71 | int last_error = ::WSAGetLastError(); 72 | ::WSACleanup(); 73 | throw_winsock_error_("error: Create Socket failed", last_error); 74 | } 75 | 76 | int option_value = TX_BUFFER_SIZE; 77 | if (::setsockopt(socket_, SOL_SOCKET, SO_SNDBUF, 78 | reinterpret_cast(&option_value), sizeof(option_value)) < 0) { 79 | int last_error = ::WSAGetLastError(); 80 | cleanup_(); 81 | throw_winsock_error_("error: setsockopt(SO_SNDBUF) Failed!", last_error); 82 | } 83 | } 84 | 85 | ~udp_client() { cleanup_(); } 86 | 87 | SOCKET fd() const { return socket_; } 88 | 89 | void send(const char *data, size_t n_bytes) { 90 | socklen_t tolen = sizeof(struct sockaddr); 91 | if (::sendto(socket_, data, static_cast(n_bytes), 0, (struct sockaddr *)&addr_, 92 | tolen) == -1) { 93 | throw_spdlog_ex("sendto(2) failed", errno); 94 | } 95 | } 96 | }; 97 | } // namespace details 98 | } // namespace spdlog 99 | -------------------------------------------------------------------------------- /include/spdlog/details/udp_client.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | // Helper RAII over unix udp client socket. 7 | // Will throw on construction if the socket creation failed. 8 | 9 | #ifdef _WIN32 10 | #error "include udp_client-windows.h instead" 11 | #endif 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | 25 | namespace spdlog { 26 | namespace details { 27 | 28 | class udp_client { 29 | static constexpr int TX_BUFFER_SIZE = 1024 * 10; 30 | int socket_ = -1; 31 | struct sockaddr_in sockAddr_; 32 | 33 | void cleanup_() { 34 | if (socket_ != -1) { 35 | ::close(socket_); 36 | socket_ = -1; 37 | } 38 | } 39 | 40 | public: 41 | udp_client(const std::string &host, uint16_t port) { 42 | socket_ = ::socket(PF_INET, SOCK_DGRAM, 0); 43 | if (socket_ < 0) { 44 | throw_spdlog_ex("error: Create Socket Failed!"); 45 | } 46 | 47 | int option_value = TX_BUFFER_SIZE; 48 | if (::setsockopt(socket_, SOL_SOCKET, SO_SNDBUF, 49 | reinterpret_cast(&option_value), sizeof(option_value)) < 0) { 50 | cleanup_(); 51 | throw_spdlog_ex("error: setsockopt(SO_SNDBUF) Failed!"); 52 | } 53 | 54 | sockAddr_.sin_family = AF_INET; 55 | sockAddr_.sin_port = htons(port); 56 | 57 | if (::inet_aton(host.c_str(), &sockAddr_.sin_addr) == 0) { 58 | cleanup_(); 59 | throw_spdlog_ex("error: Invalid address!"); 60 | } 61 | 62 | ::memset(sockAddr_.sin_zero, 0x00, sizeof(sockAddr_.sin_zero)); 63 | } 64 | 65 | ~udp_client() { cleanup_(); } 66 | 67 | int fd() const { return socket_; } 68 | 69 | // Send exactly n_bytes of the given data. 70 | // On error close the connection and throw. 71 | void send(const char *data, size_t n_bytes) { 72 | ssize_t toslen = 0; 73 | socklen_t tolen = sizeof(struct sockaddr); 74 | if ((toslen = ::sendto(socket_, data, n_bytes, 0, (struct sockaddr *)&sockAddr_, tolen)) == 75 | -1) { 76 | throw_spdlog_ex("sendto(2) failed", errno); 77 | } 78 | } 79 | }; 80 | } // namespace details 81 | } // namespace spdlog 82 | -------------------------------------------------------------------------------- /include/spdlog/details/windows_include.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef NOMINMAX 4 | #define NOMINMAX // prevent windows redefining min/max 5 | #endif 6 | 7 | #ifndef WIN32_LEAN_AND_MEAN 8 | #define WIN32_LEAN_AND_MEAN 9 | #endif 10 | 11 | #include 12 | -------------------------------------------------------------------------------- /include/spdlog/fmt/bundled/core.h: -------------------------------------------------------------------------------- 1 | // This file is only provided for compatibility and may be removed in future 2 | // versions. Use fmt/base.h if you don't need fmt::format and fmt/format.h 3 | // otherwise. 4 | 5 | #include "format.h" 6 | -------------------------------------------------------------------------------- /include/spdlog/fmt/bundled/fmt.license.rst: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | --- Optional exception to the license --- 23 | 24 | As an exception, if, as a result of your compiling your source code, portions 25 | of this Software are embedded into a machine-executable object form of such 26 | source code, you may redistribute such embedded portions in such object form 27 | without including the above copyright and permission notices. 28 | -------------------------------------------------------------------------------- /include/spdlog/fmt/chrono.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | // 8 | // include bundled or external copy of fmtlib's chrono support 9 | // 10 | #include 11 | 12 | #if !defined(SPDLOG_USE_STD_FORMAT) 13 | #if !defined(SPDLOG_FMT_EXTERNAL) 14 | #ifdef SPDLOG_HEADER_ONLY 15 | #ifndef FMT_HEADER_ONLY 16 | #define FMT_HEADER_ONLY 17 | #endif 18 | #endif 19 | #include 20 | #else 21 | #include 22 | #endif 23 | #endif 24 | -------------------------------------------------------------------------------- /include/spdlog/fmt/compile.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | // 8 | // include bundled or external copy of fmtlib's compile-time support 9 | // 10 | #include 11 | 12 | #if !defined(SPDLOG_USE_STD_FORMAT) 13 | #if !defined(SPDLOG_FMT_EXTERNAL) 14 | #ifdef SPDLOG_HEADER_ONLY 15 | #ifndef FMT_HEADER_ONLY 16 | #define FMT_HEADER_ONLY 17 | #endif 18 | #endif 19 | #include 20 | #else 21 | #include 22 | #endif 23 | #endif 24 | -------------------------------------------------------------------------------- /include/spdlog/fmt/fmt.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016-2018 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | // 9 | // Include a bundled header-only copy of fmtlib or an external one. 10 | // By default spdlog include its own copy. 11 | // 12 | #include 13 | 14 | #if defined(SPDLOG_USE_STD_FORMAT) // SPDLOG_USE_STD_FORMAT is defined - use std::format 15 | #include 16 | #elif !defined(SPDLOG_FMT_EXTERNAL) 17 | #if !defined(SPDLOG_COMPILED_LIB) && !defined(FMT_HEADER_ONLY) 18 | #define FMT_HEADER_ONLY 19 | #endif 20 | #ifndef FMT_USE_WINDOWS_H 21 | #define FMT_USE_WINDOWS_H 0 22 | #endif 23 | 24 | #include 25 | #include 26 | 27 | #else // SPDLOG_FMT_EXTERNAL is defined - use external fmtlib 28 | #include 29 | #include 30 | #endif 31 | -------------------------------------------------------------------------------- /include/spdlog/fmt/ostr.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | // 8 | // include bundled or external copy of fmtlib's ostream support 9 | // 10 | #include 11 | 12 | #if !defined(SPDLOG_USE_STD_FORMAT) 13 | #if !defined(SPDLOG_FMT_EXTERNAL) 14 | #ifdef SPDLOG_HEADER_ONLY 15 | #ifndef FMT_HEADER_ONLY 16 | #define FMT_HEADER_ONLY 17 | #endif 18 | #endif 19 | #include 20 | #else 21 | #include 22 | #endif 23 | #endif 24 | -------------------------------------------------------------------------------- /include/spdlog/fmt/ranges.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | // 8 | // include bundled or external copy of fmtlib's ranges support 9 | // 10 | #include 11 | 12 | #if !defined(SPDLOG_USE_STD_FORMAT) 13 | #if !defined(SPDLOG_FMT_EXTERNAL) 14 | #ifdef SPDLOG_HEADER_ONLY 15 | #ifndef FMT_HEADER_ONLY 16 | #define FMT_HEADER_ONLY 17 | #endif 18 | #endif 19 | #include 20 | #else 21 | #include 22 | #endif 23 | #endif 24 | -------------------------------------------------------------------------------- /include/spdlog/fmt/std.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | // 8 | // include bundled or external copy of fmtlib's std support (for formatting e.g. 9 | // std::filesystem::path, std::thread::id, std::monostate, std::variant, ...) 10 | // 11 | #include 12 | 13 | #if !defined(SPDLOG_USE_STD_FORMAT) 14 | #if !defined(SPDLOG_FMT_EXTERNAL) 15 | #ifdef SPDLOG_HEADER_ONLY 16 | #ifndef FMT_HEADER_ONLY 17 | #define FMT_HEADER_ONLY 18 | #endif 19 | #endif 20 | #include 21 | #else 22 | #include 23 | #endif 24 | #endif 25 | -------------------------------------------------------------------------------- /include/spdlog/fmt/xchar.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2016 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | // 8 | // include bundled or external copy of fmtlib's xchar support 9 | // 10 | #include 11 | 12 | #if !defined(SPDLOG_USE_STD_FORMAT) 13 | #if !defined(SPDLOG_FMT_EXTERNAL) 14 | #ifdef SPDLOG_HEADER_ONLY 15 | #ifndef FMT_HEADER_ONLY 16 | #define FMT_HEADER_ONLY 17 | #endif 18 | #endif 19 | #include 20 | #else 21 | #include 22 | #endif 23 | #endif 24 | -------------------------------------------------------------------------------- /include/spdlog/formatter.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | namespace spdlog { 10 | 11 | class formatter { 12 | public: 13 | virtual ~formatter() = default; 14 | virtual void format(const details::log_msg &msg, memory_buf_t &dest) = 0; 15 | virtual std::unique_ptr clone() const = 0; 16 | }; 17 | } // namespace spdlog 18 | -------------------------------------------------------------------------------- /include/spdlog/fwd.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | namespace spdlog { 7 | class logger; 8 | class formatter; 9 | 10 | namespace sinks { 11 | class sink; 12 | } 13 | 14 | namespace level { 15 | enum level_enum : int; 16 | } 17 | 18 | } // namespace spdlog 19 | -------------------------------------------------------------------------------- /include/spdlog/mdc.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #if defined(SPDLOG_NO_TLS) 7 | #error "This header requires thread local storage support, but SPDLOG_NO_TLS is defined." 8 | #endif 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | // MDC is a simple map of key->string values stored in thread local storage whose content will be 16 | // printed by the loggers. Note: Not supported in async mode (thread local storage - so the async 17 | // thread pool have different copy). 18 | // 19 | // Usage example: 20 | // spdlog::mdc::put("mdc_key_1", "mdc_value_1"); 21 | // spdlog::info("Hello, {}", "World!"); // => [2024-04-26 02:08:05.040] [info] 22 | // [mdc_key_1:mdc_value_1] Hello, World! 23 | 24 | namespace spdlog { 25 | class SPDLOG_API mdc { 26 | public: 27 | using mdc_map_t = std::map; 28 | 29 | static void put(const std::string &key, const std::string &value) { 30 | get_context()[key] = value; 31 | } 32 | 33 | static std::string get(const std::string &key) { 34 | auto &context = get_context(); 35 | auto it = context.find(key); 36 | if (it != context.end()) { 37 | return it->second; 38 | } 39 | return ""; 40 | } 41 | 42 | static void remove(const std::string &key) { get_context().erase(key); } 43 | 44 | static void clear() { get_context().clear(); } 45 | 46 | static mdc_map_t &get_context() { 47 | static thread_local mdc_map_t context; 48 | return context; 49 | } 50 | }; 51 | 52 | } // namespace spdlog 53 | -------------------------------------------------------------------------------- /include/spdlog/pattern_formatter.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | namespace spdlog { 20 | namespace details { 21 | 22 | // padding information. 23 | struct padding_info { 24 | enum class pad_side { left, right, center }; 25 | 26 | padding_info() = default; 27 | padding_info(size_t width, padding_info::pad_side side, bool truncate) 28 | : width_(width), 29 | side_(side), 30 | truncate_(truncate), 31 | enabled_(true) {} 32 | 33 | bool enabled() const { return enabled_; } 34 | size_t width_ = 0; 35 | pad_side side_ = pad_side::left; 36 | bool truncate_ = false; 37 | bool enabled_ = false; 38 | }; 39 | 40 | class SPDLOG_API flag_formatter { 41 | public: 42 | explicit flag_formatter(padding_info padinfo) 43 | : padinfo_(padinfo) {} 44 | flag_formatter() = default; 45 | virtual ~flag_formatter() = default; 46 | virtual void format(const details::log_msg &msg, 47 | const std::tm &tm_time, 48 | memory_buf_t &dest) = 0; 49 | 50 | protected: 51 | padding_info padinfo_; 52 | }; 53 | 54 | } // namespace details 55 | 56 | class SPDLOG_API custom_flag_formatter : public details::flag_formatter { 57 | public: 58 | virtual std::unique_ptr clone() const = 0; 59 | 60 | void set_padding_info(const details::padding_info &padding) { 61 | flag_formatter::padinfo_ = padding; 62 | } 63 | }; 64 | 65 | class SPDLOG_API pattern_formatter final : public formatter { 66 | public: 67 | using custom_flags = std::unordered_map>; 68 | 69 | explicit pattern_formatter(std::string pattern, 70 | pattern_time_type time_type = pattern_time_type::local, 71 | std::string eol = spdlog::details::os::default_eol, 72 | custom_flags custom_user_flags = custom_flags()); 73 | 74 | // use default pattern is not given 75 | explicit pattern_formatter(pattern_time_type time_type = pattern_time_type::local, 76 | std::string eol = spdlog::details::os::default_eol); 77 | 78 | pattern_formatter(const pattern_formatter &other) = delete; 79 | pattern_formatter &operator=(const pattern_formatter &other) = delete; 80 | 81 | std::unique_ptr clone() const override; 82 | void format(const details::log_msg &msg, memory_buf_t &dest) override; 83 | 84 | template 85 | pattern_formatter &add_flag(char flag, Args &&...args) { 86 | custom_handlers_[flag] = details::make_unique(std::forward(args)...); 87 | return *this; 88 | } 89 | void set_pattern(std::string pattern); 90 | void need_localtime(bool need = true); 91 | 92 | private: 93 | std::string pattern_; 94 | std::string eol_; 95 | pattern_time_type pattern_time_type_; 96 | bool need_localtime_; 97 | std::tm cached_tm_; 98 | std::chrono::seconds last_log_secs_; 99 | std::vector> formatters_; 100 | custom_flags custom_handlers_; 101 | 102 | std::tm get_time_(const details::log_msg &msg); 103 | template 104 | void handle_flag_(char flag, details::padding_info padding); 105 | 106 | // Extract given pad spec (e.g. %8X) 107 | // Advance the given it pass the end of the padding spec found (if any) 108 | // Return padding. 109 | static details::padding_info handle_padspec_(std::string::const_iterator &it, 110 | std::string::const_iterator end); 111 | 112 | void compile_pattern_(const std::string &pattern); 113 | }; 114 | } // namespace spdlog 115 | 116 | #ifdef SPDLOG_HEADER_ONLY 117 | #include "pattern_formatter-inl.h" 118 | #endif 119 | -------------------------------------------------------------------------------- /include/spdlog/sinks/ansicolor_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | namespace spdlog { 15 | namespace sinks { 16 | 17 | /** 18 | * This sink prefixes the output with an ANSI escape sequence color code 19 | * depending on the severity 20 | * of the message. 21 | * If no color terminal detected, omit the escape codes. 22 | */ 23 | 24 | template 25 | class ansicolor_sink : public sink { 26 | public: 27 | using mutex_t = typename ConsoleMutex::mutex_t; 28 | ansicolor_sink(FILE *target_file, color_mode mode); 29 | ~ansicolor_sink() override = default; 30 | 31 | ansicolor_sink(const ansicolor_sink &other) = delete; 32 | ansicolor_sink(ansicolor_sink &&other) = delete; 33 | 34 | ansicolor_sink &operator=(const ansicolor_sink &other) = delete; 35 | ansicolor_sink &operator=(ansicolor_sink &&other) = delete; 36 | 37 | void set_color(level::level_enum color_level, string_view_t color); 38 | void set_color_mode(color_mode mode); 39 | bool should_color() const; 40 | 41 | void log(const details::log_msg &msg) override; 42 | void flush() override; 43 | void set_pattern(const std::string &pattern) final override; 44 | void set_formatter(std::unique_ptr sink_formatter) override; 45 | 46 | // Formatting codes 47 | const string_view_t reset = "\033[m"; 48 | const string_view_t bold = "\033[1m"; 49 | const string_view_t dark = "\033[2m"; 50 | const string_view_t underline = "\033[4m"; 51 | const string_view_t blink = "\033[5m"; 52 | const string_view_t reverse = "\033[7m"; 53 | const string_view_t concealed = "\033[8m"; 54 | const string_view_t clear_line = "\033[K"; 55 | 56 | // Foreground colors 57 | const string_view_t black = "\033[30m"; 58 | const string_view_t red = "\033[31m"; 59 | const string_view_t green = "\033[32m"; 60 | const string_view_t yellow = "\033[33m"; 61 | const string_view_t blue = "\033[34m"; 62 | const string_view_t magenta = "\033[35m"; 63 | const string_view_t cyan = "\033[36m"; 64 | const string_view_t white = "\033[37m"; 65 | 66 | /// Background colors 67 | const string_view_t on_black = "\033[40m"; 68 | const string_view_t on_red = "\033[41m"; 69 | const string_view_t on_green = "\033[42m"; 70 | const string_view_t on_yellow = "\033[43m"; 71 | const string_view_t on_blue = "\033[44m"; 72 | const string_view_t on_magenta = "\033[45m"; 73 | const string_view_t on_cyan = "\033[46m"; 74 | const string_view_t on_white = "\033[47m"; 75 | 76 | /// Bold colors 77 | const string_view_t yellow_bold = "\033[33m\033[1m"; 78 | const string_view_t red_bold = "\033[31m\033[1m"; 79 | const string_view_t bold_on_red = "\033[1m\033[41m"; 80 | 81 | private: 82 | FILE *target_file_; 83 | mutex_t &mutex_; 84 | bool should_do_colors_; 85 | std::unique_ptr formatter_; 86 | std::array colors_; 87 | void set_color_mode_(color_mode mode); 88 | void print_ccode_(const string_view_t &color_code) const; 89 | void print_range_(const memory_buf_t &formatted, size_t start, size_t end) const; 90 | static std::string to_string_(const string_view_t &sv); 91 | }; 92 | 93 | template 94 | class ansicolor_stdout_sink : public ansicolor_sink { 95 | public: 96 | explicit ansicolor_stdout_sink(color_mode mode = color_mode::automatic); 97 | }; 98 | 99 | template 100 | class ansicolor_stderr_sink : public ansicolor_sink { 101 | public: 102 | explicit ansicolor_stderr_sink(color_mode mode = color_mode::automatic); 103 | }; 104 | 105 | using ansicolor_stdout_sink_mt = ansicolor_stdout_sink; 106 | using ansicolor_stdout_sink_st = ansicolor_stdout_sink; 107 | 108 | using ansicolor_stderr_sink_mt = ansicolor_stderr_sink; 109 | using ansicolor_stderr_sink_st = ansicolor_stderr_sink; 110 | 111 | } // namespace sinks 112 | } // namespace spdlog 113 | 114 | #ifdef SPDLOG_HEADER_ONLY 115 | #include "ansicolor_sink-inl.h" 116 | #endif 117 | -------------------------------------------------------------------------------- /include/spdlog/sinks/base_sink-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | #include 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | template 17 | SPDLOG_INLINE spdlog::sinks::base_sink::base_sink() 18 | : formatter_{details::make_unique()} {} 19 | 20 | template 21 | SPDLOG_INLINE spdlog::sinks::base_sink::base_sink( 22 | std::unique_ptr formatter) 23 | : formatter_{std::move(formatter)} {} 24 | 25 | template 26 | void SPDLOG_INLINE spdlog::sinks::base_sink::log(const details::log_msg &msg) { 27 | std::lock_guard lock(mutex_); 28 | sink_it_(msg); 29 | } 30 | 31 | template 32 | void SPDLOG_INLINE spdlog::sinks::base_sink::flush() { 33 | std::lock_guard lock(mutex_); 34 | flush_(); 35 | } 36 | 37 | template 38 | void SPDLOG_INLINE spdlog::sinks::base_sink::set_pattern(const std::string &pattern) { 39 | std::lock_guard lock(mutex_); 40 | set_pattern_(pattern); 41 | } 42 | 43 | template 44 | void SPDLOG_INLINE 45 | spdlog::sinks::base_sink::set_formatter(std::unique_ptr sink_formatter) { 46 | std::lock_guard lock(mutex_); 47 | set_formatter_(std::move(sink_formatter)); 48 | } 49 | 50 | template 51 | void SPDLOG_INLINE spdlog::sinks::base_sink::set_pattern_(const std::string &pattern) { 52 | set_formatter_(details::make_unique(pattern)); 53 | } 54 | 55 | template 56 | void SPDLOG_INLINE 57 | spdlog::sinks::base_sink::set_formatter_(std::unique_ptr sink_formatter) { 58 | formatter_ = std::move(sink_formatter); 59 | } 60 | -------------------------------------------------------------------------------- /include/spdlog/sinks/base_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | // 6 | // base sink templated over a mutex (either dummy or real) 7 | // concrete implementation should override the sink_it_() and flush_() methods. 8 | // locking is taken care of in this class - no locking needed by the 9 | // implementers.. 10 | // 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | namespace spdlog { 17 | namespace sinks { 18 | template 19 | class SPDLOG_API base_sink : public sink { 20 | public: 21 | base_sink(); 22 | explicit base_sink(std::unique_ptr formatter); 23 | ~base_sink() override = default; 24 | 25 | base_sink(const base_sink &) = delete; 26 | base_sink(base_sink &&) = delete; 27 | 28 | base_sink &operator=(const base_sink &) = delete; 29 | base_sink &operator=(base_sink &&) = delete; 30 | 31 | void log(const details::log_msg &msg) final override; 32 | void flush() final override; 33 | void set_pattern(const std::string &pattern) final override; 34 | void set_formatter(std::unique_ptr sink_formatter) final override; 35 | 36 | protected: 37 | // sink formatter 38 | std::unique_ptr formatter_; 39 | Mutex mutex_; 40 | 41 | virtual void sink_it_(const details::log_msg &msg) = 0; 42 | virtual void flush_() = 0; 43 | virtual void set_pattern_(const std::string &pattern); 44 | virtual void set_formatter_(std::unique_ptr sink_formatter); 45 | }; 46 | } // namespace sinks 47 | } // namespace spdlog 48 | 49 | #ifdef SPDLOG_HEADER_ONLY 50 | #include "base_sink-inl.h" 51 | #endif 52 | -------------------------------------------------------------------------------- /include/spdlog/sinks/basic_file_sink-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | #include 11 | #include 12 | 13 | namespace spdlog { 14 | namespace sinks { 15 | 16 | template 17 | SPDLOG_INLINE basic_file_sink::basic_file_sink(const filename_t &filename, 18 | bool truncate, 19 | const file_event_handlers &event_handlers) 20 | : file_helper_{event_handlers} { 21 | file_helper_.open(filename, truncate); 22 | } 23 | 24 | template 25 | SPDLOG_INLINE const filename_t &basic_file_sink::filename() const { 26 | return file_helper_.filename(); 27 | } 28 | 29 | template 30 | SPDLOG_INLINE void basic_file_sink::truncate() { 31 | std::lock_guard lock(base_sink::mutex_); 32 | file_helper_.reopen(true); 33 | } 34 | 35 | template 36 | SPDLOG_INLINE void basic_file_sink::sink_it_(const details::log_msg &msg) { 37 | memory_buf_t formatted; 38 | base_sink::formatter_->format(msg, formatted); 39 | file_helper_.write(formatted); 40 | } 41 | 42 | template 43 | SPDLOG_INLINE void basic_file_sink::flush_() { 44 | file_helper_.flush(); 45 | } 46 | 47 | } // namespace sinks 48 | } // namespace spdlog 49 | -------------------------------------------------------------------------------- /include/spdlog/sinks/basic_file_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | namespace spdlog { 15 | namespace sinks { 16 | /* 17 | * Trivial file sink with single file as target 18 | */ 19 | template 20 | class basic_file_sink final : public base_sink { 21 | public: 22 | explicit basic_file_sink(const filename_t &filename, 23 | bool truncate = false, 24 | const file_event_handlers &event_handlers = {}); 25 | const filename_t &filename() const; 26 | void truncate(); 27 | 28 | protected: 29 | void sink_it_(const details::log_msg &msg) override; 30 | void flush_() override; 31 | 32 | private: 33 | details::file_helper file_helper_; 34 | }; 35 | 36 | using basic_file_sink_mt = basic_file_sink; 37 | using basic_file_sink_st = basic_file_sink; 38 | 39 | } // namespace sinks 40 | 41 | // 42 | // factory functions 43 | // 44 | template 45 | inline std::shared_ptr basic_logger_mt(const std::string &logger_name, 46 | const filename_t &filename, 47 | bool truncate = false, 48 | const file_event_handlers &event_handlers = {}) { 49 | return Factory::template create(logger_name, filename, truncate, 50 | event_handlers); 51 | } 52 | 53 | template 54 | inline std::shared_ptr basic_logger_st(const std::string &logger_name, 55 | const filename_t &filename, 56 | bool truncate = false, 57 | const file_event_handlers &event_handlers = {}) { 58 | return Factory::template create(logger_name, filename, truncate, 59 | event_handlers); 60 | } 61 | 62 | } // namespace spdlog 63 | 64 | #ifdef SPDLOG_HEADER_ONLY 65 | #include "basic_file_sink-inl.h" 66 | #endif 67 | -------------------------------------------------------------------------------- /include/spdlog/sinks/callback_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | 13 | namespace spdlog { 14 | 15 | // callbacks type 16 | typedef std::function custom_log_callback; 17 | 18 | namespace sinks { 19 | /* 20 | * Trivial callback sink, gets a callback function and calls it on each log 21 | */ 22 | template 23 | class callback_sink final : public base_sink { 24 | public: 25 | explicit callback_sink(const custom_log_callback &callback) 26 | : callback_{callback} {} 27 | 28 | protected: 29 | void sink_it_(const details::log_msg &msg) override { callback_(msg); } 30 | void flush_() override {} 31 | 32 | private: 33 | custom_log_callback callback_; 34 | }; 35 | 36 | using callback_sink_mt = callback_sink; 37 | using callback_sink_st = callback_sink; 38 | 39 | } // namespace sinks 40 | 41 | // 42 | // factory functions 43 | // 44 | template 45 | inline std::shared_ptr callback_logger_mt(const std::string &logger_name, 46 | const custom_log_callback &callback) { 47 | return Factory::template create(logger_name, callback); 48 | } 49 | 50 | template 51 | inline std::shared_ptr callback_logger_st(const std::string &logger_name, 52 | const custom_log_callback &callback) { 53 | return Factory::template create(logger_name, callback); 54 | } 55 | 56 | } // namespace spdlog 57 | -------------------------------------------------------------------------------- /include/spdlog/sinks/dist_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include "base_sink.h" 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | // Distribution sink (mux). Stores a vector of sinks which get called when log 17 | // is called 18 | 19 | namespace spdlog { 20 | namespace sinks { 21 | 22 | template 23 | class dist_sink : public base_sink { 24 | public: 25 | dist_sink() = default; 26 | explicit dist_sink(std::vector> sinks) 27 | : sinks_(sinks) {} 28 | 29 | dist_sink(const dist_sink &) = delete; 30 | dist_sink &operator=(const dist_sink &) = delete; 31 | 32 | void add_sink(std::shared_ptr sub_sink) { 33 | std::lock_guard lock(base_sink::mutex_); 34 | sinks_.push_back(sub_sink); 35 | } 36 | 37 | void remove_sink(std::shared_ptr sub_sink) { 38 | std::lock_guard lock(base_sink::mutex_); 39 | sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sub_sink), sinks_.end()); 40 | } 41 | 42 | void set_sinks(std::vector> sinks) { 43 | std::lock_guard lock(base_sink::mutex_); 44 | sinks_ = std::move(sinks); 45 | } 46 | 47 | std::vector> &sinks() { return sinks_; } 48 | 49 | protected: 50 | void sink_it_(const details::log_msg &msg) override { 51 | for (auto &sub_sink : sinks_) { 52 | if (sub_sink->should_log(msg.level)) { 53 | sub_sink->log(msg); 54 | } 55 | } 56 | } 57 | 58 | void flush_() override { 59 | for (auto &sub_sink : sinks_) { 60 | sub_sink->flush(); 61 | } 62 | } 63 | 64 | void set_pattern_(const std::string &pattern) override { 65 | set_formatter_(details::make_unique(pattern)); 66 | } 67 | 68 | void set_formatter_(std::unique_ptr sink_formatter) override { 69 | base_sink::formatter_ = std::move(sink_formatter); 70 | for (auto &sub_sink : sinks_) { 71 | sub_sink->set_formatter(base_sink::formatter_->clone()); 72 | } 73 | } 74 | std::vector> sinks_; 75 | }; 76 | 77 | using dist_sink_mt = dist_sink; 78 | using dist_sink_st = dist_sink; 79 | 80 | } // namespace sinks 81 | } // namespace spdlog 82 | -------------------------------------------------------------------------------- /include/spdlog/sinks/dup_filter_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include "dist_sink.h" 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | // Duplicate message removal sink. 16 | // Skip the message if previous one is identical and less than "max_skip_duration" have passed 17 | // 18 | // Example: 19 | // 20 | // #include 21 | // 22 | // int main() { 23 | // auto dup_filter = std::make_shared(std::chrono::seconds(5), 24 | // level::info); dup_filter->add_sink(std::make_shared()); 25 | // spdlog::logger l("logger", dup_filter); 26 | // l.info("Hello"); 27 | // l.info("Hello"); 28 | // l.info("Hello"); 29 | // l.info("Different Hello"); 30 | // } 31 | // 32 | // Will produce: 33 | // [2019-06-25 17:50:56.511] [logger] [info] Hello 34 | // [2019-06-25 17:50:56.512] [logger] [info] Skipped 3 duplicate messages.. 35 | // [2019-06-25 17:50:56.512] [logger] [info] Different Hello 36 | 37 | namespace spdlog { 38 | namespace sinks { 39 | template 40 | class dup_filter_sink : public dist_sink { 41 | public: 42 | template 43 | explicit dup_filter_sink(std::chrono::duration max_skip_duration) 44 | : max_skip_duration_{max_skip_duration} {} 45 | 46 | protected: 47 | std::chrono::microseconds max_skip_duration_; 48 | log_clock::time_point last_msg_time_; 49 | std::string last_msg_payload_; 50 | size_t skip_counter_ = 0; 51 | level::level_enum skipped_msg_log_level_ = spdlog::level::level_enum::off; 52 | 53 | void sink_it_(const details::log_msg &msg) override { 54 | bool filtered = filter_(msg); 55 | if (!filtered) { 56 | skip_counter_ += 1; 57 | skipped_msg_log_level_ = msg.level; 58 | return; 59 | } 60 | 61 | // log the "skipped.." message 62 | if (skip_counter_ > 0) { 63 | char buf[64]; 64 | auto msg_size = ::snprintf(buf, sizeof(buf), "Skipped %u duplicate messages..", 65 | static_cast(skip_counter_)); 66 | if (msg_size > 0 && static_cast(msg_size) < sizeof(buf)) { 67 | details::log_msg skipped_msg{msg.source, msg.logger_name, skipped_msg_log_level_, 68 | string_view_t{buf, static_cast(msg_size)}}; 69 | dist_sink::sink_it_(skipped_msg); 70 | } 71 | } 72 | 73 | // log current message 74 | dist_sink::sink_it_(msg); 75 | last_msg_time_ = msg.time; 76 | skip_counter_ = 0; 77 | last_msg_payload_.assign(msg.payload.data(), msg.payload.data() + msg.payload.size()); 78 | } 79 | 80 | // return whether the log msg should be displayed (true) or skipped (false) 81 | bool filter_(const details::log_msg &msg) { 82 | auto filter_duration = msg.time - last_msg_time_; 83 | return (filter_duration > max_skip_duration_) || (msg.payload != last_msg_payload_); 84 | } 85 | }; 86 | 87 | using dup_filter_sink_mt = dup_filter_sink; 88 | using dup_filter_sink_st = dup_filter_sink; 89 | 90 | } // namespace sinks 91 | } // namespace spdlog 92 | -------------------------------------------------------------------------------- /include/spdlog/sinks/mongo_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | // 7 | // Custom sink for mongodb 8 | // Building and using requires mongocxx library. 9 | // For building mongocxx library check the url below 10 | // http://mongocxx.org/mongocxx-v3/installation/ 11 | // 12 | 13 | #include "spdlog/common.h" 14 | #include "spdlog/details/log_msg.h" 15 | #include "spdlog/sinks/base_sink.h" 16 | #include 17 | 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | namespace spdlog { 27 | namespace sinks { 28 | template 29 | class mongo_sink : public base_sink { 30 | public: 31 | mongo_sink(const std::string &db_name, 32 | const std::string &collection_name, 33 | const std::string &uri = "mongodb://localhost:27017") try 34 | : mongo_sink(std::make_shared(), db_name, collection_name, uri) { 35 | } catch (const std::exception &e) { 36 | throw_spdlog_ex(fmt_lib::format("Error opening database: {}", e.what())); 37 | } 38 | 39 | mongo_sink(std::shared_ptr instance, 40 | const std::string &db_name, 41 | const std::string &collection_name, 42 | const std::string &uri = "mongodb://localhost:27017") 43 | : instance_(std::move(instance)), 44 | db_name_(db_name), 45 | coll_name_(collection_name) { 46 | try { 47 | client_ = spdlog::details::make_unique(mongocxx::uri{uri}); 48 | } catch (const std::exception &e) { 49 | throw_spdlog_ex(fmt_lib::format("Error opening database: {}", e.what())); 50 | } 51 | } 52 | 53 | ~mongo_sink() { flush_(); } 54 | 55 | protected: 56 | void sink_it_(const details::log_msg &msg) override { 57 | using bsoncxx::builder::stream::document; 58 | using bsoncxx::builder::stream::finalize; 59 | 60 | if (client_ != nullptr) { 61 | auto doc = document{} << "timestamp" << bsoncxx::types::b_date(msg.time) << "level" 62 | << level::to_string_view(msg.level).data() << "level_num" 63 | << msg.level << "message" 64 | << std::string(msg.payload.begin(), msg.payload.end()) 65 | << "logger_name" 66 | << std::string(msg.logger_name.begin(), msg.logger_name.end()) 67 | << "thread_id" << static_cast(msg.thread_id) << finalize; 68 | client_->database(db_name_).collection(coll_name_).insert_one(doc.view()); 69 | } 70 | } 71 | 72 | void flush_() override {} 73 | 74 | private: 75 | std::shared_ptr instance_; 76 | std::string db_name_; 77 | std::string coll_name_; 78 | std::unique_ptr client_ = nullptr; 79 | }; 80 | 81 | #include "spdlog/details/null_mutex.h" 82 | #include 83 | using mongo_sink_mt = mongo_sink; 84 | using mongo_sink_st = mongo_sink; 85 | 86 | } // namespace sinks 87 | 88 | template 89 | inline std::shared_ptr mongo_logger_mt( 90 | const std::string &logger_name, 91 | const std::string &db_name, 92 | const std::string &collection_name, 93 | const std::string &uri = "mongodb://localhost:27017") { 94 | return Factory::template create(logger_name, db_name, collection_name, 95 | uri); 96 | } 97 | 98 | template 99 | inline std::shared_ptr mongo_logger_st( 100 | const std::string &logger_name, 101 | const std::string &db_name, 102 | const std::string &collection_name, 103 | const std::string &uri = "mongodb://localhost:27017") { 104 | return Factory::template create(logger_name, db_name, collection_name, 105 | uri); 106 | } 107 | 108 | } // namespace spdlog 109 | -------------------------------------------------------------------------------- /include/spdlog/sinks/msvc_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2016 Alexander Dalshov & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #if defined(_WIN32) 7 | 8 | #include 9 | #if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) 10 | #include 11 | #endif 12 | #include 13 | 14 | #include 15 | #include 16 | 17 | // Avoid including windows.h (https://stackoverflow.com/a/30741042) 18 | #if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) 19 | extern "C" __declspec(dllimport) void __stdcall OutputDebugStringW(const wchar_t *lpOutputString); 20 | #else 21 | extern "C" __declspec(dllimport) void __stdcall OutputDebugStringA(const char *lpOutputString); 22 | #endif 23 | extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent(); 24 | 25 | namespace spdlog { 26 | namespace sinks { 27 | /* 28 | * MSVC sink (logging using OutputDebugStringA) 29 | */ 30 | template 31 | class msvc_sink : public base_sink { 32 | public: 33 | msvc_sink() = default; 34 | msvc_sink(bool check_debugger_present) 35 | : check_debugger_present_{check_debugger_present} {} 36 | 37 | protected: 38 | void sink_it_(const details::log_msg &msg) override { 39 | if (check_debugger_present_ && !IsDebuggerPresent()) { 40 | return; 41 | } 42 | memory_buf_t formatted; 43 | base_sink::formatter_->format(msg, formatted); 44 | formatted.push_back('\0'); // add a null terminator for OutputDebugString 45 | #if defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT) 46 | wmemory_buf_t wformatted; 47 | details::os::utf8_to_wstrbuf(string_view_t(formatted.data(), formatted.size()), wformatted); 48 | OutputDebugStringW(wformatted.data()); 49 | #else 50 | OutputDebugStringA(formatted.data()); 51 | #endif 52 | } 53 | 54 | void flush_() override {} 55 | 56 | bool check_debugger_present_ = true; 57 | }; 58 | 59 | using msvc_sink_mt = msvc_sink; 60 | using msvc_sink_st = msvc_sink; 61 | 62 | using windebug_sink_mt = msvc_sink_mt; 63 | using windebug_sink_st = msvc_sink_st; 64 | 65 | } // namespace sinks 66 | } // namespace spdlog 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /include/spdlog/sinks/null_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | namespace spdlog { 13 | namespace sinks { 14 | 15 | template 16 | class null_sink final : public base_sink { 17 | protected: 18 | void sink_it_(const details::log_msg &) override {} 19 | void flush_() override {} 20 | }; 21 | 22 | using null_sink_mt = null_sink; 23 | using null_sink_st = null_sink; 24 | 25 | } // namespace sinks 26 | 27 | template 28 | inline std::shared_ptr null_logger_mt(const std::string &logger_name) { 29 | auto null_logger = Factory::template create(logger_name); 30 | null_logger->set_level(level::off); 31 | return null_logger; 32 | } 33 | 34 | template 35 | inline std::shared_ptr null_logger_st(const std::string &logger_name) { 36 | auto null_logger = Factory::template create(logger_name); 37 | null_logger->set_level(level::off); 38 | return null_logger; 39 | } 40 | 41 | } // namespace spdlog 42 | -------------------------------------------------------------------------------- /include/spdlog/sinks/ostream_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | namespace spdlog { 13 | namespace sinks { 14 | template 15 | class ostream_sink final : public base_sink { 16 | public: 17 | explicit ostream_sink(std::ostream &os, bool force_flush = false) 18 | : ostream_(os), 19 | force_flush_(force_flush) {} 20 | ostream_sink(const ostream_sink &) = delete; 21 | ostream_sink &operator=(const ostream_sink &) = delete; 22 | 23 | protected: 24 | void sink_it_(const details::log_msg &msg) override { 25 | memory_buf_t formatted; 26 | base_sink::formatter_->format(msg, formatted); 27 | ostream_.write(formatted.data(), static_cast(formatted.size())); 28 | if (force_flush_) { 29 | ostream_.flush(); 30 | } 31 | } 32 | 33 | void flush_() override { ostream_.flush(); } 34 | 35 | std::ostream &ostream_; 36 | bool force_flush_; 37 | }; 38 | 39 | using ostream_sink_mt = ostream_sink; 40 | using ostream_sink_st = ostream_sink; 41 | 42 | } // namespace sinks 43 | } // namespace spdlog 44 | -------------------------------------------------------------------------------- /include/spdlog/sinks/ringbuffer_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include "spdlog/details/circular_q.h" 7 | #include "spdlog/details/log_msg_buffer.h" 8 | #include "spdlog/details/null_mutex.h" 9 | #include "spdlog/sinks/base_sink.h" 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | namespace spdlog { 16 | namespace sinks { 17 | /* 18 | * Ring buffer sink 19 | */ 20 | template 21 | class ringbuffer_sink final : public base_sink { 22 | public: 23 | explicit ringbuffer_sink(size_t n_items) 24 | : q_{n_items} {} 25 | 26 | std::vector last_raw(size_t lim = 0) { 27 | std::lock_guard lock(base_sink::mutex_); 28 | auto items_available = q_.size(); 29 | auto n_items = lim > 0 ? (std::min)(lim, items_available) : items_available; 30 | std::vector ret; 31 | ret.reserve(n_items); 32 | for (size_t i = (items_available - n_items); i < items_available; i++) { 33 | ret.push_back(q_.at(i)); 34 | } 35 | return ret; 36 | } 37 | 38 | std::vector last_formatted(size_t lim = 0) { 39 | std::lock_guard lock(base_sink::mutex_); 40 | auto items_available = q_.size(); 41 | auto n_items = lim > 0 ? (std::min)(lim, items_available) : items_available; 42 | std::vector ret; 43 | ret.reserve(n_items); 44 | for (size_t i = (items_available - n_items); i < items_available; i++) { 45 | memory_buf_t formatted; 46 | base_sink::formatter_->format(q_.at(i), formatted); 47 | ret.push_back(SPDLOG_BUF_TO_STRING(formatted)); 48 | } 49 | return ret; 50 | } 51 | 52 | protected: 53 | void sink_it_(const details::log_msg &msg) override { 54 | q_.push_back(details::log_msg_buffer{msg}); 55 | } 56 | void flush_() override {} 57 | 58 | private: 59 | details::circular_q q_; 60 | }; 61 | 62 | using ringbuffer_sink_mt = ringbuffer_sink; 63 | using ringbuffer_sink_st = ringbuffer_sink; 64 | 65 | } // namespace sinks 66 | 67 | } // namespace spdlog 68 | -------------------------------------------------------------------------------- /include/spdlog/sinks/rotating_file_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | 14 | namespace spdlog { 15 | namespace sinks { 16 | 17 | // 18 | // Rotating file sink based on size 19 | // 20 | template 21 | class rotating_file_sink final : public base_sink { 22 | public: 23 | static constexpr size_t MaxFiles = 200000; 24 | rotating_file_sink(filename_t base_filename, 25 | std::size_t max_size, 26 | std::size_t max_files, 27 | bool rotate_on_open = false, 28 | const file_event_handlers &event_handlers = {}); 29 | static filename_t calc_filename(const filename_t &filename, std::size_t index); 30 | filename_t filename(); 31 | void rotate_now(); 32 | void set_max_size(std::size_t max_size); 33 | std::size_t get_max_size(); 34 | void set_max_files(std::size_t max_files); 35 | std::size_t get_max_files(); 36 | 37 | protected: 38 | void sink_it_(const details::log_msg &msg) override; 39 | void flush_() override; 40 | 41 | private: 42 | // Rotate files: 43 | // log.txt -> log.1.txt 44 | // log.1.txt -> log.2.txt 45 | // log.2.txt -> log.3.txt 46 | // log.3.txt -> delete 47 | void rotate_(); 48 | 49 | // delete the target if exists, and rename the src file to target 50 | // return true on success, false otherwise. 51 | bool rename_file_(const filename_t &src_filename, const filename_t &target_filename); 52 | 53 | filename_t base_filename_; 54 | std::size_t max_size_; 55 | std::size_t max_files_; 56 | std::size_t current_size_; 57 | details::file_helper file_helper_; 58 | }; 59 | 60 | using rotating_file_sink_mt = rotating_file_sink; 61 | using rotating_file_sink_st = rotating_file_sink; 62 | 63 | } // namespace sinks 64 | 65 | // 66 | // factory functions 67 | // 68 | template 69 | std::shared_ptr rotating_logger_mt(const std::string &logger_name, 70 | const filename_t &filename, 71 | size_t max_file_size, 72 | size_t max_files, 73 | bool rotate_on_open = false, 74 | const file_event_handlers &event_handlers = {}) { 75 | return Factory::template create( 76 | logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers); 77 | } 78 | 79 | template 80 | std::shared_ptr rotating_logger_st(const std::string &logger_name, 81 | const filename_t &filename, 82 | size_t max_file_size, 83 | size_t max_files, 84 | bool rotate_on_open = false, 85 | const file_event_handlers &event_handlers = {}) { 86 | return Factory::template create( 87 | logger_name, filename, max_file_size, max_files, rotate_on_open, event_handlers); 88 | } 89 | } // namespace spdlog 90 | 91 | #ifdef SPDLOG_HEADER_ONLY 92 | #include "rotating_file_sink-inl.h" 93 | #endif 94 | -------------------------------------------------------------------------------- /include/spdlog/sinks/sink-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | #include 11 | 12 | SPDLOG_INLINE bool spdlog::sinks::sink::should_log(spdlog::level::level_enum msg_level) const { 13 | return msg_level >= level_.load(std::memory_order_relaxed); 14 | } 15 | 16 | SPDLOG_INLINE void spdlog::sinks::sink::set_level(level::level_enum log_level) { 17 | level_.store(log_level, std::memory_order_relaxed); 18 | } 19 | 20 | SPDLOG_INLINE spdlog::level::level_enum spdlog::sinks::sink::level() const { 21 | return static_cast(level_.load(std::memory_order_relaxed)); 22 | } 23 | -------------------------------------------------------------------------------- /include/spdlog/sinks/sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | namespace spdlog { 10 | 11 | namespace sinks { 12 | class SPDLOG_API sink { 13 | public: 14 | virtual ~sink() = default; 15 | virtual void log(const details::log_msg &msg) = 0; 16 | virtual void flush() = 0; 17 | virtual void set_pattern(const std::string &pattern) = 0; 18 | virtual void set_formatter(std::unique_ptr sink_formatter) = 0; 19 | 20 | void set_level(level::level_enum log_level); 21 | level::level_enum level() const; 22 | bool should_log(level::level_enum msg_level) const; 23 | 24 | protected: 25 | // sink log level - default is all 26 | level_t level_{level::trace}; 27 | }; 28 | 29 | } // namespace sinks 30 | } // namespace spdlog 31 | 32 | #ifdef SPDLOG_HEADER_ONLY 33 | #include "sink-inl.h" 34 | #endif 35 | -------------------------------------------------------------------------------- /include/spdlog/sinks/stdout_color_sinks-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | #include 11 | #include 12 | 13 | namespace spdlog { 14 | 15 | template 16 | SPDLOG_INLINE std::shared_ptr stdout_color_mt(const std::string &logger_name, 17 | color_mode mode) { 18 | return Factory::template create(logger_name, mode); 19 | } 20 | 21 | template 22 | SPDLOG_INLINE std::shared_ptr stdout_color_st(const std::string &logger_name, 23 | color_mode mode) { 24 | return Factory::template create(logger_name, mode); 25 | } 26 | 27 | template 28 | SPDLOG_INLINE std::shared_ptr stderr_color_mt(const std::string &logger_name, 29 | color_mode mode) { 30 | return Factory::template create(logger_name, mode); 31 | } 32 | 33 | template 34 | SPDLOG_INLINE std::shared_ptr stderr_color_st(const std::string &logger_name, 35 | color_mode mode) { 36 | return Factory::template create(logger_name, mode); 37 | } 38 | } // namespace spdlog 39 | -------------------------------------------------------------------------------- /include/spdlog/sinks/stdout_color_sinks.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifdef _WIN32 7 | #include 8 | #else 9 | #include 10 | #endif 11 | 12 | #include 13 | 14 | namespace spdlog { 15 | namespace sinks { 16 | #ifdef _WIN32 17 | using stdout_color_sink_mt = wincolor_stdout_sink_mt; 18 | using stdout_color_sink_st = wincolor_stdout_sink_st; 19 | using stderr_color_sink_mt = wincolor_stderr_sink_mt; 20 | using stderr_color_sink_st = wincolor_stderr_sink_st; 21 | #else 22 | using stdout_color_sink_mt = ansicolor_stdout_sink_mt; 23 | using stdout_color_sink_st = ansicolor_stdout_sink_st; 24 | using stderr_color_sink_mt = ansicolor_stderr_sink_mt; 25 | using stderr_color_sink_st = ansicolor_stderr_sink_st; 26 | #endif 27 | } // namespace sinks 28 | 29 | template 30 | std::shared_ptr stdout_color_mt(const std::string &logger_name, 31 | color_mode mode = color_mode::automatic); 32 | 33 | template 34 | std::shared_ptr stdout_color_st(const std::string &logger_name, 35 | color_mode mode = color_mode::automatic); 36 | 37 | template 38 | std::shared_ptr stderr_color_mt(const std::string &logger_name, 39 | color_mode mode = color_mode::automatic); 40 | 41 | template 42 | std::shared_ptr stderr_color_st(const std::string &logger_name, 43 | color_mode mode = color_mode::automatic); 44 | 45 | } // namespace spdlog 46 | 47 | #ifdef SPDLOG_HEADER_ONLY 48 | #include "stdout_color_sinks-inl.h" 49 | #endif 50 | -------------------------------------------------------------------------------- /include/spdlog/sinks/stdout_sinks.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #ifdef _WIN32 12 | #include 13 | #endif 14 | 15 | namespace spdlog { 16 | 17 | namespace sinks { 18 | 19 | template 20 | class stdout_sink_base : public sink { 21 | public: 22 | using mutex_t = typename ConsoleMutex::mutex_t; 23 | explicit stdout_sink_base(FILE *file); 24 | ~stdout_sink_base() override = default; 25 | 26 | stdout_sink_base(const stdout_sink_base &other) = delete; 27 | stdout_sink_base(stdout_sink_base &&other) = delete; 28 | 29 | stdout_sink_base &operator=(const stdout_sink_base &other) = delete; 30 | stdout_sink_base &operator=(stdout_sink_base &&other) = delete; 31 | 32 | void log(const details::log_msg &msg) override; 33 | void flush() override; 34 | void set_pattern(const std::string &pattern) override; 35 | 36 | void set_formatter(std::unique_ptr sink_formatter) override; 37 | 38 | protected: 39 | mutex_t &mutex_; 40 | FILE *file_; 41 | std::unique_ptr formatter_; 42 | #ifdef _WIN32 43 | HANDLE handle_; 44 | #endif // WIN32 45 | }; 46 | 47 | template 48 | class stdout_sink : public stdout_sink_base { 49 | public: 50 | stdout_sink(); 51 | }; 52 | 53 | template 54 | class stderr_sink : public stdout_sink_base { 55 | public: 56 | stderr_sink(); 57 | }; 58 | 59 | using stdout_sink_mt = stdout_sink; 60 | using stdout_sink_st = stdout_sink; 61 | 62 | using stderr_sink_mt = stderr_sink; 63 | using stderr_sink_st = stderr_sink; 64 | 65 | } // namespace sinks 66 | 67 | // factory methods 68 | template 69 | std::shared_ptr stdout_logger_mt(const std::string &logger_name); 70 | 71 | template 72 | std::shared_ptr stdout_logger_st(const std::string &logger_name); 73 | 74 | template 75 | std::shared_ptr stderr_logger_mt(const std::string &logger_name); 76 | 77 | template 78 | std::shared_ptr stderr_logger_st(const std::string &logger_name); 79 | 80 | } // namespace spdlog 81 | 82 | #ifdef SPDLOG_HEADER_ONLY 83 | #include "stdout_sinks-inl.h" 84 | #endif 85 | -------------------------------------------------------------------------------- /include/spdlog/sinks/tcp_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #ifdef _WIN32 10 | #include 11 | #else 12 | #include 13 | #endif 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #pragma once 21 | 22 | // Simple tcp client sink 23 | // Connects to remote address and send the formatted log. 24 | // Will attempt to reconnect if connection drops. 25 | // If more complicated behaviour is needed (i.e get responses), you can inherit it and override the 26 | // sink_it_ method. 27 | 28 | namespace spdlog { 29 | namespace sinks { 30 | 31 | struct tcp_sink_config { 32 | std::string server_host; 33 | int server_port; 34 | bool lazy_connect = false; // if true connect on first log call instead of on construction 35 | 36 | tcp_sink_config(std::string host, int port) 37 | : server_host{std::move(host)}, 38 | server_port{port} {} 39 | }; 40 | 41 | template 42 | class tcp_sink : public spdlog::sinks::base_sink { 43 | public: 44 | // connect to tcp host/port or throw if failed 45 | // host can be hostname or ip address 46 | 47 | explicit tcp_sink(tcp_sink_config sink_config) 48 | : config_{std::move(sink_config)} { 49 | if (!config_.lazy_connect) { 50 | this->client_.connect(config_.server_host, config_.server_port); 51 | } 52 | } 53 | 54 | ~tcp_sink() override = default; 55 | 56 | protected: 57 | void sink_it_(const spdlog::details::log_msg &msg) override { 58 | spdlog::memory_buf_t formatted; 59 | spdlog::sinks::base_sink::formatter_->format(msg, formatted); 60 | if (!client_.is_connected()) { 61 | client_.connect(config_.server_host, config_.server_port); 62 | } 63 | client_.send(formatted.data(), formatted.size()); 64 | } 65 | 66 | void flush_() override {} 67 | tcp_sink_config config_; 68 | details::tcp_client client_; 69 | }; 70 | 71 | using tcp_sink_mt = tcp_sink; 72 | using tcp_sink_st = tcp_sink; 73 | 74 | } // namespace sinks 75 | } // namespace spdlog 76 | -------------------------------------------------------------------------------- /include/spdlog/sinks/udp_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #ifdef _WIN32 10 | #include 11 | #else 12 | #include 13 | #endif 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | // Simple udp client sink 21 | // Sends formatted log via udp 22 | 23 | namespace spdlog { 24 | namespace sinks { 25 | 26 | struct udp_sink_config { 27 | std::string server_host; 28 | uint16_t server_port; 29 | 30 | udp_sink_config(std::string host, uint16_t port) 31 | : server_host{std::move(host)}, 32 | server_port{port} {} 33 | }; 34 | 35 | template 36 | class udp_sink : public spdlog::sinks::base_sink { 37 | public: 38 | // host can be hostname or ip address 39 | explicit udp_sink(udp_sink_config sink_config) 40 | : client_{sink_config.server_host, sink_config.server_port} {} 41 | 42 | ~udp_sink() override = default; 43 | 44 | protected: 45 | void sink_it_(const spdlog::details::log_msg &msg) override { 46 | spdlog::memory_buf_t formatted; 47 | spdlog::sinks::base_sink::formatter_->format(msg, formatted); 48 | client_.send(formatted.data(), formatted.size()); 49 | } 50 | 51 | void flush_() override {} 52 | details::udp_client client_; 53 | }; 54 | 55 | using udp_sink_mt = udp_sink; 56 | using udp_sink_st = udp_sink; 57 | 58 | } // namespace sinks 59 | 60 | // 61 | // factory functions 62 | // 63 | template 64 | inline std::shared_ptr udp_logger_mt(const std::string &logger_name, 65 | sinks::udp_sink_config skin_config) { 66 | return Factory::template create(logger_name, skin_config); 67 | } 68 | 69 | } // namespace spdlog 70 | -------------------------------------------------------------------------------- /include/spdlog/sinks/wincolor_sink.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace spdlog { 18 | namespace sinks { 19 | /* 20 | * Windows color console sink. Uses WriteConsoleA to write to the console with 21 | * colors 22 | */ 23 | template 24 | class wincolor_sink : public sink { 25 | public: 26 | wincolor_sink(void *out_handle, color_mode mode); 27 | ~wincolor_sink() override; 28 | 29 | wincolor_sink(const wincolor_sink &other) = delete; 30 | wincolor_sink &operator=(const wincolor_sink &other) = delete; 31 | 32 | // change the color for the given level 33 | void set_color(level::level_enum level, std::uint16_t color); 34 | void log(const details::log_msg &msg) final override; 35 | void flush() final override; 36 | void set_pattern(const std::string &pattern) override final; 37 | void set_formatter(std::unique_ptr sink_formatter) override final; 38 | void set_color_mode(color_mode mode); 39 | 40 | protected: 41 | using mutex_t = typename ConsoleMutex::mutex_t; 42 | void *out_handle_; 43 | mutex_t &mutex_; 44 | bool should_do_colors_; 45 | std::unique_ptr formatter_; 46 | std::array colors_; 47 | 48 | // set foreground color and return the orig console attributes (for resetting later) 49 | std::uint16_t set_foreground_color_(std::uint16_t attribs); 50 | 51 | // print a range of formatted message to console 52 | void print_range_(const memory_buf_t &formatted, size_t start, size_t end); 53 | 54 | // in case we are redirected to file (not in console mode) 55 | void write_to_file_(const memory_buf_t &formatted); 56 | 57 | void set_color_mode_impl(color_mode mode); 58 | }; 59 | 60 | template 61 | class wincolor_stdout_sink : public wincolor_sink { 62 | public: 63 | explicit wincolor_stdout_sink(color_mode mode = color_mode::automatic); 64 | }; 65 | 66 | template 67 | class wincolor_stderr_sink : public wincolor_sink { 68 | public: 69 | explicit wincolor_stderr_sink(color_mode mode = color_mode::automatic); 70 | }; 71 | 72 | using wincolor_stdout_sink_mt = wincolor_stdout_sink; 73 | using wincolor_stdout_sink_st = wincolor_stdout_sink; 74 | 75 | using wincolor_stderr_sink_mt = wincolor_stderr_sink; 76 | using wincolor_stderr_sink_st = wincolor_stderr_sink; 77 | } // namespace sinks 78 | } // namespace spdlog 79 | 80 | #ifdef SPDLOG_HEADER_ONLY 81 | #include "wincolor_sink-inl.h" 82 | #endif 83 | -------------------------------------------------------------------------------- /include/spdlog/spdlog-inl.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #ifndef SPDLOG_HEADER_ONLY 7 | #include 8 | #endif 9 | 10 | #include 11 | #include 12 | 13 | namespace spdlog { 14 | 15 | SPDLOG_INLINE void initialize_logger(std::shared_ptr logger) { 16 | details::registry::instance().initialize_logger(std::move(logger)); 17 | } 18 | 19 | SPDLOG_INLINE std::shared_ptr get(const std::string &name) { 20 | return details::registry::instance().get(name); 21 | } 22 | 23 | SPDLOG_INLINE void set_formatter(std::unique_ptr formatter) { 24 | details::registry::instance().set_formatter(std::move(formatter)); 25 | } 26 | 27 | SPDLOG_INLINE void set_pattern(std::string pattern, pattern_time_type time_type) { 28 | set_formatter( 29 | std::unique_ptr(new pattern_formatter(std::move(pattern), time_type))); 30 | } 31 | 32 | SPDLOG_INLINE void enable_backtrace(size_t n_messages) { 33 | details::registry::instance().enable_backtrace(n_messages); 34 | } 35 | 36 | SPDLOG_INLINE void disable_backtrace() { details::registry::instance().disable_backtrace(); } 37 | 38 | SPDLOG_INLINE void dump_backtrace() { default_logger_raw()->dump_backtrace(); } 39 | 40 | SPDLOG_INLINE level::level_enum get_level() { return default_logger_raw()->level(); } 41 | 42 | SPDLOG_INLINE bool should_log(level::level_enum log_level) { 43 | return default_logger_raw()->should_log(log_level); 44 | } 45 | 46 | SPDLOG_INLINE void set_level(level::level_enum log_level) { 47 | details::registry::instance().set_level(log_level); 48 | } 49 | 50 | SPDLOG_INLINE void flush_on(level::level_enum log_level) { 51 | details::registry::instance().flush_on(log_level); 52 | } 53 | 54 | SPDLOG_INLINE void set_error_handler(void (*handler)(const std::string &msg)) { 55 | details::registry::instance().set_error_handler(handler); 56 | } 57 | 58 | SPDLOG_INLINE void register_logger(std::shared_ptr logger) { 59 | details::registry::instance().register_logger(std::move(logger)); 60 | } 61 | 62 | SPDLOG_INLINE void register_or_replace(std::shared_ptr logger) { 63 | details::registry::instance().register_or_replace(std::move(logger)); 64 | } 65 | 66 | SPDLOG_INLINE void apply_all(const std::function)> &fun) { 67 | details::registry::instance().apply_all(fun); 68 | } 69 | 70 | SPDLOG_INLINE void drop(const std::string &name) { details::registry::instance().drop(name); } 71 | 72 | SPDLOG_INLINE void drop_all() { details::registry::instance().drop_all(); } 73 | 74 | SPDLOG_INLINE void shutdown() { details::registry::instance().shutdown(); } 75 | 76 | SPDLOG_INLINE void set_automatic_registration(bool automatic_registration) { 77 | details::registry::instance().set_automatic_registration(automatic_registration); 78 | } 79 | 80 | SPDLOG_INLINE std::shared_ptr default_logger() { 81 | return details::registry::instance().default_logger(); 82 | } 83 | 84 | SPDLOG_INLINE spdlog::logger *default_logger_raw() { 85 | return details::registry::instance().get_default_raw(); 86 | } 87 | 88 | SPDLOG_INLINE void set_default_logger(std::shared_ptr default_logger) { 89 | details::registry::instance().set_default_logger(std::move(default_logger)); 90 | } 91 | 92 | SPDLOG_INLINE void apply_logger_env_levels(std::shared_ptr logger) { 93 | details::registry::instance().apply_logger_env_levels(std::move(logger)); 94 | } 95 | 96 | } // namespace spdlog 97 | -------------------------------------------------------------------------------- /include/spdlog/stopwatch.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | // Stopwatch support for spdlog (using std::chrono::steady_clock). 10 | // Displays elapsed seconds since construction as double. 11 | // 12 | // Usage: 13 | // 14 | // spdlog::stopwatch sw; 15 | // ... 16 | // spdlog::debug("Elapsed: {} seconds", sw); => "Elapsed 0.005116733 seconds" 17 | // spdlog::info("Elapsed: {:.6} seconds", sw); => "Elapsed 0.005163 seconds" 18 | // 19 | // 20 | // If other units are needed (e.g. millis instead of double), include "fmt/chrono.h" and use 21 | // "duration_cast<..>(sw.elapsed())": 22 | // 23 | // #include 24 | //.. 25 | // using std::chrono::duration_cast; 26 | // using std::chrono::milliseconds; 27 | // spdlog::info("Elapsed {}", duration_cast(sw.elapsed())); => "Elapsed 5ms" 28 | 29 | namespace spdlog { 30 | class stopwatch { 31 | using clock = std::chrono::steady_clock; 32 | std::chrono::time_point start_tp_; 33 | 34 | public: 35 | stopwatch() 36 | : start_tp_{clock::now()} {} 37 | 38 | std::chrono::duration elapsed() const { 39 | return std::chrono::duration(clock::now() - start_tp_); 40 | } 41 | 42 | std::chrono::milliseconds elapsed_ms() const { 43 | return std::chrono::duration_cast(clock::now() - start_tp_); 44 | } 45 | 46 | void reset() { start_tp_ = clock::now(); } 47 | }; 48 | } // namespace spdlog 49 | 50 | // Support for fmt formatting (e.g. "{:012.9}" or just "{}") 51 | namespace 52 | #ifdef SPDLOG_USE_STD_FORMAT 53 | std 54 | #else 55 | fmt 56 | #endif 57 | { 58 | 59 | template <> 60 | struct formatter : formatter { 61 | template 62 | auto format(const spdlog::stopwatch &sw, FormatContext &ctx) const -> decltype(ctx.out()) { 63 | return formatter::format(sw.elapsed().count(), ctx); 64 | } 65 | }; 66 | } // namespace std 67 | -------------------------------------------------------------------------------- /include/spdlog/version.h: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #pragma once 5 | 6 | #define SPDLOG_VER_MAJOR 1 7 | #define SPDLOG_VER_MINOR 15 8 | #define SPDLOG_VER_PATCH 3 9 | 10 | #define SPDLOG_TO_VERSION(major, minor, patch) (major * 10000 + minor * 100 + patch) 11 | #define SPDLOG_VERSION SPDLOG_TO_VERSION(SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH) 12 | -------------------------------------------------------------------------------- /logos/spdlog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gabime/spdlog/b18a234ed6af38638678a3338c0d7ed90210ae6c/logos/spdlog.png -------------------------------------------------------------------------------- /scripts/ci_setup_clang.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | VERSION=$1 6 | 7 | apt-get update 8 | apt-get install -y libc++-${VERSION}-dev libc++abi-${VERSION}-dev 9 | 10 | if [[ "${VERSION}" -ge 12 ]]; then 11 | apt-get install -y --no-install-recommends libunwind-${VERSION}-dev 12 | fi 13 | -------------------------------------------------------------------------------- /scripts/extract_version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import os 4 | import re 5 | 6 | base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) 7 | config_h = os.path.join(base_path, 'include', 'spdlog', 'version.h') 8 | data = {'MAJOR': 0, 'MINOR': 0, 'PATCH': 0} 9 | reg = re.compile(r'^\s*#define\s+SPDLOG_VER_([A-Z]+)\s+([0-9]+).*$') 10 | 11 | with open(config_h, 'r') as fp: 12 | for l in fp: 13 | m = reg.match(l) 14 | if m: 15 | data[m.group(1)] = int(m.group(2)) 16 | 17 | print(f"{data['MAJOR']}.{data['MINOR']}.{data['PATCH']}") 18 | -------------------------------------------------------------------------------- /scripts/format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd "$(dirname "$0")"/.. 4 | pwd 5 | find_sources="find include src tests example bench -not ( -path include/spdlog/fmt/bundled -prune ) -type f -name *\.h -o -name *\.cpp" 6 | echo -n "Running dos2unix " 7 | $find_sources | xargs -I {} sh -c "dos2unix '{}' 2>/dev/null; echo -n '.'" 8 | echo 9 | echo -n "Running clang-format " 10 | 11 | $find_sources | xargs -I {} sh -c "clang-format -i {}; echo -n '.'" 12 | 13 | echo 14 | echo -n "Running cmake-format " 15 | find . -type f -name "CMakeLists.txt" -o -name "*\.cmake"|grep -v bundled|grep -v build|xargs -I {} sh -c "cmake-format --line-width 120 --tab-size 4 --max-subgroups-hwrap 4 -i {}; echo -n '.'" 16 | echo 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/async.cpp: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #ifndef SPDLOG_COMPILED_LIB 5 | #error Please define SPDLOG_COMPILED_LIB to compile this file. 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | -------------------------------------------------------------------------------- /src/bundled_fmtlib_format.cpp: -------------------------------------------------------------------------------- 1 | // Slightly modified version of fmt lib's format.cc source file. 2 | // Copyright (c) 2012 - 2016, Victor Zverovich 3 | // All rights reserved. 4 | 5 | #ifndef SPDLOG_COMPILED_LIB 6 | #error Please define SPDLOG_COMPILED_LIB to compile this file. 7 | #endif 8 | 9 | #if !defined(SPDLOG_FMT_EXTERNAL) && !defined(SPDLOG_USE_STD_FORMAT) 10 | 11 | #include 12 | 13 | FMT_BEGIN_NAMESPACE 14 | namespace detail { 15 | 16 | template FMT_API auto dragonbox::to_decimal(float x) noexcept -> dragonbox::decimal_fp; 17 | template FMT_API auto dragonbox::to_decimal(double x) noexcept -> dragonbox::decimal_fp; 18 | 19 | #if FMT_USE_LOCALE 20 | // DEPRECATED! locale_ref in the detail namespace 21 | template FMT_API locale_ref::locale_ref(const std::locale& loc); 22 | template FMT_API auto locale_ref::get() const -> std::locale; 23 | #endif 24 | 25 | // Explicit instantiations for char. 26 | 27 | template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; 28 | template FMT_API auto decimal_point_impl(locale_ref) -> char; 29 | 30 | // DEPRECATED! 31 | template FMT_API void buffer::append(const char*, const char*); 32 | 33 | // DEPRECATED! 34 | template FMT_API void vformat_to(buffer&, 35 | string_view, 36 | typename vformat_args<>::type, 37 | locale_ref); 38 | 39 | // Explicit instantiations for wchar_t. 40 | 41 | template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; 42 | template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; 43 | 44 | template FMT_API void buffer::append(const wchar_t*, const wchar_t*); 45 | 46 | } // namespace detail 47 | FMT_END_NAMESPACE 48 | 49 | #endif // !SPDLOG_FMT_EXTERNAL 50 | -------------------------------------------------------------------------------- /src/cfg.cpp: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #ifndef SPDLOG_COMPILED_LIB 5 | #error Please define SPDLOG_COMPILED_LIB to compile this file. 6 | #endif 7 | 8 | #include 9 | -------------------------------------------------------------------------------- /src/color_sinks.cpp: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #ifndef SPDLOG_COMPILED_LIB 5 | #error Please define SPDLOG_COMPILED_LIB to compile this file. 6 | #endif 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | // 13 | // color sinks 14 | // 15 | #ifdef _WIN32 16 | #include 17 | template class SPDLOG_API spdlog::sinks::wincolor_sink; 18 | template class SPDLOG_API spdlog::sinks::wincolor_sink; 19 | template class SPDLOG_API spdlog::sinks::wincolor_stdout_sink; 20 | template class SPDLOG_API spdlog::sinks::wincolor_stdout_sink; 21 | template class SPDLOG_API spdlog::sinks::wincolor_stderr_sink; 22 | template class SPDLOG_API spdlog::sinks::wincolor_stderr_sink; 23 | #else 24 | #include "spdlog/sinks/ansicolor_sink-inl.h" 25 | template class SPDLOG_API spdlog::sinks::ansicolor_sink; 26 | template class SPDLOG_API spdlog::sinks::ansicolor_sink; 27 | template class SPDLOG_API spdlog::sinks::ansicolor_stdout_sink; 28 | template class SPDLOG_API spdlog::sinks::ansicolor_stdout_sink; 29 | template class SPDLOG_API spdlog::sinks::ansicolor_stderr_sink; 30 | template class SPDLOG_API spdlog::sinks::ansicolor_stderr_sink; 31 | #endif 32 | 33 | // factory methods for color loggers 34 | #include "spdlog/sinks/stdout_color_sinks-inl.h" 35 | template SPDLOG_API std::shared_ptr 36 | spdlog::stdout_color_mt(const std::string &logger_name, 37 | color_mode mode); 38 | template SPDLOG_API std::shared_ptr 39 | spdlog::stdout_color_st(const std::string &logger_name, 40 | color_mode mode); 41 | template SPDLOG_API std::shared_ptr 42 | spdlog::stderr_color_mt(const std::string &logger_name, 43 | color_mode mode); 44 | template SPDLOG_API std::shared_ptr 45 | spdlog::stderr_color_st(const std::string &logger_name, 46 | color_mode mode); 47 | 48 | template SPDLOG_API std::shared_ptr spdlog::stdout_color_mt( 49 | const std::string &logger_name, color_mode mode); 50 | template SPDLOG_API std::shared_ptr spdlog::stdout_color_st( 51 | const std::string &logger_name, color_mode mode); 52 | template SPDLOG_API std::shared_ptr spdlog::stderr_color_mt( 53 | const std::string &logger_name, color_mode mode); 54 | template SPDLOG_API std::shared_ptr spdlog::stderr_color_st( 55 | const std::string &logger_name, color_mode mode); 56 | -------------------------------------------------------------------------------- /src/file_sinks.cpp: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #ifndef SPDLOG_COMPILED_LIB 5 | #error Please define SPDLOG_COMPILED_LIB to compile this file. 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include 14 | 15 | template class SPDLOG_API spdlog::sinks::basic_file_sink; 16 | template class SPDLOG_API spdlog::sinks::basic_file_sink; 17 | 18 | #include 19 | template class SPDLOG_API spdlog::sinks::rotating_file_sink; 20 | template class SPDLOG_API spdlog::sinks::rotating_file_sink; 21 | -------------------------------------------------------------------------------- /src/spdlog.cpp: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #ifndef SPDLOG_COMPILED_LIB 5 | #error Please define SPDLOG_COMPILED_LIB to compile this file. 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | 23 | // template instantiate logger constructor with sinks init list 24 | template SPDLOG_API spdlog::logger::logger(std::string name, 25 | sinks_init_list::iterator begin, 26 | sinks_init_list::iterator end); 27 | template class SPDLOG_API spdlog::sinks::base_sink; 28 | template class SPDLOG_API spdlog::sinks::base_sink; 29 | -------------------------------------------------------------------------------- /src/stdout_sinks.cpp: -------------------------------------------------------------------------------- 1 | // Copyright(c) 2015-present, Gabi Melman & spdlog contributors. 2 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 3 | 4 | #ifndef SPDLOG_COMPILED_LIB 5 | #error Please define SPDLOG_COMPILED_LIB to compile this file. 6 | #endif 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | template class SPDLOG_API spdlog::sinks::stdout_sink_base; 15 | template class SPDLOG_API spdlog::sinks::stdout_sink_base; 16 | template class SPDLOG_API spdlog::sinks::stdout_sink; 17 | template class SPDLOG_API spdlog::sinks::stdout_sink; 18 | template class SPDLOG_API spdlog::sinks::stderr_sink; 19 | template class SPDLOG_API spdlog::sinks::stderr_sink; 20 | 21 | template SPDLOG_API std::shared_ptr 22 | spdlog::stdout_logger_mt(const std::string &logger_name); 23 | template SPDLOG_API std::shared_ptr 24 | spdlog::stdout_logger_st(const std::string &logger_name); 25 | template SPDLOG_API std::shared_ptr 26 | spdlog::stderr_logger_mt(const std::string &logger_name); 27 | template SPDLOG_API std::shared_ptr 28 | spdlog::stderr_logger_st(const std::string &logger_name); 29 | 30 | template SPDLOG_API std::shared_ptr spdlog::stdout_logger_mt( 31 | const std::string &logger_name); 32 | template SPDLOG_API std::shared_ptr spdlog::stdout_logger_st( 33 | const std::string &logger_name); 34 | template SPDLOG_API std::shared_ptr spdlog::stderr_logger_mt( 35 | const std::string &logger_name); 36 | template SPDLOG_API std::shared_ptr spdlog::stderr_logger_st( 37 | const std::string &logger_name); 38 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.11) 2 | project(spdlog_utests CXX) 3 | 4 | if(NOT TARGET spdlog) 5 | # Stand-alone build 6 | find_package(spdlog REQUIRED) 7 | endif() 8 | 9 | include(../cmake/utils.cmake) 10 | 11 | find_package(PkgConfig) 12 | if(PkgConfig_FOUND) 13 | pkg_check_modules(systemd libsystemd) 14 | endif() 15 | 16 | find_package(Catch2 3 QUIET) 17 | if(Catch2_FOUND) 18 | message(STATUS "Packaged version of Catch will be used.") 19 | else() 20 | message(STATUS "Bundled version of Catch will be downloaded and used.") 21 | include(FetchContent) 22 | FetchContent_Declare( 23 | Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git 24 | GIT_TAG 53d0d913a422d356b23dd927547febdf69ee9081 # v3.5.0 25 | ) 26 | FetchContent_MakeAvailable(Catch2) 27 | endif() 28 | 29 | set(SPDLOG_UTESTS_SOURCES 30 | test_file_helper.cpp 31 | test_file_logging.cpp 32 | test_daily_logger.cpp 33 | test_misc.cpp 34 | test_eventlog.cpp 35 | test_pattern_formatter.cpp 36 | test_async.cpp 37 | test_registry.cpp 38 | test_macros.cpp 39 | utils.cpp 40 | main.cpp 41 | test_mpmc_q.cpp 42 | test_dup_filter.cpp 43 | test_fmt_helper.cpp 44 | test_stdout_api.cpp 45 | test_backtrace.cpp 46 | test_create_dir.cpp 47 | test_custom_callbacks.cpp 48 | test_cfg.cpp 49 | test_time_point.cpp 50 | test_stopwatch.cpp 51 | test_circular_q.cpp 52 | test_bin_to_hex.cpp) 53 | 54 | if(NOT SPDLOG_NO_EXCEPTIONS) 55 | list(APPEND SPDLOG_UTESTS_SOURCES test_errors.cpp) 56 | endif() 57 | 58 | if(systemd_FOUND) 59 | list(APPEND SPDLOG_UTESTS_SOURCES test_systemd.cpp) 60 | endif() 61 | 62 | enable_testing() 63 | 64 | function(spdlog_prepare_test test_target spdlog_lib) 65 | add_executable(${test_target} ${SPDLOG_UTESTS_SOURCES}) 66 | spdlog_enable_warnings(${test_target}) 67 | target_link_libraries(${test_target} PRIVATE ${spdlog_lib}) 68 | if(systemd_FOUND) 69 | target_link_libraries(${test_target} PRIVATE ${systemd_LIBRARIES}) 70 | endif() 71 | target_link_libraries(${test_target} PRIVATE Catch2::Catch2WithMain) 72 | if(SPDLOG_SANITIZE_ADDRESS) 73 | spdlog_enable_addr_sanitizer(${test_target}) 74 | elseif(SPDLOG_SANITIZE_THREAD) 75 | spdlog_enable_thread_sanitizer(${test_target}) 76 | endif() 77 | add_test(NAME ${test_target} COMMAND ${test_target}) 78 | set_tests_properties(${test_target} PROPERTIES RUN_SERIAL ON) 79 | endfunction() 80 | 81 | # The compiled library tests 82 | if(SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_ALL) 83 | spdlog_prepare_test(spdlog-utests spdlog::spdlog) 84 | endif() 85 | 86 | # The header-only library version tests 87 | if(SPDLOG_BUILD_TESTS_HO OR SPDLOG_BUILD_ALL) 88 | spdlog_prepare_test(spdlog-utests-ho spdlog::spdlog_header_only) 89 | endif() 90 | -------------------------------------------------------------------------------- /tests/includes.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(__GNUC__) && __GNUC__ == 12 4 | #pragma GCC diagnostic push 5 | #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // Workaround for GCC 12 6 | #endif 7 | #include 8 | #if defined(__GNUC__) && __GNUC__ == 12 9 | #pragma GCC diagnostic pop 10 | #endif 11 | 12 | #include "utils.h" 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG 25 | 26 | #include "spdlog/spdlog.h" 27 | #include "spdlog/async.h" 28 | #include "spdlog/details/fmt_helper.h" 29 | #include "spdlog/details/os.h" 30 | 31 | #ifndef SPDLOG_NO_TLS 32 | #include "spdlog/mdc.h" 33 | #endif 34 | 35 | #include "spdlog/sinks/basic_file_sink.h" 36 | #include "spdlog/sinks/daily_file_sink.h" 37 | #include "spdlog/sinks/null_sink.h" 38 | #include "spdlog/sinks/ostream_sink.h" 39 | #include "spdlog/sinks/rotating_file_sink.h" 40 | #include "spdlog/sinks/stdout_color_sinks.h" 41 | #include "spdlog/sinks/msvc_sink.h" 42 | #include "spdlog/pattern_formatter.h" 43 | -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | #if defined(__GNUC__) && __GNUC__ == 12 2 | #pragma GCC diagnostic push 3 | #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // Workaround for GCC 12 4 | #endif 5 | 6 | #include 7 | 8 | #if defined(__GNUC__) && __GNUC__ == 12 9 | #pragma GCC diagnostic pop 10 | #endif 11 | -------------------------------------------------------------------------------- /tests/test_backtrace.cpp: -------------------------------------------------------------------------------- 1 | #include "includes.h" 2 | #include "test_sink.h" 3 | #include "spdlog/async.h" 4 | 5 | TEST_CASE("bactrace1", "[bactrace]") { 6 | using spdlog::sinks::test_sink_st; 7 | auto test_sink = std::make_shared(); 8 | size_t backtrace_size = 5; 9 | 10 | spdlog::logger logger("test-backtrace", test_sink); 11 | logger.set_pattern("%v"); 12 | logger.enable_backtrace(backtrace_size); 13 | 14 | logger.info("info message"); 15 | for (int i = 0; i < 100; i++) logger.debug("debug message {}", i); 16 | 17 | REQUIRE(test_sink->lines().size() == 1); 18 | REQUIRE(test_sink->lines()[0] == "info message"); 19 | 20 | logger.dump_backtrace(); 21 | REQUIRE(test_sink->lines().size() == backtrace_size + 3); 22 | REQUIRE(test_sink->lines()[1] == "****************** Backtrace Start ******************"); 23 | REQUIRE(test_sink->lines()[2] == "debug message 95"); 24 | REQUIRE(test_sink->lines()[3] == "debug message 96"); 25 | REQUIRE(test_sink->lines()[4] == "debug message 97"); 26 | REQUIRE(test_sink->lines()[5] == "debug message 98"); 27 | REQUIRE(test_sink->lines()[6] == "debug message 99"); 28 | REQUIRE(test_sink->lines()[7] == "****************** Backtrace End ********************"); 29 | } 30 | 31 | TEST_CASE("bactrace-empty", "[bactrace]") { 32 | using spdlog::sinks::test_sink_st; 33 | auto test_sink = std::make_shared(); 34 | size_t backtrace_size = 5; 35 | 36 | spdlog::logger logger("test-backtrace", test_sink); 37 | logger.set_pattern("%v"); 38 | logger.enable_backtrace(backtrace_size); 39 | logger.dump_backtrace(); 40 | REQUIRE(test_sink->lines().size() == 0); 41 | } 42 | 43 | TEST_CASE("bactrace-async", "[bactrace]") { 44 | using spdlog::sinks::test_sink_mt; 45 | auto test_sink = std::make_shared(); 46 | using spdlog::details::os::sleep_for_millis; 47 | 48 | size_t backtrace_size = 5; 49 | 50 | spdlog::init_thread_pool(120, 1); 51 | auto logger = std::make_shared("test-bactrace-async", test_sink, 52 | spdlog::thread_pool()); 53 | logger->set_pattern("%v"); 54 | logger->enable_backtrace(backtrace_size); 55 | 56 | logger->info("info message"); 57 | for (int i = 0; i < 100; i++) logger->debug("debug message {}", i); 58 | 59 | sleep_for_millis(100); 60 | REQUIRE(test_sink->lines().size() == 1); 61 | REQUIRE(test_sink->lines()[0] == "info message"); 62 | 63 | logger->dump_backtrace(); 64 | sleep_for_millis(100); // give time for the async dump to complete 65 | REQUIRE(test_sink->lines().size() == backtrace_size + 3); 66 | REQUIRE(test_sink->lines()[1] == "****************** Backtrace Start ******************"); 67 | REQUIRE(test_sink->lines()[2] == "debug message 95"); 68 | REQUIRE(test_sink->lines()[3] == "debug message 96"); 69 | REQUIRE(test_sink->lines()[4] == "debug message 97"); 70 | REQUIRE(test_sink->lines()[5] == "debug message 98"); 71 | REQUIRE(test_sink->lines()[6] == "debug message 99"); 72 | REQUIRE(test_sink->lines()[7] == "****************** Backtrace End ********************"); 73 | } 74 | -------------------------------------------------------------------------------- /tests/test_bin_to_hex.cpp: -------------------------------------------------------------------------------- 1 | #include "includes.h" 2 | #include "test_sink.h" 3 | #include "spdlog/fmt/bin_to_hex.h" 4 | 5 | TEST_CASE("to_hex", "[to_hex]") { 6 | std::ostringstream oss; 7 | auto oss_sink = std::make_shared(oss); 8 | spdlog::logger oss_logger("oss", oss_sink); 9 | 10 | std::vector v{9, 0xa, 0xb, 0xc, 0xff, 0xff}; 11 | oss_logger.info("{}", spdlog::to_hex(v)); 12 | 13 | auto output = oss.str(); 14 | REQUIRE(ends_with(output, 15 | "0000: 09 0a 0b 0c ff ff" + std::string(spdlog::details::os::default_eol))); 16 | } 17 | 18 | TEST_CASE("to_hex_upper", "[to_hex]") { 19 | std::ostringstream oss; 20 | auto oss_sink = std::make_shared(oss); 21 | spdlog::logger oss_logger("oss", oss_sink); 22 | 23 | std::vector v{9, 0xa, 0xb, 0xc, 0xff, 0xff}; 24 | oss_logger.info("{:X}", spdlog::to_hex(v)); 25 | 26 | auto output = oss.str(); 27 | REQUIRE(ends_with(output, 28 | "0000: 09 0A 0B 0C FF FF" + std::string(spdlog::details::os::default_eol))); 29 | } 30 | 31 | TEST_CASE("to_hex_no_delimiter", "[to_hex]") { 32 | std::ostringstream oss; 33 | auto oss_sink = std::make_shared(oss); 34 | spdlog::logger oss_logger("oss", oss_sink); 35 | 36 | std::vector v{9, 0xa, 0xb, 0xc, 0xff, 0xff}; 37 | oss_logger.info("{:sX}", spdlog::to_hex(v)); 38 | 39 | auto output = oss.str(); 40 | REQUIRE( 41 | ends_with(output, "0000: 090A0B0CFFFF" + std::string(spdlog::details::os::default_eol))); 42 | } 43 | 44 | TEST_CASE("to_hex_show_ascii", "[to_hex]") { 45 | std::ostringstream oss; 46 | auto oss_sink = std::make_shared(oss); 47 | spdlog::logger oss_logger("oss", oss_sink); 48 | 49 | std::vector v{9, 0xa, 0xb, 0x41, 0xc, 0x4b, 0xff, 0xff}; 50 | oss_logger.info("{:Xsa}", spdlog::to_hex(v, 8)); 51 | 52 | REQUIRE(ends_with(oss.str(), "0000: 090A0B410C4BFFFF ...A.K.." + 53 | std::string(spdlog::details::os::default_eol))); 54 | } 55 | 56 | TEST_CASE("to_hex_different_size_per_line", "[to_hex]") { 57 | std::ostringstream oss; 58 | auto oss_sink = std::make_shared(oss); 59 | spdlog::logger oss_logger("oss", oss_sink); 60 | 61 | std::vector v{9, 0xa, 0xb, 0x41, 0xc, 0x4b, 0xff, 0xff}; 62 | 63 | oss_logger.info("{:Xsa}", spdlog::to_hex(v, 10)); 64 | REQUIRE(ends_with(oss.str(), "0000: 090A0B410C4BFFFF ...A.K.." + 65 | std::string(spdlog::details::os::default_eol))); 66 | 67 | oss_logger.info("{:Xs}", spdlog::to_hex(v, 10)); 68 | REQUIRE(ends_with(oss.str(), 69 | "0000: 090A0B410C4BFFFF" + std::string(spdlog::details::os::default_eol))); 70 | 71 | oss_logger.info("{:Xsa}", spdlog::to_hex(v, 6)); 72 | REQUIRE(ends_with( 73 | oss.str(), "0000: 090A0B410C4B ...A.K" + std::string(spdlog::details::os::default_eol) + 74 | "0006: FFFF .." + std::string(spdlog::details::os::default_eol))); 75 | 76 | oss_logger.info("{:Xs}", spdlog::to_hex(v, 6)); 77 | REQUIRE(ends_with(oss.str(), "0000: 090A0B410C4B" + 78 | std::string(spdlog::details::os::default_eol) + "0006: FFFF" + 79 | std::string(spdlog::details::os::default_eol))); 80 | } 81 | 82 | TEST_CASE("to_hex_no_ascii", "[to_hex]") { 83 | std::ostringstream oss; 84 | auto oss_sink = std::make_shared(oss); 85 | spdlog::logger oss_logger("oss", oss_sink); 86 | 87 | std::vector v{9, 0xa, 0xb, 0x41, 0xc, 0x4b, 0xff, 0xff}; 88 | oss_logger.info("{:Xs}", spdlog::to_hex(v, 8)); 89 | 90 | REQUIRE(ends_with(oss.str(), 91 | "0000: 090A0B410C4BFFFF" + std::string(spdlog::details::os::default_eol))); 92 | 93 | oss_logger.info("{:Xsna}", spdlog::to_hex(v, 8)); 94 | 95 | REQUIRE( 96 | ends_with(oss.str(), "090A0B410C4BFFFF" + std::string(spdlog::details::os::default_eol))); 97 | } 98 | -------------------------------------------------------------------------------- /tests/test_circular_q.cpp: -------------------------------------------------------------------------------- 1 | #include "includes.h" 2 | #include "spdlog/details/circular_q.h" 3 | 4 | using q_type = spdlog::details::circular_q; 5 | TEST_CASE("test_size", "[circular_q]") { 6 | const size_t q_size = 4; 7 | q_type q(q_size); 8 | REQUIRE(q.size() == 0); 9 | REQUIRE(q.empty() == true); 10 | for (size_t i = 0; i < q_size; i++) { 11 | q.push_back(std::move(i)); 12 | } 13 | REQUIRE(q.size() == q_size); 14 | q.push_back(999); 15 | REQUIRE(q.size() == q_size); 16 | } 17 | 18 | TEST_CASE("test_rolling", "[circular_q]") { 19 | const size_t q_size = 4; 20 | q_type q(q_size); 21 | 22 | for (size_t i = 0; i < q_size + 2; i++) { 23 | q.push_back(std::move(i)); 24 | } 25 | 26 | REQUIRE(q.size() == q_size); 27 | 28 | REQUIRE(q.front() == 2); 29 | q.pop_front(); 30 | 31 | REQUIRE(q.front() == 3); 32 | q.pop_front(); 33 | 34 | REQUIRE(q.front() == 4); 35 | q.pop_front(); 36 | 37 | REQUIRE(q.front() == 5); 38 | q.pop_front(); 39 | 40 | REQUIRE(q.empty()); 41 | 42 | q.push_back(6); 43 | REQUIRE(q.front() == 6); 44 | } 45 | 46 | TEST_CASE("test_empty", "[circular_q]") { 47 | q_type q(0); 48 | q.push_back(1); 49 | REQUIRE(q.empty()); 50 | } -------------------------------------------------------------------------------- /tests/test_custom_callbacks.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This content is released under the MIT License as specified in 3 | * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE 4 | */ 5 | #include "includes.h" 6 | #include "test_sink.h" 7 | #include "spdlog/sinks/callback_sink.h" 8 | #include "spdlog/async.h" 9 | #include "spdlog/common.h" 10 | 11 | TEST_CASE("custom_callback_logger", "[custom_callback_logger]") { 12 | std::vector lines; 13 | spdlog::pattern_formatter formatter; 14 | auto callback_logger = 15 | std::make_shared([&](const spdlog::details::log_msg &msg) { 16 | spdlog::memory_buf_t formatted; 17 | formatter.format(msg, formatted); 18 | auto eol_len = strlen(spdlog::details::os::default_eol); 19 | using diff_t = 20 | typename std::iterator_traits::difference_type; 21 | lines.emplace_back(formatted.begin(), formatted.end() - static_cast(eol_len)); 22 | }); 23 | std::shared_ptr test_sink(new spdlog::sinks::test_sink_st); 24 | 25 | spdlog::logger logger("test-callback", {callback_logger, test_sink}); 26 | 27 | logger.info("test message 1"); 28 | logger.info("test message 2"); 29 | logger.info("test message 3"); 30 | 31 | std::vector ref_lines = test_sink->lines(); 32 | 33 | REQUIRE(lines[0] == ref_lines[0]); 34 | REQUIRE(lines[1] == ref_lines[1]); 35 | REQUIRE(lines[2] == ref_lines[2]); 36 | spdlog::drop_all(); 37 | } 38 | -------------------------------------------------------------------------------- /tests/test_dup_filter.cpp: -------------------------------------------------------------------------------- 1 | #include "includes.h" 2 | #include "spdlog/sinks/dup_filter_sink.h" 3 | #include "test_sink.h" 4 | 5 | TEST_CASE("dup_filter_test1", "[dup_filter_sink]") { 6 | using spdlog::sinks::dup_filter_sink_st; 7 | using spdlog::sinks::test_sink_mt; 8 | 9 | dup_filter_sink_st dup_sink{std::chrono::seconds{5}}; 10 | auto test_sink = std::make_shared(); 11 | dup_sink.add_sink(test_sink); 12 | 13 | for (int i = 0; i < 10; i++) { 14 | dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message1"}); 15 | } 16 | 17 | REQUIRE(test_sink->msg_counter() == 1); 18 | } 19 | 20 | TEST_CASE("dup_filter_test2", "[dup_filter_sink]") { 21 | using spdlog::sinks::dup_filter_sink_st; 22 | using spdlog::sinks::test_sink_mt; 23 | 24 | dup_filter_sink_st dup_sink{std::chrono::seconds{0}}; 25 | auto test_sink = std::make_shared(); 26 | dup_sink.add_sink(test_sink); 27 | 28 | for (int i = 0; i < 10; i++) { 29 | dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message1"}); 30 | std::this_thread::sleep_for(std::chrono::milliseconds(5)); 31 | } 32 | 33 | REQUIRE(test_sink->msg_counter() == 10); 34 | } 35 | 36 | TEST_CASE("dup_filter_test3", "[dup_filter_sink]") { 37 | using spdlog::sinks::dup_filter_sink_st; 38 | using spdlog::sinks::test_sink_mt; 39 | 40 | dup_filter_sink_st dup_sink{std::chrono::seconds{1}}; 41 | auto test_sink = std::make_shared(); 42 | dup_sink.add_sink(test_sink); 43 | 44 | for (int i = 0; i < 10; i++) { 45 | dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message1"}); 46 | dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message2"}); 47 | } 48 | 49 | REQUIRE(test_sink->msg_counter() == 20); 50 | } 51 | 52 | TEST_CASE("dup_filter_test4", "[dup_filter_sink]") { 53 | using spdlog::sinks::dup_filter_sink_mt; 54 | using spdlog::sinks::test_sink_mt; 55 | 56 | dup_filter_sink_mt dup_sink{std::chrono::milliseconds{10}}; 57 | auto test_sink = std::make_shared(); 58 | dup_sink.add_sink(test_sink); 59 | 60 | dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message"}); 61 | std::this_thread::sleep_for(std::chrono::milliseconds(50)); 62 | dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message"}); 63 | REQUIRE(test_sink->msg_counter() == 2); 64 | } 65 | 66 | TEST_CASE("dup_filter_test5", "[dup_filter_sink]") { 67 | using spdlog::sinks::dup_filter_sink_mt; 68 | using spdlog::sinks::test_sink_mt; 69 | 70 | dup_filter_sink_mt dup_sink{std::chrono::seconds{5}}; 71 | auto test_sink = std::make_shared(); 72 | test_sink->set_pattern("%v"); 73 | dup_sink.add_sink(test_sink); 74 | 75 | dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message1"}); 76 | dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message1"}); 77 | dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message1"}); 78 | dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message2"}); 79 | 80 | REQUIRE(test_sink->msg_counter() == 81 | 3); // skip 2 messages but log the "skipped.." message before message2 82 | REQUIRE(test_sink->lines()[1] == "Skipped 2 duplicate messages.."); 83 | } 84 | -------------------------------------------------------------------------------- /tests/test_eventlog.cpp: -------------------------------------------------------------------------------- 1 | #if _WIN32 2 | 3 | #include "includes.h" 4 | #include "test_sink.h" 5 | 6 | #include "spdlog/sinks/win_eventlog_sink.h" 7 | 8 | static const LPCSTR TEST_SOURCE = "spdlog_test"; 9 | 10 | static void test_single_print(std::function do_log, 11 | std::string const &expected_contents, 12 | WORD expected_ev_type) { 13 | using namespace std::chrono; 14 | do_log(expected_contents); 15 | const auto expected_time_generated = 16 | duration_cast(system_clock::now().time_since_epoch()).count(); 17 | 18 | struct handle_t { 19 | HANDLE handle_; 20 | 21 | ~handle_t() { 22 | if (handle_) { 23 | REQUIRE(CloseEventLog(handle_)); 24 | } 25 | } 26 | } event_log{::OpenEventLogA(nullptr, TEST_SOURCE)}; 27 | 28 | REQUIRE(event_log.handle_); 29 | 30 | DWORD read_bytes{}, size_needed{}; 31 | auto ok = ::ReadEventLogA(event_log.handle_, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ, 32 | 0, &read_bytes, 0, &read_bytes, &size_needed); 33 | REQUIRE(!ok); 34 | REQUIRE(::GetLastError() == ERROR_INSUFFICIENT_BUFFER); 35 | 36 | std::vector record_buffer(size_needed); 37 | PEVENTLOGRECORD record = (PEVENTLOGRECORD)record_buffer.data(); 38 | 39 | ok = ::ReadEventLogA(event_log.handle_, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ, 0, 40 | record, size_needed, &read_bytes, &size_needed); 41 | REQUIRE(ok); 42 | 43 | REQUIRE(record->NumStrings == 1); 44 | REQUIRE(record->EventType == expected_ev_type); 45 | REQUIRE((expected_time_generated - record->TimeGenerated) <= 3u); 46 | 47 | std::string message_in_log(((char *)record + record->StringOffset)); 48 | REQUIRE(message_in_log == expected_contents + spdlog::details::os::default_eol); 49 | } 50 | 51 | TEST_CASE("eventlog", "[eventlog]") { 52 | using namespace spdlog; 53 | 54 | auto test_sink = std::make_shared(TEST_SOURCE); 55 | 56 | spdlog::logger test_logger("eventlog", test_sink); 57 | test_logger.set_level(level::trace); 58 | 59 | test_sink->set_pattern("%v"); 60 | 61 | test_single_print([&test_logger](std::string const &msg) { test_logger.trace(msg); }, 62 | "my trace message", EVENTLOG_SUCCESS); 63 | test_single_print([&test_logger](std::string const &msg) { test_logger.debug(msg); }, 64 | "my debug message", EVENTLOG_SUCCESS); 65 | test_single_print([&test_logger](std::string const &msg) { test_logger.info(msg); }, 66 | "my info message", EVENTLOG_INFORMATION_TYPE); 67 | test_single_print([&test_logger](std::string const &msg) { test_logger.warn(msg); }, 68 | "my warn message", EVENTLOG_WARNING_TYPE); 69 | test_single_print([&test_logger](std::string const &msg) { test_logger.error(msg); }, 70 | "my error message", EVENTLOG_ERROR_TYPE); 71 | test_single_print([&test_logger](std::string const &msg) { test_logger.critical(msg); }, 72 | "my critical message", EVENTLOG_ERROR_TYPE); 73 | } 74 | 75 | #endif //_WIN32 76 | -------------------------------------------------------------------------------- /tests/test_fmt_helper.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "includes.h" 3 | 4 | using spdlog::memory_buf_t; 5 | using spdlog::details::to_string_view; 6 | 7 | void test_pad2(int n, const char *expected) { 8 | memory_buf_t buf; 9 | spdlog::details::fmt_helper::pad2(n, buf); 10 | 11 | REQUIRE(to_string_view(buf) == expected); 12 | } 13 | 14 | void test_pad3(uint32_t n, const char *expected) { 15 | memory_buf_t buf; 16 | spdlog::details::fmt_helper::pad3(n, buf); 17 | 18 | REQUIRE(to_string_view(buf) == expected); 19 | } 20 | 21 | void test_pad6(std::size_t n, const char *expected) { 22 | memory_buf_t buf; 23 | spdlog::details::fmt_helper::pad6(n, buf); 24 | 25 | REQUIRE(to_string_view(buf) == expected); 26 | } 27 | 28 | void test_pad9(std::size_t n, const char *expected) { 29 | memory_buf_t buf; 30 | spdlog::details::fmt_helper::pad9(n, buf); 31 | 32 | REQUIRE(to_string_view(buf) == expected); 33 | } 34 | 35 | TEST_CASE("pad2", "[fmt_helper]") { 36 | test_pad2(0, "00"); 37 | test_pad2(3, "03"); 38 | test_pad2(10, "10"); 39 | test_pad2(23, "23"); 40 | test_pad2(99, "99"); 41 | test_pad2(100, "100"); 42 | test_pad2(123, "123"); 43 | test_pad2(1234, "1234"); 44 | test_pad2(-5, "-5"); 45 | } 46 | 47 | TEST_CASE("pad3", "[fmt_helper]") { 48 | test_pad3(0, "000"); 49 | test_pad3(3, "003"); 50 | test_pad3(10, "010"); 51 | test_pad3(23, "023"); 52 | test_pad3(99, "099"); 53 | test_pad3(100, "100"); 54 | test_pad3(123, "123"); 55 | test_pad3(999, "999"); 56 | test_pad3(1000, "1000"); 57 | test_pad3(1234, "1234"); 58 | } 59 | 60 | TEST_CASE("pad6", "[fmt_helper]") { 61 | test_pad6(0, "000000"); 62 | test_pad6(3, "000003"); 63 | test_pad6(23, "000023"); 64 | test_pad6(123, "000123"); 65 | test_pad6(1234, "001234"); 66 | test_pad6(12345, "012345"); 67 | test_pad6(123456, "123456"); 68 | } 69 | 70 | TEST_CASE("pad9", "[fmt_helper]") { 71 | test_pad9(0, "000000000"); 72 | test_pad9(3, "000000003"); 73 | test_pad9(23, "000000023"); 74 | test_pad9(123, "000000123"); 75 | test_pad9(1234, "000001234"); 76 | test_pad9(12345, "000012345"); 77 | test_pad9(123456, "000123456"); 78 | test_pad9(1234567, "001234567"); 79 | test_pad9(12345678, "012345678"); 80 | test_pad9(123456789, "123456789"); 81 | test_pad9(1234567891, "1234567891"); 82 | } 83 | -------------------------------------------------------------------------------- /tests/test_macros.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This content is released under the MIT License as specified in 3 | * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE 4 | */ 5 | 6 | #include "includes.h" 7 | 8 | #if SPDLOG_ACTIVE_LEVEL != SPDLOG_LEVEL_DEBUG 9 | #error "Invalid SPDLOG_ACTIVE_LEVEL in test. Should be SPDLOG_LEVEL_DEBUG" 10 | #endif 11 | 12 | #define TEST_FILENAME "test_logs/simple_log" 13 | 14 | TEST_CASE("debug and trace w/o format string", "[macros]") { 15 | prepare_logdir(); 16 | spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME); 17 | 18 | auto logger = spdlog::create("logger", filename); 19 | logger->set_pattern("%v"); 20 | logger->set_level(spdlog::level::trace); 21 | 22 | SPDLOG_LOGGER_TRACE(logger, "Test message 1"); 23 | SPDLOG_LOGGER_DEBUG(logger, "Test message 2"); 24 | logger->flush(); 25 | 26 | using spdlog::details::os::default_eol; 27 | REQUIRE(ends_with(file_contents(TEST_FILENAME), 28 | spdlog::fmt_lib::format("Test message 2{}", default_eol))); 29 | REQUIRE(count_lines(TEST_FILENAME) == 1); 30 | 31 | auto orig_default_logger = spdlog::default_logger(); 32 | spdlog::set_default_logger(logger); 33 | 34 | SPDLOG_TRACE("Test message 3"); 35 | SPDLOG_DEBUG("Test message {}", 4); 36 | logger->flush(); 37 | 38 | require_message_count(TEST_FILENAME, 2); 39 | REQUIRE(ends_with(file_contents(TEST_FILENAME), 40 | spdlog::fmt_lib::format("Test message 4{}", default_eol))); 41 | spdlog::set_default_logger(std::move(orig_default_logger)); 42 | } 43 | 44 | TEST_CASE("disable param evaluation", "[macros]") { 45 | SPDLOG_TRACE("Test message {}", throw std::runtime_error("Should not be evaluated")); 46 | } 47 | 48 | TEST_CASE("pass logger pointer", "[macros]") { 49 | auto logger = spdlog::create("refmacro"); 50 | auto &ref = *logger; 51 | SPDLOG_LOGGER_TRACE(&ref, "Test message 1"); 52 | SPDLOG_LOGGER_DEBUG(&ref, "Test message 2"); 53 | } 54 | -------------------------------------------------------------------------------- /tests/test_mpmc_q.cpp: -------------------------------------------------------------------------------- 1 | #include "includes.h" 2 | 3 | using std::chrono::milliseconds; 4 | using test_clock = std::chrono::high_resolution_clock; 5 | 6 | static milliseconds millis_from(const test_clock::time_point &tp0) { 7 | return std::chrono::duration_cast(test_clock::now() - tp0); 8 | } 9 | TEST_CASE("dequeue-empty-nowait", "[mpmc_blocking_q]") { 10 | size_t q_size = 100; 11 | milliseconds tolerance_wait(20); 12 | spdlog::details::mpmc_blocking_queue q(q_size); 13 | int popped_item = 0; 14 | 15 | auto start = test_clock::now(); 16 | auto rv = q.dequeue_for(popped_item, milliseconds::zero()); 17 | auto delta_ms = millis_from(start); 18 | 19 | REQUIRE(rv == false); 20 | INFO("Delta " << delta_ms.count() << " millis"); 21 | REQUIRE(delta_ms <= tolerance_wait); 22 | } 23 | 24 | TEST_CASE("dequeue-empty-wait", "[mpmc_blocking_q]") { 25 | size_t q_size = 100; 26 | milliseconds wait_ms(250); 27 | milliseconds tolerance_wait(250); 28 | 29 | spdlog::details::mpmc_blocking_queue q(q_size); 30 | int popped_item = 0; 31 | auto start = test_clock::now(); 32 | auto rv = q.dequeue_for(popped_item, wait_ms); 33 | auto delta_ms = millis_from(start); 34 | 35 | REQUIRE(rv == false); 36 | 37 | INFO("Delta " << delta_ms.count() << " millis"); 38 | REQUIRE(delta_ms >= wait_ms - tolerance_wait); 39 | REQUIRE(delta_ms <= wait_ms + tolerance_wait); 40 | } 41 | 42 | TEST_CASE("dequeue-full-nowait", "[mpmc_blocking_q]") { 43 | spdlog::details::mpmc_blocking_queue q(1); 44 | q.enqueue(42); 45 | 46 | int item = 0; 47 | q.dequeue_for(item, milliseconds::zero()); 48 | REQUIRE(item == 42); 49 | } 50 | 51 | TEST_CASE("dequeue-full-wait", "[mpmc_blocking_q]") { 52 | spdlog::details::mpmc_blocking_queue q(1); 53 | q.enqueue(42); 54 | 55 | int item = 0; 56 | q.dequeue(item); 57 | REQUIRE(item == 42); 58 | } 59 | 60 | TEST_CASE("enqueue_nowait", "[mpmc_blocking_q]") { 61 | size_t q_size = 1; 62 | spdlog::details::mpmc_blocking_queue q(q_size); 63 | milliseconds tolerance_wait(10); 64 | 65 | q.enqueue(1); 66 | REQUIRE(q.overrun_counter() == 0); 67 | 68 | auto start = test_clock::now(); 69 | q.enqueue_nowait(2); 70 | auto delta_ms = millis_from(start); 71 | 72 | INFO("Delta " << delta_ms.count() << " millis"); 73 | REQUIRE(delta_ms <= tolerance_wait); 74 | REQUIRE(q.overrun_counter() == 1); 75 | } 76 | 77 | TEST_CASE("bad_queue", "[mpmc_blocking_q]") { 78 | size_t q_size = 0; 79 | spdlog::details::mpmc_blocking_queue q(q_size); 80 | q.enqueue_nowait(1); 81 | REQUIRE(q.overrun_counter() == 1); 82 | int i = 0; 83 | REQUIRE(q.dequeue_for(i, milliseconds(0)) == false); 84 | } 85 | 86 | TEST_CASE("empty_queue", "[mpmc_blocking_q]") { 87 | size_t q_size = 10; 88 | spdlog::details::mpmc_blocking_queue q(q_size); 89 | int i = 0; 90 | REQUIRE(q.dequeue_for(i, milliseconds(10)) == false); 91 | } 92 | 93 | TEST_CASE("full_queue", "[mpmc_blocking_q]") { 94 | size_t q_size = 100; 95 | spdlog::details::mpmc_blocking_queue q(q_size); 96 | for (int i = 0; i < static_cast(q_size); i++) { 97 | q.enqueue(i + 0); // i+0 to force rvalue and avoid tidy warnings on the same time if we 98 | // std::move(i) instead 99 | } 100 | 101 | q.enqueue_nowait(123456); 102 | REQUIRE(q.overrun_counter() == 1); 103 | 104 | for (int i = 1; i < static_cast(q_size); i++) { 105 | int item = -1; 106 | q.dequeue(item); 107 | REQUIRE(item == i); 108 | } 109 | 110 | // last item pushed has overridden the oldest. 111 | int item = -1; 112 | q.dequeue(item); 113 | REQUIRE(item == 123456); 114 | } 115 | -------------------------------------------------------------------------------- /tests/test_sink.h: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright(c) 2018 Gabi Melman. 3 | // Distributed under the MIT License (http://opensource.org/licenses/MIT) 4 | // 5 | 6 | #pragma once 7 | 8 | #include "spdlog/details/null_mutex.h" 9 | #include "spdlog/sinks/base_sink.h" 10 | #include "spdlog/fmt/fmt.h" 11 | #include 12 | #include 13 | #include 14 | 15 | namespace spdlog { 16 | namespace sinks { 17 | 18 | template 19 | class test_sink : public base_sink { 20 | const size_t lines_to_save = 100; 21 | 22 | public: 23 | size_t msg_counter() { 24 | std::lock_guard lock(base_sink::mutex_); 25 | return msg_counter_; 26 | } 27 | 28 | size_t flush_counter() { 29 | std::lock_guard lock(base_sink::mutex_); 30 | return flush_counter_; 31 | } 32 | 33 | void set_delay(std::chrono::milliseconds delay) { 34 | std::lock_guard lock(base_sink::mutex_); 35 | delay_ = delay; 36 | } 37 | 38 | // return last output without the eol 39 | std::vector lines() { 40 | std::lock_guard lock(base_sink::mutex_); 41 | return lines_; 42 | } 43 | 44 | protected: 45 | void sink_it_(const details::log_msg &msg) override { 46 | memory_buf_t formatted; 47 | base_sink::formatter_->format(msg, formatted); 48 | // save the line without the eol 49 | auto eol_len = strlen(details::os::default_eol); 50 | using diff_t = typename std::iterator_traits::difference_type; 51 | if (lines_.size() < lines_to_save) { 52 | lines_.emplace_back(formatted.begin(), formatted.end() - static_cast(eol_len)); 53 | } 54 | msg_counter_++; 55 | std::this_thread::sleep_for(delay_); 56 | } 57 | 58 | void flush_() override { flush_counter_++; } 59 | 60 | size_t msg_counter_{0}; 61 | size_t flush_counter_{0}; 62 | std::chrono::milliseconds delay_{std::chrono::milliseconds::zero()}; 63 | std::vector lines_; 64 | }; 65 | 66 | using test_sink_mt = test_sink; 67 | using test_sink_st = test_sink; 68 | 69 | } // namespace sinks 70 | } // namespace spdlog 71 | -------------------------------------------------------------------------------- /tests/test_stdout_api.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This content is released under the MIT License as specified in 3 | * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE 4 | */ 5 | #include "includes.h" 6 | #include "spdlog/sinks/stdout_sinks.h" 7 | #include "spdlog/sinks/stdout_color_sinks.h" 8 | TEST_CASE("stdout_st", "[stdout]") { 9 | auto l = spdlog::stdout_logger_st("test"); 10 | l->set_pattern("%+"); 11 | l->set_level(spdlog::level::trace); 12 | l->trace("Test stdout_st"); 13 | spdlog::drop_all(); 14 | } 15 | 16 | TEST_CASE("stdout_mt", "[stdout]") { 17 | auto l = spdlog::stdout_logger_mt("test"); 18 | l->set_pattern("%+"); 19 | l->set_level(spdlog::level::debug); 20 | l->debug("Test stdout_mt"); 21 | spdlog::drop_all(); 22 | } 23 | 24 | TEST_CASE("stderr_st", "[stderr]") { 25 | auto l = spdlog::stderr_logger_st("test"); 26 | l->set_pattern("%+"); 27 | l->info("Test stderr_st"); 28 | spdlog::drop_all(); 29 | } 30 | 31 | TEST_CASE("stderr_mt", "[stderr]") { 32 | auto l = spdlog::stderr_logger_mt("test"); 33 | l->set_pattern("%+"); 34 | l->info("Test stderr_mt"); 35 | l->warn("Test stderr_mt"); 36 | l->error("Test stderr_mt"); 37 | l->critical("Test stderr_mt"); 38 | spdlog::drop_all(); 39 | } 40 | 41 | // color loggers 42 | TEST_CASE("stdout_color_st", "[stdout]") { 43 | auto l = spdlog::stdout_color_st("test"); 44 | l->set_pattern("%+"); 45 | l->info("Test stdout_color_st"); 46 | spdlog::drop_all(); 47 | } 48 | 49 | TEST_CASE("stdout_color_mt", "[stdout]") { 50 | auto l = spdlog::stdout_color_mt("test"); 51 | l->set_pattern("%+"); 52 | l->set_level(spdlog::level::trace); 53 | l->trace("Test stdout_color_mt"); 54 | spdlog::drop_all(); 55 | } 56 | 57 | TEST_CASE("stderr_color_st", "[stderr]") { 58 | auto l = spdlog::stderr_color_st("test"); 59 | l->set_pattern("%+"); 60 | l->set_level(spdlog::level::debug); 61 | l->debug("Test stderr_color_st"); 62 | spdlog::drop_all(); 63 | } 64 | 65 | TEST_CASE("stderr_color_mt", "[stderr]") { 66 | auto l = spdlog::stderr_color_mt("test"); 67 | l->set_pattern("%+"); 68 | l->info("Test stderr_color_mt"); 69 | l->warn("Test stderr_color_mt"); 70 | l->error("Test stderr_color_mt"); 71 | l->critical("Test stderr_color_mt"); 72 | spdlog::drop_all(); 73 | } 74 | 75 | #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT 76 | 77 | TEST_CASE("wchar_api", "[stdout]") { 78 | auto l = spdlog::stdout_logger_st("wchar_logger"); 79 | l->set_pattern("%+"); 80 | l->set_level(spdlog::level::trace); 81 | l->trace(L"Test wchar_api"); 82 | l->trace(L"Test wchar_api {}", L"param"); 83 | l->trace(L"Test wchar_api {}", 1); 84 | l->trace(L"Test wchar_api {}", std::wstring{L"wstring param"}); 85 | l->trace(std::wstring{L"Test wchar_api wstring"}); 86 | SPDLOG_LOGGER_DEBUG(l, L"Test SPDLOG_LOGGER_DEBUG {}", L"param"); 87 | spdlog::drop_all(); 88 | } 89 | 90 | #endif 91 | -------------------------------------------------------------------------------- /tests/test_stopwatch.cpp: -------------------------------------------------------------------------------- 1 | #include "includes.h" 2 | #include "test_sink.h" 3 | #include "spdlog/stopwatch.h" 4 | 5 | TEST_CASE("stopwatch1", "[stopwatch]") { 6 | using std::chrono::milliseconds; 7 | using clock = std::chrono::steady_clock; 8 | milliseconds wait_ms(500); 9 | milliseconds tolerance_ms(250); 10 | auto start = clock::now(); 11 | spdlog::stopwatch sw; 12 | std::this_thread::sleep_for(wait_ms); 13 | auto stop = clock::now(); 14 | auto diff_ms = std::chrono::duration_cast(stop - start); 15 | REQUIRE(sw.elapsed() >= diff_ms); 16 | REQUIRE(sw.elapsed() <= diff_ms + tolerance_ms); 17 | } 18 | 19 | TEST_CASE("stopwatch2", "[stopwatch]") { 20 | using spdlog::sinks::test_sink_st; 21 | using std::chrono::duration_cast; 22 | using std::chrono::milliseconds; 23 | using clock = std::chrono::steady_clock; 24 | 25 | clock::duration wait_duration(milliseconds(500)); 26 | clock::duration tolerance_duration(milliseconds(250)); 27 | 28 | auto test_sink = std::make_shared(); 29 | 30 | auto start = clock::now(); 31 | spdlog::stopwatch sw; 32 | spdlog::logger logger("test-stopwatch", test_sink); 33 | logger.set_pattern("%v"); 34 | std::this_thread::sleep_for(wait_duration); 35 | auto stop = clock::now(); 36 | logger.info("{}", sw); 37 | auto val = std::stod(test_sink->lines()[0]); 38 | auto diff_duration = duration_cast>(stop - start); 39 | 40 | REQUIRE(val >= (diff_duration).count() - 0.001); 41 | REQUIRE(val <= (diff_duration + tolerance_duration).count()); 42 | } 43 | -------------------------------------------------------------------------------- /tests/test_systemd.cpp: -------------------------------------------------------------------------------- 1 | #include "includes.h" 2 | #include "spdlog/sinks/systemd_sink.h" 3 | 4 | TEST_CASE("systemd", "[all]") { 5 | auto systemd_sink = std::make_shared(); 6 | spdlog::logger logger("spdlog_systemd_test", systemd_sink); 7 | logger.set_level(spdlog::level::trace); 8 | logger.trace("test spdlog trace"); 9 | logger.debug("test spdlog debug"); 10 | SPDLOG_LOGGER_INFO((&logger), "test spdlog info"); 11 | SPDLOG_LOGGER_WARN((&logger), "test spdlog warn"); 12 | SPDLOG_LOGGER_ERROR((&logger), "test spdlog error"); 13 | SPDLOG_LOGGER_CRITICAL((&logger), "test spdlog critical"); 14 | } 15 | -------------------------------------------------------------------------------- /tests/test_time_point.cpp: -------------------------------------------------------------------------------- 1 | #include "includes.h" 2 | #include "test_sink.h" 3 | #include "spdlog/async.h" 4 | 5 | TEST_CASE("time_point1", "[time_point log_msg]") { 6 | std::shared_ptr test_sink(new spdlog::sinks::test_sink_st); 7 | spdlog::logger logger("test-time_point", test_sink); 8 | 9 | spdlog::source_loc source{}; 10 | std::chrono::system_clock::time_point tp{std::chrono::system_clock::now()}; 11 | test_sink->set_pattern("%T.%F"); // interested in the time_point 12 | 13 | // all the following should have the same time 14 | test_sink->set_delay(std::chrono::milliseconds(10)); 15 | for (int i = 0; i < 5; i++) { 16 | spdlog::details::log_msg msg{tp, source, "test_logger", spdlog::level::info, "message"}; 17 | test_sink->log(msg); 18 | } 19 | 20 | logger.log(tp, source, spdlog::level::info, "formatted message"); 21 | logger.log(tp, source, spdlog::level::info, "formatted message"); 22 | logger.log(tp, source, spdlog::level::info, "formatted message"); 23 | logger.log(tp, source, spdlog::level::info, "formatted message"); 24 | logger.log(source, spdlog::level::info, 25 | "formatted message"); // last line has different time_point 26 | 27 | // now the real test... that the times are the same. 28 | std::vector lines = test_sink->lines(); 29 | REQUIRE(lines[0] == lines[1]); 30 | REQUIRE(lines[2] == lines[3]); 31 | REQUIRE(lines[4] == lines[5]); 32 | REQUIRE(lines[6] == lines[7]); 33 | REQUIRE(lines[8] != lines[9]); 34 | spdlog::drop_all(); 35 | } 36 | -------------------------------------------------------------------------------- /tests/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "includes.h" 2 | 3 | #ifdef _WIN32 4 | #include 5 | #else 6 | #include 7 | #include 8 | #endif 9 | 10 | void prepare_logdir() { 11 | spdlog::drop_all(); 12 | #ifdef _WIN32 13 | system("rmdir /S /Q test_logs"); 14 | #else 15 | auto rv = system("rm -rf test_logs"); 16 | if (rv != 0) { 17 | throw std::runtime_error("Failed to rm -rf test_logs"); 18 | } 19 | #endif 20 | } 21 | 22 | std::string file_contents(const std::string &filename) { 23 | std::ifstream ifs(filename, std::ios_base::binary); 24 | if (!ifs) { 25 | throw std::runtime_error("Failed open file "); 26 | } 27 | return std::string((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); 28 | } 29 | 30 | std::size_t count_lines(const std::string &filename) { 31 | std::ifstream ifs(filename); 32 | if (!ifs) { 33 | throw std::runtime_error("Failed open file "); 34 | } 35 | 36 | std::string line; 37 | size_t counter = 0; 38 | while (std::getline(ifs, line)) counter++; 39 | return counter; 40 | } 41 | 42 | void require_message_count(const std::string &filename, const std::size_t messages) { 43 | if (strlen(spdlog::details::os::default_eol) == 0) { 44 | REQUIRE(count_lines(filename) == 1); 45 | } else { 46 | REQUIRE(count_lines(filename) == messages); 47 | } 48 | } 49 | 50 | std::size_t get_filesize(const std::string &filename) { 51 | std::ifstream ifs(filename, std::ifstream::ate | std::ifstream::binary); 52 | if (!ifs) { 53 | throw std::runtime_error("Failed open file " + filename); 54 | } 55 | return static_cast(ifs.tellg()); 56 | } 57 | 58 | // source: https://stackoverflow.com/a/2072890/192001 59 | bool ends_with(std::string const &value, std::string const &ending) { 60 | if (ending.size() > value.size()) { 61 | return false; 62 | } 63 | return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); 64 | } 65 | 66 | #ifdef _WIN32 67 | // Based on: https://stackoverflow.com/a/37416569/192001 68 | std::size_t count_files(const std::string &folder) { 69 | size_t counter = 0; 70 | WIN32_FIND_DATAA ffd; 71 | 72 | // Start iterating over the files in the folder directory. 73 | HANDLE hFind = ::FindFirstFileA((folder + "\\*").c_str(), &ffd); 74 | if (hFind != INVALID_HANDLE_VALUE) { 75 | do // Managed to locate and create an handle to that folder. 76 | { 77 | if (ffd.cFileName[0] != '.') counter++; 78 | } while (::FindNextFileA(hFind, &ffd) != 0); 79 | ::FindClose(hFind); 80 | } else { 81 | throw std::runtime_error("Failed open folder " + folder); 82 | } 83 | 84 | return counter; 85 | } 86 | #else 87 | // Based on: https://stackoverflow.com/a/2802255/192001 88 | std::size_t count_files(const std::string &folder) { 89 | size_t counter = 0; 90 | DIR *dp = opendir(folder.c_str()); 91 | if (dp == nullptr) { 92 | throw std::runtime_error("Failed open folder " + folder); 93 | } 94 | 95 | struct dirent *ep = nullptr; 96 | while ((ep = readdir(dp)) != nullptr) { 97 | if (ep->d_name[0] != '.') counter++; 98 | } 99 | (void)closedir(dp); 100 | return counter; 101 | } 102 | #endif 103 | -------------------------------------------------------------------------------- /tests/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | std::size_t count_files(const std::string &folder); 7 | 8 | void prepare_logdir(); 9 | 10 | std::string file_contents(const std::string &filename); 11 | 12 | std::size_t count_lines(const std::string &filename); 13 | 14 | void require_message_count(const std::string &filename, const std::size_t messages); 15 | 16 | std::size_t get_filesize(const std::string &filename); 17 | 18 | bool ends_with(std::string const &value, std::string const &ending); --------------------------------------------------------------------------------