mirror of
https://github.com/gabime/spdlog.git
synced 2024-12-25 10:01:33 +08:00
astyle
This commit is contained in:
parent
5a8cecdfb6
commit
0c276beaaf
@ -64,9 +64,9 @@ public:
|
|||||||
//Warning: this can potentialy last forever as we wait it to complete
|
//Warning: this can potentialy last forever as we wait it to complete
|
||||||
void flush() override;
|
void flush() override;
|
||||||
|
|
||||||
// Error handler
|
// Error handler
|
||||||
virtual void set_error_handler(log_err_handler) override;
|
virtual void set_error_handler(log_err_handler) override;
|
||||||
virtual log_err_handler error_handler() override;
|
virtual log_err_handler error_handler() override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _sink_it(details::log_msg& msg) override;
|
void _sink_it(details::log_msg& msg) override;
|
||||||
|
@ -32,244 +32,252 @@
|
|||||||
|
|
||||||
namespace spdlog
|
namespace spdlog
|
||||||
{
|
{
|
||||||
namespace details
|
namespace details
|
||||||
{
|
{
|
||||||
|
|
||||||
class async_log_helper
|
class async_log_helper
|
||||||
{
|
{
|
||||||
// Async msg to move to/from the queue
|
// Async msg to move to/from the queue
|
||||||
// Movable only. should never be copied
|
// Movable only. should never be copied
|
||||||
enum class async_msg_type
|
enum class async_msg_type
|
||||||
{
|
{
|
||||||
log,
|
log,
|
||||||
flush,
|
flush,
|
||||||
terminate
|
terminate
|
||||||
};
|
};
|
||||||
struct async_msg
|
struct async_msg
|
||||||
{
|
{
|
||||||
std::string logger_name;
|
std::string logger_name;
|
||||||
level::level_enum level;
|
level::level_enum level;
|
||||||
log_clock::time_point time;
|
log_clock::time_point time;
|
||||||
size_t thread_id;
|
size_t thread_id;
|
||||||
std::string txt;
|
std::string txt;
|
||||||
async_msg_type msg_type;
|
async_msg_type msg_type;
|
||||||
|
|
||||||
async_msg() = default;
|
async_msg() = default;
|
||||||
~async_msg() = default;
|
~async_msg() = default;
|
||||||
|
|
||||||
|
|
||||||
async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
|
async_msg(async_msg&& other) SPDLOG_NOEXCEPT:
|
||||||
logger_name(std::move(other.logger_name)),
|
logger_name(std::move(other.logger_name)),
|
||||||
level(std::move(other.level)),
|
level(std::move(other.level)),
|
||||||
time(std::move(other.time)),
|
time(std::move(other.time)),
|
||||||
txt(std::move(other.txt)),
|
txt(std::move(other.txt)),
|
||||||
msg_type(std::move(other.msg_type))
|
msg_type(std::move(other.msg_type))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
async_msg(async_msg_type m_type):msg_type(m_type)
|
async_msg(async_msg_type m_type):msg_type(m_type)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT
|
async_msg& operator=(async_msg&& other) SPDLOG_NOEXCEPT
|
||||||
{
|
{
|
||||||
logger_name = std::move(other.logger_name);
|
logger_name = std::move(other.logger_name);
|
||||||
level = other.level;
|
level = other.level;
|
||||||
time = std::move(other.time);
|
time = std::move(other.time);
|
||||||
thread_id = other.thread_id;
|
thread_id = other.thread_id;
|
||||||
txt = std::move(other.txt);
|
txt = std::move(other.txt);
|
||||||
msg_type = other.msg_type;
|
msg_type = other.msg_type;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
// never copy or assign. should only be moved..
|
// never copy or assign. should only be moved..
|
||||||
async_msg(const async_msg&) = delete;
|
async_msg(const async_msg&) = delete;
|
||||||
async_msg& operator=(const async_msg& other) = delete;
|
async_msg& operator=(const async_msg& other) = delete;
|
||||||
|
|
||||||
// construct from log_msg
|
// construct from log_msg
|
||||||
async_msg(const details::log_msg& m):
|
async_msg(const details::log_msg& m):
|
||||||
level(m.level),
|
level(m.level),
|
||||||
time(m.time),
|
time(m.time),
|
||||||
thread_id(m.thread_id),
|
thread_id(m.thread_id),
|
||||||
txt(m.raw.data(), m.raw.size()),
|
txt(m.raw.data(), m.raw.size()),
|
||||||
msg_type(async_msg_type::log)
|
msg_type(async_msg_type::log)
|
||||||
{
|
{
|
||||||
#ifndef SPDLOG_NO_NAME
|
#ifndef SPDLOG_NO_NAME
|
||||||
logger_name = *m.logger_name;
|
logger_name = *m.logger_name;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// copy into log_msg
|
// copy into log_msg
|
||||||
void fill_log_msg(log_msg &msg)
|
void fill_log_msg(log_msg &msg)
|
||||||
{
|
{
|
||||||
msg.logger_name = &logger_name;
|
msg.logger_name = &logger_name;
|
||||||
msg.level = level;
|
msg.level = level;
|
||||||
msg.time = time;
|
msg.time = time;
|
||||||
msg.thread_id = thread_id;
|
msg.thread_id = thread_id;
|
||||||
msg.raw << txt;
|
msg.raw << txt;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
using item_type = async_msg;
|
using item_type = async_msg;
|
||||||
using q_type = details::mpmc_bounded_queue<item_type>;
|
using q_type = details::mpmc_bounded_queue<item_type>;
|
||||||
|
|
||||||
using clock = std::chrono::steady_clock;
|
using clock = std::chrono::steady_clock;
|
||||||
|
|
||||||
|
|
||||||
async_log_helper(formatter_ptr formatter,
|
async_log_helper(formatter_ptr formatter,
|
||||||
const std::vector<sink_ptr>& sinks,
|
const std::vector<sink_ptr>& sinks,
|
||||||
size_t queue_size,
|
size_t queue_size,
|
||||||
const log_err_handler err_handler,
|
const log_err_handler err_handler,
|
||||||
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
|
const async_overflow_policy overflow_policy = async_overflow_policy::block_retry,
|
||||||
const std::function<void()>& worker_warmup_cb = nullptr,
|
const std::function<void()>& worker_warmup_cb = nullptr,
|
||||||
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
|
const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(),
|
||||||
const std::function<void()>& worker_teardown_cb = nullptr);
|
const std::function<void()>& worker_teardown_cb = nullptr);
|
||||||
|
|
||||||
void log(const details::log_msg& msg);
|
void log(const details::log_msg& msg);
|
||||||
|
|
||||||
// stop logging and join the back thread
|
// stop logging and join the back thread
|
||||||
~async_log_helper();
|
~async_log_helper();
|
||||||
|
|
||||||
void set_formatter(formatter_ptr);
|
void set_formatter(formatter_ptr);
|
||||||
|
|
||||||
void flush(bool wait_for_q);
|
void flush(bool wait_for_q);
|
||||||
|
|
||||||
void set_error_handler(spdlog::log_err_handler err_handler);
|
void set_error_handler(spdlog::log_err_handler err_handler);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
formatter_ptr _formatter;
|
formatter_ptr _formatter;
|
||||||
std::vector<std::shared_ptr<sinks::sink>> _sinks;
|
std::vector<std::shared_ptr<sinks::sink>> _sinks;
|
||||||
|
|
||||||
// queue of messages to log
|
// queue of messages to log
|
||||||
q_type _q;
|
q_type _q;
|
||||||
|
|
||||||
log_err_handler _err_handler;
|
log_err_handler _err_handler;
|
||||||
|
|
||||||
bool _flush_requested;
|
bool _flush_requested;
|
||||||
|
|
||||||
bool _terminate_requested;
|
bool _terminate_requested;
|
||||||
|
|
||||||
|
|
||||||
// overflow policy
|
// overflow policy
|
||||||
const async_overflow_policy _overflow_policy;
|
const async_overflow_policy _overflow_policy;
|
||||||
|
|
||||||
// worker thread warmup callback - one can set thread priority, affinity, etc
|
// worker thread warmup callback - one can set thread priority, affinity, etc
|
||||||
const std::function<void()> _worker_warmup_cb;
|
const std::function<void()> _worker_warmup_cb;
|
||||||
|
|
||||||
// auto periodic sink flush parameter
|
// auto periodic sink flush parameter
|
||||||
const std::chrono::milliseconds _flush_interval_ms;
|
const std::chrono::milliseconds _flush_interval_ms;
|
||||||
|
|
||||||
// worker thread teardown callback
|
// worker thread teardown callback
|
||||||
const std::function<void()> _worker_teardown_cb;
|
const std::function<void()> _worker_teardown_cb;
|
||||||
|
|
||||||
// worker thread
|
// worker thread
|
||||||
std::thread _worker_thread;
|
std::thread _worker_thread;
|
||||||
|
|
||||||
void push_msg(async_msg&& new_msg);
|
void push_msg(async_msg&& new_msg);
|
||||||
|
|
||||||
// worker thread main loop
|
// worker thread main loop
|
||||||
void worker_loop();
|
void worker_loop();
|
||||||
|
|
||||||
// pop next message from the queue and process it. will set the last_pop to the pop time
|
// 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
|
// 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);
|
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);
|
void handle_flush_interval(log_clock::time_point& now, log_clock::time_point& last_flush);
|
||||||
|
|
||||||
// sleep,yield or return immediatly using the time passed since last message as a hint
|
// sleep,yield or return immediatly 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);
|
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
|
// wait until the queue is empty
|
||||||
void wait_empty_q();
|
void wait_empty_q();
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// async_sink class implementation
|
// async_sink class implementation
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
inline spdlog::details::async_log_helper::async_log_helper(
|
inline spdlog::details::async_log_helper::async_log_helper(
|
||||||
formatter_ptr formatter,
|
formatter_ptr formatter,
|
||||||
const std::vector<sink_ptr>& sinks,
|
const std::vector<sink_ptr>& sinks,
|
||||||
size_t queue_size,
|
size_t queue_size,
|
||||||
log_err_handler err_handler,
|
log_err_handler err_handler,
|
||||||
const async_overflow_policy overflow_policy,
|
const async_overflow_policy overflow_policy,
|
||||||
const std::function<void()>& worker_warmup_cb,
|
const std::function<void()>& worker_warmup_cb,
|
||||||
const std::chrono::milliseconds& flush_interval_ms,
|
const std::chrono::milliseconds& flush_interval_ms,
|
||||||
const std::function<void()>& worker_teardown_cb):
|
const std::function<void()>& worker_teardown_cb):
|
||||||
_formatter(formatter),
|
_formatter(formatter),
|
||||||
_sinks(sinks),
|
_sinks(sinks),
|
||||||
_q(queue_size),
|
_q(queue_size),
|
||||||
_err_handler(err_handler),
|
_err_handler(err_handler),
|
||||||
_flush_requested(false),
|
_flush_requested(false),
|
||||||
_terminate_requested(false),
|
_terminate_requested(false),
|
||||||
_overflow_policy(overflow_policy),
|
_overflow_policy(overflow_policy),
|
||||||
_worker_warmup_cb(worker_warmup_cb),
|
_worker_warmup_cb(worker_warmup_cb),
|
||||||
_flush_interval_ms(flush_interval_ms),
|
_flush_interval_ms(flush_interval_ms),
|
||||||
_worker_teardown_cb(worker_teardown_cb),
|
_worker_teardown_cb(worker_teardown_cb),
|
||||||
_worker_thread(&async_log_helper::worker_loop, this)
|
_worker_thread(&async_log_helper::worker_loop, this)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
// Send to the worker thread termination message(level=off)
|
// Send to the worker thread termination message(level=off)
|
||||||
// and wait for it to finish gracefully
|
// and wait for it to finish gracefully
|
||||||
inline spdlog::details::async_log_helper::~async_log_helper()
|
inline spdlog::details::async_log_helper::~async_log_helper()
|
||||||
{
|
{
|
||||||
try {
|
try
|
||||||
push_msg(async_msg(async_msg_type::terminate));
|
{
|
||||||
_worker_thread.join();
|
push_msg(async_msg(async_msg_type::terminate));
|
||||||
}
|
_worker_thread.join();
|
||||||
catch (...) // don't crash in destructor
|
}
|
||||||
{
|
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)
|
//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)
|
inline void spdlog::details::async_log_helper::log(const details::log_msg& msg)
|
||||||
{
|
{
|
||||||
push_msg(async_msg(msg));
|
push_msg(async_msg(msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void spdlog::details::async_log_helper::push_msg(details::async_log_helper::async_msg&& new_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) {
|
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;
|
auto last_op_time = details::os::now();
|
||||||
do {
|
auto now = last_op_time;
|
||||||
now = details::os::now();
|
do
|
||||||
sleep_or_yield(now, last_op_time);
|
{
|
||||||
} while (!_q.enqueue(std::move(new_msg)));
|
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
|
// 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)
|
inline void spdlog::details::async_log_helper::flush(bool wait_for_q)
|
||||||
{
|
{
|
||||||
push_msg(async_msg(async_msg_type::flush));
|
push_msg(async_msg(async_msg_type::flush));
|
||||||
if (wait_for_q)
|
if (wait_for_q)
|
||||||
wait_empty_q(); //return only make after the above flush message was processed
|
wait_empty_q(); //return only make after the above flush message was processed
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void spdlog::details::async_log_helper::worker_loop()
|
inline void spdlog::details::async_log_helper::worker_loop()
|
||||||
{
|
{
|
||||||
if (_worker_warmup_cb) _worker_warmup_cb();
|
if (_worker_warmup_cb) _worker_warmup_cb();
|
||||||
auto last_pop = details::os::now();
|
auto last_pop = details::os::now();
|
||||||
auto last_flush = last_pop;
|
auto last_flush = last_pop;
|
||||||
auto active = true;
|
auto active = true;
|
||||||
while (active) {
|
while (active)
|
||||||
try {
|
{
|
||||||
active = process_next_msg(last_pop, last_flush);
|
try
|
||||||
}
|
{
|
||||||
catch (const std::exception &ex) {
|
active = process_next_msg(last_pop, last_flush);
|
||||||
_err_handler(ex.what());
|
}
|
||||||
}
|
catch (const std::exception &ex)
|
||||||
catch (...) {
|
{
|
||||||
_err_handler("Unknown exception");
|
_err_handler(ex.what());
|
||||||
}
|
}
|
||||||
}
|
catch (...)
|
||||||
if (_worker_teardown_cb) _worker_teardown_cb();
|
{
|
||||||
|
_err_handler("Unknown exception");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (_worker_teardown_cb) _worker_teardown_cb();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -278,98 +286,105 @@ inline void spdlog::details::async_log_helper::worker_loop()
|
|||||||
// return true if this thread should still be active (while no terminate msg was received)
|
// 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)
|
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;
|
async_msg incoming_async_msg;
|
||||||
|
|
||||||
if (_q.dequeue(incoming_async_msg)) {
|
if (_q.dequeue(incoming_async_msg))
|
||||||
last_pop = details::os::now();
|
{
|
||||||
switch (incoming_async_msg.msg_type) {
|
last_pop = details::os::now();
|
||||||
case async_msg_type::flush:
|
switch (incoming_async_msg.msg_type)
|
||||||
_flush_requested = true;
|
{
|
||||||
break;
|
case async_msg_type::flush:
|
||||||
|
_flush_requested = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case async_msg_type::terminate:
|
case async_msg_type::terminate:
|
||||||
_flush_requested = true;
|
_flush_requested = true;
|
||||||
_terminate_requested = true;
|
_terminate_requested = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log_msg incoming_log_msg;
|
log_msg incoming_log_msg;
|
||||||
incoming_async_msg.fill_log_msg(incoming_log_msg);
|
incoming_async_msg.fill_log_msg(incoming_log_msg);
|
||||||
_formatter->format(incoming_log_msg);
|
_formatter->format(incoming_log_msg);
|
||||||
for (auto &s : _sinks) {
|
for (auto &s : _sinks)
|
||||||
if (s->should_log(incoming_log_msg.level)) {
|
{
|
||||||
s->log(incoming_log_msg);
|
if (s->should_log(incoming_log_msg.level))
|
||||||
}
|
{
|
||||||
}
|
s->log(incoming_log_msg);
|
||||||
}
|
}
|
||||||
return true;
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle empty queue..
|
// Handle empty queue..
|
||||||
// This is the only place where the queue can terminate or flush to avoid losing messages already in the queue
|
// This is the only place where the queue can terminate or flush to avoid losing messages already in the queue
|
||||||
else {
|
else
|
||||||
auto now = details::os::now();
|
{
|
||||||
handle_flush_interval(now, last_flush);
|
auto now = details::os::now();
|
||||||
sleep_or_yield(now, last_pop);
|
handle_flush_interval(now, last_flush);
|
||||||
return !_terminate_requested;
|
sleep_or_yield(now, last_pop);
|
||||||
}
|
return !_terminate_requested;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// flush all sinks if _flush_interval_ms has expired
|
// 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)
|
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);
|
auto should_flush = _flush_requested || (_flush_interval_ms != std::chrono::milliseconds::zero() && now - last_flush >= _flush_interval_ms);
|
||||||
if (should_flush) {
|
if (should_flush)
|
||||||
for (auto &s : _sinks)
|
{
|
||||||
s->flush();
|
for (auto &s : _sinks)
|
||||||
now = last_flush = details::os::now();
|
s->flush();
|
||||||
_flush_requested = false;
|
now = last_flush = details::os::now();
|
||||||
}
|
_flush_requested = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter)
|
inline void spdlog::details::async_log_helper::set_formatter(formatter_ptr msg_formatter)
|
||||||
{
|
{
|
||||||
_formatter = msg_formatter;
|
_formatter = msg_formatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// spin, yield or sleep. use the time passed since last message as a hint
|
// 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)
|
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 namespace std::this_thread;
|
||||||
using std::chrono::milliseconds;
|
using std::chrono::milliseconds;
|
||||||
using std::chrono::microseconds;
|
using std::chrono::microseconds;
|
||||||
|
|
||||||
auto time_since_op = now - last_op_time;
|
auto time_since_op = now - last_op_time;
|
||||||
|
|
||||||
// spin upto 50 micros
|
// spin upto 50 micros
|
||||||
if (time_since_op <= microseconds(50))
|
if (time_since_op <= microseconds(50))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// yield upto 150 micros
|
// yield upto 150 micros
|
||||||
if (time_since_op <= microseconds(100))
|
if (time_since_op <= microseconds(100))
|
||||||
return std::this_thread::yield();
|
return std::this_thread::yield();
|
||||||
|
|
||||||
// sleep for 20 ms upto 200 ms
|
// sleep for 20 ms upto 200 ms
|
||||||
if (time_since_op <= milliseconds(200))
|
if (time_since_op <= milliseconds(200))
|
||||||
return sleep_for(milliseconds(20));
|
return sleep_for(milliseconds(20));
|
||||||
|
|
||||||
// sleep for 200 ms
|
// sleep for 200 ms
|
||||||
return sleep_for(milliseconds(200));
|
return sleep_for(milliseconds(200));
|
||||||
}
|
}
|
||||||
|
|
||||||
// wait for the queue to be empty
|
// wait for the queue to be empty
|
||||||
inline void spdlog::details::async_log_helper::wait_empty_q()
|
inline void spdlog::details::async_log_helper::wait_empty_q()
|
||||||
{
|
{
|
||||||
auto last_op = details::os::now();
|
auto last_op = details::os::now();
|
||||||
while (_q.approx_size() > 0) {
|
while (_q.approx_size() > 0)
|
||||||
sleep_or_yield(details::os::now(), last_op);
|
{
|
||||||
}
|
sleep_or_yield(details::os::now(), last_op);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler)
|
inline void spdlog::details::async_log_helper::set_error_handler(spdlog::log_err_handler err_handler)
|
||||||
{
|
{
|
||||||
_err_handler = err_handler;
|
_err_handler = err_handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -60,13 +60,13 @@ inline void spdlog::async_logger::flush()
|
|||||||
// Error handler
|
// Error handler
|
||||||
inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_handler)
|
inline void spdlog::async_logger::set_error_handler(spdlog::log_err_handler err_handler)
|
||||||
{
|
{
|
||||||
_err_handler = err_handler;
|
_err_handler = err_handler;
|
||||||
_async_log_helper->set_error_handler(err_handler);
|
_async_log_helper->set_error_handler(err_handler);
|
||||||
|
|
||||||
}
|
}
|
||||||
inline spdlog::log_err_handler spdlog::async_logger::error_handler()
|
inline spdlog::log_err_handler spdlog::async_logger::error_handler()
|
||||||
{
|
{
|
||||||
return _err_handler;
|
return _err_handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -13,74 +13,85 @@
|
|||||||
#include "format.h"
|
#include "format.h"
|
||||||
#include <ostream>
|
#include <ostream>
|
||||||
|
|
||||||
namespace fmt {
|
namespace fmt
|
||||||
|
{
|
||||||
|
|
||||||
namespace internal {
|
namespace internal
|
||||||
|
{
|
||||||
|
|
||||||
template <class Char>
|
template <class Char>
|
||||||
class FormatBuf : public std::basic_streambuf<Char> {
|
class FormatBuf : public std::basic_streambuf<Char>
|
||||||
private:
|
{
|
||||||
typedef typename std::basic_streambuf<Char>::int_type int_type;
|
private:
|
||||||
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
|
typedef typename std::basic_streambuf<Char>::int_type int_type;
|
||||||
|
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
|
||||||
|
|
||||||
Buffer<Char> &buffer_;
|
Buffer<Char> &buffer_;
|
||||||
Char *start_;
|
Char *start_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0]) {
|
FormatBuf(Buffer<Char> &buffer) : buffer_(buffer), start_(&buffer[0])
|
||||||
this->setp(start_, start_ + buffer_.capacity());
|
{
|
||||||
}
|
this->setp(start_, start_ + buffer_.capacity());
|
||||||
|
|
||||||
int_type overflow(int_type ch = traits_type::eof()) {
|
|
||||||
if (!traits_type::eq_int_type(ch, traits_type::eof())) {
|
|
||||||
size_t buf_size = size();
|
|
||||||
buffer_.resize(buf_size);
|
|
||||||
buffer_.reserve(buf_size * 2);
|
|
||||||
|
|
||||||
start_ = &buffer_[0];
|
|
||||||
start_[buf_size] = traits_type::to_char_type(ch);
|
|
||||||
this->setp(start_+ buf_size + 1, start_ + buf_size * 2);
|
|
||||||
}
|
}
|
||||||
return ch;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t size() const {
|
int_type overflow(int_type ch = traits_type::eof())
|
||||||
return to_unsigned(this->pptr() - start_);
|
{
|
||||||
}
|
if (!traits_type::eq_int_type(ch, traits_type::eof()))
|
||||||
|
{
|
||||||
|
size_t buf_size = size();
|
||||||
|
buffer_.resize(buf_size);
|
||||||
|
buffer_.reserve(buf_size * 2);
|
||||||
|
|
||||||
|
start_ = &buffer_[0];
|
||||||
|
start_[buf_size] = traits_type::to_char_type(ch);
|
||||||
|
this->setp(start_+ buf_size + 1, start_ + buf_size * 2);
|
||||||
|
}
|
||||||
|
return ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size() const
|
||||||
|
{
|
||||||
|
return to_unsigned(this->pptr() - start_);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Yes &convert(std::ostream &);
|
Yes &convert(std::ostream &);
|
||||||
|
|
||||||
struct DummyStream : std::ostream {
|
struct DummyStream : std::ostream
|
||||||
DummyStream(); // Suppress a bogus warning in MSVC.
|
{
|
||||||
// Hide all operator<< overloads from std::ostream.
|
DummyStream(); // Suppress a bogus warning in MSVC.
|
||||||
void operator<<(Null<>);
|
// Hide all operator<< overloads from std::ostream.
|
||||||
|
void operator<<(Null<>);
|
||||||
};
|
};
|
||||||
|
|
||||||
No &operator<<(std::ostream &, int);
|
No &operator<<(std::ostream &, int);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
struct ConvertToIntImpl<T, true> {
|
struct ConvertToIntImpl<T, true>
|
||||||
// Convert to int only if T doesn't have an overloaded operator<<.
|
{
|
||||||
enum {
|
// Convert to int only if T doesn't have an overloaded operator<<.
|
||||||
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
|
enum
|
||||||
};
|
{
|
||||||
|
value = sizeof(convert(get<DummyStream>() << get<T>())) == sizeof(No)
|
||||||
|
};
|
||||||
};
|
};
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
|
|
||||||
// Formats a value.
|
// Formats a value.
|
||||||
template <typename Char, typename ArgFormatter, typename T>
|
template <typename Char, typename ArgFormatter, typename T>
|
||||||
void format(BasicFormatter<Char, ArgFormatter> &f,
|
void format(BasicFormatter<Char, ArgFormatter> &f,
|
||||||
const Char *&format_str, const T &value) {
|
const Char *&format_str, const T &value)
|
||||||
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
|
{
|
||||||
|
internal::MemoryBuffer<Char, internal::INLINE_BUFFER_SIZE> buffer;
|
||||||
|
|
||||||
internal::FormatBuf<Char> format_buf(buffer);
|
internal::FormatBuf<Char> format_buf(buffer);
|
||||||
std::basic_ostream<Char> output(&format_buf);
|
std::basic_ostream<Char> output(&format_buf);
|
||||||
output << value;
|
output << value;
|
||||||
|
|
||||||
BasicStringRef<Char> str(&buffer[0], format_buf.size());
|
BasicStringRef<Char> str(&buffer[0], format_buf.size());
|
||||||
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
|
typedef internal::MakeArg< BasicFormatter<Char> > MakeArg;
|
||||||
format_str = f.format(format_str, MakeArg(str));
|
format_str = f.format(format_str, MakeArg(str));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -83,112 +83,134 @@
|
|||||||
|
|
||||||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||||
|
|
||||||
namespace fmt {
|
namespace fmt
|
||||||
|
{
|
||||||
|
|
||||||
// An error code.
|
// An error code.
|
||||||
class ErrorCode {
|
class ErrorCode
|
||||||
private:
|
{
|
||||||
int value_;
|
private:
|
||||||
|
int value_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit ErrorCode(int value = 0) FMT_NOEXCEPT : value_(value) {}
|
explicit ErrorCode(int value = 0) FMT_NOEXCEPT :
|
||||||
|
value_(value) {}
|
||||||
|
|
||||||
int get() const FMT_NOEXCEPT { return value_; }
|
int get() const FMT_NOEXCEPT
|
||||||
|
{
|
||||||
|
return value_;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// A buffered file.
|
// A buffered file.
|
||||||
class BufferedFile {
|
class BufferedFile
|
||||||
private:
|
{
|
||||||
FILE *file_;
|
private:
|
||||||
|
FILE *file_;
|
||||||
|
|
||||||
friend class File;
|
friend class File;
|
||||||
|
|
||||||
explicit BufferedFile(FILE *f) : file_(f) {}
|
explicit BufferedFile(FILE *f) : file_(f) {}
|
||||||
|
|
||||||
public:
|
|
||||||
// Constructs a BufferedFile object which doesn't represent any file.
|
|
||||||
BufferedFile() FMT_NOEXCEPT : file_(0) {}
|
|
||||||
|
|
||||||
// Destroys the object closing the file it represents if any.
|
|
||||||
~BufferedFile() FMT_NOEXCEPT;
|
|
||||||
|
|
||||||
#if !FMT_USE_RVALUE_REFERENCES
|
|
||||||
// Emulate a move constructor and a move assignment operator if rvalue
|
|
||||||
// references are not supported.
|
|
||||||
|
|
||||||
private:
|
|
||||||
// A proxy object to emulate a move constructor.
|
|
||||||
// It is private to make it impossible call operator Proxy directly.
|
|
||||||
struct Proxy {
|
|
||||||
FILE *file;
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// A "move constructor" for moving from a temporary.
|
// Constructs a BufferedFile object which doesn't represent any file.
|
||||||
BufferedFile(Proxy p) FMT_NOEXCEPT : file_(p.file) {}
|
BufferedFile() FMT_NOEXCEPT :
|
||||||
|
file_(0) {}
|
||||||
|
|
||||||
// A "move constructor" for moving from an lvalue.
|
// Destroys the object closing the file it represents if any.
|
||||||
BufferedFile(BufferedFile &f) FMT_NOEXCEPT : file_(f.file_) {
|
~BufferedFile() FMT_NOEXCEPT;
|
||||||
f.file_ = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A "move assignment operator" for moving from a temporary.
|
#if !FMT_USE_RVALUE_REFERENCES
|
||||||
BufferedFile &operator=(Proxy p) {
|
// Emulate a move constructor and a move assignment operator if rvalue
|
||||||
close();
|
// references are not supported.
|
||||||
file_ = p.file;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A "move assignment operator" for moving from an lvalue.
|
private:
|
||||||
BufferedFile &operator=(BufferedFile &other) {
|
// A proxy object to emulate a move constructor.
|
||||||
close();
|
// It is private to make it impossible call operator Proxy directly.
|
||||||
file_ = other.file_;
|
struct Proxy
|
||||||
other.file_ = 0;
|
{
|
||||||
return *this;
|
FILE *file;
|
||||||
}
|
};
|
||||||
|
|
||||||
// Returns a proxy object for moving from a temporary:
|
public:
|
||||||
// BufferedFile file = BufferedFile(...);
|
// A "move constructor" for moving from a temporary.
|
||||||
operator Proxy() FMT_NOEXCEPT {
|
BufferedFile(Proxy p) FMT_NOEXCEPT :
|
||||||
Proxy p = {file_};
|
file_(p.file) {}
|
||||||
file_ = 0;
|
|
||||||
return p;
|
// A "move constructor" for moving from an lvalue.
|
||||||
}
|
BufferedFile(BufferedFile &f) FMT_NOEXCEPT :
|
||||||
|
file_(f.file_)
|
||||||
|
{
|
||||||
|
f.file_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A "move assignment operator" for moving from a temporary.
|
||||||
|
BufferedFile &operator=(Proxy p)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
file_ = p.file;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A "move assignment operator" for moving from an lvalue.
|
||||||
|
BufferedFile &operator=(BufferedFile &other)
|
||||||
|
{
|
||||||
|
close();
|
||||||
|
file_ = other.file_;
|
||||||
|
other.file_ = 0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a proxy object for moving from a temporary:
|
||||||
|
// BufferedFile file = BufferedFile(...);
|
||||||
|
operator Proxy() FMT_NOEXCEPT
|
||||||
|
{
|
||||||
|
Proxy p = {file_};
|
||||||
|
file_ = 0;
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
private:
|
private:
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
|
FMT_DISALLOW_COPY_AND_ASSIGN(BufferedFile);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT : file_(other.file_) {
|
BufferedFile(BufferedFile &&other) FMT_NOEXCEPT :
|
||||||
other.file_ = 0;
|
file_(other.file_)
|
||||||
}
|
{
|
||||||
|
other.file_ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
BufferedFile& operator=(BufferedFile &&other) {
|
BufferedFile& operator=(BufferedFile &&other)
|
||||||
close();
|
{
|
||||||
file_ = other.file_;
|
close();
|
||||||
other.file_ = 0;
|
file_ = other.file_;
|
||||||
return *this;
|
other.file_ = 0;
|
||||||
}
|
return *this;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Opens a file.
|
// Opens a file.
|
||||||
BufferedFile(CStringRef filename, CStringRef mode);
|
BufferedFile(CStringRef filename, CStringRef mode);
|
||||||
|
|
||||||
// Closes the file.
|
// Closes the file.
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
// Returns the pointer to a FILE object representing this file.
|
// Returns the pointer to a FILE object representing this file.
|
||||||
FILE *get() const FMT_NOEXCEPT { return file_; }
|
FILE *get() const FMT_NOEXCEPT
|
||||||
|
{
|
||||||
|
return file_;
|
||||||
|
}
|
||||||
|
|
||||||
// We place parentheses around fileno to workaround a bug in some versions
|
// We place parentheses around fileno to workaround a bug in some versions
|
||||||
// of MinGW that define fileno as a macro.
|
// of MinGW that define fileno as a macro.
|
||||||
int (fileno)() const;
|
int (fileno)() const;
|
||||||
|
|
||||||
void print(CStringRef format_str, const ArgList &args) {
|
void print(CStringRef format_str, const ArgList &args)
|
||||||
fmt::print(file_, format_str, args);
|
{
|
||||||
}
|
fmt::print(file_, format_str, args);
|
||||||
FMT_VARIADIC(void, print, CStringRef)
|
}
|
||||||
|
FMT_VARIADIC(void, print, CStringRef)
|
||||||
};
|
};
|
||||||
|
|
||||||
// A file. Closed file is represented by a File object with descriptor -1.
|
// A file. Closed file is represented by a File object with descriptor -1.
|
||||||
@ -197,125 +219,141 @@ public:
|
|||||||
// closing the file multiple times will cause a crash on Windows rather
|
// closing the file multiple times will cause a crash on Windows rather
|
||||||
// than an exception. You can get standard behavior by overriding the
|
// than an exception. You can get standard behavior by overriding the
|
||||||
// invalid parameter handler with _set_invalid_parameter_handler.
|
// invalid parameter handler with _set_invalid_parameter_handler.
|
||||||
class File {
|
class File
|
||||||
private:
|
{
|
||||||
int fd_; // File descriptor.
|
private:
|
||||||
|
int fd_; // File descriptor.
|
||||||
|
|
||||||
// Constructs a File object with a given descriptor.
|
// Constructs a File object with a given descriptor.
|
||||||
explicit File(int fd) : fd_(fd) {}
|
explicit File(int fd) : fd_(fd) {}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// Possible values for the oflag argument to the constructor.
|
// Possible values for the oflag argument to the constructor.
|
||||||
enum {
|
enum
|
||||||
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
{
|
||||||
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
|
||||||
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
|
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
|
||||||
};
|
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
|
||||||
|
};
|
||||||
|
|
||||||
// Constructs a File object which doesn't represent any file.
|
// Constructs a File object which doesn't represent any file.
|
||||||
File() FMT_NOEXCEPT : fd_(-1) {}
|
File() FMT_NOEXCEPT :
|
||||||
|
fd_(-1) {}
|
||||||
|
|
||||||
// Opens a file and constructs a File object representing this file.
|
// Opens a file and constructs a File object representing this file.
|
||||||
File(CStringRef path, int oflag);
|
File(CStringRef path, int oflag);
|
||||||
|
|
||||||
#if !FMT_USE_RVALUE_REFERENCES
|
#if !FMT_USE_RVALUE_REFERENCES
|
||||||
// Emulate a move constructor and a move assignment operator if rvalue
|
// Emulate a move constructor and a move assignment operator if rvalue
|
||||||
// references are not supported.
|
// references are not supported.
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// A proxy object to emulate a move constructor.
|
// A proxy object to emulate a move constructor.
|
||||||
// It is private to make it impossible call operator Proxy directly.
|
// It is private to make it impossible call operator Proxy directly.
|
||||||
struct Proxy {
|
struct Proxy
|
||||||
int fd;
|
{
|
||||||
};
|
int fd;
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// A "move constructor" for moving from a temporary.
|
// A "move constructor" for moving from a temporary.
|
||||||
File(Proxy p) FMT_NOEXCEPT : fd_(p.fd) {}
|
File(Proxy p) FMT_NOEXCEPT :
|
||||||
|
fd_(p.fd) {}
|
||||||
|
|
||||||
// A "move constructor" for moving from an lvalue.
|
// A "move constructor" for moving from an lvalue.
|
||||||
File(File &other) FMT_NOEXCEPT : fd_(other.fd_) {
|
File(File &other) FMT_NOEXCEPT :
|
||||||
other.fd_ = -1;
|
fd_(other.fd_)
|
||||||
}
|
{
|
||||||
|
other.fd_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
// A "move assignment operator" for moving from a temporary.
|
// A "move assignment operator" for moving from a temporary.
|
||||||
File &operator=(Proxy p) {
|
File &operator=(Proxy p)
|
||||||
close();
|
{
|
||||||
fd_ = p.fd;
|
close();
|
||||||
return *this;
|
fd_ = p.fd;
|
||||||
}
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
// A "move assignment operator" for moving from an lvalue.
|
// A "move assignment operator" for moving from an lvalue.
|
||||||
File &operator=(File &other) {
|
File &operator=(File &other)
|
||||||
close();
|
{
|
||||||
fd_ = other.fd_;
|
close();
|
||||||
other.fd_ = -1;
|
fd_ = other.fd_;
|
||||||
return *this;
|
other.fd_ = -1;
|
||||||
}
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
// Returns a proxy object for moving from a temporary:
|
// Returns a proxy object for moving from a temporary:
|
||||||
// File file = File(...);
|
// File file = File(...);
|
||||||
operator Proxy() FMT_NOEXCEPT {
|
operator Proxy() FMT_NOEXCEPT
|
||||||
Proxy p = {fd_};
|
{
|
||||||
fd_ = -1;
|
Proxy p = {fd_};
|
||||||
return p;
|
fd_ = -1;
|
||||||
}
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
private:
|
private:
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(File);
|
FMT_DISALLOW_COPY_AND_ASSIGN(File);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
File(File &&other) FMT_NOEXCEPT : fd_(other.fd_) {
|
File(File &&other) FMT_NOEXCEPT :
|
||||||
other.fd_ = -1;
|
fd_(other.fd_)
|
||||||
}
|
{
|
||||||
|
other.fd_ = -1;
|
||||||
|
}
|
||||||
|
|
||||||
File& operator=(File &&other) {
|
File& operator=(File &&other)
|
||||||
close();
|
{
|
||||||
fd_ = other.fd_;
|
close();
|
||||||
other.fd_ = -1;
|
fd_ = other.fd_;
|
||||||
return *this;
|
other.fd_ = -1;
|
||||||
}
|
return *this;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Destroys the object closing the file it represents if any.
|
// Destroys the object closing the file it represents if any.
|
||||||
~File() FMT_NOEXCEPT;
|
~File() FMT_NOEXCEPT;
|
||||||
|
|
||||||
// Returns the file descriptor.
|
// Returns the file descriptor.
|
||||||
int descriptor() const FMT_NOEXCEPT { return fd_; }
|
int descriptor() const FMT_NOEXCEPT
|
||||||
|
{
|
||||||
|
return fd_;
|
||||||
|
}
|
||||||
|
|
||||||
// Closes the file.
|
// Closes the file.
|
||||||
void close();
|
void close();
|
||||||
|
|
||||||
// Returns the file size. The size has signed type for consistency with
|
// Returns the file size. The size has signed type for consistency with
|
||||||
// stat::st_size.
|
// stat::st_size.
|
||||||
LongLong size() const;
|
LongLong size() const;
|
||||||
|
|
||||||
// Attempts to read count bytes from the file into the specified buffer.
|
// Attempts to read count bytes from the file into the specified buffer.
|
||||||
std::size_t read(void *buffer, std::size_t count);
|
std::size_t read(void *buffer, std::size_t count);
|
||||||
|
|
||||||
// Attempts to write count bytes from the specified buffer to the file.
|
// Attempts to write count bytes from the specified buffer to the file.
|
||||||
std::size_t write(const void *buffer, std::size_t count);
|
std::size_t write(const void *buffer, std::size_t count);
|
||||||
|
|
||||||
// Duplicates a file descriptor with the dup function and returns
|
// Duplicates a file descriptor with the dup function and returns
|
||||||
// the duplicate as a file object.
|
// the duplicate as a file object.
|
||||||
static File dup(int fd);
|
static File dup(int fd);
|
||||||
|
|
||||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
// necessary.
|
// necessary.
|
||||||
void dup2(int fd);
|
void dup2(int fd);
|
||||||
|
|
||||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||||
// necessary.
|
// necessary.
|
||||||
void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT;
|
void dup2(int fd, ErrorCode &ec) FMT_NOEXCEPT;
|
||||||
|
|
||||||
// Creates a pipe setting up read_end and write_end file objects for reading
|
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||||
// and writing respectively.
|
// and writing respectively.
|
||||||
static void pipe(File &read_end, File &write_end);
|
static void pipe(File &read_end, File &write_end);
|
||||||
|
|
||||||
// Creates a BufferedFile object associated with this file and detaches
|
// Creates a BufferedFile object associated with this file and detaches
|
||||||
// this File object from the file.
|
// this File object from the file.
|
||||||
BufferedFile fdopen(const char *mode);
|
BufferedFile fdopen(const char *mode);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Returns the memory page size.
|
// Returns the memory page size.
|
||||||
@ -328,58 +366,77 @@ long getpagesize();
|
|||||||
|
|
||||||
#ifdef FMT_LOCALE
|
#ifdef FMT_LOCALE
|
||||||
// A "C" numeric locale.
|
// A "C" numeric locale.
|
||||||
class Locale {
|
class Locale
|
||||||
private:
|
{
|
||||||
|
private:
|
||||||
# ifdef _MSC_VER
|
# ifdef _MSC_VER
|
||||||
typedef _locale_t locale_t;
|
typedef _locale_t locale_t;
|
||||||
|
|
||||||
enum { LC_NUMERIC_MASK = LC_NUMERIC };
|
enum { LC_NUMERIC_MASK = LC_NUMERIC };
|
||||||
|
|
||||||
static locale_t newlocale(int category_mask, const char *locale, locale_t) {
|
static locale_t newlocale(int category_mask, const char *locale, locale_t)
|
||||||
return _create_locale(category_mask, locale);
|
{
|
||||||
}
|
return _create_locale(category_mask, locale);
|
||||||
|
}
|
||||||
|
|
||||||
static void freelocale(locale_t locale) {
|
static void freelocale(locale_t locale)
|
||||||
_free_locale(locale);
|
{
|
||||||
}
|
_free_locale(locale);
|
||||||
|
}
|
||||||
|
|
||||||
static double strtod_l(const char *nptr, char **endptr, _locale_t locale) {
|
static double strtod_l(const char *nptr, char **endptr, _locale_t locale)
|
||||||
return _strtod_l(nptr, endptr, locale);
|
{
|
||||||
}
|
return _strtod_l(nptr, endptr, locale);
|
||||||
|
}
|
||||||
# endif
|
# endif
|
||||||
|
|
||||||
locale_t locale_;
|
locale_t locale_;
|
||||||
|
|
||||||
FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
|
FMT_DISALLOW_COPY_AND_ASSIGN(Locale);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef locale_t Type;
|
typedef locale_t Type;
|
||||||
|
|
||||||
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", NULL)) {
|
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", NULL))
|
||||||
if (!locale_)
|
{
|
||||||
FMT_THROW(fmt::SystemError(errno, "cannot create locale"));
|
if (!locale_)
|
||||||
}
|
FMT_THROW(fmt::SystemError(errno, "cannot create locale"));
|
||||||
~Locale() { freelocale(locale_); }
|
}
|
||||||
|
~Locale()
|
||||||
|
{
|
||||||
|
freelocale(locale_);
|
||||||
|
}
|
||||||
|
|
||||||
Type get() const { return locale_; }
|
Type get() const
|
||||||
|
{
|
||||||
|
return locale_;
|
||||||
|
}
|
||||||
|
|
||||||
// Converts string to floating-point number and advances str past the end
|
// Converts string to floating-point number and advances str past the end
|
||||||
// of the parsed input.
|
// of the parsed input.
|
||||||
double strtod(const char *&str) const {
|
double strtod(const char *&str) const
|
||||||
char *end = 0;
|
{
|
||||||
double result = strtod_l(str, &end, locale_);
|
char *end = 0;
|
||||||
str = end;
|
double result = strtod_l(str, &end, locale_);
|
||||||
return result;
|
str = end;
|
||||||
}
|
return result;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
#endif // FMT_LOCALE
|
#endif // FMT_LOCALE
|
||||||
} // namespace fmt
|
} // namespace fmt
|
||||||
|
|
||||||
#if !FMT_USE_RVALUE_REFERENCES
|
#if !FMT_USE_RVALUE_REFERENCES
|
||||||
namespace std {
|
namespace std
|
||||||
|
{
|
||||||
// For compatibility with C++98.
|
// For compatibility with C++98.
|
||||||
inline fmt::BufferedFile &move(fmt::BufferedFile &f) { return f; }
|
inline fmt::BufferedFile &move(fmt::BufferedFile &f)
|
||||||
inline fmt::File &move(fmt::File &f) { return f; }
|
{
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
inline fmt::File &move(fmt::File &f)
|
||||||
|
{
|
||||||
|
return f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -13,40 +13,45 @@
|
|||||||
#include "format.h"
|
#include "format.h"
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
namespace fmt {
|
namespace fmt
|
||||||
|
{
|
||||||
template <typename ArgFormatter>
|
template <typename ArgFormatter>
|
||||||
void format(BasicFormatter<char, ArgFormatter> &f,
|
void format(BasicFormatter<char, ArgFormatter> &f,
|
||||||
const char *&format_str, const std::tm &tm) {
|
const char *&format_str, const std::tm &tm)
|
||||||
if (*format_str == ':')
|
{
|
||||||
++format_str;
|
if (*format_str == ':')
|
||||||
const char *end = format_str;
|
++format_str;
|
||||||
while (*end && *end != '}')
|
const char *end = format_str;
|
||||||
++end;
|
while (*end && *end != '}')
|
||||||
if (*end != '}')
|
++end;
|
||||||
FMT_THROW(FormatError("missing '}' in format string"));
|
if (*end != '}')
|
||||||
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format;
|
FMT_THROW(FormatError("missing '}' in format string"));
|
||||||
format.append(format_str, end + 1);
|
internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> format;
|
||||||
format[format.size() - 1] = '\0';
|
format.append(format_str, end + 1);
|
||||||
Buffer<char> &buffer = f.writer().buffer();
|
format[format.size() - 1] = '\0';
|
||||||
std::size_t start = buffer.size();
|
Buffer<char> &buffer = f.writer().buffer();
|
||||||
for (;;) {
|
std::size_t start = buffer.size();
|
||||||
std::size_t size = buffer.capacity() - start;
|
for (;;)
|
||||||
std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm);
|
{
|
||||||
if (count != 0) {
|
std::size_t size = buffer.capacity() - start;
|
||||||
buffer.resize(start + count);
|
std::size_t count = std::strftime(&buffer[start], size, &format[0], &tm);
|
||||||
break;
|
if (count != 0)
|
||||||
|
{
|
||||||
|
buffer.resize(start + count);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (size >= format.size() * 256)
|
||||||
|
{
|
||||||
|
// If the buffer is 256 times larger than the format string, assume
|
||||||
|
// that `strftime` gives an empty result. There doesn't seem to be a
|
||||||
|
// better way to distinguish the two cases:
|
||||||
|
// https://github.com/fmtlib/fmt/issues/367
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const std::size_t MIN_GROWTH = 10;
|
||||||
|
buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
||||||
}
|
}
|
||||||
if (size >= format.size() * 256) {
|
format_str = end + 1;
|
||||||
// If the buffer is 256 times larger than the format string, assume
|
|
||||||
// that `strftime` gives an empty result. There doesn't seem to be a
|
|
||||||
// better way to distinguish the two cases:
|
|
||||||
// https://github.com/fmtlib/fmt/issues/367
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const std::size_t MIN_GROWTH = 10;
|
|
||||||
buffer.reserve(buffer.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
|
|
||||||
}
|
|
||||||
format_str = end + 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +58,7 @@ public:
|
|||||||
const std::string& name() const;
|
const std::string& name() const;
|
||||||
void set_pattern(const std::string&);
|
void set_pattern(const std::string&);
|
||||||
void set_formatter(formatter_ptr);
|
void set_formatter(formatter_ptr);
|
||||||
|
|
||||||
// automatically call flush() if message level >= log_level
|
// automatically call flush() if message level >= log_level
|
||||||
void flush_on(level::level_enum log_level);
|
void flush_on(level::level_enum log_level);
|
||||||
|
|
||||||
@ -66,9 +66,9 @@ public:
|
|||||||
|
|
||||||
const std::vector<sink_ptr>& sinks() const;
|
const std::vector<sink_ptr>& sinks() const;
|
||||||
|
|
||||||
// error handler
|
// error handler
|
||||||
virtual void set_error_handler(log_err_handler);
|
virtual void set_error_handler(log_err_handler);
|
||||||
virtual log_err_handler error_handler();
|
virtual log_err_handler error_handler();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void _sink_it(details::log_msg&);
|
virtual void _sink_it(details::log_msg&);
|
||||||
|
140
tests/errors.cpp
140
tests/errors.cpp
@ -8,30 +8,30 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
class failing_sink: public spdlog::sinks::sink
|
class failing_sink: public spdlog::sinks::sink
|
||||||
{
|
{
|
||||||
void log(const spdlog::details::log_msg& msg) override
|
void log(const spdlog::details::log_msg& msg) override
|
||||||
{
|
{
|
||||||
throw std::runtime_error("some error happened during log");
|
throw std::runtime_error("some error happened during log");
|
||||||
}
|
}
|
||||||
|
|
||||||
void flush()
|
void flush()
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_CASE("default_error_handler", "[errors]]")
|
TEST_CASE("default_error_handler", "[errors]]")
|
||||||
{
|
{
|
||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
std::string filename = "logs/simple_log.txt";
|
std::string filename = "logs/simple_log.txt";
|
||||||
|
|
||||||
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
|
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
|
||||||
logger->set_pattern("%v");
|
logger->set_pattern("%v");
|
||||||
logger->info("Test message {} {}", 1);
|
logger->info("Test message {} {}", 1);
|
||||||
logger->info("Test message {}", 2);
|
logger->info("Test message {}", 2);
|
||||||
logger->flush();
|
logger->flush();
|
||||||
|
|
||||||
REQUIRE(file_contents(filename) == std::string("Test message 2\n"));
|
REQUIRE(file_contents(filename) == std::string("Test message 2\n"));
|
||||||
REQUIRE(count_lines(filename) == 1);
|
REQUIRE(count_lines(filename) == 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -41,69 +41,73 @@ struct custom_ex
|
|||||||
{};
|
{};
|
||||||
TEST_CASE("custom_error_handler", "[errors]]")
|
TEST_CASE("custom_error_handler", "[errors]]")
|
||||||
{
|
{
|
||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
std::string filename = "logs/simple_log.txt";
|
std::string filename = "logs/simple_log.txt";
|
||||||
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
|
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
|
||||||
logger->flush_on(spdlog::level::info);
|
logger->flush_on(spdlog::level::info);
|
||||||
logger->set_error_handler([=](const std::string& msg) {
|
logger->set_error_handler([=](const std::string& msg)
|
||||||
throw custom_ex();
|
{
|
||||||
});
|
throw custom_ex();
|
||||||
logger->info("Good message #1");
|
});
|
||||||
REQUIRE_THROWS_AS(logger->info("Bad format msg {} {}", "xxx"), custom_ex);
|
logger->info("Good message #1");
|
||||||
logger->info("Good message #2");
|
REQUIRE_THROWS_AS(logger->info("Bad format msg {} {}", "xxx"), custom_ex);
|
||||||
REQUIRE(count_lines(filename) == 2);
|
logger->info("Good message #2");
|
||||||
|
REQUIRE(count_lines(filename) == 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("default_error_handler2", "[errors]]")
|
TEST_CASE("default_error_handler2", "[errors]]")
|
||||||
{
|
{
|
||||||
|
|
||||||
auto logger = spdlog::create<failing_sink>("failed_logger");
|
auto logger = spdlog::create<failing_sink>("failed_logger");
|
||||||
logger->set_error_handler([=](const std::string& msg) {
|
logger->set_error_handler([=](const std::string& msg)
|
||||||
throw custom_ex();
|
{
|
||||||
});
|
throw custom_ex();
|
||||||
REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex);
|
});
|
||||||
|
REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("async_error_handler", "[errors]]")
|
TEST_CASE("async_error_handler", "[errors]]")
|
||||||
{
|
{
|
||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
std::string err_msg("log failed with some msg");
|
std::string err_msg("log failed with some msg");
|
||||||
spdlog::set_async_mode(128);
|
spdlog::set_async_mode(128);
|
||||||
std::string filename = "logs/simple_async_log.txt";
|
std::string filename = "logs/simple_async_log.txt";
|
||||||
{
|
{
|
||||||
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
|
auto logger = spdlog::create<spdlog::sinks::simple_file_sink_mt>("logger", filename, true);
|
||||||
logger->set_error_handler([=](const std::string& msg) {
|
logger->set_error_handler([=](const std::string& msg)
|
||||||
std::ofstream ofs("logs/custom_err.txt");
|
{
|
||||||
if (!ofs) throw std::runtime_error("Failed open logs/custom_err.txt");
|
std::ofstream ofs("logs/custom_err.txt");
|
||||||
ofs << err_msg;
|
if (!ofs) throw std::runtime_error("Failed open logs/custom_err.txt");
|
||||||
});
|
ofs << err_msg;
|
||||||
logger->info("Good message #1");
|
});
|
||||||
logger->info("Bad format msg {} {}", "xxx");
|
logger->info("Good message #1");
|
||||||
logger->info("Good message #2");
|
logger->info("Bad format msg {} {}", "xxx");
|
||||||
spdlog::drop("logger"); //force logger to drain the queue and shutdown
|
logger->info("Good message #2");
|
||||||
spdlog::set_sync_mode();
|
spdlog::drop("logger"); //force logger to drain the queue and shutdown
|
||||||
}
|
spdlog::set_sync_mode();
|
||||||
REQUIRE(count_lines(filename) == 2);
|
}
|
||||||
REQUIRE(file_contents("logs/custom_err.txt") == err_msg);
|
REQUIRE(count_lines(filename) == 2);
|
||||||
|
REQUIRE(file_contents("logs/custom_err.txt") == err_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure async error handler is executed
|
// Make sure async error handler is executed
|
||||||
TEST_CASE("async_error_handler2", "[errors]]")
|
TEST_CASE("async_error_handler2", "[errors]]")
|
||||||
{
|
{
|
||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
std::string err_msg("This is async handler error message");
|
std::string err_msg("This is async handler error message");
|
||||||
spdlog::set_async_mode(128);
|
spdlog::set_async_mode(128);
|
||||||
{
|
{
|
||||||
auto logger = spdlog::create<failing_sink>("failed_logger");
|
auto logger = spdlog::create<failing_sink>("failed_logger");
|
||||||
logger->set_error_handler([=](const std::string& msg) {
|
logger->set_error_handler([=](const std::string& msg)
|
||||||
std::ofstream ofs("logs/custom_err2.txt");
|
{
|
||||||
if (!ofs) throw std::runtime_error("Failed open logs/custom_err2.txt");
|
std::ofstream ofs("logs/custom_err2.txt");
|
||||||
ofs << err_msg;
|
if (!ofs) throw std::runtime_error("Failed open logs/custom_err2.txt");
|
||||||
});
|
ofs << err_msg;
|
||||||
logger->info("Hello failure");
|
});
|
||||||
spdlog::drop("failed_logger"); //force logger to drain the queue and shutdown
|
logger->info("Hello failure");
|
||||||
spdlog::set_sync_mode();
|
spdlog::drop("failed_logger"); //force logger to drain the queue and shutdown
|
||||||
}
|
spdlog::set_sync_mode();
|
||||||
|
}
|
||||||
|
|
||||||
REQUIRE(file_contents("logs/custom_err2.txt") == err_msg);
|
REQUIRE(file_contents("logs/custom_err2.txt") == err_msg);
|
||||||
}
|
}
|
||||||
|
@ -3,15 +3,15 @@
|
|||||||
|
|
||||||
void prepare_logdir()
|
void prepare_logdir()
|
||||||
{
|
{
|
||||||
spdlog::drop_all();
|
spdlog::drop_all();
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
system("if not exist logs mkdir logs");
|
system("if not exist logs mkdir logs");
|
||||||
system("del /F /Q logs\\*");
|
system("del /F /Q logs\\*");
|
||||||
#else
|
#else
|
||||||
auto rv = system("mkdir -p logs");
|
auto rv = system("mkdir -p logs");
|
||||||
rv = system("rm -f logs/*");
|
rv = system("rm -f logs/*");
|
||||||
(void)rv;
|
(void)rv;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user