diff --git a/inc/spdlog/.clang-format b/inc/spdlog/.clang-format new file mode 100644 index 0000000..c8c345f --- /dev/null +++ b/inc/spdlog/.clang-format @@ -0,0 +1,19 @@ +--- +Language: Cpp +BasedOnStyle: Google +AccessModifierOffset: -4 +Standard: c++17 +IndentWidth: 4 +TabWidth: 4 +UseTab: Never +ColumnLimit: 100 +AlignAfterOpenBracket: Align +BinPackParameters: false +AlignEscapedNewlines: Left +AlwaysBreakTemplateDeclarations: Yes +PackConstructorInitializers: Never +BreakConstructorInitializersBeforeComma: false +IndentPPDirectives: BeforeHash +SortIncludes: Never +... + diff --git a/inc/spdlog/.clang-tidy b/inc/spdlog/.clang-tidy new file mode 100644 index 0000000..34239d6 --- /dev/null +++ b/inc/spdlog/.clang-tidy @@ -0,0 +1,54 @@ +Checks: 'cppcoreguidelines-*, +performance-*, +modernize-*, +google-*, +misc-* +cert-*, +readability-*, +clang-analyzer-*, +-performance-unnecessary-value-param, +-modernize-use-trailing-return-type, +-google-runtime-references, +-misc-non-private-member-variables-in-classes, +-readability-braces-around-statements, +-google-readability-braces-around-statements, +-cppcoreguidelines-avoid-magic-numbers, +-readability-magic-numbers, +-readability-magic-numbers, +-cppcoreguidelines-pro-type-vararg, +-cppcoreguidelines-pro-bounds-pointer-arithmetic, +-cppcoreguidelines-avoid-c-arrays, +-modernize-avoid-c-arrays, +-cppcoreguidelines-pro-bounds-array-to-pointer-decay, +-readability-named-parameter, +-cert-env33-c +' + + +WarningsAsErrors: '' +HeaderFilterRegex: '*spdlog/[^f].*' +AnalyzeTemporaryDtors: false +FormatStyle: none + +CheckOptions: + - key: google-readability-braces-around-statements.ShortStatementLines + value: '1' + - key: google-readability-function-size.StatementThreshold + value: '800' + - key: google-readability-namespace-comments.ShortNamespaceLines + value: '10' + - key: google-readability-namespace-comments.SpacesBeforeComments + value: '2' + - key: modernize-loop-convert.MaxCopySize + value: '16' + - key: modernize-loop-convert.MinConfidence + value: reasonable + - key: modernize-loop-convert.NamingStyle + value: CamelCase + - key: modernize-pass-by-value.IncludeStyle + value: llvm + - key: modernize-replace-auto-ptr.IncludeStyle + value: llvm + - key: modernize-use-nullptr.NullMacros + value: 'NULL' + diff --git a/inc/spdlog/.git-blame-ignore-revs b/inc/spdlog/.git-blame-ignore-revs new file mode 100644 index 0000000..47b8fed --- /dev/null +++ b/inc/spdlog/.git-blame-ignore-revs @@ -0,0 +1,6 @@ +# clang-format +1a0bfc7a89f2d58e22605a4dc7e18a9a555b65aa +95c226e9c92928e20ccdac0d060e7241859e282b +9d52261185b5f2c454c381d626ec5c84d7b195f4 +4b2a8219d5d1b40062d030441adde7d1fb0d4f84 +0a53eafe18d983c7c8ba4cadd02d0cc7f7308f28 diff --git a/inc/spdlog/.gitattributes b/inc/spdlog/.gitattributes new file mode 100644 index 0000000..fe505b2 --- /dev/null +++ b/inc/spdlog/.gitattributes @@ -0,0 +1 @@ +* text=false diff --git a/inc/spdlog/.gitignore b/inc/spdlog/.gitignore new file mode 100644 index 0000000..e31f5eb --- /dev/null +++ b/inc/spdlog/.gitignore @@ -0,0 +1,95 @@ +# Auto generated files +[Dd]ebug/ +[Rr]elease/ +build/* +*.slo +*.lo +*.o +*.obj +*.suo +*.tlog +*.ilk +*.log +*.pdb +*.idb +*.iobj +*.ipdb +*.opensdf +*.sdf + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Codelite +.codelite + +# KDevelop +*.kdev4 + +# .orig files +*.orig + +# example files +example/* +!example/example.cpp +!example/bench.cpp +!example/utils.h +!example/Makefile* +!example/example.sln +!example/example.vcxproj +!example/CMakeLists.txt +!example/meson.build +!example/multisink.cpp +!example/jni + +# generated files +generated +version.rc + +# Cmake +CMakeCache.txt +CMakeFiles +CMakeScripts +Makefile +cmake_install.cmake +install_manifest.txt +/tests/tests.VC.VC.opendb +/tests/tests.VC.db +/tests/tests +/tests/logs/* +spdlogConfig.cmake +spdlogConfigVersion.cmake +compile_commands.json + +# idea +.idea/ +.cache/ +.vscode/ +cmake-build-*/ +*.db +*.ipch +*.filters +*.db-wal +*.opendb +*.db-shm +*.vcxproj +*.tcl +*.user +*.sln + +# macos +*.DS_store +*.xcodeproj/ diff --git a/inc/spdlog/CMakeLists.txt b/inc/spdlog/CMakeLists.txt new file mode 100644 index 0000000..ba06a0a --- /dev/null +++ b/inc/spdlog/CMakeLists.txt @@ -0,0 +1,361 @@ +# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT) +set(DETOURS_INCLUDE_DIRS "D:\\codes\\vcpkg\\installed\\x64-windows\\include") +set(DETOURS_LIBRARY "D:/codes/vcpkg/installed/x64-windows/debug/lib/detours.lib") +set(CMAKE_PREFIX_PATH "D:\\codes\\vcpkg\\installed\\x64-windows\\share\\nlohmann_json") +cmake_minimum_required(VERSION 3.11) + +# --------------------------------------------------------------------------------------- +# Start spdlog project +# --------------------------------------------------------------------------------------- +include(cmake/utils.cmake) +include(cmake/ide.cmake) + +spdlog_extract_version() + +project(spdlog VERSION ${SPDLOG_VERSION} LANGUAGES CXX) +message(STATUS "Build spdlog: ${SPDLOG_VERSION}") + +include(GNUInstallDirs) + +# --------------------------------------------------------------------------------------- +# Set default build to release +# --------------------------------------------------------------------------------------- +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE) +endif() + +# --------------------------------------------------------------------------------------- +# Compiler config +# --------------------------------------------------------------------------------------- +if(SPDLOG_USE_STD_FORMAT) + set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD_REQUIRED ON) +elseif(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 11) + set(CMAKE_CXX_STANDARD_REQUIRED ON) +endif() + +# make sure __cplusplus is defined when using msvc and enable parallel build +if(MSVC) + string(APPEND CMAKE_CXX_FLAGS " /Zc:__cplusplus /MP") +endif() + +set(CMAKE_CXX_EXTENSIONS OFF) + +if(CMAKE_SYSTEM_NAME MATCHES "CYGWIN" OR CMAKE_SYSTEM_NAME MATCHES "MSYS") + set(CMAKE_CXX_EXTENSIONS ON) +endif() + +# --------------------------------------------------------------------------------------- +# Set SPDLOG_MASTER_PROJECT to ON if we are building spdlog +# --------------------------------------------------------------------------------------- +# Check if spdlog is being used directly or via add_subdirectory, but allow overriding +if(NOT DEFINED SPDLOG_MASTER_PROJECT) + if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) + set(SPDLOG_MASTER_PROJECT ON) + else() + set(SPDLOG_MASTER_PROJECT OFF) + endif() +endif() + +option(SPDLOG_BUILD_ALL "Build all artifacts" OFF) + +# build shared option +option(SPDLOG_BUILD_SHARED "Build shared library" OFF) + +# precompiled headers option +option(SPDLOG_ENABLE_PCH "Build static or shared library using precompiled header to speed up compilation time" OFF) + +# build position independent code +option(SPDLOG_BUILD_PIC "Build position independent code (-fPIC)" OFF) + +# example options +option(SPDLOG_BUILD_EXAMPLE "Build example" ${SPDLOG_MASTER_PROJECT}) +option(SPDLOG_BUILD_EXAMPLE_HO "Build header only example" OFF) + +# testing options +option(SPDLOG_BUILD_TESTS "Build tests" OFF) +option(SPDLOG_BUILD_TESTS_HO "Build tests using the header only version" OFF) + +# bench options +option(SPDLOG_BUILD_BENCH "Build benchmarks (Requires https://github.com/google/benchmark.git to be installed)" OFF) + +# sanitizer options +option(SPDLOG_SANITIZE_ADDRESS "Enable address sanitizer in tests" OFF) + +# warning options +option(SPDLOG_BUILD_WARNINGS "Enable compiler warnings" OFF) + +# install options +option(SPDLOG_SYSTEM_INCLUDES "Include as system headers (skip for clang-tidy)." OFF) +option(SPDLOG_INSTALL "Generate the install target" ${SPDLOG_MASTER_PROJECT}) +option(SPDLOG_USE_STD_FORMAT "Use std::format instead of fmt library." OFF) +option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF) +option(SPDLOG_FMT_EXTERNAL_HO "Use external fmt header-only library instead of bundled" OFF) +option(SPDLOG_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF) + +if(SPDLOG_FMT_EXTERNAL AND SPDLOG_FMT_EXTERNAL_HO) + message(FATAL_ERROR "SPDLOG_FMT_EXTERNAL and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive") +endif() + +if(SPDLOG_USE_STD_FORMAT AND SPDLOG_FMT_EXTERNAL_HO) + message(FATAL_ERROR "SPDLOG_USE_STD_FORMAT and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive") +endif() + +if(SPDLOG_USE_STD_FORMAT AND SPDLOG_FMT_EXTERNAL) + message(FATAL_ERROR "SPDLOG_USE_STD_FORMAT and SPDLOG_FMT_EXTERNAL are mutually exclusive") +endif() + +# misc tweakme options +if(WIN32) + option(SPDLOG_WCHAR_SUPPORT "Support wchar api" OFF) + option(SPDLOG_WCHAR_FILENAMES "Support wchar filenames" OFF) +else() + set(SPDLOG_WCHAR_SUPPORT OFF CACHE BOOL "non supported option" FORCE) + set(SPDLOG_WCHAR_FILENAMES OFF CACHE BOOL "non supported option" FORCE) +endif() + +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + option(SPDLOG_CLOCK_COARSE "Use CLOCK_REALTIME_COARSE instead of the regular clock," OFF) +else() + set(SPDLOG_CLOCK_COARSE OFF CACHE BOOL "non supported option" FORCE) +endif() + +option(SPDLOG_PREVENT_CHILD_FD "Prevent from child processes to inherit log file descriptors" OFF) +option(SPDLOG_NO_THREAD_ID "prevent spdlog from querying the thread id on each log call if thread id is not needed" OFF) +option(SPDLOG_NO_TLS "prevent spdlog from using thread local storage" OFF) +option( + SPDLOG_NO_ATOMIC_LEVELS + "prevent spdlog from using of std::atomic log levels (use only if your code never modifies log levels concurrently" + OFF) +option(SPDLOG_DISABLE_DEFAULT_LOGGER "Disable default logger creation" OFF) + +# clang-tidy +option(SPDLOG_TIDY "run clang-tidy" OFF) + +if(SPDLOG_TIDY) + set(CMAKE_CXX_CLANG_TIDY "clang-tidy") + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + message(STATUS "Enabled clang-tidy") +endif() + +if(SPDLOG_BUILD_PIC) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) +endif() + +find_package(Threads REQUIRED) +message(STATUS "Build type: " ${CMAKE_BUILD_TYPE}) +# --------------------------------------------------------------------------------------- +# Static/Shared library +# --------------------------------------------------------------------------------------- +set(SPDLOG_SRCS src/spdlog.cpp src/stdout_sinks.cpp src/color_sinks.cpp src/file_sinks.cpp src/async.cpp src/cfg.cpp) + +if(NOT SPDLOG_USE_STD_FORMAT AND NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO) + list(APPEND SPDLOG_SRCS src/bundled_fmtlib_format.cpp) +endif() + +if(SPDLOG_BUILD_SHARED OR BUILD_SHARED_LIBS) + if(WIN32) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/version.rc.in ${CMAKE_CURRENT_BINARY_DIR}/version.rc @ONLY) + list(APPEND SPDLOG_SRCS ${CMAKE_CURRENT_BINARY_DIR}/version.rc) + endif() + add_library(spdlog SHARED ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS}) + target_compile_definitions(spdlog PUBLIC SPDLOG_SHARED_LIB) + if(MSVC) + target_compile_options(spdlog PUBLIC $<$,$>>:/wd4251 + /wd4275>) + endif() + if(NOT SPDLOG_USE_STD_FORMAT AND NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO) + target_compile_definitions(spdlog PRIVATE FMT_EXPORT PUBLIC FMT_SHARED) + endif() +else() + add_library(spdlog STATIC ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS}) +endif() + +add_library(spdlog::spdlog ALIAS spdlog) + +set(SPDLOG_INCLUDES_LEVEL "") +if(SPDLOG_SYSTEM_INCLUDES) + set(SPDLOG_INCLUDES_LEVEL "SYSTEM") +endif() + +target_compile_definitions(spdlog PUBLIC SPDLOG_COMPILED_LIB) +target_include_directories(spdlog ${SPDLOG_INCLUDES_LEVEL} PUBLIC "$" + "$") +target_link_libraries(spdlog PUBLIC Threads::Threads) +spdlog_enable_warnings(spdlog) + +set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION + ${SPDLOG_VERSION_MAJOR}.${SPDLOG_VERSION_MINOR}) +set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX d) + +if(COMMAND target_precompile_headers AND SPDLOG_ENABLE_PCH) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/pch.h.in ${PROJECT_BINARY_DIR}/spdlog_pch.h @ONLY) + target_precompile_headers(spdlog PRIVATE ${PROJECT_BINARY_DIR}/spdlog_pch.h) +endif() + +# --------------------------------------------------------------------------------------- +# Header only version +# --------------------------------------------------------------------------------------- +add_library(spdlog_header_only INTERFACE) +add_library(spdlog::spdlog_header_only ALIAS spdlog_header_only) + +target_include_directories( + spdlog_header_only ${SPDLOG_INCLUDES_LEVEL} INTERFACE "$" + "$") +target_link_libraries(spdlog_header_only INTERFACE Threads::Threads) + +# --------------------------------------------------------------------------------------- +# Use fmt package if using external fmt +# --------------------------------------------------------------------------------------- +if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO) + if(NOT TARGET fmt::fmt) + find_package(fmt CONFIG REQUIRED) + endif() + target_compile_definitions(spdlog PUBLIC SPDLOG_FMT_EXTERNAL) + target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FMT_EXTERNAL) + + # use external fmt-header-nly + if(SPDLOG_FMT_EXTERNAL_HO) + target_link_libraries(spdlog PUBLIC fmt::fmt-header-only) + target_link_libraries(spdlog_header_only INTERFACE fmt::fmt-header-only) + else() # use external compile fmt + target_link_libraries(spdlog PUBLIC fmt::fmt) + target_link_libraries(spdlog_header_only INTERFACE fmt::fmt) + endif() + + set(PKG_CONFIG_REQUIRES fmt) # add dependency to pkg-config +endif() + +# --------------------------------------------------------------------------------------- +# Add required libraries for Android CMake build +# --------------------------------------------------------------------------------------- +if(ANDROID) + target_link_libraries(spdlog PUBLIC log) + target_link_libraries(spdlog_header_only INTERFACE log) +endif() + +# --------------------------------------------------------------------------------------- +# Misc definitions according to tweak options +# --------------------------------------------------------------------------------------- +set(SPDLOG_WCHAR_TO_UTF8_SUPPORT ${SPDLOG_WCHAR_SUPPORT}) +foreach( + SPDLOG_OPTION + SPDLOG_WCHAR_TO_UTF8_SUPPORT + SPDLOG_WCHAR_FILENAMES + SPDLOG_NO_EXCEPTIONS + SPDLOG_CLOCK_COARSE + SPDLOG_PREVENT_CHILD_FD + SPDLOG_NO_THREAD_ID + SPDLOG_NO_TLS + SPDLOG_NO_ATOMIC_LEVELS + SPDLOG_DISABLE_DEFAULT_LOGGER + SPDLOG_USE_STD_FORMAT) + if(${SPDLOG_OPTION}) + target_compile_definitions(spdlog PUBLIC ${SPDLOG_OPTION}) + target_compile_definitions(spdlog_header_only INTERFACE ${SPDLOG_OPTION}) + endif() +endforeach() + +# --------------------------------------------------------------------------------------- +# If exceptions are disabled, disable them in the bundled fmt as well +# --------------------------------------------------------------------------------------- +if(SPDLOG_NO_EXCEPTIONS) + if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO) + target_compile_definitions(spdlog PUBLIC FMT_EXCEPTIONS=0) + endif() + if(NOT MSVC) + target_compile_options(spdlog PRIVATE -fno-exceptions) + else() + target_compile_options(spdlog PRIVATE /EHsc) + endif() +endif() +# --------------------------------------------------------------------------------------- +# Build binaries +# --------------------------------------------------------------------------------------- +if(SPDLOG_BUILD_EXAMPLE OR SPDLOG_BUILD_EXAMPLE_HO OR SPDLOG_BUILD_ALL) + message(STATUS "Generating example(s)") + add_subdirectory(example) + spdlog_enable_warnings(example) + if(SPDLOG_BUILD_EXAMPLE_HO) + spdlog_enable_warnings(example_header_only) + endif() +endif() + +if(SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_TESTS_HO OR SPDLOG_BUILD_ALL) + message(STATUS "Generating tests") + enable_testing() + add_subdirectory(tests) +endif() + +if(SPDLOG_BUILD_BENCH OR SPDLOG_BUILD_ALL) + message(STATUS "Generating benchmarks") + add_subdirectory(bench) +endif() + +# --------------------------------------------------------------------------------------- +# Install +# --------------------------------------------------------------------------------------- +if(SPDLOG_INSTALL) + message(STATUS "Generating install") + set(project_config_in "${CMAKE_CURRENT_LIST_DIR}/cmake/spdlogConfig.cmake.in") + set(project_config_out "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfig.cmake") + set(config_targets_file "spdlogConfigTargets.cmake") + set(version_config_file "${CMAKE_CURRENT_BINARY_DIR}/spdlogConfigVersion.cmake") + set(export_dest_dir "${CMAKE_INSTALL_LIBDIR}/cmake/spdlog") + set(pkgconfig_install_dir "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc") + + # --------------------------------------------------------------------------------------- + # Include files + # --------------------------------------------------------------------------------------- + install(DIRECTORY include/ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" PATTERN "fmt/bundled" EXCLUDE) + install( + TARGETS spdlog spdlog_header_only + EXPORT spdlog + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + + if(NOT SPDLOG_USE_STD_FORMAT AND NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO) + install(DIRECTORY include/${PROJECT_NAME}/fmt/bundled/ + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/fmt/bundled/") + endif() + + # --------------------------------------------------------------------------------------- + # Install pkg-config file + # --------------------------------------------------------------------------------------- + if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") + set(PKG_CONFIG_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}") + else() + set(PKG_CONFIG_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") + endif() + if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") + set(PKG_CONFIG_LIBDIR "${CMAKE_INSTALL_LIBDIR}") + else() + set(PKG_CONFIG_LIBDIR "\${exec_prefix}/${CMAKE_INSTALL_LIBDIR}") + endif() + get_target_property(PKG_CONFIG_DEFINES spdlog INTERFACE_COMPILE_DEFINITIONS) + string(REPLACE ";" " -D" PKG_CONFIG_DEFINES "${PKG_CONFIG_DEFINES}") + string(CONCAT PKG_CONFIG_DEFINES "-D" "${PKG_CONFIG_DEFINES}") + configure_file("cmake/${PROJECT_NAME}.pc.in" "${pkg_config}" @ONLY) + install(FILES "${pkg_config}" DESTINATION "${pkgconfig_install_dir}") + + # --------------------------------------------------------------------------------------- + # Install CMake config files + # --------------------------------------------------------------------------------------- + export(TARGETS spdlog spdlog_header_only NAMESPACE spdlog:: + FILE "${CMAKE_CURRENT_BINARY_DIR}/${config_targets_file}") + install(EXPORT spdlog DESTINATION ${export_dest_dir} NAMESPACE spdlog:: FILE ${config_targets_file}) + + include(CMakePackageConfigHelpers) + configure_package_config_file("${project_config_in}" "${project_config_out}" INSTALL_DESTINATION ${export_dest_dir}) + + write_basic_package_version_file("${version_config_file}" COMPATIBILITY SameMajorVersion) + install(FILES "${project_config_out}" "${version_config_file}" DESTINATION "${export_dest_dir}") + + # --------------------------------------------------------------------------------------- + # Support creation of installable packages + # --------------------------------------------------------------------------------------- + include(cmake/spdlogCPack.cmake) +endif() diff --git a/inc/spdlog/INSTALL b/inc/spdlog/INSTALL new file mode 100644 index 0000000..20a9b4c --- /dev/null +++ b/inc/spdlog/INSTALL @@ -0,0 +1,24 @@ +Header only version: +================================================================== +Just copy the files to your build tree and use a C++11 compiler. +Or use CMake: + add_executable(example_header_only example.cpp) + target_link_libraries(example_header_only spdlog::spdlog_header_only) + + +Compiled library version: +================================================================== +CMake: + add_executable(example example.cpp) + target_link_libraries(example spdlog::spdlog) + +Or copy files src/*.cpp to your build tree and pass the -DSPDLOG_COMPILED_LIB to the compiler. + +Tested on: +gcc 4.8.1 and above +clang 3.5 +Visual Studio 2013 + + + + diff --git a/inc/spdlog/LICENSE b/inc/spdlog/LICENSE new file mode 100644 index 0000000..c15941b --- /dev/null +++ b/inc/spdlog/LICENSE @@ -0,0 +1,26 @@ +The MIT License (MIT) + +Copyright (c) 2016 Gabi Melman. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +-- NOTE: Third party dependency used by this software -- +This software depends on the fmt lib (MIT License), +and users must comply to its license: https://github.com/fmtlib/fmt/blob/master/LICENSE.rst + diff --git a/inc/spdlog/README.md b/inc/spdlog/README.md new file mode 100644 index 0000000..6ec69bd --- /dev/null +++ b/inc/spdlog/README.md @@ -0,0 +1,500 @@ +# spdlog + +Very fast, header-only/compiled, C++ logging library. [![ci](https://github.com/gabime/spdlog/actions/workflows/ci.yml/badge.svg)](https://github.com/gabime/spdlog/actions/workflows/ci.yml)  [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true&branch=v1.x)](https://ci.appveyor.com/project/gabime/spdlog) [![Release](https://img.shields.io/github/release/gabime/spdlog.svg)](https://github.com/gabime/spdlog/releases/latest) + +## Install +#### Header-only version +Copy the include [folder](https://github.com/gabime/spdlog/tree/v1.x/include/spdlog) to your build tree and use a C++11 compiler. + +#### Compiled version (recommended - much faster compile times) +```console +$ git clone https://github.com/gabime/spdlog.git +$ cd spdlog && mkdir build && cd build +$ cmake .. && make -j +``` +see example [CMakeLists.txt](https://github.com/gabime/spdlog/blob/v1.x/example/CMakeLists.txt) on how to use. + +## Platforms +* Linux, FreeBSD, OpenBSD, Solaris, AIX +* Windows (msvc 2013+, cygwin) +* macOS (clang 3.5+) +* Android + +## Package managers: +* Debian: `sudo apt install libspdlog-dev` +* Homebrew: `brew install spdlog` +* MacPorts: `sudo port install spdlog` +* FreeBSD: `pkg install spdlog` +* Fedora: `dnf install spdlog` +* Gentoo: `emerge dev-libs/spdlog` +* Arch Linux: `pacman -S spdlog` +* openSUSE: `sudo zypper in spdlog-devel` +* vcpkg: `vcpkg install spdlog` +* conan: `spdlog/[>=1.4.1]` +* conda: `conda install -c conda-forge spdlog` +* build2: ```depends: spdlog ^1.8.2``` + + +## Features +* Very fast (see [benchmarks](#benchmarks) below). +* Headers only or compiled +* Feature-rich formatting, using the excellent [fmt](https://github.com/fmtlib/fmt) library. +* Asynchronous mode (optional) +* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting. +* Multi/Single threaded loggers. +* Various log targets: + * Rotating log files. + * Daily log files. + * Console logging (colors supported). + * syslog. + * Windows event log. + * Windows debugger (```OutputDebugString(..)```). + * Log to Qt widgets ([example](#log-to-qt-with-nice-colors)). + * Easily [extendable](https://github.com/gabime/spdlog/wiki/4.-Sinks#implementing-your-own-sink) with custom log targets. +* Log filtering - log levels can be modified at runtime as well as compile time. +* Support for loading log levels from argv or environment var. +* [Backtrace](#backtrace-support) support - store debug messages in a ring buffer and display them later on demand. + +## Usage samples + +#### Basic usage +```c++ +#include "spdlog/spdlog.h" + +int main() +{ + spdlog::info("Welcome to spdlog!"); + spdlog::error("Some error message with arg: {}", 1); + + spdlog::warn("Easy padding in numbers like {:08d}", 12); + spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); + spdlog::info("Support for floats {:03.2f}", 1.23456); + spdlog::info("Positional args are {1} {0}..", "too", "supported"); + spdlog::info("{:<30}", "left aligned"); + + spdlog::set_level(spdlog::level::debug); // Set global log level to debug + spdlog::debug("This message should be displayed.."); + + // change log pattern + spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v"); + + // Compile time log levels + // define SPDLOG_ACTIVE_LEVEL to desired level + SPDLOG_TRACE("Some trace message with param {}", 42); + SPDLOG_DEBUG("Some debug message"); +} + +``` +--- +#### Create stdout/stderr logger object +```c++ +#include "spdlog/spdlog.h" +#include "spdlog/sinks/stdout_color_sinks.h" +void stdout_example() +{ + // create a color multi-threaded logger + auto console = spdlog::stdout_color_mt("console"); + auto err_logger = spdlog::stderr_color_mt("stderr"); + spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)"); +} +``` + +--- +#### Basic file logger +```c++ +#include "spdlog/sinks/basic_file_sink.h" +void basic_logfile_example() +{ + try + { + auto logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt"); + } + catch (const spdlog::spdlog_ex &ex) + { + std::cout << "Log init failed: " << ex.what() << std::endl; + } +} +``` +--- +#### Rotating files +```c++ +#include "spdlog/sinks/rotating_file_sink.h" +void rotating_example() +{ + // Create a file rotating logger with 5 MB size max and 3 rotated files + auto max_size = 1048576 * 5; + auto max_files = 3; + auto logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", max_size, max_files); +} +``` + +--- +#### Daily files +```c++ + +#include "spdlog/sinks/daily_file_sink.h" +void daily_example() +{ + // Create a daily logger - a new file is created every day at 2:30 am + auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30); +} + +``` + +--- +#### Backtrace support +```c++ +// Debug messages can be stored in a ring buffer instead of being logged immediately. +// This is useful to display debug logs only when needed (e.g. when an error happens). +// When needed, call dump_backtrace() to dump them to your log. + +spdlog::enable_backtrace(32); // Store the latest 32 messages in a buffer. +// or my_logger->enable_backtrace(32).. +for(int i = 0; i < 100; i++) +{ + spdlog::debug("Backtrace message {}", i); // not logged yet.. +} +// e.g. if some error happened: +spdlog::dump_backtrace(); // log them now! show the last 32 messages +// or my_logger->dump_backtrace(32).. +``` + +--- +#### Periodic flush +```c++ +// periodically flush all *registered* loggers every 3 seconds: +// warning: only use if all your loggers are thread-safe ("_mt" loggers) +spdlog::flush_every(std::chrono::seconds(3)); + +``` + +--- +#### Stopwatch +```c++ +// Stopwatch support for spdlog +#include "spdlog/stopwatch.h" +void stopwatch_example() +{ + spdlog::stopwatch sw; + spdlog::debug("Elapsed {}", sw); + spdlog::debug("Elapsed {:.3}", sw); +} + +``` + +--- +#### Log binary data in hex +```c++ +// many types of std::container types can be used. +// ranges are supported too. +// format flags: +// {:X} - print in uppercase. +// {:s} - don't separate each byte with space. +// {:p} - don't print the position on each line start. +// {:n} - don't split the output into lines. +// {:a} - show ASCII if :n is not set. + +#include "spdlog/fmt/bin_to_hex.h" + +void binary_example() +{ + auto console = spdlog::get("console"); + std::array buf; + console->info("Binary example: {}", spdlog::to_hex(buf)); + console->info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10)); + // more examples: + // logger->info("uppercase: {:X}", spdlog::to_hex(buf)); + // logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf)); + // logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf)); +} + +``` + +--- +#### Logger with multi sinks - each with a different format and log level +```c++ + +// create a logger with 2 targets, with different log levels and formats. +// The console will show only warnings or errors, while the file will log all. +void multi_sink_example() +{ + auto console_sink = std::make_shared(); + console_sink->set_level(spdlog::level::warn); + console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v"); + + auto file_sink = std::make_shared("logs/multisink.txt", true); + file_sink->set_level(spdlog::level::trace); + + spdlog::logger logger("multi_sink", {console_sink, file_sink}); + logger.set_level(spdlog::level::debug); + logger.warn("this should appear in both console and file"); + logger.info("this message should not appear in the console, only in the file"); +} +``` + +--- +#### User-defined callbacks about log events +```c++ + +// create a logger with a lambda function callback, the callback will be called +// each time something is logged to the logger +void callback_example() +{ + auto callback_sink = std::make_shared([](const spdlog::details::log_msg &msg) { + // for example you can be notified by sending an email to yourself + }); + callback_sink->set_level(spdlog::level::err); + + auto console_sink = std::make_shared(); + spdlog::logger logger("custom_callback_logger", {console_sink, callback_sink}); + + logger.info("some info log"); + logger.error("critical issue"); // will notify you +} +``` + +--- +#### Asynchronous logging +```c++ +#include "spdlog/async.h" +#include "spdlog/sinks/basic_file_sink.h" +void async_example() +{ + // default thread pool settings can be modified *before* creating the async logger: + // spdlog::init_thread_pool(8192, 1); // queue with 8k items and 1 backing thread. + auto async_file = spdlog::basic_logger_mt("async_file_logger", "logs/async_log.txt"); + // alternatively: + // auto async_file = spdlog::create_async("async_file_logger", "logs/async_log.txt"); +} + +``` + +--- +#### Asynchronous logger with multi sinks +```c++ +#include "spdlog/sinks/stdout_color_sinks.h" +#include "spdlog/sinks/rotating_file_sink.h" + +void multi_sink_example2() +{ + spdlog::init_thread_pool(8192, 1); + auto stdout_sink = std::make_shared(); + auto rotating_sink = std::make_shared("mylog.txt", 1024*1024*10, 3); + std::vector sinks {stdout_sink, rotating_sink}; + auto logger = std::make_shared("loggername", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block); + spdlog::register_logger(logger); +} +``` + +--- +#### User-defined types +```c++ +template<> +struct fmt::formatter : fmt::formatter +{ + auto format(my_type my, format_context &ctx) const -> decltype(ctx.out()) + { + return format_to(ctx.out(), "[my_type i={}]", my.i); + } +}; + +void user_defined_example() +{ + spdlog::info("user defined type: {}", my_type(14)); +} + +``` + +--- +#### User-defined flags in the log pattern +```c++ +// Log patterns can contain custom flags. +// the following example will add new flag '%*' - which will be bound to a instance. +#include "spdlog/pattern_formatter.h" +class my_formatter_flag : public spdlog::custom_flag_formatter +{ +public: + void format(const spdlog::details::log_msg &, const std::tm &, spdlog::memory_buf_t &dest) override + { + std::string some_txt = "custom-flag"; + dest.append(some_txt.data(), some_txt.data() + some_txt.size()); + } + + std::unique_ptr clone() const override + { + return spdlog::details::make_unique(); + } +}; + +void custom_flags_example() +{ + auto formatter = std::make_unique(); + formatter->add_flag('*').set_pattern("[%n] [%*] [%^%l%$] %v"); + spdlog::set_formatter(std::move(formatter)); +} + +``` + +--- +#### Custom error handler +```c++ +void err_handler_example() +{ + // can be set globally or per logger(logger->set_error_handler(..)) + spdlog::set_error_handler([](const std::string &msg) { spdlog::get("console")->error("*** LOGGER ERROR ***: {}", msg); }); + spdlog::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3); +} + +``` + +--- +#### syslog +```c++ +#include "spdlog/sinks/syslog_sink.h" +void syslog_example() +{ + std::string ident = "spdlog-example"; + auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID); + syslog_logger->warn("This is warning that will end up in syslog."); +} +``` +--- +#### Android example +```c++ +#include "spdlog/sinks/android_sink.h" +void android_example() +{ + std::string tag = "spdlog-android"; + auto android_logger = spdlog::android_logger_mt("android", tag); + android_logger->critical("Use \"adb shell logcat\" to view this message."); +} +``` + +--- +#### Load log levels from the env variable or argv + +```c++ +#include "spdlog/cfg/env.h" +int main (int argc, char *argv[]) +{ + spdlog::cfg::load_env_levels(); + // or from the command line: + // ./example SPDLOG_LEVEL=info,mylogger=trace + // #include "spdlog/cfg/argv.h" // for loading levels from argv + // spdlog::cfg::load_argv_levels(argc, argv); +} +``` +So then you can: + +```console +$ export SPDLOG_LEVEL=info,mylogger=trace +$ ./example +``` + + +--- +#### Log file open/close event handlers +```c++ +// You can get callbacks from spdlog before/after a log file has been opened or closed. +// This is useful for cleanup procedures or for adding something to the start/end of the log file. +void file_events_example() +{ + // pass the spdlog::file_event_handlers to file sinks for open/close log file notifications + spdlog::file_event_handlers handlers; + handlers.before_open = [](spdlog::filename_t filename) { spdlog::info("Before opening {}", filename); }; + handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("After opening\n", fstream); }; + handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { fputs("Before closing\n", fstream); }; + handlers.after_close = [](spdlog::filename_t filename) { spdlog::info("After closing {}", filename); }; + auto my_logger = spdlog::basic_logger_st("some_logger", "logs/events-sample.txt", true, handlers); +} +``` + +--- +#### Replace the Default Logger +```c++ +void replace_default_logger_example() +{ + auto new_logger = spdlog::basic_logger_mt("new_default_logger", "logs/new-default-log.txt", true); + spdlog::set_default_logger(new_logger); + spdlog::info("new logger log message"); +} +``` + +--- +#### Log to Qt with nice colors +```c++ +#include "spdlog/spdlog.h" +#include "spdlog/sinks/qt_sinks.h" +MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) +{ + setMinimumSize(640, 480); + auto log_widget = new QTextEdit(this); + setCentralWidget(log_widget); + int max_lines = 500; // keep the text widget to max 500 lines. remove old lines if needed. + auto logger = spdlog::qt_color_logger_mt("qt_logger", log_widget, max_lines); + logger->info("Some info message"); +} +``` + +--- +## Benchmarks + +Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/bench.cpp) done in Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz + +#### Synchronous mode +``` +[info] ************************************************************** +[info] Single thread, 1,000,000 iterations +[info] ************************************************************** +[info] basic_st Elapsed: 0.17 secs 5,777,626/sec +[info] rotating_st Elapsed: 0.18 secs 5,475,894/sec +[info] daily_st Elapsed: 0.20 secs 5,062,659/sec +[info] empty_logger Elapsed: 0.07 secs 14,127,300/sec +[info] ************************************************************** +[info] C-string (400 bytes). Single thread, 1,000,000 iterations +[info] ************************************************************** +[info] basic_st Elapsed: 0.41 secs 2,412,483/sec +[info] rotating_st Elapsed: 0.72 secs 1,389,196/sec +[info] daily_st Elapsed: 0.42 secs 2,393,298/sec +[info] null_st Elapsed: 0.04 secs 27,446,957/sec +[info] ************************************************************** +[info] 10 threads, competing over the same logger object, 1,000,000 iterations +[info] ************************************************************** +[info] basic_mt Elapsed: 0.60 secs 1,659,613/sec +[info] rotating_mt Elapsed: 0.62 secs 1,612,493/sec +[info] daily_mt Elapsed: 0.61 secs 1,638,305/sec +[info] null_mt Elapsed: 0.16 secs 6,272,758/sec +``` +#### Asynchronous mode +``` +[info] ------------------------------------------------- +[info] Messages : 1,000,000 +[info] Threads : 10 +[info] Queue : 8,192 slots +[info] Queue memory : 8,192 x 272 = 2,176 KB +[info] ------------------------------------------------- +[info] +[info] ********************************* +[info] Queue Overflow Policy: block +[info] ********************************* +[info] Elapsed: 1.70784 secs 585,535/sec +[info] Elapsed: 1.69805 secs 588,910/sec +[info] Elapsed: 1.7026 secs 587,337/sec +[info] +[info] ********************************* +[info] Queue Overflow Policy: overrun +[info] ********************************* +[info] Elapsed: 0.372816 secs 2,682,285/sec +[info] Elapsed: 0.379758 secs 2,633,255/sec +[info] Elapsed: 0.373532 secs 2,677,147/sec + +``` + +## Documentation +Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages. + +--- + +Thanks to [JetBrains](https://www.jetbrains.com/?from=spdlog) for donating product licenses to help develop **spdlog** + + diff --git a/inc/spdlog/appveyor.yml b/inc/spdlog/appveyor.yml new file mode 100644 index 0000000..f2757f3 --- /dev/null +++ b/inc/spdlog/appveyor.yml @@ -0,0 +1,89 @@ +version: 1.0.{build} +image: Visual Studio 2017 +environment: + matrix: + - GENERATOR: '"Visual Studio 15 2017 Win64"' + BUILD_TYPE: Debug + BUILD_SHARED: 'OFF' + FATAL_ERRORS: 'OFF' + WCHAR: 'ON' + WCHAR_FILES: 'OFF' + BUILD_EXAMPLE: 'ON' + USE_STD_FORMAT: 'OFF' + CXX_STANDARD: 11 + - GENERATOR: '"Visual Studio 15 2017 Win64"' + BUILD_TYPE: Release + BUILD_SHARED: 'OFF' + FATAL_ERRORS: 'OFF' + WCHAR: 'OFF' + WCHAR_FILES: 'OFF' + BUILD_EXAMPLE: 'ON' + USE_STD_FORMAT: 'OFF' + CXX_STANDARD: 11 + - GENERATOR: '"Visual Studio 15 2017 Win64"' + BUILD_TYPE: Release + BUILD_SHARED: 'ON' + FATAL_ERRORS: 'OFF' + WCHAR: 'OFF' + WCHAR_FILES: 'OFF' + BUILD_EXAMPLE: 'ON' + USE_STD_FORMAT: 'OFF' + CXX_STANDARD: 11 + - GENERATOR: '"Visual Studio 15 2017 Win64"' + BUILD_TYPE: Release + BUILD_SHARED: 'ON' + FATAL_ERRORS: 'OFF' + WCHAR: 'ON' + WCHAR_FILES: 'ON' + BUILD_EXAMPLE: 'OFF' + USE_STD_FORMAT: 'OFF' + CXX_STANDARD: 11 + - GENERATOR: '"Visual Studio 16 2019" -A x64' + BUILD_TYPE: Release + BUILD_SHARED: 'ON' + FATAL_ERRORS: 'ON' + WCHAR: 'OFF' + WCHAR_FILES: 'OFF' + BUILD_EXAMPLE: 'OFF' + USE_STD_FORMAT: 'OFF' + CXX_STANDARD: 17 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019 + - GENERATOR: '"Visual Studio 17 2022" -A x64' + BUILD_TYPE: Release + BUILD_SHARED: 'ON' + FATAL_ERRORS: 'ON' + WCHAR: 'OFF' + WCHAR_FILES: 'OFF' + BUILD_EXAMPLE: 'OFF' + USE_STD_FORMAT: 'ON' + CXX_STANDARD: 20 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 + - GENERATOR: '"Visual Studio 17 2022" -A x64' + BUILD_TYPE: Release + BUILD_SHARED: 'ON' + FATAL_ERRORS: 'ON' + WCHAR: 'ON' + WCHAR_FILES: 'ON' + BUILD_EXAMPLE: 'OFF' + USE_STD_FORMAT: 'ON' + CXX_STANDARD: 20 + APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2022 +build_script: + - cmd: >- + set + + mkdir build + + cd build + + set PATH=%PATH%;C:\Program Files\Git\usr\bin + + cmake -G %GENERATOR% -D CMAKE_BUILD_TYPE=%BUILD_TYPE% -D BUILD_SHARED_LIBS=%BUILD_SHARED% -D SPDLOG_WCHAR_SUPPORT=%WCHAR% -D SPDLOG_WCHAR_FILENAMES=%WCHAR_FILES% -D SPDLOG_BUILD_EXAMPLE=%BUILD_EXAMPLE% -D SPDLOG_BUILD_EXAMPLE_HO=%BUILD_EXAMPLE% -D SPDLOG_BUILD_TESTS=ON -D SPDLOG_BUILD_TESTS_HO=OFF -D SPDLOG_BUILD_WARNINGS=%FATAL_ERRORS% -D SPDLOG_USE_STD_FORMAT=%USE_STD_FORMAT% -D CMAKE_CXX_STANDARD=%CXX_STANDARD% .. + + cmake --build . --config %BUILD_TYPE% + +before_test: + - set PATH=%PATH%;C:\projects\spdlog\build\_deps\catch2-build\src\%BUILD_TYPE%;C:\projects\spdlog\build\%BUILD_TYPE% + +test_script: + - C:\projects\spdlog\build\tests\%BUILD_TYPE%\spdlog-utests.exe diff --git a/inc/spdlog/bench/CMakeLists.txt b/inc/spdlog/bench/CMakeLists.txt new file mode 100644 index 0000000..3806b24 --- /dev/null +++ b/inc/spdlog/bench/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT) + +cmake_minimum_required(VERSION 3.11) +project(spdlog_bench CXX) + +if(NOT TARGET spdlog) + # Stand-alone build + find_package(spdlog CONFIG REQUIRED) +endif() + +find_package(Threads REQUIRED) +find_package(benchmark CONFIG) +if(NOT benchmark_FOUND) + message(STATUS "Using CMake Version ${CMAKE_VERSION}") + # User can fetch googlebenchmark + message(STATUS "Downloading GoogleBenchmark") + include(FetchContent) + + # disable tests + set(BENCHMARK_ENABLE_TESTING OFF CACHE INTERNAL "") + # Do not build and run googlebenchmark tests + FetchContent_Declare(googlebenchmark GIT_REPOSITORY https://github.com/google/benchmark.git GIT_TAG v1.6.0) + FetchContent_MakeAvailable(googlebenchmark) +endif() + +add_executable(bench bench.cpp) +spdlog_enable_warnings(bench) +target_link_libraries(bench PRIVATE spdlog::spdlog) + +add_executable(async_bench async_bench.cpp) +target_link_libraries(async_bench PRIVATE spdlog::spdlog) + +add_executable(latency latency.cpp) +target_link_libraries(latency PRIVATE benchmark::benchmark spdlog::spdlog) + +add_executable(formatter-bench formatter-bench.cpp) +target_link_libraries(formatter-bench PRIVATE benchmark::benchmark spdlog::spdlog) diff --git a/inc/spdlog/bench/async_bench.cpp b/inc/spdlog/bench/async_bench.cpp new file mode 100644 index 0000000..a136c7b --- /dev/null +++ b/inc/spdlog/bench/async_bench.cpp @@ -0,0 +1,168 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +// +// bench.cpp : spdlog benchmarks +// +#include "spdlog/spdlog.h" +#include "spdlog/async.h" +#include "spdlog/sinks/basic_file_sink.h" + +#if defined(SPDLOG_USE_STD_FORMAT) + #include +#elif defined(SPDLOG_FMT_EXTERNAL) + #include +#else + #include "spdlog/fmt/bundled/format.h" +#endif + +#include "utils.h" +#include +#include +#include +#include +#include + +using namespace std; +using namespace std::chrono; +using namespace spdlog; +using namespace spdlog::sinks; +using namespace utils; + +void bench_mt(int howmany, std::shared_ptr log, int thread_count); + +#ifdef _MSC_VER + #pragma warning(push) + #pragma warning(disable : 4996) // disable fopen warning under msvc +#endif // _MSC_VER + +int count_lines(const char *filename) { + int counter = 0; + auto *infile = fopen(filename, "r"); + int ch; + while (EOF != (ch = getc(infile))) { + if ('\n' == ch) counter++; + } + fclose(infile); + + return counter; +} + +void verify_file(const char *filename, int expected_count) { + spdlog::info("Verifying {} to contain {} line..", filename, expected_count); + auto count = count_lines(filename); + if (count != expected_count) { + spdlog::error("Test failed. {} has {} lines instead of {}", filename, count, + expected_count); + exit(1); + } + spdlog::info("Line count OK ({})\n", count); +} + +#ifdef _MSC_VER + #pragma warning(pop) +#endif + +int main(int argc, char *argv[]) { + int howmany = 1000000; + int queue_size = std::min(howmany + 2, 8192); + int threads = 10; + int iters = 3; + + try { + spdlog::set_pattern("[%^%l%$] %v"); + if (argc == 1) { + spdlog::info("Usage: {} ", argv[0]); + return 0; + } + + if (argc > 1) howmany = atoi(argv[1]); + if (argc > 2) threads = atoi(argv[2]); + if (argc > 3) { + queue_size = atoi(argv[3]); + if (queue_size > 500000) { + spdlog::error("Max queue size allowed: 500,000"); + exit(1); + } + } + + if (argc > 4) iters = atoi(argv[4]); + + auto slot_size = sizeof(spdlog::details::async_msg); + spdlog::info("-------------------------------------------------"); + spdlog::info("Messages : {:L}", howmany); + spdlog::info("Threads : {:L}", threads); + spdlog::info("Queue : {:L} slots", queue_size); + spdlog::info("Queue memory : {:L} x {:L} = {:L} KB ", queue_size, slot_size, + (queue_size * slot_size) / 1024); + spdlog::info("Total iters : {:L}", iters); + spdlog::info("-------------------------------------------------"); + + const char *filename = "logs/basic_async.log"; + spdlog::info(""); + spdlog::info("*********************************"); + spdlog::info("Queue Overflow Policy: block"); + spdlog::info("*********************************"); + for (int i = 0; i < iters; i++) { + auto tp = std::make_shared(queue_size, 1); + auto file_sink = std::make_shared(filename, true); + auto logger = std::make_shared( + "async_logger", std::move(file_sink), std::move(tp), async_overflow_policy::block); + bench_mt(howmany, std::move(logger), threads); + // verify_file(filename, howmany); + } + + spdlog::info(""); + spdlog::info("*********************************"); + spdlog::info("Queue Overflow Policy: overrun"); + spdlog::info("*********************************"); + // do same test but discard oldest if queue is full instead of blocking + filename = "logs/basic_async-overrun.log"; + for (int i = 0; i < iters; i++) { + auto tp = std::make_shared(queue_size, 1); + auto file_sink = std::make_shared(filename, true); + auto logger = + std::make_shared("async_logger", std::move(file_sink), std::move(tp), + async_overflow_policy::overrun_oldest); + bench_mt(howmany, std::move(logger), threads); + } + spdlog::shutdown(); + } catch (std::exception &ex) { + std::cerr << "Error: " << ex.what() << std::endl; + perror("Last error"); + return 1; + } + return 0; +} + +void thread_fun(std::shared_ptr logger, int howmany) { + for (int i = 0; i < howmany; i++) { + logger->info("Hello logger: msg number {}", i); + } +} + +void bench_mt(int howmany, std::shared_ptr logger, int thread_count) { + using std::chrono::high_resolution_clock; + vector threads; + auto start = high_resolution_clock::now(); + + int msgs_per_thread = howmany / thread_count; + int msgs_per_thread_mod = howmany % thread_count; + for (int t = 0; t < thread_count; ++t) { + if (t == 0 && msgs_per_thread_mod) + threads.push_back( + std::thread(thread_fun, logger, msgs_per_thread + msgs_per_thread_mod)); + else + threads.push_back(std::thread(thread_fun, logger, msgs_per_thread)); + } + + for (auto &t : threads) { + t.join(); + }; + + auto delta = high_resolution_clock::now() - start; + auto delta_d = duration_cast>(delta).count(); + spdlog::info("Elapsed: {} secs\t {:L}/sec", delta_d, int(howmany / delta_d)); +} diff --git a/inc/spdlog/bench/bench.cpp b/inc/spdlog/bench/bench.cpp new file mode 100644 index 0000000..609f493 --- /dev/null +++ b/inc/spdlog/bench/bench.cpp @@ -0,0 +1,246 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +// +// bench.cpp : spdlog benchmarks +// +#include "spdlog/spdlog.h" +#include "spdlog/sinks/basic_file_sink.h" +#include "spdlog/sinks/daily_file_sink.h" +#include "spdlog/sinks/null_sink.h" +#include "spdlog/sinks/rotating_file_sink.h" + +#if defined(SPDLOG_USE_STD_FORMAT) + #include +#elif defined(SPDLOG_FMT_EXTERNAL) + #include +#else + #include "spdlog/fmt/bundled/format.h" +#endif + +#include "utils.h" +#include +#include // EXIT_FAILURE +#include +#include +#include + +void bench(int howmany, std::shared_ptr log); +void bench_mt(int howmany, std::shared_ptr log, size_t thread_count); + +// void bench_default_api(int howmany, std::shared_ptr log); +// void bench_c_string(int howmany, std::shared_ptr log); + +static const size_t file_size = 30 * 1024 * 1024; +static const size_t rotating_files = 5; +static const int max_threads = 1000; + +void bench_threaded_logging(size_t threads, int iters) { + spdlog::info("**************************************************************"); + spdlog::info(spdlog::fmt_lib::format( + std::locale("en_US.UTF-8"), "Multi threaded: {:L} threads, {:L} messages", threads, iters)); + spdlog::info("**************************************************************"); + + auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true); + bench_mt(iters, std::move(basic_mt), threads); + auto basic_mt_tracing = + spdlog::basic_logger_mt("basic_mt/backtrace-on", "logs/basic_mt.log", true); + basic_mt_tracing->enable_backtrace(32); + bench_mt(iters, std::move(basic_mt_tracing), threads); + + spdlog::info(""); + auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, + rotating_files); + bench_mt(iters, std::move(rotating_mt), threads); + auto rotating_mt_tracing = spdlog::rotating_logger_mt( + "rotating_mt/backtrace-on", "logs/rotating_mt.log", file_size, rotating_files); + rotating_mt_tracing->enable_backtrace(32); + bench_mt(iters, std::move(rotating_mt_tracing), threads); + + spdlog::info(""); + auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log"); + bench_mt(iters, std::move(daily_mt), threads); + auto daily_mt_tracing = spdlog::daily_logger_mt("daily_mt/backtrace-on", "logs/daily_mt.log"); + daily_mt_tracing->enable_backtrace(32); + bench_mt(iters, std::move(daily_mt_tracing), threads); + + spdlog::info(""); + auto empty_logger = std::make_shared("level-off"); + empty_logger->set_level(spdlog::level::off); + bench(iters, empty_logger); + auto empty_logger_tracing = std::make_shared("level-off/backtrace-on"); + empty_logger_tracing->set_level(spdlog::level::off); + empty_logger_tracing->enable_backtrace(32); + bench(iters, empty_logger_tracing); +} + +void bench_single_threaded(int iters) { + spdlog::info("**************************************************************"); + spdlog::info( + spdlog::fmt_lib::format(std::locale("en_US.UTF-8"), "Single threaded: {} messages", iters)); + spdlog::info("**************************************************************"); + + auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true); + bench(iters, std::move(basic_st)); + + auto basic_st_tracing = + spdlog::basic_logger_st("basic_st/backtrace-on", "logs/basic_st.log", true); + bench(iters, std::move(basic_st_tracing)); + + spdlog::info(""); + auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, + rotating_files); + bench(iters, std::move(rotating_st)); + auto rotating_st_tracing = spdlog::rotating_logger_st( + "rotating_st/backtrace-on", "logs/rotating_st.log", file_size, rotating_files); + rotating_st_tracing->enable_backtrace(32); + bench(iters, std::move(rotating_st_tracing)); + + spdlog::info(""); + auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log"); + bench(iters, std::move(daily_st)); + auto daily_st_tracing = spdlog::daily_logger_st("daily_st/backtrace-on", "logs/daily_st.log"); + daily_st_tracing->enable_backtrace(32); + bench(iters, std::move(daily_st_tracing)); + + spdlog::info(""); + auto empty_logger = std::make_shared("level-off"); + empty_logger->set_level(spdlog::level::off); + bench(iters, empty_logger); + + auto empty_logger_tracing = std::make_shared("level-off/backtrace-on"); + empty_logger_tracing->set_level(spdlog::level::off); + empty_logger_tracing->enable_backtrace(32); + bench(iters, empty_logger_tracing); +} + +int main(int argc, char *argv[]) { + spdlog::set_automatic_registration(false); + spdlog::default_logger()->set_pattern("[%^%l%$] %v"); + int iters = 250000; + size_t threads = 4; + try { + if (argc > 1) { + iters = std::stoi(argv[1]); + } + if (argc > 2) { + threads = std::stoul(argv[2]); + } + + if (threads > max_threads) { + throw std::runtime_error( + spdlog::fmt_lib::format("Number of threads exceeds maximum({})", max_threads)); + } + + bench_single_threaded(iters); + bench_threaded_logging(1, iters); + bench_threaded_logging(threads, iters); + } catch (std::exception &ex) { + spdlog::error(ex.what()); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +void bench(int howmany, std::shared_ptr log) { + using std::chrono::duration; + using std::chrono::duration_cast; + using std::chrono::high_resolution_clock; + + auto start = high_resolution_clock::now(); + for (auto i = 0; i < howmany; ++i) { + log->info("Hello logger: msg number {}", i); + } + + auto delta = high_resolution_clock::now() - start; + auto delta_d = duration_cast>(delta).count(); + + spdlog::info(spdlog::fmt_lib::format(std::locale("en_US.UTF-8"), + "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), + delta_d, size_t(howmany / delta_d))); + spdlog::drop(log->name()); +} + +void bench_mt(int howmany, std::shared_ptr log, size_t thread_count) { + using std::chrono::duration; + using std::chrono::duration_cast; + using std::chrono::high_resolution_clock; + + std::vector threads; + threads.reserve(thread_count); + auto start = high_resolution_clock::now(); + for (size_t t = 0; t < thread_count; ++t) { + threads.emplace_back([&]() { + for (int j = 0; j < howmany / static_cast(thread_count); j++) { + log->info("Hello logger: msg number {}", j); + } + }); + } + + for (auto &t : threads) { + t.join(); + }; + + auto delta = high_resolution_clock::now() - start; + auto delta_d = duration_cast>(delta).count(); + spdlog::info(spdlog::fmt_lib::format(std::locale("en_US.UTF-8"), + "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), + delta_d, size_t(howmany / delta_d))); + spdlog::drop(log->name()); +} + +/* +void bench_default_api(int howmany, std::shared_ptr log) +{ + using std::chrono::high_resolution_clock; + using std::chrono::duration; + using std::chrono::duration_cast; + + auto orig_default = spdlog::default_logger(); + spdlog::set_default_logger(log); + auto start = high_resolution_clock::now(); + for (auto i = 0; i < howmany; ++i) + { + spdlog::info("Hello logger: msg number {}", i); + } + + auto delta = high_resolution_clock::now() - start; + auto delta_d = duration_cast>(delta).count(); + spdlog::drop(log->name()); + spdlog::set_default_logger(std::move(orig_default)); + spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16}/sec", log->name(), delta_d, int(howmany / +delta_d)); +} + +void bench_c_string(int howmany, std::shared_ptr log) +{ + using std::chrono::high_resolution_clock; + using std::chrono::duration; + using std::chrono::duration_cast; + + const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra +metus cursus " "lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus +volutpat mi, eu consequat sem " "libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam +non dapibus eros. Donec fringilla dui sed " "augue pretium, nec scelerisque est maximus. Nullam +convallis, sem nec blandit maximus, nisi turpis ornare " "nisl, sit amet volutpat neque massa eu +odio. Maecenas malesuada quam ex, posuere congue nibh turpis duis."; + + auto orig_default = spdlog::default_logger(); + spdlog::set_default_logger(log); + auto start = high_resolution_clock::now(); + for (auto i = 0; i < howmany; ++i) + { + spdlog::log(spdlog::level::info, msg); + } + + auto delta = high_resolution_clock::now() - start; + auto delta_d = duration_cast>(delta).count(); + spdlog::drop(log->name()); + spdlog::set_default_logger(std::move(orig_default)); + spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16}/sec", log->name(), delta_d, int(howmany / +delta_d)); +} + +*/ \ No newline at end of file diff --git a/inc/spdlog/bench/formatter-bench.cpp b/inc/spdlog/bench/formatter-bench.cpp new file mode 100644 index 0000000..375a154 --- /dev/null +++ b/inc/spdlog/bench/formatter-bench.cpp @@ -0,0 +1,71 @@ +// +// Copyright(c) 2018 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#include "benchmark/benchmark.h" + +#include "spdlog/spdlog.h" +#include "spdlog/pattern_formatter.h" + +void bench_formatter(benchmark::State &state, std::string pattern) { + auto formatter = spdlog::details::make_unique(pattern); + spdlog::memory_buf_t dest; + std::string logger_name = "logger-name"; + const char *text = + "Hello. This is some message with length of 80 "; + + spdlog::source_loc source_loc{"a/b/c/d/myfile.cpp", 123, "some_func()"}; + spdlog::details::log_msg msg(source_loc, logger_name, spdlog::level::info, text); + + for (auto _ : state) { + dest.clear(); + formatter->format(msg, dest); + benchmark::DoNotOptimize(dest); + } +} + +void bench_formatters() { + // basic patterns(single flag) + std::string all_flags = "+vtPnlLaAbBcCYDmdHIMSefFprRTXzEisg@luioO%"; + std::vector basic_patterns; + for (auto &flag : all_flags) { + auto pattern = std::string("%") + flag; + benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern); + + // pattern = std::string("%16") + flag; + // benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern); + // + // // bench center padding + // pattern = std::string("%=16") + flag; + // benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern); + } + + // complex patterns + std::vector patterns = { + "[%D %X] [%l] [%n] %v", + "[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] %v", + "[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] [%t] %v", + }; + for (auto &pattern : patterns) { + benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern) + ->Iterations(2500000); + } +} + +int main(int argc, char *argv[]) { + spdlog::set_pattern("[%^%l%$] %v"); + if (argc != 2) { + spdlog::error("Usage: {} (or \"all\" to bench all)", argv[0]); + exit(1); + } + + std::string pattern = argv[1]; + if (pattern == "all") { + bench_formatters(); + } else { + benchmark::RegisterBenchmark(pattern.c_str(), &bench_formatter, pattern); + } + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); +} diff --git a/inc/spdlog/bench/latency.cpp b/inc/spdlog/bench/latency.cpp new file mode 100644 index 0000000..f63251e --- /dev/null +++ b/inc/spdlog/bench/latency.cpp @@ -0,0 +1,220 @@ +// +// Copyright(c) 2018 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +// +// latency.cpp : spdlog latency benchmarks +// + +#include "benchmark/benchmark.h" + +#include "spdlog/spdlog.h" +#include "spdlog/async.h" +#include "spdlog/sinks/basic_file_sink.h" +#include "spdlog/sinks/daily_file_sink.h" +#include "spdlog/sinks/null_sink.h" +#include "spdlog/sinks/rotating_file_sink.h" + +void bench_c_string(benchmark::State &state, std::shared_ptr logger) { + const char *msg = + "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus " + "lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus volutpat mi, " + "eu consequat sem " + "libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam non dapibus eros. Donec " + "fringilla dui sed " + "augue pretium, nec scelerisque est maximus. Nullam convallis, sem nec blandit maximus, " + "nisi turpis ornare " + "nisl, sit amet volutpat neque massa eu odio. Maecenas malesuada quam ex, posuere congue " + "nibh turpis duis."; + + for (auto _ : state) { + logger->info(msg); + } +} + +void bench_logger(benchmark::State &state, std::shared_ptr logger) { + int i = 0; + for (auto _ : state) { + logger->info("Hello logger: msg number {}...............", ++i); + } +} +void bench_global_logger(benchmark::State &state, std::shared_ptr logger) { + spdlog::set_default_logger(std::move(logger)); + int i = 0; + for (auto _ : state) { + spdlog::info("Hello logger: msg number {}...............", ++i); + } +} + +void bench_disabled_macro(benchmark::State &state, std::shared_ptr logger) { + int i = 0; + benchmark::DoNotOptimize(i); // prevent unused warnings + benchmark::DoNotOptimize(logger); // prevent unused warnings + for (auto _ : state) { + SPDLOG_LOGGER_DEBUG(logger, "Hello logger: msg number {}...............", i++); + } +} + +void bench_disabled_macro_global_logger(benchmark::State &state, + std::shared_ptr logger) { + spdlog::set_default_logger(std::move(logger)); + int i = 0; + benchmark::DoNotOptimize(i); // prevent unused warnings + benchmark::DoNotOptimize(logger); // prevent unused warnings + for (auto _ : state) { + SPDLOG_DEBUG("Hello logger: msg number {}...............", i++); + } +} + +#ifdef __linux__ +void bench_dev_null() { + auto dev_null_st = spdlog::basic_logger_st("/dev/null_st", "/dev/null"); + benchmark::RegisterBenchmark("/dev/null_st", bench_logger, std::move(dev_null_st)) + ->UseRealTime(); + spdlog::drop("/dev/null_st"); + + auto dev_null_mt = spdlog::basic_logger_mt("/dev/null_mt", "/dev/null"); + benchmark::RegisterBenchmark("/dev/null_mt", bench_logger, std::move(dev_null_mt)) + ->UseRealTime(); + spdlog::drop("/dev/null_mt"); +} +#endif // __linux__ + +int main(int argc, char *argv[]) { + using spdlog::sinks::null_sink_mt; + using spdlog::sinks::null_sink_st; + + size_t file_size = 30 * 1024 * 1024; + size_t rotating_files = 5; + int n_threads = benchmark::CPUInfo::Get().num_cpus; + + auto full_bench = argc > 1 && std::string(argv[1]) == "full"; + + // disabled loggers + auto disabled_logger = + std::make_shared("bench", std::make_shared()); + disabled_logger->set_level(spdlog::level::off); + benchmark::RegisterBenchmark("disabled-at-compile-time", bench_disabled_macro, disabled_logger); + benchmark::RegisterBenchmark("disabled-at-compile-time (global logger)", + bench_disabled_macro_global_logger, disabled_logger); + benchmark::RegisterBenchmark("disabled-at-runtime", bench_logger, disabled_logger); + benchmark::RegisterBenchmark("disabled-at-runtime (global logger)", bench_global_logger, + disabled_logger); + // with backtrace of 64 + auto tracing_disabled_logger = + std::make_shared("bench", std::make_shared()); + tracing_disabled_logger->enable_backtrace(64); + benchmark::RegisterBenchmark("disabled-at-runtime/backtrace", bench_logger, + tracing_disabled_logger); + + auto null_logger_st = + std::make_shared("bench", std::make_shared()); + benchmark::RegisterBenchmark("null_sink_st (500_bytes c_str)", bench_c_string, + std::move(null_logger_st)); + benchmark::RegisterBenchmark("null_sink_st", bench_logger, null_logger_st); + benchmark::RegisterBenchmark("null_sink_st (global logger)", bench_global_logger, + null_logger_st); + // with backtrace of 64 + auto tracing_null_logger_st = + std::make_shared("bench", std::make_shared()); + tracing_null_logger_st->enable_backtrace(64); + benchmark::RegisterBenchmark("null_sink_st/backtrace", bench_logger, tracing_null_logger_st); + +#ifdef __linux + bench_dev_null(); +#endif // __linux__ + + if (full_bench) { + // basic_st + auto basic_st = spdlog::basic_logger_st("basic_st", "latency_logs/basic_st.log", true); + benchmark::RegisterBenchmark("basic_st", bench_logger, std::move(basic_st))->UseRealTime(); + spdlog::drop("basic_st"); + // with backtrace of 64 + auto tracing_basic_st = + spdlog::basic_logger_st("tracing_basic_st", "latency_logs/tracing_basic_st.log", true); + tracing_basic_st->enable_backtrace(64); + benchmark::RegisterBenchmark("basic_st/backtrace", bench_logger, + std::move(tracing_basic_st)) + ->UseRealTime(); + spdlog::drop("tracing_basic_st"); + + // rotating st + auto rotating_st = spdlog::rotating_logger_st("rotating_st", "latency_logs/rotating_st.log", + file_size, rotating_files); + benchmark::RegisterBenchmark("rotating_st", bench_logger, std::move(rotating_st)) + ->UseRealTime(); + spdlog::drop("rotating_st"); + // with backtrace of 64 + auto tracing_rotating_st = spdlog::rotating_logger_st( + "tracing_rotating_st", "latency_logs/tracing_rotating_st.log", file_size, + rotating_files); + benchmark::RegisterBenchmark("rotating_st/backtrace", bench_logger, + std::move(tracing_rotating_st)) + ->UseRealTime(); + spdlog::drop("tracing_rotating_st"); + + // daily st + auto daily_st = spdlog::daily_logger_mt("daily_st", "latency_logs/daily_st.log"); + benchmark::RegisterBenchmark("daily_st", bench_logger, std::move(daily_st))->UseRealTime(); + spdlog::drop("daily_st"); + auto tracing_daily_st = + spdlog::daily_logger_mt("tracing_daily_st", "latency_logs/daily_st.log"); + benchmark::RegisterBenchmark("daily_st/backtrace", bench_logger, + std::move(tracing_daily_st)) + ->UseRealTime(); + spdlog::drop("tracing_daily_st"); + + // + // Multi threaded bench, 10 loggers using same logger concurrently + // + auto null_logger_mt = + std::make_shared("bench", std::make_shared()); + benchmark::RegisterBenchmark("null_sink_mt", bench_logger, null_logger_mt) + ->Threads(n_threads) + ->UseRealTime(); + + // basic_mt + auto basic_mt = spdlog::basic_logger_mt("basic_mt", "latency_logs/basic_mt.log", true); + benchmark::RegisterBenchmark("basic_mt", bench_logger, std::move(basic_mt)) + ->Threads(n_threads) + ->UseRealTime(); + spdlog::drop("basic_mt"); + + // rotating mt + auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "latency_logs/rotating_mt.log", + file_size, rotating_files); + benchmark::RegisterBenchmark("rotating_mt", bench_logger, std::move(rotating_mt)) + ->Threads(n_threads) + ->UseRealTime(); + spdlog::drop("rotating_mt"); + + // daily mt + auto daily_mt = spdlog::daily_logger_mt("daily_mt", "latency_logs/daily_mt.log"); + benchmark::RegisterBenchmark("daily_mt", bench_logger, std::move(daily_mt)) + ->Threads(n_threads) + ->UseRealTime(); + spdlog::drop("daily_mt"); + } + + // async + auto queue_size = 1024 * 1024 * 3; + auto tp = std::make_shared(queue_size, 1); + auto async_logger = std::make_shared( + "async_logger", std::make_shared(), std::move(tp), + spdlog::async_overflow_policy::overrun_oldest); + benchmark::RegisterBenchmark("async_logger", bench_logger, async_logger) + ->Threads(n_threads) + ->UseRealTime(); + + auto async_logger_tracing = std::make_shared( + "async_logger_tracing", std::make_shared(), std::move(tp), + spdlog::async_overflow_policy::overrun_oldest); + async_logger_tracing->enable_backtrace(32); + benchmark::RegisterBenchmark("async_logger/tracing", bench_logger, async_logger_tracing) + ->Threads(n_threads) + ->UseRealTime(); + + benchmark::Initialize(&argc, argv); + benchmark::RunSpecifiedBenchmarks(); +} diff --git a/inc/spdlog/bench/utils.h b/inc/spdlog/bench/utils.h new file mode 100644 index 0000000..1828fc0 --- /dev/null +++ b/inc/spdlog/bench/utils.h @@ -0,0 +1,32 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include +#include +#include + +namespace utils { + +template +inline std::string format(const T &value) { + static std::locale loc(""); + std::stringstream ss; + ss.imbue(loc); + ss << value; + return ss.str(); +} + +template <> +inline std::string format(const double &value) { + static std::locale loc(""); + std::stringstream ss; + ss.imbue(loc); + ss << std::fixed << std::setprecision(1) << value; + return ss.str(); +} + +} // namespace utils diff --git a/inc/spdlog/cmake/ide.cmake b/inc/spdlog/cmake/ide.cmake new file mode 100644 index 0000000..a0656a5 --- /dev/null +++ b/inc/spdlog/cmake/ide.cmake @@ -0,0 +1,18 @@ +# --------------------------------------------------------------------------------------- +# IDE support for headers +# --------------------------------------------------------------------------------------- +set(SPDLOG_HEADERS_DIR "${CMAKE_CURRENT_LIST_DIR}/../include") + +file(GLOB SPDLOG_TOP_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/*.h") +file(GLOB SPDLOG_DETAILS_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/details/*.h") +file(GLOB SPDLOG_SINKS_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/sinks/*.h") +file(GLOB SPDLOG_FMT_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/fmt/*.h") +file(GLOB SPDLOG_FMT_BUNDELED_HEADERS "${SPDLOG_HEADERS_DIR}/spdlog/fmt/bundled/*.h") +set(SPDLOG_ALL_HEADERS ${SPDLOG_TOP_HEADERS} ${SPDLOG_DETAILS_HEADERS} ${SPDLOG_SINKS_HEADERS} ${SPDLOG_FMT_HEADERS} + ${SPDLOG_FMT_BUNDELED_HEADERS}) + +source_group("Header Files\\spdlog" FILES ${SPDLOG_TOP_HEADERS}) +source_group("Header Files\\spdlog\\details" FILES ${SPDLOG_DETAILS_HEADERS}) +source_group("Header Files\\spdlog\\sinks" FILES ${SPDLOG_SINKS_HEADERS}) +source_group("Header Files\\spdlog\\fmt" FILES ${SPDLOG_FMT_HEADERS}) +source_group("Header Files\\spdlog\\fmt\\bundled\\" FILES ${SPDLOG_FMT_BUNDELED_HEADERS}) diff --git a/inc/spdlog/cmake/pch.h.in b/inc/spdlog/cmake/pch.h.in new file mode 100644 index 0000000..a5f9415 --- /dev/null +++ b/inc/spdlog/cmake/pch.h.in @@ -0,0 +1,258 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +// details/pattern_formatter-inl.h +// fmt/bin_to_hex.h +// fmt/bundled/format-inl.h +#include + +// details/file_helper-inl.h +// details/os-inl.h +// fmt/bundled/core.h +// fmt/bundled/posix.h +// logger-inl.h +// sinks/daily_file_sink.h +// sinks/stdout_sinks.h +#include + +// details/os-inl.h +// fmt/bundled/posix.h +#include + +// details/os-inl.h +// details/pattern_formatter-inl.h +// fmt/bundled/core.h +// fmt/bundled/format-inl.h +#include + +// details/os-inl.h +// details/os.h +// details/pattern_formatter-inl.h +// details/pattern_formatter.h +// fmt/bundled/chrono.h +// sinks/daily_file_sink.h +// sinks/rotating_file_sink-inl.h +#include + +// fmt/bundled/format-inl.h +#include + +// fmt/bundled/format-inl.h +#include + +// fmt/bundled/format-inl.h +// fmt/bundled/format.h +#include + +// fmt/bundled/format-inl.h +#include + +// details/file_helper-inl.h +// fmt/bundled/format.h +// fmt/bundled/posix.h +// sinks/rotating_file_sink-inl.h +#include + +// details/circular_q.h +// details/thread_pool-inl.h +// fmt/bundled/format-inl.h +#include + +// async_logger-inl.h +// cfg/helpers-inl.h +// log_levels.h +// common.h +// details/file_helper-inl.h +// details/log_msg.h +// details/os-inl.h +// details/pattern_formatter-inl.h +// details/pattern_formatter.h +// details/registry-inl.h +// details/registry.h +// details/tcp_client-windows.h +// details/tcp_client.h +// fmt/bundled/core.h +// sinks/android_sink.h +// sinks/ansicolor_sink.h +// sinks/basic_file_sink.h +// sinks/daily_file_sink.h +// sinks/dup_filter_sink.h +// sinks/msvc_sink.h +// sinks/ringbuffer_sink.h +// sinks/rotating_file_sink-inl.h +// sinks/rotating_file_sink.h +// sinks/syslog_sink.h +// sinks/tcp_sink.h +// sinks/win_eventlog_sink.h +// sinks/wincolor_sink.h +// spdlog.h: +#include + +// cfg/helpers-inl.h +// fmt/bundled/chrono.h +#include + +// fmt/bundled/ostream.h +// sinks/ostream_sink.h +#include + +// cfg/log_levels.h +// details/registry-inl.h +// details/registry.h +#include + +// details/circular_q.h +// details/pattern_formatter-inl.h +// details/pattern_formatter.h +// details/thread_pool.h +// fmt/bundled/compile.h +// logger.h +// sinks/dist_sink.h +// sinks/ringbuffer_sink.h +// sinks/win_eventlog_sink.h +#include + +// details/os-inl.h +// details/pattern_formatter-inl.h +// sinks/ansicolor_sink.h +// sinks/syslog_sink.h +// sinks/systemd_sink.h +// sinks/wincolor_sink.h +#include + +// details/file_helper-inl.h +// details/file_helper.h +// sinks/rotating_file_sink-inl.h +#include + +// details/os-inl.h +// fmt/bundled/format.h +// fmt/bundled/printf.h +#include + +// common.h +// details/backtracer.h +// details/null_mutex.h +#include + +// common.h +// details/backtracer.h +// details/null_mutex.h +#include + +// common.h +#include + +// common.h +#include + +// common.h +// details/fmt_helper.h +// fmt/bundled/core.h +// fmt/bundled/ranges.h +#include + +// cfg/helpers-inl.h +// details/null_mutex.h +// details/pattern_formatter-inl.h +#include + +// async.h +// async_logger-inl.h +// common.h +// details/pattern_formatter-inl.h +// details/pattern_formatter.h +// details/registry-inl.h +// details/registry.h +// details/thread_pool.h +// fmt/bundled/format.h +// sinks/ansicolor_sink.h +// sinks/base_sink-inl.h +// sinks/dist_sink.h +// sinks/stdout_sinks-inl.h +// sinks/wincolor_sink.h +// spdlog.h +#include + +// async.h +// common.h +// details/backtracer.h +// details/periodic_worker.h +// details/registry-inl.h +// details/registry.h +// details/thread_pool.h +// sinks/tcp_sink.h +// spdlog.h +#include + +// details/mpmc_blocking_q.h +// details/periodic_worker.h +#include + +// details/os-inl.h +// fmt/bundled/format.h +// fmt/bundled/printf.h +// sinks/dist_sink.h +#include + +// common.h +// details/file_helper-inl.h +// details/fmt_helper.h +// details/os-inl.h +// details/pattern_formatter-inl.h +// details/pattern_formatter.h +// details/periodic_worker.h +// details/registry-inl.h +// details/registry.h +// details/thread_pool.h +// fmt/bundled/chrono.h +// sinks/android_sink.h +// sinks/daily_file_sink.h +// sinks/dup_filter_sink.h +// sinks/rotating_file_sink-inl.h +// sinks/rotating_file_sink.h +// sinks/tcp_sink.h +// spdlog.h +#include + +// details/file_helper-inl.h +// details/os-inl.h +// details/pattern_formatter-inl.h +// details/periodic_worker.h +// details/thread_pool.h +// sinks/android_sink.h +#include + +// async.h +// details/backtracer.h +// details/console_globals.h +// details/mpmc_blocking_q.h +// details/pattern_formatter-inl.h +// details/periodic_worker.h +// details/registry.h +// sinks/android_sink.h +// sinks/ansicolor_sink.h +// sinks/basic_file_sink.h +// sinks/daily_file_sink.h +// sinks/dist_sink.h +// sinks/dup_filter_sink.h +// sinks/msvc_sink.h +// sinks/null_sink.h +// sinks/ostream_sink.h +// sinks/ringbuffer_sink.h +// sinks/rotating_file_sink-inl.h +// sinks/rotating_file_sink.h +// sinks/tcp_sink.h +// sinks/win_eventlog_sink.h +// sinks/wincolor_sink.h +// +// color_sinks.cpp +// file_sinks.cpp +// spdlog.cpp +// stdout_sinks.cpp +#include + +// spdlog +#include \ No newline at end of file diff --git a/inc/spdlog/cmake/spdlog.pc.in b/inc/spdlog/cmake/spdlog.pc.in new file mode 100644 index 0000000..ffab5d6 --- /dev/null +++ b/inc/spdlog/cmake/spdlog.pc.in @@ -0,0 +1,13 @@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +includedir=@PKG_CONFIG_INCLUDEDIR@ +libdir=@PKG_CONFIG_LIBDIR@ + +Name: lib@PROJECT_NAME@ +Description: Fast C++ logging library. +URL: https://github.com/gabime/@PROJECT_NAME@ +Version: @SPDLOG_VERSION@ +CFlags: -I${includedir} @PKG_CONFIG_DEFINES@ +Libs: -L${libdir} -lspdlog -pthread +Requires: @PKG_CONFIG_REQUIRES@ + diff --git a/inc/spdlog/cmake/spdlogCPack.cmake b/inc/spdlog/cmake/spdlogCPack.cmake new file mode 100644 index 0000000..58bf401 --- /dev/null +++ b/inc/spdlog/cmake/spdlogCPack.cmake @@ -0,0 +1,60 @@ +set(CPACK_GENERATOR "TGZ;ZIP" CACHE STRING "Semicolon separated list of generators") + +set(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 0) +set(CPACK_INSTALL_CMAKE_PROJECTS "${CMAKE_BINARY_DIR}" "${PROJECT_NAME}" ALL .) + +set(CPACK_PROJECT_URL "https://github.com/gabime/spdlog") +set(CPACK_PACKAGE_VENDOR "Gabi Melman") +set(CPACK_PACKAGE_CONTACT "Gabi Melman ") +set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Fast C++ logging library") +set(CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) +set(CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) +set(CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) +set(CPACK_PACKAGE_VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}) +if(PROJECT_VERSION_TWEAK) + set(CPACK_PACKAGE_VERSION ${CPACK_PACKAGE_VERSION}.${PROJECT_VERSION_TWEAK}) +endif() +set(CPACK_PACKAGE_RELOCATABLE ON CACHE BOOL "Build relocatable package") + +set(CPACK_RPM_PACKAGE_LICENSE "MIT") +set(CPACK_RPM_PACKAGE_GROUP "Development/Libraries") +set(CPACK_DEBIAN_PACKAGE_SECTION "libs") +set(CPACK_RPM_PACKAGE_URL ${CPACK_PROJECT_URL}) +set(CPACK_DEBIAN_PACKAGE_HOMEPAGE ${CPACK_PROJECT_URL}) +set(CPACK_RPM_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.") +set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "Very fast, header-only/compiled, C++ logging library.") + +if(CPACK_PACKAGE_NAME) + set(CPACK_RPM_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") + set(CPACK_DEBIAN_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}") +else() + set(CPACK_RPM_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}") + set(CPACK_DEBIAN_FILE_NAME "${PROJECT_NAME}-${CPACK_PACKAGE_VERSION}") + set(CPACK_RPM_PACKAGE_NAME "${PROJECT_NAME}") + set(CPACK_DEBIAN_PACKAGE_NAME "${PROJECT_NAME}") +endif() + +if(CPACK_RPM_PACKAGE_RELEASE) + set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}-${CPACK_RPM_PACKAGE_RELEASE}") +endif() +if(CPACK_DEBIAN_PACKAGE_RELEASE) + set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}-${CPACK_DEBIAN_PACKAGE_RELEASE}") +endif() + +if(CPACK_RPM_PACKAGE_ARCHITECTURE) + set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.${CPACK_RPM_PACKAGE_ARCHITECTURE}") +endif() +if(CPACK_DEBIAN_PACKAGE_ARCHITECTURE) + set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}") +endif() +set(CPACK_RPM_FILE_NAME "${CPACK_RPM_FILE_NAME}.rpm") +set(CPACK_DEBIAN_FILE_NAME "${CPACK_DEBIAN_FILE_NAME}.deb") + +if(NOT CPACK_PACKAGE_RELOCATABLE) + # Depend on pkgconfig rpm to create the system pkgconfig folder + set(CPACK_RPM_PACKAGE_REQUIRES pkgconfig) + set(CPACK_RPM_EXCLUDE_FROM_AUTO_FILELIST_ADDITION + "${CPACK_PACKAGING_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/pkgconfig") +endif() + +include(CPack) diff --git a/inc/spdlog/cmake/spdlogConfig.cmake.in b/inc/spdlog/cmake/spdlogConfig.cmake.in new file mode 100644 index 0000000..d8a3ac1 --- /dev/null +++ b/inc/spdlog/cmake/spdlogConfig.cmake.in @@ -0,0 +1,20 @@ +# Copyright(c) 2019 spdlog authors +# Distributed under the MIT License (http://opensource.org/licenses/MIT) + +@PACKAGE_INIT@ + +find_package(Threads REQUIRED) + +set(SPDLOG_FMT_EXTERNAL @SPDLOG_FMT_EXTERNAL@) +set(SPDLOG_FMT_EXTERNAL_HO @SPDLOG_FMT_EXTERNAL_HO@) +set(config_targets_file @config_targets_file@) + +if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO) + include(CMakeFindDependencyMacro) + find_dependency(fmt CONFIG) +endif() + + +include("${CMAKE_CURRENT_LIST_DIR}/${config_targets_file}") + +check_required_components(spdlog) diff --git a/inc/spdlog/cmake/utils.cmake b/inc/spdlog/cmake/utils.cmake new file mode 100644 index 0000000..85fcd80 --- /dev/null +++ b/inc/spdlog/cmake/utils.cmake @@ -0,0 +1,62 @@ +# Get spdlog version from include/spdlog/version.h and put it in SPDLOG_VERSION +function(spdlog_extract_version) + file(READ "${CMAKE_CURRENT_LIST_DIR}/include/spdlog/version.h" file_contents) + string(REGEX MATCH "SPDLOG_VER_MAJOR ([0-9]+)" _ "${file_contents}") + if(NOT CMAKE_MATCH_COUNT EQUAL 1) + message(FATAL_ERROR "Could not extract major version number from spdlog/version.h") + endif() + set(ver_major ${CMAKE_MATCH_1}) + + string(REGEX MATCH "SPDLOG_VER_MINOR ([0-9]+)" _ "${file_contents}") + if(NOT CMAKE_MATCH_COUNT EQUAL 1) + message(FATAL_ERROR "Could not extract minor version number from spdlog/version.h") + endif() + + set(ver_minor ${CMAKE_MATCH_1}) + string(REGEX MATCH "SPDLOG_VER_PATCH ([0-9]+)" _ "${file_contents}") + if(NOT CMAKE_MATCH_COUNT EQUAL 1) + message(FATAL_ERROR "Could not extract patch version number from spdlog/version.h") + endif() + set(ver_patch ${CMAKE_MATCH_1}) + + set(SPDLOG_VERSION_MAJOR ${ver_major} PARENT_SCOPE) + set(SPDLOG_VERSION_MINOR ${ver_minor} PARENT_SCOPE) + set(SPDLOG_VERSION_PATCH ${ver_patch} PARENT_SCOPE) + set(SPDLOG_VERSION "${ver_major}.${ver_minor}.${ver_patch}" PARENT_SCOPE) +endfunction() + +# Turn on warnings on the given target +function(spdlog_enable_warnings target_name) + if(SPDLOG_BUILD_WARNINGS) + if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + list(APPEND MSVC_OPTIONS "/W3") + if(MSVC_VERSION GREATER 1900) # Allow non fatal security warnings for msvc 2015 + list(APPEND MSVC_OPTIONS "/WX") + endif() + endif() + + target_compile_options( + ${target_name} + PRIVATE $<$,$,$>: + -Wall + -Wextra + -Wconversion + -pedantic + -Werror + -Wfatal-errors> + $<$:${MSVC_OPTIONS}>) + endif() +endfunction() + +# Enable address sanitizer (gcc/clang only) +function(spdlog_enable_sanitizer target_name) + if(NOT CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + message(FATAL_ERROR "Sanitizer supported only for gcc/clang") + endif() + message(STATUS "Address sanitizer enabled") + target_compile_options(${target_name} PRIVATE -fsanitize=address,undefined) + target_compile_options(${target_name} PRIVATE -fno-sanitize=signed-integer-overflow) + target_compile_options(${target_name} PRIVATE -fno-sanitize-recover=all) + target_compile_options(${target_name} PRIVATE -fno-omit-frame-pointer) + target_link_libraries(${target_name} PRIVATE -fsanitize=address,undefined -fuse-ld=gold) +endfunction() diff --git a/inc/spdlog/cmake/version.rc.in b/inc/spdlog/cmake/version.rc.in new file mode 100644 index 0000000..a86c138 --- /dev/null +++ b/inc/spdlog/cmake/version.rc.in @@ -0,0 +1,42 @@ +#define APSTUDIO_READONLY_SYMBOLS +#include +#undef APSTUDIO_READONLY_SYMBOLS + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + + +VS_VERSION_INFO VERSIONINFO + FILEVERSION @SPDLOG_VERSION_MAJOR@,@SPDLOG_VERSION_MINOR@,@SPDLOG_VERSION_PATCH@,0 + PRODUCTVERSION @SPDLOG_VERSION_MAJOR@,@SPDLOG_VERSION_MINOR@,@SPDLOG_VERSION_PATCH@,0 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "spdlog dll\0" + VALUE "FileVersion", "@SPDLOG_VERSION@.0\0" + VALUE "InternalName", "spdlog.dll\0" + VALUE "LegalCopyright", "Copyright (C) spdlog\0" + VALUE "ProductName", "spdlog\0" + VALUE "ProductVersion", "@SPDLOG_VERSION@.0\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + + + + + diff --git a/inc/spdlog/example/CMakeLists.txt b/inc/spdlog/example/CMakeLists.txt new file mode 100644 index 0000000..da1ed4e --- /dev/null +++ b/inc/spdlog/example/CMakeLists.txt @@ -0,0 +1,23 @@ +# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT) + +cmake_minimum_required(VERSION 3.11) +project(spdlog_examples CXX) + +if(NOT TARGET spdlog) + # Stand-alone build + find_package(spdlog REQUIRED) +endif() + +# --------------------------------------------------------------------------------------- +# Example of using pre-compiled library +# --------------------------------------------------------------------------------------- +add_executable(example example.cpp) +target_link_libraries(example PRIVATE spdlog::spdlog $<$:ws2_32>) + +# --------------------------------------------------------------------------------------- +# Example of using header-only library +# --------------------------------------------------------------------------------------- +if(SPDLOG_BUILD_EXAMPLE_HO) + add_executable(example_header_only example.cpp) + target_link_libraries(example_header_only PRIVATE spdlog::spdlog_header_only) +endif() diff --git a/inc/spdlog/example/example.cpp b/inc/spdlog/example/example.cpp new file mode 100644 index 0000000..aeff6d7 --- /dev/null +++ b/inc/spdlog/example/example.cpp @@ -0,0 +1,378 @@ +// +// Copyright(c) 2015 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +// spdlog usage example + +#include +#include + +void load_levels_example(); +void stdout_logger_example(); +void basic_example(); +void rotating_example(); +void daily_example(); +void callback_example(); +void async_example(); +void binary_example(); +void vector_example(); +void stopwatch_example(); +void trace_example(); +void multi_sink_example(); +void user_defined_example(); +void err_handler_example(); +void syslog_example(); +void udp_example(); +void custom_flags_example(); +void file_events_example(); +void replace_default_logger_example(); + +#include "spdlog/spdlog.h" +#include "spdlog/cfg/env.h" // support for loading levels from the environment variable +#include "spdlog/fmt/ostr.h" // support for user defined types + +int main(int, char *[]) { + // Log levels can be loaded from argv/env using "SPDLOG_LEVEL" + load_levels_example(); + + spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, + SPDLOG_VER_PATCH); + + spdlog::warn("Easy padding in numbers like {:08d}", 12); + spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); + spdlog::info("Support for floats {:03.2f}", 1.23456); + spdlog::info("Positional args are {1} {0}..", "too", "supported"); + spdlog::info("{:>8} aligned, {:<8} aligned", "right", "left"); + + // Runtime log levels + spdlog::set_level(spdlog::level::info); // Set global log level to info + spdlog::debug("This message should not be displayed!"); + spdlog::set_level(spdlog::level::trace); // Set specific logger's log level + spdlog::debug("This message should be displayed.."); + + // Customize msg format for all loggers + spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [thread %t] %v"); + spdlog::info("This an info message with custom format"); + spdlog::set_pattern("%+"); // back to default format + spdlog::set_level(spdlog::level::info); + + // Backtrace support + // Loggers can store in a ring buffer all messages (including debug/trace) for later inspection. + // When needed, call dump_backtrace() to see what happened: + spdlog::enable_backtrace(10); // create ring buffer with capacity of 10 messages + for (int i = 0; i < 100; i++) { + spdlog::debug("Backtrace message {}", i); // not logged.. + } + // e.g. if some error happened: + spdlog::dump_backtrace(); // log them now! + + try { + stdout_logger_example(); + basic_example(); + rotating_example(); + daily_example(); + callback_example(); + async_example(); + binary_example(); + vector_example(); + multi_sink_example(); + user_defined_example(); + err_handler_example(); + trace_example(); + stopwatch_example(); + udp_example(); + custom_flags_example(); + file_events_example(); + replace_default_logger_example(); + + // Flush all *registered* loggers using a worker thread every 3 seconds. + // note: registered loggers *must* be thread safe for this to work correctly! + spdlog::flush_every(std::chrono::seconds(3)); + + // Apply some function on all registered loggers + spdlog::apply_all([&](std::shared_ptr l) { l->info("End of example."); }); + + // Release all spdlog resources, and drop all loggers in the registry. + // This is optional (only mandatory if using windows + async log). + spdlog::shutdown(); + } + + // Exceptions will only be thrown upon failed logger or sink construction (not during logging). + catch (const spdlog::spdlog_ex &ex) { + std::printf("Log initialization failed: %s\n", ex.what()); + return 1; + } +} + +#include "spdlog/sinks/stdout_color_sinks.h" +// or #include "spdlog/sinks/stdout_sinks.h" if no colors needed. +void stdout_logger_example() { + // Create color multi threaded logger. + auto console = spdlog::stdout_color_mt("console"); + // or for stderr: + // auto console = spdlog::stderr_color_mt("error-logger"); +} + +#include "spdlog/sinks/basic_file_sink.h" +void basic_example() { + // Create basic file logger (not rotated). + auto my_logger = spdlog::basic_logger_mt("file_logger", "logs/basic-log.txt", true); +} + +#include "spdlog/sinks/rotating_file_sink.h" +void rotating_example() { + // Create a file rotating logger with 5mb size max and 3 rotated files. + auto rotating_logger = + spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3); +} + +#include "spdlog/sinks/daily_file_sink.h" +void daily_example() { + // Create a daily logger - a new file is created every day on 2:30am. + auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30); +} + +#include "spdlog/sinks/callback_sink.h" +void callback_example() { + // Create the logger + auto logger = spdlog::callback_logger_mt("custom_callback_logger", + [](const spdlog::details::log_msg & /*msg*/) { + // do what you need to do with msg + }); +} + +#include "spdlog/cfg/env.h" +void load_levels_example() { + // Set the log level to "info" and mylogger to "trace": + // SPDLOG_LEVEL=info,mylogger=trace && ./example + spdlog::cfg::load_env_levels(); + // or from command line: + // ./example SPDLOG_LEVEL=info,mylogger=trace + // #include "spdlog/cfg/argv.h" // for loading levels from argv + // spdlog::cfg::load_argv_levels(args, argv); +} + +#include "spdlog/async.h" +void async_example() { + // Default thread pool settings can be modified *before* creating the async logger: + // spdlog::init_thread_pool(32768, 1); // queue with max 32k items 1 backing thread. + auto async_file = + spdlog::basic_logger_mt("async_file_logger", "logs/async_log.txt"); + // alternatively: + // auto async_file = + // spdlog::create_async("async_file_logger", + // "logs/async_log.txt"); + + for (int i = 1; i < 101; ++i) { + async_file->info("Async message #{}", i); + } +} + +// Log binary data as hex. +// Many types of std::container types can be used. +// Iterator ranges are supported too. +// Format flags: +// {:X} - print in uppercase. +// {:s} - don't separate each byte with space. +// {:p} - don't print the position on each line start. +// {:n} - don't split the output to lines. + +#if !defined SPDLOG_USE_STD_FORMAT || defined(_MSC_VER) + #include "spdlog/fmt/bin_to_hex.h" +void binary_example() { + std::vector buf(80); + for (int i = 0; i < 80; i++) { + buf.push_back(static_cast(i & 0xff)); + } + spdlog::info("Binary example: {}", spdlog::to_hex(buf)); + spdlog::info("Another binary example:{:n}", + spdlog::to_hex(std::begin(buf), std::begin(buf) + 10)); + // more examples: + // logger->info("uppercase: {:X}", spdlog::to_hex(buf)); + // logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf)); + // logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf)); + // logger->info("hexdump style: {:a}", spdlog::to_hex(buf)); + // logger->info("hexdump style, 20 chars per line {:a}", spdlog::to_hex(buf, 20)); +} +#else +void binary_example() { + // not supported with std::format yet +} +#endif + +// Log a vector of numbers +#ifndef SPDLOG_USE_STD_FORMAT + #include "spdlog/fmt/ranges.h" +void vector_example() { + std::vector vec = {1, 2, 3}; + spdlog::info("Vector example: {}", vec); +} + +#else +void vector_example() {} +#endif + +// ! DSPDLOG_USE_STD_FORMAT + +// Compile time log levels. +// define SPDLOG_ACTIVE_LEVEL to required level (e.g. SPDLOG_LEVEL_TRACE) +void trace_example() { + // trace from default logger + SPDLOG_TRACE("Some trace message.. {} ,{}", 1, 3.23); + // debug from default logger + SPDLOG_DEBUG("Some debug message.. {} ,{}", 1, 3.23); + + // trace from logger object + auto logger = spdlog::get("file_logger"); + SPDLOG_LOGGER_TRACE(logger, "another trace message"); +} + +// stopwatch example +#include "spdlog/stopwatch.h" +#include +void stopwatch_example() { + spdlog::stopwatch sw; + std::this_thread::sleep_for(std::chrono::milliseconds(123)); + spdlog::info("Stopwatch: {} seconds", sw); +} + +#include "spdlog/sinks/udp_sink.h" +void udp_example() { + spdlog::sinks::udp_sink_config cfg("127.0.0.1", 11091); + auto my_logger = spdlog::udp_logger_mt("udplog", cfg); + my_logger->set_level(spdlog::level::debug); + my_logger->info("hello world"); +} + +// A logger with multiple sinks (stdout and file) - each with a different format and log level. +void multi_sink_example() { + auto console_sink = std::make_shared(); + console_sink->set_level(spdlog::level::warn); + console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v"); + + auto file_sink = + std::make_shared("logs/multisink.txt", true); + file_sink->set_level(spdlog::level::trace); + + spdlog::logger logger("multi_sink", {console_sink, file_sink}); + logger.set_level(spdlog::level::debug); + logger.warn("this should appear in both console and file"); + logger.info("this message should not appear in the console, only in the file"); +} + +// User defined types logging +struct my_type { + int i = 0; + explicit my_type(int i) + : i(i){}; +}; + +#ifndef SPDLOG_USE_STD_FORMAT // when using fmtlib +template <> +struct fmt::formatter : fmt::formatter { + auto format(my_type my, format_context &ctx) -> decltype(ctx.out()) { + return fmt::format_to(ctx.out(), "[my_type i={}]", my.i); + } +}; + +#else // when using std::format +template <> +struct std::formatter : std::formatter { + auto format(my_type my, format_context &ctx) const -> decltype(ctx.out()) { + return format_to(ctx.out(), "[my_type i={}]", my.i); + } +}; +#endif + +void user_defined_example() { spdlog::info("user defined type: {}", my_type(14)); } + +// Custom error handler. Will be triggered on log failure. +void err_handler_example() { + // can be set globally or per logger(logger->set_error_handler(..)) + spdlog::set_error_handler([](const std::string &msg) { + printf("*** Custom log error handler: %s ***\n", msg.c_str()); + }); +} + +// syslog example (linux/osx/freebsd) +#ifndef _WIN32 + #include "spdlog/sinks/syslog_sink.h" +void syslog_example() { + std::string ident = "spdlog-example"; + auto syslog_logger = spdlog::syslog_logger_mt("syslog", ident, LOG_PID); + syslog_logger->warn("This is warning that will end up in syslog."); +} +#endif + +// Android example. +#if defined(__ANDROID__) + #include "spdlog/sinks/android_sink.h" +void android_example() { + std::string tag = "spdlog-android"; + auto android_logger = spdlog::android_logger_mt("android", tag); + android_logger->critical("Use \"adb shell logcat\" to view this message."); +} +#endif + +// Log patterns can contain custom flags. +// this will add custom flag '%*' which will be bound to a instance +#include "spdlog/pattern_formatter.h" +class my_formatter_flag : public spdlog::custom_flag_formatter { +public: + void format(const spdlog::details::log_msg &, + const std::tm &, + spdlog::memory_buf_t &dest) override { + std::string some_txt = "custom-flag"; + dest.append(some_txt.data(), some_txt.data() + some_txt.size()); + } + + std::unique_ptr clone() const override { + return spdlog::details::make_unique(); + } +}; + +void custom_flags_example() { + using spdlog::details::make_unique; // for pre c++14 + auto formatter = make_unique(); + formatter->add_flag('*').set_pattern("[%n] [%*] [%^%l%$] %v"); + // set the new formatter using spdlog::set_formatter(formatter) or + // logger->set_formatter(formatter) spdlog::set_formatter(std::move(formatter)); +} + +void file_events_example() { + // pass the spdlog::file_event_handlers to file sinks for open/close log file notifications + spdlog::file_event_handlers handlers; + handlers.before_open = [](spdlog::filename_t filename) { + spdlog::info("Before opening {}", filename); + }; + handlers.after_open = [](spdlog::filename_t filename, std::FILE *fstream) { + spdlog::info("After opening {}", filename); + fputs("After opening\n", fstream); + }; + handlers.before_close = [](spdlog::filename_t filename, std::FILE *fstream) { + spdlog::info("Before closing {}", filename); + fputs("Before closing\n", fstream); + }; + handlers.after_close = [](spdlog::filename_t filename) { + spdlog::info("After closing {}", filename); + }; + auto file_sink = std::make_shared("logs/events-sample.txt", + true, handlers); + spdlog::logger my_logger("some_logger", file_sink); + my_logger.info("Some log line"); +} + +void replace_default_logger_example() { + // store the old logger so we don't break other examples. + auto old_logger = spdlog::default_logger(); + + auto new_logger = + spdlog::basic_logger_mt("new_default_logger", "logs/new-default-log.txt", true); + spdlog::set_default_logger(new_logger); + spdlog::set_level(spdlog::level::info); + spdlog::debug("This message should not be displayed!"); + spdlog::set_level(spdlog::level::trace); + spdlog::debug("This message should be displayed.."); + + spdlog::set_default_logger(old_logger); +} diff --git a/inc/spdlog/async.h b/inc/spdlog/include/spdlog/async.h similarity index 100% rename from inc/spdlog/async.h rename to inc/spdlog/include/spdlog/async.h diff --git a/inc/spdlog/async_logger-inl.h b/inc/spdlog/include/spdlog/async_logger-inl.h similarity index 100% rename from inc/spdlog/async_logger-inl.h rename to inc/spdlog/include/spdlog/async_logger-inl.h diff --git a/inc/spdlog/async_logger.h b/inc/spdlog/include/spdlog/async_logger.h similarity index 100% rename from inc/spdlog/async_logger.h rename to inc/spdlog/include/spdlog/async_logger.h diff --git a/inc/spdlog/cfg/argv.h b/inc/spdlog/include/spdlog/cfg/argv.h similarity index 100% rename from inc/spdlog/cfg/argv.h rename to inc/spdlog/include/spdlog/cfg/argv.h diff --git a/inc/spdlog/cfg/env.h b/inc/spdlog/include/spdlog/cfg/env.h similarity index 100% rename from inc/spdlog/cfg/env.h rename to inc/spdlog/include/spdlog/cfg/env.h diff --git a/inc/spdlog/cfg/helpers-inl.h b/inc/spdlog/include/spdlog/cfg/helpers-inl.h similarity index 100% rename from inc/spdlog/cfg/helpers-inl.h rename to inc/spdlog/include/spdlog/cfg/helpers-inl.h diff --git a/inc/spdlog/cfg/helpers.h b/inc/spdlog/include/spdlog/cfg/helpers.h similarity index 100% rename from inc/spdlog/cfg/helpers.h rename to inc/spdlog/include/spdlog/cfg/helpers.h diff --git a/inc/spdlog/common-inl.h b/inc/spdlog/include/spdlog/common-inl.h similarity index 100% rename from inc/spdlog/common-inl.h rename to inc/spdlog/include/spdlog/common-inl.h diff --git a/inc/spdlog/common.h b/inc/spdlog/include/spdlog/common.h similarity index 100% rename from inc/spdlog/common.h rename to inc/spdlog/include/spdlog/common.h diff --git a/inc/spdlog/details/backtracer-inl.h b/inc/spdlog/include/spdlog/details/backtracer-inl.h similarity index 100% rename from inc/spdlog/details/backtracer-inl.h rename to inc/spdlog/include/spdlog/details/backtracer-inl.h diff --git a/inc/spdlog/details/backtracer.h b/inc/spdlog/include/spdlog/details/backtracer.h similarity index 100% rename from inc/spdlog/details/backtracer.h rename to inc/spdlog/include/spdlog/details/backtracer.h diff --git a/inc/spdlog/details/circular_q.h b/inc/spdlog/include/spdlog/details/circular_q.h similarity index 100% rename from inc/spdlog/details/circular_q.h rename to inc/spdlog/include/spdlog/details/circular_q.h diff --git a/inc/spdlog/details/console_globals.h b/inc/spdlog/include/spdlog/details/console_globals.h similarity index 100% rename from inc/spdlog/details/console_globals.h rename to inc/spdlog/include/spdlog/details/console_globals.h diff --git a/inc/spdlog/details/file_helper-inl.h b/inc/spdlog/include/spdlog/details/file_helper-inl.h similarity index 100% rename from inc/spdlog/details/file_helper-inl.h rename to inc/spdlog/include/spdlog/details/file_helper-inl.h diff --git a/inc/spdlog/details/file_helper.h b/inc/spdlog/include/spdlog/details/file_helper.h similarity index 100% rename from inc/spdlog/details/file_helper.h rename to inc/spdlog/include/spdlog/details/file_helper.h diff --git a/inc/spdlog/details/fmt_helper.h b/inc/spdlog/include/spdlog/details/fmt_helper.h similarity index 100% rename from inc/spdlog/details/fmt_helper.h rename to inc/spdlog/include/spdlog/details/fmt_helper.h diff --git a/inc/spdlog/details/log_msg-inl.h b/inc/spdlog/include/spdlog/details/log_msg-inl.h similarity index 100% rename from inc/spdlog/details/log_msg-inl.h rename to inc/spdlog/include/spdlog/details/log_msg-inl.h diff --git a/inc/spdlog/details/log_msg.h b/inc/spdlog/include/spdlog/details/log_msg.h similarity index 100% rename from inc/spdlog/details/log_msg.h rename to inc/spdlog/include/spdlog/details/log_msg.h diff --git a/inc/spdlog/details/log_msg_buffer-inl.h b/inc/spdlog/include/spdlog/details/log_msg_buffer-inl.h similarity index 100% rename from inc/spdlog/details/log_msg_buffer-inl.h rename to inc/spdlog/include/spdlog/details/log_msg_buffer-inl.h diff --git a/inc/spdlog/details/log_msg_buffer.h b/inc/spdlog/include/spdlog/details/log_msg_buffer.h similarity index 100% rename from inc/spdlog/details/log_msg_buffer.h rename to inc/spdlog/include/spdlog/details/log_msg_buffer.h diff --git a/inc/spdlog/details/mpmc_blocking_q.h b/inc/spdlog/include/spdlog/details/mpmc_blocking_q.h similarity index 100% rename from inc/spdlog/details/mpmc_blocking_q.h rename to inc/spdlog/include/spdlog/details/mpmc_blocking_q.h diff --git a/inc/spdlog/details/null_mutex.h b/inc/spdlog/include/spdlog/details/null_mutex.h similarity index 100% rename from inc/spdlog/details/null_mutex.h rename to inc/spdlog/include/spdlog/details/null_mutex.h diff --git a/inc/spdlog/details/os-inl.h b/inc/spdlog/include/spdlog/details/os-inl.h similarity index 100% rename from inc/spdlog/details/os-inl.h rename to inc/spdlog/include/spdlog/details/os-inl.h diff --git a/inc/spdlog/details/os.h b/inc/spdlog/include/spdlog/details/os.h similarity index 100% rename from inc/spdlog/details/os.h rename to inc/spdlog/include/spdlog/details/os.h diff --git a/inc/spdlog/details/periodic_worker-inl.h b/inc/spdlog/include/spdlog/details/periodic_worker-inl.h similarity index 100% rename from inc/spdlog/details/periodic_worker-inl.h rename to inc/spdlog/include/spdlog/details/periodic_worker-inl.h diff --git a/inc/spdlog/details/periodic_worker.h b/inc/spdlog/include/spdlog/details/periodic_worker.h similarity index 100% rename from inc/spdlog/details/periodic_worker.h rename to inc/spdlog/include/spdlog/details/periodic_worker.h diff --git a/inc/spdlog/details/registry-inl.h b/inc/spdlog/include/spdlog/details/registry-inl.h similarity index 100% rename from inc/spdlog/details/registry-inl.h rename to inc/spdlog/include/spdlog/details/registry-inl.h diff --git a/inc/spdlog/details/registry.h b/inc/spdlog/include/spdlog/details/registry.h similarity index 100% rename from inc/spdlog/details/registry.h rename to inc/spdlog/include/spdlog/details/registry.h diff --git a/inc/spdlog/details/synchronous_factory.h b/inc/spdlog/include/spdlog/details/synchronous_factory.h similarity index 100% rename from inc/spdlog/details/synchronous_factory.h rename to inc/spdlog/include/spdlog/details/synchronous_factory.h diff --git a/inc/spdlog/details/tcp_client-windows.h b/inc/spdlog/include/spdlog/details/tcp_client-windows.h similarity index 100% rename from inc/spdlog/details/tcp_client-windows.h rename to inc/spdlog/include/spdlog/details/tcp_client-windows.h diff --git a/inc/spdlog/details/tcp_client.h b/inc/spdlog/include/spdlog/details/tcp_client.h similarity index 100% rename from inc/spdlog/details/tcp_client.h rename to inc/spdlog/include/spdlog/details/tcp_client.h diff --git a/inc/spdlog/details/thread_pool-inl.h b/inc/spdlog/include/spdlog/details/thread_pool-inl.h similarity index 100% rename from inc/spdlog/details/thread_pool-inl.h rename to inc/spdlog/include/spdlog/details/thread_pool-inl.h diff --git a/inc/spdlog/details/thread_pool.h b/inc/spdlog/include/spdlog/details/thread_pool.h similarity index 100% rename from inc/spdlog/details/thread_pool.h rename to inc/spdlog/include/spdlog/details/thread_pool.h diff --git a/inc/spdlog/details/udp_client-windows.h b/inc/spdlog/include/spdlog/details/udp_client-windows.h similarity index 100% rename from inc/spdlog/details/udp_client-windows.h rename to inc/spdlog/include/spdlog/details/udp_client-windows.h diff --git a/inc/spdlog/details/udp_client.h b/inc/spdlog/include/spdlog/details/udp_client.h similarity index 100% rename from inc/spdlog/details/udp_client.h rename to inc/spdlog/include/spdlog/details/udp_client.h diff --git a/inc/spdlog/details/windows_include.h b/inc/spdlog/include/spdlog/details/windows_include.h similarity index 100% rename from inc/spdlog/details/windows_include.h rename to inc/spdlog/include/spdlog/details/windows_include.h diff --git a/inc/spdlog/fmt/bin_to_hex.h b/inc/spdlog/include/spdlog/fmt/bin_to_hex.h similarity index 100% rename from inc/spdlog/fmt/bin_to_hex.h rename to inc/spdlog/include/spdlog/fmt/bin_to_hex.h diff --git a/inc/spdlog/fmt/bundled/args.h b/inc/spdlog/include/spdlog/fmt/bundled/args.h similarity index 100% rename from inc/spdlog/fmt/bundled/args.h rename to inc/spdlog/include/spdlog/fmt/bundled/args.h diff --git a/inc/spdlog/fmt/bundled/chrono.h b/inc/spdlog/include/spdlog/fmt/bundled/chrono.h similarity index 100% rename from inc/spdlog/fmt/bundled/chrono.h rename to inc/spdlog/include/spdlog/fmt/bundled/chrono.h diff --git a/inc/spdlog/fmt/bundled/color.h b/inc/spdlog/include/spdlog/fmt/bundled/color.h similarity index 100% rename from inc/spdlog/fmt/bundled/color.h rename to inc/spdlog/include/spdlog/fmt/bundled/color.h diff --git a/inc/spdlog/fmt/bundled/compile.h b/inc/spdlog/include/spdlog/fmt/bundled/compile.h similarity index 100% rename from inc/spdlog/fmt/bundled/compile.h rename to inc/spdlog/include/spdlog/fmt/bundled/compile.h diff --git a/inc/spdlog/fmt/bundled/core.h b/inc/spdlog/include/spdlog/fmt/bundled/core.h similarity index 100% rename from inc/spdlog/fmt/bundled/core.h rename to inc/spdlog/include/spdlog/fmt/bundled/core.h diff --git a/inc/spdlog/fmt/bundled/fmt.license.rst b/inc/spdlog/include/spdlog/fmt/bundled/fmt.license.rst similarity index 100% rename from inc/spdlog/fmt/bundled/fmt.license.rst rename to inc/spdlog/include/spdlog/fmt/bundled/fmt.license.rst diff --git a/inc/spdlog/fmt/bundled/format-inl.h b/inc/spdlog/include/spdlog/fmt/bundled/format-inl.h similarity index 100% rename from inc/spdlog/fmt/bundled/format-inl.h rename to inc/spdlog/include/spdlog/fmt/bundled/format-inl.h diff --git a/inc/spdlog/fmt/bundled/format.h b/inc/spdlog/include/spdlog/fmt/bundled/format.h similarity index 100% rename from inc/spdlog/fmt/bundled/format.h rename to inc/spdlog/include/spdlog/fmt/bundled/format.h diff --git a/inc/spdlog/fmt/bundled/locale.h b/inc/spdlog/include/spdlog/fmt/bundled/locale.h similarity index 100% rename from inc/spdlog/fmt/bundled/locale.h rename to inc/spdlog/include/spdlog/fmt/bundled/locale.h diff --git a/inc/spdlog/fmt/bundled/os.h b/inc/spdlog/include/spdlog/fmt/bundled/os.h similarity index 100% rename from inc/spdlog/fmt/bundled/os.h rename to inc/spdlog/include/spdlog/fmt/bundled/os.h diff --git a/inc/spdlog/fmt/bundled/ostream.h b/inc/spdlog/include/spdlog/fmt/bundled/ostream.h similarity index 100% rename from inc/spdlog/fmt/bundled/ostream.h rename to inc/spdlog/include/spdlog/fmt/bundled/ostream.h diff --git a/inc/spdlog/fmt/bundled/printf.h b/inc/spdlog/include/spdlog/fmt/bundled/printf.h similarity index 100% rename from inc/spdlog/fmt/bundled/printf.h rename to inc/spdlog/include/spdlog/fmt/bundled/printf.h diff --git a/inc/spdlog/fmt/bundled/ranges.h b/inc/spdlog/include/spdlog/fmt/bundled/ranges.h similarity index 100% rename from inc/spdlog/fmt/bundled/ranges.h rename to inc/spdlog/include/spdlog/fmt/bundled/ranges.h diff --git a/inc/spdlog/fmt/bundled/std.h b/inc/spdlog/include/spdlog/fmt/bundled/std.h similarity index 100% rename from inc/spdlog/fmt/bundled/std.h rename to inc/spdlog/include/spdlog/fmt/bundled/std.h diff --git a/inc/spdlog/fmt/bundled/xchar.h b/inc/spdlog/include/spdlog/fmt/bundled/xchar.h similarity index 100% rename from inc/spdlog/fmt/bundled/xchar.h rename to inc/spdlog/include/spdlog/fmt/bundled/xchar.h diff --git a/inc/spdlog/fmt/chrono.h b/inc/spdlog/include/spdlog/fmt/chrono.h similarity index 100% rename from inc/spdlog/fmt/chrono.h rename to inc/spdlog/include/spdlog/fmt/chrono.h diff --git a/inc/spdlog/fmt/compile.h b/inc/spdlog/include/spdlog/fmt/compile.h similarity index 100% rename from inc/spdlog/fmt/compile.h rename to inc/spdlog/include/spdlog/fmt/compile.h diff --git a/inc/spdlog/fmt/fmt.h b/inc/spdlog/include/spdlog/fmt/fmt.h similarity index 100% rename from inc/spdlog/fmt/fmt.h rename to inc/spdlog/include/spdlog/fmt/fmt.h diff --git a/inc/spdlog/fmt/ostr.h b/inc/spdlog/include/spdlog/fmt/ostr.h similarity index 100% rename from inc/spdlog/fmt/ostr.h rename to inc/spdlog/include/spdlog/fmt/ostr.h diff --git a/inc/spdlog/fmt/ranges.h b/inc/spdlog/include/spdlog/fmt/ranges.h similarity index 100% rename from inc/spdlog/fmt/ranges.h rename to inc/spdlog/include/spdlog/fmt/ranges.h diff --git a/inc/spdlog/fmt/std.h b/inc/spdlog/include/spdlog/fmt/std.h similarity index 100% rename from inc/spdlog/fmt/std.h rename to inc/spdlog/include/spdlog/fmt/std.h diff --git a/inc/spdlog/fmt/xchar.h b/inc/spdlog/include/spdlog/fmt/xchar.h similarity index 100% rename from inc/spdlog/fmt/xchar.h rename to inc/spdlog/include/spdlog/fmt/xchar.h diff --git a/inc/spdlog/formatter.h b/inc/spdlog/include/spdlog/formatter.h similarity index 100% rename from inc/spdlog/formatter.h rename to inc/spdlog/include/spdlog/formatter.h diff --git a/inc/spdlog/fwd.h b/inc/spdlog/include/spdlog/fwd.h similarity index 100% rename from inc/spdlog/fwd.h rename to inc/spdlog/include/spdlog/fwd.h diff --git a/inc/spdlog/logger-inl.h b/inc/spdlog/include/spdlog/logger-inl.h similarity index 100% rename from inc/spdlog/logger-inl.h rename to inc/spdlog/include/spdlog/logger-inl.h diff --git a/inc/spdlog/logger.h b/inc/spdlog/include/spdlog/logger.h similarity index 100% rename from inc/spdlog/logger.h rename to inc/spdlog/include/spdlog/logger.h diff --git a/inc/spdlog/pattern_formatter-inl.h b/inc/spdlog/include/spdlog/pattern_formatter-inl.h similarity index 100% rename from inc/spdlog/pattern_formatter-inl.h rename to inc/spdlog/include/spdlog/pattern_formatter-inl.h diff --git a/inc/spdlog/pattern_formatter.h b/inc/spdlog/include/spdlog/pattern_formatter.h similarity index 100% rename from inc/spdlog/pattern_formatter.h rename to inc/spdlog/include/spdlog/pattern_formatter.h diff --git a/inc/spdlog/sinks/android_sink.h b/inc/spdlog/include/spdlog/sinks/android_sink.h similarity index 100% rename from inc/spdlog/sinks/android_sink.h rename to inc/spdlog/include/spdlog/sinks/android_sink.h diff --git a/inc/spdlog/sinks/ansicolor_sink-inl.h b/inc/spdlog/include/spdlog/sinks/ansicolor_sink-inl.h similarity index 100% rename from inc/spdlog/sinks/ansicolor_sink-inl.h rename to inc/spdlog/include/spdlog/sinks/ansicolor_sink-inl.h diff --git a/inc/spdlog/sinks/ansicolor_sink.h b/inc/spdlog/include/spdlog/sinks/ansicolor_sink.h similarity index 100% rename from inc/spdlog/sinks/ansicolor_sink.h rename to inc/spdlog/include/spdlog/sinks/ansicolor_sink.h diff --git a/inc/spdlog/sinks/base_sink-inl.h b/inc/spdlog/include/spdlog/sinks/base_sink-inl.h similarity index 100% rename from inc/spdlog/sinks/base_sink-inl.h rename to inc/spdlog/include/spdlog/sinks/base_sink-inl.h diff --git a/inc/spdlog/sinks/base_sink.h b/inc/spdlog/include/spdlog/sinks/base_sink.h similarity index 100% rename from inc/spdlog/sinks/base_sink.h rename to inc/spdlog/include/spdlog/sinks/base_sink.h diff --git a/inc/spdlog/sinks/basic_file_sink-inl.h b/inc/spdlog/include/spdlog/sinks/basic_file_sink-inl.h similarity index 100% rename from inc/spdlog/sinks/basic_file_sink-inl.h rename to inc/spdlog/include/spdlog/sinks/basic_file_sink-inl.h diff --git a/inc/spdlog/sinks/basic_file_sink.h b/inc/spdlog/include/spdlog/sinks/basic_file_sink.h similarity index 100% rename from inc/spdlog/sinks/basic_file_sink.h rename to inc/spdlog/include/spdlog/sinks/basic_file_sink.h diff --git a/inc/spdlog/sinks/callback_sink.h b/inc/spdlog/include/spdlog/sinks/callback_sink.h similarity index 100% rename from inc/spdlog/sinks/callback_sink.h rename to inc/spdlog/include/spdlog/sinks/callback_sink.h diff --git a/inc/spdlog/sinks/daily_file_sink.h b/inc/spdlog/include/spdlog/sinks/daily_file_sink.h similarity index 100% rename from inc/spdlog/sinks/daily_file_sink.h rename to inc/spdlog/include/spdlog/sinks/daily_file_sink.h diff --git a/inc/spdlog/sinks/dist_sink.h b/inc/spdlog/include/spdlog/sinks/dist_sink.h similarity index 100% rename from inc/spdlog/sinks/dist_sink.h rename to inc/spdlog/include/spdlog/sinks/dist_sink.h diff --git a/inc/spdlog/sinks/dup_filter_sink.h b/inc/spdlog/include/spdlog/sinks/dup_filter_sink.h similarity index 100% rename from inc/spdlog/sinks/dup_filter_sink.h rename to inc/spdlog/include/spdlog/sinks/dup_filter_sink.h diff --git a/inc/spdlog/sinks/hourly_file_sink.h b/inc/spdlog/include/spdlog/sinks/hourly_file_sink.h similarity index 100% rename from inc/spdlog/sinks/hourly_file_sink.h rename to inc/spdlog/include/spdlog/sinks/hourly_file_sink.h diff --git a/inc/spdlog/sinks/kafka_sink.h b/inc/spdlog/include/spdlog/sinks/kafka_sink.h similarity index 100% rename from inc/spdlog/sinks/kafka_sink.h rename to inc/spdlog/include/spdlog/sinks/kafka_sink.h diff --git a/inc/spdlog/sinks/mongo_sink.h b/inc/spdlog/include/spdlog/sinks/mongo_sink.h similarity index 100% rename from inc/spdlog/sinks/mongo_sink.h rename to inc/spdlog/include/spdlog/sinks/mongo_sink.h diff --git a/inc/spdlog/sinks/msvc_sink.h b/inc/spdlog/include/spdlog/sinks/msvc_sink.h similarity index 100% rename from inc/spdlog/sinks/msvc_sink.h rename to inc/spdlog/include/spdlog/sinks/msvc_sink.h diff --git a/inc/spdlog/sinks/null_sink.h b/inc/spdlog/include/spdlog/sinks/null_sink.h similarity index 100% rename from inc/spdlog/sinks/null_sink.h rename to inc/spdlog/include/spdlog/sinks/null_sink.h diff --git a/inc/spdlog/sinks/ostream_sink.h b/inc/spdlog/include/spdlog/sinks/ostream_sink.h similarity index 100% rename from inc/spdlog/sinks/ostream_sink.h rename to inc/spdlog/include/spdlog/sinks/ostream_sink.h diff --git a/inc/spdlog/sinks/qt_sinks.h b/inc/spdlog/include/spdlog/sinks/qt_sinks.h similarity index 100% rename from inc/spdlog/sinks/qt_sinks.h rename to inc/spdlog/include/spdlog/sinks/qt_sinks.h diff --git a/inc/spdlog/sinks/ringbuffer_sink.h b/inc/spdlog/include/spdlog/sinks/ringbuffer_sink.h similarity index 100% rename from inc/spdlog/sinks/ringbuffer_sink.h rename to inc/spdlog/include/spdlog/sinks/ringbuffer_sink.h diff --git a/inc/spdlog/sinks/rotating_file_sink-inl.h b/inc/spdlog/include/spdlog/sinks/rotating_file_sink-inl.h similarity index 100% rename from inc/spdlog/sinks/rotating_file_sink-inl.h rename to inc/spdlog/include/spdlog/sinks/rotating_file_sink-inl.h diff --git a/inc/spdlog/sinks/rotating_file_sink.h b/inc/spdlog/include/spdlog/sinks/rotating_file_sink.h similarity index 100% rename from inc/spdlog/sinks/rotating_file_sink.h rename to inc/spdlog/include/spdlog/sinks/rotating_file_sink.h diff --git a/inc/spdlog/sinks/sink-inl.h b/inc/spdlog/include/spdlog/sinks/sink-inl.h similarity index 100% rename from inc/spdlog/sinks/sink-inl.h rename to inc/spdlog/include/spdlog/sinks/sink-inl.h diff --git a/inc/spdlog/sinks/sink.h b/inc/spdlog/include/spdlog/sinks/sink.h similarity index 100% rename from inc/spdlog/sinks/sink.h rename to inc/spdlog/include/spdlog/sinks/sink.h diff --git a/inc/spdlog/sinks/stdout_color_sinks-inl.h b/inc/spdlog/include/spdlog/sinks/stdout_color_sinks-inl.h similarity index 100% rename from inc/spdlog/sinks/stdout_color_sinks-inl.h rename to inc/spdlog/include/spdlog/sinks/stdout_color_sinks-inl.h diff --git a/inc/spdlog/sinks/stdout_color_sinks.h b/inc/spdlog/include/spdlog/sinks/stdout_color_sinks.h similarity index 100% rename from inc/spdlog/sinks/stdout_color_sinks.h rename to inc/spdlog/include/spdlog/sinks/stdout_color_sinks.h diff --git a/inc/spdlog/sinks/stdout_sinks-inl.h b/inc/spdlog/include/spdlog/sinks/stdout_sinks-inl.h similarity index 100% rename from inc/spdlog/sinks/stdout_sinks-inl.h rename to inc/spdlog/include/spdlog/sinks/stdout_sinks-inl.h diff --git a/inc/spdlog/sinks/stdout_sinks.h b/inc/spdlog/include/spdlog/sinks/stdout_sinks.h similarity index 100% rename from inc/spdlog/sinks/stdout_sinks.h rename to inc/spdlog/include/spdlog/sinks/stdout_sinks.h diff --git a/inc/spdlog/sinks/syslog_sink.h b/inc/spdlog/include/spdlog/sinks/syslog_sink.h similarity index 100% rename from inc/spdlog/sinks/syslog_sink.h rename to inc/spdlog/include/spdlog/sinks/syslog_sink.h diff --git a/inc/spdlog/sinks/systemd_sink.h b/inc/spdlog/include/spdlog/sinks/systemd_sink.h similarity index 100% rename from inc/spdlog/sinks/systemd_sink.h rename to inc/spdlog/include/spdlog/sinks/systemd_sink.h diff --git a/inc/spdlog/sinks/tcp_sink.h b/inc/spdlog/include/spdlog/sinks/tcp_sink.h similarity index 100% rename from inc/spdlog/sinks/tcp_sink.h rename to inc/spdlog/include/spdlog/sinks/tcp_sink.h diff --git a/inc/spdlog/sinks/udp_sink.h b/inc/spdlog/include/spdlog/sinks/udp_sink.h similarity index 100% rename from inc/spdlog/sinks/udp_sink.h rename to inc/spdlog/include/spdlog/sinks/udp_sink.h diff --git a/inc/spdlog/sinks/win_eventlog_sink.h b/inc/spdlog/include/spdlog/sinks/win_eventlog_sink.h similarity index 100% rename from inc/spdlog/sinks/win_eventlog_sink.h rename to inc/spdlog/include/spdlog/sinks/win_eventlog_sink.h diff --git a/inc/spdlog/sinks/wincolor_sink-inl.h b/inc/spdlog/include/spdlog/sinks/wincolor_sink-inl.h similarity index 100% rename from inc/spdlog/sinks/wincolor_sink-inl.h rename to inc/spdlog/include/spdlog/sinks/wincolor_sink-inl.h diff --git a/inc/spdlog/sinks/wincolor_sink.h b/inc/spdlog/include/spdlog/sinks/wincolor_sink.h similarity index 100% rename from inc/spdlog/sinks/wincolor_sink.h rename to inc/spdlog/include/spdlog/sinks/wincolor_sink.h diff --git a/inc/spdlog/spdlog-inl.h b/inc/spdlog/include/spdlog/spdlog-inl.h similarity index 100% rename from inc/spdlog/spdlog-inl.h rename to inc/spdlog/include/spdlog/spdlog-inl.h diff --git a/inc/spdlog/spdlog.h b/inc/spdlog/include/spdlog/spdlog.h similarity index 100% rename from inc/spdlog/spdlog.h rename to inc/spdlog/include/spdlog/spdlog.h diff --git a/inc/spdlog/stopwatch.h b/inc/spdlog/include/spdlog/stopwatch.h similarity index 100% rename from inc/spdlog/stopwatch.h rename to inc/spdlog/include/spdlog/stopwatch.h diff --git a/inc/spdlog/tweakme.h b/inc/spdlog/include/spdlog/tweakme.h similarity index 100% rename from inc/spdlog/tweakme.h rename to inc/spdlog/include/spdlog/tweakme.h diff --git a/inc/spdlog/version.h b/inc/spdlog/include/spdlog/version.h similarity index 100% rename from inc/spdlog/version.h rename to inc/spdlog/include/spdlog/version.h diff --git a/inc/spdlog/logos/jetbrains-variant-4.svg b/inc/spdlog/logos/jetbrains-variant-4.svg new file mode 100644 index 0000000..e02b559 --- /dev/null +++ b/inc/spdlog/logos/jetbrains-variant-4.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/inc/spdlog/logos/spdlog.png b/inc/spdlog/logos/spdlog.png new file mode 100644 index 0000000..23014c8 Binary files /dev/null and b/inc/spdlog/logos/spdlog.png differ diff --git a/inc/spdlog/scripts/ci_setup_clang.sh b/inc/spdlog/scripts/ci_setup_clang.sh new file mode 100644 index 0000000..140f9f9 --- /dev/null +++ b/inc/spdlog/scripts/ci_setup_clang.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -ex + +VERSION=$1 + +apt-get update +apt-get install -y libc++-${VERSION}-dev libc++abi-${VERSION}-dev + +if [[ "${VERSION}" -ge 12 ]]; then + apt-get install -y --no-install-recommends libunwind-${VERSION}-dev +fi diff --git a/inc/spdlog/scripts/extract_version.py b/inc/spdlog/scripts/extract_version.py new file mode 100644 index 0000000..79b5728 --- /dev/null +++ b/inc/spdlog/scripts/extract_version.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python3 + +import os +import re + +base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) +config_h = os.path.join(base_path, 'include', 'spdlog', 'version.h') +data = {'MAJOR': 0, 'MINOR': 0, 'PATCH': 0} +reg = re.compile(r'^\s*#define\s+SPDLOG_VER_([A-Z]+)\s+([0-9]+).*$') + +with open(config_h, 'r') as fp: + for l in fp: + m = reg.match(l) + if m: + data[m.group(1)] = int(m.group(2)) + +print(f"{data['MAJOR']}.{data['MINOR']}.{data['PATCH']}") diff --git a/inc/spdlog/scripts/format.sh b/inc/spdlog/scripts/format.sh new file mode 100644 index 0000000..8eb01fb --- /dev/null +++ b/inc/spdlog/scripts/format.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +cd "$(dirname "$0")"/.. +pwd +find_sources="find include src tests example bench -not ( -path include/spdlog/fmt/bundled -prune ) -type f -name *\.h -o -name *\.cpp" +echo -n "Running dos2unix " +$find_sources | xargs -I {} sh -c "dos2unix '{}' 2>/dev/null; echo -n '.'" +echo +echo -n "Running clang-format " + +$find_sources | xargs -I {} sh -c "clang-format -i {}; echo -n '.'" + +echo +echo -n "Running cmake-format " +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 '.'" +echo + + + diff --git a/inc/spdlog/src/async.cpp b/inc/spdlog/src/async.cpp new file mode 100644 index 0000000..026185a --- /dev/null +++ b/inc/spdlog/src/async.cpp @@ -0,0 +1,11 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#ifndef SPDLOG_COMPILED_LIB + #error Please define SPDLOG_COMPILED_LIB to compile this file. +#endif + +#include +#include +#include +#include diff --git a/inc/spdlog/src/bundled_fmtlib_format.cpp b/inc/spdlog/src/bundled_fmtlib_format.cpp new file mode 100644 index 0000000..ef32468 --- /dev/null +++ b/inc/spdlog/src/bundled_fmtlib_format.cpp @@ -0,0 +1,49 @@ +// Slightly modified version of fmt lib's format.cc (version 1.9.1) source file. +// Copyright (c) 2012 - 2016, Victor Zverovich +// All rights reserved. + +#ifndef SPDLOG_COMPILED_LIB + #error Please define SPDLOG_COMPILED_LIB to compile this file. +#endif + +#if !defined(SPDLOG_FMT_EXTERNAL) && !defined(SPDLOG_USE_STD_FORMAT) + + #include + +FMT_BEGIN_NAMESPACE +namespace detail { + +template FMT_API auto dragonbox::to_decimal(float x) noexcept -> dragonbox::decimal_fp; +template FMT_API auto dragonbox::to_decimal(double x) noexcept -> dragonbox::decimal_fp; + + #ifndef FMT_STATIC_THOUSANDS_SEPARATOR +template FMT_API locale_ref::locale_ref(const std::locale &loc); +template FMT_API auto locale_ref::get() const -> std::locale; + #endif + +// Explicit instantiations for char. + +template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; +template FMT_API auto decimal_point_impl(locale_ref) -> char; + +template FMT_API void buffer::append(const char *, const char *); + +// DEPRECATED! +// There is no correspondent extern template in format.h because of +// incompatibility between clang and gcc (#2377). +template FMT_API void vformat_to(buffer &, + string_view, + basic_format_args, + locale_ref); + +// Explicit instantiations for wchar_t. + +template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; +template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; + +template FMT_API void buffer::append(const wchar_t *, const wchar_t *); + +} // namespace detail +FMT_END_NAMESPACE + +#endif // !SPDLOG_FMT_EXTERNAL diff --git a/inc/spdlog/src/cfg.cpp b/inc/spdlog/src/cfg.cpp new file mode 100644 index 0000000..ebdea16 --- /dev/null +++ b/inc/spdlog/src/cfg.cpp @@ -0,0 +1,8 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#ifndef SPDLOG_COMPILED_LIB + #error Please define SPDLOG_COMPILED_LIB to compile this file. +#endif + +#include diff --git a/inc/spdlog/src/color_sinks.cpp b/inc/spdlog/src/color_sinks.cpp new file mode 100644 index 0000000..c44db19 --- /dev/null +++ b/inc/spdlog/src/color_sinks.cpp @@ -0,0 +1,55 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#ifndef SPDLOG_COMPILED_LIB + #error Please define SPDLOG_COMPILED_LIB to compile this file. +#endif + +#include + +#include +#include +// +// color sinks +// +#ifdef _WIN32 + #include +template class SPDLOG_API spdlog::sinks::wincolor_sink; +template class SPDLOG_API spdlog::sinks::wincolor_sink; +template class SPDLOG_API spdlog::sinks::wincolor_stdout_sink; +template class SPDLOG_API spdlog::sinks::wincolor_stdout_sink; +template class SPDLOG_API spdlog::sinks::wincolor_stderr_sink; +template class SPDLOG_API spdlog::sinks::wincolor_stderr_sink; +#else + #include "spdlog/sinks/ansicolor_sink-inl.h" +template class SPDLOG_API spdlog::sinks::ansicolor_sink; +template class SPDLOG_API spdlog::sinks::ansicolor_sink; +template class SPDLOG_API spdlog::sinks::ansicolor_stdout_sink; +template class SPDLOG_API spdlog::sinks::ansicolor_stdout_sink; +template class SPDLOG_API spdlog::sinks::ansicolor_stderr_sink; +template class SPDLOG_API spdlog::sinks::ansicolor_stderr_sink; +#endif + +// factory methods for color loggers +#include "spdlog/sinks/stdout_color_sinks-inl.h" +template SPDLOG_API std::shared_ptr +spdlog::stdout_color_mt(const std::string &logger_name, + color_mode mode); +template SPDLOG_API std::shared_ptr +spdlog::stdout_color_st(const std::string &logger_name, + color_mode mode); +template SPDLOG_API std::shared_ptr +spdlog::stderr_color_mt(const std::string &logger_name, + color_mode mode); +template SPDLOG_API std::shared_ptr +spdlog::stderr_color_st(const std::string &logger_name, + color_mode mode); + +template SPDLOG_API std::shared_ptr spdlog::stdout_color_mt( + const std::string &logger_name, color_mode mode); +template SPDLOG_API std::shared_ptr spdlog::stdout_color_st( + const std::string &logger_name, color_mode mode); +template SPDLOG_API std::shared_ptr spdlog::stderr_color_mt( + const std::string &logger_name, color_mode mode); +template SPDLOG_API std::shared_ptr spdlog::stderr_color_st( + const std::string &logger_name, color_mode mode); diff --git a/inc/spdlog/src/file_sinks.cpp b/inc/spdlog/src/file_sinks.cpp new file mode 100644 index 0000000..04cb6c1 --- /dev/null +++ b/inc/spdlog/src/file_sinks.cpp @@ -0,0 +1,20 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#ifndef SPDLOG_COMPILED_LIB + #error Please define SPDLOG_COMPILED_LIB to compile this file. +#endif + +#include +#include +#include +#include + +#include + +template class SPDLOG_API spdlog::sinks::basic_file_sink; +template class SPDLOG_API spdlog::sinks::basic_file_sink; + +#include +template class SPDLOG_API spdlog::sinks::rotating_file_sink; +template class SPDLOG_API spdlog::sinks::rotating_file_sink; diff --git a/inc/spdlog/src/spdlog.cpp b/inc/spdlog/src/spdlog.cpp new file mode 100644 index 0000000..9f8390b --- /dev/null +++ b/inc/spdlog/src/spdlog.cpp @@ -0,0 +1,28 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#ifndef SPDLOG_COMPILED_LIB + #error Please define SPDLOG_COMPILED_LIB to compile this file. +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +// template instantiate logger constructor with sinks init list +template SPDLOG_API spdlog::logger::logger(std::string name, + sinks_init_list::iterator begin, + sinks_init_list::iterator end); +template class SPDLOG_API spdlog::sinks::base_sink; +template class SPDLOG_API spdlog::sinks::base_sink; diff --git a/inc/spdlog/src/stdout_sinks.cpp b/inc/spdlog/src/stdout_sinks.cpp new file mode 100644 index 0000000..bf4cfae --- /dev/null +++ b/inc/spdlog/src/stdout_sinks.cpp @@ -0,0 +1,37 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#ifndef SPDLOG_COMPILED_LIB + #error Please define SPDLOG_COMPILED_LIB to compile this file. +#endif + +#include + +#include +#include +#include + +template class SPDLOG_API spdlog::sinks::stdout_sink_base; +template class SPDLOG_API spdlog::sinks::stdout_sink_base; +template class SPDLOG_API spdlog::sinks::stdout_sink; +template class SPDLOG_API spdlog::sinks::stdout_sink; +template class SPDLOG_API spdlog::sinks::stderr_sink; +template class SPDLOG_API spdlog::sinks::stderr_sink; + +template SPDLOG_API std::shared_ptr +spdlog::stdout_logger_mt(const std::string &logger_name); +template SPDLOG_API std::shared_ptr +spdlog::stdout_logger_st(const std::string &logger_name); +template SPDLOG_API std::shared_ptr +spdlog::stderr_logger_mt(const std::string &logger_name); +template SPDLOG_API std::shared_ptr +spdlog::stderr_logger_st(const std::string &logger_name); + +template SPDLOG_API std::shared_ptr spdlog::stdout_logger_mt( + const std::string &logger_name); +template SPDLOG_API std::shared_ptr spdlog::stdout_logger_st( + const std::string &logger_name); +template SPDLOG_API std::shared_ptr spdlog::stderr_logger_mt( + const std::string &logger_name); +template SPDLOG_API std::shared_ptr spdlog::stderr_logger_st( + const std::string &logger_name); diff --git a/inc/spdlog/tests/CMakeLists.txt b/inc/spdlog/tests/CMakeLists.txt new file mode 100644 index 0000000..63cced4 --- /dev/null +++ b/inc/spdlog/tests/CMakeLists.txt @@ -0,0 +1,87 @@ +cmake_minimum_required(VERSION 3.11) +project(spdlog_utests CXX) + +if(NOT TARGET spdlog) + # Stand-alone build + find_package(spdlog REQUIRED) +endif() + +include(../cmake/utils.cmake) + +find_package(PkgConfig) +if(PkgConfig_FOUND) + pkg_check_modules(systemd libsystemd) +endif() + +find_package(Catch2 3 QUIET) +if(Catch2_FOUND) + message(STATUS "Packaged version of Catch will be used.") +else() + message(STATUS "Bundled version of Catch will be downloaded and used.") + include(FetchContent) + FetchContent_Declare(Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git GIT_TAG v3.3.2) + FetchContent_MakeAvailable(Catch2) +endif() + +set(SPDLOG_UTESTS_SOURCES + test_file_helper.cpp + test_file_logging.cpp + test_daily_logger.cpp + test_misc.cpp + test_eventlog.cpp + test_pattern_formatter.cpp + test_async.cpp + test_registry.cpp + test_macros.cpp + utils.cpp + main.cpp + test_mpmc_q.cpp + test_dup_filter.cpp + test_fmt_helper.cpp + test_stdout_api.cpp + test_backtrace.cpp + test_create_dir.cpp + test_custom_callbacks.cpp + test_cfg.cpp + test_time_point.cpp + test_stopwatch.cpp + test_circular_q.cpp) + +if(NOT SPDLOG_NO_EXCEPTIONS) + list(APPEND SPDLOG_UTESTS_SOURCES test_errors.cpp) +endif() + +if(systemd_FOUND) + list(APPEND SPDLOG_UTESTS_SOURCES test_systemd.cpp) +endif() + +if(NOT SPDLOG_USE_STD_FORMAT) + list(APPEND SPDLOG_UTESTS_SOURCES test_bin_to_hex.cpp) +endif() + +enable_testing() + +function(spdlog_prepare_test test_target spdlog_lib) + add_executable(${test_target} ${SPDLOG_UTESTS_SOURCES}) + spdlog_enable_warnings(${test_target}) + target_link_libraries(${test_target} PRIVATE ${spdlog_lib}) + if(systemd_FOUND) + target_link_libraries(${test_target} PRIVATE ${systemd_LIBRARIES}) + endif() + target_link_libraries(${test_target} PRIVATE Catch2::Catch2WithMain) + if(SPDLOG_SANITIZE_ADDRESS) + spdlog_enable_sanitizer(${test_target}) + endif() + add_test(NAME ${test_target} COMMAND ${test_target}) + set_tests_properties(${test_target} PROPERTIES RUN_SERIAL ON) +endfunction() + +# The compiled library tests +if(SPDLOG_BUILD_TESTS OR SPDLOG_BUILD_ALL) + spdlog_prepare_test(spdlog-utests spdlog::spdlog) +endif() + +# The header-only library version tests +if(SPDLOG_BUILD_TESTS_HO OR SPDLOG_BUILD_ALL) + spdlog_prepare_test(spdlog-utests-ho spdlog::spdlog_header_only) +endif() diff --git a/inc/spdlog/tests/includes.h b/inc/spdlog/tests/includes.h new file mode 100644 index 0000000..8514d64 --- /dev/null +++ b/inc/spdlog/tests/includes.h @@ -0,0 +1,36 @@ +#pragma once + +#if defined(__GNUC__) && __GNUC__ == 12 + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // Workaround for GCC 12 +#endif +#include +#if defined(__GNUC__) && __GNUC__ == 12 + #pragma GCC diagnostic pop +#endif + +#include "utils.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG + +#include "spdlog/spdlog.h" +#include "spdlog/async.h" +#include "spdlog/details/fmt_helper.h" +#include "spdlog/sinks/basic_file_sink.h" +#include "spdlog/sinks/daily_file_sink.h" +#include "spdlog/sinks/null_sink.h" +#include "spdlog/sinks/ostream_sink.h" +#include "spdlog/sinks/rotating_file_sink.h" +#include "spdlog/sinks/stdout_color_sinks.h" +#include "spdlog/sinks/msvc_sink.h" +#include "spdlog/pattern_formatter.h" diff --git a/inc/spdlog/tests/main.cpp b/inc/spdlog/tests/main.cpp new file mode 100644 index 0000000..a4a4ff1 --- /dev/null +++ b/inc/spdlog/tests/main.cpp @@ -0,0 +1,10 @@ +#if defined(__GNUC__) && __GNUC__ == 12 + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" // Workaround for GCC 12 +#endif + +#include + +#if defined(__GNUC__) && __GNUC__ == 12 + #pragma GCC diagnostic pop +#endif diff --git a/inc/spdlog/tests/test_async.cpp b/inc/spdlog/tests/test_async.cpp new file mode 100644 index 0000000..76fdd7c --- /dev/null +++ b/inc/spdlog/tests/test_async.cpp @@ -0,0 +1,200 @@ +#include "includes.h" +#include "spdlog/async.h" +#include "spdlog/sinks/basic_file_sink.h" +#include "test_sink.h" + +#define TEST_FILENAME "test_logs/async_test.log" + +TEST_CASE("basic async test ", "[async]") { + auto test_sink = std::make_shared(); + size_t overrun_counter = 0; + size_t queue_size = 128; + size_t messages = 256; + { + auto tp = std::make_shared(queue_size, 1); + auto logger = std::make_shared("as", test_sink, tp, + spdlog::async_overflow_policy::block); + for (size_t i = 0; i < messages; i++) { + logger->info("Hello message #{}", i); + } + logger->flush(); + overrun_counter = tp->overrun_counter(); + } + REQUIRE(test_sink->msg_counter() == messages); + REQUIRE(test_sink->flush_counter() == 1); + REQUIRE(overrun_counter == 0); +} + +TEST_CASE("discard policy ", "[async]") { + auto test_sink = std::make_shared(); + test_sink->set_delay(std::chrono::milliseconds(1)); + size_t queue_size = 4; + size_t messages = 1024; + + auto tp = std::make_shared(queue_size, 1); + auto logger = std::make_shared( + "as", test_sink, tp, spdlog::async_overflow_policy::overrun_oldest); + for (size_t i = 0; i < messages; i++) { + logger->info("Hello message"); + } + REQUIRE(test_sink->msg_counter() < messages); + REQUIRE(tp->overrun_counter() > 0); +} + +TEST_CASE("discard policy discard_new ", "[async]") { + auto test_sink = std::make_shared(); + test_sink->set_delay(std::chrono::milliseconds(1)); + size_t queue_size = 4; + size_t messages = 1024; + + auto tp = std::make_shared(queue_size, 1); + auto logger = std::make_shared( + "as", test_sink, tp, spdlog::async_overflow_policy::discard_new); + for (size_t i = 0; i < messages; i++) { + logger->info("Hello message"); + } + REQUIRE(test_sink->msg_counter() < messages); + REQUIRE(tp->discard_counter() > 0); +} + +TEST_CASE("discard policy using factory ", "[async]") { + size_t queue_size = 4; + size_t messages = 1024; + spdlog::init_thread_pool(queue_size, 1); + + auto logger = spdlog::create_async_nb("as2"); + auto test_sink = std::static_pointer_cast(logger->sinks()[0]); + test_sink->set_delay(std::chrono::milliseconds(3)); + + for (size_t i = 0; i < messages; i++) { + logger->info("Hello message"); + } + + REQUIRE(test_sink->msg_counter() < messages); + spdlog::drop_all(); +} + +TEST_CASE("flush", "[async]") { + auto test_sink = std::make_shared(); + size_t queue_size = 256; + size_t messages = 256; + { + auto tp = std::make_shared(queue_size, 1); + auto logger = std::make_shared("as", test_sink, tp, + spdlog::async_overflow_policy::block); + for (size_t i = 0; i < messages; i++) { + logger->info("Hello message #{}", i); + } + + logger->flush(); + } + // std::this_thread::sleep_for(std::chrono::milliseconds(250)); + REQUIRE(test_sink->msg_counter() == messages); + REQUIRE(test_sink->flush_counter() == 1); +} + +TEST_CASE("async periodic flush", "[async]") { + auto logger = spdlog::create_async("as"); + auto test_sink = std::static_pointer_cast(logger->sinks()[0]); + + spdlog::flush_every(std::chrono::seconds(1)); + std::this_thread::sleep_for(std::chrono::milliseconds(1700)); + REQUIRE(test_sink->flush_counter() == 1); + spdlog::flush_every(std::chrono::seconds(0)); + spdlog::drop_all(); +} + +TEST_CASE("tp->wait_empty() ", "[async]") { + auto test_sink = std::make_shared(); + test_sink->set_delay(std::chrono::milliseconds(5)); + size_t messages = 100; + + auto tp = std::make_shared(messages, 2); + auto logger = std::make_shared("as", test_sink, tp, + spdlog::async_overflow_policy::block); + for (size_t i = 0; i < messages; i++) { + logger->info("Hello message #{}", i); + } + logger->flush(); + tp.reset(); + + REQUIRE(test_sink->msg_counter() == messages); + REQUIRE(test_sink->flush_counter() == 1); +} + +TEST_CASE("multi threads", "[async]") { + auto test_sink = std::make_shared(); + size_t queue_size = 128; + size_t messages = 256; + size_t n_threads = 10; + { + auto tp = std::make_shared(queue_size, 1); + auto logger = std::make_shared("as", test_sink, tp, + spdlog::async_overflow_policy::block); + + std::vector threads; + for (size_t i = 0; i < n_threads; i++) { + threads.emplace_back([logger, messages] { + for (size_t j = 0; j < messages; j++) { + logger->info("Hello message #{}", j); + } + }); + logger->flush(); + } + + for (auto &t : threads) { + t.join(); + } + } + + REQUIRE(test_sink->msg_counter() == messages * n_threads); + REQUIRE(test_sink->flush_counter() == n_threads); +} + +TEST_CASE("to_file", "[async]") { + prepare_logdir(); + size_t messages = 1024; + size_t tp_threads = 1; + spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME); + { + auto file_sink = std::make_shared(filename, true); + auto tp = std::make_shared(messages, tp_threads); + auto logger = + std::make_shared("as", std::move(file_sink), std::move(tp)); + + for (size_t j = 0; j < messages; j++) { + logger->info("Hello message #{}", j); + } + } + + require_message_count(TEST_FILENAME, messages); + auto contents = file_contents(TEST_FILENAME); + using spdlog::details::os::default_eol; + REQUIRE(ends_with(contents, spdlog::fmt_lib::format("Hello message #1023{}", default_eol))); +} + +TEST_CASE("to_file multi-workers", "[async]") { + prepare_logdir(); + size_t messages = 1024 * 10; + size_t tp_threads = 10; + spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME); + { + auto file_sink = std::make_shared(filename, true); + auto tp = std::make_shared(messages, tp_threads); + auto logger = + std::make_shared("as", std::move(file_sink), std::move(tp)); + + for (size_t j = 0; j < messages; j++) { + logger->info("Hello message #{}", j); + } + } + require_message_count(TEST_FILENAME, messages); +} + +TEST_CASE("bad_tp", "[async]") { + auto test_sink = std::make_shared(); + std::shared_ptr const empty_tp; + auto logger = std::make_shared("as", test_sink, empty_tp); + logger->info("Please throw an exception"); + REQUIRE(test_sink->msg_counter() == 0); +} diff --git a/inc/spdlog/tests/test_backtrace.cpp b/inc/spdlog/tests/test_backtrace.cpp new file mode 100644 index 0000000..4d78c0c --- /dev/null +++ b/inc/spdlog/tests/test_backtrace.cpp @@ -0,0 +1,73 @@ +#include "includes.h" +#include "test_sink.h" +#include "spdlog/async.h" + +TEST_CASE("bactrace1", "[bactrace]") { + using spdlog::sinks::test_sink_st; + auto test_sink = std::make_shared(); + size_t backtrace_size = 5; + + spdlog::logger logger("test-backtrace", test_sink); + logger.set_pattern("%v"); + logger.enable_backtrace(backtrace_size); + + logger.info("info message"); + for (int i = 0; i < 100; i++) logger.debug("debug message {}", i); + + REQUIRE(test_sink->lines().size() == 1); + REQUIRE(test_sink->lines()[0] == "info message"); + + logger.dump_backtrace(); + REQUIRE(test_sink->lines().size() == backtrace_size + 3); + REQUIRE(test_sink->lines()[1] == "****************** Backtrace Start ******************"); + REQUIRE(test_sink->lines()[2] == "debug message 95"); + REQUIRE(test_sink->lines()[3] == "debug message 96"); + REQUIRE(test_sink->lines()[4] == "debug message 97"); + REQUIRE(test_sink->lines()[5] == "debug message 98"); + REQUIRE(test_sink->lines()[6] == "debug message 99"); + REQUIRE(test_sink->lines()[7] == "****************** Backtrace End ********************"); +} + +TEST_CASE("bactrace-empty", "[bactrace]") { + using spdlog::sinks::test_sink_st; + auto test_sink = std::make_shared(); + size_t backtrace_size = 5; + + spdlog::logger logger("test-backtrace", test_sink); + logger.set_pattern("%v"); + logger.enable_backtrace(backtrace_size); + logger.dump_backtrace(); + REQUIRE(test_sink->lines().size() == 0); +} + +TEST_CASE("bactrace-async", "[bactrace]") { + using spdlog::sinks::test_sink_mt; + auto test_sink = std::make_shared(); + using spdlog::details::os::sleep_for_millis; + + size_t backtrace_size = 5; + + spdlog::init_thread_pool(120, 1); + auto logger = std::make_shared("test-bactrace-async", test_sink, + spdlog::thread_pool()); + logger->set_pattern("%v"); + logger->enable_backtrace(backtrace_size); + + logger->info("info message"); + for (int i = 0; i < 100; i++) logger->debug("debug message {}", i); + + sleep_for_millis(100); + REQUIRE(test_sink->lines().size() == 1); + REQUIRE(test_sink->lines()[0] == "info message"); + + logger->dump_backtrace(); + sleep_for_millis(100); // give time for the async dump to complete + REQUIRE(test_sink->lines().size() == backtrace_size + 3); + REQUIRE(test_sink->lines()[1] == "****************** Backtrace Start ******************"); + REQUIRE(test_sink->lines()[2] == "debug message 95"); + REQUIRE(test_sink->lines()[3] == "debug message 96"); + REQUIRE(test_sink->lines()[4] == "debug message 97"); + REQUIRE(test_sink->lines()[5] == "debug message 98"); + REQUIRE(test_sink->lines()[6] == "debug message 99"); + REQUIRE(test_sink->lines()[7] == "****************** Backtrace End ********************"); +} diff --git a/inc/spdlog/tests/test_bin_to_hex.cpp b/inc/spdlog/tests/test_bin_to_hex.cpp new file mode 100644 index 0000000..45fc9fa --- /dev/null +++ b/inc/spdlog/tests/test_bin_to_hex.cpp @@ -0,0 +1,97 @@ +#include "includes.h" +#include "test_sink.h" +#include "spdlog/fmt/bin_to_hex.h" + +TEST_CASE("to_hex", "[to_hex]") { + std::ostringstream oss; + auto oss_sink = std::make_shared(oss); + spdlog::logger oss_logger("oss", oss_sink); + + std::vector v{9, 0xa, 0xb, 0xc, 0xff, 0xff}; + oss_logger.info("{}", spdlog::to_hex(v)); + + auto output = oss.str(); + REQUIRE(ends_with(output, + "0000: 09 0a 0b 0c ff ff" + std::string(spdlog::details::os::default_eol))); +} + +TEST_CASE("to_hex_upper", "[to_hex]") { + std::ostringstream oss; + auto oss_sink = std::make_shared(oss); + spdlog::logger oss_logger("oss", oss_sink); + + std::vector v{9, 0xa, 0xb, 0xc, 0xff, 0xff}; + oss_logger.info("{:X}", spdlog::to_hex(v)); + + auto output = oss.str(); + REQUIRE(ends_with(output, + "0000: 09 0A 0B 0C FF FF" + std::string(spdlog::details::os::default_eol))); +} + +TEST_CASE("to_hex_no_delimiter", "[to_hex]") { + std::ostringstream oss; + auto oss_sink = std::make_shared(oss); + spdlog::logger oss_logger("oss", oss_sink); + + std::vector v{9, 0xa, 0xb, 0xc, 0xff, 0xff}; + oss_logger.info("{:sX}", spdlog::to_hex(v)); + + auto output = oss.str(); + REQUIRE( + ends_with(output, "0000: 090A0B0CFFFF" + std::string(spdlog::details::os::default_eol))); +} + +TEST_CASE("to_hex_show_ascii", "[to_hex]") { + std::ostringstream oss; + auto oss_sink = std::make_shared(oss); + spdlog::logger oss_logger("oss", oss_sink); + + std::vector v{9, 0xa, 0xb, 0x41, 0xc, 0x4b, 0xff, 0xff}; + oss_logger.info("{:Xsa}", spdlog::to_hex(v, 8)); + + REQUIRE(ends_with(oss.str(), "0000: 090A0B410C4BFFFF ...A.K.." + + std::string(spdlog::details::os::default_eol))); +} + +TEST_CASE("to_hex_different_size_per_line", "[to_hex]") { + std::ostringstream oss; + auto oss_sink = std::make_shared(oss); + spdlog::logger oss_logger("oss", oss_sink); + + std::vector v{9, 0xa, 0xb, 0x41, 0xc, 0x4b, 0xff, 0xff}; + + oss_logger.info("{:Xsa}", spdlog::to_hex(v, 10)); + REQUIRE(ends_with(oss.str(), "0000: 090A0B410C4BFFFF ...A.K.." + + std::string(spdlog::details::os::default_eol))); + + oss_logger.info("{:Xs}", spdlog::to_hex(v, 10)); + REQUIRE(ends_with(oss.str(), + "0000: 090A0B410C4BFFFF" + std::string(spdlog::details::os::default_eol))); + + oss_logger.info("{:Xsa}", spdlog::to_hex(v, 6)); + REQUIRE(ends_with( + oss.str(), "0000: 090A0B410C4B ...A.K" + std::string(spdlog::details::os::default_eol) + + "0006: FFFF .." + std::string(spdlog::details::os::default_eol))); + + oss_logger.info("{:Xs}", spdlog::to_hex(v, 6)); + REQUIRE(ends_with(oss.str(), "0000: 090A0B410C4B" + + std::string(spdlog::details::os::default_eol) + "0006: FFFF" + + std::string(spdlog::details::os::default_eol))); +} + +TEST_CASE("to_hex_no_ascii", "[to_hex]") { + std::ostringstream oss; + auto oss_sink = std::make_shared(oss); + spdlog::logger oss_logger("oss", oss_sink); + + std::vector v{9, 0xa, 0xb, 0x41, 0xc, 0x4b, 0xff, 0xff}; + oss_logger.info("{:Xs}", spdlog::to_hex(v, 8)); + + REQUIRE(ends_with(oss.str(), + "0000: 090A0B410C4BFFFF" + std::string(spdlog::details::os::default_eol))); + + oss_logger.info("{:Xsna}", spdlog::to_hex(v, 8)); + + REQUIRE( + ends_with(oss.str(), "090A0B410C4BFFFF" + std::string(spdlog::details::os::default_eol))); +} diff --git a/inc/spdlog/tests/test_cfg.cpp b/inc/spdlog/tests/test_cfg.cpp new file mode 100644 index 0000000..684d870 --- /dev/null +++ b/inc/spdlog/tests/test_cfg.cpp @@ -0,0 +1,169 @@ + +#include "includes.h" +#include "test_sink.h" + +#include +#include + +using spdlog::cfg::load_argv_levels; +using spdlog::cfg::load_env_levels; +using spdlog::sinks::test_sink_st; + +TEST_CASE("env", "[cfg]") { + spdlog::drop("l1"); + auto l1 = spdlog::create("l1"); +#ifdef CATCH_PLATFORM_WINDOWS + _putenv_s("SPDLOG_LEVEL", "l1=warn"); +#else + setenv("SPDLOG_LEVEL", "l1=warn", 1); +#endif + load_env_levels(); + REQUIRE(l1->level() == spdlog::level::warn); + spdlog::set_default_logger(spdlog::create("cfg-default")); + REQUIRE(spdlog::default_logger()->level() == spdlog::level::info); +} + +TEST_CASE("argv1", "[cfg]") { + spdlog::drop("l1"); + const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=warn"}; + load_argv_levels(2, argv); + auto l1 = spdlog::create("l1"); + REQUIRE(l1->level() == spdlog::level::warn); + REQUIRE(spdlog::default_logger()->level() == spdlog::level::info); +} + +TEST_CASE("argv2", "[cfg]") { + spdlog::drop("l1"); + const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=warn,trace"}; + load_argv_levels(2, argv); + auto l1 = spdlog::create("l1"); + REQUIRE(l1->level() == spdlog::level::warn); + REQUIRE(spdlog::default_logger()->level() == spdlog::level::trace); +} + +TEST_CASE("argv3", "[cfg]") { + spdlog::set_level(spdlog::level::trace); + + spdlog::drop("l1"); + const char *argv[] = {"ignore", "SPDLOG_LEVEL=junk_name=warn"}; + load_argv_levels(2, argv); + auto l1 = spdlog::create("l1"); + REQUIRE(l1->level() == spdlog::level::trace); + REQUIRE(spdlog::default_logger()->level() == spdlog::level::trace); +} + +TEST_CASE("argv4", "[cfg]") { + spdlog::set_level(spdlog::level::info); + spdlog::drop("l1"); + const char *argv[] = {"ignore", "SPDLOG_LEVEL=junk"}; + load_argv_levels(2, argv); + auto l1 = spdlog::create("l1"); + REQUIRE(l1->level() == spdlog::level::info); +} + +TEST_CASE("argv5", "[cfg]") { + spdlog::set_level(spdlog::level::info); + spdlog::drop("l1"); + const char *argv[] = {"ignore", "ignore", "SPDLOG_LEVEL=l1=warn,trace"}; + load_argv_levels(3, argv); + auto l1 = spdlog::create("l1"); + REQUIRE(l1->level() == spdlog::level::warn); + REQUIRE(spdlog::default_logger()->level() == spdlog::level::trace); + spdlog::set_level(spdlog::level::info); +} + +TEST_CASE("argv6", "[cfg]") { + spdlog::set_level(spdlog::level::err); + const char *argv[] = {""}; + load_argv_levels(1, argv); + REQUIRE(spdlog::default_logger()->level() == spdlog::level::err); + spdlog::set_level(spdlog::level::info); +} + +TEST_CASE("argv7", "[cfg]") { + spdlog::set_level(spdlog::level::err); + const char *argv[] = {""}; + load_argv_levels(0, argv); + REQUIRE(spdlog::default_logger()->level() == spdlog::level::err); + spdlog::set_level(spdlog::level::info); +} + +TEST_CASE("level-not-set-test1", "[cfg]") { + spdlog::drop("l1"); + const char *argv[] = {"ignore", ""}; + load_argv_levels(2, argv); + auto l1 = spdlog::create("l1"); + l1->set_level(spdlog::level::trace); + REQUIRE(l1->level() == spdlog::level::trace); + REQUIRE(spdlog::default_logger()->level() == spdlog::level::info); +} + +TEST_CASE("level-not-set-test2", "[cfg]") { + spdlog::drop("l1"); + spdlog::drop("l2"); + const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=trace"}; + + auto l1 = spdlog::create("l1"); + l1->set_level(spdlog::level::warn); + auto l2 = spdlog::create("l2"); + l2->set_level(spdlog::level::warn); + + load_argv_levels(2, argv); + + REQUIRE(l1->level() == spdlog::level::trace); + REQUIRE(l2->level() == spdlog::level::warn); + REQUIRE(spdlog::default_logger()->level() == spdlog::level::info); +} + +TEST_CASE("level-not-set-test3", "[cfg]") { + spdlog::drop("l1"); + spdlog::drop("l2"); + const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=trace"}; + + load_argv_levels(2, argv); + + auto l1 = spdlog::create("l1"); + auto l2 = spdlog::create("l2"); + + REQUIRE(l1->level() == spdlog::level::trace); + REQUIRE(l2->level() == spdlog::level::info); + REQUIRE(spdlog::default_logger()->level() == spdlog::level::info); +} + +TEST_CASE("level-not-set-test4", "[cfg]") { + spdlog::drop("l1"); + spdlog::drop("l2"); + const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=trace,warn"}; + + load_argv_levels(2, argv); + + auto l1 = spdlog::create("l1"); + auto l2 = spdlog::create("l2"); + + REQUIRE(l1->level() == spdlog::level::trace); + REQUIRE(l2->level() == spdlog::level::warn); + REQUIRE(spdlog::default_logger()->level() == spdlog::level::warn); +} + +TEST_CASE("level-not-set-test5", "[cfg]") { + spdlog::drop("l1"); + spdlog::drop("l2"); + const char *argv[] = {"ignore", "SPDLOG_LEVEL=l1=junk,warn"}; + + load_argv_levels(2, argv); + + auto l1 = spdlog::create("l1"); + auto l2 = spdlog::create("l2"); + + REQUIRE(l1->level() == spdlog::level::warn); + REQUIRE(l2->level() == spdlog::level::warn); + REQUIRE(spdlog::default_logger()->level() == spdlog::level::warn); +} + +TEST_CASE("restore-to-default", "[cfg]") { + spdlog::drop("l1"); + spdlog::drop("l2"); + const char *argv[] = {"ignore", "SPDLOG_LEVEL=info"}; + load_argv_levels(2, argv); + REQUIRE(spdlog::default_logger()->level() == spdlog::level::info); +} diff --git a/inc/spdlog/tests/test_circular_q.cpp b/inc/spdlog/tests/test_circular_q.cpp new file mode 100644 index 0000000..c8b02d3 --- /dev/null +++ b/inc/spdlog/tests/test_circular_q.cpp @@ -0,0 +1,50 @@ +#include "includes.h" +#include "spdlog/details/circular_q.h" + +using q_type = spdlog::details::circular_q; +TEST_CASE("test_size", "[circular_q]") { + const size_t q_size = 4; + q_type q(q_size); + REQUIRE(q.size() == 0); + REQUIRE(q.empty() == true); + for (size_t i = 0; i < q_size; i++) { + q.push_back(std::move(i)); + } + REQUIRE(q.size() == q_size); + q.push_back(999); + REQUIRE(q.size() == q_size); +} + +TEST_CASE("test_rolling", "[circular_q]") { + const size_t q_size = 4; + q_type q(q_size); + + for (size_t i = 0; i < q_size + 2; i++) { + q.push_back(std::move(i)); + } + + REQUIRE(q.size() == q_size); + + REQUIRE(q.front() == 2); + q.pop_front(); + + REQUIRE(q.front() == 3); + q.pop_front(); + + REQUIRE(q.front() == 4); + q.pop_front(); + + REQUIRE(q.front() == 5); + q.pop_front(); + + REQUIRE(q.empty()); + + q.push_back(6); + REQUIRE(q.front() == 6); +} + +TEST_CASE("test_empty", "[circular_q]") { + q_type q(0); + q.push_back(1); + REQUIRE(q.empty()); +} \ No newline at end of file diff --git a/inc/spdlog/tests/test_create_dir.cpp b/inc/spdlog/tests/test_create_dir.cpp new file mode 100644 index 0000000..f88825b --- /dev/null +++ b/inc/spdlog/tests/test_create_dir.cpp @@ -0,0 +1,83 @@ +/* + * This content is released under the MIT License as specified in + * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE + */ +#include "includes.h" + +using spdlog::details::os::create_dir; +using spdlog::details::os::path_exists; + +bool try_create_dir(const spdlog::filename_t &path, const spdlog::filename_t &normalized_path) { + auto rv = create_dir(path); + REQUIRE(rv == true); + return path_exists(normalized_path); +} + +TEST_CASE("create_dir", "[create_dir]") { + prepare_logdir(); + + REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs/dir1/dir1"), + SPDLOG_FILENAME_T("test_logs/dir1/dir1"))); + REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs/dir1/dir1"), + SPDLOG_FILENAME_T("test_logs/dir1/dir1"))); // test existing + REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs/dir1///dir2//"), + SPDLOG_FILENAME_T("test_logs/dir1/dir2"))); + REQUIRE(try_create_dir(SPDLOG_FILENAME_T("./test_logs/dir1/dir3"), + SPDLOG_FILENAME_T("test_logs/dir1/dir3"))); + REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs/../test_logs/dir1/dir4"), + SPDLOG_FILENAME_T("test_logs/dir1/dir4"))); + +#ifdef WIN32 + // test backslash folder separator + REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs\\dir1\\dir222"), + SPDLOG_FILENAME_T("test_logs\\dir1\\dir222"))); + REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs\\dir1\\dir223\\"), + SPDLOG_FILENAME_T("test_logs\\dir1\\dir223\\"))); + REQUIRE(try_create_dir(SPDLOG_FILENAME_T(".\\test_logs\\dir1\\dir2\\dir99\\..\\dir23"), + SPDLOG_FILENAME_T("test_logs\\dir1\\dir2\\dir23"))); + REQUIRE(try_create_dir(SPDLOG_FILENAME_T("test_logs\\..\\test_logs\\dir1\\dir5"), + SPDLOG_FILENAME_T("test_logs\\dir1\\dir5"))); +#endif +} + +TEST_CASE("create_invalid_dir", "[create_dir]") { + REQUIRE(create_dir(SPDLOG_FILENAME_T("")) == false); + REQUIRE(create_dir(spdlog::filename_t{}) == false); +#ifdef __linux__ + REQUIRE(create_dir("/proc/spdlog-utest") == false); +#endif +} + +TEST_CASE("dir_name", "[create_dir]") { + using spdlog::details::os::dir_name; + REQUIRE(dir_name(SPDLOG_FILENAME_T("")).empty()); + REQUIRE(dir_name(SPDLOG_FILENAME_T("dir")).empty()); + +#ifdef WIN32 + REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\)")) == SPDLOG_FILENAME_T("dir")); + REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\\\)")) == SPDLOG_FILENAME_T(R"(dir\\)")); + REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\file)")) == SPDLOG_FILENAME_T("dir")); + REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir/file)")) == SPDLOG_FILENAME_T("dir")); + REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\file.txt)")) == SPDLOG_FILENAME_T("dir")); + REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir/file)")) == SPDLOG_FILENAME_T("dir")); + REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(dir\file.txt\)")) == + SPDLOG_FILENAME_T(R"(dir\file.txt)")); + REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(\dir\file.txt)")) == SPDLOG_FILENAME_T(R"(\dir)")); + REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(\\dir\file.txt)")) == SPDLOG_FILENAME_T(R"(\\dir)")); + REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(..\file.txt)")) == SPDLOG_FILENAME_T("..")); + REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(.\file.txt)")) == SPDLOG_FILENAME_T(".")); + REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(c:\\a\b\c\d\file.txt)")) == + SPDLOG_FILENAME_T(R"(c:\\a\b\c\d)")); + REQUIRE(dir_name(SPDLOG_FILENAME_T(R"(c://a/b/c/d/file.txt)")) == + SPDLOG_FILENAME_T(R"(c://a/b/c/d)")); +#endif + REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/")) == SPDLOG_FILENAME_T("dir")); + REQUIRE(dir_name(SPDLOG_FILENAME_T("dir///")) == SPDLOG_FILENAME_T("dir//")); + REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/file")) == SPDLOG_FILENAME_T("dir")); + REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/file.txt")) == SPDLOG_FILENAME_T("dir")); + REQUIRE(dir_name(SPDLOG_FILENAME_T("dir/file.txt/")) == SPDLOG_FILENAME_T("dir/file.txt")); + REQUIRE(dir_name(SPDLOG_FILENAME_T("/dir/file.txt")) == SPDLOG_FILENAME_T("/dir")); + REQUIRE(dir_name(SPDLOG_FILENAME_T("//dir/file.txt")) == SPDLOG_FILENAME_T("//dir")); + REQUIRE(dir_name(SPDLOG_FILENAME_T("../file.txt")) == SPDLOG_FILENAME_T("..")); + REQUIRE(dir_name(SPDLOG_FILENAME_T("./file.txt")) == SPDLOG_FILENAME_T(".")); +} diff --git a/inc/spdlog/tests/test_custom_callbacks.cpp b/inc/spdlog/tests/test_custom_callbacks.cpp new file mode 100644 index 0000000..4b04857 --- /dev/null +++ b/inc/spdlog/tests/test_custom_callbacks.cpp @@ -0,0 +1,35 @@ +/* + * This content is released under the MIT License as specified in + * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE + */ +#include "includes.h" +#include "test_sink.h" +#include "spdlog/sinks/callback_sink.h" +#include "spdlog/async.h" +#include "spdlog/common.h" + +TEST_CASE("custom_callback_logger", "[custom_callback_logger]") { + std::vector lines; + spdlog::pattern_formatter formatter; + auto callback_logger = + std::make_shared([&](const spdlog::details::log_msg &msg) { + spdlog::memory_buf_t formatted; + formatter.format(msg, formatted); + auto eol_len = strlen(spdlog::details::os::default_eol); + lines.emplace_back(formatted.begin(), formatted.end() - eol_len); + }); + std::shared_ptr test_sink(new spdlog::sinks::test_sink_st); + + spdlog::logger logger("test-callback", {callback_logger, test_sink}); + + logger.info("test message 1"); + logger.info("test message 2"); + logger.info("test message 3"); + + std::vector ref_lines = test_sink->lines(); + + REQUIRE(lines[0] == ref_lines[0]); + REQUIRE(lines[1] == ref_lines[1]); + REQUIRE(lines[2] == ref_lines[2]); + spdlog::drop_all(); +} diff --git a/inc/spdlog/tests/test_daily_logger.cpp b/inc/spdlog/tests/test_daily_logger.cpp new file mode 100644 index 0000000..fe4783c --- /dev/null +++ b/inc/spdlog/tests/test_daily_logger.cpp @@ -0,0 +1,173 @@ +/* + * This content is released under the MIT License as specified in + * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE + */ +#include "includes.h" + +#ifdef SPDLOG_USE_STD_FORMAT +using filename_memory_buf_t = std::basic_string; +#else +using filename_memory_buf_t = fmt::basic_memory_buffer; +#endif + +#ifdef SPDLOG_WCHAR_FILENAMES +std::string filename_buf_to_utf8string(const filename_memory_buf_t &w) { + spdlog::memory_buf_t buf; + spdlog::details::os::wstr_to_utf8buf(spdlog::wstring_view_t(w.data(), w.size()), buf); + return SPDLOG_BUF_TO_STRING(buf); +} +#else +std::string filename_buf_to_utf8string(const filename_memory_buf_t &w) { + return SPDLOG_BUF_TO_STRING(w); +} +#endif + +TEST_CASE("daily_logger with dateonly calculator", "[daily_logger]") { + using sink_type = + spdlog::sinks::daily_file_sink; + + prepare_logdir(); + + // calculate filename (time based) + spdlog::filename_t basename = SPDLOG_FILENAME_T("test_logs/daily_dateonly"); + std::tm tm = spdlog::details::os::localtime(); + filename_memory_buf_t w; + spdlog::fmt_lib::format_to(std::back_inserter(w), SPDLOG_FILENAME_T("{}_{:04d}-{:02d}-{:02d}"), + basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + + auto logger = spdlog::create("logger", basename, 0, 0); + for (int i = 0; i < 10; ++i) { + logger->info("Test message {}", i); + } + logger->flush(); + + require_message_count(filename_buf_to_utf8string(w), 10); +} + +struct custom_daily_file_name_calculator { + static spdlog::filename_t calc_filename(const spdlog::filename_t &basename, const tm &now_tm) { + filename_memory_buf_t w; + spdlog::fmt_lib::format_to(std::back_inserter(w), SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"), + basename, now_tm.tm_year + 1900, now_tm.tm_mon + 1, + now_tm.tm_mday); + + return SPDLOG_BUF_TO_STRING(w); + } +}; + +TEST_CASE("daily_logger with custom calculator", "[daily_logger]") { + using sink_type = spdlog::sinks::daily_file_sink; + + prepare_logdir(); + + // calculate filename (time based) + spdlog::filename_t basename = SPDLOG_FILENAME_T("test_logs/daily_dateonly"); + std::tm tm = spdlog::details::os::localtime(); + filename_memory_buf_t w; + spdlog::fmt_lib::format_to(std::back_inserter(w), SPDLOG_FILENAME_T("{}{:04d}{:02d}{:02d}"), + basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + + auto logger = spdlog::create("logger", basename, 0, 0); + for (int i = 0; i < 10; ++i) { + logger->info("Test message {}", i); + } + + logger->flush(); + + require_message_count(filename_buf_to_utf8string(w), 10); +} + +/* + * File name calculations + */ + +TEST_CASE("rotating_file_sink::calc_filename1", "[rotating_file_sink]") { + auto filename = + spdlog::sinks::rotating_file_sink_st::calc_filename(SPDLOG_FILENAME_T("rotated.txt"), 3); + REQUIRE(filename == SPDLOG_FILENAME_T("rotated.3.txt")); +} + +TEST_CASE("rotating_file_sink::calc_filename2", "[rotating_file_sink]") { + auto filename = + spdlog::sinks::rotating_file_sink_st::calc_filename(SPDLOG_FILENAME_T("rotated"), 3); + REQUIRE(filename == SPDLOG_FILENAME_T("rotated.3")); +} + +TEST_CASE("rotating_file_sink::calc_filename3", "[rotating_file_sink]") { + auto filename = + spdlog::sinks::rotating_file_sink_st::calc_filename(SPDLOG_FILENAME_T("rotated.txt"), 0); + REQUIRE(filename == SPDLOG_FILENAME_T("rotated.txt")); +} + +// regex supported only from gcc 4.9 and above +#if defined(_MSC_VER) || !(__GNUC__ <= 4 && __GNUC_MINOR__ < 9) + + #include + +TEST_CASE("daily_file_sink::daily_filename_calculator", "[daily_file_sink]") { + // daily_YYYY-MM-DD_hh-mm.txt + auto filename = spdlog::sinks::daily_filename_calculator::calc_filename( + SPDLOG_FILENAME_T("daily.txt"), spdlog::details::os::localtime()); + // date regex based on https://www.regular-expressions.info/dates.html + std::basic_regex re( + SPDLOG_FILENAME_T(R"(^daily_(19|20)\d\d-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])\.txt$)")); + std::match_results match; + REQUIRE(std::regex_match(filename, match, re)); +} +#endif + +TEST_CASE("daily_file_sink::daily_filename_format_calculator", "[daily_file_sink]") { + std::tm tm = spdlog::details::os::localtime(); + // example-YYYY-MM-DD.log + auto filename = spdlog::sinks::daily_filename_format_calculator::calc_filename( + SPDLOG_FILENAME_T("example-%Y-%m-%d.log"), tm); + + REQUIRE(filename == + spdlog::fmt_lib::format(SPDLOG_FILENAME_T("example-{:04d}-{:02d}-{:02d}.log"), + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday)); +} + +/* Test removal of old files */ +static spdlog::details::log_msg create_msg(std::chrono::seconds offset) { + using spdlog::log_clock; + spdlog::details::log_msg msg{"test", spdlog::level::info, "Hello Message"}; + msg.time = log_clock::now() + offset; + return msg; +} + +static void test_rotate(int days_to_run, uint16_t max_days, uint16_t expected_n_files) { + using spdlog::log_clock; + using spdlog::details::log_msg; + using spdlog::sinks::daily_file_sink_st; + + prepare_logdir(); + + spdlog::filename_t basename = SPDLOG_FILENAME_T("test_logs/daily_rotate.txt"); + daily_file_sink_st sink{basename, 2, 30, true, max_days}; + + // simulate messages with 24 intervals + + for (int i = 0; i < days_to_run; i++) { + auto offset = std::chrono::seconds{24 * 3600 * i}; + sink.log(create_msg(offset)); + } + + REQUIRE(count_files("test_logs") == static_cast(expected_n_files)); +} + +TEST_CASE("daily_logger rotate", "[daily_file_sink]") { + int days_to_run = 1; + test_rotate(days_to_run, 0, 1); + test_rotate(days_to_run, 1, 1); + test_rotate(days_to_run, 3, 1); + test_rotate(days_to_run, 10, 1); + + days_to_run = 10; + test_rotate(days_to_run, 0, 10); + test_rotate(days_to_run, 1, 1); + test_rotate(days_to_run, 3, 3); + test_rotate(days_to_run, 9, 9); + test_rotate(days_to_run, 10, 10); + test_rotate(days_to_run, 11, 10); + test_rotate(days_to_run, 20, 10); +} diff --git a/inc/spdlog/tests/test_dup_filter.cpp b/inc/spdlog/tests/test_dup_filter.cpp new file mode 100644 index 0000000..78e22be --- /dev/null +++ b/inc/spdlog/tests/test_dup_filter.cpp @@ -0,0 +1,83 @@ +#include "includes.h" +#include "spdlog/sinks/dup_filter_sink.h" +#include "test_sink.h" + +TEST_CASE("dup_filter_test1", "[dup_filter_sink]") { + using spdlog::sinks::dup_filter_sink_st; + using spdlog::sinks::test_sink_mt; + + dup_filter_sink_st dup_sink{std::chrono::seconds{5}}; + auto test_sink = std::make_shared(); + dup_sink.add_sink(test_sink); + + for (int i = 0; i < 10; i++) { + dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message1"}); + } + + REQUIRE(test_sink->msg_counter() == 1); +} + +TEST_CASE("dup_filter_test2", "[dup_filter_sink]") { + using spdlog::sinks::dup_filter_sink_st; + using spdlog::sinks::test_sink_mt; + + dup_filter_sink_st dup_sink{std::chrono::seconds{0}}; + auto test_sink = std::make_shared(); + dup_sink.add_sink(test_sink); + + for (int i = 0; i < 10; i++) { + dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message1"}); + std::this_thread::sleep_for(std::chrono::milliseconds(5)); + } + + REQUIRE(test_sink->msg_counter() == 10); +} + +TEST_CASE("dup_filter_test3", "[dup_filter_sink]") { + using spdlog::sinks::dup_filter_sink_st; + using spdlog::sinks::test_sink_mt; + + dup_filter_sink_st dup_sink{std::chrono::seconds{1}}; + auto test_sink = std::make_shared(); + dup_sink.add_sink(test_sink); + + for (int i = 0; i < 10; i++) { + dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message1"}); + dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message2"}); + } + + REQUIRE(test_sink->msg_counter() == 20); +} + +TEST_CASE("dup_filter_test4", "[dup_filter_sink]") { + using spdlog::sinks::dup_filter_sink_mt; + using spdlog::sinks::test_sink_mt; + + dup_filter_sink_mt dup_sink{std::chrono::milliseconds{10}}; + auto test_sink = std::make_shared(); + dup_sink.add_sink(test_sink); + + dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message"}); + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message"}); + REQUIRE(test_sink->msg_counter() == 2); +} + +TEST_CASE("dup_filter_test5", "[dup_filter_sink]") { + using spdlog::sinks::dup_filter_sink_mt; + using spdlog::sinks::test_sink_mt; + + dup_filter_sink_mt dup_sink{std::chrono::seconds{5}}; + auto test_sink = std::make_shared(); + test_sink->set_pattern("%v"); + dup_sink.add_sink(test_sink); + + dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message1"}); + dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message1"}); + dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message1"}); + dup_sink.log(spdlog::details::log_msg{"test", spdlog::level::info, "message2"}); + + REQUIRE(test_sink->msg_counter() == + 3); // skip 2 messages but log the "skipped.." message before message2 + REQUIRE(test_sink->lines()[1] == "Skipped 2 duplicate messages.."); +} diff --git a/inc/spdlog/tests/test_errors.cpp b/inc/spdlog/tests/test_errors.cpp new file mode 100644 index 0000000..7dde919 --- /dev/null +++ b/inc/spdlog/tests/test_errors.cpp @@ -0,0 +1,112 @@ +/* + * This content is released under the MIT License as specified in + * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE + */ +#include "includes.h" + +#include + +#define SIMPLE_LOG "test_logs/simple_log.txt" +#define SIMPLE_ASYNC_LOG "test_logs/simple_async_log.txt" + +class failing_sink : public spdlog::sinks::base_sink { +protected: + void sink_it_(const spdlog::details::log_msg &) final { + throw std::runtime_error("some error happened during log"); + } + + void flush_() final { throw std::runtime_error("some error happened during flush"); } +}; +struct custom_ex {}; + +#if !defined(SPDLOG_USE_STD_FORMAT) // std formt doesn't fully support tuntime strings +TEST_CASE("default_error_handler", "[errors]") { + prepare_logdir(); + spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG); + + auto logger = spdlog::create("test-error", filename, true); + logger->set_pattern("%v"); + logger->info(SPDLOG_FMT_RUNTIME("Test message {} {}"), 1); + logger->info("Test message {}", 2); + logger->flush(); + using spdlog::details::os::default_eol; + REQUIRE(file_contents(SIMPLE_LOG) == spdlog::fmt_lib::format("Test message 2{}", default_eol)); + REQUIRE(count_lines(SIMPLE_LOG) == 1); +} + +TEST_CASE("custom_error_handler", "[errors]") { + prepare_logdir(); + spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG); + auto logger = spdlog::create("logger", filename, true); + logger->flush_on(spdlog::level::info); + logger->set_error_handler([=](const std::string &) { throw custom_ex(); }); + logger->info("Good message #1"); + + REQUIRE_THROWS_AS(logger->info(SPDLOG_FMT_RUNTIME("Bad format msg {} {}"), "xxx"), custom_ex); + logger->info("Good message #2"); + require_message_count(SIMPLE_LOG, 2); +} +#endif + +TEST_CASE("default_error_handler2", "[errors]") { + spdlog::drop_all(); + auto logger = spdlog::create("failed_logger"); + logger->set_error_handler([=](const std::string &) { throw custom_ex(); }); + REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex); +} + +TEST_CASE("flush_error_handler", "[errors]") { + spdlog::drop_all(); + auto logger = spdlog::create("failed_logger"); + logger->set_error_handler([=](const std::string &) { throw custom_ex(); }); + REQUIRE_THROWS_AS(logger->flush(), custom_ex); +} + +#if !defined(SPDLOG_USE_STD_FORMAT) +TEST_CASE("async_error_handler", "[errors]") { + prepare_logdir(); + std::string err_msg("log failed with some msg"); + + spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_ASYNC_LOG); + { + spdlog::init_thread_pool(128, 1); + auto logger = + spdlog::create_async("logger", filename, true); + logger->set_error_handler([=](const std::string &) { + std::ofstream ofs("test_logs/custom_err.txt"); + if (!ofs) { + throw std::runtime_error("Failed open test_logs/custom_err.txt"); + } + ofs << err_msg; + }); + logger->info("Good message #1"); + logger->info(SPDLOG_FMT_RUNTIME("Bad format msg {} {}"), "xxx"); + logger->info("Good message #2"); + spdlog::drop("logger"); // force logger to drain the queue and shutdown + } + spdlog::init_thread_pool(128, 1); + require_message_count(SIMPLE_ASYNC_LOG, 2); + REQUIRE(file_contents("test_logs/custom_err.txt") == err_msg); +} +#endif + +// Make sure async error handler is executed +TEST_CASE("async_error_handler2", "[errors]") { + prepare_logdir(); + std::string err_msg("This is async handler error message"); + { + spdlog::details::os::create_dir(SPDLOG_FILENAME_T("test_logs")); + spdlog::init_thread_pool(128, 1); + auto logger = spdlog::create_async("failed_logger"); + logger->set_error_handler([=](const std::string &) { + std::ofstream ofs("test_logs/custom_err2.txt"); + if (!ofs) throw std::runtime_error("Failed open test_logs/custom_err2.txt"); + ofs << err_msg; + }); + logger->info("Hello failure"); + spdlog::drop("failed_logger"); // force logger to drain the queue and shutdown + } + + spdlog::init_thread_pool(128, 1); + REQUIRE(file_contents("test_logs/custom_err2.txt") == err_msg); +} diff --git a/inc/spdlog/tests/test_eventlog.cpp b/inc/spdlog/tests/test_eventlog.cpp new file mode 100644 index 0000000..702eabe --- /dev/null +++ b/inc/spdlog/tests/test_eventlog.cpp @@ -0,0 +1,75 @@ +#if _WIN32 + + #include "includes.h" + #include "test_sink.h" + + #include "spdlog/sinks/win_eventlog_sink.h" + +static const LPCSTR TEST_SOURCE = "spdlog_test"; + +static void test_single_print(std::function do_log, + std::string const &expected_contents, + WORD expected_ev_type) { + using namespace std::chrono; + do_log(expected_contents); + const auto expected_time_generated = + duration_cast(system_clock::now().time_since_epoch()).count(); + + struct handle_t { + HANDLE handle_; + + ~handle_t() { + if (handle_) { + REQUIRE(CloseEventLog(handle_)); + } + } + } event_log{::OpenEventLogA(nullptr, TEST_SOURCE)}; + + REQUIRE(event_log.handle_); + + DWORD read_bytes{}, size_needed{}; + auto ok = ::ReadEventLogA(event_log.handle_, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ, + 0, &read_bytes, 0, &read_bytes, &size_needed); + REQUIRE(!ok); + REQUIRE(::GetLastError() == ERROR_INSUFFICIENT_BUFFER); + + std::vector record_buffer(size_needed); + PEVENTLOGRECORD record = (PEVENTLOGRECORD)record_buffer.data(); + + ok = ::ReadEventLogA(event_log.handle_, EVENTLOG_SEQUENTIAL_READ | EVENTLOG_BACKWARDS_READ, 0, + record, size_needed, &read_bytes, &size_needed); + REQUIRE(ok); + + REQUIRE(record->NumStrings == 1); + REQUIRE(record->EventType == expected_ev_type); + REQUIRE((expected_time_generated - record->TimeGenerated) <= 3u); + + std::string message_in_log(((char *)record + record->StringOffset)); + REQUIRE(message_in_log == expected_contents + spdlog::details::os::default_eol); +} + +TEST_CASE("eventlog", "[eventlog]") { + using namespace spdlog; + + auto test_sink = std::make_shared(TEST_SOURCE); + + spdlog::logger test_logger("eventlog", test_sink); + test_logger.set_level(level::trace); + + test_sink->set_pattern("%v"); + + test_single_print([&test_logger](std::string const &msg) { test_logger.trace(msg); }, + "my trace message", EVENTLOG_SUCCESS); + test_single_print([&test_logger](std::string const &msg) { test_logger.debug(msg); }, + "my debug message", EVENTLOG_SUCCESS); + test_single_print([&test_logger](std::string const &msg) { test_logger.info(msg); }, + "my info message", EVENTLOG_INFORMATION_TYPE); + test_single_print([&test_logger](std::string const &msg) { test_logger.warn(msg); }, + "my warn message", EVENTLOG_WARNING_TYPE); + test_single_print([&test_logger](std::string const &msg) { test_logger.error(msg); }, + "my error message", EVENTLOG_ERROR_TYPE); + test_single_print([&test_logger](std::string const &msg) { test_logger.critical(msg); }, + "my critical message", EVENTLOG_ERROR_TYPE); +} + +#endif //_WIN32 diff --git a/inc/spdlog/tests/test_file_helper.cpp b/inc/spdlog/tests/test_file_helper.cpp new file mode 100644 index 0000000..de7d51d --- /dev/null +++ b/inc/spdlog/tests/test_file_helper.cpp @@ -0,0 +1,169 @@ +/* + * This content is released under the MIT License as specified in + * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE + */ +#include "includes.h" + +#define TEST_FILENAME "test_logs/file_helper_test.txt" + +using spdlog::details::file_helper; + +static void write_with_helper(file_helper &helper, size_t howmany) { + spdlog::memory_buf_t formatted; + spdlog::fmt_lib::format_to(std::back_inserter(formatted), "{}", std::string(howmany, '1')); + helper.write(formatted); + helper.flush(); +} + +TEST_CASE("file_helper_filename", "[file_helper::filename()]") { + prepare_logdir(); + + file_helper helper; + spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME); + helper.open(target_filename); + REQUIRE(helper.filename() == target_filename); +} + +TEST_CASE("file_helper_size", "[file_helper::size()]") { + prepare_logdir(); + spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME); + size_t expected_size = 123; + { + file_helper helper; + helper.open(target_filename); + write_with_helper(helper, expected_size); + REQUIRE(static_cast(helper.size()) == expected_size); + } + REQUIRE(get_filesize(TEST_FILENAME) == expected_size); +} + +TEST_CASE("file_helper_reopen", "[file_helper::reopen()]") { + prepare_logdir(); + spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME); + file_helper helper; + helper.open(target_filename); + write_with_helper(helper, 12); + REQUIRE(helper.size() == 12); + helper.reopen(true); + REQUIRE(helper.size() == 0); +} + +TEST_CASE("file_helper_reopen2", "[file_helper::reopen(false)]") { + prepare_logdir(); + spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME); + size_t expected_size = 14; + file_helper helper; + helper.open(target_filename); + write_with_helper(helper, expected_size); + REQUIRE(helper.size() == expected_size); + helper.reopen(false); + REQUIRE(helper.size() == expected_size); +} + +static void test_split_ext(const spdlog::filename_t::value_type *fname, + const spdlog::filename_t::value_type *expect_base, + const spdlog::filename_t::value_type *expect_ext) { + spdlog::filename_t filename(fname); + spdlog::filename_t expected_base(expect_base); + spdlog::filename_t expected_ext(expect_ext); + + spdlog::filename_t basename; + spdlog::filename_t ext; + std::tie(basename, ext) = file_helper::split_by_extension(filename); + REQUIRE(basename == expected_base); + REQUIRE(ext == expected_ext); +} + +TEST_CASE("file_helper_split_by_extension", "[file_helper::split_by_extension()]") { + test_split_ext(SPDLOG_FILENAME_T("mylog.txt"), SPDLOG_FILENAME_T("mylog"), + SPDLOG_FILENAME_T(".txt")); + test_split_ext(SPDLOG_FILENAME_T(".mylog.txt"), SPDLOG_FILENAME_T(".mylog"), + SPDLOG_FILENAME_T(".txt")); + test_split_ext(SPDLOG_FILENAME_T(".mylog"), SPDLOG_FILENAME_T(".mylog"), SPDLOG_FILENAME_T("")); + test_split_ext(SPDLOG_FILENAME_T("/aaa/bb.d/mylog"), SPDLOG_FILENAME_T("/aaa/bb.d/mylog"), + SPDLOG_FILENAME_T("")); + test_split_ext(SPDLOG_FILENAME_T("/aaa/bb.d/mylog.txt"), SPDLOG_FILENAME_T("/aaa/bb.d/mylog"), + SPDLOG_FILENAME_T(".txt")); + test_split_ext(SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog.txt"), + SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog"), SPDLOG_FILENAME_T(".txt")); + test_split_ext(SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog."), SPDLOG_FILENAME_T("aaa/bbb/ccc/mylog."), + SPDLOG_FILENAME_T("")); + test_split_ext(SPDLOG_FILENAME_T("aaa/bbb/ccc/.mylog.txt"), + SPDLOG_FILENAME_T("aaa/bbb/ccc/.mylog"), SPDLOG_FILENAME_T(".txt")); + test_split_ext(SPDLOG_FILENAME_T("/aaa/bbb/ccc/mylog.txt"), + SPDLOG_FILENAME_T("/aaa/bbb/ccc/mylog"), SPDLOG_FILENAME_T(".txt")); + test_split_ext(SPDLOG_FILENAME_T("/aaa/bbb/ccc/.mylog"), + SPDLOG_FILENAME_T("/aaa/bbb/ccc/.mylog"), SPDLOG_FILENAME_T("")); + test_split_ext(SPDLOG_FILENAME_T("../mylog.txt"), SPDLOG_FILENAME_T("../mylog"), + SPDLOG_FILENAME_T(".txt")); + test_split_ext(SPDLOG_FILENAME_T(".././mylog.txt"), SPDLOG_FILENAME_T(".././mylog"), + SPDLOG_FILENAME_T(".txt")); + test_split_ext(SPDLOG_FILENAME_T(".././mylog.txt/xxx"), SPDLOG_FILENAME_T(".././mylog.txt/xxx"), + SPDLOG_FILENAME_T("")); + test_split_ext(SPDLOG_FILENAME_T("/mylog.txt"), SPDLOG_FILENAME_T("/mylog"), + SPDLOG_FILENAME_T(".txt")); + test_split_ext(SPDLOG_FILENAME_T("//mylog.txt"), SPDLOG_FILENAME_T("//mylog"), + SPDLOG_FILENAME_T(".txt")); + test_split_ext(SPDLOG_FILENAME_T(""), SPDLOG_FILENAME_T(""), SPDLOG_FILENAME_T("")); + test_split_ext(SPDLOG_FILENAME_T("."), SPDLOG_FILENAME_T("."), SPDLOG_FILENAME_T("")); + test_split_ext(SPDLOG_FILENAME_T("..txt"), SPDLOG_FILENAME_T("."), SPDLOG_FILENAME_T(".txt")); +} + +TEST_CASE("file_event_handlers", "[file_helper]") { + enum class flags { before_open, after_open, before_close, after_close }; + prepare_logdir(); + + spdlog::filename_t test_filename = SPDLOG_FILENAME_T(TEST_FILENAME); + // define event handles that update vector of flags when called + std::vector events; + spdlog::file_event_handlers handlers; + handlers.before_open = [&](spdlog::filename_t filename) { + REQUIRE(filename == test_filename); + events.push_back(flags::before_open); + }; + handlers.after_open = [&](spdlog::filename_t filename, std::FILE *fstream) { + REQUIRE(filename == test_filename); + REQUIRE(fstream); + fputs("after_open\n", fstream); + events.push_back(flags::after_open); + }; + handlers.before_close = [&](spdlog::filename_t filename, std::FILE *fstream) { + REQUIRE(filename == test_filename); + REQUIRE(fstream); + fputs("before_close\n", fstream); + events.push_back(flags::before_close); + }; + handlers.after_close = [&](spdlog::filename_t filename) { + REQUIRE(filename == test_filename); + events.push_back(flags::after_close); + }; + { + spdlog::details::file_helper helper{handlers}; + REQUIRE(events.empty()); + + helper.open(test_filename); + REQUIRE(events == std::vector{flags::before_open, flags::after_open}); + + events.clear(); + helper.close(); + REQUIRE(events == std::vector{flags::before_close, flags::after_close}); + REQUIRE(file_contents(TEST_FILENAME) == "after_open\nbefore_close\n"); + + helper.reopen(true); + events.clear(); + } + // make sure that the file_helper destrcutor calls the close callbacks if needed + REQUIRE(events == std::vector{flags::before_close, flags::after_close}); + REQUIRE(file_contents(TEST_FILENAME) == "after_open\nbefore_close\n"); +} + +TEST_CASE("file_helper_open", "[file_helper]") { + prepare_logdir(); + spdlog::filename_t target_filename = SPDLOG_FILENAME_T(TEST_FILENAME); + file_helper helper; + helper.open(target_filename); + helper.close(); + + target_filename += SPDLOG_FILENAME_T("/invalid"); + REQUIRE_THROWS_AS(helper.open(target_filename), spdlog::spdlog_ex); +} diff --git a/inc/spdlog/tests/test_file_logging.cpp b/inc/spdlog/tests/test_file_logging.cpp new file mode 100644 index 0000000..ac378b5 --- /dev/null +++ b/inc/spdlog/tests/test_file_logging.cpp @@ -0,0 +1,103 @@ +/* + * This content is released under the MIT License as specified in + * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE + */ +#include "includes.h" + +#define SIMPLE_LOG "test_logs/simple_log" +#define ROTATING_LOG "test_logs/rotating_log" + +TEST_CASE("simple_file_logger", "[simple_logger]") { + prepare_logdir(); + spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG); + + auto logger = spdlog::create("logger", filename); + logger->set_pattern("%v"); + + logger->info("Test message {}", 1); + logger->info("Test message {}", 2); + + logger->flush(); + require_message_count(SIMPLE_LOG, 2); + using spdlog::details::os::default_eol; + REQUIRE(file_contents(SIMPLE_LOG) == + spdlog::fmt_lib::format("Test message 1{}Test message 2{}", default_eol, default_eol)); +} + +TEST_CASE("flush_on", "[flush_on]") { + prepare_logdir(); + spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG); + + auto logger = spdlog::create("logger", filename); + logger->set_pattern("%v"); + logger->set_level(spdlog::level::trace); + logger->flush_on(spdlog::level::info); + logger->trace("Should not be flushed"); + REQUIRE(count_lines(SIMPLE_LOG) == 0); + + logger->info("Test message {}", 1); + logger->info("Test message {}", 2); + + require_message_count(SIMPLE_LOG, 3); + using spdlog::details::os::default_eol; + REQUIRE(file_contents(SIMPLE_LOG) == + spdlog::fmt_lib::format("Should not be flushed{}Test message 1{}Test message 2{}", + default_eol, default_eol, default_eol)); +} + +TEST_CASE("rotating_file_logger1", "[rotating_logger]") { + prepare_logdir(); + size_t max_size = 1024 * 10; + spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG); + auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 0); + + for (int i = 0; i < 10; ++i) { + logger->info("Test message {}", i); + } + + logger->flush(); + require_message_count(ROTATING_LOG, 10); +} + +TEST_CASE("rotating_file_logger2", "[rotating_logger]") { + prepare_logdir(); + size_t max_size = 1024 * 10; + spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG); + + { + // make an initial logger to create the first output file + auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 2, true); + for (int i = 0; i < 10; ++i) { + logger->info("Test message {}", i); + } + // drop causes the logger destructor to be called, which is required so the + // next logger can rename the first output file. + spdlog::drop(logger->name()); + } + + auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 2, true); + for (int i = 0; i < 10; ++i) { + logger->info("Test message {}", i); + } + + logger->flush(); + + require_message_count(ROTATING_LOG, 10); + + for (int i = 0; i < 1000; i++) { + logger->info("Test message {}", i); + } + + logger->flush(); + REQUIRE(get_filesize(ROTATING_LOG) <= max_size); + REQUIRE(get_filesize(ROTATING_LOG ".1") <= max_size); +} + +// test that passing max_size=0 throws +TEST_CASE("rotating_file_logger3", "[rotating_logger]") { + prepare_logdir(); + size_t max_size = 0; + spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG); + REQUIRE_THROWS_AS(spdlog::rotating_logger_mt("logger", basename, max_size, 0), + spdlog::spdlog_ex); +} diff --git a/inc/spdlog/tests/test_fmt_helper.cpp b/inc/spdlog/tests/test_fmt_helper.cpp new file mode 100644 index 0000000..31b9306 --- /dev/null +++ b/inc/spdlog/tests/test_fmt_helper.cpp @@ -0,0 +1,82 @@ + +#include "includes.h" + +using spdlog::memory_buf_t; +using spdlog::details::to_string_view; + +void test_pad2(int n, const char *expected) { + memory_buf_t buf; + spdlog::details::fmt_helper::pad2(n, buf); + + REQUIRE(to_string_view(buf) == expected); +} + +void test_pad3(uint32_t n, const char *expected) { + memory_buf_t buf; + spdlog::details::fmt_helper::pad3(n, buf); + + REQUIRE(to_string_view(buf) == expected); +} + +void test_pad6(std::size_t n, const char *expected) { + memory_buf_t buf; + spdlog::details::fmt_helper::pad6(n, buf); + + REQUIRE(to_string_view(buf) == expected); +} + +void test_pad9(std::size_t n, const char *expected) { + memory_buf_t buf; + spdlog::details::fmt_helper::pad9(n, buf); + + REQUIRE(to_string_view(buf) == expected); +} + +TEST_CASE("pad2", "[fmt_helper]") { + test_pad2(0, "00"); + test_pad2(3, "03"); + test_pad2(10, "10"); + test_pad2(23, "23"); + test_pad2(99, "99"); + test_pad2(100, "100"); + test_pad2(123, "123"); + test_pad2(1234, "1234"); + test_pad2(-5, "-5"); +} + +TEST_CASE("pad3", "[fmt_helper]") { + test_pad3(0, "000"); + test_pad3(3, "003"); + test_pad3(10, "010"); + test_pad3(23, "023"); + test_pad3(99, "099"); + test_pad3(100, "100"); + test_pad3(123, "123"); + test_pad3(999, "999"); + test_pad3(1000, "1000"); + test_pad3(1234, "1234"); +} + +TEST_CASE("pad6", "[fmt_helper]") { + test_pad6(0, "000000"); + test_pad6(3, "000003"); + test_pad6(23, "000023"); + test_pad6(123, "000123"); + test_pad6(1234, "001234"); + test_pad6(12345, "012345"); + test_pad6(123456, "123456"); +} + +TEST_CASE("pad9", "[fmt_helper]") { + test_pad9(0, "000000000"); + test_pad9(3, "000000003"); + test_pad9(23, "000000023"); + test_pad9(123, "000000123"); + test_pad9(1234, "000001234"); + test_pad9(12345, "000012345"); + test_pad9(123456, "000123456"); + test_pad9(1234567, "001234567"); + test_pad9(12345678, "012345678"); + test_pad9(123456789, "123456789"); + test_pad9(1234567891, "1234567891"); +} diff --git a/inc/spdlog/tests/test_macros.cpp b/inc/spdlog/tests/test_macros.cpp new file mode 100644 index 0000000..132706f --- /dev/null +++ b/inc/spdlog/tests/test_macros.cpp @@ -0,0 +1,53 @@ +/* + * This content is released under the MIT License as specified in + * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE + */ + +#include "includes.h" + +#if SPDLOG_ACTIVE_LEVEL != SPDLOG_LEVEL_DEBUG + #error "Invalid SPDLOG_ACTIVE_LEVEL in test. Should be SPDLOG_LEVEL_DEBUG" +#endif + +#define TEST_FILENAME "test_logs/simple_log" + +TEST_CASE("debug and trace w/o format string", "[macros]") { + prepare_logdir(); + spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME); + + auto logger = spdlog::create("logger", filename); + logger->set_pattern("%v"); + logger->set_level(spdlog::level::trace); + + SPDLOG_LOGGER_TRACE(logger, "Test message 1"); + SPDLOG_LOGGER_DEBUG(logger, "Test message 2"); + logger->flush(); + + using spdlog::details::os::default_eol; + REQUIRE(ends_with(file_contents(TEST_FILENAME), + spdlog::fmt_lib::format("Test message 2{}", default_eol))); + REQUIRE(count_lines(TEST_FILENAME) == 1); + + auto orig_default_logger = spdlog::default_logger(); + spdlog::set_default_logger(logger); + + SPDLOG_TRACE("Test message 3"); + SPDLOG_DEBUG("Test message {}", 4); + logger->flush(); + + require_message_count(TEST_FILENAME, 2); + REQUIRE(ends_with(file_contents(TEST_FILENAME), + spdlog::fmt_lib::format("Test message 4{}", default_eol))); + spdlog::set_default_logger(std::move(orig_default_logger)); +} + +TEST_CASE("disable param evaluation", "[macros]") { + SPDLOG_TRACE("Test message {}", throw std::runtime_error("Should not be evaluated")); +} + +TEST_CASE("pass logger pointer", "[macros]") { + auto logger = spdlog::create("refmacro"); + auto &ref = *logger; + SPDLOG_LOGGER_TRACE(&ref, "Test message 1"); + SPDLOG_LOGGER_DEBUG(&ref, "Test message 2"); +} diff --git a/inc/spdlog/tests/test_misc.cpp b/inc/spdlog/tests/test_misc.cpp new file mode 100644 index 0000000..367eeb7 --- /dev/null +++ b/inc/spdlog/tests/test_misc.cpp @@ -0,0 +1,169 @@ +#include "includes.h" +#include "test_sink.h" + +template +std::string log_info(const T &what, spdlog::level::level_enum logger_level = spdlog::level::info) { + std::ostringstream oss; + auto oss_sink = std::make_shared(oss); + + spdlog::logger oss_logger("oss", oss_sink); + oss_logger.set_level(logger_level); + oss_logger.set_pattern("%v"); + oss_logger.info(what); + + return oss.str().substr(0, oss.str().length() - strlen(spdlog::details::os::default_eol)); +} + +TEST_CASE("basic_logging ", "[basic_logging]") { + // const char + REQUIRE(log_info("Hello") == "Hello"); + REQUIRE(log_info("").empty()); + + // std::string + REQUIRE(log_info(std::string("Hello")) == "Hello"); + REQUIRE(log_info(std::string()).empty()); + + // Numbers + REQUIRE(log_info(5) == "5"); + REQUIRE(log_info(5.6) == "5.6"); + + // User defined class + // REQUIRE(log_info(some_logged_class("some_val")) == "some_val"); +} + +TEST_CASE("log_levels", "[log_levels]") { + REQUIRE(log_info("Hello", spdlog::level::err).empty()); + REQUIRE(log_info("Hello", spdlog::level::critical).empty()); + REQUIRE(log_info("Hello", spdlog::level::info) == "Hello"); + REQUIRE(log_info("Hello", spdlog::level::debug) == "Hello"); + REQUIRE(log_info("Hello", spdlog::level::trace) == "Hello"); +} + +TEST_CASE("level_to_string_view", "[convert_to_string_view]") { + REQUIRE(spdlog::level::to_string_view(spdlog::level::trace) == "trace"); + REQUIRE(spdlog::level::to_string_view(spdlog::level::debug) == "debug"); + REQUIRE(spdlog::level::to_string_view(spdlog::level::info) == "info"); + REQUIRE(spdlog::level::to_string_view(spdlog::level::warn) == "warning"); + REQUIRE(spdlog::level::to_string_view(spdlog::level::err) == "error"); + REQUIRE(spdlog::level::to_string_view(spdlog::level::critical) == "critical"); + REQUIRE(spdlog::level::to_string_view(spdlog::level::off) == "off"); +} + +TEST_CASE("to_short_c_str", "[convert_to_short_c_str]") { + REQUIRE(std::string(spdlog::level::to_short_c_str(spdlog::level::trace)) == "T"); + REQUIRE(std::string(spdlog::level::to_short_c_str(spdlog::level::debug)) == "D"); + REQUIRE(std::string(spdlog::level::to_short_c_str(spdlog::level::info)) == "I"); + REQUIRE(std::string(spdlog::level::to_short_c_str(spdlog::level::warn)) == "W"); + REQUIRE(std::string(spdlog::level::to_short_c_str(spdlog::level::err)) == "E"); + REQUIRE(std::string(spdlog::level::to_short_c_str(spdlog::level::critical)) == "C"); + REQUIRE(std::string(spdlog::level::to_short_c_str(spdlog::level::off)) == "O"); +} + +TEST_CASE("to_level_enum", "[convert_to_level_enum]") { + REQUIRE(spdlog::level::from_str("trace") == spdlog::level::trace); + REQUIRE(spdlog::level::from_str("debug") == spdlog::level::debug); + REQUIRE(spdlog::level::from_str("info") == spdlog::level::info); + REQUIRE(spdlog::level::from_str("warning") == spdlog::level::warn); + REQUIRE(spdlog::level::from_str("warn") == spdlog::level::warn); + REQUIRE(spdlog::level::from_str("error") == spdlog::level::err); + REQUIRE(spdlog::level::from_str("critical") == spdlog::level::critical); + REQUIRE(spdlog::level::from_str("off") == spdlog::level::off); + REQUIRE(spdlog::level::from_str("null") == spdlog::level::off); +} + +TEST_CASE("periodic flush", "[periodic_flush]") { + using spdlog::sinks::test_sink_mt; + auto logger = spdlog::create("periodic_flush"); + auto test_sink = std::static_pointer_cast(logger->sinks()[0]); + + spdlog::flush_every(std::chrono::seconds(1)); + std::this_thread::sleep_for(std::chrono::milliseconds(1250)); + REQUIRE(test_sink->flush_counter() == 1); + spdlog::flush_every(std::chrono::seconds(0)); + spdlog::drop_all(); +} + +TEST_CASE("clone-logger", "[clone]") { + using spdlog::sinks::test_sink_mt; + auto test_sink = std::make_shared(); + auto logger = std::make_shared("orig", test_sink); + logger->set_pattern("%v"); + auto cloned = logger->clone("clone"); + + REQUIRE(cloned->name() == "clone"); + REQUIRE(logger->sinks() == cloned->sinks()); + REQUIRE(logger->level() == cloned->level()); + REQUIRE(logger->flush_level() == cloned->flush_level()); + logger->info("Some message 1"); + cloned->info("Some message 2"); + + REQUIRE(test_sink->lines().size() == 2); + REQUIRE(test_sink->lines()[0] == "Some message 1"); + REQUIRE(test_sink->lines()[1] == "Some message 2"); + + spdlog::drop_all(); +} + +TEST_CASE("clone async", "[clone]") { + using spdlog::sinks::test_sink_st; + spdlog::init_thread_pool(4, 1); + auto test_sink = std::make_shared(); + auto logger = std::make_shared("orig", test_sink, spdlog::thread_pool()); + logger->set_pattern("%v"); + auto cloned = logger->clone("clone"); + + REQUIRE(cloned->name() == "clone"); + REQUIRE(logger->sinks() == cloned->sinks()); + REQUIRE(logger->level() == cloned->level()); + REQUIRE(logger->flush_level() == cloned->flush_level()); + + logger->info("Some message 1"); + cloned->info("Some message 2"); + + spdlog::details::os::sleep_for_millis(100); + + REQUIRE(test_sink->lines().size() == 2); + REQUIRE(test_sink->lines()[0] == "Some message 1"); + REQUIRE(test_sink->lines()[1] == "Some message 2"); + + spdlog::drop_all(); +} + +TEST_CASE("default logger API", "[default logger]") { + std::ostringstream oss; + auto oss_sink = std::make_shared(oss); + + spdlog::set_default_logger(std::make_shared("oss", oss_sink)); + spdlog::set_pattern("*** %v"); + + spdlog::default_logger()->set_level(spdlog::level::trace); + spdlog::trace("hello trace"); + REQUIRE(oss.str() == "*** hello trace" + std::string(spdlog::details::os::default_eol)); + + oss.str(""); + spdlog::debug("hello debug"); + REQUIRE(oss.str() == "*** hello debug" + std::string(spdlog::details::os::default_eol)); + + oss.str(""); + spdlog::info("Hello"); + REQUIRE(oss.str() == "*** Hello" + std::string(spdlog::details::os::default_eol)); + + oss.str(""); + spdlog::warn("Hello again {}", 2); + REQUIRE(oss.str() == "*** Hello again 2" + std::string(spdlog::details::os::default_eol)); + + oss.str(""); + spdlog::error(123); + REQUIRE(oss.str() == "*** 123" + std::string(spdlog::details::os::default_eol)); + + oss.str(""); + spdlog::critical(std::string("some string")); + REQUIRE(oss.str() == "*** some string" + std::string(spdlog::details::os::default_eol)); + + oss.str(""); + spdlog::set_level(spdlog::level::info); + spdlog::debug("should not be logged"); + REQUIRE(oss.str().empty()); + spdlog::drop_all(); + spdlog::set_pattern("%v"); +} diff --git a/inc/spdlog/tests/test_mpmc_q.cpp b/inc/spdlog/tests/test_mpmc_q.cpp new file mode 100644 index 0000000..bc7a37d --- /dev/null +++ b/inc/spdlog/tests/test_mpmc_q.cpp @@ -0,0 +1,114 @@ +#include "includes.h" + +using std::chrono::milliseconds; +using test_clock = std::chrono::high_resolution_clock; + +static milliseconds millis_from(const test_clock::time_point &tp0) { + return std::chrono::duration_cast(test_clock::now() - tp0); +} +TEST_CASE("dequeue-empty-nowait", "[mpmc_blocking_q]") { + size_t q_size = 100; + milliseconds tolerance_wait(20); + spdlog::details::mpmc_blocking_queue q(q_size); + int popped_item = 0; + + auto start = test_clock::now(); + auto rv = q.dequeue_for(popped_item, milliseconds::zero()); + auto delta_ms = millis_from(start); + + REQUIRE(rv == false); + INFO("Delta " << delta_ms.count() << " millis"); + REQUIRE(delta_ms <= tolerance_wait); +} + +TEST_CASE("dequeue-empty-wait", "[mpmc_blocking_q]") { + size_t q_size = 100; + milliseconds wait_ms(250); + milliseconds tolerance_wait(250); + + spdlog::details::mpmc_blocking_queue q(q_size); + int popped_item = 0; + auto start = test_clock::now(); + auto rv = q.dequeue_for(popped_item, wait_ms); + auto delta_ms = millis_from(start); + + REQUIRE(rv == false); + + INFO("Delta " << delta_ms.count() << " millis"); + REQUIRE(delta_ms >= wait_ms - tolerance_wait); + REQUIRE(delta_ms <= wait_ms + tolerance_wait); +} + +TEST_CASE("dequeue-full-nowait", "[mpmc_blocking_q]") { + spdlog::details::mpmc_blocking_queue q(1); + q.enqueue(42); + + int item = 0; + q.dequeue_for(item, milliseconds::zero()); + REQUIRE(item == 42); +} + +TEST_CASE("dequeue-full-wait", "[mpmc_blocking_q]") { + spdlog::details::mpmc_blocking_queue q(1); + q.enqueue(42); + + int item = 0; + q.dequeue(item); + REQUIRE(item == 42); +} + +TEST_CASE("enqueue_nowait", "[mpmc_blocking_q]") { + size_t q_size = 1; + spdlog::details::mpmc_blocking_queue q(q_size); + milliseconds tolerance_wait(10); + + q.enqueue(1); + REQUIRE(q.overrun_counter() == 0); + + auto start = test_clock::now(); + q.enqueue_nowait(2); + auto delta_ms = millis_from(start); + + INFO("Delta " << delta_ms.count() << " millis"); + REQUIRE(delta_ms <= tolerance_wait); + REQUIRE(q.overrun_counter() == 1); +} + +TEST_CASE("bad_queue", "[mpmc_blocking_q]") { + size_t q_size = 0; + spdlog::details::mpmc_blocking_queue q(q_size); + q.enqueue_nowait(1); + REQUIRE(q.overrun_counter() == 1); + int i = 0; + REQUIRE(q.dequeue_for(i, milliseconds(0)) == false); +} + +TEST_CASE("empty_queue", "[mpmc_blocking_q]") { + size_t q_size = 10; + spdlog::details::mpmc_blocking_queue q(q_size); + int i = 0; + REQUIRE(q.dequeue_for(i, milliseconds(10)) == false); +} + +TEST_CASE("full_queue", "[mpmc_blocking_q]") { + size_t q_size = 100; + spdlog::details::mpmc_blocking_queue q(q_size); + for (int i = 0; i < static_cast(q_size); i++) { + q.enqueue(i + 0); // i+0 to force rvalue and avoid tidy warnings on the same time if we + // std::move(i) instead + } + + q.enqueue_nowait(123456); + REQUIRE(q.overrun_counter() == 1); + + for (int i = 1; i < static_cast(q_size); i++) { + int item = -1; + q.dequeue(item); + REQUIRE(item == i); + } + + // last item pushed has overridden the oldest. + int item = -1; + q.dequeue(item); + REQUIRE(item == 123456); +} diff --git a/inc/spdlog/tests/test_pattern_formatter.cpp b/inc/spdlog/tests/test_pattern_formatter.cpp new file mode 100644 index 0000000..350b973 --- /dev/null +++ b/inc/spdlog/tests/test_pattern_formatter.cpp @@ -0,0 +1,502 @@ +#include "includes.h" +#include "test_sink.h" + +using spdlog::memory_buf_t; +using spdlog::details::to_string_view; + +// log to str and return it +template +static std::string log_to_str(const std::string &msg, const Args &...args) { + std::ostringstream oss; + auto oss_sink = std::make_shared(oss); + spdlog::logger oss_logger("pattern_tester", oss_sink); + oss_logger.set_level(spdlog::level::info); + + oss_logger.set_formatter( + std::unique_ptr(new spdlog::pattern_formatter(args...))); + + oss_logger.info(msg); + return oss.str(); +} + +TEST_CASE("custom eol", "[pattern_formatter]") { + std::string msg = "Hello custom eol test"; + std::string eol = ";)"; + REQUIRE(log_to_str(msg, "%v", spdlog::pattern_time_type::local, ";)") == msg + eol); +} + +TEST_CASE("empty format", "[pattern_formatter]") { + REQUIRE(log_to_str("Some message", "", spdlog::pattern_time_type::local, "").empty()); +} + +TEST_CASE("empty format2", "[pattern_formatter]") { + REQUIRE(log_to_str("Some message", "", spdlog::pattern_time_type::local, "\n") == "\n"); +} + +TEST_CASE("level", "[pattern_formatter]") { + REQUIRE(log_to_str("Some message", "[%l] %v", spdlog::pattern_time_type::local, "\n") == + "[info] Some message\n"); +} + +TEST_CASE("short level", "[pattern_formatter]") { + REQUIRE(log_to_str("Some message", "[%L] %v", spdlog::pattern_time_type::local, "\n") == + "[I] Some message\n"); +} + +TEST_CASE("name", "[pattern_formatter]") { + REQUIRE(log_to_str("Some message", "[%n] %v", spdlog::pattern_time_type::local, "\n") == + "[pattern_tester] Some message\n"); +} + +TEST_CASE("date MM/DD/YY ", "[pattern_formatter]") { + auto now_tm = spdlog::details::os::localtime(); + std::stringstream oss; + oss << std::setfill('0') << std::setw(2) << now_tm.tm_mon + 1 << "/" << std::setw(2) + << now_tm.tm_mday << "/" << std::setw(2) << (now_tm.tm_year + 1900) % 1000 + << " Some message\n"; + REQUIRE(log_to_str("Some message", "%D %v", spdlog::pattern_time_type::local, "\n") == + oss.str()); +} + +TEST_CASE("color range test1", "[pattern_formatter]") { + auto formatter = std::make_shared( + "%^%v%$", spdlog::pattern_time_type::local, "\n"); + + memory_buf_t buf; + spdlog::fmt_lib::format_to(std::back_inserter(buf), "Hello"); + memory_buf_t formatted; + std::string logger_name = "test"; + spdlog::details::log_msg msg(logger_name, spdlog::level::info, + spdlog::string_view_t(buf.data(), buf.size())); + formatter->format(msg, formatted); + REQUIRE(msg.color_range_start == 0); + REQUIRE(msg.color_range_end == 5); + REQUIRE(log_to_str("hello", "%^%v%$", spdlog::pattern_time_type::local, "\n") == "hello\n"); +} + +TEST_CASE("color range test2", "[pattern_formatter]") { + auto formatter = + std::make_shared("%^%$", spdlog::pattern_time_type::local, "\n"); + std::string logger_name = "test"; + spdlog::details::log_msg msg(logger_name, spdlog::level::info, ""); + memory_buf_t formatted; + formatter->format(msg, formatted); + REQUIRE(msg.color_range_start == 0); + REQUIRE(msg.color_range_end == 0); + REQUIRE(log_to_str("", "%^%$", spdlog::pattern_time_type::local, "\n") == "\n"); +} + +TEST_CASE("color range test3", "[pattern_formatter]") { + auto formatter = std::make_shared("%^***%$"); + std::string logger_name = "test"; + spdlog::details::log_msg msg(logger_name, spdlog::level::info, "ignored"); + memory_buf_t formatted; + formatter->format(msg, formatted); + REQUIRE(msg.color_range_start == 0); + REQUIRE(msg.color_range_end == 3); +} + +TEST_CASE("color range test4", "[pattern_formatter]") { + auto formatter = std::make_shared( + "XX%^YYY%$", spdlog::pattern_time_type::local, "\n"); + std::string logger_name = "test"; + spdlog::details::log_msg msg(logger_name, spdlog::level::info, "ignored"); + + memory_buf_t formatted; + formatter->format(msg, formatted); + REQUIRE(msg.color_range_start == 2); + REQUIRE(msg.color_range_end == 5); + REQUIRE(log_to_str("ignored", "XX%^YYY%$", spdlog::pattern_time_type::local, "\n") == + "XXYYY\n"); +} + +TEST_CASE("color range test5", "[pattern_formatter]") { + auto formatter = std::make_shared("**%^"); + std::string logger_name = "test"; + spdlog::details::log_msg msg(logger_name, spdlog::level::info, "ignored"); + memory_buf_t formatted; + formatter->format(msg, formatted); + REQUIRE(msg.color_range_start == 2); + REQUIRE(msg.color_range_end == 0); +} + +TEST_CASE("color range test6", "[pattern_formatter]") { + auto formatter = std::make_shared("**%$"); + std::string logger_name = "test"; + spdlog::details::log_msg msg(logger_name, spdlog::level::info, "ignored"); + memory_buf_t formatted; + formatter->format(msg, formatted); + REQUIRE(msg.color_range_start == 0); + REQUIRE(msg.color_range_end == 2); +} + +// +// Test padding +// + +TEST_CASE("level_left_padded", "[pattern_formatter]") { + REQUIRE(log_to_str("Some message", "[%8l] %v", spdlog::pattern_time_type::local, "\n") == + "[ info] Some message\n"); + REQUIRE(log_to_str("Some message", "[%8!l] %v", spdlog::pattern_time_type::local, "\n") == + "[ info] Some message\n"); +} + +TEST_CASE("level_right_padded", "[pattern_formatter]") { + REQUIRE(log_to_str("Some message", "[%-8l] %v", spdlog::pattern_time_type::local, "\n") == + "[info ] Some message\n"); + REQUIRE(log_to_str("Some message", "[%-8!l] %v", spdlog::pattern_time_type::local, "\n") == + "[info ] Some message\n"); +} + +TEST_CASE("level_center_padded", "[pattern_formatter]") { + REQUIRE(log_to_str("Some message", "[%=8l] %v", spdlog::pattern_time_type::local, "\n") == + "[ info ] Some message\n"); + REQUIRE(log_to_str("Some message", "[%=8!l] %v", spdlog::pattern_time_type::local, "\n") == + "[ info ] Some message\n"); +} + +TEST_CASE("short level_left_padded", "[pattern_formatter]") { + REQUIRE(log_to_str("Some message", "[%3L] %v", spdlog::pattern_time_type::local, "\n") == + "[ I] Some message\n"); + REQUIRE(log_to_str("Some message", "[%3!L] %v", spdlog::pattern_time_type::local, "\n") == + "[ I] Some message\n"); +} + +TEST_CASE("short level_right_padded", "[pattern_formatter]") { + REQUIRE(log_to_str("Some message", "[%-3L] %v", spdlog::pattern_time_type::local, "\n") == + "[I ] Some message\n"); + REQUIRE(log_to_str("Some message", "[%-3!L] %v", spdlog::pattern_time_type::local, "\n") == + "[I ] Some message\n"); +} + +TEST_CASE("short level_center_padded", "[pattern_formatter]") { + REQUIRE(log_to_str("Some message", "[%=3L] %v", spdlog::pattern_time_type::local, "\n") == + "[ I ] Some message\n"); + REQUIRE(log_to_str("Some message", "[%=3!L] %v", spdlog::pattern_time_type::local, "\n") == + "[ I ] Some message\n"); +} + +TEST_CASE("left_padded_short", "[pattern_formatter]") { + REQUIRE(log_to_str("Some message", "[%3n] %v", spdlog::pattern_time_type::local, "\n") == + "[pattern_tester] Some message\n"); + REQUIRE(log_to_str("Some message", "[%3!n] %v", spdlog::pattern_time_type::local, "\n") == + "[pat] Some message\n"); +} + +TEST_CASE("right_padded_short", "[pattern_formatter]") { + REQUIRE(log_to_str("Some message", "[%-3n] %v", spdlog::pattern_time_type::local, "\n") == + "[pattern_tester] Some message\n"); + REQUIRE(log_to_str("Some message", "[%-3!n] %v", spdlog::pattern_time_type::local, "\n") == + "[pat] Some message\n"); +} + +TEST_CASE("center_padded_short", "[pattern_formatter]") { + REQUIRE(log_to_str("Some message", "[%=3n] %v", spdlog::pattern_time_type::local, "\n") == + "[pattern_tester] Some message\n"); + REQUIRE(log_to_str("Some message", "[%=3!n] %v", spdlog::pattern_time_type::local, "\n") == + "[pat] Some message\n"); +} + +TEST_CASE("left_padded_huge", "[pattern_formatter]") { + REQUIRE(log_to_str("Some message", "[%-300n] %v", spdlog::pattern_time_type::local, "\n") == + "[pattern_tester ] Some message\n"); + + REQUIRE(log_to_str("Some message", "[%-300!n] %v", spdlog::pattern_time_type::local, "\n") == + "[pattern_tester ] Some message\n"); +} + +TEST_CASE("left_padded_max", "[pattern_formatter]") { + REQUIRE(log_to_str("Some message", "[%-64n] %v", spdlog::pattern_time_type::local, "\n") == + "[pattern_tester ] Some message\n"); + + REQUIRE(log_to_str("Some message", "[%-64!n] %v", spdlog::pattern_time_type::local, "\n") == + "[pattern_tester ] Some message\n"); +} + +// Test padding + truncate flag + +TEST_CASE("paddinng_truncate", "[pattern_formatter]") { + REQUIRE(log_to_str("123456", "%6!v", spdlog::pattern_time_type::local, "\n") == "123456\n"); + REQUIRE(log_to_str("123456", "%5!v", spdlog::pattern_time_type::local, "\n") == "12345\n"); + REQUIRE(log_to_str("123456", "%7!v", spdlog::pattern_time_type::local, "\n") == " 123456\n"); + + REQUIRE(log_to_str("123456", "%-6!v", spdlog::pattern_time_type::local, "\n") == "123456\n"); + REQUIRE(log_to_str("123456", "%-5!v", spdlog::pattern_time_type::local, "\n") == "12345\n"); + REQUIRE(log_to_str("123456", "%-7!v", spdlog::pattern_time_type::local, "\n") == "123456 \n"); + + REQUIRE(log_to_str("123456", "%=6!v", spdlog::pattern_time_type::local, "\n") == "123456\n"); + REQUIRE(log_to_str("123456", "%=5!v", spdlog::pattern_time_type::local, "\n") == "12345\n"); + REQUIRE(log_to_str("123456", "%=7!v", spdlog::pattern_time_type::local, "\n") == "123456 \n"); + + REQUIRE(log_to_str("123456", "%0!v", spdlog::pattern_time_type::local, "\n") == "\n"); +} + +TEST_CASE("padding_truncate_funcname", "[pattern_formatter]") { + spdlog::sinks::test_sink_st test_sink; + + const char *pattern = "%v [%5!!]"; + auto formatter = std::unique_ptr(new spdlog::pattern_formatter(pattern)); + test_sink.set_formatter(std::move(formatter)); + + spdlog::details::log_msg msg1{spdlog::source_loc{"ignored", 1, "func"}, "test_logger", + spdlog::level::info, "message"}; + test_sink.log(msg1); + REQUIRE(test_sink.lines()[0] == "message [ func]"); + + spdlog::details::log_msg msg2{spdlog::source_loc{"ignored", 1, "function"}, "test_logger", + spdlog::level::info, "message"}; + test_sink.log(msg2); + REQUIRE(test_sink.lines()[1] == "message [funct]"); +} + +TEST_CASE("padding_funcname", "[pattern_formatter]") { + spdlog::sinks::test_sink_st test_sink; + + const char *pattern = "%v [%10!]"; + auto formatter = std::unique_ptr(new spdlog::pattern_formatter(pattern)); + test_sink.set_formatter(std::move(formatter)); + + spdlog::details::log_msg msg1{spdlog::source_loc{"ignored", 1, "func"}, "test_logger", + spdlog::level::info, "message"}; + test_sink.log(msg1); + REQUIRE(test_sink.lines()[0] == "message [ func]"); + + spdlog::details::log_msg msg2{spdlog::source_loc{"ignored", 1, "func567890123"}, "test_logger", + spdlog::level::info, "message"}; + test_sink.log(msg2); + REQUIRE(test_sink.lines()[1] == "message [func567890123]"); +} + +TEST_CASE("clone-default-formatter", "[pattern_formatter]") { + auto formatter_1 = std::make_shared(); + auto formatter_2 = formatter_1->clone(); + std::string logger_name = "test"; + spdlog::details::log_msg msg(logger_name, spdlog::level::info, "some message"); + + memory_buf_t formatted_1; + memory_buf_t formatted_2; + formatter_1->format(msg, formatted_1); + formatter_2->format(msg, formatted_2); + + REQUIRE(to_string_view(formatted_1) == to_string_view(formatted_2)); +} + +TEST_CASE("clone-default-formatter2", "[pattern_formatter]") { + auto formatter_1 = std::make_shared("%+"); + auto formatter_2 = formatter_1->clone(); + std::string logger_name = "test"; + spdlog::details::log_msg msg(logger_name, spdlog::level::info, "some message"); + + memory_buf_t formatted_1; + memory_buf_t formatted_2; + formatter_1->format(msg, formatted_1); + formatter_2->format(msg, formatted_2); + + REQUIRE(to_string_view(formatted_1) == to_string_view(formatted_2)); +} + +TEST_CASE("clone-formatter", "[pattern_formatter]") { + auto formatter_1 = std::make_shared("%D %X [%] [%n] %v"); + auto formatter_2 = formatter_1->clone(); + std::string logger_name = "test"; + spdlog::details::log_msg msg(logger_name, spdlog::level::info, "some message"); + + memory_buf_t formatted_1; + memory_buf_t formatted_2; + formatter_1->format(msg, formatted_1); + formatter_2->format(msg, formatted_2); + + REQUIRE(to_string_view(formatted_1) == to_string_view(formatted_2)); +} + +TEST_CASE("clone-formatter-2", "[pattern_formatter]") { + using spdlog::pattern_time_type; + auto formatter_1 = std::make_shared( + "%D %X [%] [%n] %v", pattern_time_type::utc, "xxxxxx\n"); + auto formatter_2 = formatter_1->clone(); + std::string logger_name = "test2"; + spdlog::details::log_msg msg(logger_name, spdlog::level::info, "some message"); + + memory_buf_t formatted_1; + memory_buf_t formatted_2; + formatter_1->format(msg, formatted_1); + formatter_2->format(msg, formatted_2); + + REQUIRE(to_string_view(formatted_1) == to_string_view(formatted_2)); +} + +class custom_test_flag : public spdlog::custom_flag_formatter { +public: + explicit custom_test_flag(std::string txt) + : some_txt{std::move(txt)} {} + + void format(const spdlog::details::log_msg &, + const std::tm &tm, + spdlog::memory_buf_t &dest) override { + if (some_txt == "throw_me") { + throw spdlog::spdlog_ex("custom_flag_exception_test"); + } else if (some_txt == "time") { + auto formatted = spdlog::fmt_lib::format("{:d}:{:02d}{:s}", tm.tm_hour % 12, tm.tm_min, + tm.tm_hour / 12 ? "PM" : "AM"); + dest.append(formatted.data(), formatted.data() + formatted.size()); + return; + } + some_txt = std::string(padinfo_.width_, ' ') + some_txt; + dest.append(some_txt.data(), some_txt.data() + some_txt.size()); + } + spdlog::details::padding_info get_padding_info() { return padinfo_; } + + std::string some_txt; + + std::unique_ptr clone() const override { + return spdlog::details::make_unique(some_txt); + } +}; +// test clone with custom flag formatters +TEST_CASE("clone-custom_formatter", "[pattern_formatter]") { + auto formatter_1 = std::make_shared(); + formatter_1->add_flag('t', "custom_output").set_pattern("[%n] [%t] %v"); + auto formatter_2 = formatter_1->clone(); + std::string logger_name = "logger-name"; + spdlog::details::log_msg msg(logger_name, spdlog::level::info, "some message"); + + memory_buf_t formatted_1; + memory_buf_t formatted_2; + formatter_1->format(msg, formatted_1); + formatter_2->format(msg, formatted_2); + + auto expected = spdlog::fmt_lib::format("[logger-name] [custom_output] some message{}", + spdlog::details::os::default_eol); + + REQUIRE(to_string_view(formatted_1) == expected); + REQUIRE(to_string_view(formatted_2) == expected); +} + +// +// Test source location formatting +// + +#ifdef _WIN32 +static const char *const test_path = "\\a\\b\\c/myfile.cpp"; +#else +static const char *const test_path = "/a/b//myfile.cpp"; +#endif + +TEST_CASE("short filename formatter-1", "[pattern_formatter]") { + spdlog::pattern_formatter formatter("%s", spdlog::pattern_time_type::local, ""); + memory_buf_t formatted; + std::string logger_name = "logger-name"; + spdlog::source_loc source_loc{test_path, 123, "some_func()"}; + spdlog::details::log_msg msg(source_loc, "logger-name", spdlog::level::info, "Hello"); + formatter.format(msg, formatted); + + REQUIRE(to_string_view(formatted) == "myfile.cpp"); +} + +TEST_CASE("short filename formatter-2", "[pattern_formatter]") { + spdlog::pattern_formatter formatter("%s:%#", spdlog::pattern_time_type::local, ""); + memory_buf_t formatted; + std::string logger_name = "logger-name"; + spdlog::source_loc source_loc{"myfile.cpp", 123, "some_func()"}; + spdlog::details::log_msg msg(source_loc, "logger-name", spdlog::level::info, "Hello"); + formatter.format(msg, formatted); + + REQUIRE(to_string_view(formatted) == "myfile.cpp:123"); +} + +TEST_CASE("short filename formatter-3", "[pattern_formatter]") { + spdlog::pattern_formatter formatter("%s %v", spdlog::pattern_time_type::local, ""); + memory_buf_t formatted; + std::string logger_name = "logger-name"; + spdlog::source_loc source_loc{"", 123, "some_func()"}; + spdlog::details::log_msg msg(source_loc, "logger-name", spdlog::level::info, "Hello"); + formatter.format(msg, formatted); + + REQUIRE(to_string_view(formatted) == " Hello"); +} + +TEST_CASE("full filename formatter", "[pattern_formatter]") { + spdlog::pattern_formatter formatter("%g", spdlog::pattern_time_type::local, ""); + memory_buf_t formatted; + std::string logger_name = "logger-name"; + spdlog::source_loc source_loc{test_path, 123, "some_func()"}; + spdlog::details::log_msg msg(source_loc, "logger-name", spdlog::level::info, "Hello"); + formatter.format(msg, formatted); + + REQUIRE(to_string_view(formatted) == test_path); +} + +TEST_CASE("custom flags", "[pattern_formatter]") { + auto formatter = std::make_shared(); + formatter->add_flag('t', "custom1") + .add_flag('u', "custom2") + .set_pattern("[%n] [%t] [%u] %v"); + + memory_buf_t formatted; + + spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info, + "some message"); + formatter->format(msg, formatted); + auto expected = spdlog::fmt_lib::format("[logger-name] [custom1] [custom2] some message{}", + spdlog::details::os::default_eol); + + REQUIRE(to_string_view(formatted) == expected); +} + +TEST_CASE("custom flags-padding", "[pattern_formatter]") { + auto formatter = std::make_shared(); + formatter->add_flag('t', "custom1") + .add_flag('u', "custom2") + .set_pattern("[%n] [%t] [%5u] %v"); + + memory_buf_t formatted; + + spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info, + "some message"); + formatter->format(msg, formatted); + auto expected = spdlog::fmt_lib::format("[logger-name] [custom1] [ custom2] some message{}", + spdlog::details::os::default_eol); + + REQUIRE(to_string_view(formatted) == expected); +} + +TEST_CASE("custom flags-exception", "[pattern_formatter]") { + auto formatter = std::make_shared(); + formatter->add_flag('t', "throw_me") + .add_flag('u', "custom2") + .set_pattern("[%n] [%t] [%u] %v"); + + memory_buf_t formatted; + spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info, + "some message"); + CHECK_THROWS_AS(formatter->format(msg, formatted), spdlog::spdlog_ex); +} + +TEST_CASE("override need_localtime", "[pattern_formatter]") { + auto formatter = + std::make_shared(spdlog::pattern_time_type::local, "\n"); + formatter->add_flag('t', "time").set_pattern("%t> %v"); + + { + memory_buf_t formatted; + spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info, + "some message"); + formatter->format(msg, formatted); + REQUIRE(to_string_view(formatted) == "0:00AM> some message\n"); + } + + { + formatter->need_localtime(); + + auto now_tm = spdlog::details::os::localtime(); + std::stringstream oss; + oss << (now_tm.tm_hour % 12) << ":" << std::setfill('0') << std::setw(2) << now_tm.tm_min + << (now_tm.tm_hour / 12 ? "PM" : "AM") << "> some message\n"; + + memory_buf_t formatted; + spdlog::details::log_msg msg(spdlog::source_loc{}, "logger-name", spdlog::level::info, + "some message"); + formatter->format(msg, formatted); + REQUIRE(to_string_view(formatted) == oss.str()); + } +} diff --git a/inc/spdlog/tests/test_registry.cpp b/inc/spdlog/tests/test_registry.cpp new file mode 100644 index 0000000..69ec8f5 --- /dev/null +++ b/inc/spdlog/tests/test_registry.cpp @@ -0,0 +1,112 @@ +#include "includes.h" + +static const char *const tested_logger_name = "null_logger"; +static const char *const tested_logger_name2 = "null_logger2"; + +#ifndef SPDLOG_NO_EXCEPTIONS +TEST_CASE("register_drop", "[registry]") { + spdlog::drop_all(); + spdlog::create(tested_logger_name); + REQUIRE(spdlog::get(tested_logger_name) != nullptr); + // Throw if registering existing name + REQUIRE_THROWS_AS(spdlog::create(tested_logger_name), + spdlog::spdlog_ex); +} + +TEST_CASE("explicit register", "[registry]") { + spdlog::drop_all(); + auto logger = std::make_shared(tested_logger_name, + std::make_shared()); + spdlog::register_logger(logger); + REQUIRE(spdlog::get(tested_logger_name) != nullptr); + // Throw if registering existing name + REQUIRE_THROWS_AS(spdlog::create(tested_logger_name), + spdlog::spdlog_ex); +} +#endif + +TEST_CASE("apply_all", "[registry]") { + spdlog::drop_all(); + auto logger = std::make_shared(tested_logger_name, + std::make_shared()); + spdlog::register_logger(logger); + auto logger2 = std::make_shared( + tested_logger_name2, std::make_shared()); + spdlog::register_logger(logger2); + + int counter = 0; + spdlog::apply_all([&counter](std::shared_ptr) { counter++; }); + REQUIRE(counter == 2); + + counter = 0; + spdlog::drop(tested_logger_name2); + spdlog::apply_all([&counter](std::shared_ptr l) { + REQUIRE(l->name() == tested_logger_name); + counter++; + }); + REQUIRE(counter == 1); +} + +TEST_CASE("drop", "[registry]") { + spdlog::drop_all(); + spdlog::create(tested_logger_name); + spdlog::drop(tested_logger_name); + REQUIRE_FALSE(spdlog::get(tested_logger_name)); +} + +TEST_CASE("drop-default", "[registry]") { + spdlog::set_default_logger(spdlog::null_logger_st(tested_logger_name)); + spdlog::drop(tested_logger_name); + REQUIRE_FALSE(spdlog::default_logger()); + REQUIRE_FALSE(spdlog::get(tested_logger_name)); +} + +TEST_CASE("drop_all", "[registry]") { + spdlog::drop_all(); + spdlog::create(tested_logger_name); + spdlog::create(tested_logger_name2); + spdlog::drop_all(); + REQUIRE_FALSE(spdlog::get(tested_logger_name)); + REQUIRE_FALSE(spdlog::get(tested_logger_name2)); + REQUIRE_FALSE(spdlog::default_logger()); +} + +TEST_CASE("drop non existing", "[registry]") { + spdlog::drop_all(); + spdlog::create(tested_logger_name); + spdlog::drop("some_name"); + REQUIRE_FALSE(spdlog::get("some_name")); + REQUIRE(spdlog::get(tested_logger_name)); + spdlog::drop_all(); +} + +TEST_CASE("default logger", "[registry]") { + spdlog::drop_all(); + spdlog::set_default_logger(spdlog::null_logger_st(tested_logger_name)); + REQUIRE(spdlog::get(tested_logger_name) == spdlog::default_logger()); + spdlog::drop_all(); +} + +TEST_CASE("set_default_logger(nullptr)", "[registry]") { + spdlog::set_default_logger(nullptr); + REQUIRE_FALSE(spdlog::default_logger()); +} + +TEST_CASE("disable automatic registration", "[registry]") { + // set some global parameters + spdlog::level::level_enum log_level = spdlog::level::level_enum::warn; + spdlog::set_level(log_level); + // but disable automatic registration + spdlog::set_automatic_registration(false); + auto logger1 = spdlog::create( + tested_logger_name, SPDLOG_FILENAME_T("filename"), 11, 59); + auto logger2 = spdlog::create_async(tested_logger_name2); + // loggers should not be part of the registry + REQUIRE_FALSE(spdlog::get(tested_logger_name)); + REQUIRE_FALSE(spdlog::get(tested_logger_name2)); + // but make sure they are still initialized according to global defaults + REQUIRE(logger1->level() == log_level); + REQUIRE(logger2->level() == log_level); + spdlog::set_level(spdlog::level::info); + spdlog::set_automatic_registration(true); +} diff --git a/inc/spdlog/tests/test_sink.h b/inc/spdlog/tests/test_sink.h new file mode 100644 index 0000000..529d86d --- /dev/null +++ b/inc/spdlog/tests/test_sink.h @@ -0,0 +1,69 @@ +// +// Copyright(c) 2018 Gabi Melman. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) +// + +#pragma once + +#include "spdlog/details/null_mutex.h" +#include "spdlog/sinks/base_sink.h" +#include "spdlog/fmt/fmt.h" +#include +#include +#include + +namespace spdlog { +namespace sinks { + +template +class test_sink : public base_sink { + const size_t lines_to_save = 100; + +public: + size_t msg_counter() { + std::lock_guard lock(base_sink::mutex_); + return msg_counter_; + } + + size_t flush_counter() { + std::lock_guard lock(base_sink::mutex_); + return flush_counter_; + } + + void set_delay(std::chrono::milliseconds delay) { + std::lock_guard lock(base_sink::mutex_); + delay_ = delay; + } + + // return last output without the eol + std::vector lines() { + std::lock_guard lock(base_sink::mutex_); + return lines_; + } + +protected: + void sink_it_(const details::log_msg &msg) override { + memory_buf_t formatted; + base_sink::formatter_->format(msg, formatted); + // save the line without the eol + auto eol_len = strlen(details::os::default_eol); + if (lines_.size() < lines_to_save) { + lines_.emplace_back(formatted.begin(), formatted.end() - eol_len); + } + msg_counter_++; + std::this_thread::sleep_for(delay_); + } + + void flush_() override { flush_counter_++; } + + size_t msg_counter_{0}; + size_t flush_counter_{0}; + std::chrono::milliseconds delay_{std::chrono::milliseconds::zero()}; + std::vector lines_; +}; + +using test_sink_mt = test_sink; +using test_sink_st = test_sink; + +} // namespace sinks +} // namespace spdlog diff --git a/inc/spdlog/tests/test_stdout_api.cpp b/inc/spdlog/tests/test_stdout_api.cpp new file mode 100644 index 0000000..67659b8 --- /dev/null +++ b/inc/spdlog/tests/test_stdout_api.cpp @@ -0,0 +1,90 @@ +/* + * This content is released under the MIT License as specified in + * https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE + */ +#include "includes.h" +#include "spdlog/sinks/stdout_sinks.h" +#include "spdlog/sinks/stdout_color_sinks.h" +TEST_CASE("stdout_st", "[stdout]") { + auto l = spdlog::stdout_logger_st("test"); + l->set_pattern("%+"); + l->set_level(spdlog::level::trace); + l->trace("Test stdout_st"); + spdlog::drop_all(); +} + +TEST_CASE("stdout_mt", "[stdout]") { + auto l = spdlog::stdout_logger_mt("test"); + l->set_pattern("%+"); + l->set_level(spdlog::level::debug); + l->debug("Test stdout_mt"); + spdlog::drop_all(); +} + +TEST_CASE("stderr_st", "[stderr]") { + auto l = spdlog::stderr_logger_st("test"); + l->set_pattern("%+"); + l->info("Test stderr_st"); + spdlog::drop_all(); +} + +TEST_CASE("stderr_mt", "[stderr]") { + auto l = spdlog::stderr_logger_mt("test"); + l->set_pattern("%+"); + l->info("Test stderr_mt"); + l->warn("Test stderr_mt"); + l->error("Test stderr_mt"); + l->critical("Test stderr_mt"); + spdlog::drop_all(); +} + +// color loggers +TEST_CASE("stdout_color_st", "[stdout]") { + auto l = spdlog::stdout_color_st("test"); + l->set_pattern("%+"); + l->info("Test stdout_color_st"); + spdlog::drop_all(); +} + +TEST_CASE("stdout_color_mt", "[stdout]") { + auto l = spdlog::stdout_color_mt("test"); + l->set_pattern("%+"); + l->set_level(spdlog::level::trace); + l->trace("Test stdout_color_mt"); + spdlog::drop_all(); +} + +TEST_CASE("stderr_color_st", "[stderr]") { + auto l = spdlog::stderr_color_st("test"); + l->set_pattern("%+"); + l->set_level(spdlog::level::debug); + l->debug("Test stderr_color_st"); + spdlog::drop_all(); +} + +TEST_CASE("stderr_color_mt", "[stderr]") { + auto l = spdlog::stderr_color_mt("test"); + l->set_pattern("%+"); + l->info("Test stderr_color_mt"); + l->warn("Test stderr_color_mt"); + l->error("Test stderr_color_mt"); + l->critical("Test stderr_color_mt"); + spdlog::drop_all(); +} + +#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT + +TEST_CASE("wchar_api", "[stdout]") { + auto l = spdlog::stdout_logger_st("wchar_logger"); + l->set_pattern("%+"); + l->set_level(spdlog::level::trace); + l->trace(L"Test wchar_api"); + l->trace(L"Test wchar_api {}", L"param"); + l->trace(L"Test wchar_api {}", 1); + l->trace(L"Test wchar_api {}", std::wstring{L"wstring param"}); + l->trace(std::wstring{L"Test wchar_api wstring"}); + SPDLOG_LOGGER_DEBUG(l, L"Test SPDLOG_LOGGER_DEBUG {}", L"param"); + spdlog::drop_all(); +} + +#endif diff --git a/inc/spdlog/tests/test_stopwatch.cpp b/inc/spdlog/tests/test_stopwatch.cpp new file mode 100644 index 0000000..b2aa246 --- /dev/null +++ b/inc/spdlog/tests/test_stopwatch.cpp @@ -0,0 +1,42 @@ +#include "includes.h" +#include "test_sink.h" +#include "spdlog/stopwatch.h" + +TEST_CASE("stopwatch1", "[stopwatch]") { + using std::chrono::milliseconds; + using clock = std::chrono::steady_clock; + milliseconds wait_ms(200); + milliseconds tolerance_ms(250); + auto start = clock::now(); + spdlog::stopwatch sw; + std::this_thread::sleep_for(wait_ms); + auto stop = clock::now(); + auto diff_ms = std::chrono::duration_cast(stop - start); + REQUIRE(sw.elapsed() >= diff_ms); + REQUIRE(sw.elapsed() <= diff_ms + tolerance_ms); +} + +TEST_CASE("stopwatch2", "[stopwatch]") { + using spdlog::sinks::test_sink_st; + using std::chrono::duration_cast; + using std::chrono::milliseconds; + using clock = std::chrono::steady_clock; + + clock::duration wait_duration(milliseconds(200)); + clock::duration tolerance_duration(milliseconds(250)); + + auto test_sink = std::make_shared(); + + auto start = clock::now(); + spdlog::stopwatch sw; + spdlog::logger logger("test-stopwatch", test_sink); + logger.set_pattern("%v"); + std::this_thread::sleep_for(wait_duration); + auto stop = clock::now(); + logger.info("{}", sw); + auto val = std::stod(test_sink->lines()[0]); + auto diff_duration = duration_cast>(stop - start); + + REQUIRE(val >= (diff_duration).count() - 0.001); + REQUIRE(val <= (diff_duration + tolerance_duration).count()); +} diff --git a/inc/spdlog/tests/test_systemd.cpp b/inc/spdlog/tests/test_systemd.cpp new file mode 100644 index 0000000..e363657 --- /dev/null +++ b/inc/spdlog/tests/test_systemd.cpp @@ -0,0 +1,14 @@ +#include "includes.h" +#include "spdlog/sinks/systemd_sink.h" + +TEST_CASE("systemd", "[all]") { + auto systemd_sink = std::make_shared(); + spdlog::logger logger("spdlog_systemd_test", systemd_sink); + logger.set_level(spdlog::level::trace); + logger.trace("test spdlog trace"); + logger.debug("test spdlog debug"); + SPDLOG_LOGGER_INFO((&logger), "test spdlog info"); + SPDLOG_LOGGER_WARN((&logger), "test spdlog warn"); + SPDLOG_LOGGER_ERROR((&logger), "test spdlog error"); + SPDLOG_LOGGER_CRITICAL((&logger), "test spdlog critical"); +} diff --git a/inc/spdlog/tests/test_time_point.cpp b/inc/spdlog/tests/test_time_point.cpp new file mode 100644 index 0000000..b7b1b23 --- /dev/null +++ b/inc/spdlog/tests/test_time_point.cpp @@ -0,0 +1,35 @@ +#include "includes.h" +#include "test_sink.h" +#include "spdlog/async.h" + +TEST_CASE("time_point1", "[time_point log_msg]") { + std::shared_ptr test_sink(new spdlog::sinks::test_sink_st); + spdlog::logger logger("test-time_point", test_sink); + + spdlog::source_loc source{}; + std::chrono::system_clock::time_point tp{std::chrono::system_clock::now()}; + test_sink->set_pattern("%T.%F"); // interested in the time_point + + // all the following should have the same time + test_sink->set_delay(std::chrono::milliseconds(10)); + for (int i = 0; i < 5; i++) { + spdlog::details::log_msg msg{tp, source, "test_logger", spdlog::level::info, "message"}; + test_sink->log(msg); + } + + logger.log(tp, source, spdlog::level::info, "formatted message"); + logger.log(tp, source, spdlog::level::info, "formatted message"); + logger.log(tp, source, spdlog::level::info, "formatted message"); + logger.log(tp, source, spdlog::level::info, "formatted message"); + logger.log(source, spdlog::level::info, + "formatted message"); // last line has different time_point + + // now the real test... that the times are the same. + std::vector lines = test_sink->lines(); + REQUIRE(lines[0] == lines[1]); + REQUIRE(lines[2] == lines[3]); + REQUIRE(lines[4] == lines[5]); + REQUIRE(lines[6] == lines[7]); + REQUIRE(lines[8] != lines[9]); + spdlog::drop_all(); +} diff --git a/inc/spdlog/tests/utils.cpp b/inc/spdlog/tests/utils.cpp new file mode 100644 index 0000000..1fa2617 --- /dev/null +++ b/inc/spdlog/tests/utils.cpp @@ -0,0 +1,103 @@ +#include "includes.h" + +#ifdef _WIN32 + #include +#else + #include + #include +#endif + +void prepare_logdir() { + spdlog::drop_all(); +#ifdef _WIN32 + system("rmdir /S /Q test_logs"); +#else + auto rv = system("rm -rf test_logs"); + if (rv != 0) { + throw std::runtime_error("Failed to rm -rf test_logs"); + } +#endif +} + +std::string file_contents(const std::string &filename) { + std::ifstream ifs(filename, std::ios_base::binary); + if (!ifs) { + throw std::runtime_error("Failed open file "); + } + return std::string((std::istreambuf_iterator(ifs)), (std::istreambuf_iterator())); +} + +std::size_t count_lines(const std::string &filename) { + std::ifstream ifs(filename); + if (!ifs) { + throw std::runtime_error("Failed open file "); + } + + std::string line; + size_t counter = 0; + while (std::getline(ifs, line)) counter++; + return counter; +} + +void require_message_count(const std::string &filename, const std::size_t messages) { + if (strlen(spdlog::details::os::default_eol) == 0) { + REQUIRE(count_lines(filename) == 1); + } else { + REQUIRE(count_lines(filename) == messages); + } +} + +std::size_t get_filesize(const std::string &filename) { + std::ifstream ifs(filename, std::ifstream::ate | std::ifstream::binary); + if (!ifs) { + throw std::runtime_error("Failed open file "); + } + + return static_cast(ifs.tellg()); +} + +// source: https://stackoverflow.com/a/2072890/192001 +bool ends_with(std::string const &value, std::string const &ending) { + if (ending.size() > value.size()) { + return false; + } + return std::equal(ending.rbegin(), ending.rend(), value.rbegin()); +} + +#ifdef _WIN32 +// Based on: https://stackoverflow.com/a/37416569/192001 +std::size_t count_files(const std::string &folder) { + size_t counter = 0; + WIN32_FIND_DATAA ffd; + + // Start iterating over the files in the folder directory. + HANDLE hFind = ::FindFirstFileA((folder + "\\*").c_str(), &ffd); + if (hFind != INVALID_HANDLE_VALUE) { + do // Managed to locate and create an handle to that folder. + { + if (ffd.cFileName[0] != '.') counter++; + } while (::FindNextFileA(hFind, &ffd) != 0); + ::FindClose(hFind); + } else { + throw std::runtime_error("Failed open folder " + folder); + } + + return counter; +} +#else +// Based on: https://stackoverflow.com/a/2802255/192001 +std::size_t count_files(const std::string &folder) { + size_t counter = 0; + DIR *dp = opendir(folder.c_str()); + if (dp == nullptr) { + throw std::runtime_error("Failed open folder " + folder); + } + + struct dirent *ep = nullptr; + while ((ep = readdir(dp)) != nullptr) { + if (ep->d_name[0] != '.') counter++; + } + (void)closedir(dp); + return counter; +} +#endif diff --git a/inc/spdlog/tests/utils.h b/inc/spdlog/tests/utils.h new file mode 100644 index 0000000..53c09b4 --- /dev/null +++ b/inc/spdlog/tests/utils.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +std::size_t count_files(const std::string &folder); + +void prepare_logdir(); + +std::string file_contents(const std::string &filename); + +std::size_t count_lines(const std::string &filename); + +void require_message_count(const std::string &filename, const std::size_t messages); + +std::size_t get_filesize(const std::string &filename); + +bool ends_with(std::string const &value, std::string const &ending); \ No newline at end of file diff --git a/lib/debug/spdlog.lib b/lib/debug/spdlog.lib deleted file mode 100644 index 6ff51ac..0000000 Binary files a/lib/debug/spdlog.lib and /dev/null differ diff --git a/lib/release/spdlog.lib b/lib/release/spdlog.lib index bbfa6c9..81784d9 100644 Binary files a/lib/release/spdlog.lib and b/lib/release/spdlog.lib differ diff --git a/wxhelper.vcxproj b/wxhelper.vcxproj index 5ecd629..cba8448 100644 --- a/wxhelper.vcxproj +++ b/wxhelper.vcxproj @@ -111,15 +111,11 @@ $(SolutionDir)\$(Configuration)\ $(Platform)\$(Configuration)\ true - $(ProjectDir)\inc;$(ExternalIncludePath) - $(ProjectDir)\lib\debug;$(LibraryPath) $(SolutionDir)\$(Configuration)\ $(Platform)\$(Configuration)\ true - $(ProjectDir)\inc;$(IncludePath) - $(ProjectDir)\lib\release;$(LibraryPath) @@ -135,15 +131,17 @@ Level3 ProgramDatabase Disabled - Default + stdcpp17 + inc;inc\spdlog\include;%(AdditionalIncludeDirectories) true Console MachineX64 - base.lib;base64.lib;detours.lib;lz4.lib;mongoose.lib;spdlog.lib;tinyxml2.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;%(AdditionalDependencies) + base.lib;base64.lib;detours.lib;lz4.lib;mongoose.lib;spdlogd.lib;tinyxml2.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;%(AdditionalDependencies) %(IgnoreSpecificDefaultLibraries) RequireAdministrator + lib\debug;%(AdditionalLibraryDirectories) @@ -158,6 +156,8 @@ Level3 ProgramDatabase + stdcpp17 + inc\spdlog\include;inc;%(AdditionalIncludeDirectories) true @@ -167,6 +167,7 @@ MachineX64 base.lib;base64.lib;detours.lib;lz4.lib;mongoose.lib;spdlog.lib;tinyxml2.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;comdlg32.lib;advapi32.lib;%(AdditionalDependencies) %(IgnoreSpecificDefaultLibraries) + lib\release;%(AdditionalLibraryDirectories)