Merge branch 'v1.x' of https://github.com/gabime/spdlog into v1.x

This commit is contained in:
gabime 2018-07-10 10:34:08 +03:00
commit f9019870da
44 changed files with 1095 additions and 846 deletions

View File

@ -4,7 +4,7 @@
#
cmake_minimum_required(VERSION 3.1)
project(spdlog VERSION 0.16.3 LANGUAGES CXX)
project(spdlog VERSION 1.0.0 LANGUAGES CXX)
include(CTest)
include(CMakeDependentOption)
include(GNUInstallDirs)

241
README.md
View File

@ -20,7 +20,7 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
## Platforms
* Linux, FreeBSD, Solaris
* Linux, FreeBSD, Solaris, AIX
* Windows (vc 2013+, cygwin)
* Mac OSX (clang 3.5+)
* Android
@ -29,8 +29,7 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
* Very fast - performance is the primary goal (see [benchmarks](#benchmarks) below).
* Headers only, just copy and use.
* Feature rich [call style](#usage-example) using the excellent [fmt](https://github.com/fmtlib/fmt) library.
* Optional printf syntax support.
* Extremely fast asynchronous mode (optional) - using lockfree queues and other tricks to reach millions of calls/sec.
* Fast asynchronous mode (optional)
* [Custom](https://github.com/gabime/spdlog/wiki/3.-Custom-formatting) formatting.
* Conditional Logging
* Multi/Single threaded loggers.
@ -47,61 +46,51 @@ Very fast, header only, C++ logging library. [![Build Status](https://travis-ci.
## Benchmarks
Below are some [benchmarks](bench) comparing popular log libraries under Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz
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
Time needed to log 1,000,000 lines in synchronous mode (in seconds, the best of 3 runs):
|threads|boost log 1.54|glog |easylogging |spdlog|
|-------|:-------:|:-----:|----------:|------:|
|1| 4.169s |1.066s |0.975s |0.302s|
|10| 6.180s |3.032s |2.857s |0.968s|
|100| 5.981s |1.139s |4.512s |0.497s|
```
*******************************************************************************
Single thread, 1,000,000 iterations
*******************************************************************************
basic_st... Elapsed: 0.231041 4,328,228/sec
rotating... Elapsed: 0.233466 4,283,282/sec
daily_st... Elapsed: 0.244491 4,090,136/sec
null_st... Elapsed: 0.162708 6,145,995/sec
*******************************************************************************
10 threads sharing same logger, 1,000,000 iterations
*******************************************************************************
basic_mt... Elapsed: 0.854029 1,170,920/sec
rotating_mt Elapsed: 0.867038 1,153,351/sec
daily_mt... Elapsed: 0.869593 1,149,963/sec
null_mt... Elapsed: 0.171215 5,840,602/sec
```
#### Asynchronous mode
Time needed to log 1,000,000 lines in asynchronous mode, i.e. the time it takes to put them in the async queue (in seconds, the best of 3 runs):
|threads|g2log <sup>async logger</sup> |spdlog <sup>async mode</sup>|
|:-------|:-----:|-------------------------:|
|1| 1.850s |0.216s |
|10| 0.943s |0.173s|
|100| 0.959s |0.202s|
```
*******************************************************************************
10 threads sharing same logger, 1,000,000 iterations
*******************************************************************************
async... Elapsed: 0.442731 2,258,706/sec
async... Elapsed: 0.427072 2,341,527/sec
async... Elapsed: 0.449768 2,223,369/sec
```
## Usage Example
```c++
#define SPDLOG_TRACE_ON
#define SPDLOG_DEBUG_ON
// include only features that you use
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/simple_file_sink.h"
#include "spdlog/spdlog.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include <iostream>
#include <memory>
void async_example();
void user_defined_example();
void err_handler_example();
namespace spd = spdlog;
int main(int, char *[])
void stdout_example()
{
try
{
auto console = spdlog::stdout_color_st("console");
console->info("Welcome to spdlog!");
// 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);
err_handler_example();
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);
@ -109,107 +98,86 @@ int main(int, char *[])
console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
// Create basic file logger (not rotated)
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt");
my_logger->info("Some log message");
// Create a file rotating logger with 5mb size max and 3 rotated files
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
for (int i = 0; i < 10; ++i)
{
rotating_logger->info("{} * {} equals {:>10}", i, i, i * i);
}
// Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
// trigger flush if the log severity is error or higher
daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44);
// Customize msg format for all messages
spd::set_pattern("[%^+++%$] [%H:%M:%S %z] [thread %t] %v");
console->info("This an info message with custom format");
console->error("This an error message with custom format");
// Change format back to to default
spd::set_pattern("%+");
spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
// Runtime log levels
spd::set_level(spd::level::info); // Set global log level to info
spdlog::set_level(spdlog::level::info); // Set global log level to info
console->debug("This message should not be displayed!");
console->set_level(spd::level::debug); // Set specific logger's log level
console->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);
}
// Asynchronous logging is very fast..
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
async_example();
// Log user-defined types example
user_defined_example();
// Change default log error handler
err_handler_example();
// Apply a function on all registered loggers
spd::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); });
// Release and close all loggers
spdlog::drop_all();
#include "spdlog/sinks/basic_file_sink.h"
void basic_logfile_example()
{
// Create basic file logger (not rotated)
try
{
auto my_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
}
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
catch (const spd::spdlog_ex &ex)
catch (const spdlog::spdlog_ex &ex)
{
std::cout << "Log init failed: " << ex.what() << std::endl;
return 1;
}
}
// must be included to use async logger
#include "spdlog/sinks/rotating_file_sink.h"
void rotating_example()
{
// Create a file rotating logger with 5mb size max and 3 rotated files
auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
}
#include "spdlog/sinks/daily_file_sink.h"
void daily_example()
{
// Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
}
#include "spdlog/async.h"
void async_example()
{
auto async_file = spd::basic_logger_mt<spdlog::create_async>("async_file_logger", "logs/async_log.txt");
for (int i = 0; i < 100; ++i)
// default thread pool settings can be modified *before* creating the async logger:
// spdlog::init_thread_pool(32768, 1); // queue with 32k 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");
for (int i = 1; i < 101; ++i)
{
async_file->info("Async message #{}", i);
}
// you can also modify thread pool settings *before* creating the logger:
// spdlog::init_thread_pool(32768, 4); // queue with 32k of pre allocated items and 4 backing threads.
// if not called a defaults are: preallocated 8192 queue items and 1 worker thread.
}
// syslog example (linux/osx/freebsd)
#ifndef _WIN32
#include "spdlog/sinks/syslog_sink.h"
void syslog_example()
// 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()
{
std::string ident = "spdlog-example";
auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID);
syslog_logger->warn("This is warning that will end up in syslog.");
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");
}
#endif
// Android example
#if defined(__ANDROID__)
#incude "spdlog/sinks/android_sink.h"
void android_example()
{
std::string tag = "spdlog-android";
auto android_logger = spd::android_logger("android", tag);
android_logger->critical("Use \"adb shell logcat\" to view this message.");
}
#endif
// user defined types logging by implementing operator<<
#include "spdlog/fmt/ostr.h" // must be included
struct my_type
{
int i;
@ -220,10 +188,9 @@ struct my_type
}
};
#include "spdlog/fmt/ostr.h" // must be included
void user_defined_example()
{
spd::get("console")->info("user defined type: {}", my_type{14});
spdlog::get("console")->info("user defined type: {}", my_type{14});
}
//
@ -231,12 +198,34 @@ void user_defined_example()
//
void err_handler_example()
{
// can be set globaly or per logger(logger->set_error_handler(..))
spdlog::set_error_handler([](const std::string &msg) { spd::get("console")->error("*******my err handler: {}", msg); });
spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
// spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
// 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 example (linux/osx/freebsd)
#ifndef _WIN32
#include "spdlog/sinks/syslog_sink.h"
void syslog_example()
{
std::string ident = "spdlog-example";
auto syslog_logger = spdlog::syslog_logger("syslog", ident, LOG_PID);
syslog_logger->warn("This is warning that will end up in syslog.");
}
#endif
// Android example
#if defined(__ANDROID__)
#incude "spdlog/sinks/android_sink.h"
void android_example()
{
std::string tag = "spdlog-android";
auto android_logger = spdlog::android_logger("android", tag);
android_logger->critical("Use \"adb shell logcat\" to view this message.");
}
#endif
```
## Documentation

View File

@ -1,17 +1,18 @@
CXX ?= g++
CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -I../include
CXX_RELEASE_FLAGS = -O3 -flto -DNDEBUG
CXX = g++
CXXFLAGS = -march=native -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1
CXX_RELEASE_FLAGS = -O3 -flto
binaries=bench spdlog-null-async
binaries=bench latency
all: $(binaries)
bench: bench.cpp
$(CXX) bench.cpp -o bench $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
spdlog-null-async: spdlog-null-async.cpp
$(CXX) spdlog-null-async.cpp -o spdlog-null-async $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
latency: latency.cpp
$(CXX) latency.cpp -o latency $(CXXFLAGS) $(CXX_RELEASE_FLAGS)
.PHONY: clean

View File

@ -7,10 +7,10 @@
// bench.cpp : spdlog benchmarks
//
#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/sinks/simple_file_sink.h"
#include "spdlog/spdlog.h"
#include "utils.h"
#include <atomic>
@ -32,8 +32,9 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count
int main(int argc, char *argv[])
{
int queue_size = 1048576;
int howmany = 1000000;
int queue_size = howmany + 2;
int threads = 10;
int file_size = 30 * 1024 * 1024;
int rotating_files = 5;
@ -52,16 +53,24 @@ int main(int argc, char *argv[])
cout << "Single thread, " << format(howmany) << " iterations" << endl;
cout << "*******************************************************************************\n";
auto basic_st = spdlog::basic_logger_mt("basic_st", "logs/basic_st.log", true);
bench(howmany, basic_st);
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
bench(howmany, rotating_st);
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
bench(howmany, daily_st);
bench(howmany, spdlog::create<null_sink_st>("null_st"));
cout << "\n*******************************************************************************\n";
cout << threads << " threads sharing same logger, " << format(howmany) << " iterations" << endl;
cout << "*******************************************************************************\n";
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
bench_mt(howmany, 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);
@ -76,9 +85,9 @@ int main(int argc, char *argv[])
for (int i = 0; i < 3; ++i)
{
spdlog::init_thread_pool(queue_size, 1);
auto as = spdlog::basic_logger_mt<spdlog::create_async>("as", "logs/basic_async.log", true);
auto as = spdlog::basic_logger_mt<spdlog::async_factory>("async", "logs/basic_async.log", true);
bench_mt(howmany, as, threads);
spdlog::drop("as");
spdlog::drop("async");
}
}
catch (std::exception &ex)
@ -92,34 +101,31 @@ 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 = system_clock::now();
auto start = high_resolution_clock::now();
for (auto i = 0; i < howmany; ++i)
{
log->info("Hello logger: msg number {}", i);
}
auto delta = system_clock::now() - start;
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;
}
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;
std::atomic<int> msg_counter{0};
vector<thread> threads;
auto start = system_clock::now();
auto start = high_resolution_clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]() {
for (;;)
for (int j = 0; j < howmany / thread_count; j++)
{
int counter = ++msg_counter;
if (counter > howmany)
break;
log->info("Hello logger: msg number {}", counter);
log->info("Hello logger: msg number {}", j);
}
}));
}
@ -129,7 +135,7 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count
t.join();
};
auto delta = system_clock::now() - start;
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;
}

149
bench/latency.cpp Normal file
View File

@ -0,0 +1,149 @@
//
// Copyright(c) 2018 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
//
// latency.cpp : spdlog latency benchmarks
//
#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>
#include <iostream>
#include <memory>
#include <string>
#include <thread>
using namespace std;
using namespace std::chrono;
using namespace spdlog;
using namespace spdlog::sinks;
using namespace utils;
void bench(int howmany, std::shared_ptr<spdlog::logger> log);
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
int main(int , char *[])
{
std::srand(std::time(nullptr)); // use current time as seed for random generator
int howmany = 1000000;
int queue_size = howmany + 2;
int threads = 10;
int file_size = 30 * 1024 * 1024;
int rotating_files = 5;
try
{
cout << "*******************************************************************************\n";
cout << "Single thread\n";
cout << "*******************************************************************************\n";
auto basic_st = spdlog::basic_logger_mt("basic_st", "logs/basic_st.log", true);
bench(howmany, basic_st);
auto rotating_st = spdlog::rotating_logger_st("rotating_st", "logs/rotating_st.log", file_size, rotating_files);
bench(howmany, rotating_st);
auto daily_st = spdlog::daily_logger_st("daily_st", "logs/daily_st.log");
bench(howmany, daily_st);
bench(howmany, spdlog::create<null_sink_st>("null_st"));
cout << "\n*******************************************************************************\n";
cout << threads << " threads sharing same logger\n";
cout << "*******************************************************************************\n";
auto basic_mt = spdlog::basic_logger_mt("basic_mt", "logs/basic_mt.log", true);
bench_mt(howmany, 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);
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt.log");
bench_mt(howmany, daily_mt, threads);
bench(howmany, spdlog::create<null_sink_st>("null_mt"));
cout << "\n*******************************************************************************\n";
cout << "async logging.. " << threads << " threads sharing same logger\n";
cout << "*******************************************************************************\n";
for (int i = 0; i < 3; ++i)
{
spdlog::init_thread_pool(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");
}
}
catch (std::exception &ex)
{
std::cerr << "Error: " << ex.what() << std::endl;
perror("Last error");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
void bench(int howmany, std::shared_ptr<spdlog::logger> log)
{
using namespace std::chrono;
using chrono::high_resolution_clock;
using chrono::milliseconds;
using chrono::nanoseconds;
cout << log->name() << "...\t\t" << flush;
nanoseconds total_nanos = nanoseconds::zero();
for (auto i = 0; i < howmany; ++i)
{
auto start = high_resolution_clock::now();
log->info("Hello logger: msg number {}", i);
auto delta_nanos = chrono::duration_cast<nanoseconds>(high_resolution_clock::now() - start);
total_nanos+= delta_nanos;
}
auto avg = total_nanos.count()/howmany;
cout << format(avg) << " ns/call" << endl;
}
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
{
using namespace std::chrono;
using chrono::high_resolution_clock;
using chrono::milliseconds;
using chrono::nanoseconds;
cout << log->name() << "...\t\t" << flush;
vector<thread> threads;
std::atomic<nanoseconds::rep> total_nanos{0};
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]() {
for (int j = 0; j < howmany / thread_count; j++)
{
auto start = high_resolution_clock::now();
log->info("Hello logger: msg number {}", j);
auto delta_nanos = chrono::duration_cast<nanoseconds>(high_resolution_clock::now() - start);
total_nanos+= delta_nanos.count();
}
}));
}
for (auto &t : threads)
{
t.join();
};
auto avg = total_nanos/howmany;
cout << format(avg) << " ns/call" << endl;
}

View File

@ -11,7 +11,7 @@
#include <vector>
#include "spdlog/async.h"
#include "spdlog/sinks/simple_file_sink.h"
#include "spdlog/sinks/basic_file_sink.h"
using namespace std;
@ -27,7 +27,7 @@ int main(int argc, char *argv[])
int howmany = 1000000;
spdlog::init_thread_pool(howmany, 1);
auto logger = spdlog::create_async_logger<spdlog::sinks::simple_file_sink_mt>("file_logger", "logs/spdlog-bench-async.log", false);
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;

View File

@ -1,116 +0,0 @@
//
// Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
//
// bench.cpp : spdlog benchmarks
//
#include "spdlog/async.h"
#include "spdlog/async_logger.h"
#include "spdlog/sinks/null_sink.h"
#include "spdlog/spdlog.h"
#include "utils.h"
#include <atomic>
#include <cstdlib> // EXIT_FAILURE
#include <iostream>
#include <memory>
#include <string>
#include <thread>
using namespace std;
using namespace std::chrono;
using namespace spdlog;
using namespace spdlog::sinks;
using namespace utils;
size_t bench_as(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
int main(int argc, char *argv[])
{
int howmany;
int tp_queue_size;
int tp_threads = 1;
int client_threads = 10;
int iters = 10;
try
{
if (argc < 2)
{
cout << "Usage: " << argv[0] << " <howmany> [client_threads] [q_size] [tp_threads]" << endl;
return (1);
}
howmany = atoi(argv[1]);
if (argc > 2)
client_threads = atoi(argv[2]);
if (argc > 3)
tp_queue_size = atoi(argv[3]);
else
tp_queue_size = howmany;
if (argc > 4)
tp_threads = atoi(argv[4]);
cout << "\n*******************************************************************************\n";
cout << "messages:\t" << format(howmany) << endl;
cout << "client_threads:\t" << client_threads << endl;
cout << "tp queue:\t" << format(tp_queue_size) << endl;
cout << "tp threads:\t" << tp_threads << endl;
cout << "*******************************************************************************\n";
size_t total_rate = 0;
for (int i = 0; i < iters; ++i)
{
spdlog::init_thread_pool(tp_queue_size, tp_threads);
auto as = spdlog::create_async_logger<null_sink_mt>("async(null-sink)");
total_rate += bench_as(howmany, as, client_threads);
spdlog::drop("async(null-sink)");
}
std::cout << endl;
std::cout << "Avg rate: " << format(total_rate / iters) << "/sec" << std::endl;
}
catch (std::exception &ex)
{
std::cerr << "Error: " << ex.what() << std::endl;
perror("Last error");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
// return rate/sec
size_t bench_as(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count)
{
cout << log->name() << "...\t\t" << flush;
std::atomic<int> msg_counter{0};
vector<thread> threads;
auto start = system_clock::now();
for (int t = 0; t < thread_count; ++t)
{
threads.push_back(std::thread([&]() {
for (;;)
{
int counter = ++msg_counter;
if (counter > howmany)
break;
log->info("Hello logger: msg number {}", counter);
}
}));
}
for (auto &t : threads)
{
t.join();
}
auto delta = system_clock::now() - start;
auto delta_d = duration_cast<duration<double>>(delta).count();
auto per_sec = size_t(howmany / delta_d);
cout << format(per_sec) << "/sec" << endl;
return per_sec;
}

View File

@ -1,5 +1,5 @@
CXX = g++
CXX_FLAGS = -Wall -Wextra -pedantic -std=c++11 -pthread -I../include
CXX_FLAGS = -Wall -Wextra -pedantic -std=c++11 -pthread -I../include -fmax-errors=1
CXX_RELEASE_FLAGS = -O3 -march=native
CXX_DEBUG_FLAGS= -g

View File

@ -7,34 +7,69 @@
//
//
#define SPDLOG_TRACE_ON
#define SPDLOG_DEBUG_ON
#include "spdlog/sinks/daily_file_sink.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/simple_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include <iostream>
#include <memory>
void stdout_example();
void basic_example();
void rotating_example();
void daily_example();
void async_example();
void multi_sink_example();
void user_defined_example();
void err_handler_example();
void syslog_example();
namespace spd = spdlog;
#include "spdlog/spdlog.h"
int main(int, char *[])
{
try
{
auto console = spdlog::stdout_color_st("console");
console->info("Welcome to spdlog!");
// console logging example
stdout_example();
// various file loggers
basic_example();
rotating_example();
daily_example();
// async logging using a backing thread pool
async_example();
// a logger can have multiple targets with different formats
multi_sink_example();
// user defined types logging by implementing operator<<
user_defined_example();
// custom error handler
err_handler_example();
// apply some function on all registered loggers
spdlog::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); });
// Release and close all loggers
spdlog::drop_all();
}
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
catch (const spdlog::spdlog_ex &ex)
{
std::cout << "Log init failed: " << ex.what() << std::endl;
return 1;
}
}
#include "spdlog/sinks/stdout_color_sinks.h" // or "/sinks/stdout_sinks.h" if no colors needed
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);
err_handler_example();
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);
@ -42,105 +77,79 @@ int main(int, char *[])
console->info("Positional args are {1} {0}..", "too", "supported");
console->info("{:<30}", "left aligned");
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
// Create basic file logger (not rotated)
auto my_logger = spd::basic_logger_mt("basic_logger", "logs/basic-log.txt");
my_logger->info("Some log message");
// Create a file rotating logger with 5mb size max and 3 rotated files
auto rotating_logger = spd::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
for (int i = 0; i < 10; ++i)
{
rotating_logger->info("{} * {} equals {:>10}", i, i, i * i);
}
// Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spd::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
// trigger flush if the log severity is error or higher
daily_logger->flush_on(spd::level::err);
daily_logger->info(123.44);
// Customize msg format for all messages
spd::set_pattern("[%^+++%$] [%H:%M:%S %z] [thread %t] %v");
console->info("This an info message with custom format");
console->error("This an error message with custom format");
// Change format back to to default
spd::set_pattern("%+");
spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)");
// Runtime log levels
spd::set_level(spd::level::info); // Set global log level to info
spdlog::set_level(spdlog::level::info); // Set global log level to info
console->debug("This message should not be displayed!");
console->set_level(spd::level::trace); // Set specific logger's log level
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);
// Asynchronous logging example
async_example();
// Log user-defined types example
user_defined_example();
// Change default log error handler
err_handler_example();
// Apply a function on all registered loggers
spd::apply_all([&](std::shared_ptr<spdlog::logger> l) { l->info("End of example."); });
// Release and close all loggers
spdlog::drop_all();
}
// Exceptions will only be thrown upon failed logger or sink construction (not during logging)
catch (const spd::spdlog_ex &ex)
{
std::cout << "Log init failed: " << ex.what() << std::endl;
return 1;
}
}
// must be included to use async logger
#include "spdlog/sinks/basic_file_sink.h"
void basic_example()
{
// Create basic file logger (not rotated)
auto my_logger = spdlog::basic_logger_mt("basic_logger", "logs/basic-log.txt");
}
#include "spdlog/sinks/rotating_file_sink.h"
void rotating_example()
{
// Create a file rotating logger with 5mb size max and 3 rotated files
auto rotating_logger = spdlog::rotating_logger_mt("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
}
#include "spdlog/sinks/daily_file_sink.h"
void daily_example()
{
// Create a daily logger - a new file is created every day on 2:30am
auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30);
}
#include "spdlog/async.h"
void async_example()
{
auto async_file = spd::basic_logger_mt<spdlog::create_async>("async_file_logger", "logs/async_log.txt");
// default thread pool settings can be modified *before* creating the async logger:
// spdlog::init_thread_pool(32768, 1); // queue with max 32k items 1 backing thread.
auto async_file = spdlog::basic_logger_mt<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");
// thread pool settings can be modified *before* creating the async logger:
// spdlog::init_thread_pool(32768, 4); // queue with max 32k items 4 backing threads.
for (int i = 0; i < 100; ++i)
for (int i = 1; i < 101; ++i)
{
async_file->info("Async message #{}", i + 1);
async_file->info("Async message #{}", i);
}
}
// syslog example (linux/osx/freebsd)
#ifndef _WIN32
#include "spdlog/sinks/syslog_sink.h"
void syslog_example()
// 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()
{
std::string ident = "spdlog-example";
auto syslog_logger = spd::syslog_logger("syslog", ident, LOG_PID);
syslog_logger->warn("This is warning that will end up in syslog.");
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");
}
#endif
// Android example
#if defined(__ANDROID__)
#incude "spdlog/sinks/android_sink.h"
void android_example()
{
std::string tag = "spdlog-android";
auto android_logger = spd::android_logger("android", tag);
android_logger->critical("Use \"adb shell logcat\" to view this message.");
}
#endif
// user defined types logging by implementing operator<<
#include "spdlog/fmt/ostr.h" // must be included
struct my_type
{
int i;
@ -151,10 +160,9 @@ struct my_type
}
};
#include "spdlog/fmt/ostr.h" // must be included
void user_defined_example()
{
spd::get("console")->info("user defined type: {}", my_type{14});
spdlog::get("console")->info("user defined type: {}", my_type{14});
}
//
@ -162,8 +170,30 @@ void user_defined_example()
//
void err_handler_example()
{
// can be set globaly or per logger(logger->set_error_handler(..))
spdlog::set_error_handler([](const std::string &msg) { spd::get("console")->error("*******my err handler: {}", msg); });
spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
// spd::get("console")->info("some invalid message to trigger an error {}{}{}{}", 3);
// 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 example (linux/osx/freebsd)
#ifndef _WIN32
#include "spdlog/sinks/syslog_sink.h"
void syslog_example()
{
std::string ident = "spdlog-example";
auto syslog_logger = spdlog::syslog_logger("syslog", ident, LOG_PID);
syslog_logger->warn("This is warning that will end up in syslog.");
}
#endif
// Android example
#if defined(__ANDROID__)
#incude "spdlog/sinks/android_sink.h"
void android_example()
{
std::string tag = "spdlog-android";
auto android_logger = spdlog::android_logger("android", tag);
android_logger->critical("Use \"adb shell logcat\" to view this message.");
}
#endif

View File

@ -15,8 +15,8 @@ int main(int, char *[])
// Each sink can have it's own log level and a message will be logged.
std::vector<spdlog::sink_ptr> sinks;
sinks.push_back(std::make_shared<spdlog::sinks::stdout_sink_mt>());
sinks.push_back(std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_regular_file.txt"));
sinks.push_back(std::make_shared<spdlog::sinks::simple_file_sink_mt>("./log_debug_file.txt"));
sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>("./log_regular_file.txt"));
sinks.push_back(std::make_shared<spdlog::sinks::basic_file_sink_mt>("./log_debug_file.txt"));
spdlog::logger console_multisink("multisink", sinks.begin(), sinks.end());
console_multisink.set_level(spdlog::level::warn);

View File

@ -23,7 +23,7 @@ namespace spdlog {
// async logger factory - creates async loggers backed with thread pool.
// if a global thread pool doesn't already exist, create it with default queue size of 8192 items and single thread.
struct create_async
struct async_factory
{
template<typename Sink, typename... SinkArgs>
static std::shared_ptr<async_logger> create(const std::string &logger_name, SinkArgs &&... args)
@ -39,16 +39,16 @@ struct create_async
}
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), async_overflow_policy::block_retry);
auto new_logger = std::make_shared<async_logger>(logger_name, std::move(sink), std::move(tp), async_overflow_policy::block);
registry::instance().register_and_init(new_logger);
return new_logger;
}
};
template<typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create_async_logger(const std::string &logger_name, SinkArgs &&... sink_args)
inline std::shared_ptr<spdlog::logger> create_async(const std::string &logger_name, SinkArgs &&... sink_args)
{
return create_async::create<Sink>(logger_name, std::forward<SinkArgs>(sink_args)...);
return async_factory::create<Sink>(logger_name, std::forward<SinkArgs>(sink_args)...);
}
// set global thread pool.

