mirror of https://github.com/gabime/spdlog.git
merge
commit
04c116315a
@ -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
|
|
@ -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
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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
|
@ -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 {
|
|
@ -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
|
|
||||||
{}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue