Merge pull request #1 from gabime/master

Synch with gambie
This commit is contained in:
Rob Patro 2014-12-10 15:32:04 -05:00
commit e166fadeb9
35 changed files with 4992 additions and 1257 deletions

View File

@ -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");
// 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);
//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 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");
//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");
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;
}
```

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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");

View File

@ -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

View File

@ -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";
@ -91,12 +92,14 @@ int main(int argc, char* argv[])
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;
}

View File

@ -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!";
//

View File

@ -24,7 +24,6 @@
#pragma once
#include <iostream>
#include <sstream>
#include <iomanip>
#include <locale>

View File

@ -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)
// 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;
};
}

View 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
{}
}

View File

@ -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);
}

View File

@ -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;
}
};
}
}

View File

@ -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;
}
}
}

View File

@ -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;
};
}
}

View File

@ -99,9 +99,10 @@ 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)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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;

View File

@ -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) :
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;
};
}
}

View File

@ -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...);
}

View 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

View File

@ -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());
}
};
@ -571,10 +572,18 @@ inline void spdlog::pattern_formatter::handle_flag(char flag)
inline void spdlog::pattern_formatter::format(details::log_msg& msg)
{
try
{
auto tm_time = details::os::localtime(log_clock::to_time_t(msg.time));
for (auto &f : _formatters)
{
f->format(msg);
f->format(msg, tm_time);
}
//write eol
msg.formatted.write(details::os::eol(), details::os::eol_size());
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()));
}
}

View File

@ -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;
};
}
}

View File

@ -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()

View File

@ -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 {

View File

@ -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);
};
}

View File

@ -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
{}
}
}

View File

@ -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;

View File

@ -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;
};

View File

@ -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();