View File

@ -34,13 +34,13 @@ class async_logger SPDLOG_FINAL : public std::enable_shared_from_this<async_logg
public:
template<class It>
async_logger(const std::string &logger_name, const It &begin, const It &end, std::weak_ptr<details::thread_pool> tp,
async_overflow_policy overflow_policy = async_overflow_policy::block_retry);
async_overflow_policy overflow_policy = async_overflow_policy::block);
async_logger(const std::string &logger_name, sinks_init_list sinks, std::weak_ptr<details::thread_pool> tp,
async_overflow_policy overflow_policy = async_overflow_policy::block_retry);
async_overflow_policy overflow_policy = async_overflow_policy::block);
async_logger(const std::string &logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp,
async_overflow_policy overflow_policy = async_overflow_policy::block_retry);
async_overflow_policy overflow_policy = async_overflow_policy::block);
protected:
void sink_it_(details::log_msg &msg) override;

View File

@ -5,7 +5,7 @@
#pragma once
#define SPDLOG_VERSION "0.16.4-rc"
#define SPDLOG_VERSION "1.0.0-rc"
#include "spdlog/tweakme.h"
@ -63,6 +63,7 @@ using log_clock = std::chrono::system_clock;
using sink_ptr = std::shared_ptr<sinks::sink>;
using sinks_init_list = std::initializer_list<sink_ptr>;
using formatter_ptr = std::shared_ptr<spdlog::formatter>;
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
using level_t = details::null_atomic_int;
#else
@ -126,8 +127,8 @@ using level_hasher = std::hash<int>;
//
enum class async_overflow_policy
{
block_retry, // Block until message can be enqueued
overrun_oldeset // Discard oldest message in the queue if full when trying to add new item.
block, // Block until message can be enqueued
overrun_oldest // Discard oldest message in the queue if full when trying to add new item.
};
//

View File

@ -60,7 +60,7 @@ inline void spdlog::async_logger::flush_()
}
else
{
throw spdlog_ex("async flush: thread pool doens't exist anymore");
throw spdlog_ex("async flush: thread pool doesn't exist anymore");
}
}

View File

