support flush_on(..) in async loggers too

This commit is contained in:
gabime 2016-09-02 16:19:29 +03:00
parent 3a12f3c560
commit 1df30a0733
4 changed files with 475 additions and 462 deletions

View File

@ -102,6 +102,10 @@ void async_example()
size_t q_size = 4096; //queue size must be power of 2 size_t q_size = 4096; //queue size must be power of 2
spdlog::set_async_mode(q_size); spdlog::set_async_mode(q_size);
auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt"); auto async_file = spd::daily_logger_st("async_file_logger", "logs/async_log.txt");
// auto flush if the log severity is error or higher
async_file->flush_on(spd::level::err);
for (int i = 0; i < 100; ++i) for (int i = 0; i < 100; ++i)
async_file->info("Async message #{}", i); async_file->info("Async message #{}", i);
} }

View File

@ -1,88 +1,89 @@
// //
// Copyright(c) 2015 Gabi Melman. // Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#pragma once #pragma once
// Async Logger implementation // Async Logger implementation
// Use an async_sink (queue per logger) to perform the logging in a worker thread // Use an async_sink (queue per logger) to perform the logging in a worker thread
#include <spdlog/details/async_log_helper.h> #include <spdlog/details/async_log_helper.h>
#include <spdlog/async_logger.h> #include <spdlog/async_logger.h>
#include <string> #include <string>
#include <functional> #include <functional>
#include <chrono> #include <chrono>
#include <memory> #include <memory>
template<class It> template<class It>
inline spdlog::async_logger::async_logger(const std::string& logger_name, inline spdlog::async_logger::async_logger(const std::string& logger_name,
const It& begin, const It& begin,
const It& end, const It& end,
size_t queue_size, size_t queue_size,
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) :
logger(logger_name, begin, end), 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)) _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, inline spdlog::async_logger::async_logger(const std::string& logger_name,
sinks_init_list sinks, sinks_init_list sinks,
size_t queue_size, size_t queue_size,
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) :
async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {} async_logger(logger_name, sinks.begin(), sinks.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, inline spdlog::async_logger::async_logger(const std::string& logger_name,
sink_ptr single_sink, sink_ptr single_sink,
size_t queue_size, size_t queue_size,
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) :
async_logger(logger_name, async_logger(logger_name,
{ {
single_sink single_sink
}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {} }, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {}
inline void spdlog::async_logger::flush() inline void spdlog::async_logger::flush()
{ {
_async_log_helper->flush();
_async_log_helper->flush(); }
}
inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter)
inline void spdlog::async_logger::_set_formatter(spdlog::formatter_ptr msg_formatter) {
{ _formatter = msg_formatter;
_formatter = msg_formatter; _async_log_helper->set_formatter(_formatter);
_async_log_helper->set_formatter(_formatter); }
}
inline void spdlog::async_logger::_set_pattern(const std::string& pattern)
inline void spdlog::async_logger::_set_pattern(const std::string& pattern) {
{ _formatter = std::make_shared<pattern_formatter>(pattern);
_formatter = std::make_shared<pattern_formatter>(pattern); _async_log_helper->set_formatter(_formatter);
_async_log_helper->set_formatter(_formatter); }
}
inline void spdlog::async_logger::_sink_it(details::log_msg& msg)
inline void spdlog::async_logger::_sink_it(details::log_msg& msg) {
{ try
try {
{ _async_log_helper->log(msg);
_async_log_helper->log(msg); if (_should_flush_on(msg))
} flush();
catch (const std::exception &ex) }
{ catch (const std::exception &ex)
_err_handler(ex.what()); {
} _err_handler(ex.what());
catch (...) }
{ catch (...)
_err_handler("Unknown exception"); {
} _err_handler("Unknown exception");
} }
}

View File

@ -1,284 +1,288 @@
// //
// Copyright(c) 2015 Gabi Melman. // Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#pragma once #pragma once
#include <spdlog/logger.h> #include <spdlog/logger.h>
#include <spdlog/sinks/stdout_sinks.h> #include <spdlog/sinks/stdout_sinks.h>
#include <memory> #include <memory>
#include <string> #include <string>
// create logger with given name, sinks and the default pattern formatter // create logger with given name, sinks and the default pattern formatter
// all other ctors will call this one // all other ctors will call this one
template<class It> template<class It>
inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end): inline spdlog::logger::logger(const std::string& logger_name, const It& begin, const It& end):
_name(logger_name), _name(logger_name),
_sinks(begin, end), _sinks(begin, end),
_formatter(std::make_shared<pattern_formatter>("%+")) _formatter(std::make_shared<pattern_formatter>("%+"))
{ {
_level = level::info; _level = level::info;
_flush_level = level::off; _flush_level = level::off;
_last_err_time = 0; _last_err_time = 0;
_err_handler = [this](const std::string &msg) _err_handler = [this](const std::string &msg)
{ {
this->_default_err_handler(msg); this->_default_err_handler(msg);
}; };
} }
// ctor with sinks as init list // ctor with sinks as init list
inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list): inline spdlog::logger::logger(const std::string& logger_name, sinks_init_list sinks_list):
logger(logger_name, sinks_list.begin(), sinks_list.end()) logger(logger_name, sinks_list.begin(), sinks_list.end())
{} {}
// ctor with single sink // ctor with single sink
inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink): inline spdlog::logger::logger(const std::string& logger_name, spdlog::sink_ptr single_sink):
logger(logger_name, logger(logger_name,
{ {
single_sink single_sink
}) })
{} {}
inline spdlog::logger::~logger() = default; inline spdlog::logger::~logger() = default;
inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter) inline void spdlog::logger::set_formatter(spdlog::formatter_ptr msg_formatter)
{ {
_set_formatter(msg_formatter); _set_formatter(msg_formatter);
} }
inline void spdlog::logger::set_pattern(const std::string& pattern) inline void spdlog::logger::set_pattern(const std::string& pattern)
{ {
_set_pattern(pattern); _set_pattern(pattern);
} }
template <typename... Args> template <typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args) inline void spdlog::logger::log(level::level_enum lvl, const char* fmt, const Args&... args)
{ {
if (!should_log(lvl)) return; if (!should_log(lvl)) return;
try try
{ {
details::log_msg log_msg(&_name, lvl); details::log_msg log_msg(&_name, lvl);
log_msg.raw.write(fmt, args...); log_msg.raw.write(fmt, args...);
_sink_it(log_msg); _sink_it(log_msg);
} }
catch (const std::exception &ex) catch (const std::exception &ex)
{ {
_err_handler(ex.what()); _err_handler(ex.what());
} }
catch (...) catch (...)
{ {
_err_handler("Unknown exception"); _err_handler("Unknown exception");
} }
} }
template <typename... Args> template <typename... Args>
inline void spdlog::logger::log(level::level_enum lvl, const char* msg) inline void spdlog::logger::log(level::level_enum lvl, const char* msg)
{ {
if (!should_log(lvl)) return; if (!should_log(lvl)) return;
try try
{ {
details::log_msg log_msg(&_name, lvl); details::log_msg log_msg(&_name, lvl);
log_msg.raw << msg; log_msg.raw << msg;
_sink_it(log_msg); _sink_it(log_msg);
} }
catch (const std::exception &ex) catch (const std::exception &ex)
{ {
_err_handler(ex.what()); _err_handler(ex.what());
} }
catch (...) catch (...)
{ {
_err_handler("Unknown exception"); _err_handler("Unknown exception");
} }
} }
template<typename T> template<typename T>
inline void spdlog::logger::log(level::level_enum lvl, const T& msg) inline void spdlog::logger::log(level::level_enum lvl, const T& msg)
{ {
if (!should_log(lvl)) return; if (!should_log(lvl)) return;
try try
{ {
details::log_msg log_msg(&_name, lvl); details::log_msg log_msg(&_name, lvl);
log_msg.raw << msg; log_msg.raw << msg;
_sink_it(log_msg); _sink_it(log_msg);
} }
catch (const std::exception &ex) catch (const std::exception &ex)
{ {
_err_handler(ex.what()); _err_handler(ex.what());
} }
catch (...) catch (...)
{ {
_err_handler("Unknown exception"); _err_handler("Unknown exception");
} }
} }
template <typename... Args> template <typename... Args>
inline void spdlog::logger::trace(const char* fmt, const Args&... args) inline void spdlog::logger::trace(const char* fmt, const Args&... args)
{ {
log(level::trace, fmt, args...); log(level::trace, fmt, args...);
} }
template <typename... Args> template <typename... Args>
inline void spdlog::logger::debug(const char* fmt, const Args&... args) inline void spdlog::logger::debug(const char* fmt, const Args&... args)
{ {
log(level::debug, fmt, args...); log(level::debug, fmt, args...);
} }
template <typename... Args> template <typename... Args>
inline void spdlog::logger::info(const char* fmt, const Args&... args) inline void spdlog::logger::info(const char* fmt, const Args&... args)
{ {
log(level::info, fmt, args...); log(level::info, fmt, args...);
} }
template <typename... Args> template <typename... Args>
inline void spdlog::logger::warn(const char* fmt, const Args&... args) inline void spdlog::logger::warn(const char* fmt, const Args&... args)
{ {
log(level::warn, fmt, args...); log(level::warn, fmt, args...);
} }
template <typename... Args> template <typename... Args>
inline void spdlog::logger::error(const char* fmt, const Args&... args) inline void spdlog::logger::error(const char* fmt, const Args&... args)
{ {
log(level::err, fmt, args...); log(level::err, fmt, args...);
} }
template <typename... Args> template <typename... Args>
inline void spdlog::logger::critical(const char* fmt, const Args&... args) inline void spdlog::logger::critical(const char* fmt, const Args&... args)
{ {
log(level::critical, fmt, args...); log(level::critical, fmt, args...);
} }
template<typename T> template<typename T>
inline void spdlog::logger::trace(const T& msg) inline void spdlog::logger::trace(const T& msg)
{ {
log(level::trace, msg); log(level::trace, msg);
} }
template<typename T> template<typename T>
inline void spdlog::logger::debug(const T& msg) inline void spdlog::logger::debug(const T& msg)
{ {
log(level::debug, msg); log(level::debug, msg);
} }
template<typename T> template<typename T>
inline void spdlog::logger::info(const T& msg) inline void spdlog::logger::info(const T& msg)
{ {
log(level::info, msg); log(level::info, msg);
} }
template<typename T> template<typename T>
inline void spdlog::logger::warn(const T& msg) inline void spdlog::logger::warn(const T& msg)
{ {
log(level::warn, msg); log(level::warn, msg);
} }
template<typename T> template<typename T>
inline void spdlog::logger::error(const T& msg) inline void spdlog::logger::error(const T& msg)
{ {
log(level::err, msg); log(level::err, msg);
} }
template<typename T> template<typename T>
inline void spdlog::logger::critical(const T& msg) inline void spdlog::logger::critical(const T& msg)
{ {
log(level::critical, msg); log(level::critical, msg);
} }
// //
// name and level // name and level
// //
inline const std::string& spdlog::logger::name() const inline const std::string& spdlog::logger::name() const
{ {
return _name; return _name;
} }
inline void spdlog::logger::set_level(spdlog::level::level_enum log_level) inline void spdlog::logger::set_level(spdlog::level::level_enum log_level)
{ {
_level.store(log_level); _level.store(log_level);
} }
inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler) inline void spdlog::logger::set_error_handler(spdlog::log_err_handler err_handler)
{ {
_err_handler = err_handler; _err_handler = err_handler;
} }
inline spdlog::log_err_handler spdlog::logger::error_handler() inline spdlog::log_err_handler spdlog::logger::error_handler()
{ {
return _err_handler; return _err_handler;
} }
inline void spdlog::logger::flush_on(level::level_enum log_level) inline void spdlog::logger::flush_on(level::level_enum log_level)
{ {
_flush_level.store(log_level); _flush_level.store(log_level);
} }
inline spdlog::level::level_enum spdlog::logger::level() const inline spdlog::level::level_enum spdlog::logger::level() const
{ {
return static_cast<spdlog::level::level_enum>(_level.load(std::memory_order_relaxed)); 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 inline bool spdlog::logger::should_log(spdlog::level::level_enum msg_level) const
{ {
return msg_level >= _level.load(std::memory_order_relaxed); 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 // 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) inline void spdlog::logger::_sink_it(details::log_msg& msg)
{ {
_formatter->format(msg);
_formatter->format(msg); for (auto &sink : _sinks)
for (auto &sink : _sinks) sink->log(msg);
sink->log(msg);
if(_should_flush_on(msg))
const auto flush_level = _flush_level.load(std::memory_order_relaxed); flush();
if (msg.level >= flush_level) }
flush();
} inline void spdlog::logger::_set_pattern(const std::string& pattern)
{
inline void spdlog::logger::_set_pattern(const std::string& pattern) _formatter = std::make_shared<pattern_formatter>(pattern);
{ }
_formatter = std::make_shared<pattern_formatter>(pattern); inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter)
} {
inline void spdlog::logger::_set_formatter(formatter_ptr msg_formatter) _formatter = msg_formatter;
{ }
_formatter = msg_formatter;
} inline void spdlog::logger::flush()
{
inline void spdlog::logger::flush() for (auto& sink : _sinks)
{ sink->flush();
for (auto& sink : _sinks) }
sink->flush();
} inline void spdlog::logger::_default_err_handler(const std::string &msg)
{
inline void spdlog::logger::_default_err_handler(const std::string &msg) auto now = time(nullptr);
{ if (now - _last_err_time < 60)
auto now = time(nullptr); return;
if (now - _last_err_time < 60) auto tm_time = details::os::localtime(now);
return; char date_buf[100];
auto tm_time = details::os::localtime(now); std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
char date_buf[100]; details::log_msg err_msg;
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time); err_msg.formatted.write("[*** LOG ERROR ***] [{}] [{}] [{}]{}", name(), msg, date_buf, details::os::eol);
details::log_msg err_msg; sinks::stderr_sink_mt::instance()->log(err_msg);
err_msg.formatted.write("[*** LOG ERROR ***] [{}] [{}] [{}]{}", name(), msg, date_buf, details::os::eol); _last_err_time = now;
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);
}

View File

@ -1,90 +1,94 @@
// //
// Copyright(c) 2015 Gabi Melman. // Copyright(c) 2015 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT) // Distributed under the MIT License (http://opensource.org/licenses/MIT)
// //
#pragma once #pragma once
// Thread safe logger // Thread safe logger
// Has name, log level, vector of std::shared sink pointers and formatter // Has name, log level, vector of std::shared sink pointers and formatter
// Upon each log write the logger: // Upon each log write the logger:
// 1. Checks if its log level is enough to log the message // 1. Checks if its log level is enough to log the message
// 2. Format the message using the formatter function // 2. Format the message using the formatter function
// 3. Pass the formatted message to its sinks to performa the actual logging // 3. Pass the formatted message to its sinks to performa the actual logging
#include <spdlog/sinks/base_sink.h> #include <spdlog/sinks/base_sink.h>
#include <spdlog/common.h> #include <spdlog/common.h>
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <string> #include <string>
namespace spdlog namespace spdlog
{ {
class logger class logger
{ {
public: public:
logger(const std::string& logger_name, sink_ptr single_sink); logger(const std::string& logger_name, sink_ptr single_sink);
logger(const std::string& name, sinks_init_list); logger(const std::string& name, sinks_init_list);
template<class It> template<class It>
logger(const std::string& name, const It& begin, const It& end); logger(const std::string& name, const It& begin, const It& end);
virtual ~logger(); virtual ~logger();
logger(const logger&) = delete; logger(const logger&) = delete;
logger& operator=(const logger&) = delete; logger& operator=(const logger&) = delete;
template <typename... Args> void log(level::level_enum lvl, const char* fmt, const Args&... args); template <typename... Args> void log(level::level_enum lvl, const char* fmt, const Args&... args);
template <typename... Args> void log(level::level_enum lvl, const char* msg); template <typename... Args> void log(level::level_enum lvl, const char* msg);
template <typename... Args> void trace(const char* fmt, const Args&... args); template <typename... Args> void trace(const char* fmt, const Args&... args);
template <typename... Args> void debug(const char* fmt, const Args&... args); template <typename... Args> void debug(const char* fmt, const Args&... args);
template <typename... Args> void info(const char* fmt, const Args&... args); template <typename... Args> void info(const char* fmt, const Args&... args);
template <typename... Args> void warn(const char* fmt, const Args&... args); template <typename... Args> void warn(const char* fmt, const Args&... args);
template <typename... Args> void error(const char* fmt, const Args&... args); template <typename... Args> void error(const char* fmt, const Args&... args);
template <typename... Args> void critical(const char* fmt, const Args&... args); template <typename... Args> void critical(const char* fmt, const Args&... args);
template <typename T> void log(level::level_enum lvl, const T&); template <typename T> void log(level::level_enum lvl, const T&);
template <typename T> void trace(const T&); template <typename T> void trace(const T&);
template <typename T> void debug(const T&); template <typename T> void debug(const T&);
template <typename T> void info(const T&); template <typename T> void info(const T&);
template <typename T> void warn(const T&); template <typename T> void warn(const T&);
template <typename T> void error(const T&); template <typename T> void error(const T&);
template <typename T> void critical(const T&); template <typename T> void critical(const T&);
bool should_log(level::level_enum) const; bool should_log(level::level_enum) const;
void set_level(level::level_enum); void set_level(level::level_enum);
level::level_enum level() const; level::level_enum level() const;
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);
// error handler // error handler
void set_error_handler(log_err_handler); void set_error_handler(log_err_handler);
log_err_handler error_handler(); log_err_handler error_handler();
// 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);
virtual void flush();
virtual void flush();
protected:
virtual void _sink_it(details::log_msg&); protected:
virtual void _set_pattern(const std::string&); virtual void _sink_it(details::log_msg&);
virtual void _set_formatter(formatter_ptr); virtual void _set_pattern(const std::string&);
virtual void _set_formatter(formatter_ptr);
// default error handler: print the error to stderr with the max rate of 1 message/minute
virtual void _default_err_handler(const std::string &msg); // default error handler: print the error to stderr with the max rate of 1 message/minute
virtual void _default_err_handler(const std::string &msg);
const std::string _name;
std::vector<sink_ptr> _sinks; // return true if the given message level should trigger a flush
formatter_ptr _formatter; bool _should_flush_on(const details::log_msg&);
spdlog::level_t _level;
spdlog::level_t _flush_level; const std::string _name;
log_err_handler _err_handler; std::vector<sink_ptr> _sinks;
std::atomic<time_t> _last_err_time; formatter_ptr _formatter;
}; spdlog::level_t _level;
} spdlog::level_t _flush_level;
log_err_handler _err_handler;
#include <spdlog/details/logger_impl.h> std::atomic<time_t> _last_err_time;
};
}
#include <spdlog/details/logger_impl.h>