diff --git a/CMakeLists.txt b/CMakeLists.txt index 873979dc..6d55b8ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/README.md b/README.md index f52f7a01..be79838c 100644 --- a/README.md +++ b/README.md @@ -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,169 +46,138 @@ 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 async logger |spdlog async mode| -|:-------|:-----:|-------------------------:| -|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 -#include - -void async_example(); -void user_defined_example(); -void err_handler_example(); - -namespace spd = spdlog; -int main(int, char *[]) +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); - try + auto err_logger = spdlog::stderr_color_mt("stderr"); + err_logger->error("Some error message"); + + // Formatting examples + console->warn("Easy padding in numbers like {:08d}", 12); + console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); + console->info("Support for floats {:03.2f}", 1.23456); + console->info("Positional args are {1} {0}..", "too", "supported"); + console->info("{:<30}", "left aligned"); + + spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)"); + + // Runtime log levels + spdlog::set_level(spdlog::level::info); // Set global log level to info + console->debug("This message should not be displayed!"); + console->set_level(spdlog::level::trace); // Set specific logger's log level + console->debug("This message should be displayed.."); + + // Customize msg format for all loggers + spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v"); + console->info("This an info message with custom format"); + + // Compile time log levels + // define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON + SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23); + SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23); +} + +#include "spdlog/sinks/basic_file_sink.h" +void basic_logfile_example() +{ + // Create basic file logger (not rotated) + try { - auto console = spdlog::stdout_color_st("console"); - console->info("Welcome to spdlog!"); - - console->info("Welcome to spdlog!"); - console->error("Some error message with arg: {}", 1); - err_handler_example(); - // Formatting examples - console->warn("Easy padding in numbers like {:08d}", 12); - console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); - console->info("Support for floats {:03.2f}", 1.23456); - console->info("Positional args are {1} {0}..", "too", "supported"); - console->info("{:<30}", "left aligned"); - - 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("%+"); - - // Runtime log levels - spd::set_level(spd::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->debug("This message should be displayed.."); - - // 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 l) { l->info("End of example."); }); - - // Release and close all loggers - spdlog::drop_all(); + 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("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("async_file_logger", "logs/async_log.txt"); + // alternatively: + // auto async_file = spdlog::create_async("async_file_logger", "logs/async_log.txt"); + + for (int i = 1; i < 101; ++i) { async_file->info("Async message #{}", i); } - - // 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(); + console_sink->set_level(spdlog::level::warn); + console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v"); + + auto file_sink = std::make_shared("logs/multisink.txt", true); + file_sink->set_level(spdlog::level::trace); + + spdlog::logger logger("multi_sink", {console_sink, file_sink}); + logger.set_level(spdlog::level::debug); + logger.warn("this should appear in both console and file"); + logger.info("this message should not appear in the console, only in the file"); } -#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 diff --git a/bench/Makefile b/bench/Makefile index 24a4d7b0..534c55c3 100644 --- a/bench/Makefile +++ b/bench/Makefile @@ -1,18 +1,19 @@ -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 diff --git a/bench/bench.cpp b/bench/bench.cpp index fe03fb20..c5e8a5a4 100644 --- a/bench/bench.cpp +++ b/bench/bench.cpp @@ -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 @@ -32,8 +32,9 @@ void bench_mt(int howmany, std::shared_ptr 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_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("as", "logs/basic_async.log", true); + auto as = spdlog::basic_logger_mt("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 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>(delta).count(); cout << "Elapsed: " << delta_d << "\t" << format(int(howmany / delta_d)) << "/sec" << endl; } void bench_mt(int howmany, std::shared_ptr log, int thread_count) { - + using std::chrono::high_resolution_clock; cout << log->name() << "...\t\t" << flush; - std::atomic msg_counter{0}; vector 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 log, int thread_count t.join(); }; - auto delta = system_clock::now() - start; + auto delta = high_resolution_clock::now() - start; auto delta_d = duration_cast>(delta).count(); cout << "Elapsed: " << delta_d << "\t" << format(int(howmany / delta_d)) << "/sec" << endl; } diff --git a/bench/latency.cpp b/bench/latency.cpp new file mode 100644 index 00000000..687565a8 --- /dev/null +++ b/bench/latency.cpp @@ -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 +#include +#include +#include +#include +#include + +using namespace std; +using namespace std::chrono; +using namespace spdlog; +using namespace spdlog::sinks; +using namespace utils; + +void bench(int howmany, std::shared_ptr log); +void bench_mt(int howmany, std::shared_ptr 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_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_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("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 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(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 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 threads; + std::atomic 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(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; + + + +} diff --git a/bench/spdlog-async.cpp b/bench/spdlog-async.cpp index 9f2f75b9..fc4a61fe 100644 --- a/bench/spdlog-async.cpp +++ b/bench/spdlog-async.cpp @@ -11,7 +11,7 @@ #include #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("file_logger", "logs/spdlog-bench-async.log", false); + auto logger = spdlog::create_async_logger("file_logger", "logs/spdlog-bench-async.log", false); logger->set_pattern("[%Y-%m-%d %T.%F]: %L %t %v"); std::cout << "To stop, press " << std::endl; diff --git a/bench/spdlog-null-async.cpp b/bench/spdlog-null-async.cpp deleted file mode 100644 index ee64eb92..00000000 --- a/bench/spdlog-null-async.cpp +++ /dev/null @@ -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 -#include // EXIT_FAILURE -#include -#include -#include -#include - -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 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] << " [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("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 log, int thread_count) -{ - cout << log->name() << "...\t\t" << flush; - std::atomic msg_counter{0}; - vector 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>(delta).count(); - auto per_sec = size_t(howmany / delta_d); - cout << format(per_sec) << "/sec" << endl; - return per_sec; -} diff --git a/example/Makefile b/example/Makefile index 38c43f19..0f085d18 100644 --- a/example/Makefile +++ b/example/Makefile @@ -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 diff --git a/example/example.cpp b/example/example.cpp index e491518a..c848141d 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -7,140 +7,149 @@ // // -#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 -#include +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(); - console->info("Welcome to spdlog!"); - console->error("Some error message with arg: {}", 1); - err_handler_example(); - // Formatting examples - console->warn("Easy padding in numbers like {:08d}", 12); - console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); - console->info("Support for floats {:03.2f}", 1.23456); - console->info("Positional args are {1} {0}..", "too", "supported"); - console->info("{:<30}", "left aligned"); + // various file loggers + basic_example(); + rotating_example(); + daily_example(); - 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("%+"); - - // Runtime log levels - spd::set_level(spd::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->debug("This message should be displayed.."); - - // 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 logging using a backing thread pool async_example(); - // Log user-defined types example + // a logger can have multiple targets with different formats + multi_sink_example(); + + // user defined types logging by implementing operator<< user_defined_example(); - // Change default log error handler + // custom error handler err_handler_example(); - // Apply a function on all registered loggers - spd::apply_all([&](std::shared_ptr l) { l->info("End of example."); }); + // apply some function on all registered loggers + spdlog::apply_all([&](std::shared_ptr 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) + 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/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); + + auto err_logger = spdlog::stderr_color_mt("stderr"); + err_logger->error("Some error message"); + + // Formatting examples + console->warn("Easy padding in numbers like {:08d}", 12); + console->critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42); + console->info("Support for floats {:03.2f}", 1.23456); + console->info("Positional args are {1} {0}..", "too", "supported"); + console->info("{:<30}", "left aligned"); + + spdlog::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name)"); + + // Runtime log levels + spdlog::set_level(spdlog::level::info); // Set global log level to info + console->debug("This message should not be displayed!"); + console->set_level(spdlog::level::trace); // Set specific logger's log level + console->debug("This message should be displayed.."); + + // Customize msg format for all loggers + spdlog::set_pattern("[%H:%M:%S %z] [%n] [%^---%L---%$] [thread %t] %v"); + console->info("This an info message with custom format"); + + // Compile time log levels + // define SPDLOG_DEBUG_ON or SPDLOG_TRACE_ON + SPDLOG_TRACE(console, "Enabled only #ifdef SPDLOG_TRACE_ON..{} ,{}", 1, 3.23); + SPDLOG_DEBUG(console, "Enabled only #ifdef SPDLOG_DEBUG_ON.. {} ,{}", 1, 3.23); +} + +#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("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("async_file_logger", "logs/async_log.txt"); + // alternatively: + // auto async_file = spdlog::create_async("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(); + console_sink->set_level(spdlog::level::warn); + console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v"); + + auto file_sink = std::make_shared("logs/multisink.txt", true); + file_sink->set_level(spdlog::level::trace); + + spdlog::logger logger("multi_sink", {console_sink, file_sink}); + logger.set_level(spdlog::level::debug); + logger.warn("this should appear in both console and file"); + logger.info("this message should not appear in the console, only in the file"); } -#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 diff --git a/example/multisink.cpp b/example/multisink.cpp index 98b86ce2..4b3f2d9c 100644 --- a/example/multisink.cpp +++ b/example/multisink.cpp @@ -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 sinks; sinks.push_back(std::make_shared()); - sinks.push_back(std::make_shared("./log_regular_file.txt")); - sinks.push_back(std::make_shared("./log_debug_file.txt")); + sinks.push_back(std::make_shared("./log_regular_file.txt")); + sinks.push_back(std::make_shared("./log_debug_file.txt")); spdlog::logger console_multisink("multisink", sinks.begin(), sinks.end()); console_multisink.set_level(spdlog::level::warn); diff --git a/include/spdlog/async.h b/include/spdlog/async.h index a0ede2ad..3aab7a89 100644 --- a/include/spdlog/async.h +++ b/include/spdlog/async.h @@ -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 static std::shared_ptr create(const std::string &logger_name, SinkArgs &&... args) @@ -39,16 +39,16 @@ struct create_async } auto sink = std::make_shared(std::forward(args)...); - auto new_logger = std::make_shared(logger_name, std::move(sink), std::move(tp), async_overflow_policy::block_retry); + auto new_logger = std::make_shared(logger_name, std::move(sink), std::move(tp), async_overflow_policy::block); registry::instance().register_and_init(new_logger); return new_logger; } }; template -inline std::shared_ptr create_async_logger(const std::string &logger_name, SinkArgs &&... sink_args) +inline std::shared_ptr create_async(const std::string &logger_name, SinkArgs &&... sink_args) { - return create_async::create(logger_name, std::forward(sink_args)...); + return async_factory::create(logger_name, std::forward(sink_args)...); } // set global thread pool. diff --git a/include/spdlog/async_logger.h b/include/spdlog/async_logger.h index e4badf14..4faaabc8 100644 --- a/include/spdlog/async_logger.h +++ b/include/spdlog/async_logger.h @@ -34,13 +34,13 @@ class async_logger SPDLOG_FINAL : public std::enable_shared_from_this async_logger(const std::string &logger_name, const It &begin, const It &end, std::weak_ptr 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 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 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; diff --git a/include/spdlog/common.h b/include/spdlog/common.h index 34c91714..c18d203e 100644 --- a/include/spdlog/common.h +++ b/include/spdlog/common.h @@ -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; using sinks_init_list = std::initializer_list; using formatter_ptr = std::shared_ptr; + #if defined(SPDLOG_NO_ATOMIC_LEVELS) using level_t = details::null_atomic_int; #else @@ -126,8 +127,8 @@ using level_hasher = std::hash; // 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. }; // diff --git a/include/spdlog/details/async_logger_impl.h b/include/spdlog/details/async_logger_impl.h index 9b3b86d3..a3e6fb98 100644 --- a/include/spdlog/details/async_logger_impl.h +++ b/include/spdlog/details/async_logger_impl.h @@ -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"); } } diff --git a/include/spdlog/details/traits.h b/include/spdlog/details/console_globals.h similarity index 87% rename from include/spdlog/details/traits.h rename to include/spdlog/details/console_globals.h index d494cf73..58f86a95 100644 --- a/include/spdlog/details/traits.h +++ b/include/spdlog/details/console_globals.h @@ -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() diff --git a/include/spdlog/details/fmt_helper.h b/include/spdlog/details/fmt_helper.h index f728b176..4f9a9fc4 100644 --- a/include/spdlog/details/fmt_helper.h +++ b/include/spdlog/details/fmt_helper.h @@ -8,13 +8,16 @@ namespace spdlog { namespace details { namespace fmt_helper { -inline void append_str(const std::string &str, fmt::memory_buffer &dest) + +template +inline void append_str(const std::string &str, fmt::basic_memory_buffer &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 +inline void append_c_str(const char *c_str, fmt::basic_memory_buffer &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 -inline void append_buf(const fmt::basic_memory_buffer &buf, fmt::basic_memory_buffer &dest) +template +inline void append_buf(const fmt::basic_memory_buffer &buf, fmt::basic_memory_buffer &dest) { auto *buf_ptr = buf.data(); dest.append(buf_ptr, buf_ptr + buf.size()); } -template -inline void append_int(T n, fmt::memory_buffer &dest) +template +inline void append_int(T n, fmt::basic_memory_buffer &dest) { fmt::format_int i(n); dest.append(i.data(), i.data() + i.size()); } -inline void pad2(int n, fmt::memory_buffer &dest) +template +inline void pad2(int n, fmt::basic_memory_buffer &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 +inline void pad3(int n, fmt::basic_memory_buffer &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 +inline void pad6(size_t n, fmt::basic_memory_buffer &dest) { + // todo: maybe replace this implementation with + // pad3(n / 1000, dest); + // pad3(n % 1000, dest); + if (n > 99999) { append_int(n, dest); @@ -122,7 +132,7 @@ inline void pad6(size_t n, fmt::memory_buffer &dest) dest.push_back('0'); dest.push_back('0'); dest.push_back('0'); - } + } append_int(n, dest); } diff --git a/include/spdlog/details/logger_impl.h b/include/spdlog/details/logger_impl.h index 5a61a709..9b9b467f 100644 --- a/include/spdlog/details/logger_impl.h +++ b/include/spdlog/details/logger_impl.h @@ -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(pattern, pattern_time); + set_formatter(pattern, time_type); } template @@ -100,40 +100,40 @@ inline void spdlog::logger::log(level::level_enum lvl, const T &msg) SPDLOG_CATCH_AND_HANDLE } -template -inline void spdlog::logger::trace(const char *fmt, const Arg1 &arg1, const Args &... args) +template +inline void spdlog::logger::trace(const char *fmt, const Args &... args) { - log(level::trace, fmt, arg1, args...); + log(level::trace, fmt, args...); } -template -inline void spdlog::logger::debug(const char *fmt, const Arg1 &arg1, const Args &... args) +template +inline void spdlog::logger::debug(const char *fmt, const Args &... args) { - log(level::debug, fmt, arg1, args...); + log(level::debug, fmt, args...); } -template -inline void spdlog::logger::info(const char *fmt, const Arg1 &arg1, const Args &... args) +template +inline void spdlog::logger::info(const char *fmt, const Args &... args) { - log(level::info, fmt, arg1, args...); + log(level::info, fmt, args...); } -template -inline void spdlog::logger::warn(const char *fmt, const Arg1 &arg1, const Args &... args) +template +inline void spdlog::logger::warn(const char *fmt, const Args &... args) { - log(level::warn, fmt, arg1, args...); + log(level::warn, fmt, args...); } -template -inline void spdlog::logger::error(const char *fmt, const Arg1 &arg1, const Args &... args) +template +inline void spdlog::logger::error(const char *fmt, const Args &... args) { - log(level::err, fmt, arg1, args...); + log(level::err, fmt, args...); } -template -inline void spdlog::logger::critical(const char *fmt, const Arg1 &arg1, const Args &... args) +template +inline void spdlog::logger::critical(const char *fmt, const Args &... args) { - log(level::critical, fmt, arg1, args...); + log(level::critical, fmt, args...); } template diff --git a/include/spdlog/details/pattern_formatter_impl.h b/include/spdlog/details/pattern_formatter.h similarity index 87% rename from include/spdlog/details/pattern_formatter_impl.h rename to include/spdlog/details/pattern_formatter.h index 65bfa483..394c941d 100644 --- a/include/spdlog/details/pattern_formatter_impl.h +++ b/include/spdlog/details/pattern_formatter.h @@ -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(duration).count(); - if (cached_header_.size() == 0 || cached_seconds_ts_ != seconds) + std::chrono::seconds seconds = std::chrono::duration_cast(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(duration).count() % 1000; - fmt_helper::pad3(static_cast(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 cached_datetime_; + fmt::basic_memory_buffer 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(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> 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 user_chars; @@ -732,7 +746,7 @@ private: // if( if (++it != end) { - handle_flag(*it); + handle_flag_(*it); } else { diff --git a/include/spdlog/details/registry.h b/include/spdlog/details/registry.h index 85111d23..6de2f57a 100644 --- a/include/spdlog/details/registry.h +++ b/include/spdlog/details/registry.h @@ -36,7 +36,7 @@ public: { std::lock_guard 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 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(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 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() = 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> 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_; diff --git a/include/spdlog/details/thread_pool.h b/include/spdlog/details/thread_pool.h index 9019823a..cec47c60 100644 --- a/include/spdlog/details/thread_pool.h +++ b/include/spdlog/details/thread_pool.h @@ -29,7 +29,7 @@ struct async_msg level::level_enum level; log_clock::time_point time; size_t thread_id; - fmt::basic_memory_buffer raw; + fmt::basic_memory_buffer 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(worker_ptr), async_msg_type::log, std::forward(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 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)); diff --git a/include/spdlog/fmt/bundled/core.h b/include/spdlog/fmt/bundled/core.h index 60135a0f..4627789a 100644 --- a/include/spdlog/fmt/bundled/core.h +++ b/include/spdlog/fmt/bundled/core.h @@ -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 std::add_rvalue_reference::type declval() FMT_NOEXCEPT; + +// Casts nonnegative integer to unsigned. +template +FMT_CONSTEXPR typename std::make_unsigned::type to_unsigned(Int value) +{ + FMT_ASSERT(value >= 0, "negative value"); + return static_cast::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 -FMT_CONSTEXPR uint64_t get_types() +FMT_CONSTEXPR unsigned long long get_types() { return 0; } template -FMT_CONSTEXPR uint64_t get_types() +FMT_CONSTEXPR unsigned long long get_types() { return get_type::value | (get_types() << 4); } @@ -1187,27 +1193,29 @@ private: typedef typename std::conditional, basic_format_arg>::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; - static FMT_CONSTEXPR int64_t get_types() + static FMT_CONSTEXPR long long get_types() { - return IS_PACKED ? static_cast(internal::get_types()) : -static_cast(NUM_ARGS); + return IS_PACKED ? static_cast(internal::get_types()) : -static_cast(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(args)...}; + value_type init[DATA_SIZE] = {internal::make_arg(args)...}; + std::memcpy(data_, init, sizeof(init)); } #else format_arg_store(const Args &... args) @@ -1219,7 +1227,7 @@ public: #if !FMT_USE_CONSTEXPR template -const int64_t format_arg_store::TYPES = get_types(); +const long long format_arg_store::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((types_ & (mask << shift)) >> shift); } @@ -1284,10 +1292,10 @@ private: format_arg do_get(size_type index) const { - int64_t signed_types = static_cast(types_); + long long signed_types = static_cast(types_); if (signed_types < 0) { - uint64_t num_args = -signed_types; + unsigned long long num_args = static_cast(-signed_types); return index < num_args ? args_[index] : format_arg(); } format_arg arg; @@ -1314,7 +1322,7 @@ public: */ template basic_format_args(const format_arg_store &store) - : types_(store.TYPES) + : types_(static_cast(store.TYPES)) { set_data(store.data_); } @@ -1328,8 +1336,8 @@ public: unsigned max_size() const { - int64_t signed_types = static_cast(types_); - return static_cast(signed_types < 0 ? -signed_types : static_cast(internal::max_packed_args)); + long long signed_types = static_cast(types_); + return static_cast(signed_types < 0 ? -signed_types : static_cast(internal::max_packed_args)); } }; @@ -1414,6 +1422,8 @@ inline internal::named_arg arg(wstring_view name, const T &arg) template void arg(S, internal::named_arg) 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 inline void print_colored(color c, string_view format_str, const Args &... args) { vprint_colored(c, format_str, make_format_args(args...)); } - template inline void print_colored(color c, wstring_view format_str, const Args &... args) { vprint_colored(c, format_str, make_format_args(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); diff --git a/include/spdlog/fmt/bundled/format-inl.h b/include/spdlog/fmt/bundled/format-inl.h index 267d2449..76e27225 100644 --- a/include/spdlog/fmt/bundled/format-inl.h +++ b/include/spdlog/fmt/bundled/format-inl.h @@ -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::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 +const char basic_data::RESET_COLOR[] = "\x1b[0m"; +template +const wchar_t basic_data::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('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('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('0' + c / 100); + out[offset + 1] = static_cast('0' + c / 10 % 10); + out[offset + 2] = static_cast('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() { diff --git a/include/spdlog/fmt/bundled/format.cc b/include/spdlog/fmt/bundled/format.cc deleted file mode 100644 index 41746612..00000000 --- a/include/spdlog/fmt/bundled/format.cc +++ /dev/null @@ -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; - -// Explicit instantiations for char. - -template FMT_API char internal::thousands_sep(locale_provider *lp); - -template void basic_fixed_buffer::grow(std::size_t); - -template void internal::arg_map::init( - const basic_format_args &args); - -template FMT_API int internal::char_traits::format_float( - char *buffer, std::size_t size, const char *format, - unsigned width, int precision, double value); - -template FMT_API int internal::char_traits::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::grow(std::size_t); - -template void internal::arg_map::init(const wformat_args &args); - -template FMT_API int internal::char_traits::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::format_float( - wchar_t *buffer, std::size_t size, const wchar_t *format, - unsigned width, int precision, long double value); -} // namespace fmt diff --git a/include/spdlog/fmt/bundled/format.h b/include/spdlog/fmt/bundled/format.h index 05a0430f..2e26f907 100644 --- a/include/spdlog/fmt/bundled/format.h +++ b/include/spdlog/fmt/bundled/format.h @@ -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 -FMT_CONSTEXPR typename std::make_unsigned::type to_unsigned(Int value) -{ - FMT_ASSERT(value >= 0, "negative value"); - return static_cast::type>(value); -} - #if FMT_SECURE_SCL template 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(t); write_pair(i, t >> 32); @@ -1561,31 +1555,6 @@ struct align_spec : empty_spec template class basic_format_specs : public align_spec { -private: - template - typename std::enable_if::value || std::is_same::value, void>::type set( - fill_spec fill) - { - fill_ = fill.value(); - } - - void set(width_spec width) - { - width_ = width.value(); - } - - void set(type_spec type) - { - type_ = type.value(); - } - - template - void set(Spec spec, Specs... tail) - { - set(spec); - set(tail...); - } - public: unsigned flags_; int precision_; @@ -1599,16 +1568,6 @@ public: { } - template - 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; @@ -1929,7 +1888,22 @@ public: template typename std::enable_if::value, iterator>::type operator()(T value) { - writer_.write_int(value, specs_); + // MSVC2013 fails to compile separate overloads for bool and char_type so + // use std::is_same instead. + if (std::is_same::value) + { + if (specs_.type_) + return (*this)(value ? 1 : 0); + write(value != 0); + } + else if (std::is_same::value) + { + internal::handle_char_specs(specs_, char_spec_handler(*this, static_cast(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(value); } template @@ -2135,7 +2095,7 @@ public: { if (is_negative(value)) handler_.on_error("negative precision"); - return value; + return static_cast(value); } template @@ -2201,7 +2161,7 @@ public: } FMT_CONSTEXPR void on_precision(unsigned precision) { - specs_.precision_ = precision; + specs_.precision_ = static_cast(precision); } FMT_CONSTEXPR void end_precision() {} @@ -2300,7 +2260,7 @@ FMT_CONSTEXPR void set_dynamic_spec(T &value, basic_format_arg arg, Err unsigned long long big_value = visit(Handler(eh), arg); if (big_value > (std::numeric_limits::max)()) eh.on_error("number is too big"); - value = static_cast(big_value); + value = static_cast(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(pointer_from(start), it - start)); + handler(basic_string_view(pointer_from(start), to_unsigned(it - start))); return it; } @@ -3063,8 +3023,8 @@ private: } else if (spec.precision() > static_cast(num_digits)) { - size = prefix.size() + spec.precision(); - padding = spec.precision() - num_digits; + size = prefix.size() + static_cast(spec.precision()); + padding = static_cast(spec.precision()) - num_digits; fill = '0'; } align_spec as = spec; @@ -3941,8 +3901,7 @@ struct formatter(specs_.width_, specs_.width_ref, ctx); internal::handle_dynamic_spec(specs_.precision_, specs_.precision_ref, ctx); typedef output_range range_type; - visit(arg_formatter(ctx, specs_), internal::make_arg(val)); - return ctx.out(); + return visit(arg_formatter(ctx, specs_), internal::make_arg(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 -std::basic_string to_string(const basic_memory_buffer &buffer) +template +std::basic_string to_string(const basic_memory_buffer &buf) { - return std::basic_string(buffer.data(), buffer.size()); + return std::basic_string(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>(buf, format_str, args); } -template -inline format_context::iterator format_to(memory_buffer &buf, string_view format_str, const Args &... args) +template +inline format_context::iterator format_to(basic_memory_buffer &buf, string_view format_str, const Args &... args) { return vformat_to(buf, format_str, make_format_args(args...)); } -template -inline wformat_context::iterator format_to(wmemory_buffer &buf, wstring_view format_str, const Args &... args) +template +inline wformat_context::iterator format_to(basic_memory_buffer &buf, wstring_view format_str, const Args &... args) { return vformat_to(buf, format_str, make_format_args(args...)); } @@ -4276,6 +4235,12 @@ inline OutputIt vformat_to(OutputIt out, string_view format_str, typename format typedef output_range range; return vformat_to>(range(out), format_str, args); } +template +inline OutputIt vformat_to(OutputIt out, wstring_view format_str, typename format_args_t::type args) +{ + typedef output_range range; + return vformat_to>(range(out), format_str, args); +} /** \rst @@ -4317,6 +4282,26 @@ struct format_to_n_result std::size_t size; }; +template +using format_to_n_context = typename fmt::format_context_t>::type; + +template +using format_to_n_args = fmt::basic_format_args>; + +template +inline format_arg_store, Args...> make_format_to_n_args(const Args &... args) +{ + return format_arg_store, Args...>(args...); +} + +template +inline format_to_n_result vformat_to_n(OutputIt out, std::size_t n, string_view format_str, format_to_n_args args) +{ + typedef internal::truncating_iterator 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 inline format_to_n_result format_to_n(OutputIt out, std::size_t n, string_view format_str, const Args &... args) +{ + return vformat_to_n(out, n, format_str, make_format_to_n_args(args...)); +} +template +inline format_to_n_result format_to_n(OutputIt out, std::size_t n, wstring_view format_str, const Args &... args) { typedef internal::truncating_iterator It; - auto it = vformat_to(It(out, n), format_str, make_format_args::type>(args...)); + auto it = vformat_to(It(out, n), format_str, make_format_args::type>(args...)); return {it.base(), it.count()}; } @@ -4361,8 +4351,8 @@ inline typename std::enable_if::value>::type } /** - Returns the number of characters in the output of - ``format(format_str, args...)``. + Returns the number of characters in the output of + ``format(format_str, args...)``. */ template inline std::size_t formatted_size(string_view format_str, const Args &... args) @@ -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 +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 +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 { diff --git a/include/spdlog/fmt/bundled/printf.h b/include/spdlog/fmt/bundled/printf.h index 073439e6..1782c2ea 100644 --- a/include/spdlog/fmt/bundled/printf.h +++ b/include/spdlog/fmt/bundled/printf.h @@ -290,27 +290,38 @@ public: { } - using base::operator(); - - /** Formats an argument of type ``bool``. */ - iterator operator()(bool value) + template + typename std::enable_if::value, iterator>::type operator()(T value) { - format_specs &fmt_spec = this->spec(); - if (fmt_spec.type_ != 's') - return (*this)(value ? 1 : 0); - fmt_spec.type_ = 0; - this->write(value); + // MSVC2013 fails to compile separate overloads for bool and char_type so + // use std::is_same instead. + if (std::is_same::value) + { + format_specs &fmt_spec = this->spec(); + if (fmt_spec.type_ != 's') + return base::operator()(value ? 1 : 0); + fmt_spec.type_ = 0; + this->write(value != 0); + } + else if (std::is_same::value) + { + format_specs &fmt_spec = this->spec(); + if (fmt_spec.type_ && fmt_spec.type_ != 'c') + return (*this)(static_cast(value)); + fmt_spec.flags_ = 0; + fmt_spec.align_ = ALIGN_RIGHT; + return base::operator()(value); + } + else + { + return base::operator()(value); + } return this->out(); } - /** Formats a character. */ - iterator operator()(char_type value) + template + typename std::enable_if::value, iterator>::type operator()(T value) { - format_specs &fmt_spec = this->spec(); - if (fmt_spec.type_ && fmt_spec.type_ != 'c') - return (*this)(static_cast(value)); - fmt_spec.flags_ = 0; - fmt_spec.align_ = ALIGN_RIGHT; return base::operator()(value); } @@ -338,6 +349,16 @@ public: return this->out(); } + iterator operator()(basic_string_view value) + { + return base::operator()(value); + } + + iterator operator()(monostate value) + { + return base::operator()(value); + } + /** Formats a pointer. */ iterator operator()(const void *value) { diff --git a/include/spdlog/fmt/bundled/ranges.h b/include/spdlog/fmt/bundled/ranges.h index 0c871cc6..c5d8c356 100644 --- a/include/spdlog/fmt/bundled/ranges.h +++ b/include/spdlog/fmt/bundled/ranges.h @@ -114,12 +114,14 @@ struct is_range_ : std::false_type { }; +#if !FMT_MSC_VER || FMT_MSC_VER > 1800 template struct is_range_().begin()), decltype(internal::declval().end())>, void>::type> : std::true_type { }; +#endif /// tuple_size and tuple_element check. template @@ -171,7 +173,7 @@ using make_index_sequence = make_integer_sequence; #endif template -void for_each(index_sequence, Tuple &&tup, F &&f) noexcept +void for_each(index_sequence, Tuple &&tup, F &&f) FMT_NOEXCEPT { using std::get; // using free function get(T) now. diff --git a/include/spdlog/formatter.h b/include/spdlog/formatter.h index ec343f83..39d86524 100644 --- a/include/spdlog/formatter.h +++ b/include/spdlog/formatter.h @@ -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" diff --git a/include/spdlog/logger.h b/include/spdlog/logger.h index 4fd88ff0..372d8314 100644 --- a/include/spdlog/logger.h +++ b/include/spdlog/logger.h @@ -45,23 +45,23 @@ public: template void log(level::level_enum lvl, const char *msg); - template - void trace(const char *fmt, const Arg1 &, const Args &... args); + template + void trace(const char *fmt, const Args &... args); - template - void debug(const char *fmt, const Arg1 &, const Args &... args); + template + void debug(const char *fmt, const Args &... args); - template - void info(const char *fmt, const Arg1 &, const Args &... args); + template + void info(const char *fmt, const Args &... args); - template - void warn(const char *fmt, const Arg1 &, const Args &... args); + template + void warn(const char *fmt, const Args &... args); - template - void error(const char *fmt, const Arg1 &, const Args &... args); + template + void error(const char *fmt, const Args &... args); - template - void critical(const char *fmt, const Arg1 &, const Args &... args); + template + void critical(const char *fmt, const Args &... args); #ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT template @@ -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 void set_formatter(const Args &... args); diff --git a/include/spdlog/sinks/android_sink.h b/include/spdlog/sinks/android_sink.h index 9b1259e1..97515db9 100644 --- a/include/spdlog/sinks/android_sink.h +++ b/include/spdlog/sinks/android_sink.h @@ -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) { diff --git a/include/spdlog/sinks/ansicolor_sink.h b/include/spdlog/sinks/ansicolor_sink.h index d817dffb..961062ac 100644 --- a/include/spdlog/sinks/ansicolor_sink.h +++ b/include/spdlog/sinks/ansicolor_sink.h @@ -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 #include @@ -22,14 +22,14 @@ namespace sinks { * of the message. * If no color terminal detected, omit the escape codes. */ -template +template 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 colors_; }; -using ansicolor_stdout_sink_mt = ansicolor_sink; -using ansicolor_stdout_sink_st = ansicolor_sink; -using ansicolor_stderr_sink_mt = ansicolor_sink; -using ansicolor_stderr_sink_st = ansicolor_sink; +using ansicolor_stdout_sink_mt = ansicolor_sink; +using ansicolor_stdout_sink_st = ansicolor_sink; + +using ansicolor_stderr_sink_mt = ansicolor_sink; +using ansicolor_stderr_sink_st = ansicolor_sink; } // namespace sinks diff --git a/include/spdlog/sinks/base_sink.h b/include/spdlog/sinks/base_sink.h index 871f2b81..292a3261 100644 --- a/include/spdlog/sinks/base_sink.h +++ b/include/spdlog/sinks/base_sink.h @@ -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 sink_formatter) - : sink(std::move(sink_formatter)) - { - } - base_sink(const base_sink &) = delete; base_sink &operator=(const base_sink &) = delete; diff --git a/include/spdlog/sinks/simple_file_sink.h b/include/spdlog/sinks/basic_file_sink.h similarity index 63% rename from include/spdlog/sinks/simple_file_sink.h rename to include/spdlog/sinks/basic_file_sink.h index 5af863dc..ffa92be0 100644 --- a/include/spdlog/sinks/simple_file_sink.h +++ b/include/spdlog/sinks/basic_file_sink.h @@ -18,28 +18,18 @@ namespace sinks { * Trivial file sink with single file as target */ template -class simple_file_sink SPDLOG_FINAL : public base_sink +class basic_file_sink SPDLOG_FINAL : public base_sink { 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; -using simple_file_sink_st = simple_file_sink; +using basic_file_sink_mt = basic_file_sink; +using basic_file_sink_st = basic_file_sink; } // namespace sinks @@ -63,13 +52,13 @@ using simple_file_sink_st = simple_file_sink; template inline std::shared_ptr basic_logger_mt(const std::string &logger_name, const filename_t &filename, bool truncate = false) { - return Factory::template create(logger_name, filename, truncate); + return Factory::template create(logger_name, filename, truncate); } template inline std::shared_ptr basic_logger_st(const std::string &logger_name, const filename_t &filename, bool truncate = false) { - return Factory::template create(logger_name, filename, truncate); + return Factory::template create(logger_name, filename, truncate); } } // namespace spdlog diff --git a/include/spdlog/sinks/dist_sink.h b/include/spdlog/sinks/dist_sink.h index 1ad22120..80861247 100644 --- a/include/spdlog/sinks/dist_sink.h +++ b/include/spdlog/sinks/dist_sink.h @@ -7,7 +7,6 @@ #include "spdlog/details/log_msg.h" #include "spdlog/details/null_mutex.h" -#include "spdlog/sink/base_sink.h" #include #include @@ -18,22 +17,18 @@ namespace spdlog { namespace sinks { -template -class dist_sink : public base_sink + +template +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> sinks_; - - void sink_it_(const details::log_msg &msg) override + void log(const details::log_msg &msg) SPDLOG_FINAL override { + std::lock_guard 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 lock(mutex_); for (auto &sink : sinks_) sink->flush(); } -public: void add_sink(std::shared_ptr sink) { - std::lock_guard lock(base_sink::mutex_); + std::lock_guard lock(mutex_); sinks_.push_back(sink); } void remove_sink(std::shared_ptr sink) { - std::lock_guard lock(base_sink::mutex_); + std::lock_guard lock(mutex_); sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end()); } + +private: + Mutex mutex_; + std::vector> sinks_; }; using dist_sink_mt = dist_sink; diff --git a/include/spdlog/sinks/msvc_sink.h b/include/spdlog/sinks/msvc_sink.h index b12a1b49..4c45635a 100644 --- a/include/spdlog/sinks/msvc_sink.h +++ b/include/spdlog/sinks/msvc_sink.h @@ -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; using msvc_sink_st = msvc_sink; +using windebug_sink_mt = msvc_sink_mt; +using windebug_sink_st = msvc_sink_st; + } // namespace sinks } // namespace spdlog diff --git a/include/spdlog/sinks/sink.h b/include/spdlog/sinks/sink.h index 513e0661..14557e8d 100644 --- a/include/spdlog/sinks/sink.h +++ b/include/spdlog/sinks/sink.h @@ -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(new pattern_formatter(formatter_pattern))) - { - } - - // sink with custom formatter - explicit sink(std::unique_ptr 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(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 formatter_; diff --git a/include/spdlog/sinks/stdout_sinks.h b/include/spdlog/sinks/stdout_sinks.h index 80854730..5d25a954 100644 --- a/include/spdlog/sinks/stdout_sinks.h +++ b/include/spdlog/sinks/stdout_sinks.h @@ -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 #include #include +#include namespace spdlog { namespace sinks { -template +template 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 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 lock(mutex_); - fflush(StdoutTrait::stream()); + fflush(TargetStream::stream()); } private: @@ -50,10 +53,11 @@ private: FILE *file_; }; -using stdout_sink_mt = stdout_sink; -using stdout_sink_st = stdout_sink; -using stderr_sink_mt = stdout_sink; -using stderr_sink_st = stdout_sink; +using stdout_sink_mt = stdout_sink; +using stdout_sink_st = stdout_sink; + +using stderr_sink_mt = stdout_sink; +using stderr_sink_st = stdout_sink; } // namespace sinks diff --git a/include/spdlog/sinks/wincolor_sink.h b/include/spdlog/sinks/wincolor_sink.h index 9b93dc13..7199db11 100644 --- a/include/spdlog/sinks/wincolor_sink.h +++ b/include/spdlog/sinks/wincolor_sink.h @@ -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 @@ -22,7 +22,7 @@ namespace sinks { /* * Windows color console sink. Uses WriteConsoleA to write to the console with colors */ -template +template 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 colors_; }; -using wincolor_stdout_sink_mt = wincolor_sink; -using wincolor_stdout_sink_st = wincolor_sink; +using wincolor_stdout_sink_mt = wincolor_sink; +using wincolor_stdout_sink_st = wincolor_sink; -using wincolor_stderr_sink_mt = wincolor_sink; -using wincolor_stderr_sink_st = wincolor_sink; +using wincolor_stderr_sink_mt = wincolor_sink; +using wincolor_stderr_sink_st = wincolor_sink; } // namespace sinks } // namespace spdlog diff --git a/include/spdlog/sinks/windebug_sink.h b/include/spdlog/sinks/windebug_sink.h deleted file mode 100644 index 15ffdc2e..00000000 --- a/include/spdlog/sinks/windebug_sink.h +++ /dev/null @@ -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 -using windebug_sink = msvc_sink; - -using windebug_sink_mt = msvc_sink_mt; -using windebug_sink_st = msvc_sink_st; - -} // namespace sinks -} // namespace spdlog - -#endif diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h index b51578fa..90148d6f 100644 --- a/include/spdlog/spdlog.h +++ b/include/spdlog/spdlog.h @@ -19,7 +19,7 @@ namespace spdlog { // Default logger factory- creates synchronous loggers -struct create_synchronous +struct synchronous_factory { template @@ -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 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); } // diff --git a/tests/errors.cpp b/tests/errors.cpp index c3ccbe2f..1320b748 100644 --- a/tests/errors.cpp +++ b/tests/errors.cpp @@ -24,7 +24,7 @@ TEST_CASE("default_error_handler", "[errors]]") prepare_logdir(); std::string filename = "logs/simple_log.txt"; - auto logger = spdlog::create("test-error", filename, true); + auto logger = spdlog::create("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("logger", filename, true); + auto logger = spdlog::create("logger", filename, true); logger->flush_on(spdlog::level::info); logger->set_error_handler([=](const std::string &) { throw custom_ex(); }); logger->info("Good message #1"); @@ -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("logger", filename, true); + auto logger = spdlog::create_async("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("failed_logger"); + auto logger = spdlog::create_async("failed_logger"); logger->set_error_handler([=](const std::string &) { std::ofstream ofs("logs/custom_err2.txt"); if (!ofs) diff --git a/tests/file_log.cpp b/tests/file_log.cpp index 9cef256d..55240b78 100644 --- a/tests/file_log.cpp +++ b/tests/file_log.cpp @@ -8,7 +8,7 @@ TEST_CASE("simple_file_logger", "[simple_logger]]") prepare_logdir(); std::string filename = "logs/simple_log"; - auto logger = spdlog::create("logger", filename); + auto logger = spdlog::create("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("logger", filename); + auto logger = spdlog::create("logger", filename); logger->set_pattern("%v"); logger->set_level(spdlog::level::trace); logger->flush_on(spdlog::level::info); diff --git a/tests/includes.h b/tests/includes.h index 10bfb062..c9056a02 100644 --- a/tests/includes.h +++ b/tests/includes.h @@ -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" diff --git a/tests/test_async.cpp b/tests/test_async.cpp index 7ade2676..467b37a6 100644 --- a/tests/test_async.cpp +++ b/tests/test_async.cpp @@ -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(queue_size, 1); - auto logger = std::make_shared("as", test_sink, tp, async_overflow_policy::block_retry); + auto logger = std::make_shared("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(queue_size, 1); - auto logger = std::make_shared("as", test_sink, tp, async_overflow_policy::overrun_oldeset); + auto logger = std::make_shared("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(queue_size, 1); - auto logger = std::make_shared("as", test_sink, tp, async_overflow_policy::block_retry); + auto logger = std::make_shared("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(messages, 2); - auto logger = std::make_shared("as", test_sink, tp, async_overflow_policy::block_retry); + auto logger = std::make_shared("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(queue_size, 1); - auto logger = std::make_shared("as", test_sink, tp, async_overflow_policy::block_retry); + auto logger = std::make_shared("as", test_sink, tp, async_overflow_policy::block); std::vector 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(filename, true); + auto file_sink = std::make_shared(filename, true); auto tp = std::make_shared(messages, tp_threads); auto logger = std::make_shared("as", std::move(file_sink), std::move(tp)); @@ -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(filename, true); + auto file_sink = std::make_shared(filename, true); auto tp = std::make_shared(messages, tp_threads); auto logger = std::make_shared("as", std::move(file_sink), std::move(tp)); diff --git a/tests/test_macros.cpp b/tests/test_macros.cpp index 2d165aae..0b3e0b7d 100644 --- a/tests/test_macros.cpp +++ b/tests/test_macros.cpp @@ -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("logger", filename); + auto logger = spdlog::create("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("logger", filename); + auto logger = spdlog::create("logger", filename); logger->set_pattern("%v"); logger->set_level(spdlog::level::trace);