@ -7,7 +7,7 @@
#include "stdio.h"
namespace spdlog {
namespace details {
struct console_stdout_trait
struct console_stdout_stream
{
static FILE *stream()
{
@ -21,7 +21,7 @@ struct console_stdout_trait
#endif
};
struct console_stderr_trait
struct console_stderr_stream
{
static FILE *stream()
{
@ -35,7 +35,7 @@ struct console_stderr_trait
#endif
};
struct console_mutex_trait
struct console_global_mutex
{
using mutex_t = std::mutex;
static mutex_t &console_mutex()
@ -45,7 +45,7 @@ struct console_mutex_trait
}
};
struct console_null_mutex_trait
struct console_global_nullmutex
{
using mutex_t = null_mutex;
static mutex_t &console_mutex()

View File

@ -8,13 +8,16 @@
namespace spdlog {
namespace details {
namespace fmt_helper {
inline void append_str(const std::string &str, fmt::memory_buffer &dest)
template <size_t Buffer_Size>
inline void append_str(const std::string &str, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
auto *str_ptr = str.data();
dest.append(str_ptr, str_ptr + str.size());
}
inline void append_c_str(const char *c_str, fmt::memory_buffer &dest)
template <size_t Buffer_Size>
inline void append_c_str(const char *c_str, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
char ch;
while ((ch = *c_str) != '\0')
@ -24,21 +27,22 @@ inline void append_c_str(const char *c_str, fmt::memory_buffer &dest)
}
}
template<size_t N1, size_t N2>
inline void append_buf(const fmt::basic_memory_buffer<char, N1> &buf, fmt::basic_memory_buffer<char, N2> &dest)
template<size_t Buffer_Size1, size_t Buffer_Size2>
inline void append_buf(const fmt::basic_memory_buffer<char, Buffer_Size1> &buf, fmt::basic_memory_buffer<char, Buffer_Size2> &dest)
{
auto *buf_ptr = buf.data();
dest.append(buf_ptr, buf_ptr + buf.size());
}
template<typename T>
inline void append_int(T n, fmt::memory_buffer &dest)
template<typename T, size_t Buffer_Size>
inline void append_int(T n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
fmt::format_int i(n);
dest.append(i.data(), i.data() + i.size());
}
inline void pad2(int n, fmt::memory_buffer &dest)
template <size_t Buffer_Size>
inline void pad2(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
if (n > 99)
{
@ -61,7 +65,8 @@ inline void pad2(int n, fmt::memory_buffer &dest)
fmt::format_to(dest, "{:02}", n);
}
inline void pad3(int n, fmt::memory_buffer &dest)
template <size_t Buffer_Size>
inline void pad3(int n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
if (n > 99)
{
@ -86,8 +91,13 @@ inline void pad3(int n, fmt::memory_buffer &dest)
fmt::format_to(dest, "{:03}", n);
}
inline void pad6(size_t n, fmt::memory_buffer &dest)
template <size_t Buffer_Size>
inline void pad6(size_t n, fmt::basic_memory_buffer<char, Buffer_Size> &dest)
{
// todo: maybe replace this implementation with
// pad3(n / 1000, dest);
// pad3(n % 1000, dest);
if (n > 99999)
{
append_int(n, dest);

View File

@ -46,9 +46,9 @@ inline void spdlog::logger::set_formatter(const Args &... args)
}
}
inline void spdlog::logger::set_pattern(const std::string &pattern, pattern_time_type pattern_time)
inline void spdlog::logger::set_pattern(const std::string &pattern, pattern_time_type time_type)
{
set_formatter<spdlog::pattern_formatter>(pattern, pattern_time);
set_formatter<spdlog::pattern_formatter>(pattern, time_type);
}
template<typename... Args>
@ -100,40 +100,40 @@ inline void spdlog::logger::log(level::level_enum lvl, const T &msg)
SPDLOG_CATCH_AND_HANDLE
}
template<typename Arg1, typename... Args>
inline void spdlog::logger::trace(const char *fmt, const Arg1 &arg1, const Args &... args)
template<typename... Args>
inline void spdlog::logger::trace(const char *fmt, const Args &... args)
{
log(level::trace, fmt, arg1, args...);
log(level::trace, fmt, args...);
}
template<typename Arg1, typename... Args>
inline void spdlog::logger::debug(const char *fmt, const Arg1 &arg1, const Args &... args)
template<typename... Args>
inline void spdlog::logger::debug(const char *fmt, const Args &... args)
{
log(level::debug, fmt, arg1, args...);
log(level::debug, fmt, args...);
}
template<typename Arg1, typename... Args>
inline void spdlog::logger::info(const char *fmt, const Arg1 &arg1, const Args &... args)
template<typename... Args>
inline void spdlog::logger::info(const char *fmt, const Args &... args)
{
log(level::info, fmt, arg1, args...);
log(level::info, fmt, args...);
}
template<typename Arg1, typename... Args>
inline void spdlog::logger::warn(const char *fmt, const Arg1 &arg1, const Args &... args)
template<typename... Args>
inline void spdlog::logger::warn(const char *fmt, const Args &... args)
{
log(level::warn, fmt, arg1, args...);
log(level::warn, fmt, args...);
}
template<typename Arg1, typename... Args>
inline void spdlog::logger::error(const char *fmt, const Arg1 &arg1, const Args &... args)
template<typename... Args>
inline void spdlog::logger::error(const char *fmt, const Args &... args)
{
log(level::err, fmt, arg1, args...);
log(level::err, fmt, args...);
}
template<typename Arg1, typename... Args>
inline void spdlog::logger::critical(const char *fmt, const Arg1 &arg1, const Args &... args)
template<typename... Args>
inline void spdlog::logger::critical(const char *fmt, const Args &... args)
{
log(level::critical, fmt, arg1, args...);
log(level::critical, fmt, args...);
}
template<typename T>

View File

@ -120,7 +120,16 @@ class c_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
fmt::format_to(dest, "{} {} {} ", days[tm_time.tm_wday], months[tm_time.tm_mon], tm_time.tm_mday); //
//fmt::format_to(dest, "{} {} {} ", days[tm_time.tm_wday], months[tm_time.tm_mon], tm_time.tm_mday);
// date
fmt_helper::append_str(days[tm_time.tm_wday], dest);
dest.push_back(' ');
fmt_helper::append_str(months[tm_time.tm_mon], dest);
dest.push_back(' ');
fmt_helper::append_int(tm_time.tm_mday, dest);
dest.push_back(' ');
// time
fmt_helper::pad2(tm_time.tm_hour, dest);
dest.push_back(':');
fmt_helper::pad2(tm_time.tm_min, dest);
@ -176,7 +185,6 @@ class d_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
// fmt::format_to(dest, "{:02}", tm_time.tm_mday);
fmt_helper::pad2(tm_time.tm_mday, dest);
}
};
@ -265,7 +273,7 @@ class p_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
fmt::format_to(dest, "{}", ampm(tm_time));
fmt_helper::append_c_str(ampm(tm_time), dest);
}
};
@ -274,13 +282,13 @@ class r_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &, const std::tm &tm_time, fmt::memory_buffer &dest) override
{
// fmt::format_to(dest, "{:02}:{:02}:{:02} {}", to12h(tm_time), tm_time.tm_min, tm_time.tm_sec, ampm(tm_time));
fmt_helper::pad2(to12h(tm_time), dest);
dest.push_back(':');
fmt_helper::pad2(tm_time.tm_min, dest);
dest.push_back(':');
fmt_helper::pad2(tm_time.tm_sec, dest);
fmt::format_to(dest, " {}", ampm(tm_time));
dest.push_back(' ');
fmt_helper::append_c_str(ampm(tm_time), dest);
}
};
@ -330,23 +338,19 @@ public:
int total_minutes = os::utc_minutes_offset(tm_time);
#endif
bool is_negative = total_minutes < 0;
char sign;
if (is_negative)
{
total_minutes = -total_minutes;
sign = '-';
dest.push_back('-');
}
else
{
sign = '+';
dest.push_back('+');
}
int h = total_minutes / 60;
int m = total_minutes % 60;
dest.push_back(sign);
fmt_helper::pad2(h, dest);
fmt_helper::pad2(total_minutes / 60, dest); // hours
dest.push_back(':');
fmt_helper::pad2(m, dest);
fmt_helper::pad2(total_minutes % 60, dest); // minutes
}
private:
@ -371,7 +375,6 @@ class t_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{
fmt_helper::pad6(msg.thread_id, dest);
}
};
@ -390,7 +393,6 @@ class i_formatter SPDLOG_FINAL : public flag_formatter
{
void format(const details::log_msg &msg, const std::tm &, fmt::memory_buffer &dest) override
{
fmt_helper::pad6(msg.msg_id, dest);
}
};
@ -462,39 +464,48 @@ class full_formatter SPDLOG_FINAL : public flag_formatter
{
#ifndef SPDLOG_NO_DATETIME
// each second cache the header
// cache the date/time part for the next second.
auto duration = msg.time.time_since_epoch();
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
if (cached_header_.size() == 0 || cached_seconds_ts_ != seconds)
std::chrono::seconds seconds = std::chrono::duration_cast<std::chrono::seconds>(duration);
if (cache_timestamp_ != seconds || cached_datetime_.size() == 0)
{
cached_header_ = fmt::memory_buffer();
cached_header_.push_back('[');
fmt_helper::append_int(tm_time.tm_year + 1900, cached_header_);
cached_header_.push_back('-');
cached_datetime_.resize(0);
cached_datetime_.push_back('[');
fmt_helper::append_int(tm_time.tm_year + 1900, cached_datetime_);
cached_datetime_.push_back('-');
fmt_helper::pad2(tm_time.tm_mon + 1, cached_header_);
cached_header_.push_back('-');
fmt_helper::pad2(tm_time.tm_mon + 1, cached_datetime_);
cached_datetime_.push_back('-');
fmt_helper::pad2(tm_time.tm_mday, cached_header_);
cached_header_.push_back(' ');
fmt_helper::pad2(tm_time.tm_mday, cached_datetime_);
cached_datetime_.push_back(' ');
fmt_helper::pad2(tm_time.tm_hour, cached_header_);
cached_header_.push_back(':');
fmt_helper::pad2(tm_time.tm_hour, cached_datetime_);
cached_datetime_.push_back(':');
fmt_helper::pad2(tm_time.tm_min, cached_header_);
cached_header_.push_back(':');
fmt_helper::pad2(tm_time.tm_min, cached_datetime_);
cached_datetime_.push_back(':');
fmt_helper::pad2(tm_time.tm_sec, cached_header_);
cached_header_.push_back('.');
fmt_helper::pad2(tm_time.tm_sec, cached_datetime_);
cached_datetime_.push_back('.');
cached_seconds_ts_ = seconds;
cache_timestamp_ = seconds;
}
fmt_helper::append_buf(cached_header_, dest);
fmt_helper::append_buf(cached_datetime_, dest);
// cache the millis part for the next milli.
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
fmt_helper::pad3(static_cast<int>(millis), dest);
dest.push_back(']');
dest.push_back(' ');
if(millis != millis_cache_timestamp_ || cached_millis_.size() == 0)
{
cached_millis_.resize(0);
fmt_helper::pad3(millis, cached_millis_);
cached_millis_.push_back(']');
cached_millis_.push_back(' ');
millis_cache_timestamp_ = millis;
}
fmt_helper::append_buf(cached_millis_, dest);
#else // no datetime needed
(void)tm_time;
#endif
@ -517,8 +528,10 @@ class full_formatter SPDLOG_FINAL : public flag_formatter
}
private:
std::chrono::seconds::rep cached_seconds_ts_{0};
fmt::memory_buffer cached_header_;
std::chrono::seconds cache_timestamp_ {0};
std::chrono::milliseconds::rep millis_cache_timestamp_ {0};
fmt::basic_memory_buffer<char, 128> cached_datetime_;
fmt::basic_memory_buffer<char, 8> cached_millis_;
};
} // namespace details
@ -526,14 +539,14 @@ private:
class pattern_formatter SPDLOG_FINAL : public formatter
{
public:
explicit pattern_formatter(const std::string &pattern, pattern_time_type pattern_time = pattern_time_type::local,
explicit pattern_formatter(const std::string &pattern, pattern_time_type time_type = pattern_time_type::local,
std::string eol = spdlog::details::os::default_eol)
: eol_(std::move(eol))
, pattern_time_(pattern_time)
, pattern_time_type_(time_type)
, last_log_secs_(0)
{
std::memset(&cached_tm_, 0, sizeof(cached_tm_));
compile_pattern(pattern);
compile_pattern_(pattern);
}
pattern_formatter(const pattern_formatter &) = default;
@ -544,7 +557,7 @@ public:
auto secs = std::chrono::duration_cast<std::chrono::seconds>(msg.time.time_since_epoch());
if (secs != last_log_secs_)
{
cached_tm_ = get_time(msg);
cached_tm_ = get_time_(msg);
last_log_secs_ = secs;
}
#endif
@ -558,21 +571,22 @@ public:
private:
const std::string eol_;
const pattern_time_type pattern_time_;
pattern_time_type pattern_time_type_;
std::tm cached_tm_;
std::chrono::seconds last_log_secs_;
std::vector<std::unique_ptr<details::flag_formatter>> formatters_;
std::tm get_time(const details::log_msg &msg)
std::tm get_time_(const details::log_msg &msg)
{
if (pattern_time_ == pattern_time_type::local)
if (pattern_time_type_ == pattern_time_type::local)
{
return details::os::localtime(log_clock::to_time_t(msg.time));
}
return details::os::gmtime(log_clock::to_time_t(msg.time));
}
void handle_flag(char flag)
void handle_flag_(char flag)
{
switch (flag)
{
@ -717,7 +731,7 @@ private:
}
}
void compile_pattern(const std::string &pattern)
void compile_pattern_(const std::string &pattern)
{
auto end = pattern.end();
std::unique_ptr<details::aggregate_formatter> user_chars;
@ -732,7 +746,7 @@ private:
// if(
if (++it != end)
{
handle_flag(*it);
handle_flag_(*it);
}
else
{

View File

@ -36,7 +36,7 @@ public:
{
std::lock_guard<Mutex> lock(mutex_);
auto logger_name = new_logger->name();
throw_if_exists(logger_name);
throw_if_exists_(logger_name);
loggers_[logger_name] = new_logger;
}
@ -44,11 +44,11 @@ public:
{
std::lock_guard<Mutex> lock(mutex_);
auto logger_name = new_logger->name();
throw_if_exists(logger_name);
throw_if_exists_(logger_name);
// create default formatter if not exists
new_logger->set_pattern(formatter_pattern_);
new_logger->set_formatter<pattern_formatter>(formatter_pattern_, pattern_time_type_);
if (err_handler_)
{
@ -81,13 +81,14 @@ public:
return tp_;
}
void set_pattern(const std::string &pattern)
void set_pattern(const std::string &pattern, pattern_time_type time_type)
{
std::lock_guard<Mutex> lock(mutex_);
formatter_pattern_ = pattern;
pattern_time_type_ = time_type;
for (auto &l : loggers_)
{
l.second->set_pattern(pattern);
l.second->set_pattern(pattern, time_type);
}
}
@ -162,7 +163,7 @@ public:
private:
registry_t<Mutex>() = default;
void throw_if_exists(const std::string &logger_name)
void throw_if_exists_(const std::string &logger_name)
{
if (loggers_.find(logger_name) != loggers_.end())
{
@ -174,6 +175,7 @@ private:
Mutex tp_mutex_;
std::unordered_map<std::string, std::shared_ptr<logger>> loggers_;
std::string formatter_pattern_ = "%+";
pattern_time_type pattern_time_type_ = pattern_time_type::local;
level::level_enum level_ = level::info;
level::level_enum flush_level_ = level::off;
log_err_handler err_handler_;

View File

@ -29,7 +29,7 @@ struct async_msg
level::level_enum level;
log_clock::time_point time;
size_t thread_id;
fmt::basic_memory_buffer<char, 128> raw;
fmt::basic_memory_buffer<char, 176> raw;
size_t msg_id;
async_logger_ptr worker_ptr;
@ -95,7 +95,7 @@ public:
}
for (size_t i = 0; i < threads_n; i++)
{
threads_.emplace_back(std::bind(&thread_pool::worker_loop, this));
threads_.emplace_back(std::bind(&thread_pool::worker_loop_, this));
}
}
@ -106,14 +106,13 @@ public:
{
for (size_t i = 0; i < threads_.size(); i++)
{
post_async_msg(async_msg(async_msg_type::terminate), async_overflow_policy::block_retry);
post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block);
}
for (auto &t : threads_)
{
t.join();
}
// std::cout << "~thread_pool() msg_counter_: " << msg_counter_ << std::endl;
}
catch (...)
{
@ -123,12 +122,12 @@ public:
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));
post_async_msg(std::move(async_m), overflow_policy);
post_async_msg_(std::move(async_m), overflow_policy);
}
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy)
{
post_async_msg(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy);
post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy);
}
private:
@ -136,9 +135,9 @@ private:
std::vector<std::thread> threads_;
void post_async_msg(async_msg &&new_msg, async_overflow_policy overflow_policy)
void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy)
{
if (overflow_policy == async_overflow_policy::block_retry)
if (overflow_policy == async_overflow_policy::block)
{
q_.enqueue(std::move(new_msg));
}
@ -148,14 +147,14 @@ private:
}
}
void worker_loop()
void worker_loop_()
{
while (process_next_msg()) {};
while (process_next_msg_()) {};
}
// process next message in the queue
// return true if this thread should still be active (while no terminate msg was received)
bool process_next_msg()
bool process_next_msg_()
{
async_msg incoming_async_msg;
bool dequeued = q_.dequeue_for(incoming_async_msg, std::chrono::seconds(10));

View File

@ -196,12 +196,22 @@
FMT_BEGIN_NAMESPACE
// An implementation of declval for pre-C++11 compilers such as gcc 4.
namespace internal {
// An implementation of declval for pre-C++11 compilers such as gcc 4.
template<typename T>
typename std::add_rvalue_reference<T>::type declval() FMT_NOEXCEPT;
// Casts nonnegative integer to unsigned.
template<typename Int>
FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value)
{
FMT_ASSERT(value >= 0, "negative value");
return static_cast<typename std::make_unsigned<Int>::type>(value);
}
} // namespace internal
/**
An implementation of ``std::basic_string_view`` for pre-C++17. It provides a
subset of the API. ``fmt::basic_string_view`` is used for format strings even
@ -242,7 +252,7 @@ public:
FMT_CONSTEXPR basic_string_view() FMT_NOEXCEPT : data_(FMT_NULL), size_(0) {}
/** Constructs a string reference object from a C string and a size. */
FMT_CONSTEXPR basic_string_view(const Char *s, size_t str_size) FMT_NOEXCEPT : data_(s), size_(str_size) {}
FMT_CONSTEXPR basic_string_view(const Char *s, size_t count) FMT_NOEXCEPT : data_(s), size_(count) {}
/**
\rst
@ -354,11 +364,7 @@ private:
std::size_t capacity_;
protected:
basic_buffer(T *p = FMT_NULL, std::size_t buf_size = 0, std::size_t buf_capacity = 0) FMT_NOEXCEPT : ptr_(p),
size_(buf_size),
capacity_(buf_capacity)
{
}
basic_buffer(T *p = FMT_NULL, std::size_t sz = 0, std::size_t cap = 0) FMT_NOEXCEPT : ptr_(p), size_(sz), capacity_(cap) {}
/** Sets the buffer data and capacity. */
void set(T *buf_data, std::size_t buf_capacity) FMT_NOEXCEPT
@ -418,11 +424,11 @@ public:
size_ = new_size;
}
/** Reserves space to store at least *buf_capacity* elements. */
void reserve(std::size_t buf_capacity)
/** Reserves space to store at least *capacity* elements. */
void reserve(std::size_t new_capacity)
{
if (buf_capacity > capacity_)
grow(buf_capacity);
if (new_capacity > capacity_)
grow(new_capacity);
}
void push_back(const T &value)
@ -893,7 +899,7 @@ public:
// Advances the begin iterator to ``it``.
FMT_CONSTEXPR void advance_to(iterator it)
{
format_str_.remove_prefix(it - begin());
format_str_.remove_prefix(internal::to_unsigned(it - begin()));
}
// Returns the next argument index.
@ -1135,13 +1141,13 @@ struct get_type
};
template<typename Context>
FMT_CONSTEXPR uint64_t get_types()
FMT_CONSTEXPR unsigned long long get_types()
{
return 0;
}
template<typename Context, typename Arg, typename... Args>
FMT_CONSTEXPR uint64_t get_types()
FMT_CONSTEXPR unsigned long long get_types()
{
return get_type<Context, Arg>::value | (get_types<Context, Args...>() << 4);
}
@ -1187,27 +1193,29 @@ private:
typedef typename std::conditional<IS_PACKED, internal::value<Context>, basic_format_arg<Context>>::type value_type;
// If the arguments are not packed, add one more element to mark the end.
value_type data_[NUM_ARGS + (IS_PACKED && NUM_ARGS != 0 ? 0 : 1)];
static const size_t DATA_SIZE = NUM_ARGS + (IS_PACKED && NUM_ARGS != 0 ? 0 : 1);
value_type data_[DATA_SIZE];
friend class basic_format_args<Context>;
static FMT_CONSTEXPR int64_t get_types()
static FMT_CONSTEXPR long long get_types()
{
return IS_PACKED ? static_cast<int64_t>(internal::get_types<Context, Args...>()) : -static_cast<int64_t>(NUM_ARGS);
return IS_PACKED ? static_cast<long long>(internal::get_types<Context, Args...>()) : -static_cast<long long>(NUM_ARGS);
}
public:
#if FMT_USE_CONSTEXPR
static constexpr int64_t TYPES = get_types();
static constexpr long long TYPES = get_types();
#else
static const int64_t TYPES;
static const long long TYPES;
#endif
#if FMT_GCC_VERSION && FMT_GCC_VERSION <= 405
// Workaround an array initialization bug in gcc 4.5 and earlier.
#if (FMT_GCC_VERSION && FMT_GCC_VERSION <= 405) || (FMT_MSC_VER && FMT_MSC_VER <= 1800)
// Workaround array initialization issues in gcc <= 4.5 and MSVC <= 2013.
format_arg_store(const Args &... args)
{
data_ = {internal::make_arg<IS_PACKED, Context>(args)...};
value_type init[DATA_SIZE] = {internal::make_arg<IS_PACKED, Context>(args)...};
std::memcpy(data_, init, sizeof(init));
}
#else
format_arg_store(const Args &... args)
@ -1219,7 +1227,7 @@ public:
#if !FMT_USE_CONSTEXPR
template<typename Context, typename... Args>
const int64_t format_arg_store<Context, Args...>::TYPES = get_types();
const long long format_arg_store<Context, Args...>::TYPES = get_types();
#endif
/**
@ -1252,7 +1260,7 @@ public:
private:
// To reduce compiled code size per formatting function call, types of first
// max_packed_args arguments are passed in the types_ field.
uint64_t types_;
unsigned long long types_;
union
{
// If the number of arguments is less than max_packed_args, the argument
@ -1267,7 +1275,7 @@ private:
typename internal::type type(unsigned index) const
{
unsigned shift = index * 4;
uint64_t mask = 0xf;
unsigned long long mask = 0xf;
return static_cast<typename internal::type>((types_ & (mask << shift)) >> shift);
}
@ -1284,10 +1292,10 @@ private:
format_arg do_get(size_type index) const
{
int64_t signed_types = static_cast<int64_t>(types_);
long long signed_types = static_cast<long long>(types_);
if (signed_types < 0)
{
uint64_t num_args = -signed_types;
unsigned long long num_args = static_cast<unsigned long long>(-signed_types);
return index < num_args ? args_[index] : format_arg();
}
format_arg arg;
@ -1314,7 +1322,7 @@ public:
*/
template<typename... Args>
basic_format_args(const format_arg_store<Context, Args...> &store)
: types_(store.TYPES)
: types_(static_cast<unsigned long long>(store.TYPES))
{
set_data(store.data_);
}
@ -1328,8 +1336,8 @@ public:
unsigned max_size() const
{
int64_t signed_types = static_cast<int64_t>(types_);
return static_cast<unsigned>(signed_types < 0 ? -signed_types : static_cast<int64_t>(internal::max_packed_args));
long long signed_types = static_cast<long long>(types_);
return static_cast<unsigned>(signed_types < 0 ? -signed_types : static_cast<long long>(internal::max_packed_args));
}
};
@ -1414,6 +1422,8 @@ inline internal::named_arg<T, wchar_t> arg(wstring_view name, const T &arg)
template<typename S, typename T, typename Char>
void arg(S, internal::named_arg<T, Char>) FMT_DELETED;
#ifndef FMT_EXTENDED_COLORS
// color and (v)print_colored are deprecated.
enum color
{
black,
@ -1425,27 +1435,19 @@ enum color
cyan,
white
};
FMT_API void vprint_colored(color c, string_view format, format_args args);
FMT_API void vprint_colored(color c, wstring_view format, wformat_args args);
/**
Formats a string and prints it to stdout using ANSI escape sequences to
specify color (experimental).
Example:
fmt::print_colored(fmt::RED, "Elapsed time: {0:.2f} seconds", 1.23);
*/
template<typename... Args>
inline void print_colored(color c, string_view format_str, const Args &... args)
{
vprint_colored(c, format_str, make_format_args(args...));
}
template<typename... Args>
inline void print_colored(color c, wstring_view format_str, const Args &... args)
{
vprint_colored(c, format_str, make_format_args<wformat_context>(args...));
}
#endif
format_context::iterator vformat_to(internal::buffer &buf, string_view format_str, format_args args);
wformat_context::iterator vformat_to(internal::wbuffer &buf, wstring_view format_str, wformat_args args);

View File

@ -89,9 +89,6 @@ inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...)
#define FMT_SWPRINTF swprintf
#endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
const char RESET_COLOR[] = "\x1b[0m";
const wchar_t WRESET_COLOR[] = L"\x1b[0m";
typedef void (*FormatFunc)(internal::buffer &, int, string_view);
// Portable thread-safe version of strerror.
@ -300,6 +297,11 @@ const int16_t basic_data<T>::POW10_EXPONENTS[] = {-1220, -1193, -1166, -1140, -1
-289, -263, -236, -210, -183, -157, -130, -103, -77, -50, -24, 3, 30, 56, 83, 109, 136, 162, 189, 216, 242, 269, 295, 322, 348, 375,
402, 428, 455, 481, 508, 534, 561, 588, 614, 641, 667, 694, 720, 747, 774, 800, 827, 853, 880, 907, 933, 960, 986, 1013, 1039, 1066};
template<typename T>
const char basic_data<T>::RESET_COLOR[] = "\x1b[0m";
template<typename T>
const wchar_t basic_data<T>::WRESET_COLOR[] = L"\x1b[0m";
FMT_FUNC fp operator*(fp x, fp y)
{
// Multiply 32-bit parts of significands.
@ -502,13 +504,14 @@ FMT_FUNC void vprint(wstring_view format_str, wformat_args args)
vprint(stdout, format_str, args);
}
#ifndef FMT_EXTENDED_COLORS
FMT_FUNC void vprint_colored(color c, string_view format, format_args args)
{
char escape[] = "\x1b[30m";
escape[3] = static_cast<char>('0' + c);
std::fputs(escape, stdout);
vprint(format, args);
std::fputs(RESET_COLOR, stdout);
std::fputs(internal::data::RESET_COLOR, stdout);
}
FMT_FUNC void vprint_colored(color c, wstring_view format, wformat_args args)
@ -517,8 +520,48 @@ FMT_FUNC void vprint_colored(color c, wstring_view format, wformat_args args)
escape[3] = static_cast<wchar_t>('0' + c);
std::fputws(escape, stdout);
vprint(format, args);
std::fputws(WRESET_COLOR, stdout);
std::fputws(internal::data::WRESET_COLOR, stdout);
}
#else
namespace internal {
FMT_CONSTEXPR void to_esc(uint8_t c, char out[], int offset)
{
out[offset + 0] = static_cast<char>('0' + c / 100);
out[offset + 1] = static_cast<char>('0' + c / 10 % 10);
out[offset + 2] = static_cast<char>('0' + c % 10);
}
} // namespace internal
FMT_FUNC void vprint_rgb(rgb fd, string_view format, format_args args)
{
char escape_fd[] = "\x1b[38;2;000;000;000m";
internal::to_esc(fd.r, escape_fd, 7);
internal::to_esc(fd.g, escape_fd, 11);
internal::to_esc(fd.b, escape_fd, 15);
std::fputs(escape_fd, stdout);
vprint(format, args);
std::fputs(internal::data::RESET_COLOR, stdout);
}
FMT_FUNC void vprint_rgb(rgb fd, rgb bg, string_view format, format_args args)
{
char escape_fd[] = "\x1b[38;2;000;000;000m"; // foreground color
char escape_bg[] = "\x1b[48;2;000;000;000m"; // background color
internal::to_esc(fd.r, escape_fd, 7);
internal::to_esc(fd.g, escape_fd, 11);
internal::to_esc(fd.b, escape_fd, 15);
internal::to_esc(bg.r, escape_bg, 7);
internal::to_esc(bg.g, escape_bg, 11);
internal::to_esc(bg.b, escape_bg, 15);
std::fputs(escape_fd, stdout);
std::fputs(escape_bg, stdout);
vprint(format, args);
std::fputs(internal::data::RESET_COLOR, stdout);
}
#endif
FMT_FUNC locale locale_provider::locale()
{

View File

@ -1,46 +0,0 @@
// Formatting library for C++
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include "fmt/format-inl.h"
namespace fmt {
template struct internal::basic_data<void>;
// Explicit instantiations for char.
template FMT_API char internal::thousands_sep(locale_provider *lp);
template void basic_fixed_buffer<char>::grow(std::size_t);
template void internal::arg_map<format_context>::init(
const basic_format_args<format_context> &args);
template FMT_API int internal::char_traits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, double value);
template FMT_API int internal::char_traits<char>::format_float(
char *buffer, std::size_t size, const char *format,
unsigned width, int precision, long double value);
// Explicit instantiations for wchar_t.
template FMT_API wchar_t internal::thousands_sep(locale_provider *lp);
template void basic_fixed_buffer<wchar_t>::grow(std::size_t);
template void internal::arg_map<wformat_context>::init(const wformat_args &args);
template FMT_API int internal::char_traits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, double value);
template FMT_API int internal::char_traits<wchar_t>::format_float(
wchar_t *buffer, std::size_t size, const wchar_t *format,
unsigned width, int precision, long double value);
} // namespace fmt

View File

