Commit 4fe98bf6 authored by Philip Miller's avatar Philip Miller

Merge remote-tracking branch 'origin/v1.x' into pwm1234/rotate_on_open

parents 22f85deb 57c30238
......@@ -88,7 +88,7 @@ PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Right
ReflowComments: true
SortIncludes: true
SortIncludes: false
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterTemplateKeyword: false
......
Checks: 'modernize-*,modernize-use-override,google-*,-google-runtime-references,misc-*,clang-analyzer-*'
WarningsAsErrors: ''
HeaderFilterRegex: 'async.h|async_logger.h|common.h|details|formatter.h|logger.h|sinks|spdlog.h|tweakme.h|version.h'
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'
......@@ -104,6 +104,7 @@ script:
-DCMAKE_CXX_STANDARD=$CPP \
-DSPDLOG_BUILD_EXAMPLES=ON \
-DSPDLOG_BUILD_BENCH=OFF \
-DSPDLOG_BUILD_TESTS=ON \
-DSPDLOG_SANITIZE_ADDRESS=$ASAN \
-DSPDLOG_SANITIZE_THREAD=$TSAN
- make VERBOSE=1 -j2
......
......@@ -4,8 +4,7 @@
#
cmake_minimum_required(VERSION 3.1)
project(spdlog VERSION 1.1.0 LANGUAGES CXX)
include(CTest)
project(spdlog VERSION 1.3.0 LANGUAGES CXX)
include(CMakeDependentOption)
include(GNUInstallDirs)
......@@ -45,13 +44,20 @@ include(cmake/sanitizers.cmake)
add_library(spdlog INTERFACE)
add_library(spdlog::spdlog ALIAS spdlog)
option(SPDLOG_BUILD_EXAMPLES "Build examples" ON)
option(SPDLOG_BUILD_BENCH "Build benchmarks" ON)
# Check if spdlog is being used directly or via add_subdirectory
set(SPDLOG_MASTER_PROJECT OFF)
if (CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(SPDLOG_MASTER_PROJECT ON)
endif()
cmake_dependent_option(SPDLOG_BUILD_TESTING
"Build spdlog tests" ON
"BUILD_TESTING" OFF
)
option(SPDLOG_BUILD_EXAMPLES "Build examples" ${SPDLOG_MASTER_PROJECT})
option(SPDLOG_BUILD_BENCH "Build benchmarks" ${SPDLOG_MASTER_PROJECT})
option(SPDLOG_BUILD_TESTS "Build tests" ${SPDLOG_MASTER_PROJECT})
option(SPDLOG_FMT_EXTERNAL "Use external fmt library instead of bundled" OFF)
if(SPDLOG_FMT_EXTERNAL)
find_package(fmt REQUIRED CONFIG)
endif()
target_include_directories(
spdlog
......@@ -60,13 +66,19 @@ target_include_directories(
"$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
if(SPDLOG_FMT_EXTERNAL)
target_compile_definitions(spdlog INTERFACE SPDLOG_FMT_EXTERNAL)
target_link_libraries(spdlog INTERFACE fmt::fmt)
endif()
set(HEADER_BASE "${CMAKE_CURRENT_SOURCE_DIR}/include")
if(SPDLOG_BUILD_EXAMPLES)
add_subdirectory(example)
endif()
if(SPDLOG_BUILD_TESTING)
if(SPDLOG_BUILD_TESTS)
include(CTest)
add_subdirectory(tests)
endif()
......@@ -82,7 +94,8 @@ set(config_install_dir "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}")
set(include_install_dir "${CMAKE_INSTALL_INCLUDEDIR}")
set(pkgconfig_install_dir "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
set(version_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake")
set(project_config "${PROJECT_NAME}Config.cmake")
set(project_config "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake")
set(targets_config "${PROJECT_NAME}Targets.cmake")
set(pkg_config "${CMAKE_BINARY_DIR}/${PROJECT_NAME}.pc")
set(targets_export_name "${PROJECT_NAME}Targets")
set(namespace "${PROJECT_NAME}::")
......@@ -95,6 +108,8 @@ write_basic_package_version_file(
# configure pkg config file
configure_file("cmake/spdlog.pc.in" "${pkg_config}" @ONLY)
# configure spdlogConfig.cmake file
configure_file("cmake/Config.cmake.in" "${project_config}" @ONLY)
# install targets
install(
......@@ -108,9 +123,9 @@ install(
DESTINATION "${include_install_dir}"
)
# install project version file
# install project config and version file
install(
FILES "${version_config}"
FILES "${project_config}" "${version_config}"
DESTINATION "${config_install_dir}"
)
......@@ -120,19 +135,19 @@ install(
DESTINATION "${pkgconfig_install_dir}"
)
# install project config file
# install targets config file
install(
EXPORT "${targets_export_name}"
NAMESPACE "${namespace}"
DESTINATION "${config_install_dir}"
FILE ${project_config}
FILE ${targets_config}
)
# export build directory config file
# export build directory targets file
export(
EXPORT ${targets_export_name}
NAMESPACE "${namespace}"
FILE ${project_config}
FILE ${targets_config}
)
# register project in CMake user registry
......
......@@ -21,18 +21,17 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
## Platforms
* Linux, FreeBSD, Solaris, AIX
* Windows (vc 2013+, cygwin)
* Mac OSX (clang 3.5+)
* Linux, FreeBSD, OpenBSD, Solaris, AIX
* Windows (msvc 2013+, cygwin)
* macOS (clang 3.5+)
* Android
## Features
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
* Very fast (see [benchmarks](#benchmarks) below).
* Headers only, just copy and use.
* Feature rich using the excellent [fmt](https://github.com/fmtlib/fmt) library.
* Feature rich formatting, using the excellent [fmt](https://github.com/fmtlib/fmt) library.
* Fast asynchronous mode (optional)
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
* Conditional Logging
* Multi/Single threaded loggers.
* Various log targets:
* Rotating log files.
......@@ -42,7 +41,7 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
* Windows debugger (```OutputDebugString(..)```)
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
* Severity based filtering - threshold levels can be modified in runtime as well as in compile time.
* Binary data logging.
## Benchmarks
......@@ -54,68 +53,67 @@ Below are some [benchmarks](https://github.com/gabime/spdlog/blob/v1.x/bench/ben
*******************************************************************************
Single thread, 1,000,000 iterations
*******************************************************************************
basic_st... Elapsed: 0.226664 4,411,806/sec
rotating_st... Elapsed: 0.214339 4,665,499/sec
daily_st... Elapsed: 0.211292 4,732,797/sec
null_st... Elapsed: 0.102815 9,726,227/sec
basic_st... Elapsed: 0.181652 5,505,042/sec
rotating_st... Elapsed: 0.181781 5,501,117/sec
daily_st... Elapsed: 0.187595 5,330,630/sec
null_st... Elapsed: 0.0504704 19,813,602/sec
*******************************************************************************
10 threads sharing same logger, 1,000,000 iterations
*******************************************************************************
basic_mt... Elapsed: 0.882268 1,133,441/sec
rotating_mt... Elapsed: 0.875515 1,142,184/sec
daily_mt... Elapsed: 0.879573 1,136,915/sec
null_mt... Elapsed: 0.220114 4,543,105/sec
basic_mt... Elapsed: 0.616035 1,623,284/sec
rotating_mt... Elapsed: 0.620344 1,612,008/sec
daily_mt... Elapsed: 0.648353 1,542,369/sec
null_mt... Elapsed: 0.151972 6,580,166/sec
```
#### Asynchronous mode
```
*******************************************************************************
10 threads sharing same logger, 1,000,000 iterations
*******************************************************************************
async... Elapsed: 0.429088 2,330,524/sec
async... Elapsed: 0.411501 2,430,126/sec
async... Elapsed: 0.428979 2,331,116/sec
async... Elapsed: 0.350066 2,856,606/sec
async... Elapsed: 0.314865 3,175,960/sec
async... Elapsed: 0.349851 2,858,358/sec
```
## 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 {}", {});
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");
console->info("Welcome to spdlog!");
console->error("Some error message with arg: {}", 1);
auto err_logger = spdlog::stderr_color_mt("stderr");
err_logger->error("Some error message");
// Formatting examples
console->warn("Easy padding in numbers like {:08d}", 12);
console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
console->info("Support for floats {:03.2f}", 1.23456);
console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");
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)");
// Runtime log levels
spdlog::set_level(spdlog::level::info); // Set global log level to info
console->debug("This message should not be displayed!");
console->set_level(spdlog::level::trace); // Set specific logger's log level
console->debug("This message should be displayed..");
// Customize msg format for all loggers
spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v");
console->info("This an info message with custom format");
// Compile time log levels
// define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON
SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23);
SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23);
}
```
---
......@@ -131,7 +129,6 @@ void basic_logfile_example()
catch (const spdlog::spdlog_ex &ex)
{
std::cout << "Log init failed: " << ex.what() << std::endl;
return 1;
}
}
```
......@@ -180,6 +177,33 @@ spdlog::flush_every(std::chrono::seconds(3));
```
---
#### Binary logging
```c++
// log binary data as hex.
// 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.
#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
......@@ -282,7 +306,7 @@ void syslog_example()
---
#### Android example
```c++
#incude "spdlog/sinks/android_sink.h"
#include "spdlog/sinks/android_sink.h"
void android_example()
{
std::string tag = "spdlog-android";
......
......@@ -26,7 +26,9 @@ build_script:
set PATH=C:\mingw-w64\i686-5.3.0-posix-dwarf-rt_v4-rev0\mingw32\bin;%PATH%
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE%
cmake .. -G %GENERATOR% -DCMAKE_BUILD_TYPE=%BUILD_TYPE% -DSPDLOG_BUILD_BENCH=OFF
cmake --build . --config %BUILD_TYPE%
test: off
test_script:
- ctest -VV -C "%BUILD_TYPE%"
......@@ -38,6 +38,12 @@ add_executable(async_bench async_bench.cpp)
target_link_libraries(async_bench spdlog::spdlog Threads::Threads)
add_executable(latency latency.cpp)
set(CMAKE_CXX_STANDARD_LIBRARIES -lbenchmark)
target_link_libraries(latency spdlog::spdlog Threads::Threads)
add_executable(formatter-bench formatter-bench.cpp)
set(CMAKE_CXX_STANDARD_LIBRARIES -lbenchmark)
target_link_libraries(formatter-bench spdlog::spdlog Threads::Threads)
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/logs")
CXX ?= g++
CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1
CXX_RELEASE_FLAGS = -Ofast -flto -Wl,--no-as-needed
CXXFLAGS = -march=native -Wall -Wextra -pedantic -Wconversion -std=c++11 -pthread -I../include -fmax-errors=1
CXX_RELEASE_FLAGS = -O3 -flto -Wl,--no-as-needed
binaries=bench latency async_bench
binaries=bench async_bench latency formatter-bench
all: $(binaries)
bench: bench.cpp
$(CXX) bench.cpp -o bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
async_bench: async_bench.cpp
$(CXX) async_bench.cpp -o async_bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
latency: latency.cpp
$(CXX) latency.cpp -o latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
$(CXX) latency.cpp -o latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -lbenchmark
formatter-bench: formatter-bench.cpp
$(CXX) formatter-bench.cpp -o formatter-bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS) -lbenchmark
.PHONY: clean
clean:
rm -f *.o logs/* $(binaries)
rm -f *.o logs/* latecy_logs $(binaries)
rebuild: clean all
......@@ -6,10 +6,11 @@
//
// bench.cpp : spdlog benchmarks
//
#include "spdlog/spdlog.h"
#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/spdlog.h"
#include "utils.h"
#include <atomic>
#include <iostream>
......@@ -35,6 +36,7 @@ int count_lines(const char *filename)
if ('\n' == ch)
counter++;
}
fclose(infile);
return counter;
}
......@@ -48,11 +50,10 @@ int main(int argc, char *argv[])
try
{
auto console = spdlog::stdout_color_mt("console");
if (argc == 1)
{
console->set_pattern("%v");
console->info("Usage: {} <message_count> <threads> <q_size> <iterations>", argv[0]);
spdlog::set_pattern("%v");
spdlog::info("Usage: {} <message_count> <threads> <q_size> <iterations>", argv[0]);
return 0;
}
......@@ -66,12 +67,12 @@ int main(int argc, char *argv[])
if (argc > 4)
iters = atoi(argv[4]);
console->info("-------------------------------------------------");
console->info("Messages: {:14n}", howmany);
console->info("Threads : {:14n}", threads);
console->info("Queue : {:14n}", queue_size);
console->info("Iters : {:>14n}", iters);
console->info("-------------------------------------------------");
spdlog::info("-------------------------------------------------");
spdlog::info("Messages: {:14n}", howmany);
spdlog::info("Threads : {:14n}", threads);
spdlog::info("Queue : {:14n}", queue_size);
spdlog::info("Iters : {:>14n}", iters);
spdlog::info("-------------------------------------------------");
const char *filename = "logs/basic_async.log";
......@@ -85,14 +86,15 @@ int main(int argc, char *argv[])
if (count != howmany)
{
console->error("Test failed. {} has {:n} lines instead of {:n}", filename, count, howmany);
spdlog::error("Test failed. {} has {:n} lines instead of {:n}", filename, count, howmany);
exit(1);
}
else
{
console->info("Line count OK ({:n})\n", count);
spdlog::info("Line count OK ({:n})\n", count);
}
}
spdlog::shutdown();
}
catch (std::exception &ex)
{
......@@ -100,6 +102,7 @@ int main(int argc, char *argv[])
perror("Last error");
return 1;
}
return 0;
}
......@@ -134,5 +137,5 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> logger, int thread_co
auto delta = high_resolution_clock::now() - start;
auto delta_d = duration_cast<duration<double>>(delta).count();
spdlog::get("console")->info("Elapsed: {} secs\t {:n}/sec", delta_d, int(howmany / delta_d));
spdlog::info("Elapsed: {} secs\t {:n}/sec", delta_d, int(howmany / delta_d));
}
......@@ -6,16 +6,16 @@
//
// bench.cpp : spdlog benchmarks
//
#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"
#include "spdlog/spdlog.h"
#include "utils.h"
#include <atomic>
#include <cstdlib> // EXIT_FAILURE
#include <iostream>
#include <memory>
#include <string>
#include <thread>
......@@ -28,10 +28,13 @@ using namespace utils;
void bench(int howmany, std::shared_ptr<spdlog::logger> log);
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
void bench_default_api(int howmany, std::shared_ptr<spdlog::logger> log);
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log);
int main(int argc, char *argv[])
{
spdlog::default_logger()->set_pattern("[%^%l%$] %v");
int howmany = 1000000;
int queue_size = howmany + 2;
int threads = 10;
......@@ -48,57 +51,65 @@ int main(int argc, char *argv[])
if (argc > 3)
queue_size = atoi(argv[3]);
cout << "******************************************************************"
"*************\n";
cout << "Single thread, " << format(howmany) << " iterations" << endl;
cout << "******************************************************************"
"*************\n";
spdlog::info("**************************************************************");
spdlog::info("Single thread, {:n} iterations", howmany);
spdlog::info("**************************************************************");
auto basic_st = spdlog::basic_logger_mt("basic_st", "logs/basic_st.log", true);
bench(howmany, basic_st);
auto basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_st.log", true);
bench(howmany, std::move(basic_st));
basic_st.reset();
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
bench(howmany, rotating_st);
bench(howmany, std::move(rotating_st));
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
bench(howmany, daily_st);
bench(howmany, std::move(daily_st));
bench(howmany, spdlog::create<null_sink_st>("null_st"));
cout << "\n****************************************************************"
"***************\n";
cout << threads << " threads sharing same logger, " << format(howmany) << " iterations" << endl;
cout << "******************************************************************"
"*************\n";
spdlog::info("**************************************************************");
spdlog::info("C-string (400 bytes). Single thread, {:n} iterations", howmany);
spdlog::info("**************************************************************");
basic_st = spdlog::basic_logger_st("basic_st", "logs/basic_cs.log", true);
bench_c_string(howmany, std::move(basic_st));
rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_cs.log", file_size, rotating_files);
bench_c_string(howmany, std::move(rotating_st));
daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_cs.log");
bench_c_string(howmany, std::move(daily_st));
bench_c_string(howmany, spdlog::create<null_sink_st>("null_st"));
spdlog::info("**************************************************************");
spdlog::info("{:n} threads sharing same logger, {:n} iterations", threads, howmany);
spdlog::info("**************************************************************");
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
bench_mt(howmany, basic_mt, threads);
bench_mt(howmany, std::move(basic_mt), threads);
auto rotating_mt = spdlog::rotating_logger_mt("rotating_mt", "logs/rotating_mt.log", file_size, rotating_files);
bench_mt(howmany, rotating_mt, threads);
bench_mt(howmany, std::move(rotating_mt), threads);
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
bench_mt(howmany, daily_mt, threads);
bench_mt(howmany, std::move(daily_mt), threads);
bench_mt(howmany, spdlog::create<null_sink_mt>("null_mt"), threads);
cout << "\n****************************************************************"
"***************\n";
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations " << endl;
cout << "******************************************************************"
"*************\n";
spdlog::info("**************************************************************");
spdlog::info("Asyncronous.. {:n} threads sharing same logger, {:n} iterations", threads, howmany);
spdlog::info("**************************************************************");
for (int i = 0; i < 3; ++i)
{
spdlog::init_thread_pool(static_cast<size_t>(queue_size), 1);
auto as = spdlog::basic_logger_mt<spdlog::async_factory>("async", "logs/basic_async.log", true);
bench_mt(howmany, as, threads);
spdlog::drop("async");
bench_mt(howmany, std::move(as), threads);
}
}
catch (std::exception &ex)
{
std::cerr << "Error: " << ex.what() << std::endl;
perror("Last error");
spdlog::error(ex.what());
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
......@@ -107,7 +118,6 @@ int main(int argc, char *argv[])
void bench(int howmany, std::shared_ptr<spdlog::logger> log)
{
using std::chrono::high_resolution_clock;
cout << log->name() << "...\t\t" << flush;
auto start = high_resolution_clock::now();
for (auto i = 0; i < howmany; ++i)
{
......@@ -117,14 +127,13 @@ void bench(int howmany, std::shared_ptr<spdlog::logger> log)
auto delta = high_resolution_clock::now() - start;
auto delta_d = duration_cast<duration<double>>(delta).count();
cout << "Elapsed: " << delta_d << "\t" << format(int(howmany / delta_d)) << "/sec" << endl;
spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
spdlog::drop(log->name());
}
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
{
using std::chrono::high_resolution_clock;
cout << log->name() << "...\t\t" << flush;
vector<thread> threads;
auto start = high_resolution_clock::now();
for (int t = 0; t < thread_count; ++t)
......@@ -144,5 +153,47 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count
auto delta = high_resolution_clock::now() - start;
auto delta_d = duration_cast<duration<double>>(delta).count();
cout << "Elapsed: " << delta_d << "\t" << format(int(howmany / delta_d)) << "/sec" << endl;
spdlog::info("{:<16} Elapsed: {:0.2f} secs {:>16n}/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;
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("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
}
void bench_c_string(int howmany, std::shared_ptr<spdlog::logger> log)
{
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.";
using std::chrono::high_resolution_clock;
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(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("{:<16} Elapsed: {:0.2f} secs {:>16n}/sec", log->name(), delta_d, int(howmany / delta_d));
}
//
// 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/details/pattern_formatter.h"
void bench_scoped_pad(benchmark::State &state, size_t wrapped_size, spdlog::details::padding_info padinfo)
{
fmt::memory_buffer dest;
for (auto _ : state)
{
{
spdlog::details::scoped_pad p(wrapped_size, padinfo, dest);
benchmark::DoNotOptimize(p);
dest.clear();
}
}
}
void bench_formatter(benchmark::State &state, std::string pattern)
{
auto formatter = spdlog::details::make_unique<spdlog::pattern_formatter>(pattern);
fmt::memory_buffer dest;
std::string logger_name = "logger-name";
const char *text = "Hello. This is some message with length of 80 ";
spdlog::details::log_msg msg(&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 = "+vtPnlLaAbBcCYDmdHIMSefFprRTXzEi%";
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();
}
This diff is collapsed.
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include <atomic>
#include <chrono>
#include <cstdlib>
#include <iostream>
#include <thread>
#include <vector>
#include "spdlog/async.h"
#include "spdlog/sinks/basic_file_sink.h"
using namespace std;
int main(int argc, char *argv[])
{
using namespace std::chrono;
using clock = steady_clock;
int thread_count = 10;
if (argc > 1)
thread_count = std::atoi(argv[1]);
int howmany = 1000000;
spdlog::init_thread_pool(howmany, 1);
auto logger = spdlog::create_async_logger<spdlog::sinks::basic_file_sink_mt>("file_logger", "logs/spdlog-bench-async.log", false);
logger->set_pattern("[%Y-%m-%d %T.%F]: %L %t %v");
std::cout << "To stop, press <Enter>" << std::endl;
std::atomic<bool> run{true};
std::thread stoper(std::thread([&run]() {
std::cin.get();
run = false;
}));
while (run)
{
std::atomic<int> msg_counter{0};
std::vector<std::thread> threads;
auto start = clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]() {
while (true)
{
int counter = ++msg_counter;
if (counter > howmany)
break;
logger->info("spdlog message #{}: This is some text for your pleasure", counter);
}
}));
}
for (auto &t : threads)
{
t.join();
}
duration<float> delta = clock::now() - start;
float deltaf = delta.count();
auto rate = howmany / deltaf;
std::cout << "Total: " << howmany << std::endl;
std::cout << "Threads: " << thread_count << std::endl;
std::cout << "Delta = " << std::fixed << deltaf << " seconds" << std::endl;
std::cout << "Rate = " << std::fixed << rate << "/sec" << std::endl;
} // while
stoper.join();
return 0;
}
#!/bin/bash
clang-tidy example/example.cpp -- -I ./include
......@@ -21,4 +21,11 @@
# * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
# *************************************************************************/
set(SPDLOG_FMT_EXTERNAL @SPDLOG_FMT_EXTERNAL@)
include("${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake")
if(SPDLOG_FMT_EXTERNAL)
include(CMakeFindDependencyMacro)
find_dependency(fmt CONFIG)
endif()
......@@ -32,7 +32,13 @@ endif()
find_package(Threads REQUIRED)
add_executable(example example.cpp)
target_link_libraries(example spdlog::spdlog Threads::Threads)
if(CMAKE_SYSTEM_NAME STREQUAL "Android")
find_library(log-lib log)
target_link_libraries(example spdlog::spdlog Threads::Threads log)
else()
target_link_libraries(example spdlog::spdlog Threads::Threads)
endif()
add_executable(multisink multisink.cpp)
target_link_libraries(multisink spdlog::spdlog Threads::Threads)
......
This diff is collapsed.
......@@ -25,7 +25,6 @@
<ProjectGuid>{9E5AB93A-0CCE-4BAC-9FCB-0FC9CB5EB8D2}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>.</RootNamespace>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
......
#include "spdlog/spdlog.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/sinks/stdout_sinks.h"
#include "spdlog/spdlog.h"
#include <iostream>
#include <memory>
......
#!/bin/bash
echo -n "Running dos2unix "
find . -name "*\.h" -o -name "*\.cpp"|xargs -I {} sh -c "dos2unix '{}' 2>/dev/null; echo -n '.'"
find . -name "*\.h" -o -name "*\.cpp"|grep -v bundled|xargs -I {} sh -c "dos2unix '{}' 2>/dev/null; echo -n '.'"
echo
echo -n "Running clang-format "
find . -name "*\.h" -o -name "*\.cpp"|xargs -I {} sh -c "clang-format -i {}; echo -n '.'"
find . -name "*\.h" -o -name "*\.cpp"|grep -v bundled|xargs -I {} sh -c "clang-format -i {}; echo -n '.'"
echo
......@@ -37,7 +37,7 @@ template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
struct async_factory_impl
{
template<typename Sink, typename... SinkArgs>
static std::shared_ptr<async_logger> create(const std::string &logger_name, SinkArgs &&... args)
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&... args)
{
auto &registry_inst = details::registry::instance();
......@@ -51,8 +51,8 @@ struct async_factory_impl
}
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
auto new_logger = std::make_shared<async_logger>(logger_name, std::move(sink), std::move(tp), OverflowPolicy);
registry_inst.register_and_init(new_logger);
auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy);
registry_inst.initialize_logger(new_logger);
return new_logger;
}
};
......@@ -61,15 +61,15 @@ using async_factory = async_factory_impl<async_overflow_policy::block>;
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
template<typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create_async(const std::string &logger_name, SinkArgs &&... sink_args)
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&... sink_args)
{
return async_factory::create<Sink>(logger_name, std::forward<SinkArgs>(sink_args)...);
return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
}
template<typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create_async_nb(const std::string &logger_name, SinkArgs &&... sink_args)
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&... sink_args)
{
return async_factory_nonblock::create<Sink>(logger_name, std::forward<SinkArgs>(sink_args)...);
return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
}
// set global thread pool.
......
......@@ -40,13 +40,13 @@ namespace details {
class thread_pool;
}
class async_logger SPDLOG_FINAL : public std::enable_shared_from_this<async_logger>, public logger
class async_logger final : public std::enable_shared_from_this<async_logger>, public logger
{
friend class details::thread_pool;
public:
template<typename It>
async_logger(std::string logger_name, const It &begin, const It &end, std::weak_ptr<details::thread_pool> tp,
async_logger(std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp,
async_overflow_policy overflow_policy = async_overflow_policy::block);
async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp,
......@@ -61,7 +61,7 @@ protected:
void sink_it_(details::log_msg &msg) override;
void flush_() override;
void backend_log_(details::log_msg &incoming_log_msg);
void backend_log_(const details::log_msg &incoming_log_msg);
void backend_flush_();
private:
......
......@@ -14,6 +14,8 @@
#include <memory>
#include <stdexcept>
#include <string>
#include <cstring>
#include <type_traits>
#include <unordered_map>
#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
......@@ -23,6 +25,8 @@
#include "spdlog/details/null_mutex.h"
#include "spdlog/fmt/fmt.h"
// visual studio upto 2013 does not support noexcept nor constexpr
#if defined(_MSC_VER) && (_MSC_VER < 1900)
#define SPDLOG_NOEXCEPT throw()
......@@ -32,13 +36,6 @@
#define SPDLOG_CONSTEXPR constexpr
#endif
// final keyword support. On by default. See tweakme.h
#if defined(SPDLOG_NO_FINAL)
#define SPDLOG_FINAL
#else
#define SPDLOG_FINAL final
#endif
#if defined(__GNUC__) || defined(__clang__)
#define SPDLOG_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
......@@ -47,7 +44,29 @@
#define SPDLOG_DEPRECATED
#endif
#include "spdlog/fmt/fmt.h"
// disable thread local on msvc 2013
#ifndef SPDLOG_NO_TLS
#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
#define SPDLOG_NO_TLS 1
#endif
#endif
// Get the basename of __FILE__ (at compile time if possible)
#if FMT_HAS_FEATURE(__builtin_strrchr)
#define SPDLOG_STRRCHR(str, sep) __builtin_strrchr(str, sep)
#else
#define SPDLOG_STRRCHR(str, sep) strrchr(str, sep)
#endif //__builtin_strrchr not defined
#ifdef _WIN32
#define SPDLOG_FILE_BASENAME(file) SPDLOG_STRRCHR("\\" file, '\\') + 1
#else
#define SPDLOG_FILE_BASENAME(file) SPDLOG_STRRCHR("/" file, '/') + 1
#endif
#ifndef SPDLOG_FUNCTION
#define SPDLOG_FUNCTION __FUNCTION__
#endif
namespace spdlog {
......@@ -62,23 +81,42 @@ using sink_ptr = std::shared_ptr<sinks::sink>;
using sinks_init_list = std::initializer_list<sink_ptr>;
using log_err_handler = std::function<void(const std::string &err_msg)>;
// string_view type - either std::string_view or fmt::string_view (pre c++17)
#if defined(FMT_USE_STD_STRING_VIEW)
using string_view_t = std::string_view;
#else
using string_view_t = fmt::string_view;
#endif
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
using level_t = details::null_atomic_int;
#else
using level_t = std::atomic<int>;
#endif
#define SPDLOG_LEVEL_TRACE 0
#define SPDLOG_LEVEL_DEBUG 1
#define SPDLOG_LEVEL_INFO 2
#define SPDLOG_LEVEL_WARN 3
#define SPDLOG_LEVEL_ERROR 4
#define SPDLOG_LEVEL_CRITICAL 5
#define SPDLOG_LEVEL_OFF 6
#if !defined(SPDLOG_ACTIVE_LEVEL)
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
#endif
// Log level enum
namespace level {
enum level_enum
{
trace = 0,
debug = 1,
info = 2,
warn = 3,
err = 4,
critical = 5,
off = 6
trace = SPDLOG_LEVEL_TRACE,
debug = SPDLOG_LEVEL_DEBUG,
info = SPDLOG_LEVEL_INFO,
warn = SPDLOG_LEVEL_WARN,
err = SPDLOG_LEVEL_ERROR,
critical = SPDLOG_LEVEL_CRITICAL,
off = SPDLOG_LEVEL_OFF,
};
#if !defined(SPDLOG_LEVEL_NAMES)
......@@ -87,33 +125,32 @@ enum level_enum
"trace", "debug", "info", "warning", "error", "critical", "off" \
}
#endif
static const char *level_names[] SPDLOG_LEVEL_NAMES;
static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
static const char *short_level_names[]{"T", "D", "I", "W", "E", "C", "O"};
inline const char *to_c_str(spdlog::level::level_enum l)
inline string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
{
return level_names[l];
return level_string_views[l];
}
inline const char *to_short_c_str(spdlog::level::level_enum l)
inline const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
{
return short_level_names[l];
}
inline spdlog::level::level_enum from_str(const std::string &name)
inline spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT
{
static std::unordered_map<std::string, level_enum> name_to_level = // map string->level
{{level_names[0], level::trace}, // trace
{level_names[1], level::debug}, // debug
{level_names[2], level::info}, // info
{level_names[3], level::warn}, // warn
{level_names[4], level::err}, // err
{level_names[5], level::critical}, // critical
{level_names[6], level::off}}; // off
auto lvl_it = name_to_level.find(name);
return lvl_it != name_to_level.end() ? lvl_it->second : level::off;
int level = 0;
for (const auto &level_str : level_string_views)
{
if (level_str == name)
{
return static_cast<level::level_enum>(level);
}
level++;
}
return level::off;
}
using level_hasher = std::hash<int>;
......@@ -135,8 +172,8 @@ enum class pattern_time_type
class spdlog_ex : public std::exception
{
public:
explicit spdlog_ex(const std::string &msg)
: msg_(msg)
explicit spdlog_ex(std::string msg)
: msg_(std::move(msg))
{
}
......@@ -165,26 +202,42 @@ using filename_t = std::wstring;
using filename_t = std::string;
#endif
#define SPDLOG_CATCH_AND_HANDLE \
catch (const std::exception &ex) \
{ \
err_handler_(ex.what()); \
} \
catch (...) \
{ \
err_handler_("Unknown exeption in logger"); \
struct source_loc
{
SPDLOG_CONSTEXPR source_loc()
: filename{""}
, line{0}
, funcname{""}
{
}
SPDLOG_CONSTEXPR source_loc(const char *filename, int line, const char *funcname)
: filename{filename}
, line{static_cast<uint32_t>(line)}
, funcname{funcname}
{
}
//
// make_unique support
//
SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT
{
return line == 0;
}
const char *filename;
uint32_t line;
const char *funcname;
};
namespace details {
// make_unique support for pre c++14
#if __cplusplus >= 201402L // C++14 and beyond
using std::make_unique;
#else
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args &&... args)
{
static_assert(!std::is_array<T>::value, "arrays not supported");
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
#endif
} // namespace details
} // namespace spdlog
......@@ -16,22 +16,22 @@
template<typename It>
inline spdlog::async_logger::async_logger(
std::string logger_name, const It &begin, const It &end, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
: logger(std::move(logger_name), begin, end)
, thread_pool_(tp)
, thread_pool_(std::move(tp))
, overflow_policy_(overflow_policy)
{
}
inline spdlog::async_logger::async_logger(
std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
: async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), tp, overflow_policy)
: async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), std::move(tp), overflow_policy)
{
}
inline spdlog::async_logger::async_logger(
std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
: async_logger(std::move(logger_name), {single_sink}, tp, overflow_policy)
: async_logger(std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy)
{
}
......@@ -43,7 +43,7 @@ inline void spdlog::async_logger::sink_it_(details::log_msg &msg)
#endif
if (auto pool_ptr = thread_pool_.lock())
{
pool_ptr->post_log(shared_from_this(), std::move(msg), overflow_policy_);
pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
}
else
{
......@@ -67,7 +67,7 @@ inline void spdlog::async_logger::flush_()
//
// backend functions - called from the thread pool to do the actual job
//
inline void spdlog::async_logger::backend_log_(details::log_msg &incoming_log_msg)
inline void spdlog::async_logger::backend_log_(const details::log_msg &incoming_log_msg)
{
try
{
......
......@@ -122,7 +122,7 @@ public:
// ".mylog" => (".mylog". "")
// "my_folder/.mylog" => ("my_folder/.mylog", "")
// "my_folder/.mylog.txt" => ("my_folder/.mylog", ".txt")
static std::tuple<filename_t, filename_t> split_by_extenstion(const spdlog::filename_t &fname)
static std::tuple<filename_t, filename_t> split_by_extension(const spdlog::filename_t &fname)
{
auto ext_index = fname.rfind('.');
......
......@@ -4,7 +4,8 @@
#pragma once
#include "chrono"
#include <chrono>
#include <type_traits>
#include "spdlog/fmt/fmt.h"
// Some fmt helpers to efficiently format and pad ints and strings
......@@ -13,17 +14,9 @@ namespace details {
namespace fmt_helper {
template<size_t Buffer_Size>
inline void append_str(const std::string &str, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
inline spdlog::string_view_t to_string_view(const fmt::basic_memory_buffer<char, Buffer_Size> &buf) SPDLOG_NOEXCEPT
{
auto *str_ptr = str.data();
dest.append(str_ptr, str_ptr + str.size());
}
template<size_t Buffer_Size>
inline void append_c_str(const char *c_str, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
auto len = std::char_traits<char>::length(c_str);
dest.append(c_str, c_str + len);
return spdlog::string_view_t(buf.data(), buf.size());
}
template<size_t Buffer_Size1, size_t Buffer_Size2>
......@@ -33,6 +26,16 @@ inline void append_buf(const fmt::basic_memory_buffer<char, Buffer_Size1> &buf,
dest.append(buf_ptr, buf_ptr + buf.size());
}
template<size_t Buffer_Size>
inline void append_string_view(spdlog::string_view_t view, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
auto *buf_ptr = view.data();
if (buf_ptr != nullptr)
{
dest.append(buf_ptr, buf_ptr + view.size());
}
}
template<typename T, size_t Buffer_Size>
inline void append_int(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
......@@ -40,73 +43,65 @@ inline void append_int(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
dest.append(i.data(), i.data() + i.size());
}
template<typename T>
inline unsigned count_digits(T n)
{
using count_type = typename std::conditional<(sizeof(T) > sizeof(uint32_t)), uint64_t, uint32_t>::type;
return static_cast<unsigned>(fmt::internal::count_digits(static_cast<count_type>(n)));
}
template<size_t Buffer_Size>
inline void pad2(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
if (n > 99)
{
append_int(n, dest);
return;
}
if (n > 9) // 10-99
else if (n > 9) // 10-99
{
dest.push_back(static_cast<char>('0' + n / 10));
dest.push_back(static_cast<char>('0' + n % 10));
return;
}
if (n >= 0) // 0-9
else if (n >= 0) // 0-9
{
dest.push_back('0');
dest.push_back(static_cast<char>('0' + n));
return;
}
// negatives (unlikely, but just in case, let fmt deal with it)
fmt::format_to(dest, "{:02}", n);
else // negatives (unlikely, but just in case, let fmt deal with it)
{
fmt::format_to(dest, "{:02}", n);
}
}
template<size_t Buffer_Size>
inline void pad3(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
template<typename T, size_t Buffer_Size>
inline void pad_uint(T n, unsigned int width, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
if (n > 999)
static_assert(std::is_unsigned<T>::value, "pad_uint must get unsigned T");
auto digits = count_digits(n);
if (width > digits)
{
append_int(n, dest);
return;
const char *zeroes = "0000000000000000000";
dest.append(zeroes, zeroes + width - digits);
}
append_int(n, dest);
}
if (n > 99) // 100-999
{
append_int(n / 100, dest);
pad2(n % 100, dest);
return;
}
if (n > 9) // 10-99
{
dest.push_back('0');
dest.push_back(static_cast<char>('0' + n / 10));
dest.push_back(static_cast<char>('0' + n % 10));
return;
}
if (n >= 0)
{
dest.push_back('0');
dest.push_back('0');
dest.push_back(static_cast<char>('0' + n));
return;
}
// negatives (unlikely, but just in case let fmt deal with it)
fmt::format_to(dest, "{:03}", n);
template<typename T, size_t Buffer_Size>
inline void pad3(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
pad_uint(n, 3, dest);
}
template<size_t Buffer_Size>
inline void pad6(size_t n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
template<typename T, size_t Buffer_Size>
inline void pad6(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
if (n > 99999)
{
append_int(n, dest);
return;
}
pad3(static_cast<int>(n / 1000), dest);
pad3(static_cast<int>(n % 1000), dest);
pad_uint(n, 6, dest);
}
template<typename T, size_t Buffer_Size>
inline void pad9(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
pad_uint(n, 9, dest);
}
// return fraction of a second of the given time_point.
......@@ -115,7 +110,8 @@ inline void pad6(size_t n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
template<typename ToDuration>
inline ToDuration time_fraction(const log_clock::time_point &tp)
{
using namespace std::chrono;
using std::chrono::duration_cast;
using std::chrono::seconds;
auto duration = tp.time_since_epoch();
auto secs = duration_cast<seconds>(duration);
return duration_cast<ToDuration>(duration) - duration_cast<ToDuration>(secs);
......
......@@ -15,9 +15,8 @@ namespace spdlog {
namespace details {
struct log_msg
{
log_msg() = default;
log_msg(const std::string *loggers_name, level::level_enum lvl)
log_msg(source_loc loc, const std::string *loggers_name, level::level_enum lvl, string_view_t view)
: logger_name(loggers_name)
, level(lvl)
#ifndef SPDLOG_NO_DATETIME
......@@ -26,23 +25,31 @@ struct log_msg
#ifndef SPDLOG_NO_THREAD_ID
, thread_id(os::thread_id())
, source(loc)
, payload(view)
#endif
{
}
log_msg(const log_msg &other) = delete;
log_msg(log_msg &&other) = delete;
log_msg &operator=(log_msg &&other) = delete;
log_msg(const std::string *loggers_name, level::level_enum lvl, string_view_t view)
: log_msg(source_loc{}, loggers_name, lvl, view)
{
}
log_msg(const log_msg &other) = default;
const std::string *logger_name{nullptr};
level::level_enum level;
level::level_enum level{level::off};
log_clock::time_point time;
size_t thread_id;
fmt::memory_buffer raw;
size_t thread_id{0};
size_t msg_id{0};
// info about wrapping the formatted text with color
// wrapping the formatted text with color (updated by pattern_formatter).
mutable size_t color_range_start{0};
mutable size_t color_range_end{0};
source_loc source;
const string_view_t payload;
};
} // namespace details
} // namespace spdlog
......@@ -10,19 +10,23 @@
#include <memory>
#include <string>
#define SPDLOG_CATCH_AND_HANDLE \
catch (const std::exception &ex) \
{ \
err_handler_(ex.what()); \
} \
catch (...) \
{ \
err_handler_("Unknown exception in logger"); \
}
// create logger with given name, sinks and the default pattern formatter
// all other ctors will call this one
template<typename It>
inline spdlog::logger::logger(std::string logger_name, const It &begin, const It &end)
inline spdlog::logger::logger(std::string logger_name, It begin, It end)
: name_(std::move(logger_name))
, sinks_(begin, end)
, level_(level::info)
, flush_level_(level::off)
, last_err_time_(0)
, msg_counter_(1) // message counter will start from 1. 0-message id will be
// reserved for controll messages
{
err_handler_ = [this](const std::string &msg) { this->default_err_handler_(msg); };
}
// ctor with sinks as init list
......@@ -49,11 +53,12 @@ inline void spdlog::logger::set_formatter(std::unique_ptr<spdlog::formatter> f)
inline void spdlog::logger::set_pattern(std::string pattern, pattern_time_type time_type)
{
set_formatter(std::unique_ptr<spdlog::formatter>(new pattern_formatter(std::move(pattern), time_type)));
auto new_formatter = details::make_unique<spdlog::pattern_formatter>(std::move(pattern), time_type);
set_formatter(std::move(new_formatter));
}
template<typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args)
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const char *fmt, const Args &... args)
{
if (!should_log(lvl))
{
......@@ -62,15 +67,43 @@ inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Ar
try
{
details::log_msg log_msg(&name_, lvl);
fmt::format_to(log_msg.raw, fmt, args...);
using details::fmt_helper::to_string_view;
fmt::memory_buffer buf;
fmt::format_to(buf, fmt, args...);
details::log_msg log_msg(source, &name_, lvl, to_string_view(buf));
sink_it_(log_msg);
}
SPDLOG_CATCH_AND_HANDLE
}
template<typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char *fmt, const Args &... args)
{
log(source_loc{}, lvl, fmt, args...);
}
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const char *msg)
{
if (!should_log(lvl))
{
return;
}
try
{
details::log_msg log_msg(source, &name_, lvl, spdlog::string_view_t(msg));
sink_it_(log_msg);
}
SPDLOG_CATCH_AND_HANDLE
}
inline void spdlog::logger::log(level::level_enum lvl, const char *msg)
{
log(source_loc{}, lvl, msg);
}
template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const T &msg)
{
if (!should_log(lvl))
{
......@@ -78,15 +111,20 @@ inline void spdlog::logger::log(level::level_enum lvl, const char *msg)
}
try
{
details::log_msg log_msg(&name_, lvl);
details::fmt_helper::append_c_str(msg, log_msg.raw);
details::log_msg log_msg(source, &name_, lvl, msg);
sink_it_(log_msg);
}
SPDLOG_CATCH_AND_HANDLE
}
template<typename T>
template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
{
log(source_loc{}, lvl, msg);
}
template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const T &msg)
{
if (!should_log(lvl))
{
......@@ -94,13 +132,21 @@ inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
}
try
{
details::log_msg log_msg(&name_, lvl);
fmt::format_to(log_msg.raw, "{}", msg);
using details::fmt_helper::to_string_view;
fmt::memory_buffer buf;
fmt::format_to(buf, "{}", msg);
details::log_msg log_msg(source, &name_, lvl, to_string_view(buf));
sink_it_(log_msg);
}
SPDLOG_CATCH_AND_HANDLE
}
template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type *>
inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
{
log(source_loc{}, lvl, msg);
}
template<typename... Args>
inline void spdlog::logger::trace(const char *fmt, const Args &... args)
{
......@@ -174,27 +220,56 @@ inline void spdlog::logger::critical(const T &msg)
}
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
inline void wbuf_to_utf8buf(const fmt::wmemory_buffer &wbuf, fmt::memory_buffer &target)
{
int wbuf_size = static_cast<int>(wbuf.size());
if (wbuf_size == 0)
{
return;
}
auto result_size = ::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, NULL, 0, NULL, NULL);
if (result_size > 0)
{
target.resize(result_size);
::WideCharToMultiByte(CP_UTF8, 0, wbuf.data(), wbuf_size, &target.data()[0], result_size, NULL, NULL);
}
else
{
throw spdlog::spdlog_ex(fmt::format("WideCharToMultiByte failed. Last error: {}", ::GetLastError()));
}
}
template<typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args)
inline void spdlog::logger::log(source_loc source, level::level_enum lvl, const wchar_t *fmt, const Args &... args)
{
if (!should_log(lvl))
{
return;
}
decltype(wstring_converter_)::byte_string utf8_string;
try
{
{
std::lock_guard<std::mutex> lock(wstring_converter_mutex_);
utf8_string = wstring_converter_.to_bytes(fmt);
}
log(lvl, utf8_string.c_str(), args...);
// format to wmemory_buffer and convert to utf8
using details::fmt_helper::to_string_view;
fmt::wmemory_buffer wbuf;
fmt::format_to(wbuf, fmt, args...);
fmt::memory_buffer buf;
wbuf_to_utf8buf(wbuf, buf);
details::log_msg log_msg(source, &name_, lvl, to_string_view(buf));
sink_it_(log_msg);
}
SPDLOG_CATCH_AND_HANDLE
}
template<typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t *fmt, const Args &... args)
{
log(source_loc{}, lvl, fmt, args...);
}
template<typename... Args>
inline void spdlog::logger::trace(const wchar_t *fmt, const Args &... args)
{
......@@ -251,7 +326,7 @@ inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handle
err_handler_ = std::move(err_handler);
}
inline spdlog::log_err_handler spdlog::logger::error_handler()
inline spdlog::log_err_handler spdlog::logger::error_handler() const
{
return err_handler_;
}
......@@ -281,6 +356,11 @@ inline bool spdlog::logger::should_flush_(const details::log_msg &msg)
return (msg.level >= flush_level) && (msg.level != level::off);
}
inline spdlog::level::level_enum spdlog::logger::default_level()
{
return static_cast<spdlog::level::level_enum>(SPDLOG_ACTIVE_LEVEL);
}
inline spdlog::level::level_enum spdlog::logger::level() const
{
return static_cast<spdlog::level::level_enum>(level_.load(std::memory_order_relaxed));
......@@ -310,7 +390,7 @@ inline void spdlog::logger::sink_it_(details::log_msg &msg)
if (should_flush_(msg))
{
flush();
flush_();
}
}
......
......@@ -57,7 +57,7 @@ namespace spdlog {
namespace details {
namespace os {
inline spdlog::log_clock::time_point now()
inline spdlog::log_clock::time_point now() SPDLOG_NOEXCEPT
{
#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
......@@ -70,7 +70,7 @@ inline spdlog::log_clock::time_point now()
return log_clock::now();
#endif
}
inline std::tm localtime(const std::time_t &time_tt)
inline std::tm localtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
{
#ifdef _WIN32
......@@ -83,13 +83,13 @@ inline std::tm localtime(const std::time_t &time_tt)
return tm;
}
inline std::tm localtime()
inline std::tm localtime() SPDLOG_NOEXCEPT
{
std::time_t now_t = time(nullptr);
return localtime(now_t);
}
inline std::tm gmtime(const std::time_t &time_tt)
inline std::tm gmtime(const std::time_t &time_tt) SPDLOG_NOEXCEPT
{
#ifdef _WIN32
......@@ -102,21 +102,11 @@ inline std::tm gmtime(const std::time_t &time_tt)
return tm;
}
inline std::tm gmtime()
inline std::tm gmtime() SPDLOG_NOEXCEPT
{
std::time_t now_t = time(nullptr);
return gmtime(now_t);
}
inline bool operator==(const std::tm &tm1, const std::tm &tm2)
{
return (tm1.tm_sec == tm2.tm_sec && tm1.tm_min == tm2.tm_min && tm1.tm_hour == tm2.tm_hour && tm1.tm_mday == tm2.tm_mday &&
tm1.tm_mon == tm2.tm_mon && tm1.tm_year == tm2.tm_year && tm1.tm_isdst == tm2.tm_isdst);
}
inline bool operator!=(const std::tm &tm1, const std::tm &tm2)
{
return !(tm1 == tm2);
}
// eol definition
#if !defined(SPDLOG_EOL)
......@@ -176,7 +166,7 @@ inline bool fopen_s(FILE **fp, const filename_t &filename, const filename_t &mod
return *fp == nullptr;
}
inline int remove(const filename_t &filename)
inline int remove(const filename_t &filename) SPDLOG_NOEXCEPT
{
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
return _wremove(filename.c_str());
......@@ -185,7 +175,7 @@ inline int remove(const filename_t &filename)
#endif
}
inline int rename(const filename_t &filename1, const filename_t &filename2)
inline int rename(const filename_t &filename1, const filename_t &filename2) SPDLOG_NOEXCEPT
{
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
return _wrename(filename1.c_str(), filename2.c_str());
......@@ -195,7 +185,7 @@ inline int rename(const filename_t &filename1, const filename_t &filename2)
}
// Return if file exists
inline bool file_exists(const filename_t &filename)
inline bool file_exists(const filename_t &filename) SPDLOG_NOEXCEPT
{
#ifdef _WIN32
#ifdef SPDLOG_WCHAR_FILENAMES
......@@ -220,10 +210,10 @@ inline size_t filesize(FILE *f)
#if defined(_WIN32) && !defined(__CYGWIN__)
int fd = _fileno(f);
#if _WIN64 // 64 bits
struct _stat64 st;
if (_fstat64(fd, &st) == 0)
__int64 ret = _filelengthi64(fd);
if (ret >= 0)
{
return st.st_size;
return static_cast<size_t>(ret);
}
#else // windows 32 bits
......@@ -323,7 +313,7 @@ inline int utc_minutes_offset(const std::tm &tm = details::os::localtime())
// Return current thread id as size_t
// It exists because the std::this_thread::get_id() is much slower(especially
// under VS 2013)
inline size_t _thread_id()
inline size_t _thread_id() SPDLOG_NOEXCEPT
{
#ifdef _WIN32
return static_cast<size_t>(::GetCurrentThreadId());
......@@ -346,10 +336,9 @@ inline size_t _thread_id()
}
// Return current thread id as size_t (from thread local storage)
inline size_t thread_id()
inline size_t thread_id() SPDLOG_NOEXCEPT
{
#if defined(SPDLOG_DISABLE_TID_CACHING) || (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt) || \
(defined(__clang__) && !__has_feature(cxx_thread_local))
#if defined(SPDLOG_NO_TLS)
return _thread_id();
#else // cache thread id in tls
static thread_local const size_t tid = _thread_id();
......@@ -359,7 +348,7 @@ inline size_t thread_id()
// This is avoid msvc issue in sleep_for that happens if the clock changes.
// See https://github.com/gabime/spdlog/issues/609
inline void sleep_for_millis(int milliseconds)
inline void sleep_for_millis(int milliseconds) SPDLOG_NOEXCEPT
{
#if defined(_WIN32)
::Sleep(milliseconds);
......@@ -396,7 +385,7 @@ inline int pid()
// Determine if the terminal supports colors
// Source: https://github.com/agauniyal/rang/
inline bool is_color_terminal()
inline bool is_color_terminal() SPDLOG_NOEXCEPT
{
#ifdef _WIN32
return true;
......@@ -418,7 +407,7 @@ inline bool is_color_terminal()
// Detrmine if the terminal attached
// Source: https://github.com/agauniyal/rang/
inline bool in_terminal(FILE *file)
inline bool in_terminal(FILE *file) SPDLOG_NOEXCEPT
{
#ifdef _WIN32
......
This diff is collapsed.
......@@ -10,7 +10,7 @@
//
// RAII over the owned thread:
// creates the thread on construction.
// stops and joins the thread on destruction.
// stops and joins the thread on destruction (if the thread is executing a callback, wait for it to finish first).
#include <chrono>
#include <condition_variable>
......
......@@ -14,6 +14,15 @@
#include "spdlog/details/periodic_worker.h"
#include "spdlog/logger.h"
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
// support for the default stdout color logger
#ifdef _WIN32
#include "spdlog/sinks/wincolor_sink.h"
#else
#include "spdlog/sinks/ansicolor_sink.h"
#endif
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
#include <chrono>
#include <functional>
#include <memory>
......@@ -33,18 +42,12 @@ public:
void register_logger(std::shared_ptr<logger> new_logger)
{
std::lock_guard<std::mutex> lock(logger_map_mutex_);
auto logger_name = new_logger->name();
throw_if_exists_(logger_name);
loggers_[logger_name] = std::move(new_logger);
register_logger_(std::move(new_logger));
}
void register_and_init(std::shared_ptr<logger> new_logger)
void initialize_logger(std::shared_ptr<logger> new_logger)
{
std::lock_guard<std::mutex> lock(logger_map_mutex_);
auto logger_name = new_logger->name();
throw_if_exists_(logger_name);
// set the global formatter pattern
new_logger->set_formatter(formatter_->clone());
if (err_handler_)
......@@ -55,8 +58,10 @@ public:
new_logger->set_level(level_);
new_logger->flush_on(flush_level_);
// add to registry
loggers_[logger_name] = std::move(new_logger);
if (automatic_registration_)
{
register_logger_(std::move(new_logger));
}
}
std::shared_ptr<logger> get(const std::string &logger_name)
......@@ -66,6 +71,38 @@ public:
return found == loggers_.end() ? nullptr : found->second;
}
std::shared_ptr<logger> default_logger()
{
std::lock_guard<std::mutex> lock(logger_map_mutex_);
return default_logger_;
}
// Return raw ptr to the default logger.
// To be used directly by the spdlog default api (e.g. spdlog::info)
// This make the default API faster, but cannot be used concurrently with set_default_logger().
// e.g do not call set_default_logger() from one thread while calling spdlog::info() from another.
logger *get_default_raw()
{
return default_logger_.get();
}
// set default logger.
// default logger is stored in default_logger_ (for faster retrieval) and in the loggers_ map.
void set_default_logger(std::shared_ptr<logger> new_default_logger)
{
std::lock_guard<std::mutex> lock(logger_map_mutex_);
// remove previous default logger from the map
if (default_logger_ != nullptr)
{
loggers_.erase(default_logger_->name());
}
if (new_default_logger != nullptr)
{
loggers_[new_default_logger->name()] = new_default_logger;
}
default_logger_ = std::move(new_default_logger);
}
void set_tp(std::shared_ptr<thread_pool> tp)
{
std::lock_guard<std::recursive_mutex> lock(tp_mutex_);
......@@ -113,7 +150,7 @@ public:
{
std::lock_guard<std::mutex> lock(flusher_mutex_);
std::function<void()> clbk = std::bind(&registry::flush_all, this);
periodic_flusher_ = spdlog::make_unique<periodic_worker>(clbk, interval);
periodic_flusher_ = details::make_unique<periodic_worker>(clbk, interval);
}
void set_error_handler(log_err_handler handler)
......@@ -148,15 +185,20 @@ public:
{
std::lock_guard<std::mutex> lock(logger_map_mutex_);
loggers_.erase(logger_name);
if (default_logger_ && default_logger_->name() == logger_name)
{
default_logger_.reset();
}
}
void drop_all()
{
std::lock_guard<std::mutex> lock(logger_map_mutex_);
loggers_.clear();
default_logger_.reset();
}
// clean all reasources and threads started by the registry
// clean all resources and threads started by the registry
void shutdown()
{
{
......@@ -177,6 +219,12 @@ public:
return tp_mutex_;
}
void set_automatic_registration(bool automatic_regsistration)
{
std::lock_guard<std::mutex> lock(logger_map_mutex_);
automatic_registration_ = automatic_regsistration;
}
static registry &instance()
{
static registry s_instance;
......@@ -185,8 +233,22 @@ public:
private:
registry()
: formatter_(new pattern_formatter("%+"))
: formatter_(new pattern_formatter())
{
#ifndef SPDLOG_DISABLE_DEFAULT_LOGGER
// create default logger (ansicolor_stdout_sink_mt or wincolor_stdout_sink_mt in windows).
#ifdef _WIN32
auto color_sink = std::make_shared<sinks::wincolor_stdout_sink_mt>();
#else
auto color_sink = std::make_shared<sinks::ansicolor_stdout_sink_mt>();
#endif
const char *default_logger_name = "";
default_logger_ = std::make_shared<spdlog::logger>(default_logger_name, std::move(color_sink));
loggers_[default_logger_name] = default_logger_;
#endif // SPDLOG_DISABLE_DEFAULT_LOGGER
}
~registry() = default;
......@@ -199,15 +261,24 @@ private:
}
}
void register_logger_(std::shared_ptr<logger> new_logger)
{
auto logger_name = new_logger->name();
throw_if_exists_(logger_name);
loggers_[logger_name] = std::move(new_logger);
}
std::mutex logger_map_mutex_, flusher_mutex_;
std::recursive_mutex tp_mutex_;
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
std::unique_ptr<formatter> formatter_;
level::level_enum level_ = level::info;
level::level_enum level_ = spdlog::logger::default_level();
level::level_enum flush_level_ = level::off;
log_err_handler err_handler_;
std::shared_ptr<thread_pool> tp_;
std::unique_ptr<periodic_worker> periodic_flusher_;
std::shared_ptr<logger> default_logger_;
bool automatic_registration_ = true;
};
} // namespace details
......
#pragma once
#include "spdlog/details/fmt_helper.h"
#include "spdlog/details/log_msg.h"
#include "spdlog/details/mpmc_blocking_q.h"
#include "spdlog/details/os.h"
......@@ -32,6 +33,7 @@ struct async_msg
fmt::basic_memory_buffer<char, 176> raw;
size_t msg_id;
source_loc source;
async_logger_ptr worker_ptr;
async_msg() = default;
......@@ -48,6 +50,7 @@ struct async_msg
thread_id(other.thread_id),
raw(move(other.raw)),
msg_id(other.msg_id),
source(other.source),
worker_ptr(std::move(other.worker_ptr))
{
}
......@@ -60,6 +63,7 @@ struct async_msg
thread_id = other.thread_id;
raw = std::move(other.raw);
msg_id = other.msg_id;
source = other.source;
worker_ptr = std::move(other.worker_ptr);
return *this;
}
......@@ -69,38 +73,45 @@ struct async_msg
#endif
// construct from log_msg with given type
async_msg(async_logger_ptr &&worker, async_msg_type the_type, details::log_msg &&m)
async_msg(async_logger_ptr &&worker, async_msg_type the_type, details::log_msg &m)
: msg_type(the_type)
, level(m.level)
, time(m.time)
, thread_id(m.thread_id)
, msg_id(m.msg_id)
, worker_ptr(std::forward<async_logger_ptr>(worker))
, source(m.source)
, worker_ptr(std::move(worker))
{
fmt_helper::append_buf(m.raw, raw);
fmt_helper::append_string_view(m.payload, raw);
}
async_msg(async_logger_ptr &&worker, async_msg_type the_type)
: async_msg(std::forward<async_logger_ptr>(worker), the_type, details::log_msg())
: msg_type(the_type)
, level(level::off)
, time()
, thread_id(0)
, msg_id(0)
, source()
, worker_ptr(std::move(worker))
{
}
explicit async_msg(async_msg_type the_type)
: async_msg(nullptr, the_type, details::log_msg())
: async_msg(nullptr, the_type)
{
}
// copy into log_msg
void to_log_msg(log_msg &msg)
log_msg to_log_msg()
{
msg.logger_name = &worker_ptr->name();
msg.level = level;
log_msg msg(&worker_ptr->name(), level, string_view_t(raw.data(), raw.size()));
msg.time = time;
msg.thread_id = thread_id;
fmt_helper::append_buf(raw, msg.raw);
msg.msg_id = msg_id;
msg.source = source;
msg.color_range_start = 0;
msg.color_range_end = 0;
return msg;
}
};
......@@ -146,9 +157,12 @@ public:
}
}
void post_log(async_logger_ptr &&worker_ptr, details::log_msg &&msg, async_overflow_policy overflow_policy)
thread_pool(const thread_pool &) = delete;
thread_pool &operator=(thread_pool &&) = delete;
void post_log(async_logger_ptr &&worker_ptr, details::log_msg &msg, async_overflow_policy overflow_policy)
{
async_msg async_m(std::forward<async_logger_ptr>(worker_ptr), async_msg_type::log, std::forward<log_msg>(msg));
async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg);
post_async_msg_(std::move(async_m), overflow_policy);
}
......@@ -200,8 +214,7 @@ private:
{
case async_msg_type::log:
{
log_msg msg;
incoming_async_msg.to_log_msg(msg);
auto msg = incoming_async_msg.to_log_msg();
incoming_async_msg.worker_ptr->backend_log_(msg);
return true;
}
......
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
//
// Support for logging binary data as hex
// 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.
//
// Examples:
//
// std::vector<char> v(200, 0x0b);
// logger->info("Some buffer {}", spdlog::to_hex(v));
// char buf[128];
// logger->info("Some buffer {:X}", spdlog::to_hex(std::begin(buf), std::end(buf)));
namespace spdlog {
namespace details {
template<typename It>
class bytes_range
{
public:
bytes_range(It range_begin, It range_end)
: begin_(range_begin)
, end_(range_end)
{
}
It begin() const
{
return begin_;
}
It end() const
{
return end_;
}
private:
It begin_, end_;
};
} // namespace details
// create a bytes_range that wraps the given container
template<typename Container>
inline details::bytes_range<typename Container::const_iterator> to_hex(const Container &container)
{
static_assert(sizeof(typename Container::value_type) == 1, "sizeof(Container::value_type) != 1");
using Iter = typename Container::const_iterator;
return details::bytes_range<Iter>(std::begin(container), std::end(container));
}
// create bytes_range from ranges
template<typename It>
inline details::bytes_range<It> to_hex(const It range_begin, const It range_end)
{
return details::bytes_range<It>(range_begin, range_end);
}
} // namespace spdlog
namespace fmt {
template<typename T>
struct formatter<spdlog::details::bytes_range<T>>
{
const std::size_t line_size = 100;
const char delimiter = ' ';
bool put_newlines = true;
bool put_delimiters = true;
bool use_uppercase = false;
bool put_positions = true; // position on start of each line
// parse the format string flags
template<typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin())
{
auto it = ctx.begin();
while (*it && *it != '}')
{
switch (*it)
{
case 'X':
use_uppercase = true;
break;
case 's':
put_delimiters = false;
break;
case 'p':
put_positions = false;
break;
case 'n':
put_newlines = false;
break;
}
++it;
}
return it;
}
// format the given bytes range as hex
template<typename FormatContext, typename Container>
auto format(const spdlog::details::bytes_range<Container> &the_range, FormatContext &ctx) -> decltype(ctx.out())
{
SPDLOG_CONSTEXPR const char *hex_upper = "0123456789ABCDEF";
SPDLOG_CONSTEXPR const char *hex_lower = "0123456789abcdef";
const char *hex_chars = use_uppercase ? hex_upper : hex_lower;
std::size_t pos = 0;
std::size_t column = line_size;
auto inserter = ctx.begin();
for (auto &item : the_range)
{
auto ch = static_cast<unsigned char>(item);
pos++;
if (put_newlines && column >= line_size)
{
column = put_newline(inserter, pos);
// put first byte without delimiter in front of it
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
*inserter++ = hex_chars[ch & 0x0f];
column += 2;
continue;
}
if (put_delimiters)
{
*inserter++ = delimiter;
++column;
}
*inserter++ = hex_chars[(ch >> 4) & 0x0f];
*inserter++ = hex_chars[ch & 0x0f];
column += 2;
}
return inserter;
}
// put newline(and position header)
// return the next column
template<typename It>
std::size_t put_newline(It inserter, std::size_t pos)
{
#ifdef _WIN32
*inserter++ = '\r';
#endif
*inserter++ = '\n';
if (put_positions)
{
fmt::format_to(inserter, "{:<04X}: ", pos - 1);
return 7;
}
else
{
return 1;
}
}
};
} // namespace fmt
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
// Formatting library for C++ - locale support
// Formatting library for C++ - std::locale support
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_LOCALE_H_
#define FMT_LOCALE_H_
#include "format.h"
#include <locale>
namespace fmt {
class locale
{
private:
std::locale locale_;
public:
explicit locale(std::locale loc = std::locale())
: locale_(loc)
{
}
std::locale get()
{
return locale_;
}
};
} // namespace fmt
FMT_BEGIN_NAMESPACE
namespace internal {
template <typename Char>
typename buffer_context<Char>::type::iterator vformat_to(
const std::locale &loc, basic_buffer<Char> &buf,
basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) {
typedef back_insert_range<basic_buffer<Char> > range;
return vformat_to<arg_formatter<range>>(
buf, to_string_view(format_str), args, internal::locale_ref(loc));
}
template <typename Char>
std::basic_string<Char> vformat(
const std::locale &loc, basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) {
basic_memory_buffer<Char> buffer;
internal::vformat_to(loc, buffer, format_str, args);
return fmt::to_string(buffer);
}
}
template <typename S, typename Char = FMT_CHAR(S)>
inline std::basic_string<Char> vformat(
const std::locale &loc, const S &format_str,
basic_format_args<typename buffer_context<Char>::type> args) {
return internal::vformat(loc, to_string_view(format_str), args);
}
template <typename S, typename... Args>
inline std::basic_string<FMT_CHAR(S)> format(
const std::locale &loc, const S &format_str, const Args &... args) {
return internal::vformat(
loc, to_string_view(format_str),
*internal::checked_args<S, Args...>(format_str, args...));
}
template <typename String, typename OutputIt, typename... Args>
inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value,
OutputIt>::type
vformat_to(OutputIt out, const std::locale &loc, const String &format_str,
typename format_args_t<OutputIt, FMT_CHAR(String)>::type args) {
typedef output_range<OutputIt, FMT_CHAR(String)> range;
return vformat_to<arg_formatter<range>>(
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
}
template <typename OutputIt, typename S, typename... Args>
inline typename std::enable_if<
internal::is_string<S>::value &&
internal::is_output_iterator<OutputIt>::value, OutputIt>::type
format_to(OutputIt out, const std::locale &loc, const S &format_str,
const Args &... args) {
internal::check_format_string<Args...>(format_str);
typedef typename format_context_t<OutputIt, FMT_CHAR(S)>::type context;
format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str),
basic_format_args<context>(as));
}
FMT_END_NAMESPACE
#endif // FMT_LOCALE_H_
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -35,7 +35,7 @@ public:
logger(std::string name, sinks_init_list sinks);
template<typename It>
logger(std::string name, const It &begin, const It &end);
logger(std::string name, It begin, It end);
virtual ~logger();
......@@ -46,8 +46,12 @@ public:
void log(level::level_enum lvl, const char *fmt, const Args &... args);
template<typename... Args>
void log(source_loc loc, level::level_enum lvl, const char *fmt, const Args &... args);
void log(level::level_enum lvl, const char *msg);
void log(source_loc loc, level::level_enum lvl, const char *msg);
template<typename... Args>
void trace(const char *fmt, const Args &... args);
......@@ -67,9 +71,15 @@ public:
void critical(const char *fmt, const Args &... args);
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
#ifndef _WIN32
#error SPDLOG_WCHAR_TO_UTF8_SUPPORT only supported on windows
#else
template<typename... Args>
void log(level::level_enum lvl, const wchar_t *fmt, const Args &... args);
template<typename... Args>
void log(source_loc source, level::level_enum lvl, const wchar_t *fmt, const Args &... args);
template<typename... Args>
void trace(const wchar_t *fmt, const Args &... args);
......@@ -87,11 +97,25 @@ public:
template<typename... Args>
void critical(const wchar_t *fmt, const Args &... args);
#endif // _WIN32
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
template<typename T>
// T can be statically converted to string_view
template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
void log(level::level_enum lvl, const T &);
// T can be statically converted to string_view
template<class T, typename std::enable_if<std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
void log(source_loc loc, level::level_enum lvl, const T &);
// T cannot be statically converted to string_view
template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
void log(level::level_enum lvl, const T &);
// T cannot be statically converted to string_view
template<class T, typename std::enable_if<!std::is_convertible<T, spdlog::string_view_t>::value, T>::type * = nullptr>
void log(source_loc loc, level::level_enum lvl, const T &);
template<typename T>
void trace(const T &msg);
......@@ -112,6 +136,8 @@ public:
bool should_log(level::level_enum msg_level) const;
void set_level(level::level_enum log_level);
static level::level_enum default_level();
level::level_enum level() const;
const std::string &name() const;
......@@ -131,7 +157,7 @@ public:
// error handler
void set_error_handler(log_err_handler err_handler);
log_err_handler error_handler();
log_err_handler error_handler() const;
// create new logger with same sinks and configuration.
virtual std::shared_ptr<logger> clone(std::string logger_name);
......@@ -142,26 +168,20 @@ protected:
bool should_flush_(const details::log_msg &msg);
// default error handler: print the error to stderr with the max rate of 1
// message/minute
// default error handler.
// print the error to stderr with the max rate of 1 message/minute.
void default_err_handler_(const std::string &msg);
// increment the message count (only if
// defined(SPDLOG_ENABLE_MESSAGE_COUNTER))
// increment the message count (only if defined(SPDLOG_ENABLE_MESSAGE_COUNTER))
void incr_msg_counter_(details::log_msg &msg);
const std::string name_;
std::vector<sink_ptr> sinks_;
spdlog::level_t level_;
spdlog::level_t flush_level_;
log_err_handler err_handler_;
std::atomic<time_t> last_err_time_;
std::atomic<size_t> msg_counter_;
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
std::wstring_convert<std::codecvt_utf8<wchar_t>> wstring_converter_;
std::mutex wstring_converter_mutex_;
#endif
spdlog::level_t level_{spdlog::logger::default_level()};
spdlog::level_t flush_level_{level::off};
log_err_handler err_handler_{[this](const std::string &msg) { this->default_err_handler_(msg); }};
std::atomic<time_t> last_err_time_{0};
std::atomic<size_t> msg_counter_{1};
};
} // namespace spdlog
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -5,6 +5,10 @@
#pragma once
#ifndef SPDLOG_H
#error "spdlog.h must be included before this file."
#endif
#if defined(_WIN32)
#include "spdlog/details/null_mutex.h"
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
......@@ -16,7 +16,7 @@ class sink
public:
sink()
: level_(level::trace)
, formatter_(new pattern_formatter("%+"))
, formatter_(new pattern_formatter())
{
}
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment