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