@ -168,7 +168,7 @@ FMT_END_NAMESPACE
#endif
// A workaround for gcc 4.4 that doesn't support union members with ctors.
#if FMT_GCC_VERSION && FMT_GCC_VERSION <= 404
#if (FMT_GCC_VERSION && FMT_GCC_VERSION <= 404) || (FMT_MSC_VER && FMT_MSC_VER <= 1800)
#define FMT_UNION struct
#else
#define FMT_UNION union
@ -531,14 +531,6 @@ public:
namespace internal {
// Casts nonnegative integer to unsigned.
template<typename Int>
FMT_CONSTEXPR typename std::make_unsigned<Int>::type to_unsigned(Int value)
{
FMT_ASSERT(value >= 0, "negative value");
return static_cast<typename std::make_unsigned<Int>::type>(value);
}
#if FMT_SECURE_SCL
template<typename T>
struct checked
@ -1072,6 +1064,8 @@ struct FMT_API basic_data
static const uint64_t POW10_SIGNIFICANDS[];
static const int16_t POW10_EXPONENTS[];
static const char DIGITS[];
static const char RESET_COLOR[];
static const wchar_t WRESET_COLOR[];
};
#if FMT_USE_EXTERN_TEMPLATES
@ -1172,7 +1166,7 @@ public:
uint64_t t = ((1ULL << (32 + a)) / data::POWERS_OF_10_32[n] + 1 - n / 9);
t = ((t * u) >> a) + n / 5 * 4;
write_pair(0, t >> 32);
for (int i = 2; i < N; i += 2)
for (unsigned i = 2; i < N; i += 2)
{
t = 100ULL * static_cast<uint32_t>(t);
write_pair(i, t >> 32);
@ -1561,31 +1555,6 @@ struct align_spec : empty_spec
template<typename Char>
class basic_format_specs : public align_spec
{
private:
template<typename FillChar>
typename std::enable_if<std::is_same<FillChar, Char>::value || std::is_same<FillChar, char>::value, void>::type set(
fill_spec<FillChar> fill)
{
fill_ = fill.value();
}
void set(width_spec width)
{
width_ = width.value();
}
void set(type_spec type)
{
type_ = type.value();
}
template<typename Spec, typename... Specs>
void set(Spec spec, Specs... tail)
{
set(spec);
set(tail...);
}
public:
unsigned flags_;
int precision_;
@ -1599,16 +1568,6 @@ public:
{
}
template<typename... FormatSpecs>
explicit basic_format_specs(FormatSpecs... specs)
: align_spec(0, ' ')
, flags_(0)
, precision_(-1)
, type_(0)
{
set(specs...);
}
FMT_CONSTEXPR bool flag(unsigned f) const
{
return (flags_ & f) != 0;
@ -1928,8 +1887,23 @@ public:
template<typename T>
typename std::enable_if<std::is_integral<T>::value, iterator>::type operator()(T value)
{
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
if (std::is_same<T, bool>::value)
{
if (specs_.type_)
return (*this)(value ? 1 : 0);
write(value != 0);
}
else if (std::is_same<T, char_type>::value)
{
internal::handle_char_specs(specs_, char_spec_handler(*this, static_cast<char_type>(value)));
}
else
{
writer_.write_int(value, specs_);
}
return out();
}
@ -1940,14 +1914,6 @@ public:
return out();
}
iterator operator()(bool value)
{
if (specs_.type_)
return (*this)(value ? 1 : 0);
write(value);
return out();
}
struct char_spec_handler : internal::error_handler
{
arg_formatter_base &formatter;
@ -1969,12 +1935,6 @@ public:
}
};
iterator operator()(char_type value)
{
internal::handle_char_specs(specs_, char_spec_handler(*this, value));
return out();
}
struct cstring_spec_handler : internal::error_handler
{
arg_formatter_base &formatter;
@ -2047,7 +2007,7 @@ FMT_CONSTEXPR unsigned parse_nonnegative_int(Iterator &it, ErrorHandler &&eh)
value = max_int + 1;
break;
}
value = value * 10 + (*it - '0');
value = value * 10 + unsigned(*it - '0');
// Workaround for MSVC "setup_exception stack overflow" error:
auto next = it;
++next;
@ -2107,7 +2067,7 @@ public:
{
if (is_negative(value))
handler_.on_error("negative width");
return value;
return static_cast<unsigned long long>(value);
}
template<typename T>
@ -2135,7 +2095,7 @@ public:
{
if (is_negative(value))
handler_.on_error("negative precision");
return value;
return static_cast<unsigned long long>(value);
}
template<typename T>
@ -2201,7 +2161,7 @@ public:
}
FMT_CONSTEXPR void on_precision(unsigned precision)
{
specs_.precision_ = precision;
specs_.precision_ = static_cast<int>(precision);
}
FMT_CONSTEXPR void end_precision() {}
@ -2300,7 +2260,7 @@ FMT_CONSTEXPR void set_dynamic_spec(T &value, basic_format_arg<Context> arg, Err
unsigned long long big_value = visit(Handler<ErrorHandler>(eh), arg);
if (big_value > (std::numeric_limits<int>::max)())
eh.on_error("number is too big");
value = static_cast<int>(big_value);
value = static_cast<T>(big_value);
}
struct auto_id
@ -2494,7 +2454,7 @@ FMT_CONSTEXPR Iterator parse_arg_id(Iterator it, IDHandler &&handler)
{
c = *++it;
} while (is_name_start(c) || ('0' <= c && c <= '9'));
handler(basic_string_view<char_type>(pointer_from(start), it - start));
handler(basic_string_view<char_type>(pointer_from(start), to_unsigned(it - start)));
return it;
}
@ -3063,8 +3023,8 @@ private:
}
else if (spec.precision() > static_cast<int>(num_digits))
{
size = prefix.size() + spec.precision();
padding = spec.precision() - num_digits;
size = prefix.size() + static_cast<std::size_t>(spec.precision());
padding = static_cast<std::size_t>(spec.precision()) - num_digits;
fill = '0';
}
align_spec as = spec;
@ -3941,8 +3901,7 @@ struct formatter<T, Char, typename std::enable_if<internal::format_type<typename
internal::handle_dynamic_spec<internal::width_checker>(specs_.width_, specs_.width_ref, ctx);
internal::handle_dynamic_spec<internal::precision_checker>(specs_.precision_, specs_.precision_ref, ctx);
typedef output_range<typename FormatContext::iterator, typename FormatContext::char_type> range_type;
visit(arg_formatter<range_type>(ctx, specs_), internal::make_arg<FormatContext>(val));
return ctx.out();
return visit(arg_formatter<range_type>(ctx, specs_), internal::make_arg<FormatContext>(val));
}
private:
@ -4059,7 +4018,7 @@ struct format_handler : internal::error_handler
void on_text(iterator begin, iterator end)
{
size_t size = end - begin;
auto size = internal::to_unsigned(end - begin);
auto out = context.out();
auto &&it = internal::reserve(out, size);
it = std::copy_n(begin, size, it);
@ -4226,10 +4185,10 @@ std::wstring to_wstring(const T &value)
return str;
}
template<typename Char>
std::basic_string<Char> to_string(const basic_memory_buffer<Char> &buffer)
template<typename Char, std::size_t SIZE>
std::basic_string<Char> to_string(const basic_memory_buffer<Char, SIZE> &buf)
{
return std::basic_string<Char>(buffer.data(), buffer.size());
return std::basic_string<Char>(buf.data(), buf.size());
}
inline format_context::iterator vformat_to(internal::buffer &buf, string_view format_str, format_args args)
@ -4244,14 +4203,14 @@ inline wformat_context::iterator vformat_to(internal::wbuffer &buf, wstring_view
return vformat_to<arg_formatter<range>>(buf, format_str, args);
}
template<typename... Args>
inline format_context::iterator format_to(memory_buffer &buf, string_view format_str, const Args &... args)
template<typename... Args, std::size_t SIZE = inline_buffer_size>
inline format_context::iterator format_to(basic_memory_buffer<char, SIZE> &buf, string_view format_str, const Args &... args)
{
return vformat_to(buf, format_str, make_format_args(args...));
}
template<typename... Args>
inline wformat_context::iterator format_to(wmemory_buffer &buf, wstring_view format_str, const Args &... args)
template<typename... Args, std::size_t SIZE = inline_buffer_size>
inline wformat_context::iterator format_to(basic_memory_buffer<wchar_t, SIZE> &buf, wstring_view format_str, const Args &... args)
{
return vformat_to(buf, format_str, make_format_args<wformat_context>(args...));
}
@ -4276,6 +4235,12 @@ inline OutputIt vformat_to(OutputIt out, string_view format_str, typename format
typedef output_range<OutputIt, char> range;
return vformat_to<arg_formatter<range>>(range(out), format_str, args);
}
template<typename OutputIt, typename... Args>
inline OutputIt vformat_to(OutputIt out, wstring_view format_str, typename format_args_t<OutputIt, wchar_t>::type args)
{
typedef output_range<OutputIt, wchar_t> range;
return vformat_to<arg_formatter<range>>(range(out), format_str, args);
}
/**
\rst
@ -4317,6 +4282,26 @@ struct format_to_n_result
std::size_t size;
};
template<typename OutputIt>
using format_to_n_context = typename fmt::format_context_t<fmt::internal::truncating_iterator<OutputIt>>::type;
template<typename OutputIt>
using format_to_n_args = fmt::basic_format_args<format_to_n_context<OutputIt>>;
template<typename OutputIt, typename... Args>
inline format_arg_store<format_to_n_context<OutputIt>, Args...> make_format_to_n_args(const Args &... args)
{
return format_arg_store<format_to_n_context<OutputIt>, Args...>(args...);
}
template<typename OutputIt, typename... Args>
inline format_to_n_result<OutputIt> vformat_to_n(OutputIt out, std::size_t n, string_view format_str, format_to_n_args<OutputIt> args)
{
typedef internal::truncating_iterator<OutputIt> It;
auto it = vformat_to(It(out, n), format_str, args);
return {it.base(), it.count()};
}
/**
\rst
Formats arguments, writes up to ``n`` characters of the result to the output
@ -4326,9 +4311,14 @@ struct format_to_n_result
*/
template<typename OutputIt, typename... Args>
inline format_to_n_result<OutputIt> format_to_n(OutputIt out, std::size_t n, string_view format_str, const Args &... args)
{
return vformat_to_n<OutputIt>(out, n, format_str, make_format_to_n_args<OutputIt>(args...));
}
template<typename OutputIt, typename... Args>
inline format_to_n_result<OutputIt> format_to_n(OutputIt out, std::size_t n, wstring_view format_str, const Args &... args)
{
typedef internal::truncating_iterator<OutputIt> It;
auto it = vformat_to(It(out, n), format_str, make_format_args<typename format_context_t<It>::type>(args...));
auto it = vformat_to(It(out, n), format_str, make_format_args<typename format_context_t<It, wchar_t>::type>(args...));
return {it.base(), it.count()};
}
@ -4371,6 +4361,215 @@ inline std::size_t formatted_size(string_view format_str, const Args &... args)
return it.count();
}
// Experimental color support.
#ifdef FMT_EXTENDED_COLORS
enum class color : uint32_t
{
alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215)
aqua = 0x00FFFF, // rgb(0,255,255)
aquamarine = 0x7FFFD4, // rgb(127,255,212)
azure = 0xF0FFFF, // rgb(240,255,255)
beige = 0xF5F5DC, // rgb(245,245,220)
bisque = 0xFFE4C4, // rgb(255,228,196)
black = 0x000000, // rgb(0,0,0)
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
blue = 0x0000FF, // rgb(0,0,255)
blue_violet = 0x8A2BE2, // rgb(138,43,226)
brown = 0xA52A2A, // rgb(165,42,42)
burly_wood = 0xDEB887, // rgb(222,184,135)
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
chartreuse = 0x7FFF00, // rgb(127,255,0)
chocolate = 0xD2691E, // rgb(210,105,30)
coral = 0xFF7F50, // rgb(255,127,80)
cornflower_blue = 0x6495ED, // rgb(100,149,237)
cornsilk = 0xFFF8DC, // rgb(255,248,220)
crimson = 0xDC143C, // rgb(220,20,60)
cyan = 0x00FFFF, // rgb(0,255,255)
dark_blue = 0x00008B, // rgb(0,0,139)
dark_cyan = 0x008B8B, // rgb(0,139,139)
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
dark_gray = 0xA9A9A9, // rgb(169,169,169)
dark_green = 0x006400, // rgb(0,100,0)
dark_khaki = 0xBDB76B, // rgb(189,183,107)
dark_magenta = 0x8B008B, // rgb(139,0,139)
dark_olive_green = 0x556B2F, // rgb(85,107,47)
dark_orange = 0xFF8C00, // rgb(255,140,0)
dark_orchid = 0x9932CC, // rgb(153,50,204)
dark_red = 0x8B0000, // rgb(139,0,0)
dark_salmon = 0xE9967A, // rgb(233,150,122)
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
dark_turquoise = 0x00CED1, // rgb(0,206,209)
dark_violet = 0x9400D3, // rgb(148,0,211)
deep_pink = 0xFF1493, // rgb(255,20,147)
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
dim_gray = 0x696969, // rgb(105,105,105)
dodger_blue = 0x1E90FF, // rgb(30,144,255)
fire_brick = 0xB22222, // rgb(178,34,34)
floral_white = 0xFFFAF0, // rgb(255,250,240)
forest_green = 0x228B22, // rgb(34,139,34)
fuchsia = 0xFF00FF, // rgb(255,0,255)
gainsboro = 0xDCDCDC, // rgb(220,220,220)
ghost_white = 0xF8F8FF, // rgb(248,248,255)
gold = 0xFFD700, // rgb(255,215,0)
golden_rod = 0xDAA520, // rgb(218,165,32)
gray = 0x808080, // rgb(128,128,128)
green = 0x008000, // rgb(0,128,0)
green_yellow = 0xADFF2F, // rgb(173,255,47)
honey_dew = 0xF0FFF0, // rgb(240,255,240)
hot_pink = 0xFF69B4, // rgb(255,105,180)
indian_red = 0xCD5C5C, // rgb(205,92,92)
indigo = 0x4B0082, // rgb(75,0,130)
ivory = 0xFFFFF0, // rgb(255,255,240)
khaki = 0xF0E68C, // rgb(240,230,140)
lavender = 0xE6E6FA, // rgb(230,230,250)
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
lawn_green = 0x7CFC00, // rgb(124,252,0)
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
light_blue = 0xADD8E6, // rgb(173,216,230)
light_coral = 0xF08080, // rgb(240,128,128)
light_cyan = 0xE0FFFF, // rgb(224,255,255)
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
light_gray = 0xD3D3D3, // rgb(211,211,211)
light_green = 0x90EE90, // rgb(144,238,144)
light_pink = 0xFFB6C1, // rgb(255,182,193)
light_salmon = 0xFFA07A, // rgb(255,160,122)
light_sea_green = 0x20B2AA, // rgb(32,178,170)
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
light_slate_gray = 0x778899, // rgb(119,136,153)
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
light_yellow = 0xFFFFE0, // rgb(255,255,224)
lime = 0x00FF00, // rgb(0,255,0)
lime_green = 0x32CD32, // rgb(50,205,50)
linen = 0xFAF0E6, // rgb(250,240,230)
magenta = 0xFF00FF, // rgb(255,0,255)
maroon = 0x800000, // rgb(128,0,0)
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
medium_blue = 0x0000CD, // rgb(0,0,205)
medium_orchid = 0xBA55D3, // rgb(186,85,211)
medium_purple = 0x9370DB, // rgb(147,112,219)
medium_sea_green = 0x3CB371, // rgb(60,179,113)
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
medium_violet_red = 0xC71585, // rgb(199,21,133)
midnight_blue = 0x191970, // rgb(25,25,112)
mint_cream = 0xF5FFFA, // rgb(245,255,250)
misty_rose = 0xFFE4E1, // rgb(255,228,225)
moccasin = 0xFFE4B5, // rgb(255,228,181)
navajo_white = 0xFFDEAD, // rgb(255,222,173)
navy = 0x000080, // rgb(0,0,128)
old_lace = 0xFDF5E6, // rgb(253,245,230)
olive = 0x808000, // rgb(128,128,0)
olive_drab = 0x6B8E23, // rgb(107,142,35)
orange = 0xFFA500, // rgb(255,165,0)
orange_red = 0xFF4500, // rgb(255,69,0)
orchid = 0xDA70D6, // rgb(218,112,214)
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
pale_green = 0x98FB98, // rgb(152,251,152)
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
pale_violet_red = 0xDB7093, // rgb(219,112,147)
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
peach_puff = 0xFFDAB9, // rgb(255,218,185)
peru = 0xCD853F, // rgb(205,133,63)
pink = 0xFFC0CB, // rgb(255,192,203)
plum = 0xDDA0DD, // rgb(221,160,221)
powder_blue = 0xB0E0E6, // rgb(176,224,230)
purple = 0x800080, // rgb(128,0,128)
rebecca_purple = 0x663399, // rgb(102,51,153)
red = 0xFF0000, // rgb(255,0,0)
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
royal_blue = 0x4169E1, // rgb(65,105,225)
saddle_brown = 0x8B4513, // rgb(139,69,19)
salmon = 0xFA8072, // rgb(250,128,114)
sandy_brown = 0xF4A460, // rgb(244,164,96)
sea_green = 0x2E8B57, // rgb(46,139,87)
sea_shell = 0xFFF5EE, // rgb(255,245,238)
sienna = 0xA0522D, // rgb(160,82,45)
silver = 0xC0C0C0, // rgb(192,192,192)
sky_blue = 0x87CEEB, // rgb(135,206,235)
slate_blue = 0x6A5ACD, // rgb(106,90,205)
slate_gray = 0x708090, // rgb(112,128,144)
snow = 0xFFFAFA, // rgb(255,250,250)
spring_green = 0x00FF7F, // rgb(0,255,127)
steel_blue = 0x4682B4, // rgb(70,130,180)
tan = 0xD2B48C, // rgb(210,180,140)
teal = 0x008080, // rgb(0,128,128)
thistle = 0xD8BFD8, // rgb(216,191,216)
tomato = 0xFF6347, // rgb(255,99,71)
turquoise = 0x40E0D0, // rgb(64,224,208)
violet = 0xEE82EE, // rgb(238,130,238)
wheat = 0xF5DEB3, // rgb(245,222,179)
white = 0xFFFFFF, // rgb(255,255,255)
white_smoke = 0xF5F5F5, // rgb(245,245,245)
yellow = 0xFFFF00, // rgb(255,255,0)
yellow_green = 0x9ACD32, // rgb(154,205,50)
}; // enum class color
// rgb is a struct for red, green and blue colors.
// We use rgb as name because some editors will show it as color direct in the
// editor.
struct rgb
{
FMT_CONSTEXPR_DECL rgb()
: r(0)
, g(0)
, b(0)
{
}
FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_)
: r(r_)
, g(g_)
, b(b_)
{
}
FMT_CONSTEXPR_DECL rgb(uint32_t hex)
: r((hex >> 16) & 0xFF)
, g((hex >> 8) & 0xFF)
, b((hex)&0xFF)
{
}
FMT_CONSTEXPR_DECL rgb(color hex)
: r((uint32_t(hex) >> 16) & 0xFF)
, g((uint32_t(hex) >> 8) & 0xFF)
, b(uint32_t(hex) & 0xFF)
{
}
uint8_t r;
uint8_t g;
uint8_t b;
};
void vprint_rgb(rgb fd, string_view format, format_args args);
void vprint_rgb(rgb fd, rgb bg, string_view format, format_args args);
/**
Formats a string and prints it to stdout using ANSI escape sequences to
specify foreground color 'fd'.
Example:
fmt::print(fmt::color::red, "Elapsed time: {0:.2f} seconds", 1.23);
*/
template<typename... Args>
inline void print(rgb fd, string_view format_str, const Args &... args)
{
vprint_rgb(fd, format_str, make_format_args(args...));
}
/**
Formats a string and prints it to stdout using ANSI escape sequences to
specify foreground color 'fd' and background color 'bg'.
Example:
fmt::print(fmt::color::red, fmt::color::black, "Elapsed time: {0:.2f} seconds", 1.23);
*/
template<typename... Args>
inline void print(rgb fd, rgb bg, string_view format_str, const Args &... args)
{
vprint_rgb(fd, bg, format_str, make_format_args(args...));
}
#endif // FMT_EXTENDED_COLORS
#if FMT_USE_USER_DEFINED_LITERALS
namespace internal {

View File

@ -290,21 +290,20 @@ public:
{
}
using base::operator();
/** Formats an argument of type ``bool``. */
iterator operator()(bool value)
template<typename T>
typename std::enable_if<std::is_integral<T>::value, iterator>::type operator()(T value)
{
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
if (std::is_same<T, bool>::value)
{
format_specs &fmt_spec = this->spec();
if (fmt_spec.type_ != 's')
return (*this)(value ? 1 : 0);
return base::operator()(value ? 1 : 0);
fmt_spec.type_ = 0;
this->write(value);
return this->out();
this->write(value != 0);
}
/** Formats a character. */
iterator operator()(char_type value)
else if (std::is_same<T, char_type>::value)
{
format_specs &fmt_spec = this->spec();
if (fmt_spec.type_ && fmt_spec.type_ != 'c')
@ -313,6 +312,18 @@ public:
fmt_spec.align_ = ALIGN_RIGHT;
return base::operator()(value);
}
else
{
return base::operator()(value);
}
return this->out();
}
template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, iterator>::type operator()(T value)
{
return base::operator()(value);
}
/** Formats a null-terminated C string. */
iterator operator()(const char *value)
@ -338,6 +349,16 @@ public:
return this->out();
}
iterator operator()(basic_string_view<char_type> value)
{
return base::operator()(value);
}
iterator operator()(monostate value)
{
return base::operator()(value);
}
/** Formats a pointer. */
iterator operator()(const void *value)
{

View File

@ -114,12 +114,14 @@ struct is_range_ : std::false_type
{
};
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
template<typename T>
struct is_range_<T, typename std::conditional<false,
conditional_helper<decltype(internal::declval<T>().begin()), decltype(internal::declval<T>().end())>, void>::type>
: std::true_type
{
};
#endif
/// tuple_size and tuple_element check.
template<typename T>
@ -171,7 +173,7 @@ using make_index_sequence = make_integer_sequence<std::size_t, N>;
#endif
template<class Tuple, class F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) noexcept
void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT
{
using std::get;
// using free function get<I>(T) now.

View File

@ -17,5 +17,3 @@ public:
virtual void format(const details::log_msg &msg, fmt::memory_buffer &dest) = 0;
};
} // namespace spdlog
#include "details/pattern_formatter_impl.h"

View File

@ -45,23 +45,23 @@ public:
template<typename... Args>
void log(level::level_enum lvl, const char *msg);
template<typename Arg1, typename... Args>
void trace(const char *fmt, const Arg1 &, const Args &... args);
template<typename... Args>
void trace(const char *fmt, const Args &... args);
template<typename Arg1, typename... Args>
void debug(const char *fmt, const Arg1 &, const Args &... args);
template<typename... Args>
void debug(const char *fmt, const Args &... args);
template<typename Arg1, typename... Args>
void info(const char *fmt, const Arg1 &, const Args &... args);
template<typename... Args>
void info(const char *fmt, const Args &... args);
template<typename Arg1, typename... Args>
void warn(const char *fmt, const Arg1 &, const Args &... args);
template<typename... Args>
void warn(const char *fmt, const Args &... args);
template<typename Arg1, typename... Args>
void error(const char *fmt, const Arg1 &, const Args &... args);
template<typename... Args>
void error(const char *fmt, const Args &... args);
template<typename Arg1, typename... Args>
void critical(const char *fmt, const Arg1 &, const Args &... args);
template<typename... Args>
void critical(const char *fmt, const Args &... args);
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
template<typename... Args>
@ -117,10 +117,10 @@ public:
// create a pattern formatter all the sinks in this logger.
// each sink gets itw own private copy of a formatter object.
void set_pattern(const std::string &pattern, pattern_time_type pattern_time = pattern_time_type::local);
void set_pattern(const std::string &pattern, pattern_time_type time_type = pattern_time_type::local);
// create a FormatterT formatter all the sinks in this logger.
// each sink gets itw own private copy of a formatter object.
// create a FormatterT formatter for each sink in this logger.
// each sink gets its own private copy of a formatter object.
template<class FormatterT, typename... Args>
void set_formatter(const Args &... args);

View File

@ -38,7 +38,7 @@ public:
void log(const details::log_msg &msg) override
{
const android_LogPriority priority = convert_to_android(msg.level);
const android_LogPriority priority = convert_to_android_(msg.level);
fmt::memory_buffer formatted;
if (use_raw_msg_)
{
@ -70,7 +70,7 @@ public:
void flush() override {}
private:
static android_LogPriority convert_to_android(spdlog::level::level_enum level)
static android_LogPriority convert_to_android_(spdlog::level::level_enum level)
{
switch (level)
{

View File

@ -5,9 +5,9 @@
#pragma once
#include "spdlog/details/console_globals.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/details/os.h"
#include "spdlog/details/traits.h"
#include <memory>
#include <mutex>
@ -22,14 +22,14 @@ namespace sinks {
* of the message.
* If no color terminal detected, omit the escape codes.
*/
template<class StreamTrait, class ConsoleMutexTrait>
template<class TargetStream, class ConsoleMutex>
class ansicolor_sink : public sink
{
public:
using mutex_t = typename ConsoleMutexTrait::mutex_t;
using mutex_t = typename ConsoleMutex::mutex_t;
ansicolor_sink()
: target_file_(StreamTrait::stream())
, mutex_(ConsoleMutexTrait::console_mutex())
: target_file_(TargetStream::stream())
, mutex_(ConsoleMutex::console_mutex())
{
should_do_colors_ = details::os::in_terminal(target_file_) && details::os::is_color_terminal();
@ -132,10 +132,11 @@ private:
std::unordered_map<level::level_enum, std::string, level::level_hasher> colors_;
};
using ansicolor_stdout_sink_mt = ansicolor_sink<details::console_stdout_trait, details::console_mutex_trait>;
using ansicolor_stdout_sink_st = ansicolor_sink<details::console_stdout_trait, details::console_null_mutex_trait>;
using ansicolor_stderr_sink_mt = ansicolor_sink<details::console_stderr_trait, details::console_mutex_trait>;
using ansicolor_stderr_sink_st = ansicolor_sink<details::console_stderr_trait, details::console_null_mutex_trait>;
using ansicolor_stdout_sink_mt = ansicolor_sink<details::console_stdout_stream, details::console_global_mutex>;
using ansicolor_stdout_sink_st = ansicolor_sink<details::console_stdout_stream, details::console_global_nullmutex>;
using ansicolor_stderr_sink_mt = ansicolor_sink<details::console_stderr_stream, details::console_global_mutex>;
using ansicolor_stderr_sink_st = ansicolor_sink<details::console_stderr_stream, details::console_global_nullmutex>;
} // namespace sinks

View File

@ -6,8 +6,8 @@
#pragma once
//
// base sink templated over a mutex (either dummy or real)
// concrete implementation should only override the sink_it_ method.
// all locking is taken care of here so no locking needed by the implementers..
// concrete implementation should override the sink_it_() and flush_() methods.
// locking is taken care of in this class - no locking needed by the implementers..
//
#include "spdlog/common.h"
@ -26,16 +26,6 @@ public:
{
}
base_sink(const std::string &formatter_pattern)
: sink(formatter_pattern)
{
}
base_sink(std::unique_ptr<spdlog::formatter> sink_formatter)
: sink(std::move(sink_formatter))
{
}
base_sink(const base_sink &) = delete;
base_sink &operator=(const base_sink &) = delete;

View File

@ -18,28 +18,18 @@ namespace sinks {
* Trivial file sink with single file as target
*/
template<class Mutex>
class simple_file_sink SPDLOG_FINAL : public base_sink<Mutex>
class basic_file_sink SPDLOG_FINAL : public base_sink<Mutex>
{
public:
explicit simple_file_sink(const filename_t &filename, bool truncate = false)
: force_flush_(false)
explicit basic_file_sink(const filename_t &filename, bool truncate = false)
{
file_helper_.open(filename, truncate);
}
void set_force_flush(bool force_flush)
{
force_flush_ = force_flush;
}
protected:
void sink_it_(const details::log_msg &, const fmt::memory_buffer &formatted) override
{
file_helper_.write(formatted);
if (force_flush_)
{
file_helper_.flush();
}
}
void flush_() override
@ -49,11 +39,10 @@ protected:
private:
details::file_helper file_helper_;
bool force_flush_;
};
using simple_file_sink_mt = simple_file_sink<std::mutex>;
using simple_file_sink_st = simple_file_sink<details::null_mutex>;
using basic_file_sink_mt = basic_file_sink<std::mutex>;
using basic_file_sink_st = basic_file_sink<details::null_mutex>;
} // namespace sinks
@ -63,13 +52,13 @@ using simple_file_sink_st = simple_file_sink<details::null_mutex>;
template<typename Factory = default_factory>
inline std::shared_ptr<logger> basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false)
{
return Factory::template create<sinks::simple_file_sink_mt>(logger_name, filename, truncate);
return Factory::template create<sinks::basic_file_sink_mt>(logger_name, filename, truncate);
}
template<typename Factory = default_factory>
inline std::shared_ptr<logger> basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false)
{
return Factory::template create<sinks::simple_file_sink_st>(logger_name, filename, truncate);
return Factory::template create<sinks::basic_file_sink_st>(logger_name, filename, truncate);
}
} // namespace spdlog

View File

@ -7,7 +7,6 @@
#include "spdlog/details/log_msg.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/sink/base_sink.h"
#include <algorithm>
#include <memory>
@ -18,22 +17,18 @@
namespace spdlog {
namespace sinks {
template<class Mutex>
class dist_sink : public base_sink<Mutex>
template<typename Mutex>
class dist_sink : public sink
{
public:
explicit dist_sink()
: sinks_()
{
}
dist_sink() = default;
dist_sink(const dist_sink &) = delete;
dist_sink &operator=(const dist_sink &) = delete;
protected:
std::vector<std::shared_ptr<sink>> sinks_;
void sink_it_(const details::log_msg &msg) override
void log(const details::log_msg &msg) SPDLOG_FINAL override
{
std::lock_guard<Mutex> lock(mutex_);
for (auto &sink : sinks_)
{
if (sink->should_log(msg.level))
@ -43,24 +38,28 @@ protected:
}
}
void flush_() override
void flush() SPDLOG_FINAL override
{
std::lock_guard<Mutex> lock(mutex_);
for (auto &sink : sinks_)
sink->flush();
}
public:
void add_sink(std::shared_ptr<sink> sink)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
std::lock_guard<Mutex> lock(mutex_);
sinks_.push_back(sink);
}
void remove_sink(std::shared_ptr<sink> sink)
{
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
std::lock_guard<Mutex> lock(mutex_);
sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end());
}
private:
Mutex mutex_;
std::vector<std::shared_ptr<sink>> sinks_;
};
using dist_sink_mt = dist_sink<std::mutex>;

View File

@ -27,9 +27,9 @@ public:
explicit msvc_sink() {}
protected:
void sink_it_(const details::log_msg &msg) override
void sink_it_(const details::log_msg &, const fmt::memory_buffer &formatted) override
{
OutputDebugStringA(msg.formatted.c_str());
OutputDebugStringA(fmt::to_string(formatted).c_str());
}
void flush_() override {}
@ -38,6 +38,9 @@ protected:
using msvc_sink_mt = msvc_sink<std::mutex>;
using msvc_sink_st = msvc_sink<details::null_mutex>;
using windebug_sink_mt = msvc_sink_mt;
using windebug_sink_st = msvc_sink_st;
} // namespace sinks
} // namespace spdlog

View File

