Squashed 'vendor/spdlog/' content from commit 47c17be
git-subtree-dir: vendor/spdlog git-subtree-split: 47c17be9a111ab20123e1bd8e6c6717aa2adfea2
This commit is contained in:
399
include/spdlog/details/async_log_helper.h
Normal file
399
include/spdlog/details/async_log_helper.h
Normal file
@@ -0,0 +1,399 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
// 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.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "spdlog/common.h"
|
||||
#include "spdlog/sinks/sink.h"
|
||||
#include "spdlog/details/mpmc_bounded_q.h"
|
||||
#include "spdlog/details/log_msg.h"
|
||||
#include "spdlog/details/os.h"
|
||||
#include "spdlog/formatter.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
|
||||
class async_log_helper
|
||||
{
|
||||
// Async msg to move to/from the queue
|
||||
// Movable only. should never be copied
|
||||
enum class async_msg_type
|
||||
{
|
||||
log,
|
||||
flush,
|
||||
terminate
|
||||
};
|
||||
struct async_msg
|
||||
{
|
||||
std::string logger_name;
|
||||
level::level_enum level;
|
||||
log_clock::time_point time;
|
||||
size_t thread_id;
|
||||
std::string txt;
|
||||
async_msg_type msg_type;
|
||||
size_t msg_id;
|
||||
|
||||
async_msg() = default;
|
||||
~async_msg() = default;
|
||||
|
||||
|
||||
async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
|
||||
logger_name(std::move(other.logger_name)),
|
||||
level(std::move(other.level)),
|
||||
time(std::move(other.time)),
|
||||
thread_id(other.thread_id),
|
||||
txt(std::move(other.txt)),
|
||||
msg_type(std::move(other.msg_type)),
|
||||
msg_id(other.msg_id)
|
||||
{}
|
||||
|
||||
async_msg(async_msg_type m_type):
|
||||
level(level::info),
|
||||
thread_id(0),
|
||||
msg_type(m_type),
|
||||
msg_id(0)
|
||||
{}
|
||||
|
||||
async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT
|
||||
{
|
||||
logger_name = std::move(other.logger_name);
|
||||
level = other.level;
|
||||
time = std::move(other.time);
|
||||
thread_id = other.thread_id;
|
||||
txt = std::move(other.txt);
|
||||
msg_type = other.msg_type;
|
||||
msg_id = other.msg_id;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// never copy or assign. should only be moved..
|
||||
async_msg(const async_msg&) = delete;
|
||||
async_msg& operator=(const async_msg& other) = delete;
|
||||
|
||||
// construct from log_msg
|
||||
async_msg(const details::log_msg& m):
|
||||
level(m.level),
|
||||
time(m.time),
|
||||
thread_id(m.thread_id),
|
||||
txt(m.raw.data(), m.raw.size()),
|
||||
msg_type(async_msg_type::log),
|
||||
msg_id(m.msg_id)
|
||||
{
|
||||
#ifndef SPDLOG_NO_NAME
|
||||
logger_name = *m.logger_name;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// copy into log_msg
|
||||
void fill_log_msg(log_msg &msg)
|
||||
{
|
||||
msg.logger_name = &logger_name;
|
||||
msg.level = level;
|
||||
msg.time = time;
|
||||
msg.thread_id = thread_id;
|
||||
msg.raw << txt;
|
||||
msg.msg_id = msg_id;
|
||||
}
|
||||
};
|
||||
|
||||
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,
|
||||
const log_err_handler err_handler,
|
||||
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
|
||||
const std::function<void()>& worker_warmup_cb = nullptr,
|
||||
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
|
||||
const std::function<void()>& worker_teardown_cb = nullptr);
|
||||
|
||||
void log(const details::log_msg& msg);
|
||||
|
||||
// stop logging and join the back thread
|
||||
~async_log_helper();
|
||||
|
||||
void set_formatter(formatter_ptr);
|
||||
|
||||
void flush(bool wait_for_q);
|
||||
|
||||
void set_error_handler(spdlog::log_err_handler err_handler);
|
||||
|
||||
private:
|
||||
formatter_ptr _formatter;
|
||||
std::vector<std::shared_ptr<sinks::sink>> _sinks;
|
||||
|
||||
// queue of messages to log
|
||||
q_type _q;
|
||||
|
||||
log_err_handler _err_handler;
|
||||
|
||||
bool _flush_requested;
|
||||
|
||||
bool _terminate_requested;
|
||||
|
||||
|
||||
// overflow policy
|
||||
const async_overflow_policy _overflow_policy;
|
||||
|
||||
// worker thread warmup callback - one can set thread priority, affinity, etc
|
||||
const std::function<void()> _worker_warmup_cb;
|
||||
|
||||
// auto periodic sink flush parameter
|
||||
const std::chrono::milliseconds _flush_interval_ms;
|
||||
|
||||
// worker thread teardown callback
|
||||
const std::function<void()> _worker_teardown_cb;
|
||||
|
||||
// worker thread
|
||||
std::thread _worker_thread;
|
||||
|
||||
void push_msg(async_msg&& new_msg);
|
||||
|
||||
// worker thread main loop
|
||||
void worker_loop();
|
||||
|
||||
// pop next message from the queue and process it. will set the last_pop to the pop time
|
||||
// return false if termination of the queue is required
|
||||
bool process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush);
|
||||
|
||||
void handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush);
|
||||
|
||||
// sleep,yield or return immediately using the time passed since last message as a hint
|
||||
static void sleep_or_yield(const spdlog::log_clock::time_point& now, const log_clock::time_point& last_op_time);
|
||||
|
||||
// wait until the queue is empty
|
||||
void wait_empty_q();
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// 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,
|
||||
log_err_handler err_handler,
|
||||
const async_overflow_policy overflow_policy,
|
||||
const std::function<void()>& worker_warmup_cb,
|
||||
const std::chrono::milliseconds& flush_interval_ms,
|
||||
const std::function<void()>& worker_teardown_cb):
|
||||
_formatter(formatter),
|
||||
_sinks(sinks),
|
||||
_q(queue_size),
|
||||
_err_handler(err_handler),
|
||||
_flush_requested(false),
|
||||
_terminate_requested(false),
|
||||
_overflow_policy(overflow_policy),
|
||||
_worker_warmup_cb(worker_warmup_cb),
|
||||
_flush_interval_ms(flush_interval_ms),
|
||||
_worker_teardown_cb(worker_teardown_cb),
|
||||
_worker_thread(&async_log_helper::worker_loop, this)
|
||||
{}
|
||||
|
||||
// Send to the worker thread termination message(level=off)
|
||||
// and wait for it to finish gracefully
|
||||
inline spdlog::details::async_log_helper::~async_log_helper()
|
||||
{
|
||||
try
|
||||
{
|
||||
push_msg(async_msg(async_msg_type::terminate));
|
||||
_worker_thread.join();
|
||||
}
|
||||
catch (...) // don't crash in destructor
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//Try to push and block until succeeded (if the policy is not to discard when the queue is full)
|
||||
inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
|
||||
{
|
||||
push_msg(async_msg(msg));
|
||||
}
|
||||
|
||||
inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_msg)
|
||||
{
|
||||
if (!_q.enqueue(std::move(new_msg)) && _overflow_policy != async_overflow_policy::discard_log_msg)
|
||||
{
|
||||
auto last_op_time = details::os::now();
|
||||
auto now = last_op_time;
|
||||
do
|
||||
{
|
||||
now = details::os::now();
|
||||
sleep_or_yield(now, last_op_time);
|
||||
}
|
||||
while (!_q.enqueue(std::move(new_msg)));
|
||||
}
|
||||
}
|
||||
|
||||
// optionally wait for the queue be empty and request flush from the sinks
|
||||
inline void spdlog::details::async_log_helper::flush(bool wait_for_q)
|
||||
{
|
||||
push_msg(async_msg(async_msg_type::flush));
|
||||
if (wait_for_q)
|
||||
wait_empty_q(); //return only make after the above flush message was processed
|
||||
}
|
||||
|
||||
inline void spdlog::details::async_log_helper::worker_loop()
|
||||
{
|
||||
if (_worker_warmup_cb) _worker_warmup_cb();
|
||||
auto last_pop = details::os::now();
|
||||
auto last_flush = last_pop;
|
||||
auto active = true;
|
||||
while (active)
|
||||
{
|
||||
try
|
||||
{
|
||||
active = process_next_msg(last_pop, last_flush);
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
_err_handler(ex.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
_err_handler("Unknown exception");
|
||||
}
|
||||
}
|
||||
if (_worker_teardown_cb) _worker_teardown_cb();
|
||||
|
||||
|
||||
}
|
||||
|
||||
// process next message in the queue
|
||||
// return true if this thread should still be active (while no terminate msg was received)
|
||||
inline bool spdlog::details::async_log_helper::process_next_msg(log_clock::time_point& last_pop, log_clock::time_point& last_flush)
|
||||
{
|
||||
async_msg incoming_async_msg;
|
||||
|
||||
if (_q.dequeue(incoming_async_msg))
|
||||
{
|
||||
last_pop = details::os::now();
|
||||
switch (incoming_async_msg.msg_type)
|
||||
{
|
||||
case async_msg_type::flush:
|
||||
_flush_requested = true;
|
||||
break;
|
||||
|
||||
case async_msg_type::terminate:
|
||||
_flush_requested = true;
|
||||
_terminate_requested = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
log_msg incoming_log_msg;
|
||||
incoming_async_msg.fill_log_msg(incoming_log_msg);
|
||||
_formatter->format(incoming_log_msg);
|
||||
for (auto &s : _sinks)
|
||||
{
|
||||
if (s->should_log(incoming_log_msg.level))
|
||||
{
|
||||
s->log(incoming_log_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle empty queue..
|
||||
// This is the only place where the queue can terminate or flush to avoid losing messages already in the queue
|
||||
else
|
||||
{
|
||||
auto now = details::os::now();
|
||||
handle_flush_interval(now, last_flush);
|
||||
sleep_or_yield(now, last_pop);
|
||||
return !_terminate_requested;
|
||||
}
|
||||
}
|
||||
|
||||
// flush all sinks if _flush_interval_ms has expired
|
||||
inline void spdlog::details::async_log_helper::handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush)
|
||||
{
|
||||
auto should_flush = _flush_requested || (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms);
|
||||
if (should_flush)
|
||||
{
|
||||
for (auto &s : _sinks)
|
||||
s->flush();
|
||||
now = last_flush = details::os::now();
|
||||
_flush_requested = false;
|
||||
}
|
||||
}
|
||||
|
||||
inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter)
|
||||
{
|
||||
_formatter = msg_formatter;
|
||||
}
|
||||
|
||||
|
||||
// spin, yield or sleep. use the time passed since last message as a hint
|
||||
inline void spdlog::details::async_log_helper::sleep_or_yield(const spdlog::log_clock::time_point& now, const spdlog::log_clock::time_point& last_op_time)
|
||||
{
|
||||
using namespace std::this_thread;
|
||||
using std::chrono::milliseconds;
|
||||
using std::chrono::microseconds;
|
||||
|
||||
auto time_since_op = now - last_op_time;
|
||||
|
||||
// spin upto 50 micros
|
||||
if (time_since_op <= microseconds(50))
|
||||
return;
|
||||
|
||||
// yield upto 150 micros
|
||||
if (time_since_op <= microseconds(100))
|
||||
return std::this_thread::yield();
|
||||
|
||||
// sleep for 20 ms upto 200 ms
|
||||
if (time_since_op <= milliseconds(200))
|
||||
return sleep_for(milliseconds(20));
|
||||
|
||||
// sleep for 200 ms
|
||||
return sleep_for(milliseconds(200));
|
||||
}
|
||||
|
||||
// wait for the queue to be empty
|
||||
inline void spdlog::details::async_log_helper::wait_empty_q()
|
||||
{
|
||||
auto last_op = details::os::now();
|
||||
while (_q.approx_size() > 0)
|
||||
{
|
||||
sleep_or_yield(details::os::now(), last_op);
|
||||
}
|
||||
}
|
||||
|
||||
inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler)
|
||||
{
|
||||
_err_handler = err_handler;
|
||||
}
|
||||
|
||||
|
||||
|
||||
105
include/spdlog/details/async_logger_impl.h
Normal file
105
include/spdlog/details/async_logger_impl.h
Normal file
@@ -0,0 +1,105 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
// Async Logger implementation
|
||||
// Use an async_sink (queue per logger) to perform the logging in a worker thread
|
||||
|
||||
#include "spdlog/details/async_log_helper.h"
|
||||
#include "spdlog/async_logger.h"
|
||||
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
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 async_overflow_policy overflow_policy,
|
||||
const std::function<void()>& worker_warmup_cb,
|
||||
const std::chrono::milliseconds& flush_interval_ms,
|
||||
const std::function<void()>& worker_teardown_cb) :
|
||||
logger(logger_name, begin, end),
|
||||
_async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, _err_handler, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb))
|
||||
{
|
||||
}
|
||||
|
||||
inline spdlog::async_logger::async_logger(const std::string& logger_name,
|
||||
sinks_init_list sinks_list,
|
||||
size_t queue_size,
|
||||
const async_overflow_policy overflow_policy,
|
||||
const std::function<void()>& worker_warmup_cb,
|
||||
const std::chrono::milliseconds& flush_interval_ms,
|
||||
const std::function<void()>& worker_teardown_cb) :
|
||||
async_logger(logger_name, sinks_list.begin(), sinks_list.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
|
||||
|
||||
inline spdlog::async_logger::async_logger(const std::string& logger_name,
|
||||
sink_ptr single_sink,
|
||||
size_t queue_size,
|
||||
const async_overflow_policy overflow_policy,
|
||||
const std::function<void()>& worker_warmup_cb,
|
||||
const std::chrono::milliseconds& flush_interval_ms,
|
||||
const std::function<void()>& worker_teardown_cb) :
|
||||
async_logger(logger_name,
|
||||
{
|
||||
single_sink
|
||||
}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
|
||||
|
||||
|
||||
inline void spdlog::async_logger::flush()
|
||||
{
|
||||
_async_log_helper->flush(true);
|
||||
}
|
||||
|
||||
// Error handler
|
||||
inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_handler)
|
||||
{
|
||||
_err_handler = err_handler;
|
||||
_async_log_helper->set_error_handler(err_handler);
|
||||
|
||||
}
|
||||
inline spdlog::log_err_handler spdlog::async_logger::error_handler()
|
||||
{
|
||||
return _err_handler;
|
||||
}
|
||||
|
||||
|
||||
inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)
|
||||
{
|
||||
_formatter = msg_formatter;
|
||||
_async_log_helper->set_formatter(_formatter);
|
||||
}
|
||||
|
||||
inline void spdlog::async_logger::_set_pattern(const std::string& pattern, pattern_time_type pattern_time)
|
||||
{
|
||||
_formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
|
||||
_async_log_helper->set_formatter(_formatter);
|
||||
}
|
||||
|
||||
|
||||
inline void spdlog::async_logger::_sink_it(details::log_msg& msg)
|
||||
{
|
||||
try
|
||||
{
|
||||
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
|
||||
_incr_msg_counter(msg);
|
||||
#endif
|
||||
_async_log_helper->log(msg);
|
||||
if (_should_flush_on(msg))
|
||||
_async_log_helper->flush(false); // do async flush
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
_err_handler(ex.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
_err_handler("Unknown exception");
|
||||
}
|
||||
}
|
||||
117
include/spdlog/details/file_helper.h
Normal file
117
include/spdlog/details/file_helper.h
Normal file
@@ -0,0 +1,117 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
// Helper class for file sink
|
||||
// When failing to open a file, retry several times(5) with small delay between the tries(10 ms)
|
||||
// Throw spdlog_ex exception on errors
|
||||
|
||||
#include "spdlog/details/os.h"
|
||||
#include "spdlog/details/log_msg.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <cerrno>
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
|
||||
class file_helper
|
||||
{
|
||||
|
||||
public:
|
||||
const int open_tries = 5;
|
||||
const int open_interval = 10;
|
||||
|
||||
explicit file_helper() :
|
||||
_fd(nullptr)
|
||||
{}
|
||||
|
||||
file_helper(const file_helper&) = delete;
|
||||
file_helper& operator=(const file_helper&) = delete;
|
||||
|
||||
~file_helper()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
|
||||
void open(const filename_t& fname, bool truncate = false)
|
||||
{
|
||||
|
||||
close();
|
||||
auto *mode = truncate ? SPDLOG_FILENAME_T("wb") : SPDLOG_FILENAME_T("ab");
|
||||
_filename = fname;
|
||||
for (int tries = 0; tries < open_tries; ++tries)
|
||||
{
|
||||
if (!os::fopen_s(&_fd, fname, mode))
|
||||
return;
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(open_interval));
|
||||
}
|
||||
|
||||
throw spdlog_ex("Failed opening file " + os::filename_to_str(_filename) + " for writing", errno);
|
||||
}
|
||||
|
||||
void reopen(bool truncate)
|
||||
{
|
||||
if (_filename.empty())
|
||||
throw spdlog_ex("Failed re opening file - was not opened before");
|
||||
open(_filename, truncate);
|
||||
|
||||
}
|
||||
|
||||
void flush()
|
||||
{
|
||||
std::fflush(_fd);
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
if (_fd)
|
||||
{
|
||||
std::fclose(_fd);
|
||||
_fd = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void write(const log_msg& msg)
|
||||
{
|
||||
|
||||
size_t msg_size = msg.formatted.size();
|
||||
auto data = msg.formatted.data();
|
||||
if (std::fwrite(data, 1, msg_size, _fd) != msg_size)
|
||||
throw spdlog_ex("Failed writing to file " + os::filename_to_str(_filename), errno);
|
||||
}
|
||||
|
||||
size_t size()
|
||||
{
|
||||
if (!_fd)
|
||||
throw spdlog_ex("Cannot use size() on closed file " + os::filename_to_str(_filename));
|
||||
return os::filesize(_fd);
|
||||
}
|
||||
|
||||
const filename_t& filename() const
|
||||
{
|
||||
return _filename;
|
||||
}
|
||||
|
||||
static bool file_exists(const filename_t& name)
|
||||
{
|
||||
|
||||
return os::file_exists(name);
|
||||
}
|
||||
|
||||
private:
|
||||
FILE* _fd;
|
||||
filename_t _filename;
|
||||
};
|
||||
}
|
||||
}
|
||||
50
include/spdlog/details/log_msg.h
Normal file
50
include/spdlog/details/log_msg.h
Normal file
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "spdlog/common.h"
|
||||
#include "spdlog/details/os.h"
|
||||
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
struct log_msg
|
||||
{
|
||||
log_msg() = default;
|
||||
log_msg(const std::string *loggers_name, level::level_enum lvl) :
|
||||
logger_name(loggers_name),
|
||||
level(lvl),
|
||||
msg_id(0)
|
||||
{
|
||||
#ifndef SPDLOG_NO_DATETIME
|
||||
time = os::now();
|
||||
#endif
|
||||
|
||||
#ifndef SPDLOG_NO_THREAD_ID
|
||||
thread_id = os::thread_id();
|
||||
#endif
|
||||
}
|
||||
|
||||
log_msg(const log_msg& other) = delete;
|
||||
log_msg& operator=(log_msg&& other) = delete;
|
||||
log_msg(log_msg&& other) = delete;
|
||||
|
||||
|
||||
const std::string *logger_name;
|
||||
level::level_enum level;
|
||||
log_clock::time_point time;
|
||||
size_t thread_id;
|
||||
fmt::MemoryWriter raw;
|
||||
fmt::MemoryWriter formatted;
|
||||
size_t msg_id;
|
||||
};
|
||||
}
|
||||
}
|
||||
574
include/spdlog/details/logger_impl.h
Normal file
574
include/spdlog/details/logger_impl.h
Normal file
@@ -0,0 +1,574 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "spdlog/logger.h"
|
||||
#include "spdlog/sinks/stdout_sinks.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
// create logger with given name, sinks and the default pattern formatter
|
||||
// all other ctors will call this one
|
||||
template<class It>
|
||||
inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end):
|
||||
_name(logger_name),
|
||||
_sinks(begin, end),
|
||||
_formatter(std::make_shared<pattern_formatter>("%+")),
|
||||
_level(level::info),
|
||||
_flush_level(level::off),
|
||||
_last_err_time(0),
|
||||
_msg_counter(1) // message counter will start from 1. 0-message id will be reserved for controll messages
|
||||
{
|
||||
_err_handler = [this](const std::string &msg)
|
||||
{
|
||||
this->_default_err_handler(msg);
|
||||
};
|
||||
}
|
||||
|
||||
// ctor with sinks as init list
|
||||
inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list):
|
||||
logger(logger_name, sinks_list.begin(), sinks_list.end())
|
||||
{}
|
||||
|
||||
|
||||
// ctor with single sink
|
||||
inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink):
|
||||
logger(logger_name,
|
||||
{
|
||||
single_sink
|
||||
})
|
||||
{}
|
||||
|
||||
|
||||
inline spdlog::logger::~logger() = default;
|
||||
|
||||
|
||||
inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter)
|
||||
{
|
||||
_set_formatter(msg_formatter);
|
||||
}
|
||||
|
||||
inline void spdlog::logger::set_pattern(const std::string& pattern, pattern_time_type pattern_time)
|
||||
{
|
||||
_set_pattern(pattern, pattern_time);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args)
|
||||
{
|
||||
if (!should_log(lvl)) return;
|
||||
|
||||
try
|
||||
{
|
||||
details::log_msg log_msg(&_name, lvl);
|
||||
|
||||
#if defined(SPDLOG_FMT_PRINTF)
|
||||
fmt::printf(log_msg.raw, fmt, args...);
|
||||
#else
|
||||
log_msg.raw.write(fmt, args...);
|
||||
#endif
|
||||
_sink_it(log_msg);
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
_err_handler(ex.what());
|
||||
}
|
||||
SPDLOG_CATCH_ALL
|
||||
{
|
||||
_err_handler("Unknown exception");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::log(level::level_enum lvl, const char* msg)
|
||||
{
|
||||
if (!should_log(lvl)) return;
|
||||
try
|
||||
{
|
||||
details::log_msg log_msg(&_name, lvl);
|
||||
log_msg.raw << msg;
|
||||
_sink_it(log_msg);
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
_err_handler(ex.what());
|
||||
}
|
||||
SPDLOG_CATCH_ALL
|
||||
{
|
||||
_err_handler("Unknown exception");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void spdlog::logger::log(level::level_enum lvl, const T& msg)
|
||||
{
|
||||
if (!should_log(lvl)) return;
|
||||
try
|
||||
{
|
||||
details::log_msg log_msg(&_name, lvl);
|
||||
log_msg.raw << msg;
|
||||
_sink_it(log_msg);
|
||||
}
|
||||
catch (const std::exception &ex)
|
||||
{
|
||||
_err_handler(ex.what());
|
||||
}
|
||||
SPDLOG_CATCH_ALL
|
||||
{
|
||||
_err_handler("Unknown exception");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename Arg1, typename... Args>
|
||||
inline void spdlog::logger::trace(const char* fmt, const Arg1 &arg1, const Args&... args)
|
||||
{
|
||||
log(level::trace, fmt, arg1, args...);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename... Args>
|
||||
inline void spdlog::logger::debug(const char* fmt, const Arg1 &arg1, const Args&... args)
|
||||
{
|
||||
log(level::debug, fmt, arg1, args...);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename... Args>
|
||||
inline void spdlog::logger::info(const char* fmt, const Arg1 &arg1, const Args&... args)
|
||||
{
|
||||
log(level::info, fmt, arg1, args...);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename... Args>
|
||||
inline void spdlog::logger::warn(const char* fmt, const Arg1 &arg1, const Args&... args)
|
||||
{
|
||||
log(level::warn, fmt, arg1, args...);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename... Args>
|
||||
inline void spdlog::logger::error(const char* fmt, const Arg1 &arg1, const Args&... args)
|
||||
{
|
||||
log(level::err, fmt, arg1, args...);
|
||||
}
|
||||
|
||||
template <typename Arg1, typename... Args>
|
||||
inline void spdlog::logger::critical(const char* fmt, const Arg1 &arg1, const Args&... args)
|
||||
{
|
||||
log(level::critical, fmt, arg1, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const char* msg)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(lvl, msg);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const T& msg)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(lvl, msg);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Arg1, typename... Args>
|
||||
inline void spdlog::logger::trace_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(level::trace, fmt, arg1, args...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Arg1, typename... Args>
|
||||
inline void spdlog::logger::debug_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(level::debug, fmt, arg1, args...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Arg1, typename... Args>
|
||||
inline void spdlog::logger::info_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(level::info, fmt, arg1, args...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Arg1, typename... Args>
|
||||
inline void spdlog::logger::warn_if(const bool flag, const char* fmt, const Arg1& arg1, const Args&... args)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(level::warn, fmt, arg1, args...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Arg1, typename... Args>
|
||||
inline void spdlog::logger::error_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(level::err, fmt, arg1, args...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Arg1, typename... Args>
|
||||
inline void spdlog::logger::critical_if(const bool flag, const char* fmt, const Arg1 &arg1, const Args&... args)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(level::critical, fmt, arg1, args...);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline void spdlog::logger::trace(const T& msg)
|
||||
{
|
||||
log(level::trace, msg);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void spdlog::logger::debug(const T& msg)
|
||||
{
|
||||
log(level::debug, msg);
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline void spdlog::logger::info(const T& msg)
|
||||
{
|
||||
log(level::info, msg);
|
||||
}
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline void spdlog::logger::warn(const T& msg)
|
||||
{
|
||||
log(level::warn, msg);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void spdlog::logger::error(const T& msg)
|
||||
{
|
||||
log(level::err, msg);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void spdlog::logger::critical(const T& msg)
|
||||
{
|
||||
log(level::critical, msg);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void spdlog::logger::trace_if(const bool flag, const T& msg)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(level::trace, msg);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void spdlog::logger::debug_if(const bool flag, const T& msg)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(level::debug, msg);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void spdlog::logger::info_if(const bool flag, const T& msg)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(level::info, msg);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void spdlog::logger::warn_if(const bool flag, const T& msg)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(level::warn, msg);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void spdlog::logger::error_if(const bool flag, const T& msg)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(level::err, msg);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void spdlog::logger::critical_if(const bool flag, const T& msg)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(level::critical, msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifdef SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
#include <codecvt>
|
||||
#include <locale>
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* msg)
|
||||
{
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t> > conv;
|
||||
|
||||
log(lvl, conv.to_bytes(msg));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::log(level::level_enum lvl, const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
fmt::WMemoryWriter wWriter;
|
||||
|
||||
wWriter.write(fmt, args...);
|
||||
log(lvl, wWriter.c_str());
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::trace(const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
log(level::trace, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::debug(const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
log(level::debug, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::info(const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
log(level::info, fmt, args...);
|
||||
}
|
||||
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::warn(const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
log(level::warn, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::error(const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
log(level::err, fmt, args...);
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::critical(const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
log(level::critical, fmt, args...);
|
||||
}
|
||||
|
||||
//
|
||||
// conditional logging
|
||||
//
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const wchar_t* msg)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(lvl, msg);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::log_if(const bool flag, level::level_enum lvl, const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(lvl, fmt, args...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::trace_if(const bool flag, const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(level::trace, fmt, args...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::debug_if(const bool flag, const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(level::debug, fmt, args...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::info_if(const bool flag, const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(level::info, fmt, args...);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::warn_if(const bool flag, const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(level::warn, fmt, args...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::error_if(const bool flag, const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(level::err, fmt, args...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
inline void spdlog::logger::critical_if(const bool flag, const wchar_t* fmt, const Args&... args)
|
||||
{
|
||||
if (flag)
|
||||
{
|
||||
log(level::critical, fmt, args...);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
|
||||
|
||||
|
||||
//
|
||||
// name and level
|
||||
//
|
||||
inline const std::string& spdlog::logger::name() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
inline void spdlog::logger::set_level(spdlog::level::level_enum log_level)
|
||||
{
|
||||
_level.store(log_level);
|
||||
}
|
||||
|
||||
inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler)
|
||||
{
|
||||
_err_handler = err_handler;
|
||||
}
|
||||
|
||||
inline spdlog::log_err_handler spdlog::logger::error_handler()
|
||||
{
|
||||
return _err_handler;
|
||||
}
|
||||
|
||||
|
||||
inline void spdlog::logger::flush_on(level::level_enum log_level)
|
||||
{
|
||||
_flush_level.store(log_level);
|
||||
}
|
||||
|
||||
inline spdlog::level::level_enum spdlog::logger::level() const
|
||||
{
|
||||
return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed));
|
||||
}
|
||||
|
||||
inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const
|
||||
{
|
||||
return msg_level >= _level.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
//
|
||||
// protected virtual called at end of each user log call (if enabled) by the line_logger
|
||||
//
|
||||
inline void spdlog::logger::_sink_it(details::log_msg& msg)
|
||||
{
|
||||
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
|
||||
_incr_msg_counter(msg);
|
||||
#endif
|
||||
_formatter->format(msg);
|
||||
for (auto &sink : _sinks)
|
||||
{
|
||||
if( sink->should_log( msg.level))
|
||||
{
|
||||
sink->log(msg);
|
||||
}
|
||||
}
|
||||
|
||||
if(_should_flush_on(msg))
|
||||
flush();
|
||||
}
|
||||
|
||||
inline void spdlog::logger::_set_pattern(const std::string& pattern, pattern_time_type pattern_time)
|
||||
{
|
||||
_formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
|
||||
}
|
||||
inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter)
|
||||
{
|
||||
_formatter = msg_formatter;
|
||||
}
|
||||
|
||||
inline void spdlog::logger::flush()
|
||||
{
|
||||
for (auto& sink : _sinks)
|
||||
sink->flush();
|
||||
}
|
||||
|
||||
inline void spdlog::logger::_default_err_handler(const std::string &msg)
|
||||
{
|
||||
auto now = time(nullptr);
|
||||
if (now - _last_err_time < 60)
|
||||
return;
|
||||
auto tm_time = details::os::localtime(now);
|
||||
char date_buf[100];
|
||||
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
|
||||
details::log_msg err_msg;
|
||||
err_msg.formatted.write("[*** LOG ERROR ***] [{}] [{}] [{}]{}", name(), msg, date_buf, details::os::eol);
|
||||
sinks::stderr_sink_mt::instance()->log(err_msg);
|
||||
_last_err_time = now;
|
||||
}
|
||||
|
||||
inline bool spdlog::logger::_should_flush_on(const details::log_msg &msg)
|
||||
{
|
||||
const auto flush_level = _flush_level.load(std::memory_order_relaxed);
|
||||
return (msg.level >= flush_level) && (msg.level != level::off);
|
||||
}
|
||||
|
||||
inline void spdlog::logger::_incr_msg_counter(details::log_msg &msg)
|
||||
{
|
||||
msg.msg_id = _msg_counter.fetch_add(1, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
inline const std::vector<spdlog::sink_ptr>& spdlog::logger::sinks() const
|
||||
{
|
||||
return _sinks;
|
||||
}
|
||||
|
||||
#undef SPDLOG_CATCH_ALL
|
||||
172
include/spdlog/details/mpmc_bounded_q.h
Normal file
172
include/spdlog/details/mpmc_bounded_q.h
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
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:
|
||||
|
||||
Copyright(c) 2015 Gabi Melman.
|
||||
Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "spdlog/common.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
class mpmc_bounded_queue
|
||||
{
|
||||
public:
|
||||
|
||||
using item_type = T;
|
||||
mpmc_bounded_queue(size_t buffer_size)
|
||||
:max_size_(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 = static_cast<intptr_t>(seq) - static_cast<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 = static_cast<intptr_t>(seq) - static_cast<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;
|
||||
}
|
||||
|
||||
size_t approx_size()
|
||||
{
|
||||
size_t first_pos = dequeue_pos_.load(std::memory_order_relaxed);
|
||||
size_t last_pos = enqueue_pos_.load(std::memory_order_relaxed);
|
||||
if (last_pos <= first_pos)
|
||||
return 0;
|
||||
auto size = last_pos - first_pos;
|
||||
return size < max_size_ ? size : max_size_;
|
||||
}
|
||||
|
||||
private:
|
||||
struct cell_t
|
||||
{
|
||||
std::atomic<size_t> sequence_;
|
||||
T data_;
|
||||
};
|
||||
|
||||
size_t const max_size_;
|
||||
|
||||
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&) = delete;
|
||||
void operator= (mpmc_bounded_queue const&) = delete;
|
||||
};
|
||||
|
||||
} // ns details
|
||||
} // ns spdlog
|
||||
45
include/spdlog/details/null_mutex.h
Normal file
45
include/spdlog/details/null_mutex.h
Normal file
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
// null, no cost dummy "mutex" and dummy "atomic" int
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
struct null_mutex
|
||||
{
|
||||
void lock() {}
|
||||
void unlock() {}
|
||||
bool try_lock()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct null_atomic_int
|
||||
{
|
||||
int value;
|
||||
null_atomic_int() = default;
|
||||
|
||||
null_atomic_int(int val):value(val)
|
||||
{}
|
||||
|
||||
int load(std::memory_order) const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
void store(int val)
|
||||
{
|
||||
value = val;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
469
include/spdlog/details/os.h
Normal file
469
include/spdlog/details/os.h
Normal file
@@ -0,0 +1,469 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
#pragma once
|
||||
|
||||
#include "spdlog/common.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <thread>
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX //prevent windows redefining min/max
|
||||
#endif
|
||||
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
#include <windows.h>
|
||||
#include <process.h> // _get_pid support
|
||||
#include <io.h> // _get_osfhandle and _isatty support
|
||||
|
||||
#ifdef __MINGW32__
|
||||
#include <share.h>
|
||||
#endif
|
||||
|
||||
#else // unix
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/syscall.h> //Use gettid() syscall under linux to get thread id
|
||||
|
||||
#elif __FreeBSD__
|
||||
#include <sys/thr.h> //Use thr_self() syscall under FreeBSD to get thread id
|
||||
#endif
|
||||
|
||||
#endif //unix
|
||||
|
||||
#ifndef __has_feature // Clang - feature checking macros.
|
||||
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
|
||||
#endif
|
||||
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
namespace os
|
||||
{
|
||||
|
||||
inline spdlog::log_clock::time_point now()
|
||||
{
|
||||
|
||||
#if defined __linux__ && defined SPDLOG_CLOCK_COARSE
|
||||
timespec ts;
|
||||
::clock_gettime(CLOCK_REALTIME_COARSE, &ts);
|
||||
return std::chrono::time_point<log_clock, typename log_clock::duration>(
|
||||
std::chrono::duration_cast<typename log_clock::duration>(
|
||||
std::chrono::seconds(ts.tv_sec) + std::chrono::nanoseconds(ts.tv_nsec)));
|
||||
|
||||
|
||||
#else
|
||||
return log_clock::now();
|
||||
#endif
|
||||
|
||||
}
|
||||
inline std::tm localtime(const std::time_t &time_tt)
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
std::tm tm;
|
||||
localtime_s(&tm, &time_tt);
|
||||
#else
|
||||
std::tm tm;
|
||||
localtime_r(&time_tt, &tm);
|
||||
#endif
|
||||
return tm;
|
||||
}
|
||||
|
||||
inline std::tm localtime()
|
||||
{
|
||||
std::time_t now_t = time(nullptr);
|
||||
return localtime(now_t);
|
||||
}
|
||||
|
||||
|
||||
inline std::tm gmtime(const std::time_t &time_tt)
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
std::tm tm;
|
||||
gmtime_s(&tm, &time_tt);
|
||||
#else
|
||||
std::tm tm;
|
||||
gmtime_r(&time_tt, &tm);
|
||||
#endif
|
||||
return tm;
|
||||
}
|
||||
|
||||
inline std::tm gmtime()
|
||||
{
|
||||
std::time_t now_t = time(nullptr);
|
||||
return gmtime(now_t);
|
||||
}
|
||||
inline bool operator==(const std::tm& tm1, const std::tm& tm2)
|
||||
{
|
||||
return (tm1.tm_sec == tm2.tm_sec &&
|
||||
tm1.tm_min == tm2.tm_min &&
|
||||
tm1.tm_hour == tm2.tm_hour &&
|
||||
tm1.tm_mday == tm2.tm_mday &&
|
||||
tm1.tm_mon == tm2.tm_mon &&
|
||||
tm1.tm_year == tm2.tm_year &&
|
||||
tm1.tm_isdst == tm2.tm_isdst);
|
||||
}
|
||||
|
||||
inline bool operator!=(const std::tm& tm1, const std::tm& tm2)
|
||||
{
|
||||
return !(tm1 == tm2);
|
||||
}
|
||||
|
||||
// eol definition
|
||||
#if !defined (SPDLOG_EOL)
|
||||
#ifdef _WIN32
|
||||
#define SPDLOG_EOL "\r\n"
|
||||
#else
|
||||
#define SPDLOG_EOL "\n"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
SPDLOG_CONSTEXPR static const char* eol = SPDLOG_EOL;
|
||||
SPDLOG_CONSTEXPR static int eol_size = sizeof(SPDLOG_EOL) - 1;
|
||||
|
||||
inline void prevent_child_fd(FILE *f)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
auto file_handle = (HANDLE)_get_osfhandle(_fileno(f));
|
||||
if (!::SetHandleInformation(file_handle, HANDLE_FLAG_INHERIT, 0))
|
||||
throw spdlog_ex("SetHandleInformation failed", errno);
|
||||
#else
|
||||
auto fd = fileno(f);
|
||||
if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1)
|
||||
throw spdlog_ex("fcntl with FD_CLOEXEC failed", errno);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//fopen_s on non windows for writing
|
||||
inline int fopen_s(FILE** fp, const filename_t& filename, const filename_t& mode)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||
*fp = _wfsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
|
||||
#else
|
||||
*fp = _fsopen((filename.c_str()), mode.c_str(), _SH_DENYWR);
|
||||
#endif
|
||||
#else //unix
|
||||
*fp = fopen((filename.c_str()), mode.c_str());
|
||||
#endif
|
||||
|
||||
#ifdef SPDLOG_PREVENT_CHILD_FD
|
||||
if (*fp != nullptr)
|
||||
prevent_child_fd(*fp);
|
||||
#endif
|
||||
return *fp == nullptr;
|
||||
}
|
||||
|
||||
|
||||
inline int remove(const filename_t &filename)
|
||||
{
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
return _wremove(filename.c_str());
|
||||
#else
|
||||
return std::remove(filename.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int rename(const filename_t& filename1, const filename_t& filename2)
|
||||
{
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
return _wrename(filename1.c_str(), filename2.c_str());
|
||||
#else
|
||||
return std::rename(filename1.c_str(), filename2.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
//Return if file exists
|
||||
inline bool file_exists(const filename_t& filename)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
#ifdef SPDLOG_WCHAR_FILENAMES
|
||||
auto attribs = GetFileAttributesW(filename.c_str());
|
||||
#else
|
||||
auto attribs = GetFileAttributesA(filename.c_str());
|
||||
#endif
|
||||
return (attribs != INVALID_FILE_ATTRIBUTES && !(attribs & FILE_ATTRIBUTE_DIRECTORY));
|
||||
#else //common linux/unix all have the stat system call
|
||||
struct stat buffer;
|
||||
return (stat(filename.c_str(), &buffer) == 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//Return file size according to open FILE* object
|
||||
inline size_t filesize(FILE *f)
|
||||
{
|
||||
if (f == nullptr)
|
||||
throw spdlog_ex("Failed getting file size. fd is null");
|
||||
#ifdef _WIN32
|
||||
int fd = _fileno(f);
|
||||
#if _WIN64 //64 bits
|
||||
struct _stat64 st;
|
||||
if (_fstat64(fd, &st) == 0)
|
||||
return st.st_size;
|
||||
|
||||
#else //windows 32 bits
|
||||
long ret = _filelength(fd);
|
||||
if (ret >= 0)
|
||||
return static_cast<size_t>(ret);
|
||||
#endif
|
||||
|
||||
#else // unix
|
||||
int fd = fileno(f);
|
||||
//64 bits(but not in osx, where fstat64 is deprecated)
|
||||
#if !defined(__FreeBSD__) && !defined(__APPLE__) && (defined(__x86_64__) || defined(__ppc64__))
|
||||
struct stat64 st;
|
||||
if (fstat64(fd, &st) == 0)
|
||||
return static_cast<size_t>(st.st_size);
|
||||
#else // unix 32 bits or osx
|
||||
struct stat st;
|
||||
if (fstat(fd, &st) == 0)
|
||||
return static_cast<size_t>(st.st_size);
|
||||
#endif
|
||||
#endif
|
||||
throw spdlog_ex("Failed getting file size from fd", errno);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//Return utc offset in minutes or throw spdlog_ex on failure
|
||||
inline int utc_minutes_offset(const std::tm& tm = details::os::localtime())
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
#if _WIN32_WINNT < _WIN32_WINNT_WS08
|
||||
TIME_ZONE_INFORMATION tzinfo;
|
||||
auto rv = GetTimeZoneInformation(&tzinfo);
|
||||
#else
|
||||
DYNAMIC_TIME_ZONE_INFORMATION tzinfo;
|
||||
auto rv = GetDynamicTimeZoneInformation(&tzinfo);
|
||||
#endif
|
||||
if (rv == TIME_ZONE_ID_INVALID)
|
||||
throw spdlog::spdlog_ex("Failed getting timezone info. ", errno);
|
||||
|
||||
int offset = -tzinfo.Bias;
|
||||
if (tm.tm_isdst)
|
||||
offset -= tzinfo.DaylightBias;
|
||||
else
|
||||
offset -= tzinfo.StandardBias;
|
||||
return offset;
|
||||
#else
|
||||
|
||||
#if defined(sun) || defined(__sun)
|
||||
// 'tm_gmtoff' field is BSD extension and it's missing on SunOS/Solaris
|
||||
struct helper
|
||||
{
|
||||
static long int calculate_gmt_offset(const std::tm & localtm = details::os::localtime(), const std::tm & gmtm = details::os::gmtime())
|
||||
{
|
||||
int local_year = localtm.tm_year + (1900 - 1);
|
||||
int gmt_year = gmtm.tm_year + (1900 - 1);
|
||||
|
||||
long int days = (
|
||||
// difference in day of year
|
||||
localtm.tm_yday - gmtm.tm_yday
|
||||
|
||||
// + intervening leap days
|
||||
+ ((local_year >> 2) - (gmt_year >> 2))
|
||||
- (local_year / 100 - gmt_year / 100)
|
||||
+ ((local_year / 100 >> 2) - (gmt_year / 100 >> 2))
|
||||
|
||||
// + difference in years * 365 */
|
||||
+ (long int)(local_year - gmt_year) * 365
|
||||
);
|
||||
|
||||
long int hours = (24 * days) + (localtm.tm_hour - gmtm.tm_hour);
|
||||
long int mins = (60 * hours) + (localtm.tm_min - gmtm.tm_min);
|
||||
long int secs = (60 * mins) + (localtm.tm_sec - gmtm.tm_sec);
|
||||
|
||||
return secs;
|
||||
}
|
||||
};
|
||||
|
||||
long int offset_seconds = helper::calculate_gmt_offset(tm);
|
||||
#else
|
||||
long int offset_seconds = tm.tm_gmtoff;
|
||||
#endif
|
||||
|
||||
return static_cast<int>(offset_seconds / 60);
|
||||
#endif
|
||||
}
|
||||
|
||||
//Return current thread id as size_t
|
||||
//It exists because the std::this_thread::get_id() is much slower(espcially under VS 2013)
|
||||
inline size_t _thread_id()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return static_cast<size_t>(::GetCurrentThreadId());
|
||||
#elif __linux__
|
||||
# if defined(__ANDROID__) && defined(__ANDROID_API__) && (__ANDROID_API__ < 21)
|
||||
# define SYS_gettid __NR_gettid
|
||||
# endif
|
||||
return static_cast<size_t>(syscall(SYS_gettid));
|
||||
#elif __FreeBSD__
|
||||
long tid;
|
||||
thr_self(&tid);
|
||||
return static_cast<size_t>(tid);
|
||||
#elif __APPLE__
|
||||
uint64_t tid;
|
||||
pthread_threadid_np(nullptr, &tid);
|
||||
return static_cast<size_t>(tid);
|
||||
#else //Default to standard C++11 (other Unix)
|
||||
return static_cast<size_t>(std::hash<std::thread::id>()(std::this_thread::get_id()));
|
||||
#endif
|
||||
}
|
||||
|
||||
//Return current thread id as size_t (from thread local storage)
|
||||
inline size_t thread_id()
|
||||
{
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900) || defined(__clang__) && !__has_feature(cxx_thread_local)
|
||||
return _thread_id();
|
||||
#else
|
||||
static thread_local const size_t tid = _thread_id();
|
||||
return tid;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// wchar support for windows file names (SPDLOG_WCHAR_FILENAMES must be defined)
|
||||
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
|
||||
#define SPDLOG_FILENAME_T(s) L ## s
|
||||
inline std::string filename_to_str(const filename_t& filename)
|
||||
{
|
||||
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> c;
|
||||
return c.to_bytes(filename);
|
||||
}
|
||||
#else
|
||||
#define SPDLOG_FILENAME_T(s) s
|
||||
inline std::string filename_to_str(const filename_t& filename)
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline std::string errno_to_string(char[256], char* res)
|
||||
{
|
||||
return std::string(res);
|
||||
}
|
||||
|
||||
inline std::string errno_to_string(char buf[256], int res)
|
||||
{
|
||||
if (res == 0)
|
||||
{
|
||||
return std::string(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
return "Unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
// Return errno string (thread safe)
|
||||
inline std::string errno_str(int err_num)
|
||||
{
|
||||
char buf[256];
|
||||
SPDLOG_CONSTEXPR auto buf_size = sizeof(buf);
|
||||
|
||||
#ifdef _WIN32
|
||||
if (strerror_s(buf, buf_size, err_num) == 0)
|
||||
return std::string(buf);
|
||||
else
|
||||
return "Unknown error";
|
||||
|
||||
#elif defined(__FreeBSD__) || defined(__APPLE__) || defined(ANDROID) || defined(__SUNPRO_CC) || \
|
||||
((_POSIX_C_SOURCE >= 200112L) && ! defined(_GNU_SOURCE)) // posix version
|
||||
|
||||
if (strerror_r(err_num, buf, buf_size) == 0)
|
||||
return std::string(buf);
|
||||
else
|
||||
return "Unknown error";
|
||||
|
||||
#else // gnu version (might not use the given buf, so its retval pointer must be used)
|
||||
auto err = strerror_r(err_num, buf, buf_size); // let compiler choose type
|
||||
return errno_to_string(buf, err); // use overloading to select correct stringify function
|
||||
#endif
|
||||
}
|
||||
|
||||
inline int pid()
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
return ::_getpid();
|
||||
#else
|
||||
return static_cast<int>(::getpid());
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Detrmine if the terminal supports colors
|
||||
// Source: https://github.com/agauniyal/rang/
|
||||
inline bool is_color_terminal()
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return true;
|
||||
#else
|
||||
static constexpr const char* Terms[] =
|
||||
{
|
||||
"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm",
|
||||
"linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"
|
||||
};
|
||||
|
||||
const char *env_p = std::getenv("TERM");
|
||||
if (env_p == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
static const bool result = std::any_of(
|
||||
std::begin(Terms), std::end(Terms), [&](const char* term)
|
||||
{
|
||||
return std::strstr(env_p, term) != nullptr;
|
||||
});
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// Detrmine if the terminal attached
|
||||
// Source: https://github.com/agauniyal/rang/
|
||||
inline bool in_terminal(FILE* file)
|
||||
{
|
||||
|
||||
#ifdef _WIN32
|
||||
return _isatty(_fileno(file)) ? true : false;
|
||||
#else
|
||||
return isatty(fileno(file)) ? true : false;
|
||||
#endif
|
||||
}
|
||||
} //os
|
||||
} //details
|
||||
} //spdlog
|
||||
686
include/spdlog/details/pattern_formatter_impl.h
Normal file
686
include/spdlog/details/pattern_formatter_impl.h
Normal file
@@ -0,0 +1,686 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "spdlog/formatter.h"
|
||||
#include "spdlog/details/log_msg.h"
|
||||
#include "spdlog/details/os.h"
|
||||
#include "spdlog/fmt/fmt.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
class flag_formatter
|
||||
{
|
||||
public:
|
||||
virtual ~flag_formatter()
|
||||
{}
|
||||
virtual void format(details::log_msg& msg, const std::tm& tm_time) = 0;
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// name & level pattern appenders
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
namespace
|
||||
{
|
||||
class name_formatter:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
msg.formatted << *msg.logger_name;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// log level appender
|
||||
class level_formatter:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
msg.formatted << level::to_str(msg.level);
|
||||
}
|
||||
};
|
||||
|
||||
// short log level appender
|
||||
class short_level_formatter:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
msg.formatted << level::to_short_str(msg.level);
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// Date time pattern appenders
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const char* ampm(const tm& t)
|
||||
{
|
||||
return t.tm_hour >= 12 ? "PM" : "AM";
|
||||
}
|
||||
|
||||
static int to12h(const tm& t)
|
||||
{
|
||||
return t.tm_hour > 12 ? t.tm_hour - 12 : t.tm_hour;
|
||||
}
|
||||
|
||||
//Abbreviated weekday name
|
||||
static const std::string days[] { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
|
||||
class a_formatter:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted << days[tm_time.tm_wday];
|
||||
}
|
||||
};
|
||||
|
||||
//Full weekday name
|
||||
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, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted << full_days[tm_time.tm_wday];
|
||||
}
|
||||
};
|
||||
|
||||
//Abbreviated month
|
||||
static const std::string months[] { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sept", "Oct", "Nov", "Dec" };
|
||||
class b_formatter:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted << months[tm_time.tm_mon];
|
||||
}
|
||||
};
|
||||
|
||||
//Full month name
|
||||
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, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted << full_months[tm_time.tm_mon];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
//write 2 ints separated 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 separated 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 SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// year - 2 digit
|
||||
class C_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted << fmt::pad(tm_time.tm_year % 100, 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Short MM/DD/YY date, equivalent to %m/%d/%y 08/23/01
|
||||
class D_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
pad_n_join(msg.formatted, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_year % 100, '/');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// year - 4 digit
|
||||
class Y_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted << tm_time.tm_year + 1900;
|
||||
}
|
||||
};
|
||||
|
||||
// month 1-12
|
||||
class m_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted << fmt::pad(tm_time.tm_mon + 1, 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// day of month 1-31
|
||||
class d_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted << fmt::pad(tm_time.tm_mday, 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// hours in 24 format 0-23
|
||||
class H_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted << fmt::pad(tm_time.tm_hour, 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// hours in 12 format 1-12
|
||||
class I_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted << fmt::pad(to12h(tm_time), 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// minutes 0-59
|
||||
class M_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted << fmt::pad(tm_time.tm_min, 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// seconds 0-59
|
||||
class S_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted << fmt::pad(tm_time.tm_sec, 2, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// milliseconds
|
||||
class e_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
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 << fmt::pad(static_cast<int>(millis), 3, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// microseconds
|
||||
class f_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
auto duration = msg.time.time_since_epoch();
|
||||
auto micros = std::chrono::duration_cast<std::chrono::microseconds>(duration).count() % 1000000;
|
||||
msg.formatted << fmt::pad(static_cast<int>(micros), 6, '0');
|
||||
}
|
||||
};
|
||||
|
||||
// nanoseconds
|
||||
class F_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
auto duration = msg.time.time_since_epoch();
|
||||
auto ns = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count() % 1000000000;
|
||||
msg.formatted << fmt::pad(static_cast<int>(ns), 9, '0');
|
||||
}
|
||||
};
|
||||
|
||||
class E_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
auto duration = msg.time.time_since_epoch();
|
||||
auto seconds = std::chrono::duration_cast<std::chrono::seconds>(duration).count();
|
||||
msg.formatted << seconds;
|
||||
}
|
||||
};
|
||||
|
||||
// AM/PM
|
||||
class p_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
msg.formatted << ampm(tm_time);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 12 hour clock 02:55:02 pm
|
||||
class r_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
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 SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
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 SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
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)
|
||||
class z_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
public:
|
||||
const std::chrono::seconds cache_refresh = std::chrono::seconds(5);
|
||||
|
||||
z_formatter():_last_update(std::chrono::seconds(0)), _offset_minutes(0)
|
||||
{}
|
||||
z_formatter(const z_formatter&) = delete;
|
||||
z_formatter& operator=(const z_formatter&) = delete;
|
||||
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
#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
|
||||
bool is_negative = total_minutes < 0;
|
||||
char sign;
|
||||
if (is_negative)
|
||||
{
|
||||
total_minutes = -total_minutes;
|
||||
sign = '-';
|
||||
}
|
||||
else
|
||||
{
|
||||
sign = '+';
|
||||
}
|
||||
|
||||
int h = total_minutes / 60;
|
||||
int m = total_minutes % 60;
|
||||
msg.formatted << sign;
|
||||
pad_n_join(msg.formatted, h, m, ':');
|
||||
}
|
||||
private:
|
||||
log_clock::time_point _last_update;
|
||||
int _offset_minutes;
|
||||
std::mutex _mutex;
|
||||
|
||||
int get_cached_offset(const log_msg& msg, const std::tm& tm_time)
|
||||
{
|
||||
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 SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
msg.formatted << msg.thread_id;
|
||||
}
|
||||
};
|
||||
|
||||
// Current pid
|
||||
class pid_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
msg.formatted << details::os::pid();
|
||||
}
|
||||
};
|
||||
|
||||
// message counter formatter
|
||||
class i_formatter SPDLOG_FINAL :public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
msg.formatted << fmt::pad(msg.msg_id, 6, '0');
|
||||
}
|
||||
};
|
||||
|
||||
class v_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
|
||||
}
|
||||
};
|
||||
|
||||
class ch_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
public:
|
||||
explicit ch_formatter(char ch): _ch(ch)
|
||||
{}
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
msg.formatted << _ch;
|
||||
}
|
||||
private:
|
||||
char _ch;
|
||||
};
|
||||
|
||||
|
||||
//aggregate user chars to display as is
|
||||
class aggregate_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
public:
|
||||
aggregate_formatter()
|
||||
{}
|
||||
void add_ch(char ch)
|
||||
{
|
||||
_str += ch;
|
||||
}
|
||||
void format(details::log_msg& msg, const std::tm&) override
|
||||
{
|
||||
msg.formatted << _str;
|
||||
}
|
||||
private:
|
||||
std::string _str;
|
||||
};
|
||||
|
||||
// Full info formatter
|
||||
// pattern: [%Y-%m-%d %H:%M:%S.%e] [%n] [%l] %v
|
||||
class full_formatter SPDLOG_FINAL:public flag_formatter
|
||||
{
|
||||
void format(details::log_msg& msg, const std::tm& tm_time) override
|
||||
{
|
||||
#ifndef SPDLOG_NO_DATETIME
|
||||
auto duration = msg.time.time_since_epoch();
|
||||
auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count() % 1000;
|
||||
|
||||
/* 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') << "] ";
|
||||
|
||||
//no datetime needed
|
||||
#else
|
||||
(void)tm_time;
|
||||
#endif
|
||||
|
||||
#ifndef SPDLOG_NO_NAME
|
||||
msg.formatted << '[' << *msg.logger_name << "] ";
|
||||
#endif
|
||||
|
||||
msg.formatted << '[' << level::to_str(msg.level) << "] ";
|
||||
msg.formatted << fmt::StringRef(msg.raw.data(), msg.raw.size());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// pattern_formatter inline impl
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
inline spdlog::pattern_formatter::pattern_formatter(const std::string& pattern, pattern_time_type pattern_time)
|
||||
: _pattern_time(pattern_time)
|
||||
{
|
||||
compile_pattern(pattern);
|
||||
}
|
||||
|
||||
inline void spdlog::pattern_formatter::compile_pattern(const std::string& pattern)
|
||||
{
|
||||
auto end = pattern.end();
|
||||
std::unique_ptr<details::aggregate_formatter> user_chars;
|
||||
for (auto it = pattern.begin(); it != end; ++it)
|
||||
{
|
||||
if (*it == '%')
|
||||
{
|
||||
if (user_chars) //append user chars found so far
|
||||
_formatters.push_back(std::move(user_chars));
|
||||
|
||||
if (++it != end)
|
||||
handle_flag(*it);
|
||||
else
|
||||
break;
|
||||
}
|
||||
else // chars not following the % sign should be displayed as is
|
||||
{
|
||||
if (!user_chars)
|
||||
user_chars = std::unique_ptr<details::aggregate_formatter>(new details::aggregate_formatter());
|
||||
user_chars->add_ch(*it);
|
||||
}
|
||||
}
|
||||
if (user_chars) //append raw chars found so far
|
||||
{
|
||||
_formatters.push_back(std::move(user_chars));
|
||||
}
|
||||
|
||||
}
|
||||
inline void spdlog::pattern_formatter::handle_flag(char flag)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
// logger name
|
||||
case 'n':
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::name_formatter()));
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::level_formatter()));
|
||||
break;
|
||||
|
||||
case 'L':
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::short_level_formatter()));
|
||||
break;
|
||||
|
||||
case('t'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::t_formatter()));
|
||||
break;
|
||||
|
||||
case('v'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::v_formatter()));
|
||||
break;
|
||||
|
||||
case('a'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::a_formatter()));
|
||||
break;
|
||||
|
||||
case('A'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::A_formatter()));
|
||||
break;
|
||||
|
||||
case('b'):
|
||||
case('h'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::b_formatter()));
|
||||
break;
|
||||
|
||||
case('B'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::B_formatter()));
|
||||
break;
|
||||
case('c'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::c_formatter()));
|
||||
break;
|
||||
|
||||
case('C'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::C_formatter()));
|
||||
break;
|
||||
|
||||
case('Y'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::Y_formatter()));
|
||||
break;
|
||||
|
||||
case('D'):
|
||||
case('x'):
|
||||
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::D_formatter()));
|
||||
break;
|
||||
|
||||
case('m'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::m_formatter()));
|
||||
break;
|
||||
|
||||
case('d'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::d_formatter()));
|
||||
break;
|
||||
|
||||
case('H'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::H_formatter()));
|
||||
break;
|
||||
|
||||
case('I'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::I_formatter()));
|
||||
break;
|
||||
|
||||
case('M'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::M_formatter()));
|
||||
break;
|
||||
|
||||
case('S'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::S_formatter()));
|
||||
break;
|
||||
|
||||
case('e'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::e_formatter()));
|
||||
break;
|
||||
|
||||
case('f'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::f_formatter()));
|
||||
break;
|
||||
case('F'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::F_formatter()));
|
||||
break;
|
||||
|
||||
case('E'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::E_formatter()));
|
||||
break;
|
||||
|
||||
case('p'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::p_formatter()));
|
||||
break;
|
||||
|
||||
case('r'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::r_formatter()));
|
||||
break;
|
||||
|
||||
case('R'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::R_formatter()));
|
||||
break;
|
||||
|
||||
case('T'):
|
||||
case('X'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::T_formatter()));
|
||||
break;
|
||||
|
||||
case('z'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::z_formatter()));
|
||||
break;
|
||||
|
||||
case ('+'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::full_formatter()));
|
||||
break;
|
||||
|
||||
case ('P'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::pid_formatter()));
|
||||
break;
|
||||
|
||||
|
||||
case ('i'):
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::i_formatter()));
|
||||
break;
|
||||
|
||||
default: //Unknown flag appears as is
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter('%')));
|
||||
_formatters.push_back(std::unique_ptr<details::flag_formatter>(new details::ch_formatter(flag)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::tm spdlog::pattern_formatter::get_time(details::log_msg& msg)
|
||||
{
|
||||
if (_pattern_time == pattern_time_type::local)
|
||||
return details::os::localtime(log_clock::to_time_t(msg.time));
|
||||
else
|
||||
return details::os::gmtime(log_clock::to_time_t(msg.time));
|
||||
}
|
||||
|
||||
inline void spdlog::pattern_formatter::format(details::log_msg& msg)
|
||||
{
|
||||
|
||||
#ifndef SPDLOG_NO_DATETIME
|
||||
auto tm_time = get_time(msg);
|
||||
#else
|
||||
std::tm tm_time;
|
||||
#endif
|
||||
for (auto &f : _formatters)
|
||||
{
|
||||
f->format(msg, tm_time);
|
||||
}
|
||||
//write eol
|
||||
msg.formatted.write(details::os::eol, details::os::eol_size);
|
||||
}
|
||||
214
include/spdlog/details/registry.h
Normal file
214
include/spdlog/details/registry.h
Normal file
@@ -0,0 +1,214 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
// Loggers registy of unique name->logger pointer
|
||||
// An attempt to create a logger with an already existing name will be ignored
|
||||
// If user requests a non existing logger, nullptr will be returned
|
||||
// This class is thread safe
|
||||
|
||||
#include "spdlog/details/null_mutex.h"
|
||||
#include "spdlog/logger.h"
|
||||
#include "spdlog/async_logger.h"
|
||||
#include "spdlog/common.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace spdlog
|
||||
{
|
||||
namespace details
|
||||
{
|
||||
template <class Mutex> class registry_t
|
||||
{
|
||||
public:
|
||||
|
||||
void register_logger(std::shared_ptr<logger> logger)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
auto logger_name = logger->name();
|
||||
throw_if_exists(logger_name);
|
||||
_loggers[logger_name] = logger;
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<logger> get(const std::string& logger_name)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
auto found = _loggers.find(logger_name);
|
||||
return found == _loggers.end() ? nullptr : found->second;
|
||||
}
|
||||
|
||||
template<class It>
|
||||
std::shared_ptr<logger> create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
throw_if_exists(logger_name);
|
||||
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, _overflow_policy, _worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb);
|
||||
else
|
||||
new_logger = std::make_shared<logger>(logger_name, sinks_begin, sinks_end);
|
||||
|
||||
if (_formatter)
|
||||
new_logger->set_formatter(_formatter);
|
||||
|
||||
if (_err_handler)
|
||||
new_logger->set_error_handler(_err_handler);
|
||||
|
||||
new_logger->set_level(_level);
|
||||
|
||||
|
||||
//Add to registry
|
||||
_loggers[logger_name] = new_logger;
|
||||
return new_logger;
|
||||
}
|
||||
|
||||
template<class It>
|
||||
std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, const It& sinks_begin, const It& sinks_end)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
throw_if_exists(logger_name);
|
||||
auto new_logger = std::make_shared<async_logger>(logger_name, sinks_begin, sinks_end, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb);
|
||||
|
||||
if (_formatter)
|
||||
new_logger->set_formatter(_formatter);
|
||||
|
||||
if (_err_handler)
|
||||
new_logger->set_error_handler(_err_handler);
|
||||
|
||||
new_logger->set_level(_level);
|
||||
|
||||
//Add to registry
|
||||
_loggers[logger_name] = new_logger;
|
||||
return new_logger;
|
||||
}
|
||||
|
||||
void apply_all(std::function<void(std::shared_ptr<logger>)> fun)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
for (auto &l : _loggers)
|
||||
fun(l.second);
|
||||
}
|
||||
|
||||
void drop(const std::string& logger_name)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
_loggers.erase(logger_name);
|
||||
}
|
||||
|
||||
void drop_all()
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
_loggers.clear();
|
||||
}
|
||||
std::shared_ptr<logger> create(const std::string& logger_name, sinks_init_list sinks)
|
||||
{
|
||||
return create(logger_name, sinks.begin(), sinks.end());
|
||||
}
|
||||
|
||||
std::shared_ptr<logger> create(const std::string& logger_name, sink_ptr sink)
|
||||
{
|
||||
return create(logger_name, { sink });
|
||||
}
|
||||
|
||||
std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, sinks_init_list sinks)
|
||||
{
|
||||
return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks.begin(), sinks.end());
|
||||
}
|
||||
|
||||
std::shared_ptr<async_logger> create_async(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb, sink_ptr sink)
|
||||
{
|
||||
return create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, { sink });
|
||||
}
|
||||
|
||||
void formatter(formatter_ptr f)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
_formatter = f;
|
||||
for (auto& l : _loggers)
|
||||
l.second->set_formatter(_formatter);
|
||||
}
|
||||
|
||||
void set_pattern(const std::string& pattern)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
_formatter = std::make_shared<pattern_formatter>(pattern);
|
||||
for (auto& l : _loggers)
|
||||
l.second->set_formatter(_formatter);
|
||||
}
|
||||
|
||||
void set_level(level::level_enum log_level)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
for (auto& l : _loggers)
|
||||
l.second->set_level(log_level);
|
||||
_level = log_level;
|
||||
}
|
||||
|
||||
void set_error_handler(log_err_handler handler)
|
||||
{
|
||||
for (auto& l : _loggers)
|
||||
l.second->set_error_handler(handler);
|
||||
_err_handler = handler;
|
||||
}
|
||||
|
||||
void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
_async_mode = true;
|
||||
_async_q_size = q_size;
|
||||
_overflow_policy = overflow_policy;
|
||||
_worker_warmup_cb = worker_warmup_cb;
|
||||
_flush_interval_ms = flush_interval_ms;
|
||||
_worker_teardown_cb = worker_teardown_cb;
|
||||
}
|
||||
|
||||
void set_sync_mode()
|
||||
{
|
||||
std::lock_guard<Mutex> lock(_mutex);
|
||||
_async_mode = false;
|
||||
}
|
||||
|
||||
static registry_t<Mutex>& instance()
|
||||
{
|
||||
static registry_t<Mutex> s_instance;
|
||||
return s_instance;
|
||||
}
|
||||
|
||||
private:
|
||||
registry_t<Mutex>() {}
|
||||
registry_t<Mutex>(const registry_t<Mutex>&) = delete;
|
||||
registry_t<Mutex>& operator=(const registry_t<Mutex>&) = delete;
|
||||
|
||||
void throw_if_exists(const std::string &logger_name)
|
||||
{
|
||||
if (_loggers.find(logger_name) != _loggers.end())
|
||||
throw spdlog_ex("logger with name '" + logger_name + "' already exists");
|
||||
}
|
||||
Mutex _mutex;
|
||||
std::unordered_map <std::string, std::shared_ptr<logger>> _loggers;
|
||||
formatter_ptr _formatter;
|
||||
level::level_enum _level = level::info;
|
||||
log_err_handler _err_handler;
|
||||
bool _async_mode = false;
|
||||
size_t _async_q_size = 0;
|
||||
async_overflow_policy _overflow_policy = async_overflow_policy::block_retry;
|
||||
std::function<void()> _worker_warmup_cb = nullptr;
|
||||
std::chrono::milliseconds _flush_interval_ms;
|
||||
std::function<void()> _worker_teardown_cb = nullptr;
|
||||
};
|
||||
#ifdef SPDLOG_NO_REGISTRY_MUTEX
|
||||
typedef registry_t<spdlog::details::null_mutex> registry;
|
||||
#else
|
||||
typedef registry_t<std::mutex> registry;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
263
include/spdlog/details/spdlog_impl.h
Normal file
263
include/spdlog/details/spdlog_impl.h
Normal file
@@ -0,0 +1,263 @@
|
||||
//
|
||||
// Copyright(c) 2015 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// Global registry functions
|
||||
//
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "spdlog/details/registry.h"
|
||||
#include "spdlog/sinks/file_sinks.h"
|
||||
#include "spdlog/sinks/stdout_sinks.h"
|
||||
#ifdef SPDLOG_ENABLE_SYSLOG
|
||||
#include "spdlog/sinks/syslog_sink.h"
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#include "spdlog/sinks/wincolor_sink.h"
|
||||
#else
|
||||
#include "spdlog/sinks/ansicolor_sink.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include "spdlog/sinks/android_sink.h"
|
||||
#endif
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
inline void spdlog::register_logger(std::shared_ptr<logger> logger)
|
||||
{
|
||||
return details::registry::instance().register_logger(logger);
|
||||
}
|
||||
|
||||
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 simple file logger
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_mt(const std::string& logger_name, const filename_t& filename, bool truncate)
|
||||
{
|
||||
return create<spdlog::sinks::simple_file_sink_mt>(logger_name, filename, truncate);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::basic_logger_st(const std::string& logger_name, const filename_t& filename, bool truncate)
|
||||
{
|
||||
return create<spdlog::sinks::simple_file_sink_st>(logger_name, filename, truncate);
|
||||
}
|
||||
|
||||
// Create multi/single threaded rotating file logger
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_mt(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
|
||||
{
|
||||
return create<spdlog::sinks::rotating_file_sink_mt>(logger_name, filename, max_file_size, max_files);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::rotating_logger_st(const std::string& logger_name, const filename_t& filename, size_t max_file_size, size_t max_files)
|
||||
{
|
||||
return create<spdlog::sinks::rotating_file_sink_st>(logger_name, filename, max_file_size, max_files);
|
||||
}
|
||||
|
||||
// Create file logger which creates new file at midnight):
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_mt(const std::string& logger_name, const filename_t& filename, int hour, int minute)
|
||||
{
|
||||
return create<spdlog::sinks::daily_file_sink_mt>(logger_name, filename, hour, minute);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::daily_logger_st(const std::string& logger_name, const filename_t& filename, int hour, int minute)
|
||||
{
|
||||
return create<spdlog::sinks::daily_file_sink_st>(logger_name, filename, hour, minute);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// stdout/stderr loggers
|
||||
//
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_mt(const std::string& logger_name)
|
||||
{
|
||||
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_mt::instance());
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_logger_st(const std::string& logger_name)
|
||||
{
|
||||
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stdout_sink_st::instance());
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_mt(const std::string& logger_name)
|
||||
{
|
||||
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_mt::instance());
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_logger_st(const std::string& logger_name)
|
||||
{
|
||||
return spdlog::details::registry::instance().create(logger_name, spdlog::sinks::stderr_sink_st::instance());
|
||||
}
|
||||
|
||||
//
|
||||
// stdout/stderr color loggers
|
||||
//
|
||||
#ifdef _WIN32
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_st>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_mt>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::wincolor_stderr_sink_st>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
#else //ansi terminal colors
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt(const std::string& logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_mt>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stdout_color_st(const std::string& logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::ansicolor_stdout_sink_st>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt(const std::string& logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_mt>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::stderr_color_st(const std::string& logger_name)
|
||||
{
|
||||
auto sink = std::make_shared<spdlog::sinks::ansicolor_stderr_sink_st>();
|
||||
return spdlog::details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef SPDLOG_ENABLE_SYSLOG
|
||||
// Create syslog logger
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::syslog_logger(const std::string& logger_name, const std::string& syslog_ident, int syslog_option)
|
||||
{
|
||||
return create<spdlog::sinks::syslog_sink>(logger_name, syslog_ident, syslog_option);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef __ANDROID__
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::android_logger(const std::string& logger_name, const std::string& tag)
|
||||
{
|
||||
return create<spdlog::sinks::android_sink>(logger_name, tag);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Create and register a logger a single sink
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const spdlog::sink_ptr& sink)
|
||||
{
|
||||
return details::registry::instance().create(logger_name, sink);
|
||||
}
|
||||
|
||||
//Create logger with multiple sinks
|
||||
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, spdlog::sinks_init_list sinks)
|
||||
{
|
||||
return details::registry::instance().create(logger_name, sinks);
|
||||
}
|
||||
|
||||
|
||||
template <typename Sink, typename... Args>
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, Args... args)
|
||||
{
|
||||
sink_ptr sink = std::make_shared<Sink>(args...);
|
||||
return details::registry::instance().create(logger_name, { sink });
|
||||
}
|
||||
|
||||
|
||||
template<class It>
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::create(const std::string& logger_name, const It& sinks_begin, const It& sinks_end)
|
||||
{
|
||||
return details::registry::instance().create(logger_name, sinks_begin, sinks_end);
|
||||
}
|
||||
|
||||
// Create and register an async logger with a single sink
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, const sink_ptr& sink, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
|
||||
{
|
||||
return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sink);
|
||||
}
|
||||
|
||||
// Create and register an async logger with multiple sinks
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb )
|
||||
{
|
||||
return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks);
|
||||
}
|
||||
|
||||
template<class It>
|
||||
inline std::shared_ptr<spdlog::logger> spdlog::create_async(const std::string& logger_name, const It& sinks_begin, const It& sinks_end, size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
|
||||
{
|
||||
return details::registry::instance().create_async(logger_name, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb, sinks_begin, sinks_end);
|
||||
}
|
||||
|
||||
inline void spdlog::set_formatter(spdlog::formatter_ptr f)
|
||||
{
|
||||
details::registry::instance().formatter(f);
|
||||
}
|
||||
|
||||
inline void spdlog::set_pattern(const std::string& format_string)
|
||||
{
|
||||
return details::registry::instance().set_pattern(format_string);
|
||||
}
|
||||
|
||||
inline void spdlog::set_level(level::level_enum log_level)
|
||||
{
|
||||
return details::registry::instance().set_level(log_level);
|
||||
}
|
||||
|
||||
inline void spdlog::set_error_handler(log_err_handler handler)
|
||||
{
|
||||
return details::registry::instance().set_error_handler(handler);
|
||||
}
|
||||
|
||||
|
||||
inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function<void()>& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function<void()>& worker_teardown_cb)
|
||||
{
|
||||
details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb);
|
||||
}
|
||||
|
||||
inline void spdlog::set_sync_mode()
|
||||
{
|
||||
details::registry::instance().set_sync_mode();
|
||||
}
|
||||
|
||||
inline void spdlog::apply_all(std::function<void(std::shared_ptr<logger>)> fun)
|
||||
{
|
||||
details::registry::instance().apply_all(fun);
|
||||
}
|
||||
|
||||
inline void spdlog::drop_all()
|
||||
{
|
||||
details::registry::instance().drop_all();
|
||||
}
|
||||
Reference in New Issue
Block a user