Просмотр исходного кода

first pass at adding logging framework

Jeremy Borgman 4 лет назад
Родитель
Сommit
76ad9e5a6f

+ 1 - 0
CMakeLists.txt

@@ -49,6 +49,7 @@ set(QAPPLICATION_CLASS
     QApplication
     CACHE STRING "Inheritance class for SingleApplication")
 add_subdirectory(external/singleapplication)
+add_subdirectory(external/spdlog)
 add_subdirectory(src)
 
 # CPack

+ 83 - 0
external/spdlog/.gitignore

@@ -0,0 +1,83 @@
+# Auto generated files
+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
+
+# 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/*
+
+# idea
+.idea/
+cmake-build-*/
+*.db
+*.ipch
+*.filters
+*.db-wal
+*.opendb
+*.db-shm
+*.vcxproj
+*.tcl
+*.user
+*.sln

+ 293 - 0
external/spdlog/CMakeLists.txt

@@ -0,0 +1,293 @@
+# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+cmake_minimum_required(VERSION 3.10)
+
+# ---------------------------------------------------------------------------------------
+# 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)
+    set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose Release or Debug" FORCE)
+endif()
+
+# ---------------------------------------------------------------------------------------
+# Compiler config
+# ---------------------------------------------------------------------------------------
+if(NOT CMAKE_CXX_STANDARD)
+    set(CMAKE_CXX_STANDARD 11)
+    set(CMAKE_CXX_STANDARD_REQUIRED ON)
+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)
+
+# example options
+option(SPDLOG_BUILD_EXAMPLE "Build example" ${SPDLOG_MASTER_PROJECT})
+option(SPDLOG_BUILD_EXAMPLE_HO "Build header only example" OFF)
+
+# testing options
+option(SPDLOG_BUILD_TESTS "Build tests" OFF)
+option(SPDLOG_BUILD_TESTS_HO "Build tests using the header only version" OFF)
+
+# bench options
+option(SPDLOG_BUILD_BENCH "Build benchmarks (Requires https://github.com/google/benchmark.git to be installed)" OFF)
+
+# sanitizer options
+option(SPDLOG_SANITIZE_ADDRESS "Enable address sanitizer in tests" OFF)
+
+# warning options
+option(SPDLOG_BUILD_WARNINGS "Enable compiler warnings" OFF)
+
+# install options
+option(SPDLOG_INSTALL "Generate the install target" ${SPDLOG_MASTER_PROJECT})
+option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF)
+option(SPDLOG_FMT_EXTERNAL_HO "Use external fmt header-only library instead of bundled" OFF)
+option(SPDLOG_NO_EXCEPTIONS "Compile with -fno-exceptions. Call abort() on any spdlog exceptions" OFF)
+
+if(SPDLOG_FMT_EXTERNAL AND SPDLOG_FMT_EXTERNAL_HO)
+    message(FATAL_ERROR "SPDLOG_FMT_EXTERNAL and SPDLOG_FMT_EXTERNAL_HO are mutually exclusive")
+endif()
+
+# misc tweakme options
+if(WIN32)
+    option(SPDLOG_WCHAR_SUPPORT "Support wchar api" OFF)
+    option(SPDLOG_WCHAR_FILENAMES "Support wchar filenames" OFF)
+endif()
+if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+    option(SPDLOG_CLOCK_COARSE
+           "Use the much faster (but much less accurate) CLOCK_REALTIME_COARSE instead of the regular clock," OFF)
+endif()
+
+option(SPDLOG_PREVENT_CHILD_FD "Prevent from child processes to inherit log file descriptors" OFF)
+option(SPDLOG_NO_THREAD_ID "prevent spdlog from querying the thread id on each log call if thread id is not needed" OFF)
+option(SPDLOG_NO_TLS "prevent spdlog from using thread local storage" OFF)
+option(
+    SPDLOG_NO_ATOMIC_LEVELS
+    "prevent spdlog from using of std::atomic log levels (use only if your code never modifies log levels concurrently"
+    OFF)
+option(SPDLOG_DISABLE_DEFAULT_LOGGER "Disable default logger creation" OFF)
+
+# clang-tidy
+if(${CMAKE_VERSION} VERSION_GREATER "3.5")
+    option(SPDLOG_TIDY "run clang-tidy" OFF)
+endif()
+
+if(SPDLOG_TIDY)
+    set(CMAKE_CXX_CLANG_TIDY "clang-tidy")
+    set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+    message(STATUS "Enabled clang-tidy")
+endif()
+
+find_package(Threads REQUIRED)
+message(STATUS "Build type: " ${CMAKE_BUILD_TYPE})
+# ---------------------------------------------------------------------------------------
+# Static/Shared library (shared not supported in windows yet)
+# ---------------------------------------------------------------------------------------
+set(SPDLOG_SRCS src/spdlog.cpp src/stdout_sinks.cpp src/color_sinks.cpp src/file_sinks.cpp src/async.cpp src/cfg.cpp)
+
+if(NOT SPDLOG_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
+    list(APPEND SPDLOG_SRCS src/fmt.cpp)
+endif()
+
+if(SPDLOG_BUILD_SHARED)
+    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_FMT_EXTERNAL AND NOT SPDLOG_FMT_EXTERNAL_HO)
+        target_compile_definitions(spdlog PRIVATE FMT_EXPORT PUBLIC FMT_SHARED)
+    endif()
+else()
+    add_library(spdlog STATIC ${SPDLOG_SRCS} ${SPDLOG_ALL_HEADERS})
+endif()
+
+add_library(spdlog::spdlog ALIAS spdlog)
+
+target_compile_definitions(spdlog PUBLIC SPDLOG_COMPILED_LIB)
+target_include_directories(spdlog PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
+                                         "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
+target_link_libraries(spdlog PUBLIC Threads::Threads)
+spdlog_enable_warnings(spdlog)
+
+set_target_properties(spdlog PROPERTIES VERSION ${SPDLOG_VERSION} SOVERSION ${SPDLOG_VERSION_MAJOR})
+set_target_properties(spdlog PROPERTIES DEBUG_POSTFIX d)
+
+if(COMMAND target_precompile_headers AND SPDLOG_ENABLE_PCH)
+    configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/pch.h.in ${PROJECT_BINARY_DIR}/spdlog_pch.h @ONLY)
+    target_precompile_headers(spdlog PRIVATE ${PROJECT_BINARY_DIR}/spdlog_pch.h)
+endif()
+
+# ---------------------------------------------------------------------------------------
+# Header only version
+# ---------------------------------------------------------------------------------------
+add_library(spdlog_header_only INTERFACE)
+add_library(spdlog::spdlog_header_only ALIAS spdlog_header_only)
+
+target_include_directories(spdlog_header_only INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include>"
+                                                        "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>")
+target_link_libraries(spdlog_header_only INTERFACE Threads::Threads)
+
+# ---------------------------------------------------------------------------------------
+# Use fmt package if using external fmt
+# ---------------------------------------------------------------------------------------
+if(SPDLOG_FMT_EXTERNAL OR SPDLOG_FMT_EXTERNAL_HO)
+    if(NOT TARGET fmt::fmt)
+        find_package(fmt 5.3.0 CONFIG REQUIRED)
+    endif()
+    target_compile_definitions(spdlog PUBLIC SPDLOG_FMT_EXTERNAL)
+    target_compile_definitions(spdlog_header_only INTERFACE SPDLOG_FMT_EXTERNAL)
+
+    # use external fmt-header-nly
+    if(SPDLOG_FMT_EXTERNAL_HO)
+        target_link_libraries(spdlog PUBLIC fmt::fmt-header-only)
+        target_link_libraries(spdlog_header_only INTERFACE fmt::fmt-header-only)
+    else() # use external compile fmt
+        target_link_libraries(spdlog PUBLIC fmt::fmt)
+        target_link_libraries(spdlog_header_only INTERFACE fmt::fmt)
+    endif()
+
+    set(PKG_CONFIG_REQUIRES fmt) # add dependency to pkg-config
+endif()
+
+# ---------------------------------------------------------------------------------------
+# Misc definitions according to tweak options
+# ---------------------------------------------------------------------------------------
+set(SPDLOG_WCHAR_TO_UTF8_SUPPORT ${SPDLOG_WCHAR_SUPPORT})
+foreach(
+    SPDLOG_OPTION
+    SPDLOG_WCHAR_TO_UTF8_SUPPORT
+    SPDLOG_WCHAR_FILENAMES
+    SPDLOG_NO_EXCEPTIONS
+    SPDLOG_CLOCK_COARSE
+    SPDLOG_PREVENT_CHILD_FD
+    SPDLOG_NO_THREAD_ID
+    SPDLOG_NO_TLS
+    SPDLOG_NO_ATOMIC_LEVELS
+    SPDLOG_DISABLE_DEFAULT_LOGGER)
+    if(${SPDLOG_OPTION})
+        target_compile_definitions(spdlog PUBLIC ${SPDLOG_OPTION})
+        target_compile_definitions(spdlog_header_only INTERFACE ${SPDLOG_OPTION})
+    endif()
+endforeach()
+
+if(SPDLOG_NO_EXCEPTIONS AND NOT MSVC)
+    target_compile_options(spdlog PRIVATE -fno-exceptions)
+endif()
+
+# ---------------------------------------------------------------------------------------
+# Build binaries
+# ---------------------------------------------------------------------------------------
+if(SPDLOG_BUILD_EXAMPLE OR SPDLOG_BUILD_EXAMPLE_HO OR SPDLOG_BUILD_ALL)
+    message(STATUS "Generating example(s)")
+    add_subdirectory(example)
+    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_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
+    # ---------------------------------------------------------------------------------------
+    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
+    # ---------------------------------------------------------------------------------------
+    install(EXPORT spdlog DESTINATION ${export_dest_dir} NAMESPACE spdlog:: FILE ${config_targets_file})
+
+    include(CMakePackageConfigHelpers)
+    configure_file("${project_config_in}" "${project_config_out}" @ONLY)
+
+    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()

+ 24 - 0
external/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 src/spdlog.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
+
+
+
+

+ 26 - 0
external/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
+

+ 433 - 0
external/spdlog/README.md

@@ -0,0 +1,433 @@
+# spdlog
+
+Very fast, header-only/compiled, C++ logging library. [![Build Status](https://travis-ci.org/gabime/spdlog.svg?branch=v1.x)](https://travis-ci.org/gabime/spdlog)&nbsp; [![Build status](https://ci.appveyor.com/api/projects/status/d2jnxclg20vd0o50?svg=true)](https://ci.appveyor.com/project/gabime/spdlog) [![Release](https://img.shields.io/github/release/gabime/spdlog.svg)](https://github.com/gabime/spdlog/releases/latest)
+
+## Install 
+#### Header only version
+Copy the source [folder](https://github.com/gabime/spdlog/tree/v1.x/include/spdlog) to your build tree and use a C++11 compiler.
+
+#### Static lib version (recommended - much faster compile times)
+```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:
+* Homebrew: `brew install spdlog`
+* MacPorts: `sudo port install spdlog`
+* FreeBSD:  `cd /usr/ports/devel/spdlog/ && make install clean`
+* Fedora: `dnf install spdlog`
+* Gentoo: `emerge dev-libs/spdlog`
+* Arch Linux: `pacman -S spdlog`
+* vcpkg: `vcpkg install spdlog`
+* conan: `spdlog/[>=1.4.1]`
+* conda: `conda install -c conda-forge spdlog`
+
+
+## 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 debugger (```OutputDebugString(..)```)
+    * Easily extendable with custom log targets  (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
+* Log filtering - log levels can be modified in runtime as well as in compile time.
+* Support for loading log levels from argv or from environment var.
+* [Backtrace](#backtrace-support) support - store debug messages in a ring buffer and display later on demand.
+ 
+## Usage samples
+
+#### Basic usage
+```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 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 5mb size max and 3 rotated files
+    auto max_size = 1048576 * 5;
+    auto max_files = 3;
+    auto logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", max_size, max_files);
+}
+```
+
+---
+#### Daily files
+```c++
+
+#include "spdlog/sinks/daily_file_sink.h"
+void daily_example()
+{
+    // Create a daily logger - a new file is created every day on 2:30am
+    auto logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
+}
+
+```
+
+---
+#### Backtrace support
+```c++
+// Loggers can store in a ring buffer all messages (including debug/trace) and display later on demand.
+// When needed, call dump_backtrace() to see them
+
+spdlog::enable_backtrace(32); // Store the latest 32 messages in a buffer. Older messages will be dropped.
+// or my_logger->enable_backtrace(32)..
+for(int i = 0; i < 100; i++)
+{
+  spdlog::debug("Backtrace message {}", i); // not logged yet..
+}
+// e.g. if some error happened:
+spdlog::dump_backtrace(); // log them now! show the last 32 messages
+
+// or my_logger->dump_backtrace(32)..
+```
+
+---
+#### Periodic flush
+```c++
+// periodically flush all *registered* loggers every 3 seconds:
+// warning: only use if all your loggers are thread safe ("_mt" loggers)
+spdlog::flush_every(std::chrono::seconds(3));
+
+```
+
+---
+#### Stopwatch
+```c++
+// Stopwatch support for spdlog
+#include "spdlog/stopwatch.h"
+void stopwatch_example()
+{
+    spdlog::stopwatch sw;    
+    spdlog::debug("Elapsed {}", sw);
+    spdlog::debug("Elapsed {:.3}", sw);       
+}
+
+```
+
+---
+#### Log binary data in hex
+```c++
+// many types of std::container<char> types can be used.
+// ranges are supported too.
+// format flags:
+// {:X} - print in uppercase.
+// {:s} - don't separate each byte with space.
+// {:p} - don't print the position on each line start.
+// {:n} - don't split the output to lines.
+// {:a} - show ASCII if :n is not set.
+
+#include "spdlog/fmt/bin_to_hex.h"
+
+void binary_example()
+{
+    auto console = spdlog::get("console");
+    std::array<char, 80> buf;
+    console->info("Binary example: {}", spdlog::to_hex(buf));
+    console->info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10));
+    // more examples:
+    // logger->info("uppercase: {:X}", spdlog::to_hex(buf));
+    // logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf));
+    // logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf));
+}
+
+```
+
+---
+#### Logger with multi sinks - each with different format and log level
+```c++
+
+// create logger with 2 targets with different log levels and formats.
+// the console will show only warnings or errors, while the file will log all.
+void multi_sink_example()
+{
+    auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
+    console_sink->set_level(spdlog::level::warn);
+    console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v");
+
+    auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/multisink.txt", true);
+    file_sink->set_level(spdlog::level::trace);
+
+    spdlog::logger logger("multi_sink", {console_sink, file_sink});
+    logger.set_level(spdlog::level::debug);
+    logger.warn("this should appear in both console and file");
+    logger.info("this message should not appear in the console, only in the file");
+}
+```
+
+---
+#### Asynchronous logging
+```c++
+#include "spdlog/async.h"
+#include "spdlog/sinks/basic_file_sink.h"
+void async_example()
+{
+    // 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<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
+    // alternatively:
+    // auto async_file = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("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<spdlog::sinks::stdout_color_sink_mt >();
+    auto rotating_sink = std::make_shared<spdlog::sinks::rotating_file_sink_mt>("mylog.txt", 1024*1024*10, 3);
+    std::vector<spdlog::sink_ptr> sinks {stdout_sink, rotating_sink};
+    auto logger = std::make_shared<spdlog::async_logger>("loggername", sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block);
+    spdlog::register_logger(logger);
+}
+```
+ 
+---
+#### User defined types
+```c++
+// user defined types logging by implementing operator<<
+#include "spdlog/fmt/ostr.h" // must be included
+struct my_type
+{
+    int i;
+    template<typename OStream>
+    friend OStream &operator<<(OStream &os, const my_type &c)
+    {
+        return os << "[my_type i=" << c.i << "]";
+    }
+};
+
+void user_defined_example()
+{
+    spdlog::get("console")->info("user defined type: {}", my_type{14});
+}
+
+```
+
+---
+#### User defined flags in the log pattern
+```c++ 
+// Log patterns can contain custom flags.
+// the following example will add new flag '%*' - which will be bound to a <my_formatter_flag> instance.
+#include "spdlog/pattern_formatter.h"
+class my_formatter_flag : public spdlog::custom_flag_formatter
+{
+public:
+    void format(const spdlog::details::log_msg &, const std::tm &, spdlog::memory_buf_t &dest) override
+    {
+        std::string some_txt = "custom-flag";
+        dest.append(some_txt.data(), some_txt.data() + some_txt.size());
+    }
+
+    std::unique_ptr<custom_flag_formatter> clone() const override
+    {
+        return spdlog::details::make_unique<my_formatter_flag>();
+    }
+};
+
+void custom_flags_example()
+{    
+    auto formatter = std::make_unique<spdlog::pattern_formatter>();
+    formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v");
+    spdlog::set_formatter(std::move(formatter));
+}
+
+```
+
+---
+#### Custom error handler
+```c++
+void err_handler_example()
+{
+    // 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 env variable or from argv
+
+```c++
+#include "spdlog/cfg/env.h"
+int main (int argc, char *argv[])
+{
+    spdlog::cfg::load_env_levels();
+    // or from command line:
+    // ./example SPDLOG_LEVEL=info,mylogger=trace
+    // #include "spdlog/cfg/argv.h" // for loading levels from argv
+    // spdlog::cfg::load_argv_levels(argc, argv);
+}
+```
+So then you can:
+
+```console
+$ export SPDLOG_LEVEL=info,mylogger=trace
+$ ./example
+```
+
+---
+## Benchmarks
+
+Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/bench.cpp) done in Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
+
+#### Synchronous mode
+```
+[info] **************************************************************
+[info] Single thread, 1,000,000 iterations
+[info] **************************************************************
+[info] basic_st         Elapsed: 0.17 secs        5,777,626/sec
+[info] rotating_st      Elapsed: 0.18 secs        5,475,894/sec
+[info] daily_st         Elapsed: 0.20 secs        5,062,659/sec
+[info] empty_logger     Elapsed: 0.07 secs       14,127,300/sec
+[info] **************************************************************
+[info] C-string (400 bytes). Single thread, 1,000,000 iterations
+[info] **************************************************************
+[info] basic_st         Elapsed: 0.41 secs        2,412,483/sec
+[info] rotating_st      Elapsed: 0.72 secs        1,389,196/sec
+[info] daily_st         Elapsed: 0.42 secs        2,393,298/sec
+[info] null_st          Elapsed: 0.04 secs       27,446,957/sec
+[info] **************************************************************
+[info] 10 threads, competing over the same logger object, 1,000,000 iterations
+[info] **************************************************************
+[info] basic_mt         Elapsed: 0.60 secs        1,659,613/sec
+[info] rotating_mt      Elapsed: 0.62 secs        1,612,493/sec
+[info] daily_mt         Elapsed: 0.61 secs        1,638,305/sec
+[info] null_mt          Elapsed: 0.16 secs        6,272,758/sec
+```
+#### Asynchronous mode
+```
+[info] -------------------------------------------------
+[info] Messages     : 1,000,000
+[info] Threads      : 10
+[info] Queue        : 8,192 slots
+[info] Queue memory : 8,192 x 272 = 2,176 KB 
+[info] -------------------------------------------------
+[info] 
+[info] *********************************
+[info] Queue Overflow Policy: block
+[info] *********************************
+[info] Elapsed: 1.70784 secs     585,535/sec
+[info] Elapsed: 1.69805 secs     588,910/sec
+[info] Elapsed: 1.7026 secs      587,337/sec
+[info] 
+[info] *********************************
+[info] Queue Overflow Policy: overrun
+[info] *********************************
+[info] Elapsed: 0.372816 secs    2,682,285/sec
+[info] Elapsed: 0.379758 secs    2,633,255/sec
+[info] Elapsed: 0.373532 secs    2,677,147/sec
+
+```
+
+## Documentation
+Documentation can be found in the [wiki](https://github.com/gabime/spdlog/wiki/1.-QuickStart) pages.
+
+---
+
+Thanks to [JetBrains](https://www.jetbrains.com/?from=spdlog) for donating product licenses to help develop **spdlog** <a href="https://www.jetbrains.com/?from=spdlog"><img src="logos/jetbrains-variant-4.svg" width="94" align="center" /></a>
+
+

+ 42 - 0
external/spdlog/bench/CMakeLists.txt

@@ -0,0 +1,42 @@
+# Copyright(c) 2019 spdlog authors Distributed under the MIT License (http://opensource.org/licenses/MIT)
+
+cmake_minimum_required(VERSION 3.10)
+project(spdlog_bench CXX)
+
+if(NOT TARGET spdlog)
+    # Stand-alone build
+    find_package(spdlog CONFIG REQUIRED)
+endif()
+
+find_package(Threads REQUIRED)
+find_package(benchmark CONFIG)
+if (NOT benchmark_FOUND)
+    message(STATUS "Using CMake Version ${CMAKE_VERSION}")
+    if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.11.0")
+        # User can fetch googlebenchmark 			
+        message(STATUS "Downloading GoogleBenchmark")
+        include(FetchContent)
+        set(BENCHMARK_ENABLE_GTEST_TESTS OFF CACHE INTERNAL "")
+        # Do not build and run googlebenchmark tests
+        FetchContent_Declare(googlebenchmark
+            GIT_REPOSITORY https://github.com/google/benchmark.git
+            GIT_TAG v1.5.2)
+
+        FetchContent_MakeAvailable(googlebenchmark)
+    else()
+        message(FATAL_ERROR "GoogleBenchmark is missing. Use CMake >= 3.11 or download it")
+    endif()
+endif()
+
+add_executable(bench bench.cpp)
+spdlog_enable_warnings(bench)
+target_link_libraries(bench PRIVATE spdlog::spdlog)
+
+add_executable(async_bench async_bench.cpp)
+target_link_libraries(async_bench PRIVATE spdlog::spdlog)
+
+add_executable(latency latency.cpp)
+target_link_libraries(latency PRIVATE benchmark::benchmark spdlog::spdlog)
+
+add_executable(formatter-bench formatter-bench.cpp)
+target_link_libraries(formatter-bench PRIVATE benchmark::benchmark spdlog::spdlog)

+ 185 - 0
external/spdlog/bench/async_bench.cpp

@@ -0,0 +1,185 @@
+//
+// Copyright(c) 2015 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+//
+// bench.cpp : spdlog benchmarks
+//
+#include "spdlog/spdlog.h"
+#include "spdlog/async.h"
+#include "spdlog/sinks/basic_file_sink.h"
+
+#ifdef SPDLOG_FMT_EXTERNAL
+#include <fmt/locale.h>
+#else
+#include "spdlog/fmt/bundled/locale.h"
+#endif
+
+#include "utils.h"
+#include <atomic>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <thread>
+
+using namespace std;
+using namespace std::chrono;
+using namespace spdlog;
+using namespace spdlog::sinks;
+using namespace utils;
+
+void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4996) // disable fopen warning under msvc
+#endif // _MSC_VER
+
+int count_lines(const char *filename)
+{
+    int counter = 0;
+    auto *infile = fopen(filename, "r");
+    int ch;
+    while (EOF != (ch = getc(infile)))
+    {
+        if ('\n' == ch)
+            counter++;
+    }
+    fclose(infile);
+
+    return counter;
+}
+
+void verify_file(const char *filename, int expected_count)
+{
+    spdlog::info("Verifying {} to contain {} line..", filename, expected_count);
+    auto count = count_lines(filename);
+    if (count != expected_count)
+    {
+        spdlog::error("Test failed. {} has {} lines instead of {}", filename, count, expected_count);
+        exit(1);
+    }
+    spdlog::info("Line count OK ({})\n", count);
+}
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+int main(int argc, char *argv[])
+{
+
+    int howmany = 1000000;
+    int queue_size = std::min(howmany + 2, 8192);
+    int threads = 10;
+    int iters = 3;
+
+    try
+    {
+        spdlog::set_pattern("[%^%l%$] %v");
+        if (argc == 1)
+        {
+            spdlog::info("Usage: {} <message_count> <threads> <q_size> <iterations>", argv[0]);
+            return 0;
+        }
+
+        if (argc > 1)
+            howmany = atoi(argv[1]);
+        if (argc > 2)
+            threads = atoi(argv[2]);
+        if (argc > 3)
+        {
+            queue_size = atoi(argv[3]);
+            if (queue_size > 500000)
+            {
+                spdlog::error("Max queue size allowed: 500,000");
+                exit(1);
+            }
+        }
+
+        if (argc > 4)
+            iters = atoi(argv[4]);
+
+        auto slot_size = sizeof(spdlog::details::async_msg);
+        spdlog::info("-------------------------------------------------");
+        spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Messages     : {:L}", howmany));
+        spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Threads      : {:L}", threads));
+        spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Queue        : {:L} slots", queue_size));
+        spdlog::info(fmt::format(
+            std::locale("en_US.UTF-8"), "Queue memory : {:L} x {:L} = {:L} KB ", queue_size, slot_size, (queue_size * slot_size) / 1024));
+        spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Total iters  : {:L}", iters));
+        spdlog::info("-------------------------------------------------");
+
+        const char *filename = "logs/basic_async.log";
+        spdlog::info("");
+        spdlog::info("*********************************");
+        spdlog::info("Queue Overflow Policy: block");
+        spdlog::info("*********************************");
+        for (int i = 0; i < iters; i++)
+        {
+            auto tp = std::make_shared<details::thread_pool>(queue_size, 1);
+            auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
+            auto logger = std::make_shared<async_logger>("async_logger", std::move(file_sink), std::move(tp), async_overflow_policy::block);
+            bench_mt(howmany, std::move(logger), threads);
+            // verify_file(filename, howmany);
+        }
+
+        spdlog::info("");
+        spdlog::info("*********************************");
+        spdlog::info("Queue Overflow Policy: overrun");
+        spdlog::info("*********************************");
+        // do same test but discard oldest if queue is full instead of blocking
+        filename = "logs/basic_async-overrun.log";
+        for (int i = 0; i < iters; i++)
+        {
+            auto tp = std::make_shared<details::thread_pool>(queue_size, 1);
+            auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
+            auto logger =
+                std::make_shared<async_logger>("async_logger", std::move(file_sink), std::move(tp), async_overflow_policy::overrun_oldest);
+            bench_mt(howmany, std::move(logger), threads);
+        }
+        spdlog::shutdown();
+    }
+    catch (std::exception &ex)
+    {
+        std::cerr << "Error: " << ex.what() << std::endl;
+        perror("Last error");
+        return 1;
+    }
+    return 0;
+}
+
+void thread_fun(std::shared_ptr<spdlog::logger> logger, int howmany)
+{
+    for (int i = 0; i < howmany; i++)
+    {
+        logger->info("Hello logger: msg number {}", i);
+    }
+}
+
+void bench_mt(int howmany, std::shared_ptr<spdlog::logger> logger, int thread_count)
+{
+    using std::chrono::high_resolution_clock;
+    vector<thread> threads;
+    auto start = high_resolution_clock::now();
+
+    int msgs_per_thread = howmany / thread_count;
+    int msgs_per_thread_mod = howmany % thread_count;
+    for (int t = 0; t < thread_count; ++t)
+    {
+        if (t == 0 && msgs_per_thread_mod)
+            threads.push_back(std::thread(thread_fun, logger, msgs_per_thread + msgs_per_thread_mod));
+        else
+            threads.push_back(std::thread(thread_fun, logger, msgs_per_thread));
+    }
+
+    for (auto &t : threads)
+    {
+        t.join();
+    };
+
+    auto delta = high_resolution_clock::now() - start;
+    auto delta_d = duration_cast<duration<double>>(delta).count();
+    spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Elapsed: {} secs\t {:L}/sec", delta_d, int(howmany / delta_d)));
+}

+ 246 - 0
external/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"
+
+#ifdef SPDLOG_FMT_EXTERNAL
+#include <fmt/locale.h>
+#else
+#include "spdlog/fmt/bundled/locale.h"
+#endif
+
+#include "utils.h"
+#include <atomic>
+#include <cstdlib> // EXIT_FAILURE
+#include <memory>
+#include <string>
+#include <thread>
+
+void bench(int howmany, std::shared_ptr<spdlog::logger> log);
+void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, size_t thread_count);
+
+// void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log);
+// void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log);
+
+static const size_t file_size = 30 * 1024 * 1024;
+static const size_t rotating_files = 5;
+static const int max_threads = 1000;
+
+void bench_threaded_logging(size_t threads, int iters)
+{
+    spdlog::info("**************************************************************");
+    spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Multi threaded: {:L} threads, {:L} messages", threads, iters));
+    spdlog::info("**************************************************************");
+
+    auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
+    bench_mt(iters, std::move(basic_mt), threads);
+    auto basic_mt_tracing = spdlog::basic_logger_mt("basic_mt/backtrace-on", "logs/basic_mt.log", true);
+    basic_mt_tracing->enable_backtrace(32);
+    bench_mt(iters, std::move(basic_mt_tracing), threads);
+
+    spdlog::info("");
+    auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files);
+    bench_mt(iters, std::move(rotating_mt), threads);
+    auto rotating_mt_tracing = spdlog::rotating_logger_mt("rotating_mt/backtrace-on", "logs/rotating_mt.log", file_size, rotating_files);
+    rotating_mt_tracing->enable_backtrace(32);
+    bench_mt(iters, std::move(rotating_mt_tracing), threads);
+
+    spdlog::info("");
+    auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
+    bench_mt(iters, std::move(daily_mt), threads);
+    auto daily_mt_tracing = spdlog::daily_logger_mt("daily_mt/backtrace-on", "logs/daily_mt.log");
+    daily_mt_tracing->enable_backtrace(32);
+    bench_mt(iters, std::move(daily_mt_tracing), threads);
+
+    spdlog::info("");
+    auto empty_logger = std::make_shared<spdlog::logger>("level-off");
+    empty_logger->set_level(spdlog::level::off);
+    bench(iters, empty_logger);
+    auto empty_logger_tracing = std::make_shared<spdlog::logger>("level-off/backtrace-on");
+    empty_logger_tracing->set_level(spdlog::level::off);
+    empty_logger_tracing->enable_backtrace(32);
+    bench(iters, empty_logger_tracing);
+}
+
+void bench_single_threaded(int iters)
+{
+    spdlog::info("**************************************************************");
+    spdlog::info(fmt::format(std::locale("en_US.UTF-8"), "Single threaded: {} messages", iters));
+    spdlog::info("**************************************************************");
+
+    auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true);
+    bench(iters, std::move(basic_st));
+
+    auto basic_st_tracing = spdlog::basic_logger_st("basic_st/backtrace-on", "logs/basic_st.log", true);
+    bench(iters, std::move(basic_st_tracing));
+
+    spdlog::info("");
+    auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
+    bench(iters, std::move(rotating_st));
+    auto rotating_st_tracing = spdlog::rotating_logger_st("rotating_st/backtrace-on", "logs/rotating_st.log", file_size, rotating_files);
+    rotating_st_tracing->enable_backtrace(32);
+    bench(iters, std::move(rotating_st_tracing));
+
+    spdlog::info("");
+    auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
+    bench(iters, std::move(daily_st));
+    auto daily_st_tracing = spdlog::daily_logger_st("daily_st/backtrace-on", "logs/daily_st.log");
+    daily_st_tracing->enable_backtrace(32);
+    bench(iters, std::move(daily_st_tracing));
+
+    spdlog::info("");
+    auto empty_logger = std::make_shared<spdlog::logger>("level-off");
+    empty_logger->set_level(spdlog::level::off);
+    bench(iters, empty_logger);
+
+    auto empty_logger_tracing = std::make_shared<spdlog::logger>("level-off/backtrace-on");
+    empty_logger_tracing->set_level(spdlog::level::off);
+    empty_logger_tracing->enable_backtrace(32);
+    bench(iters, empty_logger_tracing);
+}
+
+int main(int argc, char *argv[])
+{
+    spdlog::set_automatic_registration(false);
+    spdlog::default_logger()->set_pattern("[%^%l%$] %v");
+    int iters = 250000;
+    size_t threads = 4;
+    try
+    {
+
+        if (argc > 1)
+        {
+            iters = std::stoi(argv[1]);
+        }
+        if (argc > 2)
+        {
+            threads = std::stoul(argv[2]);
+        }
+
+        if (threads > max_threads)
+        {
+            throw std::runtime_error(fmt::format("Number of threads exceeds maximum({}})", max_threads));
+        }
+
+        bench_single_threaded(iters);
+        bench_threaded_logging(1, iters);
+        bench_threaded_logging(threads, iters);
+    }
+    catch (std::exception &ex)
+    {
+        spdlog::error(ex.what());
+        return EXIT_FAILURE;
+    }
+    return EXIT_SUCCESS;
+}
+
+void bench(int howmany, std::shared_ptr<spdlog::logger> log)
+{
+    using std::chrono::duration;
+    using std::chrono::duration_cast;
+    using std::chrono::high_resolution_clock;
+
+    auto start = high_resolution_clock::now();
+    for (auto i = 0; i < howmany; ++i)
+    {
+        log->info("Hello logger: msg number {}", i);
+    }
+
+    auto delta = high_resolution_clock::now() - start;
+    auto delta_d = duration_cast<duration<double>>(delta).count();
+
+    spdlog::info(
+        fmt::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d)));
+    spdlog::drop(log->name());
+}
+
+void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, size_t thread_count)
+{
+    using std::chrono::duration;
+    using std::chrono::duration_cast;
+    using std::chrono::high_resolution_clock;
+
+    std::vector<std::thread> threads;
+    threads.reserve(thread_count);
+    auto start = high_resolution_clock::now();
+    for (size_t t = 0; t < thread_count; ++t)
+    {
+        threads.emplace_back([&]() {
+            for (int j = 0; j < howmany / static_cast<int>(thread_count); j++)
+            {
+                log->info("Hello logger: msg number {}", j);
+            }
+        });
+    }
+
+    for (auto &t : threads)
+    {
+        t.join();
+    };
+
+    auto delta = high_resolution_clock::now() - start;
+    auto delta_d = duration_cast<duration<double>>(delta).count();
+    spdlog::info(
+        fmt::format(std::locale("en_US.UTF-8"), "{:<30} Elapsed: {:0.2f} secs {:>16L}/sec", log->name(), delta_d, int(howmany / delta_d)));
+    spdlog::drop(log->name());
+}
+
+/*
+void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log)
+{
+    using std::chrono::high_resolution_clock;
+    using std::chrono::duration;
+    using std::chrono::duration_cast;
+
+    auto orig_default = spdlog::default_logger();
+    spdlog::set_default_logger(log);
+    auto start = high_resolution_clock::now();
+    for (auto i = 0; i < howmany; ++i)
+    {
+        spdlog::info("Hello logger: msg number {}", i);
+    }
+
+    auto delta = high_resolution_clock::now() - start;
+    auto delta_d = duration_cast<duration<double>>(delta).count();
+    spdlog::drop(log->name());
+    spdlog::set_default_logger(std::move(orig_default));
+    spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16}/sec", log->name(), delta_d, int(howmany / delta_d));
+}
+
+void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
+{
+    using std::chrono::high_resolution_clock;
+    using std::chrono::duration;
+    using std::chrono::duration_cast;
+
+    const char *msg = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum pharetra metus cursus "
+                      "lacus placerat congue. Nulla egestas, mauris a tincidunt tempus, enim lectus volutpat mi, eu consequat sem "
+                      "libero nec massa. In dapibus ipsum a diam rhoncus gravida. Etiam non dapibus eros. Donec fringilla dui sed "
+                      "augue pretium, nec scelerisque est maximus. Nullam convallis, sem nec blandit maximus, nisi turpis ornare "
+                      "nisl, sit amet volutpat neque massa eu odio. Maecenas malesuada quam ex, posuere congue nibh turpis duis.";
+
+    auto orig_default = spdlog::default_logger();
+    spdlog::set_default_logger(log);
+    auto start = high_resolution_clock::now();
+    for (auto i = 0; i < howmany; ++i)
+    {
+        spdlog::log(spdlog::level::info, msg);
+    }
+
+    auto delta = high_resolution_clock::now() - start;
+    auto delta_d = duration_cast<duration<double>>(delta).count();
+    spdlog::drop(log->name());
+    spdlog::set_default_logger(std::move(orig_default));
+    spdlog::info("{:<30} Elapsed: {:0.2f} secs {:>16}/sec", log->name(), delta_d, int(howmany / delta_d));
+}
+
+*/

+ 80 - 0
external/spdlog/bench/formatter-bench.cpp

@@ -0,0 +1,80 @@
+//
+// Copyright(c) 2018 Gabi Melman.
+// Distributed under the MIT License (http://opensource.org/licenses/MIT)
+//
+
+#include "benchmark/benchmark.h"
+
+#include "spdlog/spdlog.h"
+#include "spdlog/pattern_formatter.h"
+
+void bench_formatter(benchmark::State &state, std::string pattern)
+{
+    auto formatter = spdlog::details::make_unique<spdlog::pattern_formatter>(pattern);
+    spdlog::memory_buf_t dest;
+    std::string logger_name = "logger-name";
+    const char *text = "Hello. This is some message with length of 80                                   ";
+
+    spdlog::source_loc source_loc{"a/b/c/d/myfile.cpp", 123, "some_func()"};
+    spdlog::details::log_msg msg(source_loc, logger_name, spdlog::level::info, text);
+
+    for (auto _ : state)
+    {
+        dest.clear();
+        formatter->format(msg, dest);
+        benchmark::DoNotOptimize(dest);
+    }
+}
+
+void bench_formatters()
+{
+    // basic patterns(single flag)
+    std::string all_flags = "+vtPnlLaAbBcCYDmdHIMSefFprRTXzEisg@luioO%";
+    std::vector<std::string> basic_patterns;
+    for (auto &flag : all_flags)
+    {
+        auto pattern = std::string("%") + flag;
+        benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
+
+        //        pattern = std::string("%16") + flag;
+        //        benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
+        //
+        //        // bench center padding
+        //        pattern = std::string("%=16") + flag;
+        //        benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
+    }
+
+    // complex patterns
+    std::vector<std::string> patterns = {
+        "[%D %X] [%l] [%n] %v",
+        "[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] %v",
+        "[%Y-%m-%d %H:%M:%S.%e] [%l] [%n] [%t] %v",
+    };
+    for (auto &pattern : patterns)
+    {
+        benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern)->Iterations(2500000);
+    }
+}
+
+int main(int argc, char *argv[])
+{
+
+    spdlog::set_pattern("[%^%l%$] %v");
+    if (argc != 2)
+    {
+        spdlog::error("Usage: {} <pattern> (or \"all\" to bench all)", argv[0]);
+        exit(1);
+    }
+
+    std::string pattern = argv[1];
+    if (pattern == "all")
+    {
+        bench_formatters();
+    }
+    else
+    {
+        benchmark::RegisterBenchmark(pattern.c_str(), bench_formatter, pattern);
+    }
+    benchmark::Initialize(&argc, argv);
+    benchmark::RunSpecifiedBenchmarks();
+}

Некоторые файлы не были показаны из-за большого количества измененных файлов