Update to spdlog 1.3.1

This commit is contained in:
Sebastian Messmer 2019-06-01 21:05:26 -07:00
parent e927a001e9
commit f3b71f058a
69 changed files with 12961 additions and 9506 deletions

View File

@ -2,13 +2,11 @@
#ifndef MESSMER_CPPUTILS_LOGGING_LOGGER_H
#define MESSMER_CPPUTILS_LOGGING_LOGGER_H
#if !defined(_MSC_VER)
#define SPDLOG_ENABLE_SYSLOG
#endif
#include <spdlog/spdlog.h>
#include "../macros.h"
#include <spdlog/sinks/stdout_sinks.h>
namespace cpputils {
namespace logging {
class Logger final {

View File

@ -5,9 +5,12 @@
#include "Logger.h"
#include <stdexcept>
#include <spdlog/fmt/ostr.h>
#include <spdlog/sinks/basic_file_sink.h>
#if defined(_MSC_VER)
#include <spdlog/sinks/msvc_sink.h>
#else
#include <spdlog/sinks/syslog_sink.h>
#endif
namespace cpputils {
@ -75,7 +78,7 @@ namespace cpputils {
#if defined(_MSC_VER)
return spdlog::create<spdlog::sinks::msvc_sink_mt>(name);
#else
return spdlog::syslog_logger(name, name, LOG_PID);
return spdlog::syslog_logger_mt(name, name, LOG_PID);
#endif
}
}

View File

@ -315,7 +315,7 @@ namespace cryfs_cli {
//TODO Test that --logfile parameter works. Should be: file if specified, otherwise stderr if foreground, else syslog.
if (options.logFile() != none) {
cpputils::logging::setLogger(
spdlog::create<spdlog::sinks::simple_file_sink<std::mutex>>("cryfs", options.logFile()->string()));
spdlog::create<spdlog::sinks::basic_file_sink_mt>("cryfs", options.logFile()->string()));
} else if (options.foreground()) {
cpputils::logging::setLogger(spdlog::stderr_logger_mt("cryfs"));
} else {

View File

@ -29,9 +29,9 @@ TYPED_TEST_P(FsppDirTest_Timestamps, createAndOpenFile_inRootDir) {
TYPED_TEST_P(FsppDirTest_Timestamps, createAndOpenFile_TimestampsOfCreatedFile) {
auto dir = this->CreateDir("/mydir");
timespec lowerBound = now();
timespec lowerBound = cpputils::time::now();
dir->createAndOpenFile("childname", fspp::mode_t().addFileFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
timespec upperBound = now();
timespec upperBound = cpputils::time::now();
auto child = this->Load("/mydir/childname");
this->EXPECT_ACCESS_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child);
this->EXPECT_MODIFICATION_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child);
@ -57,9 +57,9 @@ TYPED_TEST_P(FsppDirTest_Timestamps, createDir_inRootDir) {
TYPED_TEST_P(FsppDirTest_Timestamps, createDir_TimestampsOfCreatedDir) {
auto dir = this->CreateDir("/mydir");
timespec lowerBound = now();
timespec lowerBound = cpputils::time::now();
dir->createDir("childname", fspp::mode_t().addDirFlag(), fspp::uid_t(1000), fspp::gid_t(1000));
timespec upperBound = now();
timespec upperBound = cpputils::time::now();
auto child = this->Load("/mydir/childname");
this->EXPECT_ACCESS_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child);
this->EXPECT_MODIFICATION_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child);
@ -85,9 +85,9 @@ TYPED_TEST_P(FsppDirTest_Timestamps, createSymlink_inRootDir) {
TYPED_TEST_P(FsppDirTest_Timestamps, createSymlink_TimestampsOfCreatedSymlink) {
auto dir = this->CreateDir("/mydir");
timespec lowerBound = now();
timespec lowerBound = cpputils::time::now();
dir->createSymlink("childname", "/target", fspp::uid_t(1000), fspp::gid_t(1000));
timespec upperBound = now();
timespec upperBound = cpputils::time::now();
auto child = this->Load("/mydir/childname");
this->EXPECT_ACCESS_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child);
this->EXPECT_MODIFICATION_TIMESTAMP_BETWEEN (lowerBound, upperBound, *child);

View File

@ -7,17 +7,15 @@
#include "testutils/TimestampTestUtils.h"
#include <cpp-utils/system/stat.h>
using namespace cpputils::time;
using std::function;
template<class ConcreteFileSystemTestFixture>
class FsppNodeTest_Timestamps: public FsppNodeTest<ConcreteFileSystemTestFixture>, public TimestampTestUtils<ConcreteFileSystemTestFixture> {
public:
void Test_Create() {
timespec lowerBound = now();
timespec lowerBound = cpputils::time::now();
auto node = this->CreateNode("/mynode");
timespec upperBound = now();
timespec upperBound = cpputils::time::now();
this->EXPECT_ACCESS_TIMESTAMP_BETWEEN (lowerBound, upperBound, *node);
this->EXPECT_MODIFICATION_TIMESTAMP_BETWEEN (lowerBound, upperBound, *node);
this->EXPECT_METADATACHANGE_TIMESTAMP_BETWEEN(lowerBound, upperBound, *node);

View File

@ -19,9 +19,14 @@
#include <set>
using namespace boost;
using namespace boost::filesystem;
using namespace std;
using std::endl;
using std::cout;
using std::set;
using std::flush;
using std::vector;
using boost::none;
using boost::filesystem::path;
using namespace cryfs;
using namespace cpputils;
using namespace blockstore;

View File

@ -11,8 +11,7 @@ class MockLogger final {
public:
MockLogger():
_capturedLogData(),
_sink(std::make_shared<spdlog::sinks::ostream_sink<std::mutex>>(_capturedLogData, true)),
_logger(spdlog::create("MockLogger", {_sink})) {
_logger(spdlog::create<spdlog::sinks::ostream_sink_mt>("MockLogger", _capturedLogData, true)) {
}
~MockLogger() {
@ -28,7 +27,6 @@ public:
}
private:
std::ostringstream _capturedLogData;
std::shared_ptr<spdlog::sinks::ostream_sink<std::mutex>> _sink;
std::shared_ptr<spdlog::logger> _logger;
};

2
vendor/README vendored
View File

@ -1,6 +1,6 @@
This directory contains external projects, taken from the following locations:
googletest: https://github.com/google/googletest/tree/release-1.8.1
spdlog: https://github.com/gabime/spdlog/tree/v0.16.3/include/spdlog
spdlog: https://github.com/gabime/spdlog/tree/v1.3.1/include/spdlog
cryptopp: https://github.com/weidai11/cryptopp/tree/CRYPTOPP_8_1_0
- changed: added CMakeLists.txt and cryptopp-config.cmake from https://github.com/noloader/cryptopp-cmake/tree/07a064d57d97477cb055f994a498f45425df0c1d
- changed: commented out line including winapifamily.h in CMakeLists.txt

87
vendor/spdlog/spdlog/async.h vendored Normal file
View File

@ -0,0 +1,87 @@
//
// Copyright(c) 2018 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#pragma once
//
// Async logging using global thread pool
// All loggers created here share same global thread pool.
// Each log message is pushed to a queue along withe a shared pointer to the
// logger.
// If a logger deleted while having pending messages in the queue, it's actual
// destruction will defer
// until all its messages are processed by the thread pool.
// This is because each message in the queue holds a shared_ptr to the
// originating logger.
#include "spdlog/async_logger.h"
#include "spdlog/details/registry.h"
#include "spdlog/details/thread_pool.h"
#include <memory>
#include <mutex>
namespace spdlog {
namespace details {
static const size_t default_async_q_size = 8192;
}
// async logger factory - creates async loggers backed with thread pool.
// if a global thread pool doesn't already exist, create it with default queue
// size of 8192 items and single thread.
template<async_overflow_policy OverflowPolicy = async_overflow_policy::block>
struct async_factory_impl
{
template<typename Sink, typename... SinkArgs>
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&... args)
{
auto &registry_inst = details::registry::instance();
// create global thread pool if not already exists..
std::lock_guard<std::recursive_mutex> tp_lock(registry_inst.tp_mutex());
auto tp = registry_inst.get_tp();
if (tp == nullptr)
{
tp = std::make_shared<details::thread_pool>(details::default_async_q_size, 1);
registry_inst.set_tp(tp);
}
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy);
registry_inst.initialize_logger(new_logger);
return new_logger;
}
};
using async_factory = async_factory_impl<async_overflow_policy::block>;
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
template<typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&... sink_args)
{
return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
}
template<typename Sink, typename... SinkArgs>
inline std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&... sink_args)
{
return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
}
// set global thread pool.
inline void init_thread_pool(size_t q_size, size_t thread_count)
{
auto tp = std::make_shared<details::thread_pool>(q_size, thread_count);
details::registry::instance().set_tp(std::move(tp));
}
// get the global thread pool.
inline std::shared_ptr<spdlog::details::thread_pool> thread_pool()
{
return details::registry::instance().get_tp();
}
} // namespace spdlog

View File

@ -5,78 +5,69 @@
#pragma once
// Very fast asynchronous logger (millions of logs per second on an average desktop)
// Uses pre allocated lockfree queue for maximum throughput even under large number of threads.
// Very fast asynchronous logger (millions of logs per second on an average
// desktop)
// Uses pre allocated lockfree queue for maximum throughput even under large
// number of threads.
// Creates a single back thread to pop messages from the queue and log them.
//
// Upon each log write the logger:
// 1. Checks if its log level is enough to log the message
// 2. Push a new copy of the message to a queue (or block the caller until space is available in the queue)
// 2. Push a new copy of the message to a queue (or block the caller until
// space is available in the queue)
// 3. will throw spdlog_ex upon log exceptions
// Upon destruction, logs all remaining messages in the queue before destructing..
// Upon destruction, logs all remaining messages in the queue before
// destructing..
#include "common.h"
#include "logger.h"
#include "spdlog/common.h"
#include "spdlog/logger.h"
#include <chrono>
#include <functional>
#include <string>
#include <memory>
#include <string>
namespace spdlog
{
namespace spdlog {
namespace details
// Async overflow policy - block by default.
enum class async_overflow_policy
{
class async_log_helper;
block, // Block until message can be enqueued
overrun_oldest // Discard oldest message in the queue if full when trying to
// add new item.
};
namespace details {
class thread_pool;
}
class async_logger SPDLOG_FINAL :public logger
class async_logger final : public std::enable_shared_from_this<async_logger>, public logger
{
friend class details::thread_pool;
public:
template<class It>
async_logger(const std::string& name,
const It& begin,
const It& end,
size_t queue_size,
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);
template<typename It>
async_logger(std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp,
async_overflow_policy overflow_policy = async_overflow_policy::block);
async_logger(const std::string& logger_name,
sinks_init_list sinks,
size_t queue_size,
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);
async_logger(std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp,
async_overflow_policy overflow_policy = async_overflow_policy::block);
async_logger(const std::string& logger_name,
sink_ptr single_sink,
size_t queue_size,
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);
async_logger(std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp,
async_overflow_policy overflow_policy = async_overflow_policy::block);
//Wait for the queue to be empty, and flush synchronously
//Warning: this can potentially last forever as we wait it to complete
void flush() override;
// Error handler
virtual void set_error_handler(log_err_handler) override;
virtual log_err_handler error_handler() override;
std::shared_ptr<logger> clone(std::string new_name) override;
protected:
void _sink_it(details::log_msg& msg) override;
void _set_formatter(spdlog::formatter_ptr msg_formatter) override;
void _set_pattern(const std::string& pattern, pattern_time_type pattern_time) override;
void sink_it_(details::log_msg &msg) override;
void flush_() override;
void backend_log_(const details::log_msg &incoming_log_msg);
void backend_flush_();
private:
std::unique_ptr<details::async_log_helper> _async_log_helper;
std::weak_ptr<details::thread_pool> thread_pool_;
async_overflow_policy overflow_policy_;
};
}
} // namespace spdlog
#include "details/async_logger_impl.h"

View File

@ -5,22 +5,29 @@
#pragma once
#include <string>
#include <initializer_list>
#include <chrono>
#include <memory>
#include <atomic>
#include <exception>
#include<functional>
#include "spdlog/tweakme.h"
#if defined(_WIN32) && defined(SPDLOG_WCHAR_FILENAMES)
#include <atomic>
#include <chrono>
#include <functional>
#include <initializer_list>
#include <memory>
#include <stdexcept>
#include <string>
#include <cstring>
#include <type_traits>
#include <unordered_map>
#if defined(SPDLOG_WCHAR_FILENAMES) || defined(SPDLOG_WCHAR_TO_UTF8_SUPPORT)
#include <codecvt>
#include <locale>
#endif
#include "details/null_mutex.h"
#include "spdlog/details/null_mutex.h"
//visual studio upto 2013 does not support noexcept nor constexpr
#include "spdlog/fmt/fmt.h"
// visual studio upto 2013 does not support noexcept nor constexpr
#if defined(_MSC_VER) && (_MSC_VER < 1900)
#define SPDLOG_NOEXCEPT throw()
#define SPDLOG_CONSTEXPR
@ -29,14 +36,7 @@
#define SPDLOG_CONSTEXPR constexpr
#endif
// final keyword support. On by default. See tweakme.h
#if defined(SPDLOG_NO_FINAL)
#define SPDLOG_FINAL
#else
#define SPDLOG_FINAL final
#endif
#if defined(__GNUC__) || defined(__clang__)
#if defined(__GNUC__) || defined(__clang__)
#define SPDLOG_DEPRECATED __attribute__((deprecated))
#elif defined(_MSC_VER)
#define SPDLOG_DEPRECATED __declspec(deprecated)
@ -44,71 +44,117 @@
#define SPDLOG_DEPRECATED
#endif
#include "fmt/fmt.h"
// disable thread local on msvc 2013
#ifndef SPDLOG_NO_TLS
#if (defined(_MSC_VER) && (_MSC_VER < 1900)) || defined(__cplusplus_winrt)
#define SPDLOG_NO_TLS 1
#endif
#endif
namespace spdlog
{
// Get the basename of __FILE__ (at compile time if possible)
#if FMT_HAS_FEATURE(__builtin_strrchr)
#define SPDLOG_STRRCHR(str, sep) __builtin_strrchr(str, sep)
#else
#define SPDLOG_STRRCHR(str, sep) strrchr(str, sep)
#endif //__builtin_strrchr not defined
#ifdef _WIN32
#define SPDLOG_FILE_BASENAME(file) SPDLOG_STRRCHR("\\" file, '\\') + 1
#else
#define SPDLOG_FILE_BASENAME(file) SPDLOG_STRRCHR("/" file, '/') + 1
#endif
#ifndef SPDLOG_FUNCTION
#define SPDLOG_FUNCTION __FUNCTION__
#endif
namespace spdlog {
class formatter;
namespace sinks
{
namespace sinks {
class sink;
}
using log_clock = std::chrono::system_clock;
using sink_ptr = std::shared_ptr < sinks::sink >;
using sinks_init_list = std::initializer_list < sink_ptr >;
using formatter_ptr = std::shared_ptr<spdlog::formatter>;
using sink_ptr = std::shared_ptr<sinks::sink>;
using sinks_init_list = std::initializer_list<sink_ptr>;
using log_err_handler = std::function<void(const std::string &err_msg)>;
// string_view type - either std::string_view or fmt::string_view (pre c++17)
#if defined(FMT_USE_STD_STRING_VIEW)
using string_view_t = std::string_view;
#else
using string_view_t = fmt::string_view;
#endif
#if defined(SPDLOG_NO_ATOMIC_LEVELS)
using level_t = details::null_atomic_int;
#else
using level_t = std::atomic<int>;
#endif
using log_err_handler = std::function<void(const std::string &err_msg)>;
#define SPDLOG_LEVEL_TRACE 0
#define SPDLOG_LEVEL_DEBUG 1
#define SPDLOG_LEVEL_INFO 2
#define SPDLOG_LEVEL_WARN 3
#define SPDLOG_LEVEL_ERROR 4
#define SPDLOG_LEVEL_CRITICAL 5
#define SPDLOG_LEVEL_OFF 6
//Log level enum
namespace level
#if !defined(SPDLOG_ACTIVE_LEVEL)
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
#endif
// Log level enum
namespace level {
enum level_enum
{
typedef enum
{
trace = 0,
debug = 1,
info = 2,
warn = 3,
err = 4,
critical = 5,
off = 6
} level_enum;
trace = SPDLOG_LEVEL_TRACE,
debug = SPDLOG_LEVEL_DEBUG,
info = SPDLOG_LEVEL_INFO,
warn = SPDLOG_LEVEL_WARN,
err = SPDLOG_LEVEL_ERROR,
critical = SPDLOG_LEVEL_CRITICAL,
off = SPDLOG_LEVEL_OFF,
};
#if !defined(SPDLOG_LEVEL_NAMES)
#define SPDLOG_LEVEL_NAMES { "trace", "debug", "info", "warning", "error", "critical", "off" }
#define SPDLOG_LEVEL_NAMES \
{ \
"trace", "debug", "info", "warning", "error", "critical", "off" \
}
#endif
static const char* level_names[] SPDLOG_LEVEL_NAMES;
static const char* short_level_names[] { "T", "D", "I", "W", "E", "C", "O" };
static string_view_t level_string_views[] SPDLOG_LEVEL_NAMES;
static const char *short_level_names[]{"T", "D", "I", "W", "E", "C", "O"};
inline const char* to_str(spdlog::level::level_enum l)
inline string_view_t &to_string_view(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
{
return level_names[l];
return level_string_views[l];
}
inline const char* to_short_str(spdlog::level::level_enum l)
inline const char *to_short_c_str(spdlog::level::level_enum l) SPDLOG_NOEXCEPT
{
return short_level_names[l];
}
} //level
//
// Async overflow policy - block by default.
//
enum class async_overflow_policy
inline spdlog::level::level_enum from_str(const std::string &name) SPDLOG_NOEXCEPT
{
block_retry, // Block / yield / sleep until message can be enqueued
discard_log_msg // Discard the message it enqueue fails
};
int level = 0;
for (const auto &level_str : level_string_views)
{
if (level_str == name)
{
return static_cast<level::level_enum>(level);
}
level++;
}
return level::off;
}
using level_hasher = std::hash<int>;
} // namespace level
//
// Pattern time - specific time getting to use for pattern_formatter.
@ -123,29 +169,28 @@ enum class pattern_time_type
//
// Log exception
//
namespace details
{
namespace os
{
std::string errno_str(int err_num);
}
}
class spdlog_ex: public std::exception
class spdlog_ex : public std::exception
{
public:
spdlog_ex(const std::string& msg):_msg(msg)
{}
spdlog_ex(const std::string& msg, int last_errno)
explicit spdlog_ex(std::string msg)
: msg_(std::move(msg))
{
_msg = msg + ": " + details::os::errno_str(last_errno);
}
const char* what() const SPDLOG_NOEXCEPT override
{
return _msg.c_str();
}
private:
std::string _msg;
spdlog_ex(const std::string &msg, int last_errno)
{
fmt::memory_buffer outbuf;
fmt::format_system_error(outbuf, last_errno, msg);
msg_ = fmt::to_string(outbuf);
}
const char *what() const SPDLOG_NOEXCEPT override
{
return msg_.c_str();
}
private:
std::string msg_;
};
//
@ -157,5 +202,42 @@ using filename_t = std::wstring;
using filename_t = std::string;
#endif
struct source_loc
{
SPDLOG_CONSTEXPR source_loc()
: filename{""}
, line{0}
, funcname{""}
{
}
SPDLOG_CONSTEXPR source_loc(const char *filename, int line, const char *funcname)
: filename{filename}
, line{static_cast<uint32_t>(line)}
, funcname{funcname}
{
}
} //spdlog
SPDLOG_CONSTEXPR bool empty() const SPDLOG_NOEXCEPT
{
return line == 0;
}
const char *filename;
uint32_t line;
const char *funcname;
};
namespace details {
// make_unique support for pre c++14
#if __cplusplus >= 201402L // C++14 and beyond
using std::make_unique;
#else
template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args &&... args)
{
static_assert(!std::is_array<T>::value, "arrays not supported");
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}
#endif
} // namespace details
} // namespace spdlog

View File

@ -1,398 +0,0 @@
//
// 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 "../common.h"
#include "../sinks/sink.h"
#include "../details/mpmc_bounded_q.h"
#include "../details/log_msg.h"
#include "../details/os.h"
#include "../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 when queue is empty
}
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 exeption in async logger worker loop.");
}
}
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 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 details::os::sleep_for_millis(20);
// sleep for 500 ms
return details::os::sleep_for_millis(500);
}
// 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.is_empty())
{
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;
}

