commit
e166fadeb9
86
README.md
86
README.md
@ -2,11 +2,9 @@
|
||||
|
||||
Very fast, header only, C++ logging library.
|
||||
|
||||
|
||||
## Install
|
||||
Just copy the files to your build tree and use a C++11 compiler
|
||||
|
||||
|
||||
## Tested on:
|
||||
* gcc 4.8.1 and above
|
||||
* clang 3.5
|
||||
@ -14,11 +12,13 @@ Just copy the files to your build tree and use a C++11 compiler
|
||||
* mingw with g++ 4.9.x
|
||||
|
||||
##Features
|
||||
* Very fast - performance is the primary goal (see becnhmarks below).
|
||||
* Very fast - performance is the primary goal (see [becnhmarks](#benchmarks) below).
|
||||
* Headers only.
|
||||
* No dependencies.
|
||||
* No dependencies - just copy and use.
|
||||
* Cross platform - Linux / Windows on 32/64 bits.
|
||||
* Variadic-template/stream call styles: ```logger.info("variadic", x, y) << "or stream" << z;```
|
||||
* **new!** Feature rich [call style](#usage-example) using the excellent [cppformat](http://cppformat.github.io/) library.
|
||||
* ostream call style is supported too.
|
||||
* Extremely fast asynchronous mode (optional) - use of lockfree queues and other tricks to reach millions of calls per second from multiple threads.
|
||||
* [Custom](https://github.com/gabime/spdlog/wiki/Custom-formatting) formatting.
|
||||
* Multi/Single threaded loggers.
|
||||
* Various log targets:
|
||||
@ -27,22 +27,18 @@ Just copy the files to your build tree and use a C++11 compiler
|
||||
* Console logging.
|
||||
* Linux syslog.
|
||||
* Easily extendable with custom log targets (just implement a single function in the [sink](include/spdlog/sinks/sink.h) interface).
|
||||
* Optional async logging .
|
||||
* Log levels.
|
||||
|
||||
* Severity based filtering - threshold levels can be modified in runtime.
|
||||
|
||||
|
||||
|
||||
## Benchmarks
|
||||
|
||||
Below are some [benchmarks](bench) comparing the time needed to log 1,000,000 lines to file under Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz:
|
||||
Below are some [benchmarks](bench) comparing the time needed to log 1,000,000 lines to file under Ubuntu 64 bit, Intel i7-4770 CPU @ 3.40GHz (the best of 3 runs for each logger):
|
||||
|
||||
|threads|boost log|glog|g2log|spdlog|spdlog <sup>async mode</sup>|
|
||||
|threads|boost log|glog|g2log <sup>async mode</sup>|spdlog|spdlog <sup>async mode</sup>|
|
||||
|-------|:-------:|:-----:|------:|------:|------:|
|
||||
|1|4.779s|1.109s|3.155s|0.947s|1.455s
|
||||
|10|15.151ss|3.546s|3.500s|1.549s|2.040s|
|
||||
|
||||
|
||||
|1|4.779s|1.109s|3.155s|0.319s|0.212s
|
||||
|10|15.151ss|3.546s|3.500s|0.641s|0.199s|
|
||||
|
||||
|
||||
## Usage Example
|
||||
@ -53,37 +49,61 @@ Below are some [benchmarks](bench) comparing the time needed to log 1,000,000 li
|
||||
int main(int, char* [])
|
||||
{
|
||||
namespace spd = spdlog;
|
||||
|
||||
try
|
||||
{
|
||||
std::string filename = "spdlog_example";
|
||||
// Set log level to all loggers to DEBUG and above
|
||||
spd::set_level(spd::level::DEBUG);
|
||||
|
||||
//Create console, multithreaded logger
|
||||
auto console = spd::stdout_logger_mt("console");
|
||||
console->info("Welcome to spdlog!") ;
|
||||
console->info() << "Creating file " << filename << "..";
|
||||
console->info("An info message example {}..", 1);
|
||||
console->info() << "Streams are supported too " << 1;
|
||||
|
||||
auto file_logger = spd::rotating_logger_mt("file_logger", filename, 1024 * 1024 * 5, 3);
|
||||
file_logger->info("Log file message number", 1);
|
||||
console->info("Easy padding in numbers like {:08d}", 12);
|
||||
console->info("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");
|
||||
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
auto square = i*i;
|
||||
file_logger->info() << i << '*' << i << '=' << square << " (" << "0x" << std::hex << square << ")";
|
||||
}
|
||||
console->info("{:<30}", "left aligned");
|
||||
console->info("{:>30}", "right aligned");
|
||||
console->info("{:^30}", "centered");
|
||||
|
||||
//Create a file rotating logger with 5mb size max and 3 rotated files
|
||||
auto file_logger = spd::rotating_logger_mt("file_logger", "logs/mylogfile", 1048576 * 5, 3);
|
||||
file_logger->set_level(spd::level::INFO);
|
||||
for(int i = 0; i < 10; ++i)
|
||||
file_logger->info("{} * {} equals {:>10}", i, i, i*i);
|
||||
|
||||
// Change log level to all loggers to warning and above
|
||||
spd::set_level(spd::level::WARN);
|
||||
console->info("This should not be displayed");
|
||||
console->warn("This should!");
|
||||
spd::set_level(spd::level::INFO);
|
||||
//Customize msg format for all messages
|
||||
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
|
||||
file_logger->info("This is another message with custom format");
|
||||
|
||||
// Change format pattern to all loggers
|
||||
spd::set_pattern(" **** %Y-%m-%d %H:%M:%S.%e %l **** %v");
|
||||
spd::get("console")->info("This is another message with different format");
|
||||
spd::get("console")->info("loggers can be retrieved from a global registry using the spdlog::get(logger_name) function");
|
||||
|
||||
SPDLOG_TRACE(file_logger, "This is a trace message (only #ifdef _DEBUG)", 123);
|
||||
|
||||
//
|
||||
// Asynchronous logging is very fast..
|
||||
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
|
||||
//
|
||||
size_t q_size = 1048576; //queue size must be power of 2
|
||||
spdlog::set_async_mode(q_size);
|
||||
auto async_file= spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
|
||||
async_file->info() << "This is async log.." << "Should be very fast!";
|
||||
|
||||
//
|
||||
// syslog example
|
||||
//
|
||||
#ifdef __linux__
|
||||
auto syslog_logger = spd::syslog_logger("syslog");
|
||||
syslog_logger->warn("This is warning that will end up in syslog. This is Linux only!");
|
||||
#endif
|
||||
}
|
||||
catch (const spd::spdlog_ex& ex)
|
||||
{
|
||||
std::cout << "Log failed: " << ex.what() << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
```
|
||||
|
@ -1,5 +1,5 @@
|
||||
CXX = g++
|
||||
CXXFLAGS = -g -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
|
||||
CXXFLAGS = -march=native -Wall -Wshadow -Wextra -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
|
||||
CXX_RELEASE_FLAGS = -O3 -flto
|
||||
|
||||
|
||||
|
@ -19,7 +19,7 @@ int main(int argc, char* argv[])
|
||||
|
||||
g2LogWorker g2log(argv[0], "logs");
|
||||
g2::initializeLogging(&g2log);
|
||||
|
||||
|
||||
|
||||
std::atomic<int > msg_counter {0};
|
||||
vector<thread> threads;
|
||||
|
@ -5,10 +5,10 @@
|
||||
int main(int, char* argv[])
|
||||
{
|
||||
int howmany = 1000000;
|
||||
|
||||
g2LogWorker g2log(argv[0], "logs");
|
||||
|
||||
g2LogWorker g2log(argv[0], "logs");
|
||||
g2::initializeLogging(&g2log);
|
||||
|
||||
|
||||
for(int i = 0 ; i < howmany; ++i)
|
||||
LOG(INFO) << "g2log message # " << i << ": This is some text for your pleasure";
|
||||
|
||||
|
@ -1,80 +0,0 @@
|
||||
Running benchmakrs (all with 1000,000 writes to the logs folder
|
||||
|
||||
boost-bench (single thread)..
|
||||
|
||||
real 0m4.779s
|
||||
user 0m4.256s
|
||||
sys 0m0.044s
|
||||
|
||||
|
||||
glog-bench (single thread)..
|
||||
|
||||
real 0m1.109s
|
||||
user 0m0.967s
|
||||
sys 0m0.140s
|
||||
|
||||
|
||||
g2log-bench (single thread)..
|
||||
|
||||
Exiting, log location: logs/g2log-bench.g2log.20141124-233419.log
|
||||
|
||||
real 0m3.155s
|
||||
user 0m4.255s
|
||||
sys 0m0.874s
|
||||
|
||||
|
||||
spdlog-bench (single thread)
|
||||
|
||||
real 0m0.947s
|
||||
user 0m0.914s
|
||||
sys 0m0.032s
|
||||
|
||||
|
||||
------------------------------------
|
||||
Multithreaded benchmarks..
|
||||
------------------------------------
|
||||
boost-bench-mt (10 threads, single logger)..
|
||||
|
||||
real 0m15.151s
|
||||
user 0m35.555s
|
||||
sys 0m7.453s
|
||||
|
||||
|
||||
glog-bench-mt (10 threads, single logger)..
|
||||
|
||||
real 0m3.546s
|
||||
user 0m9.836s
|
||||
sys 0m10.985s
|
||||
|
||||
|
||||
g2log-bench-mt (10 threads, single logger)..
|
||||
|
||||
Exiting, log location: logs/g2log-bench-mt.g2log.20141124-233502.log
|
||||
|
||||
real 0m3.500s
|
||||
user 0m7.671s
|
||||
sys 0m1.646s
|
||||
|
||||
|
||||
spdlog-bench-mt (10 threads, single logger)..
|
||||
|
||||
real 0m1.549s
|
||||
user 0m6.994s
|
||||
sys 0m2.969s
|
||||
|
||||
|
||||
------------------------------------
|
||||
Async benchmarks..
|
||||
------------------------------------
|
||||
spdlog-bench-async (single thread)..
|
||||
|
||||
real 0m1.455s
|
||||
user 0m2.381s
|
||||
sys 0m0.417s
|
||||
|
||||
|
||||
spdlog-bench-mt-async (10 threads, single logger)..
|
||||
|
||||
real 0m2.040s
|
||||
user 0m4.076s
|
||||
sys 0m2.822s
|
@ -2,29 +2,29 @@
|
||||
echo "Running benchmakrs (all with 1000,000 writes to the logs folder)"
|
||||
echo
|
||||
echo "boost-bench (single thread).."
|
||||
time ./boost-bench
|
||||
rm logs/*
|
||||
for i in {1..3}; do time ./boost-bench; done
|
||||
rm -f logs/*
|
||||
echo
|
||||
echo
|
||||
sleep 5
|
||||
|
||||
echo "glog-bench (single thread).."
|
||||
time ./glog-bench
|
||||
rm logs/*
|
||||
for i in {1..3}; do time ./glog-bench; done
|
||||
rm -f logs/*
|
||||
echo
|
||||
echo
|
||||
sleep 5
|
||||
|
||||
echo "g2log-bench (single thread).."
|
||||
time ./g2log-bench
|
||||
rm logs/*
|
||||
for i in {1..3}; do time ./g2log-bench; done
|
||||
rm -f logs/*
|
||||
echo
|
||||
echo
|
||||
sleep 5
|
||||
|
||||
echo "spdlog-bench (single thread)"
|
||||
time ./spdlog-bench
|
||||
rm logs/*
|
||||
for i in {1..3}; do time ./spdlog-bench; done
|
||||
rm -f logs/*
|
||||
echo
|
||||
echo
|
||||
sleep 5
|
||||
@ -32,29 +32,29 @@ echo "------------------------------------"
|
||||
echo "Multithreaded benchmarks.."
|
||||
echo "------------------------------------"
|
||||
echo "boost-bench-mt (10 threads, single logger)"..
|
||||
time ./boost-bench-mt
|
||||
rm logs/*
|
||||
for i in {1..3}; do ./boost-bench-mt; done
|
||||
rm -f logs/*
|
||||
echo
|
||||
echo
|
||||
sleep 5
|
||||
|
||||
echo "glog-bench-mt (10 threads, single logger)"..
|
||||
time ./glog-bench-mt
|
||||
rm logs/*
|
||||
for i in {1..3}; do time ./glog-bench-mt; done
|
||||
rm -f logs/*
|
||||
echo
|
||||
echo
|
||||
sleep 5
|
||||
|
||||
echo "g2log-bench-mt (10 threads, single logger)"..
|
||||
time ./g2log-bench-mt
|
||||
rm logs/*
|
||||
for i in {1..3}; do time ./g2log-bench-mt; done
|
||||
rm -f logs/*
|
||||
echo
|
||||
echo
|
||||
sleep 5
|
||||
|
||||
echo "spdlog-bench-mt (10 threads, single logger)"..
|
||||
time ./spdlog-bench-mt
|
||||
rm logs/*
|
||||
for i in {1..3}; do time ./spdlog-bench-mt; done
|
||||
rm -f logs/*
|
||||
echo
|
||||
echo
|
||||
sleep 5
|
||||
@ -64,14 +64,14 @@ echo "Async benchmarks.."
|
||||
echo "------------------------------------"
|
||||
|
||||
echo "spdlog-bench-async (single thread)"..
|
||||
time ./spdlog-bench-async
|
||||
rm logs/*
|
||||
for i in {1..3}; do time ./spdlog-bench-async; done
|
||||
rm -f logs/*
|
||||
echo
|
||||
echo
|
||||
sleep 5
|
||||
|
||||
echo "spdlog-bench-mt-async (10 threads, single logger)"..
|
||||
time ./spdlog-bench-mt-async
|
||||
for i in {1..3}; do time ./spdlog-bench-mt-async; done
|
||||
|
||||
|
||||
|
||||
|
@ -4,15 +4,19 @@
|
||||
|
||||
int main(int, char* [])
|
||||
{
|
||||
int howmany = 1000000;
|
||||
int howmany = 1048576;
|
||||
namespace spd = spdlog;
|
||||
spd::set_async_mode(2500, std::chrono::seconds(0));
|
||||
spd::set_async_mode(howmany);
|
||||
///Create a file rotating logger with 5mb size max and 3 rotated files
|
||||
auto logger = spd::rotating_logger_mt("file_logger", "logs/spd-sample", 10 *1024 * 1024 , 5);
|
||||
|
||||
logger->set_pattern("[%Y-%b-%d %T.%e]: %v");
|
||||
for(int i = 0 ; i < howmany; ++i)
|
||||
logger->info() << "spdlog message #" << i << ": This is some text for your pleasure";
|
||||
spd::stop();
|
||||
|
||||
|
||||
//because spdlog async logger waits for the back thread logger to finish all messages upon destrcuting,
|
||||
//and we want to measure only the time it took to push those messages to the backthread..
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
@ -14,10 +14,10 @@ int main(int argc, char* argv[])
|
||||
if(argc > 1)
|
||||
thread_count = atoi(argv[1]);
|
||||
|
||||
int howmany = 1000000;
|
||||
int howmany = 1048576;
|
||||
|
||||
namespace spd = spdlog;
|
||||
spd::set_async_mode(2500, std::chrono::seconds(0));
|
||||
spd::set_async_mode(howmany);
|
||||
///Create a file rotating logger with 5mb size max and 3 rotated files
|
||||
auto logger = spd::rotating_logger_mt("file_logger", "logs/spd-sample", 10 *1024 * 1024 , 5);
|
||||
|
||||
@ -45,6 +45,7 @@ int main(int argc, char* argv[])
|
||||
t.join();
|
||||
};
|
||||
|
||||
spd::stop();
|
||||
return 0;
|
||||
//because spdlog async logger waits for the back thread logger to finish all messages upon destrcuting,
|
||||
//and we want to measure only the time it took to push those messages to the backthread..
|
||||
abort();
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ int main(int argc, char* argv[])
|
||||
int howmany = 1000000;
|
||||
|
||||
namespace spd = spdlog;
|
||||
///Create a file rotating logger with 5mb size max and 5 rotated files
|
||||
auto logger = spd::rotating_logger_mt("file_logger", "logs/spd-sample", 10 *1024 * 1024 , 5);
|
||||
///Create a file rotating logger with 10mb size max and 5 rotated files
|
||||
auto logger = spd::rotating_logger_mt("file_logger", "logs/spd-sample", 10 *1024 * 1024 , 5, false);
|
||||
|
||||
logger->set_pattern("[%Y-%b-%d %T.%e]: %v");
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
CXX = g++
|
||||
CXXFLAGS = -march=native -Wall -Wextra -Wshadow -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
|
||||
CXXFLAGS = -march=native -Wall -Wshadow -Wextra -pedantic -std=c++11 -pthread -Wl,--no-as-needed -I../include
|
||||
CXX_RELEASE_FLAGS = -O3 -flto
|
||||
CXX_DEBUG_FLAGS= -g
|
||||
|
||||
|
@ -50,9 +50,9 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
|
||||
int howmany = 1000000;
|
||||
int howmany = 1048576;
|
||||
int threads = 10;
|
||||
bool auto_flush = true;
|
||||
bool auto_flush = false;
|
||||
int file_size = 30 * 1024 * 1024;
|
||||
int rotating_files = 5;
|
||||
|
||||
@ -64,6 +64,7 @@ int main(int argc, char* argv[])
|
||||
if (argc > 2)
|
||||
threads = atoi(argv[2]);
|
||||
|
||||
|
||||
cout << "*******************************************************************************\n";
|
||||
cout << "Single thread, " << format(howmany) << " iterations, auto flush=" << auto_flush << endl;
|
||||
cout << "*******************************************************************************\n";
|
||||
@ -85,18 +86,20 @@ int main(int argc, char* argv[])
|
||||
auto daily_mt = spdlog::daily_logger_mt("daily_mt", "logs/daily_mt", auto_flush);
|
||||
bench_mt(howmany, daily_mt, threads);
|
||||
bench(howmany, spdlog::create<null_sink_st>("null_mt"));
|
||||
|
||||
|
||||
cout << "\n*******************************************************************************\n";
|
||||
cout << "async logging.. " << threads << " threads sharing same logger, " << format(howmany) << " iterations, auto_flush=" << auto_flush << endl;
|
||||
cout << "*******************************************************************************\n";
|
||||
cout << "*******************************************************************************\n";
|
||||
|
||||
|
||||
spdlog::set_async_mode(2500);
|
||||
auto daily_st_async = spdlog::daily_logger_st("daily_async", "logs/daily_async", auto_flush);
|
||||
bench_mt(howmany, daily_st_async, threads);
|
||||
|
||||
spdlog::stop();
|
||||
|
||||
spdlog::set_async_mode(howmany);
|
||||
|
||||
for(int i = 0; i < 5; ++i)
|
||||
{
|
||||
auto as = spdlog::daily_logger_st("as", "logs/daily_async", auto_flush);
|
||||
bench_mt(howmany, as, threads);
|
||||
spdlog::drop("as");
|
||||
}
|
||||
}
|
||||
catch (std::exception &ex)
|
||||
{
|
||||
@ -113,7 +116,7 @@ void bench(int howmany, std::shared_ptr<spdlog::logger> log)
|
||||
auto start = system_clock::now();
|
||||
for (auto i = 0; i < howmany; ++i)
|
||||
{
|
||||
log->info("Hello logger: msg number ", i);
|
||||
log->info("Hello logger: msg number {}", i);
|
||||
}
|
||||
|
||||
|
||||
@ -138,7 +141,7 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count
|
||||
{
|
||||
int counter = ++msg_counter;
|
||||
if (counter > howmany) break;
|
||||
log->info("Hello logger: msg number ") << counter;
|
||||
log->info("Hello logger: msg number {}", counter);
|
||||
}
|
||||
}));
|
||||
}
|
||||
@ -154,4 +157,3 @@ void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count
|
||||
auto delta_d = duration_cast<duration<double>> (delta).count();
|
||||
cout << format(int(howmany / delta_d)) << "/sec" << endl;
|
||||
}
|
||||
|
||||
|
@ -44,17 +44,25 @@ int main(int, char* [])
|
||||
//Create console, multithreaded logger
|
||||
auto console = spd::stdout_logger_mt("console");
|
||||
console->info("Welcome to spdlog!") ;
|
||||
console->info("An info message example", "...", 1, 2, 3.5);
|
||||
console->info() << "Streams are supported too " << std::setw(5) << std::setfill('0') << 1;
|
||||
console->info("An info message example {}..", 1);
|
||||
console->info() << "Streams are supported too " << 1;
|
||||
|
||||
|
||||
console->info("Easy padding in numbers like {:08d}", 12);
|
||||
console->info("Support for int: {0:d}; hex: {0:08x}; 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");
|
||||
console->info("{:>30}", "right aligned");
|
||||
console->info("{:^30}", "centered");
|
||||
|
||||
//Create a file rotating logger with 5mb size max and 3 rotated files
|
||||
auto file_logger = spd::rotating_logger_mt("file_logger", filename, 1024 * 1024 * 5, 3);
|
||||
file_logger->info("Log file message number", 1);
|
||||
|
||||
for (int i = 0; i < 100; ++i)
|
||||
{
|
||||
file_logger->info(i, "in hex is", "0x") << std::hex << std::uppercase << i;
|
||||
}
|
||||
file_logger->info("Log file message number", 1);
|
||||
for(int i = 0; i < 10; ++i)
|
||||
file_logger->info("{} * {} equals {:>10}", i, i, i*i);
|
||||
|
||||
spd::set_pattern("*** [%H:%M:%S %z] [thread %t] %v ***");
|
||||
file_logger->info("This is another message with custom format");
|
||||
@ -65,12 +73,12 @@ int main(int, char* [])
|
||||
|
||||
//
|
||||
// Asynchronous logging is easy..
|
||||
// Just call spdlog::set_async_mode(max_q_size) and all created loggers from now on will be asynchronous..
|
||||
// Just call spdlog::set_async_mode(q_size) and all created loggers from now on will be asynchronous..
|
||||
// Note: queue size must be power of 2!
|
||||
//
|
||||
|
||||
size_t max_q_size = 100000;
|
||||
size_t max_q_size = 1048576;
|
||||
spdlog::set_async_mode(max_q_size);
|
||||
auto async_file= spd::daily_logger_st("async_file_logger", "async_" + filename);
|
||||
auto async_file= spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
|
||||
async_file->info() << "This is async log.." << "Should be very fast!";
|
||||
|
||||
//
|
||||
|
@ -24,7 +24,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include <locale>
|
||||
|
@ -24,10 +24,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
// Async logger
|
||||
// Very fast asynchronous logger (millions of logs per second on an average desktop)
|
||||
// Uses pre allocated lockfree queue for maximum throughput even under large number of threads.
|
||||
// Creates a single back thread to pop messages from the queue and log them.
|
||||
//
|
||||
// Upon each log write the logger:
|
||||
// 1. Checks if its log level is enough to log the message
|
||||
// 2. Push a new copy of the message to a queue (uses sinks::async_sink for this)
|
||||
// 1. Checks if its log level is enough to log the message
|
||||
// 2. Push a new copy of the message to a queue (or block the caller until space is available in the queue)
|
||||
// 3. will throw spdlog_ex upon log exceptions
|
||||
// Upong destruction, logs all remaining messages in the queue before destructing..
|
||||
|
||||
#include <chrono>
|
||||
#include "common.h"
|
||||
@ -37,18 +42,18 @@
|
||||
namespace spdlog
|
||||
{
|
||||
|
||||
namespace sinks
|
||||
namespace details
|
||||
{
|
||||
class async_sink;
|
||||
class async_log_helper;
|
||||
}
|
||||
|
||||
class async_logger :public logger
|
||||
{
|
||||
public:
|
||||
template<class It>
|
||||
async_logger(const std::string& name, const It& begin, const It& end, size_t queue_size, const log_clock::duration& shutdown_duration);
|
||||
async_logger(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const log_clock::duration& shutdown_duration);
|
||||
async_logger(const std::string& logger_name, sink_ptr single_sink, size_t queue_size, const log_clock::duration& shutdown_duration);
|
||||
async_logger(const std::string& name, const It& begin, const It& end, size_t queue_size);
|
||||
async_logger(const std::string& logger_name, sinks_init_list sinks, size_t queue_size);
|
||||
async_logger(const std::string& logger_name, sink_ptr single_sink, size_t queue_size);
|
||||
|
||||
|
||||
protected:
|
||||
@ -58,8 +63,7 @@ protected:
|
||||
void _stop() override;
|
||||
|
||||
private:
|
||||
log_clock::duration _shutdown_duration;
|
||||
std::unique_ptr<sinks::async_sink> _as;
|
||||
std::unique_ptr<details::async_log_helper> _async_log_helper;
|
||||
};
|
||||
}
|
||||
|
||||
|
289
include/spdlog/details/async_log_helper.h
Normal file
289
include/spdlog/details/async_log_helper.h
Normal file
@ -0,0 +1,289 @@
|
||||
/*************************************************************************/
|
||||
/* spdlog - an extremely fast and easy to use c++11 logging library. */
|
||||
/* Copyright (c) 2014 Gabi Melman. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
// async log helper :
|
||||
// Process logs asynchronously using a back thread.
|
||||
//
|
||||
// If the internal queue of log messages reaches its max size,
|
||||
// then the client call will block until there is more room.
|
||||
//
|
||||
// If the back thread throws during logging, a spdlog::spdlog_ex exception
|
||||
// will be thrown in client's thread when tries to log the next message
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
#include "../common.h"
|
||||
#include "../sinks/sink.h"
|
||||
#include "./mpmc_bounded_q.h"
|
||||
#include "./log_msg.h"
|
||||
#include "./format.h"
|
||||
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
|
||||
|
||||
class async_log_helper
|
||||
{
|
||||
// Async msg to move to/from the queue
|
||||
// Movable only. should never be copied
|
||||
struct async_msg
|
||||
{
|
||||
std::string logger_name;
|
||||
level::level_enum level;
|
||||
log_clock::time_point time;
|
||||
std::string txt;
|
||||
|
||||
async_msg() = default;
|
||||
~async_msg() = default;
|
||||
|
||||
async_msg(const async_msg&) = delete;
|
||||
async_msg& operator=(async_msg& other) = delete;
|
||||
|
||||
async_msg(const details::log_msg& m) :
|
||||
logger_name(m.logger_name),
|
||||
level(m.level),
|
||||
time(m.time),
|
||||
txt(m.raw.data(), m.raw.size())
|
||||
{}
|
||||
|
||||
async_msg(async_msg&& other) :
|
||||
logger_name(std::move(other.logger_name)),
|
||||
level(std::move(other.level)),
|
||||
time(std::move(other.time)),
|
||||
txt(std::move(other.txt))
|
||||
{}
|
||||
|
||||
async_msg& operator=(async_msg&& other)
|
||||
{
|
||||
logger_name = std::move(other.logger_name);
|
||||
level = other.level;
|
||||
time = std::move(other.time);
|
||||
txt = std::move(other.txt);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void fill_log_msg(log_msg &msg)
|
||||
{
|
||||
msg.clear();
|
||||
msg.logger_name = logger_name;
|
||||
msg.level = level;
|
||||
msg.time = time;
|
||||
msg.raw << txt;
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
|
||||
using item_type = async_msg;
|
||||
using q_type = details::mpmc_bounded_queue<item_type>;
|
||||
|
||||
using clock = std::chrono::steady_clock;
|
||||
|
||||
|
||||
async_log_helper(formatter_ptr formatter, const std::vector<sink_ptr>& sinks, size_t queue_size);
|
||||
void log(const details::log_msg& msg);
|
||||
|
||||
//Stop logging and join the back thread
|
||||
~async_log_helper();
|
||||
void set_formatter(formatter_ptr);
|
||||
|
||||
|
||||
private:
|
||||
std::atomic<bool> _active;
|
||||
formatter_ptr _formatter;
|
||||
std::vector<std::shared_ptr<sinks::sink>> _sinks;
|
||||
q_type _q;
|
||||
std::thread _worker_thread;
|
||||
|
||||
// last exception thrown from the worker thread
|
||||
std::shared_ptr<spdlog_ex> _last_workerthread_ex;
|
||||
|
||||
|
||||
// throw last worker thread exception or if worker thread is not active
|
||||
void throw_if_bad_worker();
|
||||
|
||||
// worker thread main loop
|
||||
void worker_loop();
|
||||
|
||||
//pop next message from the queue and process it
|
||||
//return true if a message was available (queue was not empty), will set the last_pop to the pop time
|
||||
bool process_next_msg(clock::time_point& last_pop);
|
||||
|
||||
// guess how much to sleep if queue is empty/full using last succesful op time as hint
|
||||
static void sleep_or_yield(const clock::time_point& last_op_time);
|
||||
|
||||
|
||||
// clear all remaining messages(if any), stop the _worker_thread and join it
|
||||
void join_worker();
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// async_sink class implementation
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
inline spdlog::details::async_log_helper::async_log_helper(formatter_ptr formatter, const std::vector<sink_ptr>& sinks, size_t queue_size):
|
||||
_active(true),
|
||||
_formatter(formatter),
|
||||
_sinks(sinks),
|
||||
_q(queue_size),
|
||||
_worker_thread(&async_log_helper::worker_loop, this)
|
||||
{}
|
||||
|
||||
inline spdlog::details::async_log_helper::~async_log_helper()
|
||||
{
|
||||
join_worker();
|
||||
}
|
||||
|
||||
|
||||
//Try to push and block until succeeded
|
||||
inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
|
||||
{
|
||||
throw_if_bad_worker();
|
||||
async_msg new_msg(msg);
|
||||
if (!_q.enqueue(std::move(new_msg)))
|
||||
{
|
||||
auto last_op_time = clock::now();
|
||||
do
|
||||
{
|
||||
sleep_or_yield(last_op_time);
|
||||
}
|
||||
while (!_q.enqueue(std::move(new_msg)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
inline void spdlog::details::async_log_helper::worker_loop()
|
||||
{
|
||||
clock::time_point last_pop = clock::now();
|
||||
while (_active)
|
||||
{
|
||||
//Dont die if there are still messages in the q to process
|
||||
while(process_next_msg(last_pop));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
inline bool spdlog::details::async_log_helper::process_next_msg(clock::time_point& last_pop)
|
||||
{
|
||||
|
||||
async_msg incoming_async_msg;
|
||||
log_msg incoming_log_msg;
|
||||
|
||||
if (_q.dequeue(incoming_async_msg))
|
||||
{
|
||||
last_pop = clock::now();
|
||||
try
|
||||
{
|
||||
incoming_async_msg.fill_log_msg(incoming_log_msg);
|
||||
_formatter->format(incoming_log_msg);
|
||||
for (auto &s : _sinks)
|
||||
s->log(incoming_log_msg);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
_last_workerthread_ex = std::make_shared<spdlog_ex>(std::string("async_logger worker thread exception: ") + ex.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
_last_workerthread_ex = std::make_shared<spdlog_ex>("async_logger worker thread exception");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// sleep or yield if queue is empty.
|
||||
else
|
||||
{
|
||||
sleep_or_yield(last_pop);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter)
|
||||
{
|
||||
_formatter = msg_formatter;
|
||||
}
|
||||
|
||||
|
||||
// Sleep,yield or return immediatly using the time passed since last message as a hint
|
||||
inline void spdlog::details::async_log_helper::sleep_or_yield(const clock::time_point& last_op_time)
|
||||
{
|
||||
using std::chrono::milliseconds;
|
||||
using namespace std::this_thread;
|
||||
|
||||
auto time_since_op = clock::now() - last_op_time;
|
||||
|
||||
//spin upto 1 ms
|
||||
if (time_since_op <= milliseconds(1))
|
||||
return;
|
||||
|
||||
// yield upto 10ms
|
||||
if (time_since_op <= milliseconds(10))
|
||||
return yield();
|
||||
|
||||
|
||||
// sleep for half of duration since last op
|
||||
if (time_since_op <= milliseconds(100))
|
||||
return sleep_for(time_since_op / 2);
|
||||
|
||||
return sleep_for(milliseconds(100));
|
||||
}
|
||||
|
||||
//throw if the worker thread threw an exception or not active
|
||||
inline void spdlog::details::async_log_helper::throw_if_bad_worker()
|
||||
{
|
||||
if (_last_workerthread_ex)
|
||||
{
|
||||
auto ex = std::move(_last_workerthread_ex);
|
||||
_last_workerthread_ex.reset();
|
||||
throw *ex;
|
||||
}
|
||||
if (!_active)
|
||||
throw(spdlog_ex("async logger is not active"));
|
||||
}
|
||||
|
||||
|
||||
inline void spdlog::details::async_log_helper::join_worker()
|
||||
{
|
||||
_active = false;
|
||||
|
||||
try
|
||||
{
|
||||
_worker_thread.join();
|
||||
}
|
||||
catch (const std::system_error&) //Dont crash if thread not joinable
|
||||
{}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -25,8 +25,7 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
#include <memory>
|
||||
#include "../sinks/async_sink.h"
|
||||
#include "./async_log_helper.h"
|
||||
|
||||
//
|
||||
// Async Logger implementation
|
||||
@ -35,38 +34,29 @@
|
||||
|
||||
|
||||
template<class It>
|
||||
inline spdlog::async_logger::async_logger(const std::string& logger_name, const It& begin, const It& end, size_t queue_size, const log_clock::duration& shutdown_duration) :
|
||||
inline spdlog::async_logger::async_logger(const std::string& logger_name, const It& begin, const It& end, size_t queue_size) :
|
||||
logger(logger_name, begin, end),
|
||||
_shutdown_duration(shutdown_duration),
|
||||
_as(std::unique_ptr<sinks::async_sink>(new sinks::async_sink(queue_size)))
|
||||
_async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size))
|
||||
{
|
||||
_as->set_formatter(_formatter);
|
||||
for (auto &s : _sinks)
|
||||
_as->add_sink(s);
|
||||
}
|
||||
|
||||
inline spdlog::async_logger::async_logger(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const log_clock::duration& shutdown_duration) :
|
||||
async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, shutdown_duration) {}
|
||||
inline spdlog::async_logger::async_logger(const std::string& logger_name, sinks_init_list sinks, size_t queue_size) :
|
||||
async_logger(logger_name, sinks.begin(), sinks.end(), queue_size) {}
|
||||
|
||||
inline spdlog::async_logger::async_logger(const std::string& logger_name, sink_ptr single_sink, size_t queue_size, const log_clock::duration& shutdown_duration) :
|
||||
async_logger(logger_name, { single_sink }, queue_size, shutdown_duration) {}
|
||||
inline spdlog::async_logger::async_logger(const std::string& logger_name, sink_ptr single_sink, size_t queue_size) :
|
||||
async_logger(logger_name, { single_sink }, queue_size) {}
|
||||
|
||||
|
||||
inline void spdlog::async_logger::_log_msg(details::log_msg& msg)
|
||||
{
|
||||
_as->log(msg);
|
||||
}
|
||||
|
||||
inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)
|
||||
{
|
||||
_formatter = msg_formatter;
|
||||
_as->set_formatter(_formatter);
|
||||
_async_log_helper->set_formatter(_formatter);
|
||||
}
|
||||
|
||||
inline void spdlog::async_logger::_set_pattern(const std::string& pattern)
|
||||
{
|
||||
_formatter = std::make_shared<pattern_formatter>(pattern);
|
||||
_as->set_formatter(_formatter);
|
||||
_async_log_helper->set_formatter(_formatter);
|
||||
}
|
||||
|
||||
|
||||
@ -74,5 +64,9 @@ inline void spdlog::async_logger::_set_pattern(const std::string& pattern)
|
||||
inline void spdlog::async_logger::_stop()
|
||||
{
|
||||
set_level(level::OFF);
|
||||
_as->shutdown(_shutdown_duration);
|
||||
}
|
||||
|
||||
inline void spdlog::async_logger::_log_msg(details::log_msg& msg)
|
||||
{
|
||||
_async_log_helper->log(msg);
|
||||
}
|
||||
|
@ -1,155 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* spdlog - an extremely fast and easy to use c++11 logging library. */
|
||||
/* Copyright (c) 2014 Gabi Melman. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
// A blocking multi-consumer/multi-producer thread safe queue.
|
||||
// Has max capacity and supports timeout on push or pop operations.
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <mutex>
|
||||
#include <condition_variable>
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class blocking_queue
|
||||
{
|
||||
public:
|
||||
using queue_type = std::queue<T>;
|
||||
using item_type = T;
|
||||
using size_type = typename queue_type::size_type;
|
||||
using clock = std::chrono::system_clock;
|
||||
|
||||
explicit blocking_queue(size_type max_size) :
|
||||
_max_size(max_size),
|
||||
_q(),
|
||||
_mutex()
|
||||
{
|
||||
}
|
||||
blocking_queue(const blocking_queue&) = delete;
|
||||
blocking_queue& operator=(const blocking_queue&) = delete;
|
||||
~blocking_queue() = default;
|
||||
|
||||
size_type size()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
return _q.size();
|
||||
}
|
||||
|
||||
// Push copy of item into the back of the queue.
|
||||
// If the queue is full, block the calling thread util there is room or timeout have passed.
|
||||
// Return: false on timeout, true on successful push.
|
||||
template<typename Duration_Rep, typename Duration_Period, typename TT>
|
||||
bool push(TT&& item, const std::chrono::duration<Duration_Rep, Duration_Period>& timeout)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> ul(_mutex);
|
||||
if (is_full())
|
||||
{
|
||||
if (!_item_popped_cond.wait_until(ul, clock::now() + timeout, [this]()
|
||||
{
|
||||
return !this->is_full();
|
||||
}))
|
||||
return false;
|
||||
}
|
||||
_q.push(std::forward<TT>(item));
|
||||
}
|
||||
_item_pushed_cond.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Push copy of item into the back of the queue.
|
||||
// If the queue is full, block the calling thread until there is room.
|
||||
template<typename TT>
|
||||
void push(TT&& item)
|
||||
{
|
||||
while (!push(std::forward<TT>(item), std::chrono::seconds(60)));
|
||||
}
|
||||
|
||||
// Pop a copy of the front item in the queue into the given item ref.
|
||||
// If the queue is empty, block the calling thread util there is item to pop or timeout have passed.
|
||||
// Return: false on timeout , true on successful pop/
|
||||
template<class Duration_Rep, class Duration_Period>
|
||||
bool pop(T& item, const std::chrono::duration<Duration_Rep, Duration_Period>& timeout)
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> ul(_mutex);
|
||||
if (is_empty())
|
||||
{
|
||||
if (!_item_pushed_cond.wait_until(ul, clock::now() + timeout, [this]()
|
||||
{
|
||||
return !this->is_empty();
|
||||
}))
|
||||
return false;
|
||||
}
|
||||
item = std::move(_q.front());
|
||||
_q.pop();
|
||||
}
|
||||
_item_popped_cond.notify_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pop a copy of the front item in the queue into the given item ref.
|
||||
// If the queue is empty, block the calling thread util there is item to pop.
|
||||
void pop(T& item)
|
||||
{
|
||||
while (!pop(item, std::chrono::hours(1)));
|
||||
}
|
||||
|
||||
// Clear the queue
|
||||
void clear()
|
||||
{
|
||||
{
|
||||
std::unique_lock<std::mutex> ul(_mutex);
|
||||
queue_type().swap(_q);
|
||||
}
|
||||
_item_popped_cond.notify_all();
|
||||
}
|
||||
|
||||
private:
|
||||
size_type _max_size;
|
||||
std::queue<T> _q;
|
||||
std::mutex _mutex;
|
||||
std::condition_variable _item_pushed_cond;
|
||||
std::condition_variable _item_popped_cond;
|
||||
|
||||
inline bool is_full()
|
||||
{
|
||||
return _q.size() >= _max_size;
|
||||
}
|
||||
|
||||
inline bool is_empty()
|
||||
{
|
||||
return _q.size() == 0;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -1,130 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* spdlog - an extremely fast and easy to use c++11 logging library. */
|
||||
/* Copyright (c) 2014 Gabi Melman. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
//Fast to int to string
|
||||
//Base on :http://stackoverflow.com/a/4351484/192001
|
||||
//Modified version to pad zeros according to padding arg
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
|
||||
const char digit_pairs[201] =
|
||||
{
|
||||
"00010203040506070809"
|
||||
"10111213141516171819"
|
||||
"20212223242526272829"
|
||||
"30313233343536373839"
|
||||
"40414243444546474849"
|
||||
"50515253545556575859"
|
||||
"60616263646566676869"
|
||||
"70717273747576777879"
|
||||
"80818283848586878889"
|
||||
"90919293949596979899"
|
||||
};
|
||||
|
||||
|
||||
inline std::string& fast_itostr(int n, std::string& s, size_t padding)
|
||||
{
|
||||
if (n == 0)
|
||||
{
|
||||
s = std::string(padding, '0');
|
||||
return s;
|
||||
}
|
||||
|
||||
int sign = -(n < 0);
|
||||
unsigned int val = static_cast<unsigned int>((n^sign) - sign);
|
||||
|
||||
size_t size;
|
||||
if (val >= 10000)
|
||||
{
|
||||
if (val >= 10000000)
|
||||
{
|
||||
if (val >= 1000000000)
|
||||
size = 10;
|
||||
else if (val >= 100000000)
|
||||
size = 9;
|
||||
else
|
||||
size = 8;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (val >= 1000000)
|
||||
size = 7;
|
||||
else if (val >= 100000)
|
||||
size = 6;
|
||||
else
|
||||
size = 5;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (val >= 100)
|
||||
{
|
||||
if (val >= 1000)
|
||||
size = 4;
|
||||
else
|
||||
size = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (val >= 10)
|
||||
size = 2;
|
||||
else
|
||||
size = 1;
|
||||
}
|
||||
}
|
||||
size -= sign;
|
||||
if (size < padding)
|
||||
size = padding;
|
||||
|
||||
s.resize(size);
|
||||
char* c = &s[0];
|
||||
if (sign)
|
||||
*c = '-';
|
||||
|
||||
c += size - 1;
|
||||
while (val >= 100)
|
||||
{
|
||||
size_t pos = val % 100;
|
||||
val /= 100;
|
||||
*(short*)(c - 1) = *(short*)(digit_pairs + 2 * pos);
|
||||
c -= 2;
|
||||
}
|
||||
while (val > 0)
|
||||
{
|
||||
*c-- = static_cast<char>('0' + (val % 10));
|
||||
val /= 10;
|
||||
}
|
||||
|
||||
while (c >= s.data())
|
||||
*c-- = '0';
|
||||
return s;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,189 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* spdlog - an extremely fast and easy to use c++11 logging library. */
|
||||
/* Copyright (c) 2014 Gabi Melman. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
// A faster-than-ostringstream class
|
||||
// uses stack_buf as the underlying buffer (upto 256 bytes before using the heap)
|
||||
|
||||
#include <ostream>
|
||||
#include <iomanip>
|
||||
#include "fast_istostr.h"
|
||||
#include "stack_buf.h"
|
||||
#include<iostream>
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
|
||||
class stack_devicebuf :public std::streambuf
|
||||
{
|
||||
public:
|
||||
static const unsigned short stack_size = 256;
|
||||
using stackbuf_t = stack_buf<stack_size>;
|
||||
|
||||
stack_devicebuf() = default;
|
||||
~stack_devicebuf() = default;
|
||||
|
||||
stack_devicebuf(const stack_devicebuf& other) :std::basic_streambuf<char>(), _stackbuf(other._stackbuf)
|
||||
{}
|
||||
|
||||
stack_devicebuf(stack_devicebuf&& other):
|
||||
std::basic_streambuf<char>(),
|
||||
_stackbuf(std::move(other._stackbuf))
|
||||
{
|
||||
other.clear();
|
||||
}
|
||||
|
||||
stack_devicebuf& operator=(stack_devicebuf other)
|
||||
{
|
||||
std::swap(_stackbuf, other._stackbuf);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const stackbuf_t& buf() const
|
||||
{
|
||||
return _stackbuf;
|
||||
}
|
||||
std::size_t size() const
|
||||
{
|
||||
return _stackbuf.size();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
_stackbuf.clear();
|
||||
}
|
||||
|
||||
protected:
|
||||
// copy the give buffer into the accumulated fast buffer
|
||||
std::streamsize xsputn(const char_type* s, std::streamsize count) override
|
||||
{
|
||||
_stackbuf.append(s, static_cast<unsigned int>(count));
|
||||
return count;
|
||||
}
|
||||
|
||||
int_type overflow(int_type ch) override
|
||||
{
|
||||
if (traits_type::not_eof(ch))
|
||||
{
|
||||
char c = traits_type::to_char_type(ch);
|
||||
xsputn(&c, 1);
|
||||
}
|
||||
return ch;
|
||||
}
|
||||
private:
|
||||
stackbuf_t _stackbuf;
|
||||
};
|
||||
|
||||
|
||||
class fast_oss :public std::ostream
|
||||
{
|
||||
public:
|
||||
fast_oss() :std::ostream(&_dev) {}
|
||||
~fast_oss() = default;
|
||||
|
||||
fast_oss(const fast_oss& other) :std::basic_ios<char>(), std::ostream(&_dev), _dev(other._dev)
|
||||
{}
|
||||
|
||||
fast_oss(fast_oss&& other) :std::basic_ios<char>(), std::ostream(&_dev), _dev(std::move(other._dev))
|
||||
{
|
||||
other.clear();
|
||||
}
|
||||
|
||||
|
||||
fast_oss& operator=(fast_oss other)
|
||||
{
|
||||
swap(*this, other);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void swap(fast_oss& first, fast_oss& second) // nothrow
|
||||
{
|
||||
using std::swap;
|
||||
swap(first._dev, second._dev);
|
||||
}
|
||||
|
||||
std::string str() const
|
||||
{
|
||||
auto& buffer = _dev.buf();
|
||||
const char*data = buffer.data();
|
||||
return std::string(data, data+buffer.size());
|
||||
}
|
||||
|
||||
const stack_devicebuf::stackbuf_t& buf() const
|
||||
{
|
||||
return _dev.buf();
|
||||
}
|
||||
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
return _dev.size();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
_dev.clear();
|
||||
}
|
||||
|
||||
//
|
||||
// The following were added because they significantly boost to perfromance
|
||||
//
|
||||
void putc(char c)
|
||||
{
|
||||
_dev.sputc(c);
|
||||
}
|
||||
|
||||
// put int and pad with zeroes if smalled than min_width
|
||||
void put_int(int n, size_t padding)
|
||||
{
|
||||
std::string s;
|
||||
details::fast_itostr(n, s, padding);
|
||||
_dev.sputn(s.data(), s.size());
|
||||
}
|
||||
|
||||
void put_data(const char* p, std::size_t data_size)
|
||||
{
|
||||
_dev.sputn(p, data_size);
|
||||
}
|
||||
|
||||
void put_str(const std::string& s)
|
||||
{
|
||||
_dev.sputn(s.data(), s.size());
|
||||
}
|
||||
|
||||
void put_fast_oss(const fast_oss& oss)
|
||||
{
|
||||
auto& buffer = oss.buf();
|
||||
_dev.sputn(buffer.data(), buffer.size());
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
stack_devicebuf _dev;
|
||||
};
|
||||
}
|
||||
}
|
@ -52,7 +52,7 @@ public:
|
||||
explicit file_helper(bool auto_flush):
|
||||
_fd(nullptr),
|
||||
_auto_flush(auto_flush)
|
||||
{};
|
||||
{};
|
||||
|
||||
file_helper(const file_helper&) = delete;
|
||||
file_helper& operator=(const file_helper&) = delete;
|
||||
@ -99,14 +99,15 @@ public:
|
||||
|
||||
void write(const log_msg& msg)
|
||||
{
|
||||
auto& buf = msg.formatted.buf();
|
||||
size_t size = buf.size();
|
||||
if(std::fwrite(buf.data(), sizeof(char), size, _fd) != size)
|
||||
|
||||
size_t size = msg.formatted.size();
|
||||
auto data = msg.formatted.data();
|
||||
if(std::fwrite(data, 1, size, _fd) != size)
|
||||
throw spdlog_ex("Failed writing to file " + _filename);
|
||||
|
||||
if(_auto_flush)
|
||||
std::fflush(_fd);
|
||||
|
||||
|
||||
}
|
||||
|
||||
const std::string& filename() const
|
||||
|
1168
include/spdlog/details/format.cc
Normal file
1168
include/spdlog/details/format.cc
Normal file
File diff suppressed because it is too large
Load Diff
2882
include/spdlog/details/format.h
Normal file
2882
include/spdlog/details/format.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -26,7 +26,6 @@
|
||||
|
||||
#include "../common.h"
|
||||
#include "../logger.h"
|
||||
#include "fast_oss.h"
|
||||
|
||||
|
||||
// Line logger class - aggregates operator<< calls to fast ostream
|
||||
@ -66,27 +65,35 @@ public:
|
||||
{
|
||||
_log_msg.logger_name = _callback_logger->name();
|
||||
_log_msg.time = log_clock::now();
|
||||
_log_msg.tm_time = details::os::localtime(log_clock::to_time_t(_log_msg.time));
|
||||
_callback_logger->_log_msg(_log_msg);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void write(const T& what)
|
||||
|
||||
template <typename... Args>
|
||||
void write(const char* fmt, const Args&... args)
|
||||
{
|
||||
if (_enabled)
|
||||
if (!_enabled)
|
||||
return;
|
||||
try
|
||||
{
|
||||
_log_msg.raw << what;
|
||||
_log_msg.raw.write(fmt, args...);
|
||||
}
|
||||
catch (const fmt::FormatError& e)
|
||||
{
|
||||
throw spdlog_ex(fmt::format("formatting error while processing format string '{}': {}", fmt, e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
line_logger& operator<<(const T& what)
|
||||
{
|
||||
write(what);
|
||||
if (_enabled)
|
||||
_log_msg.raw << what;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
void disable()
|
||||
{
|
||||
_enabled = false;
|
||||
|
@ -25,7 +25,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "./fast_oss.h"
|
||||
#include "./format.h"
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
@ -38,25 +38,31 @@ struct log_msg
|
||||
logger_name(),
|
||||
level(l),
|
||||
time(),
|
||||
tm_time(),
|
||||
raw(),
|
||||
formatted() {}
|
||||
|
||||
log_msg(const log_msg& other):
|
||||
|
||||
log_msg(const log_msg& other) :
|
||||
logger_name(other.logger_name),
|
||||
level(other.level),
|
||||
time(other.time),
|
||||
tm_time(other.tm_time),
|
||||
raw(other.raw),
|
||||
formatted(other.formatted) {}
|
||||
time(other.time)
|
||||
{
|
||||
if (other.raw.size())
|
||||
raw << fmt::BasicStringRef<char>(other.raw.data(), other.raw.size());
|
||||
if (other.formatted.size())
|
||||
formatted << fmt::BasicStringRef<char>(other.formatted.data(), other.formatted.size());
|
||||
|
||||
}
|
||||
|
||||
log_msg(log_msg&& other) :
|
||||
logger_name(std::move(other.logger_name)),
|
||||
level(other.level),
|
||||
time(std::move(other.time)),
|
||||
tm_time(other.tm_time),
|
||||
raw(std::move(other.raw)),
|
||||
formatted(std::move(other.formatted)) {}
|
||||
formatted(std::move(other.formatted))
|
||||
{
|
||||
other.clear();
|
||||
}
|
||||
|
||||
log_msg& operator=(log_msg&& other)
|
||||
{
|
||||
@ -66,16 +72,15 @@ struct log_msg
|
||||
logger_name = std::move(other.logger_name);
|
||||
level = other.level;
|
||||
time = std::move(other.time);
|
||||
tm_time = other.tm_time;
|
||||
raw = std::move(other.raw);
|
||||
formatted = std::move(other.formatted);
|
||||
other.clear();
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void clear()
|
||||
{
|
||||
level = level::OFF;
|
||||
raw.clear();
|
||||
formatted.clear();
|
||||
}
|
||||
@ -83,9 +88,8 @@ struct log_msg
|
||||
std::string logger_name;
|
||||
level::level_enum level;
|
||||
log_clock::time_point time;
|
||||
std::tm tm_time;
|
||||
fast_oss raw;
|
||||
fast_oss formatted;
|
||||
fmt::MemoryWriter raw;
|
||||
fmt::MemoryWriter formatted;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -63,78 +63,146 @@ inline void spdlog::logger::set_pattern(const std::string& pattern)
|
||||
_set_pattern(pattern);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// cppformat API of the form logger.info("hello {} {}", "world", 1);
|
||||
//
|
||||
template <typename... Args>
|
||||
inline spdlog::details::line_logger spdlog::logger::log(level::level_enum lvl, const Args&... args)
|
||||
inline spdlog::details::line_logger spdlog::logger::_log(level::level_enum lvl, const char* fmt, const Args&... args)
|
||||
{
|
||||
bool msg_enabled = should_log(lvl);
|
||||
details::line_logger l(this, lvl, msg_enabled);
|
||||
if (msg_enabled)
|
||||
_variadic_log(l, args...);
|
||||
l.write(fmt, args...);
|
||||
return l;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline spdlog::details::line_logger spdlog::logger::log(const Args&... args)
|
||||
inline spdlog::details::line_logger spdlog::logger::log(const char* fmt, const Args&... args)
|
||||
{
|
||||
return log(level::ALWAYS, args...);
|
||||
return _log(level::ALWAYS, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline spdlog::details::line_logger spdlog::logger::trace(const Args&... args)
|
||||
inline spdlog::details::line_logger spdlog::logger::trace(const char* fmt, const Args&... args)
|
||||
{
|
||||
return log(level::TRACE, args...);
|
||||
return _log(level::TRACE, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline spdlog::details::line_logger spdlog::logger::debug(const Args&... args)
|
||||
inline spdlog::details::line_logger spdlog::logger::debug(const char* fmt, const Args&... args)
|
||||
{
|
||||
return log(level::DEBUG, args...);
|
||||
return _log(level::DEBUG, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline spdlog::details::line_logger spdlog::logger::info(const Args&... args)
|
||||
inline spdlog::details::line_logger spdlog::logger::info(const char* fmt, const Args&... args)
|
||||
{
|
||||
return log(level::INFO, args...);
|
||||
return _log(level::INFO, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline spdlog::details::line_logger spdlog::logger::notice(const Args&... args)
|
||||
inline spdlog::details::line_logger spdlog::logger::notice(const char* fmt, const Args&... args)
|
||||
{
|
||||
return log(level::NOTICE, args...);
|
||||
return _log(level::NOTICE, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline spdlog::details::line_logger spdlog::logger::warn(const Args&... args)
|
||||
inline spdlog::details::line_logger spdlog::logger::warn(const char* fmt, const Args&... args)
|
||||
{
|
||||
return log(level::WARN, args...);
|
||||
return _log(level::WARN, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline spdlog::details::line_logger spdlog::logger::error(const Args&... args)
|
||||
inline spdlog::details::line_logger spdlog::logger::error(const char* fmt, const Args&... args)
|
||||
{
|
||||
return log(level::ERR, args...);
|
||||
return _log(level::ERR, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline spdlog::details::line_logger spdlog::logger::critical(const Args&... args)
|
||||
inline spdlog::details::line_logger spdlog::logger::critical(const char* fmt, const Args&... args)
|
||||
{
|
||||
return log(level::CRITICAL, args...);
|
||||
return _log(level::CRITICAL, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline spdlog::details::line_logger spdlog::logger::alert(const Args&... args)
|
||||
inline spdlog::details::line_logger spdlog::logger::alert(const char* fmt, const Args&... args)
|
||||
{
|
||||
return log(level::ALERT, args...);
|
||||
return _log(level::ALERT, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline spdlog::details::line_logger spdlog::logger::emerg(const Args&... args)
|
||||
inline spdlog::details::line_logger spdlog::logger::emerg(const char* fmt, const Args&... args)
|
||||
{
|
||||
return log(level::EMERG, args...);
|
||||
return _log(level::EMERG, fmt, args...);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// //API to support logger.info() << ".." calls
|
||||
//
|
||||
|
||||
inline spdlog::details::line_logger spdlog::logger::_log(level::level_enum lvl)
|
||||
{
|
||||
bool msg_enabled = should_log(lvl);
|
||||
details::line_logger l(this, lvl, msg_enabled);
|
||||
return l;
|
||||
}
|
||||
|
||||
inline spdlog::details::line_logger spdlog::logger::log()
|
||||
{
|
||||
return _log(level::ALWAYS);
|
||||
}
|
||||
|
||||
inline spdlog::details::line_logger spdlog::logger::trace()
|
||||
{
|
||||
return _log(level::TRACE);
|
||||
}
|
||||
|
||||
|
||||
inline spdlog::details::line_logger spdlog::logger::debug()
|
||||
{
|
||||
return _log(level::DEBUG);
|
||||
}
|
||||
|
||||
inline spdlog::details::line_logger spdlog::logger::info()
|
||||
{
|
||||
return _log(level::INFO);
|
||||
}
|
||||
|
||||
inline spdlog::details::line_logger spdlog::logger::notice()
|
||||
{
|
||||
return _log(level::NOTICE);
|
||||
}
|
||||
|
||||
inline spdlog::details::line_logger spdlog::logger::warn()
|
||||
{
|
||||
return _log(level::WARN);
|
||||
}
|
||||
|
||||
inline spdlog::details::line_logger spdlog::logger::error()
|
||||
{
|
||||
return _log(level::ERR);
|
||||
}
|
||||
|
||||
inline spdlog::details::line_logger spdlog::logger::critical()
|
||||
{
|
||||
return _log(level::CRITICAL);
|
||||
}
|
||||
|
||||
inline spdlog::details::line_logger spdlog::logger::alert()
|
||||
{
|
||||
return _log(level::ALERT);
|
||||
}
|
||||
|
||||
inline spdlog::details::line_logger spdlog::logger::emerg()
|
||||
{
|
||||
return _log(level::EMERG);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//
|
||||
// name and level
|
||||
//
|
||||
inline const std::string& spdlog::logger::name() const
|
||||
{
|
||||
return _name;
|
||||
@ -160,7 +228,9 @@ inline void spdlog::logger::stop()
|
||||
_stop();
|
||||
}
|
||||
|
||||
/* protected virtual */
|
||||
//
|
||||
// protected virtual called at end of each user log call (if enabled) by the line_logger
|
||||
//
|
||||
inline void spdlog::logger::_log_msg(details::log_msg& msg)
|
||||
{
|
||||
_formatter->format(msg);
|
||||
@ -182,24 +252,7 @@ inline void spdlog::logger::_stop()
|
||||
set_level(level::OFF);
|
||||
}
|
||||
|
||||
/* private functions */
|
||||
inline void spdlog::logger::_variadic_log(spdlog::details::line_logger&) {}
|
||||
|
||||
template <typename Last>
|
||||
inline void spdlog::logger::_variadic_log(spdlog::details::line_logger& l, const Last& last)
|
||||
{
|
||||
l.write(last);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename First, typename... Rest>
|
||||
inline void spdlog::logger::_variadic_log(spdlog::details::line_logger& l, const First& first, const Rest&... rest)
|
||||
{
|
||||
l.write(first);
|
||||
l.write(' ');
|
||||
_variadic_log(l, rest...);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
175
include/spdlog/details/mpmc_bounded_q.h
Normal file
175
include/spdlog/details/mpmc_bounded_q.h
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
A modified version of Bounded MPMC queue by Dmitry Vyukov.
|
||||
|
||||
Original code from:
|
||||
http://www.1024cores.net/home/lock-free-algorithms/queues/bounded-mpmc-queue
|
||||
|
||||
licensed by Dmitry Vyukov under the terms below:
|
||||
|
||||
Simplified BSD license
|
||||
|
||||
Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved.
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
of conditions and the following disclaimer in the documentation and/or other materials
|
||||
provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
The views and conclusions contained in the software and documentation are those of the authors and
|
||||
should not be interpreted as representing official policies, either expressed or implied, of Dmitry Vyukov.
|
||||
*/
|
||||
|
||||
/*
|
||||
The code in its current form adds the license below:
|
||||
|
||||
spdlog - an extremely fast and easy to use c++11 logging library.
|
||||
Copyright (c) 2014 Gabi Melman.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include "../common.h"
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class mpmc_bounded_queue
|
||||
{
|
||||
public:
|
||||
|
||||
using item_type = T;
|
||||
mpmc_bounded_queue(size_t buffer_size)
|
||||
: buffer_(new cell_t [buffer_size]),
|
||||
buffer_mask_(buffer_size - 1)
|
||||
{
|
||||
//queue size must be power of two
|
||||
if(!((buffer_size >= 2) && ((buffer_size & (buffer_size - 1)) == 0)))
|
||||
throw spdlog_ex("async logger queue size must be power of two");
|
||||
|
||||
for (size_t i = 0; i != buffer_size; i += 1)
|
||||
buffer_[i].sequence_.store(i, std::memory_order_relaxed);
|
||||
enqueue_pos_.store(0, std::memory_order_relaxed);
|
||||
dequeue_pos_.store(0, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
~mpmc_bounded_queue()
|
||||
{
|
||||
delete [] buffer_;
|
||||
}
|
||||
|
||||
|
||||
bool enqueue(T&& data)
|
||||
{
|
||||
cell_t* cell;
|
||||
size_t pos = enqueue_pos_.load(std::memory_order_relaxed);
|
||||
for (;;)
|
||||
{
|
||||
cell = &buffer_[pos & buffer_mask_];
|
||||
size_t seq = cell->sequence_.load(std::memory_order_acquire);
|
||||
intptr_t dif = (intptr_t)seq - (intptr_t)pos;
|
||||
if (dif == 0)
|
||||
{
|
||||
if (enqueue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
|
||||
break;
|
||||
}
|
||||
else if (dif < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = enqueue_pos_.load(std::memory_order_relaxed);
|
||||
}
|
||||
}
|
||||
cell->data_ = std::move(data);
|
||||
cell->sequence_.store(pos + 1, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool dequeue(T& data)
|
||||
{
|
||||
cell_t* cell;
|
||||
size_t pos = dequeue_pos_.load(std::memory_order_relaxed);
|
||||
for (;;)
|
||||
{
|
||||
cell = &buffer_[pos & buffer_mask_];
|
||||
size_t seq =
|
||||
cell->sequence_.load(std::memory_order_acquire);
|
||||
intptr_t dif = (intptr_t)seq - (intptr_t)(pos + 1);
|
||||
if (dif == 0)
|
||||
{
|
||||
if (dequeue_pos_.compare_exchange_weak(pos, pos + 1, std::memory_order_relaxed))
|
||||
break;
|
||||
}
|
||||
else if (dif < 0)
|
||||
return false;
|
||||
else
|
||||
pos = dequeue_pos_.load(std::memory_order_relaxed);
|
||||
}
|
||||
data = std::move(cell->data_);
|
||||
cell->sequence_.store(pos + buffer_mask_ + 1, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
struct cell_t
|
||||
{
|
||||
std::atomic<size_t> sequence_;
|
||||
T data_;
|
||||
};
|
||||
|
||||
static size_t const cacheline_size = 64;
|
||||
typedef char cacheline_pad_t [cacheline_size];
|
||||
|
||||
cacheline_pad_t pad0_;
|
||||
cell_t* const buffer_;
|
||||
size_t const buffer_mask_;
|
||||
cacheline_pad_t pad1_;
|
||||
std::atomic<size_t> enqueue_pos_;
|
||||
cacheline_pad_t pad2_;
|
||||
std::atomic<size_t> dequeue_pos_;
|
||||
cacheline_pad_t pad3_;
|
||||
|
||||
mpmc_bounded_queue(mpmc_bounded_queue const&);
|
||||
void operator = (mpmc_bounded_queue const&);
|
||||
};
|
||||
|
||||
} // ns details
|
||||
} // ns spdlog
|
@ -30,9 +30,9 @@
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
|
||||
|
||||
#include "../formatter.h"
|
||||
#include "./log_msg.h"
|
||||
#include "./fast_oss.h"
|
||||
#include "./os.h"
|
||||
|
||||
namespace spdlog
|
||||
@ -43,7 +43,7 @@ class flag_formatter
|
||||
{
|
||||
public:
|
||||
virtual ~flag_formatter() {}
|
||||
virtual void format(details::log_msg& msg) = 0;
|
||||
virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
@ -53,7 +53,7 @@ namespace
|
||||
{
|
||||
class name_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
msg.formatted << msg.logger_name;
|
||||
}
|
||||
@ -63,7 +63,7 @@ class name_formatter :public flag_formatter
|
||||
// log level appender
|
||||
class level_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
msg.formatted << level::to_str(msg.level);
|
||||
}
|
||||
@ -87,9 +87,9 @@ static int to12h(const tm& t)
|
||||
static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
|
||||
class a_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.put_str(days[msg.tm_time.tm_wday]);
|
||||
msg.formatted << days[tm_time.tm_wday];
|
||||
}
|
||||
};
|
||||
|
||||
@ -97,9 +97,9 @@ class a_formatter :public flag_formatter
|
||||
static const std::string full_days[] { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" };
|
||||
class A_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.put_str(full_days[msg.tm_time.tm_wday]);
|
||||
msg.formatted << full_days[tm_time.tm_wday];
|
||||
}
|
||||
};
|
||||
|
||||
@ -107,9 +107,9 @@ class A_formatter :public flag_formatter
|
||||
static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" };
|
||||
class b_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.put_str(months[msg.tm_time.tm_mon]);
|
||||
msg.formatted<< months[tm_time.tm_mon];
|
||||
}
|
||||
};
|
||||
|
||||
@ -117,28 +117,35 @@ class b_formatter :public flag_formatter
|
||||
static const std::string full_months[] { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
|
||||
class B_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.put_str(full_months[msg.tm_time.tm_mon]);
|
||||
msg.formatted << full_months[tm_time.tm_mon];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//write 2 ints seperated by sep with padding of 2
|
||||
static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, char sep)
|
||||
{
|
||||
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0');
|
||||
return w;
|
||||
}
|
||||
|
||||
//write 3 ints seperated by sep with padding of 2
|
||||
static fmt::MemoryWriter& pad_n_join(fmt::MemoryWriter& w, int v1, int v2, int v3, char sep)
|
||||
{
|
||||
w << fmt::pad(v1, 2, '0') << sep << fmt::pad(v2, 2, '0') << sep << fmt::pad(v3, 2, '0');
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
//Date and time representation (Thu Aug 23 15:35:46 2014)
|
||||
class c_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.put_str(days[msg.tm_time.tm_wday]);
|
||||
msg.formatted.putc(' ');
|
||||
msg.formatted.put_str(months[msg.tm_time.tm_mon]);
|
||||
msg.formatted.putc(' ');
|
||||
msg.formatted.put_int(msg.tm_time.tm_mday, 2);
|
||||
msg.formatted.putc(' ');
|
||||
msg.formatted.put_int(msg.tm_time.tm_hour, 2);
|
||||
msg.formatted.putc(':');
|
||||
msg.formatted.put_int(msg.tm_time.tm_min, 2);
|
||||
msg.formatted.putc(':');
|
||||
msg.formatted.put_int(msg.tm_time.tm_sec, 2);
|
||||
msg.formatted << days[tm_time.tm_wday] << ' ' << months[tm_time.tm_mon] << ' ' << tm_time.tm_mday << ' ';
|
||||
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << tm_time.tm_year + 1900;
|
||||
}
|
||||
};
|
||||
|
||||
@ -146,9 +153,9 @@ class c_formatter :public flag_formatter
|
||||
// year - 2 digit
|
||||
class C_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.put_int(msg.tm_time.tm_year % 100, 2);
|
||||
msg.formatted << fmt::pad(tm_time.tm_year % 100, 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
@ -157,13 +164,9 @@ class C_formatter :public flag_formatter
|
||||
// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
|
||||
class D_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.put_int(msg.tm_time.tm_mon + 1, 2);
|
||||
msg.formatted.putc('/');
|
||||
msg.formatted.put_int(msg.tm_time.tm_mday, 2);
|
||||
msg.formatted.putc('/');
|
||||
msg.formatted.put_int(msg.tm_time.tm_year % 100, 2);
|
||||
pad_n_join(msg.formatted, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_year % 100, '/');
|
||||
}
|
||||
};
|
||||
|
||||
@ -171,83 +174,83 @@ class D_formatter :public flag_formatter
|
||||
// year - 4 digit
|
||||
class Y_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.put_int(msg.tm_time.tm_year + 1900, 4);
|
||||
msg.formatted << tm_time.tm_year + 1900;
|
||||
}
|
||||
};
|
||||
|
||||
// month 1-12
|
||||
class m_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.put_int(msg.tm_time.tm_mon + 1, 2);
|
||||
msg.formatted << fmt::pad(tm_time.tm_mon + 1, 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// day of month 1-31
|
||||
class d_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.put_int(msg.tm_time.tm_mday, 2);
|
||||
msg.formatted << fmt::pad(tm_time.tm_mday, 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// hours in 24 format 0-23
|
||||
class H_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.put_int(msg.tm_time.tm_hour, 2);
|
||||
msg.formatted << fmt::pad(tm_time.tm_hour, 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// hours in 12 format 1-12
|
||||
class I_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.put_int(to12h(msg.tm_time), 2);
|
||||
msg.formatted << fmt::pad(to12h(tm_time), 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// ninutes 0-59
|
||||
class M_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.put_int(msg.tm_time.tm_min, 2);
|
||||
msg.formatted << fmt::pad(tm_time.tm_min, 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// seconds 0-59
|
||||
class S_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.put_int(msg.tm_time.tm_sec, 2);
|
||||
msg.formatted << fmt::pad(tm_time.tm_sec, 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// milliseconds
|
||||
class e_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
auto duration = msg.time.time_since_epoch();
|
||||
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
|
||||
msg.formatted.put_int(static_cast<int>(millis), 3);
|
||||
msg.formatted << fmt::pad(static_cast<int>(millis), 3, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// AM/PM
|
||||
class p_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.put_data(ampm(msg.tm_time), 2);
|
||||
msg.formatted << ampm(tm_time);
|
||||
}
|
||||
};
|
||||
|
||||
@ -255,100 +258,92 @@ class p_formatter :public flag_formatter
|
||||
// 12 hour clock 02:55:02 pm
|
||||
class r_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.put_int(to12h(msg.tm_time), 2);
|
||||
msg.formatted.putc(':');
|
||||
msg.formatted.put_int(msg.tm_time.tm_min, 2);
|
||||
msg.formatted.putc(':');
|
||||
msg.formatted.put_int(msg.tm_time.tm_sec, 2);
|
||||
msg.formatted.putc(' ');
|
||||
msg.formatted.put_data(ampm(msg.tm_time), 2);
|
||||
pad_n_join(msg.formatted, to12h(tm_time), tm_time.tm_min, tm_time.tm_sec, ':') << ' ' << ampm(tm_time);
|
||||
}
|
||||
};
|
||||
|
||||
// 24-hour HH:MM time, equivalent to %H:%M
|
||||
class R_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.put_int(msg.tm_time.tm_hour, 2);
|
||||
msg.formatted.putc(':');
|
||||
msg.formatted.put_int(msg.tm_time.tm_min, 2);
|
||||
|
||||
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, ':');
|
||||
}
|
||||
};
|
||||
|
||||
// ISO 8601 time format (HH:MM:SS), equivalent to %H:%M:%S
|
||||
class T_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.put_int(msg.tm_time.tm_hour, 2);
|
||||
msg.formatted.putc(':');
|
||||
msg.formatted.put_int(msg.tm_time.tm_min, 2);
|
||||
msg.formatted.putc(':');
|
||||
msg.formatted.put_int(msg.tm_time.tm_sec, 2);
|
||||
pad_n_join(msg.formatted, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec, ':');
|
||||
}
|
||||
};
|
||||
|
||||
// ISO 8601 offset from UTC in timezone (HH:MM)
|
||||
|
||||
// ISO 8601 offset from UTC in timezone (+-HH:MM)
|
||||
class z_formatter :public flag_formatter
|
||||
{
|
||||
public:
|
||||
z_formatter() {}
|
||||
const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
|
||||
|
||||
z_formatter() :_last_update(std::chrono::seconds(0)) {}
|
||||
z_formatter(const z_formatter&) = delete;
|
||||
z_formatter& operator=(const z_formatter&) = delete;
|
||||
|
||||
void format(log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
std::lock_guard<std::mutex> l(_mutex);
|
||||
using namespace std::chrono;
|
||||
auto diff = msg.time - _last_update;
|
||||
auto secs_diff = std::abs((duration_cast<seconds>(diff)).count());
|
||||
if (secs_diff >= 2)
|
||||
{
|
||||
_value = get_value(msg);
|
||||
_last_update = msg.time;
|
||||
}
|
||||
msg.formatted.put_str(_value);
|
||||
#ifdef _WIN32
|
||||
int total_minutes = get_cached_offset(msg, tm_time);
|
||||
#else
|
||||
// No need to chache under gcc,
|
||||
// it is very fast (already stored in tm.tm_gmtoff)
|
||||
int total_minutes = os::utc_minutes_offset(tm_time);
|
||||
#endif
|
||||
|
||||
int h = total_minutes / 60;
|
||||
int m = total_minutes % 60;
|
||||
char sign = h >= 0 ? '+' : '-';
|
||||
msg.formatted << sign;
|
||||
pad_n_join(msg.formatted, h, m, ':');
|
||||
}
|
||||
private:
|
||||
log_clock::time_point _last_update;
|
||||
std::string _value;
|
||||
int _offset_minutes;
|
||||
std::mutex _mutex;
|
||||
|
||||
std::string get_value(const log_msg& msg)
|
||||
int get_cached_offset(const log_msg& msg, const std::tm& tm_time)
|
||||
{
|
||||
int total_minutes = os::utc_minutes_offset(msg.tm_time);
|
||||
int h = total_minutes / 60;
|
||||
int m = total_minutes % 60;
|
||||
fast_oss oss;
|
||||
oss.putc(h < 0 ? '-' : '+');
|
||||
oss.put_int(h, 2);
|
||||
oss.putc(':');
|
||||
oss.put_int(m, 2);
|
||||
return oss.str();
|
||||
using namespace std::chrono;
|
||||
std::lock_guard<std::mutex> l(_mutex);
|
||||
if (msg.time - _last_update >= cache_refresh)
|
||||
{
|
||||
_offset_minutes = os::utc_minutes_offset(tm_time);
|
||||
_last_update = msg.time;
|
||||
}
|
||||
return _offset_minutes;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
//Thread id
|
||||
class t_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
msg.formatted << std::this_thread::get_id();
|
||||
msg.formatted << std::hash<std::thread::id>()(std::this_thread::get_id());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class v_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
msg.formatted.put_fast_oss(msg.raw);
|
||||
msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
|
||||
}
|
||||
};
|
||||
|
||||
@ -357,9 +352,9 @@ class ch_formatter :public flag_formatter
|
||||
public:
|
||||
explicit ch_formatter(char ch) : _ch(ch)
|
||||
{}
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
msg.formatted.putc(_ch);
|
||||
msg.formatted << _ch;
|
||||
}
|
||||
private:
|
||||
char _ch;
|
||||
@ -376,9 +371,9 @@ public:
|
||||
{
|
||||
_str += ch;
|
||||
}
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
msg.formatted.put_str(_str);
|
||||
msg.formatted << _str;
|
||||
}
|
||||
private:
|
||||
std::string _str;
|
||||
@ -388,29 +383,35 @@ private:
|
||||
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
|
||||
class full_formatter :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg) override
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted.putc('[');
|
||||
msg.formatted.put_int(msg.tm_time.tm_year+1900, 4);
|
||||
msg.formatted.putc('-');
|
||||
msg.formatted.put_int(msg.tm_time.tm_mon+ 1, 2);
|
||||
msg.formatted.putc('-');
|
||||
msg.formatted.put_int(msg.tm_time.tm_mday, 2);
|
||||
msg.formatted.putc(' ');
|
||||
msg.formatted.put_int(msg.tm_time.tm_hour, 2);
|
||||
msg.formatted.putc(':');
|
||||
msg.formatted.put_int(msg.tm_time.tm_min, 2);
|
||||
msg.formatted.putc(':');
|
||||
msg.formatted.put_int(msg.tm_time.tm_sec, 2);
|
||||
//millis
|
||||
msg.formatted.putc('.');
|
||||
auto duration = msg.time.time_since_epoch();
|
||||
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
|
||||
msg.formatted.put_int(static_cast<int>(millis), 3);
|
||||
msg.formatted.putc(']');
|
||||
msg.formatted << " [" << msg.logger_name << "] [" << level::to_str(msg.level) << "] ";
|
||||
msg.formatted.put_fast_oss(msg.raw);
|
||||
|
||||
/* Slower version(while still very fast - about 3.2 million lines/sec under 10 threads),
|
||||
msg.formatted.write("[{:d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}.{:03d}] [{}] [{}] {} ",
|
||||
tm_time.tm_year + 1900,
|
||||
tm_time.tm_mon + 1,
|
||||
tm_time.tm_mday,
|
||||
tm_time.tm_hour,
|
||||
tm_time.tm_min,
|
||||
tm_time.tm_sec,
|
||||
static_cast<int>(millis),
|
||||
msg.logger_name,
|
||||
level::to_str(msg.level),
|
||||
msg.raw.str());*/
|
||||
|
||||
// Faster (albeit uglier) way to format the line (5.6 million lines/sec under 10 threads)
|
||||
msg.formatted << '[' << static_cast<unsigned int>(tm_time.tm_year + 1900) << '-'
|
||||
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_mon + 1), 2, '0') << '-'
|
||||
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_mday), 2, '0') << ' '
|
||||
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_hour), 2, '0') << ':'
|
||||
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_min), 2, '0') << ':'
|
||||
<< fmt::pad(static_cast<unsigned int>(tm_time.tm_sec), 2, '0') << '.'
|
||||
<< fmt::pad(static_cast<unsigned int>(millis), 3, '0') << "] ";
|
||||
|
||||
msg.formatted << '[' << msg.logger_name << "] [" << level::to_str(msg.level) << "] ";
|
||||
msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
|
||||
}
|
||||
};
|
||||
|
||||
@ -457,7 +458,7 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
// logger name
|
||||
// logger name
|
||||
case 'n':
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::name_formatter()));
|
||||
break;
|
||||
@ -571,10 +572,18 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
|
||||
|
||||
inline void spdlog::pattern_formatter::format(details::log_msg& msg)
|
||||
{
|
||||
for (auto &f : _formatters)
|
||||
try
|
||||
{
|
||||
f->format(msg);
|
||||
auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time));
|
||||
for (auto &f : _formatters)
|
||||
{
|
||||
f->format(msg, tm_time);
|
||||
}
|
||||
//write eol
|
||||
msg.formatted << details::os::eol();
|
||||
}
|
||||
catch(const details::fmt::FormatError& e)
|
||||
{
|
||||
throw spdlog_ex(details::fmt::format("formatting error while processing format string: {}", e.what()));
|
||||
}
|
||||
//write eol
|
||||
msg.formatted.write(details::os::eol(), details::os::eol_size());
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ public:
|
||||
return found->second;
|
||||
std::shared_ptr<logger> new_logger;
|
||||
if (_async_mode)
|
||||
new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size, _async_shutdown_duration);
|
||||
new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, _async_q_size);
|
||||
else
|
||||
new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end);
|
||||
|
||||
@ -72,6 +72,11 @@ public:
|
||||
return new_logger;
|
||||
}
|
||||
|
||||
void drop(const std::string& logger_name)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
_loggers.erase(logger_name);
|
||||
}
|
||||
|
||||
std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks)
|
||||
{
|
||||
@ -109,12 +114,11 @@ public:
|
||||
l.second->set_level(log_level);
|
||||
}
|
||||
|
||||
void set_async_mode(size_t q_size, const log_clock::duration& shutdown_duration)
|
||||
void set_async_mode(size_t q_size)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
_async_mode = true;
|
||||
_async_q_size = q_size;
|
||||
_async_shutdown_duration = shutdown_duration;
|
||||
}
|
||||
|
||||
void set_sync_mode()
|
||||
@ -148,7 +152,6 @@ private:
|
||||
level::level_enum _level = level::INFO;
|
||||
bool _async_mode = false;
|
||||
size_t _async_q_size = 0;
|
||||
log_clock::duration _async_shutdown_duration;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,10 @@ inline std::shared_ptr<spdlog::logger> spdlog::get(const std::string& name)
|
||||
return details::registry::instance().get(name);
|
||||
}
|
||||
|
||||
inline void spdlog::drop(const std::string &name)
|
||||
{
|
||||
details::registry::instance().drop(name);
|
||||
}
|
||||
|
||||
// Create multi/single threaded rotating file logger
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const std::string& filename, size_t max_file_size, size_t max_files, bool auto_flush)
|
||||
@ -129,9 +133,9 @@ inline void spdlog::set_level(level::level_enum log_level)
|
||||
}
|
||||
|
||||
|
||||
inline void spdlog::set_async_mode(size_t queue_size, const log_clock::duration& shutdown_duration)
|
||||
inline void spdlog::set_async_mode(size_t queue_size)
|
||||
{
|
||||
details::registry::instance().set_async_mode(queue_size, shutdown_duration);
|
||||
details::registry::instance().set_async_mode(queue_size);
|
||||
}
|
||||
|
||||
inline void spdlog::set_sync_mode()
|
||||
|
@ -1,134 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* spdlog - an extremely fast and easy to use c++11 logging library. */
|
||||
/* Copyright (c) 2014 Gabi Melman. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <cstring>
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
|
||||
// Fast memory storage on the stack when possible or in std::vector
|
||||
template<unsigned short STACK_SIZE>
|
||||
class stack_buf
|
||||
{
|
||||
public:
|
||||
static const unsigned short stack_size = STACK_SIZE;
|
||||
stack_buf() :_v(), _stack_size(0) {}
|
||||
~stack_buf() = default;
|
||||
stack_buf(const stack_buf& other) :stack_buf(other, delegate_copy_or_move {})
|
||||
{}
|
||||
|
||||
stack_buf(stack_buf&& other) :stack_buf(other, delegate_copy_or_move {})
|
||||
{
|
||||
other.clear();
|
||||
}
|
||||
template<class T1>
|
||||
stack_buf& operator=(T1&& other)
|
||||
{
|
||||
_stack_size = other._stack_size;
|
||||
if (other.vector_used())
|
||||
_v = std::forward<T1>(other)._v;
|
||||
else
|
||||
std::copy_n(other._stack_array.begin(), other._stack_size, _stack_array.begin());
|
||||
return *this;
|
||||
}
|
||||
|
||||
void append(const char* buf, std::size_t buf_size)
|
||||
{
|
||||
//If we are aleady using _v, forget about the stack
|
||||
if (vector_used())
|
||||
{
|
||||
_v.insert(_v.end(), buf, buf + buf_size);
|
||||
}
|
||||
//Try use the stack
|
||||
else
|
||||
{
|
||||
if (_stack_size + buf_size <= STACK_SIZE)
|
||||
{
|
||||
std::memcpy(&_stack_array[_stack_size], buf, buf_size);
|
||||
_stack_size += buf_size;
|
||||
}
|
||||
//Not enough stack space. Copy all to _v
|
||||
else
|
||||
{
|
||||
_v.reserve(_stack_size + buf_size);
|
||||
_v.insert(_v.end(), _stack_array.begin(), _stack_array.begin() + _stack_size);
|
||||
_v.insert(_v.end(), buf, buf + buf_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void clear()
|
||||
{
|
||||
_stack_size = 0;
|
||||
_v.clear();
|
||||
}
|
||||
|
||||
const char* data() const
|
||||
{
|
||||
if (vector_used())
|
||||
return _v.data();
|
||||
else
|
||||
return _stack_array.data();
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
if (vector_used())
|
||||
return _v.size();
|
||||
else
|
||||
return _stack_size;
|
||||
}
|
||||
|
||||
private:
|
||||
struct delegate_copy_or_move {};
|
||||
template<class T1>
|
||||
stack_buf(T1&& other, delegate_copy_or_move)
|
||||
{
|
||||
_stack_size = other._stack_size;
|
||||
if (other.vector_used())
|
||||
_v = std::forward<T1>(other)._v;
|
||||
else
|
||||
std::copy_n(other._stack_array.begin(), other._stack_size, _stack_array.begin());
|
||||
}
|
||||
|
||||
inline bool vector_used() const
|
||||
{
|
||||
return !(_v.empty());
|
||||
}
|
||||
|
||||
std::vector<char> _v;
|
||||
std::array<char, STACK_SIZE> _stack_array;
|
||||
std::size_t _stack_size;
|
||||
};
|
||||
|
||||
}
|
||||
} //namespace spdlog { namespace details {
|
@ -34,7 +34,6 @@
|
||||
#include<vector>
|
||||
#include<memory>
|
||||
#include "sinks/base_sink.h"
|
||||
#include "sinks/async_sink.h"
|
||||
#include "common.h"
|
||||
|
||||
namespace spdlog
|
||||
@ -66,17 +65,29 @@ public:
|
||||
//Stop logging
|
||||
void stop();
|
||||
|
||||
template <typename... Args> details::line_logger log(level::level_enum lvl, const Args&... args);
|
||||
template <typename... Args> details::line_logger log(const Args&... args);
|
||||
template <typename... Args> details::line_logger trace(const Args&... args);
|
||||
template <typename... Args> details::line_logger debug(const Args&... args);
|
||||
template <typename... Args> details::line_logger info(const Args&... args);
|
||||
template <typename... Args> details::line_logger notice(const Args&... args);
|
||||
template <typename... Args> details::line_logger warn(const Args&... args);
|
||||
template <typename... Args> details::line_logger error(const Args&... args);
|
||||
template <typename... Args> details::line_logger critical(const Args&... args);
|
||||
template <typename... Args> details::line_logger alert(const Args&... args);
|
||||
template <typename... Args> details::line_logger emerg(const Args&... args);
|
||||
template <typename... Args> details::line_logger log(const char* fmt, const Args&... args);
|
||||
template <typename... Args> details::line_logger trace(const char* fmt, const Args&... args);
|
||||
template <typename... Args> details::line_logger debug(const char* fmt, const Args&... args);
|
||||
template <typename... Args> details::line_logger info(const char* fmt, const Args&... args);
|
||||
template <typename... Args> details::line_logger notice(const char* fmt, const Args&... args);
|
||||
template <typename... Args> details::line_logger warn(const char* fmt, const Args&... args);
|
||||
template <typename... Args> details::line_logger error(const char* fmt, const Args&... args);
|
||||
template <typename... Args> details::line_logger critical(const char* fmt, const Args&... args);
|
||||
template <typename... Args> details::line_logger alert(const char* fmt, const Args&... args);
|
||||
template <typename... Args> details::line_logger emerg(const char* fmt, const Args&... args);
|
||||
|
||||
//API to support logger.info() << ".." calls
|
||||
|
||||
details::line_logger log();
|
||||
details::line_logger trace();
|
||||
details::line_logger debug();
|
||||
details::line_logger info();
|
||||
details::line_logger notice();
|
||||
details::line_logger warn();
|
||||
details::line_logger error();
|
||||
details::line_logger critical();
|
||||
details::line_logger alert();
|
||||
details::line_logger emerg();
|
||||
|
||||
|
||||
void set_pattern(const std::string&);
|
||||
@ -84,10 +95,12 @@ public:
|
||||
|
||||
|
||||
protected:
|
||||
virtual void _log_msg(details::log_msg& msg);
|
||||
virtual void _log_msg(details::log_msg&);
|
||||
virtual void _set_pattern(const std::string&);
|
||||
virtual void _set_formatter(formatter_ptr);
|
||||
virtual void _stop();
|
||||
details::line_logger _log(level::level_enum lvl);
|
||||
template <typename... Args> details::line_logger _log(level::level_enum lvl, const char* fmt, const Args&... args);
|
||||
|
||||
|
||||
friend details::line_logger;
|
||||
@ -96,13 +109,6 @@ protected:
|
||||
formatter_ptr _formatter;
|
||||
std::atomic_int _level;
|
||||
|
||||
private:
|
||||
void _variadic_log(details::line_logger& l);
|
||||
template <typename Last>
|
||||
inline void _variadic_log(spdlog::details::line_logger& l, const Last& last);
|
||||
template <typename First, typename... Rest>
|
||||
void _variadic_log(details::line_logger&l, const First& first, const Rest&... rest);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,209 +0,0 @@
|
||||
/*************************************************************************/
|
||||
/* spdlog - an extremely fast and easy to use c++11 logging library. */
|
||||
/* Copyright (c) 2014 Gabi Melman. */
|
||||
/* */
|
||||
/* Permission is hereby granted, free of charge, to any person obtaining */
|
||||
/* a copy of this software and associated documentation files (the */
|
||||
/* "Software"), to deal in the Software without restriction, including */
|
||||
/* without limitation the rights to use, copy, modify, merge, publish, */
|
||||
/* distribute, sublicense, and/or sell copies of the Software, and to */
|
||||
/* permit persons to whom the Software is furnished to do so, subject to */
|
||||
/* the following conditions: */
|
||||
/* */
|
||||
/* The above copyright notice and this permission notice shall be */
|
||||
/* included in all copies or substantial portions of the Software. */
|
||||
/* */
|
||||
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
|
||||
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
|
||||
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
|
||||
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
|
||||
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
|
||||
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
|
||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||
/*************************************************************************/
|
||||
|
||||
// async sink:
|
||||
// Process logs asynchronously using a back thread.
|
||||
//
|
||||
// If the internal queue of log messages reaches its max size,
|
||||
// then the client call will block until there is more room.
|
||||
//
|
||||
// If the back thread throws during logging, a spdlog::spdlog_ex exception
|
||||
// will be thrown in client's thread when tries to log the next message
|
||||
#pragma once
|
||||
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
#include <atomic>
|
||||
|
||||
#include "./base_sink.h"
|
||||
#include "../logger.h"
|
||||
#include "../details/blocking_queue.h"
|
||||
#include "../details/null_mutex.h"
|
||||
#include "../details/log_msg.h"
|
||||
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace sinks
|
||||
{
|
||||
|
||||
|
||||
class async_sink : public base_sink < details::null_mutex > //single worker thread so null_mutex
|
||||
{
|
||||
public:
|
||||
using q_type = details::blocking_queue < std::unique_ptr<details::log_msg> > ;
|
||||
|
||||
explicit async_sink(const q_type::size_type max_queue_size);
|
||||
|
||||
//Stop logging and join the back thread
|
||||
~async_sink();
|
||||
void add_sink(sink_ptr sink);
|
||||
void remove_sink(sink_ptr sink_ptr);
|
||||
void set_formatter(formatter_ptr);
|
||||
//Wait to remaining items (if any) in the queue to be written and shutdown
|
||||
void shutdown(const log_clock::duration& timeout);
|
||||
|
||||
|
||||
|
||||
protected:
|
||||
void _sink_it(const details::log_msg& msg) override;
|
||||
void _thread_loop();
|
||||
|
||||
private:
|
||||
std::vector<std::shared_ptr<sink>> _sinks;
|
||||
std::atomic<bool> _active;
|
||||
q_type _q;
|
||||
std::thread _back_thread;
|
||||
std::mutex _mutex;
|
||||
//Last exception thrown from the back thread
|
||||
std::shared_ptr<spdlog_ex> _last_backthread_ex;
|
||||
|
||||
formatter_ptr _formatter;
|
||||
|
||||
//will throw last back thread exception or if backthread no active
|
||||
void _push_sentry();
|
||||
|
||||
//Clear all remaining messages(if any), stop the _back_thread and join it
|
||||
void _join();
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// async_sink class implementation
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
inline spdlog::sinks::async_sink::async_sink(const q_type::size_type max_queue_size)
|
||||
:_sinks(),
|
||||
_active(true),
|
||||
_q(max_queue_size),
|
||||
_back_thread(&async_sink::_thread_loop, this)
|
||||
{}
|
||||
|
||||
inline spdlog::sinks::async_sink::~async_sink()
|
||||
{
|
||||
_join();
|
||||
}
|
||||
|
||||
inline void spdlog::sinks::async_sink::_sink_it(const details::log_msg& msg)
|
||||
{
|
||||
_push_sentry();
|
||||
_q.push(std::unique_ptr<details::log_msg>(new details::log_msg(msg)));
|
||||
}
|
||||
|
||||
|
||||
inline void spdlog::sinks::async_sink::_thread_loop()
|
||||
{
|
||||
std::chrono::seconds pop_timeout { 1 };
|
||||
|
||||
while (_active)
|
||||
{
|
||||
q_type::item_type msg;
|
||||
if (_q.pop(msg, pop_timeout))
|
||||
{
|
||||
if (!_active)
|
||||
return;
|
||||
_formatter->format(*msg);
|
||||
for (auto &s : _sinks)
|
||||
{
|
||||
try
|
||||
{
|
||||
s->log(*msg);
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
_last_backthread_ex = std::make_shared<spdlog_ex>(ex.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
_last_backthread_ex = std::make_shared<spdlog_ex>("Unknown exception");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void spdlog::sinks::async_sink::add_sink(spdlog::sink_ptr s)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_mutex);
|
||||
_sinks.push_back(s);
|
||||
}
|
||||
|
||||
|
||||
inline void spdlog::sinks::async_sink::remove_sink(spdlog::sink_ptr s)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_mutex);
|
||||
_sinks.erase(std::remove(_sinks.begin(), _sinks.end(), s), _sinks.end());
|
||||
}
|
||||
|
||||
|
||||
inline void spdlog::sinks::async_sink::set_formatter(formatter_ptr msg_formatter)
|
||||
{
|
||||
_formatter = msg_formatter;
|
||||
}
|
||||
|
||||
|
||||
|
||||
inline void spdlog::sinks::async_sink::shutdown(const log_clock::duration& timeout)
|
||||
{
|
||||
if (timeout > std::chrono::milliseconds::zero())
|
||||
{
|
||||
auto until = log_clock::now() + timeout;
|
||||
while (_q.size() > 0 && log_clock::now() < until)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(5));
|
||||
}
|
||||
}
|
||||
_join();
|
||||
}
|
||||
|
||||
|
||||
inline void spdlog::sinks::async_sink::_push_sentry()
|
||||
{
|
||||
if (_last_backthread_ex)
|
||||
{
|
||||
auto ex = std::move(_last_backthread_ex);
|
||||
_last_backthread_ex.reset();
|
||||
throw *ex;
|
||||
}
|
||||
if (!_active)
|
||||
throw(spdlog_ex("async_sink not active"));
|
||||
}
|
||||
|
||||
|
||||
inline void spdlog::sinks::async_sink::_join()
|
||||
{
|
||||
_active = false;
|
||||
if (_back_thread.joinable())
|
||||
{
|
||||
try
|
||||
{
|
||||
_back_thread.join();
|
||||
}
|
||||
catch (const std::system_error&) //Dont crash if thread not joinable
|
||||
{}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -28,8 +28,7 @@
|
||||
#include "base_sink.h"
|
||||
#include "../details/null_mutex.h"
|
||||
#include "../details/file_helper.h"
|
||||
#include "../details/fast_oss.h"
|
||||
|
||||
#include "../details/format.h"
|
||||
|
||||
|
||||
namespace spdlog
|
||||
@ -100,12 +99,12 @@ protected:
|
||||
private:
|
||||
static std::string calc_filename(const std::string& filename, std::size_t index, const std::string& extension)
|
||||
{
|
||||
details::fast_oss oss;
|
||||
details::fmt::MemoryWriter w;
|
||||
if (index)
|
||||
oss << filename << "." << index << "." << extension;
|
||||
w.write("{}.{}.{}", filename, index, extension);
|
||||
else
|
||||
oss << filename << "." << extension;
|
||||
return oss.str();
|
||||
w.write("{}.{}", filename, extension);
|
||||
return w.str();
|
||||
}
|
||||
|
||||
|
||||
@ -197,11 +196,9 @@ private:
|
||||
static std::string calc_filename(const std::string& basename, const std::string& extension)
|
||||
{
|
||||
std::tm tm = spdlog::details::os::localtime();
|
||||
details::fast_oss oss;
|
||||
oss << basename << '.';
|
||||
oss << tm.tm_year + 1900 << '-' << std::setw(2) << std::setfill('0') << tm.tm_mon + 1 << '-' << tm.tm_mday;
|
||||
oss << '.' << extension;
|
||||
return oss.str();
|
||||
details::fmt::MemoryWriter w;
|
||||
w.write("{}.{:04d}-{:02d}-{:02d}.{}", basename, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, extension);
|
||||
return w.str();
|
||||
}
|
||||
|
||||
std::string _base_filename;
|
||||
|
@ -24,7 +24,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <ostream>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
|
||||
@ -47,8 +47,7 @@ public:
|
||||
protected:
|
||||
virtual void _sink_it(const details::log_msg& msg) override
|
||||
{
|
||||
auto& buf = msg.formatted.buf();
|
||||
_ostream.write(buf.data(), buf.size());
|
||||
_ostream.write(msg.formatted.data(), msg.formatted.size());
|
||||
}
|
||||
std::ostream& _ostream;
|
||||
};
|
||||
|
@ -42,6 +42,10 @@ namespace spdlog
|
||||
// logger.info() << "This is another message" << x << y << z;
|
||||
std::shared_ptr<logger> get(const std::string& name);
|
||||
|
||||
//
|
||||
// Drop the reference to this logger.
|
||||
//
|
||||
void drop(const std::string &name);
|
||||
|
||||
//
|
||||
// Set global formatting
|
||||
@ -61,9 +65,8 @@ void set_level(level::level_enum log_level);
|
||||
//
|
||||
|
||||
// Turn on async mode and set the queue size for each async_logger
|
||||
// shutdown_duration indicates max time to wait for the worker thread to log its messages before terminating.
|
||||
|
||||
void set_async_mode(size_t queue_size, const log_clock::duration& shutdown_duration = std::chrono::seconds(5));
|
||||
void set_async_mode(size_t queue_size);
|
||||
// Turn off async mode
|
||||
void set_sync_mode();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user