@ -6,6 +6,7 @@
#pragma once
#include "spdlog/details/log_msg.h"
#include "spdlog/details/pattern_formatter.h"
#include "spdlog/formatter.h"
namespace spdlog {
@ -19,17 +20,6 @@ public:
{
}
explicit sink(const std::string &formatter_pattern)
: formatter_(std::unique_ptr<spdlog::formatter>(new pattern_formatter(formatter_pattern)))
{
}
// sink with custom formatter
explicit sink(std::unique_ptr<spdlog::formatter> sink_formatter)
: formatter_(std::move(sink_formatter))
{
}
virtual ~sink() = default;
virtual void log(const details::log_msg &msg) = 0;
@ -39,10 +29,12 @@ public:
{
return msg_level >= level_.load(std::memory_order_relaxed);
}
void set_level(level::level_enum log_level)
{
level_.store(log_level);
}
level::level_enum level() const
{
return static_cast<spdlog::level::level_enum>(level_.load(std::memory_order_relaxed));
@ -58,11 +50,6 @@ public:
formatter_ = std::move(sink_formatter);
}
spdlog::formatter *formatter()
{
return formatter_.get();
}
protected:
level_t level_{level::trace};
std::unique_ptr<spdlog::formatter> formatter_;

View File

@ -5,26 +5,27 @@
#pragma once
#include "spdlog/details/console_globals.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/details/traits.h"
#include "spdlog/spdlog.h"
#include <cstdio>
#include <memory>
#include <mutex>
#include <spdlog/details/console_globals.h>
namespace spdlog {
namespace sinks {
template<class StdoutTrait, class ConsoleMutexTrait>
template<class TargetStream, class ConsoleMutex>
class stdout_sink : public sink
{
public:
using mutex_t = typename ConsoleMutexTrait::mutex_t;
using mutex_t = typename ConsoleMutex::mutex_t;
stdout_sink()
: mutex_(ConsoleMutexTrait::console_mutex())
, file_(StdoutTrait::stream())
: mutex_(ConsoleMutex::console_mutex())
, file_(TargetStream::stream())
{
}
~stdout_sink() = default;
@ -35,14 +36,16 @@ public:
void log(const details::log_msg &msg) override
{
std::lock_guard<mutex_t> lock(mutex_);
fwrite(msg.formatted.data(), sizeof(char), msg.formatted.size(), file_);
fflush(StdoutTrait::stream());
fmt::memory_buffer formatted;
formatter_->format(msg, formatted);
fwrite(formatted.data(), sizeof(char), formatted.size(), file_);
fflush(TargetStream::stream());
}
void flush() override
{
std::lock_guard<mutex_t> lock(mutex_);
fflush(StdoutTrait::stream());
fflush(TargetStream::stream());
}
private:
@ -50,10 +53,11 @@ private:
FILE *file_;
};
using stdout_sink_mt = stdout_sink<details::console_stdout_trait, details::console_mutex_trait>;
using stdout_sink_st = stdout_sink<details::console_stdout_trait, details::console_null_mutex_trait>;
using stderr_sink_mt = stdout_sink<details::console_stderr_trait, details::console_mutex_trait>;
using stderr_sink_st = stdout_sink<details::console_stderr_trait, details::console_null_mutex_trait>;
using stdout_sink_mt = stdout_sink<details::console_stdout_stream, details::console_global_mutex>;
using stdout_sink_st = stdout_sink<details::console_stdout_stream, details::console_global_nullmutex>;
using stderr_sink_mt = stdout_sink<details::console_stderr_stream, details::console_global_mutex>;
using stderr_sink_st = stdout_sink<details::console_stderr_stream, details::console_global_nullmutex>;
} // namespace sinks

View File

@ -7,8 +7,8 @@
#include "../fmt/fmt.h"
#include "spdlog/common.h"
#include "spdlog/details/console_globals.h"
#include "spdlog/details/null_mutex.h"
#include "spdlog/details/traits.h"
#include "spdlog/sinks/sink.h"
#include <memory>
@ -22,7 +22,7 @@ namespace sinks {
/*
* Windows color console sink. Uses WriteConsoleA to write to the console with colors
*/
template<class HandleTrait, class ConsoleMutexTrait>
template<class OutHandle, class ConsoleMutex>
class wincolor_sink : public sink
{
public:
@ -34,8 +34,8 @@ public:
const WORD YELLOW = FOREGROUND_RED | FOREGROUND_GREEN;
wincolor_sink()
: out_handle_(HandleTrait::handle())
, mutex_(ConsoleMutexTrait::console_mutex())
: out_handle_(OutHandle::handle())
, mutex_(ConsoleMutex::console_mutex())
{
colors_[level::trace] = WHITE;
colors_[level::debug] = CYAN;
@ -90,7 +90,7 @@ public:
}
private:
using mutex_t = typename ConsoleMutexTrait::mutex_t;
using mutex_t = typename ConsoleMutex::mutex_t;
// set color and return the orig console attributes (for resetting later)
WORD set_console_attribs(WORD attribs)
{
@ -116,11 +116,11 @@ private:
std::unordered_map<level::level_enum, WORD, level::level_hasher> colors_;
};
using wincolor_stdout_sink_mt = wincolor_sink<details::console_stdout_trait, details::console_mutex_trait>;
using wincolor_stdout_sink_st = wincolor_sink<details::console_stdout_trait, details::console_null_mutex_trait>;
using wincolor_stdout_sink_mt = wincolor_sink<details::console_stdout_stream, details::console_global_mutex>;
using wincolor_stdout_sink_st = wincolor_sink<details::console_stdout_stream, details::console_global_nullmutex>;
using wincolor_stderr_sink_mt = wincolor_sink<details::console_stderr_trait, details::console_mutex_trait>;
using wincolor_stderr_sink_st = wincolor_sink<details::console_stderr_trait, details::console_null_mutex_trait>;
using wincolor_stderr_sink_mt = wincolor_sink<details::console_stderr_stream, details::console_global_mutex>;
using wincolor_stderr_sink_st = wincolor_sink<details::console_stderr_stream, details::console_global_nullmutex>;
} // namespace sinks
} // namespace spdlog

View File

@ -1,27 +0,0 @@
//
// Copyright(c) 2017 Alexander Dalshov.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
#if defined(_WIN32)
#include "spdlog/sinks/msvc_sink.h"
namespace spdlog {
namespace sinks {
/*
* Windows debug sink (logging using OutputDebugStringA, synonym for msvc_sink)
*/
template<class Mutex>
using windebug_sink = msvc_sink<Mutex>;
using windebug_sink_mt = msvc_sink_mt;
using windebug_sink_st = msvc_sink_st;
} // namespace sinks
} // namespace spdlog
#endif

View File

@ -19,7 +19,7 @@
namespace spdlog {
// Default logger factory- creates synchronous loggers
struct create_synchronous
struct synchronous_factory
{
template<typename Sink, typename... SinkArgs>
@ -32,7 +32,7 @@ struct create_synchronous
}
};
using default_factory = create_synchronous;
using default_factory = synchronous_factory;
// Create and register a logger with a templated sink type
// The logger's level, formatter and flush level will be set according the global settings.
@ -57,9 +57,9 @@ inline std::shared_ptr<logger> get(const std::string &name)
// Set global formatting
// example: spdlog::set_pattern("%Y-%m-%d %H:%M:%S.%e %l : %v");
//
inline void set_pattern(const std::string &format_string)
inline void set_pattern(const std::string &format_string, pattern_time_type time_type = pattern_time_type::local)
{
details::registry::instance().set_pattern(format_string);
details::registry::instance().set_pattern(format_string, time_type);
}
//

View File

@ -24,7 +24,7 @@ TEST_CASE("default_error_handler", "[errors]]")
prepare_logdir();
std::string filename = "logs/simple_log.txt";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("test-error", filename, true);
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("test-error", filename, true);
logger->set_pattern("%v");
logger->info("Test message {} {}", 1);
logger->info("Test message {}", 2);
@ -41,7 +41,7 @@ TEST_CASE("custom_error_handler", "[errors]]")
{
prepare_logdir();
std::string filename = "logs/simple_log.txt";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename, true);
logger->flush_on(spdlog::level::info);
logger->set_error_handler([=](const std::string &) { throw custom_ex(); });
logger->info("Good message #1");
@ -75,7 +75,7 @@ TEST_CASE("async_error_handler", "[errors]]")
std::string filename = "logs/simple_async_log.txt";
{
spdlog::init_thread_pool(128, 1);
auto logger = spdlog::create_async_logger<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
auto logger = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("logger", filename, true);
logger->set_error_handler([=](const std::string &) {
std::ofstream ofs("logs/custom_err.txt");
if (!ofs)
@ -99,7 +99,7 @@ TEST_CASE("async_error_handler2", "[errors]]")
std::string err_msg("This is async handler error message");
{
spdlog::init_thread_pool(128, 1);
auto logger = spdlog::create_async_logger<failing_sink>("failed_logger");
auto logger = spdlog::create_async<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string &) {
std::ofstream ofs("logs/custom_err2.txt");
if (!ofs)

View File

@ -8,7 +8,7 @@ TEST_CASE("simple_file_logger", "[simple_logger]]")
prepare_logdir();
std::string filename = "logs/simple_log";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
logger->info("Test message {}", 1);
@ -24,7 +24,7 @@ TEST_CASE("flush_on", "[flush_on]]")
prepare_logdir();
std::string filename = "logs/simple_log";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
logger->set_level(spdlog::level::trace);
logger->flush_on(spdlog::level::info);

View File

@ -13,10 +13,10 @@
#define SPDLOG_DEBUG_ON
#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/ostream_sink.h"
#include "spdlog/sinks/rotating_file_sink.h"
#include "spdlog/sinks/simple_file_sink.h"
#include "spdlog/sinks/stdout_color_sinks.h"
#include "spdlog/spdlog.h"

View File

@ -1,6 +1,6 @@
#include "includes.h"
#include "spdlog/async.h"
#include "spdlog/sinks/simple_file_sink.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "test_sink.h"
TEST_CASE("basic async test ", "[async]")
@ -11,7 +11,7 @@ TEST_CASE("basic async test ", "[async]")
size_t messages = 256;
{
auto tp = std::make_shared<details::thread_pool>(queue_size, 1);
auto logger = std::make_shared<async_logger>("as", test_sink, tp, async_overflow_policy::block_retry);
auto logger = std::make_shared<async_logger>("as", test_sink, tp, async_overflow_policy::block);
for (size_t i = 0; i < messages; i++)
{
logger->info("Hello message #{}", i);
@ -30,7 +30,7 @@ TEST_CASE("discard policy ", "[async]")
size_t messages = 1024;
{
auto tp = std::make_shared<details::thread_pool>(queue_size, 1);
auto logger = std::make_shared<async_logger>("as", test_sink, tp, async_overflow_policy::overrun_oldeset);
auto logger = std::make_shared<async_logger>("as", test_sink, tp, async_overflow_policy::overrun_oldest);
for (size_t i = 0; i < messages; i++)
{
logger->info("Hello message #{}", i);
@ -48,7 +48,7 @@ TEST_CASE("flush", "[async]")
size_t messages = 256;
{
auto tp = std::make_shared<details::thread_pool>(queue_size, 1);
auto logger = std::make_shared<async_logger>("as", test_sink, tp, async_overflow_policy::block_retry);
auto logger = std::make_shared<async_logger>("as", test_sink, tp, async_overflow_policy::block);
for (size_t i = 0; i < messages; i++)
{
logger->info("Hello message #{}", i);
@ -69,7 +69,7 @@ TEST_CASE("tp->wait_empty() ", "[async]")
size_t messages = 100;
auto tp = std::make_shared<details::thread_pool>(messages, 2);
auto logger = std::make_shared<async_logger>("as", test_sink, tp, async_overflow_policy::block_retry);
auto logger = std::make_shared<async_logger>("as", test_sink, tp, async_overflow_policy::block);
for (size_t i = 0; i < messages; i++)
{
logger->info("Hello message #{}", i);
@ -90,7 +90,7 @@ TEST_CASE("multi threads", "[async]")
size_t n_threads = 10;
{
auto tp = std::make_shared<details::thread_pool>(queue_size, 1);
auto logger = std::make_shared<async_logger>("as", test_sink, tp, async_overflow_policy::block_retry);
auto logger = std::make_shared<async_logger>("as", test_sink, tp, async_overflow_policy::block);
std::vector<std::thread> threads;
for (size_t i = 0; i < n_threads; i++)
@ -121,7 +121,7 @@ TEST_CASE("to_file", "[async]")
size_t tp_threads = 1;
std::string filename = "logs/async_test.log";
{
auto file_sink = std::make_shared<spdlog::sinks::simple_file_sink_mt>(filename, true);
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
auto tp = std::make_shared<spdlog::details::thread_pool>(messages, tp_threads);
auto logger = std::make_shared<spdlog::async_logger>("as", std::move(file_sink), std::move(tp));
@ -143,7 +143,7 @@ TEST_CASE("to_file multi-workers", "[async]")
size_t tp_threads = 10;
std::string filename = "logs/async_test.log";
{
auto file_sink = std::make_shared<spdlog::sinks::simple_file_sink_mt>(filename, true);
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
auto tp = std::make_shared<spdlog::details::thread_pool>(messages, tp_threads);
auto logger = std::make_shared<spdlog::async_logger>("as", std::move(file_sink), std::move(tp));

View File

@ -9,12 +9,11 @@ TEST_CASE("debug and trace w/o format string", "[macros]]")
prepare_logdir();
std::string filename = "logs/simple_log";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
logger->set_level(spdlog::level::trace);
SPDLOG_TRACE(logger, "Test message 1");
// SPDLOG_DEBUG(logger, "Test message 2");
SPDLOG_DEBUG(logger, "Test message 2");
logger->flush();
@ -27,7 +26,7 @@ TEST_CASE("debug and trace with format strings", "[macros]]")
prepare_logdir();
std::string filename = "logs/simple_log";
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename);
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
logger->set_level(spdlog::level::trace);