View File

@ -5,103 +5,106 @@
#pragma once
// Async Logger implementation
// Use an async_sink (queue per logger) to perform the logging in a worker thread
// async logger implementation
// uses a thread pool to perform the actual logging
#include "../details/async_log_helper.h"
#include "../async_logger.h"
#include "spdlog/details/thread_pool.h"
#include <string>
#include <functional>
#include <chrono>
#include <memory>
#include <string>
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))
template<typename It>
inline spdlog::async_logger::async_logger(
std::string logger_name, It begin, It end, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
: logger(std::move(logger_name), begin, end)
, thread_pool_(std::move(tp))
, overflow_policy_(overflow_policy)
{
}
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,
inline spdlog::async_logger::async_logger(
std::string logger_name, sinks_init_list sinks_list, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
: async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), std::move(tp), overflow_policy)
{
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)
inline spdlog::async_logger::async_logger(
std::string logger_name, sink_ptr single_sink, std::weak_ptr<details::thread_pool> tp, async_overflow_policy overflow_policy)
: async_logger(std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy)
{
_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)
// send the log message to the thread pool
inline void spdlog::async_logger::sink_it_(details::log_msg &msg)
{
_formatter = msg_formatter;
_async_log_helper->set_formatter(_formatter);
#if defined(SPDLOG_ENABLE_MESSAGE_COUNTER)
incr_msg_counter_(msg);
#endif
if (auto pool_ptr = thread_pool_.lock())
{
pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
}
else
{
throw spdlog_ex("async log: thread pool doesn't exist anymore");
}
}
inline void spdlog::async_logger::_set_pattern(const std::string& pattern, pattern_time_type pattern_time)
// send flush request to the thread pool
inline void spdlog::async_logger::flush_()
{
_formatter = std::make_shared<pattern_formatter>(pattern, pattern_time);
_async_log_helper->set_formatter(_formatter);
if (auto pool_ptr = thread_pool_.lock())
{
pool_ptr->post_flush(shared_from_this(), overflow_policy_);
}
else
{
throw spdlog_ex("async flush: thread pool doesn't exist anymore");
}
}
inline void spdlog::async_logger::_sink_it(details::log_msg& msg)
//
// backend functions - called from the thread pool to do the actual job
//
inline void spdlog::async_logger::backend_log_(const details::log_msg &incoming_log_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 in logger " + _name);
throw;
for (auto &s : sinks_)
{
if (s->should_log(incoming_log_msg.level))
{
s->log(incoming_log_msg);
}
}
}
SPDLOG_CATCH_AND_HANDLE
if (should_flush_(incoming_log_msg))
{
backend_flush_();
}
}
inline void spdlog::async_logger::backend_flush_()
{
try
{
for (auto &sink : sinks_)
{
sink->flush();
}
}
SPDLOG_CATCH_AND_HANDLE
}
inline std::shared_ptr<spdlog::logger> spdlog::async_logger::clone(std::string new_name)
{
auto cloned = std::make_shared<spdlog::async_logger>(std::move(new_name), sinks_.begin(), sinks_.end(), thread_pool_, overflow_policy_);
cloned->set_level(this->level());
cloned->flush_on(this->flush_level());
cloned->set_error_handler(this->error_handler());
return std::move(cloned);
}

View File

@ -0,0 +1,72 @@
//
// Copyright(c) 2018 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
// cirucal q view of std::vector.
#pragma once
#include <vector>
namespace spdlog {
namespace details {
template<typename T>
class circular_q
{
public:
using item_type = T;
explicit circular_q(size_t max_items)
: max_items_(max_items + 1) // one item is reserved as marker for full q
, v_(max_items_)
{
}
// push back, overrun (oldest) item if no room left
void push_back(T &&item)
{
v_[tail_] = std::move(item);
tail_ = (tail_ + 1) % max_items_;
if (tail_ == head_) // overrun last item if full
{
head_ = (head_ + 1) % max_items_;
++overrun_counter_;
}
}
// Pop item from front.
// If there are no elements in the container, the behavior is undefined.
void pop_front(T &popped_item)
{
popped_item = std::move(v_[head_]);
head_ = (head_ + 1) % max_items_;
}
bool empty()
{
return tail_ == head_;
}
bool full()
{
// head is ahead of the tail by 1
return ((tail_ + 1) % max_items_) == head_;
}
size_t overrun_counter() const
{
return overrun_counter_;
}
private:
size_t max_items_;
typename std::vector<T>::size_type head_ = 0;
typename std::vector<T>::size_type tail_ = 0;
std::vector<T> v_;
size_t overrun_counter_ = 0;
};
} // namespace details
} // namespace spdlog

View File

@ -0,0 +1,74 @@
#pragma once
//
// Copyright(c) 2018 Gabi Melman.
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
//
#include "spdlog/details/null_mutex.h"
#include <cstdio>
#include <mutex>
#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>
#endif
namespace spdlog {
namespace details {
struct console_stdout
{
static std::FILE *stream()
{
return stdout;
}
#ifdef _WIN32
static HANDLE handle()
{
return ::GetStdHandle(STD_OUTPUT_HANDLE);
}
#endif
};
struct console_stderr
{
static std::FILE *stream()
{
return stderr;
}
#ifdef _WIN32
static HANDLE handle()
{
return ::GetStdHandle(STD_ERROR_HANDLE);
}
#endif
};
struct console_mutex
{
using mutex_t = std::mutex;
static mutex_t &mutex()
{
static mutex_t s_mutex;
return s_mutex;
}
};
struct console_nullmutex
{
using mutex_t = null_mutex;
static mutex_t &mutex()
{
static mutex_t s_mutex;
return s_mutex;
}
};
} // namespace details
} // namespace spdlog

View File

@ -5,24 +5,22 @@
#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)