async sink config param and tests
This commit is contained in:
parent
345af1c0a0
commit
a7298c5b8f
@ -8,10 +8,12 @@
|
|||||||
//
|
//
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <locale>
|
#include <locale>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "spdlog/sinks/async_sink.h"
|
#include "spdlog/sinks/async_sink.h"
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
#include "spdlog/sinks/basic_file_sink.h"
|
||||||
@ -24,20 +26,9 @@ using namespace spdlog::sinks;
|
|||||||
|
|
||||||
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
void bench_mt(int howmany, std::shared_ptr<spdlog::logger> log, int thread_count);
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable : 4996) // disable fopen warning under msvc
|
|
||||||
#endif // _MSC_VER
|
|
||||||
|
|
||||||
int count_lines(const char *filename) {
|
int count_lines(const char *filename) {
|
||||||
int counter = 0;
|
std::ifstream ifs(filename);
|
||||||
auto *infile = fopen(filename, "r");
|
return std::count(std::istreambuf_iterator(ifs), std::istreambuf_iterator<char>(), '\n');
|
||||||
int ch = 0;
|
|
||||||
while (EOF != (ch = getc(infile))) {
|
|
||||||
if ('\n' == ch) counter++;
|
|
||||||
}
|
|
||||||
fclose(infile);
|
|
||||||
return counter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void verify_file(const char *filename, int expected_count) {
|
void verify_file(const char *filename, int expected_count) {
|
||||||
@ -66,10 +57,10 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
spdlog::set_pattern("[%^%l%$] %v");
|
spdlog::set_pattern("[%^%l%$] %v");
|
||||||
// if (argc == 1) {
|
if (argc > 1 && (std::string(argv[1]) == "-h" || std::string(argv[1]) == "--help")) {
|
||||||
// spdlog::info("Usage: {} <message_count> <threads> <q_size> <iterations>", argv[0]);
|
spdlog::info("Usage: {} <message_count> <threads> <q_size> <iterations>", argv[0]);
|
||||||
// return 0;
|
return 0;
|
||||||
// }
|
}
|
||||||
|
|
||||||
if (argc > 1) howmany = atoi(argv[1]);
|
if (argc > 1) howmany = atoi(argv[1]);
|
||||||
if (argc > 2) threads = atoi(argv[2]);
|
if (argc > 2) threads = atoi(argv[2]);
|
||||||
@ -82,6 +73,11 @@ int main(int argc, char *argv[]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (argc > 4) iters = atoi(argv[4]);
|
if (argc > 4) iters = atoi(argv[4]);
|
||||||
|
// validate all argc values
|
||||||
|
if (howmany < 1 || threads < 1 || queue_size < 1 || iters < 1) {
|
||||||
|
spdlog::error("Invalid input values");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
auto slot_size = sizeof(details::async_log_msg);
|
auto slot_size = sizeof(details::async_log_msg);
|
||||||
spdlog::info("-------------------------------------------------");
|
spdlog::info("-------------------------------------------------");
|
||||||
@ -98,14 +94,17 @@ int main(int argc, char *argv[]) {
|
|||||||
spdlog::info("Queue Overflow Policy: block");
|
spdlog::info("Queue Overflow Policy: block");
|
||||||
spdlog::info("*********************************");
|
spdlog::info("*********************************");
|
||||||
for (int i = 0; i < iters; i++) {
|
for (int i = 0; i < iters; i++) {
|
||||||
auto async_sink = std::make_shared<async_sink_mt>(queue_size);
|
{
|
||||||
auto file_sink = std::make_shared<basic_file_sink_mt>(filename, true);
|
auto file_sink = std::make_shared<basic_file_sink_mt>(filename, true);
|
||||||
async_sink->add_sink(std::move(file_sink));
|
auto cfg = async_sink::config();
|
||||||
|
cfg.queue_size = queue_size;
|
||||||
|
cfg.sinks.push_back(std::move(file_sink));
|
||||||
|
auto async_sink = std::make_shared<sinks::async_sink>(cfg);
|
||||||
auto logger = std::make_shared<spdlog::logger>("async_logger", std::move(async_sink));
|
auto logger = std::make_shared<spdlog::logger>("async_logger", std::move(async_sink));
|
||||||
bench_mt(howmany, std::move(logger), threads);
|
bench_mt(howmany, std::move(logger), threads);
|
||||||
verify_file(filename, howmany);
|
|
||||||
}
|
}
|
||||||
|
//verify_file(filename, howmany); // in separate scope to ensure logger is destroyed and all logs were written
|
||||||
|
}
|
||||||
spdlog::info("");
|
spdlog::info("");
|
||||||
spdlog::info("*********************************");
|
spdlog::info("*********************************");
|
||||||
spdlog::info("Queue Overflow Policy: overrun");
|
spdlog::info("Queue Overflow Policy: overrun");
|
||||||
@ -113,10 +112,12 @@ int main(int argc, char *argv[]) {
|
|||||||
// do same test but discard the oldest if queue is full instead of blocking
|
// do same test but discard the oldest if queue is full instead of blocking
|
||||||
filename = "logs/basic_async-overrun.log";
|
filename = "logs/basic_async-overrun.log";
|
||||||
for (int i = 0; i < iters; i++) {
|
for (int i = 0; i < iters; i++) {
|
||||||
auto async_sink = std::make_shared<async_sink_mt>(queue_size);
|
async_sink::config cfg;
|
||||||
async_sink->set_overflow_policy(async_sink_mt::overflow_policy::overrun_oldest);
|
cfg.policy = async_sink::overflow_policy::overrun_oldest;
|
||||||
|
cfg.queue_size = queue_size;
|
||||||
auto file_sink = std::make_shared<basic_file_sink_mt>(filename, true);
|
auto file_sink = std::make_shared<basic_file_sink_mt>(filename, true);
|
||||||
async_sink->add_sink(std::move(file_sink));
|
cfg.sinks.push_back(std::move(file_sink));
|
||||||
|
auto async_sink = std::make_shared<sinks::async_sink>(cfg);
|
||||||
auto logger = std::make_shared<spdlog::logger>("async_logger", std::move(async_sink));
|
auto logger = std::make_shared<spdlog::logger>("async_logger", std::move(async_sink));
|
||||||
bench_mt(howmany, std::move(logger), threads);
|
bench_mt(howmany, std::move(logger), threads);
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,16 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "../details/async_log_msg.h"
|
#include "../details/async_log_msg.h"
|
||||||
#include "dist_sink.h"
|
#include "sink.h"
|
||||||
|
|
||||||
// async_sink is a sink that sends log messages to a dist_sink in a separate thread using a queue.
|
// async_sink is a sink that sends log messages to a dist_sink in a separate thread using a queue.
|
||||||
// The worker thread dequeues the messages and sends them to the dist_sink to perform the actual logging.
|
// The worker thread dequeues the messages and sends them to the dist_sink to perform the actual logging.
|
||||||
// The worker thread is terminated when the async_sink is destroyed.
|
// Once the sink is destroyed, the worker thread empties the queue and exits.
|
||||||
|
|
||||||
namespace spdlog::details { // forward declaration
|
namespace spdlog::details { // forward declaration
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -20,61 +22,62 @@ class mpmc_blocking_queue;
|
|||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace sinks {
|
namespace sinks {
|
||||||
|
|
||||||
template <typename Mutex>
|
class async_sink final : public sink {
|
||||||
class async_sink final : public dist_sink<Mutex> {
|
|
||||||
public:
|
public:
|
||||||
using base_t = dist_sink<Mutex>;
|
|
||||||
using async_log_msg = details::async_log_msg;
|
|
||||||
using queue_t = details::mpmc_blocking_queue<async_log_msg>;
|
|
||||||
enum { default_queue_size = 8192, max_queue_size = 1024 * 1024 * 10 };
|
|
||||||
|
|
||||||
// Async overflow policy - block by default.
|
|
||||||
enum class overflow_policy : std::uint8_t {
|
enum class overflow_policy : std::uint8_t {
|
||||||
block, // Block until the log message can be enqueued (default).
|
block, // Block until the log message can be enqueued (default).
|
||||||
overrun_oldest, // Overrun the oldest message in the queue if full.
|
overrun_oldest, // Overrun the oldest message in the queue if full.
|
||||||
discard_new // Discard the log message if the queue is full
|
discard_new // Discard the log message if the queue is full
|
||||||
};
|
};
|
||||||
|
|
||||||
async_sink(size_t queue_size, std::function<void()> on_thread_start, std::function<void()> on_thread_stop);
|
enum { default_queue_size = 8192, max_queue_size = 10 * 1024 * 1024 };
|
||||||
|
|
||||||
|
struct config {
|
||||||
|
size_t queue_size = default_queue_size;
|
||||||
|
overflow_policy policy = overflow_policy::block;
|
||||||
|
std::vector<std::shared_ptr<sink>> sinks;
|
||||||
|
std::function<void()> on_thread_start = nullptr;
|
||||||
|
std::function<void()> on_thread_stop = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
explicit async_sink(config async_config);
|
||||||
|
|
||||||
|
// create an async_sink with one backend sink
|
||||||
|
template <typename Sink, typename... SinkArgs>
|
||||||
|
static std::shared_ptr<async_sink> with_sink(SinkArgs &&...sink_args) {
|
||||||
|
config cfg{};
|
||||||
|
cfg.sinks.emplace_back(std::make_shared<Sink>(std::forward<SinkArgs>(sink_args)...));
|
||||||
|
return std::make_shared<async_sink>(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
~async_sink() override;
|
~async_sink() override;
|
||||||
|
|
||||||
async_sink();
|
// sink interface implementation
|
||||||
explicit async_sink(size_t queue_size);
|
void log(const details::log_msg &msg) override;
|
||||||
async_sink(std::function<void()> on_thread_start, std::function<void()> on_thread_stop);
|
void flush() override;
|
||||||
async_sink(const async_sink &) = delete;
|
void set_pattern(const std::string &pattern) override;
|
||||||
async_sink &operator=(const async_sink &) = delete;
|
void set_formatter(std::unique_ptr<formatter> sink_formatter) override;
|
||||||
async_sink(async_sink &&) = default;
|
|
||||||
async_sink &operator=(async_sink &&) = default;
|
|
||||||
|
|
||||||
void set_overflow_policy(overflow_policy policy);
|
|
||||||
[[nodiscard]] overflow_policy get_overflow_policy() const;
|
|
||||||
|
|
||||||
|
// async sink specific methods
|
||||||
[[nodiscard]] size_t get_overrun_counter() const;
|
[[nodiscard]] size_t get_overrun_counter() const;
|
||||||
void reset_overrun_counter() const;
|
void reset_overrun_counter() const;
|
||||||
|
|
||||||
[[nodiscard]] size_t get_discard_counter() const;
|
[[nodiscard]] size_t get_discard_counter() const;
|
||||||
void reset_discard_counter() const;
|
void reset_discard_counter() const;
|
||||||
|
[[nodiscard]] const config &get_config() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void sink_it_(const details::log_msg &msg) override;
|
using async_log_msg = details::async_log_msg;
|
||||||
void flush_() override;
|
using queue_t = details::mpmc_blocking_queue<async_log_msg>;
|
||||||
void send_message_(async_log_msg::type msg_type, const details::log_msg &msg);
|
|
||||||
void backend_loop_();
|
|
||||||
|
|
||||||
std::atomic<overflow_policy> overflow_policy_ = overflow_policy::block;
|
void send_message_(async_log_msg::type msg_type, const details::log_msg &msg) const;
|
||||||
|
void backend_loop_() const;
|
||||||
|
void backend_log_(const details::log_msg &msg) const;
|
||||||
|
void backend_flush_() const;
|
||||||
|
|
||||||
|
config config_;
|
||||||
std::unique_ptr<queue_t> q_;
|
std::unique_ptr<queue_t> q_;
|
||||||
std::thread worker_thread_;
|
std::thread worker_thread_;
|
||||||
};
|
};
|
||||||
|
|
||||||
using async_sink_mt = async_sink<std::mutex>;
|
|
||||||
using async_sink_st = async_sink<details::null_mutex>;
|
|
||||||
|
|
||||||
} // namespace sinks
|
} // namespace sinks
|
||||||
|
|
||||||
class logger;
|
|
||||||
template <typename... SinkArgs>
|
|
||||||
std::shared_ptr<logger> create_async(std::string logger_name, SinkArgs &&...sink_args) {
|
|
||||||
auto async_sink = std::make_shared<sinks::async_sink_mt>(std::forward<SinkArgs>(sink_args)...);
|
|
||||||
return std::make_shared<logger>(std::move(logger_name), std::move(async_sink));
|
|
||||||
}
|
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
@ -3,99 +3,71 @@
|
|||||||
|
|
||||||
#include "spdlog/sinks/async_sink.h"
|
#include "spdlog/sinks/async_sink.h"
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include "spdlog/details/mpmc_blocking_q.h"
|
|
||||||
#include "spdlog/common.h"
|
#include "spdlog/common.h"
|
||||||
|
#include "spdlog/details/mpmc_blocking_q.h"
|
||||||
#include "spdlog/pattern_formatter.h"
|
#include "spdlog/pattern_formatter.h"
|
||||||
|
#include "spdlog/spdlog.h"
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace sinks {
|
namespace sinks {
|
||||||
|
|
||||||
template <typename Mutex>
|
async_sink::async_sink(config async_config)
|
||||||
async_sink<Mutex>::async_sink(size_t queue_size, std::function<void()> on_thread_start, std::function<void()> on_thread_stop)
|
: config_(std::move(async_config)) {
|
||||||
: base_t() {
|
if (config_.queue_size == 0 || config_.queue_size > max_queue_size) {
|
||||||
if (queue_size == 0 || queue_size > max_queue_size) {
|
|
||||||
throw spdlog_ex("async_sink: invalid queue size");
|
throw spdlog_ex("async_sink: invalid queue size");
|
||||||
}
|
}
|
||||||
// printf("........... Allocating queue: slot: %zu X %zu bytes ====> %lld KB ..............\n",
|
q_ = std::make_unique<queue_t>(config_.queue_size);
|
||||||
// queue_size, sizeof(details::async_log_msg), (sizeof(details::async_log_msg) * queue_size)/1024);
|
worker_thread_ = std::thread([this] {
|
||||||
q_ = std::make_unique<queue_t>(queue_size);
|
if (config_.on_thread_start) config_.on_thread_start();
|
||||||
|
|
||||||
worker_thread_ = std::thread([this, on_thread_start, on_thread_stop] {
|
|
||||||
if (on_thread_start) on_thread_start();
|
|
||||||
this->backend_loop_();
|
this->backend_loop_();
|
||||||
if (on_thread_stop) on_thread_stop();
|
if (config_.on_thread_stop) config_.on_thread_stop();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Mutex>
|
async_sink::~async_sink() {
|
||||||
async_sink<Mutex>::~async_sink() {
|
|
||||||
try {
|
try {
|
||||||
q_->enqueue(async_log_msg(async_log_msg::type::terminate));
|
q_->enqueue(async_log_msg(async_log_msg::type::terminate));
|
||||||
worker_thread_.join();
|
worker_thread_.join();
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
|
printf("Exception in ~async_sink()\n");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Mutex>
|
void async_sink::log(const details::log_msg &msg) { send_message_(async_log_msg::type::log, msg); }
|
||||||
async_sink<Mutex>::async_sink()
|
|
||||||
: async_sink(default_queue_size, nullptr, nullptr) {}
|
|
||||||
|
|
||||||
template <typename Mutex>
|
void async_sink::flush() { send_message_(async_log_msg::type::flush, details::log_msg()); }
|
||||||
async_sink<Mutex>::async_sink(size_t queue_size)
|
|
||||||
: async_sink(queue_size, nullptr, nullptr) {}
|
|
||||||
|
|
||||||
template <typename Mutex>
|
void async_sink::set_pattern(const std::string &pattern) { set_formatter(std::make_unique<pattern_formatter>(pattern)); }
|
||||||
async_sink<Mutex>::async_sink(std::function<void()> on_thread_start, std::function<void()> on_thread_stop)
|
|
||||||
: async_sink(default_queue_size, on_thread_start, on_thread_stop) {}
|
|
||||||
|
|
||||||
template <typename Mutex>
|
void async_sink::set_formatter(std::unique_ptr<formatter> formatter) {
|
||||||
void async_sink<Mutex>::sink_it_(const details::log_msg &msg) {
|
const auto &sinks = config_.sinks;
|
||||||
send_message_(async_log_msg::type::log, msg);
|
for (auto it = sinks.begin(); it != sinks.end(); ++it) {
|
||||||
|
if (std::next(it) == sinks.end()) {
|
||||||
|
// last element - we can move it.
|
||||||
|
(*it)->set_formatter(std::move(formatter));
|
||||||
|
break; // to prevent clang-tidy warning
|
||||||
|
}
|
||||||
|
(*it)->set_formatter(formatter->clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t async_sink::get_overrun_counter() const { return q_->overrun_counter(); }
|
||||||
|
|
||||||
template <typename Mutex>
|
void async_sink::reset_overrun_counter() const { q_->reset_overrun_counter(); }
|
||||||
void async_sink<Mutex>::set_overflow_policy(overflow_policy policy) {
|
|
||||||
overflow_policy_ = policy;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Mutex>
|
size_t async_sink::get_discard_counter() const { return q_->discard_counter(); }
|
||||||
typename async_sink<Mutex>::overflow_policy async_sink<Mutex>::get_overflow_policy() const {
|
|
||||||
return overflow_policy_;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Mutex>
|
void async_sink::reset_discard_counter() const { q_->reset_discard_counter(); }
|
||||||
size_t async_sink<Mutex>::get_overrun_counter() const {
|
|
||||||
return q_->overrun_counter();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Mutex>
|
const async_sink::config &async_sink::get_config() const { return config_; }
|
||||||
void async_sink<Mutex>::reset_overrun_counter() const {
|
|
||||||
q_->reset_overrun_counter();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Mutex>
|
// private methods
|
||||||
size_t async_sink<Mutex>::get_discard_counter() const {
|
void async_sink::send_message_(async_log_msg::type msg_type, const details::log_msg &msg) const {
|
||||||
return q_->discard_counter();
|
switch (config_.policy) {
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Mutex>
|
|
||||||
void async_sink<Mutex>::reset_discard_counter() const {
|
|
||||||
q_->reset_discard_counter();
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Mutex>
|
|
||||||
void async_sink<Mutex>::flush_() {
|
|
||||||
send_message_(async_log_msg::type::flush, details::log_msg());
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Mutex>
|
|
||||||
void async_sink<Mutex>::send_message_(async_log_msg::type msg_type, const details::log_msg &msg) {
|
|
||||||
switch (overflow_policy_) {
|
|
||||||
case overflow_policy::block:
|
case overflow_policy::block:
|
||||||
q_->enqueue(async_log_msg(msg_type, msg));
|
q_->enqueue(async_log_msg(msg_type, msg));
|
||||||
break;
|
break;
|
||||||
@ -111,17 +83,16 @@ void async_sink<Mutex>::send_message_(async_log_msg::type msg_type, const detail
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Mutex>
|
void async_sink::backend_loop_() const {
|
||||||
void async_sink<Mutex>::backend_loop_() {
|
|
||||||
details::async_log_msg incoming_msg;
|
details::async_log_msg incoming_msg;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
q_->dequeue(incoming_msg);
|
q_->dequeue(incoming_msg);
|
||||||
switch (incoming_msg.message_type()) {
|
switch (incoming_msg.message_type()) {
|
||||||
case async_log_msg::type::log:
|
case async_log_msg::type::log:
|
||||||
base_t::sink_it_(incoming_msg);
|
backend_log_(incoming_msg);
|
||||||
break;
|
break;
|
||||||
case async_log_msg::type::flush:
|
case async_log_msg::type::flush:
|
||||||
base_t::flush_();
|
backend_flush_();
|
||||||
break;
|
break;
|
||||||
case async_log_msg::type::terminate:
|
case async_log_msg::type::terminate:
|
||||||
return;
|
return;
|
||||||
@ -131,10 +102,19 @@ void async_sink<Mutex>::backend_loop_() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void async_sink::backend_log_(const details::log_msg &msg) const {
|
||||||
|
for (const auto &sink : config_.sinks) {
|
||||||
|
if (sink->should_log(msg.log_level)) {
|
||||||
|
sink->log(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void async_sink::backend_flush_() const {
|
||||||
|
for (const auto &sink : config_.sinks) {
|
||||||
|
sink->flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sinks
|
} // namespace sinks
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
|
||||||
// template instantiations
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
|
||||||
template class SPDLOG_API spdlog::sinks::async_sink<std::mutex>;
|
|
||||||
template class SPDLOG_API spdlog::sinks::async_sink<spdlog::details::null_mutex>;
|
|
||||||
|
@ -7,15 +7,17 @@
|
|||||||
|
|
||||||
#define TEST_FILENAME "test_logs/async_test.log"
|
#define TEST_FILENAME "test_logs/async_test.log"
|
||||||
|
|
||||||
using spdlog::sinks::async_sink_mt;
|
using spdlog::sinks::async_sink;
|
||||||
using spdlog::sinks::sink;
|
using spdlog::sinks::sink;
|
||||||
using spdlog::sinks::test_sink_mt;
|
using spdlog::sinks::test_sink_mt;
|
||||||
|
|
||||||
auto creat_async_logger(size_t queue_size, std::shared_ptr<sink> backend_sink) {
|
auto creat_async_logger(size_t queue_size, std::shared_ptr<sink> backend_sink) {
|
||||||
auto async_sink = std::make_shared<async_sink_mt>(queue_size);
|
async_sink::config cfg;
|
||||||
async_sink->add_sink(std::move(backend_sink));
|
cfg.queue_size = queue_size;
|
||||||
auto logger = std::make_shared<spdlog::logger>("async_logger", async_sink);
|
cfg.sinks.push_back(std::move(backend_sink));
|
||||||
return std::make_tuple(logger, async_sink);
|
auto s = std::make_shared<async_sink>(cfg);
|
||||||
|
auto logger = std::make_shared<spdlog::logger>("async_logger", s);
|
||||||
|
return std::make_tuple(logger, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("basic async test ", "[async]") {
|
TEST_CASE("basic async test ", "[async]") {
|
||||||
@ -40,41 +42,46 @@ TEST_CASE("basic async test ", "[async]") {
|
|||||||
TEST_CASE("discard policy ", "[async]") {
|
TEST_CASE("discard policy ", "[async]") {
|
||||||
auto test_sink = std::make_shared<test_sink_mt>();
|
auto test_sink = std::make_shared<test_sink_mt>();
|
||||||
test_sink->set_delay(std::chrono::milliseconds(1));
|
test_sink->set_delay(std::chrono::milliseconds(1));
|
||||||
size_t queue_size = 4;
|
async_sink::config config;
|
||||||
|
config.queue_size = 4;
|
||||||
|
config.policy = async_sink::overflow_policy::overrun_oldest;
|
||||||
|
config.sinks.push_back(test_sink);
|
||||||
size_t messages = 1024;
|
size_t messages = 1024;
|
||||||
|
auto as = std::make_shared<async_sink>(config);
|
||||||
auto [logger, async_sink] = creat_async_logger(queue_size, test_sink);
|
auto logger = std::make_shared<spdlog::logger>("async_logger", as);
|
||||||
async_sink->set_overflow_policy(async_sink_mt::overflow_policy::overrun_oldest);
|
REQUIRE(as->get_discard_counter() == 0);
|
||||||
REQUIRE(async_sink->get_overflow_policy() == async_sink_mt::overflow_policy::overrun_oldest);
|
REQUIRE(as->get_overrun_counter() == 0);
|
||||||
REQUIRE(async_sink->get_discard_counter() == 0);
|
|
||||||
REQUIRE(async_sink->get_overrun_counter() == 0);
|
|
||||||
for (size_t i = 0; i < messages; i++) {
|
for (size_t i = 0; i < messages; i++) {
|
||||||
logger->info("Hello message");
|
logger->info("Hello message");
|
||||||
}
|
}
|
||||||
REQUIRE(test_sink->msg_counter() < messages);
|
REQUIRE(test_sink->msg_counter() < messages);
|
||||||
REQUIRE(async_sink->get_overrun_counter() > 0);
|
REQUIRE(as->get_overrun_counter() > 0);
|
||||||
async_sink->reset_overrun_counter();
|
as->reset_overrun_counter();
|
||||||
REQUIRE(async_sink->get_overrun_counter() == 0);
|
REQUIRE(as->get_overrun_counter() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("discard policy discard_new ", "[async]") {
|
TEST_CASE("discard policy discard_new ", "[async]") {
|
||||||
auto test_sink = std::make_shared<test_sink_mt>();
|
auto test_sink = std::make_shared<test_sink_mt>();
|
||||||
test_sink->set_delay(std::chrono::milliseconds(1));
|
test_sink->set_delay(std::chrono::milliseconds(1));
|
||||||
size_t queue_size = 4;
|
async_sink::config config;
|
||||||
|
config.queue_size = 4;
|
||||||
|
config.policy = async_sink::overflow_policy::discard_new;
|
||||||
|
config.sinks.push_back(test_sink);
|
||||||
size_t messages = 1024;
|
size_t messages = 1024;
|
||||||
|
auto as = std::make_shared<async_sink>(config);
|
||||||
|
auto logger = std::make_shared<spdlog::logger>("async_logger", as);
|
||||||
|
|
||||||
auto [logger, async_sink] = creat_async_logger(queue_size, test_sink);
|
|
||||||
async_sink->set_overflow_policy(async_sink_mt::overflow_policy::discard_new);
|
REQUIRE(as->get_config().policy == async_sink::overflow_policy::discard_new);
|
||||||
REQUIRE(async_sink->get_overflow_policy() == async_sink_mt::overflow_policy::discard_new);
|
REQUIRE(as->get_discard_counter() == 0);
|
||||||
REQUIRE(async_sink->get_discard_counter() == 0);
|
REQUIRE(as->get_overrun_counter() == 0);
|
||||||
REQUIRE(async_sink->get_overrun_counter() == 0);
|
|
||||||
for (size_t i = 0; i < messages; i++) {
|
for (size_t i = 0; i < messages; i++) {
|
||||||
logger->info("Hello message");
|
logger->info("Hello message");
|
||||||
}
|
}
|
||||||
REQUIRE(test_sink->msg_counter() < messages);
|
REQUIRE(test_sink->msg_counter() < messages);
|
||||||
REQUIRE(async_sink->get_discard_counter() > 0);
|
REQUIRE(as->get_discard_counter() > 0);
|
||||||
async_sink->reset_discard_counter();
|
as->reset_discard_counter();
|
||||||
REQUIRE(async_sink->get_discard_counter() == 0);
|
REQUIRE(as->get_discard_counter() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("flush", "[async]") {
|
TEST_CASE("flush", "[async]") {
|
||||||
@ -86,7 +93,6 @@ TEST_CASE("flush", "[async]") {
|
|||||||
for (size_t i = 0; i < messages; i++) {
|
for (size_t i = 0; i < messages; i++) {
|
||||||
logger->info("Hello message #{}", i);
|
logger->info("Hello message #{}", i);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger->flush();
|
logger->flush();
|
||||||
}
|
}
|
||||||
// std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
// std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||||
@ -95,19 +101,22 @@ TEST_CASE("flush", "[async]") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("wait_dtor ", "[async]") {
|
TEST_CASE("wait_dtor ", "[async]") {
|
||||||
auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
|
auto test_sink = std::make_shared<test_sink_mt>();
|
||||||
test_sink->set_delay(std::chrono::milliseconds(5));
|
test_sink->set_delay(std::chrono::milliseconds(5));
|
||||||
|
async_sink::config config;
|
||||||
|
config.sinks.push_back(test_sink);
|
||||||
|
config.queue_size = 4;
|
||||||
|
config.policy = async_sink::overflow_policy::block;
|
||||||
size_t messages = 100;
|
size_t messages = 100;
|
||||||
{
|
{
|
||||||
auto [logger, async_sink] = creat_async_logger(messages, test_sink);
|
auto as = std::make_shared<async_sink>(config);
|
||||||
async_sink->set_overflow_policy(async_sink_mt::overflow_policy::block);
|
auto logger = std::make_shared<spdlog::logger>("async_logger", as);
|
||||||
|
|
||||||
for (size_t i = 0; i < messages; i++) {
|
for (size_t i = 0; i < messages; i++) {
|
||||||
logger->info("Hello message #{}", i);
|
logger->info("Hello message #{}", i);
|
||||||
}
|
}
|
||||||
logger->flush();
|
logger->flush();
|
||||||
REQUIRE(async_sink->get_overrun_counter() == 0);
|
REQUIRE(as->get_overrun_counter() == 0);
|
||||||
REQUIRE(async_sink->get_discard_counter() == 0);
|
REQUIRE(as->get_discard_counter() == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
REQUIRE(test_sink->msg_counter() == messages);
|
REQUIRE(test_sink->msg_counter() == messages);
|
||||||
@ -158,15 +167,27 @@ TEST_CASE("to_file", "[async]") {
|
|||||||
REQUIRE(ends_with(contents, spdlog::fmt_lib::format("Hello message #1023{}", default_eol)));
|
REQUIRE(ends_with(contents, spdlog::fmt_lib::format("Hello message #1023{}", default_eol)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("bad_ctor", "[async]") { REQUIRE_THROWS_AS(std::make_shared<async_sink_mt>(0), spdlog::spdlog_ex); }
|
|
||||||
|
|
||||||
TEST_CASE("bad_ctor2", "[async]") { REQUIRE_THROWS_AS(std::make_shared<async_sink_mt>(-1), spdlog::spdlog_ex); }
|
TEST_CASE("bad_ctor", "[async]") {
|
||||||
|
async_sink::config cfg;
|
||||||
|
cfg.queue_size = 0;
|
||||||
|
REQUIRE_THROWS_AS(std::make_shared<async_sink>(cfg), spdlog::spdlog_ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("bad_ctor2", "[async]") {
|
||||||
|
async_sink::config cfg;
|
||||||
|
cfg.queue_size = async_sink::max_queue_size + 1;
|
||||||
|
REQUIRE_THROWS_AS(std::make_shared<async_sink>(cfg), spdlog::spdlog_ex);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("start_stop_clbks", "[async]") {
|
TEST_CASE("start_stop_clbks", "[async]") {
|
||||||
bool start_called = false;
|
bool start_called = false;
|
||||||
bool stop_called = false;
|
bool stop_called = false;
|
||||||
{
|
{
|
||||||
auto sink = std::make_shared<async_sink_mt>([&] { start_called = true; }, [&] { stop_called = true; });
|
async_sink::config cfg;
|
||||||
|
cfg.on_thread_start = [&] { start_called = true; };
|
||||||
|
cfg.on_thread_stop = [&] { stop_called = true; };
|
||||||
|
auto sink = std::make_shared<async_sink>(cfg);
|
||||||
}
|
}
|
||||||
REQUIRE(start_called);
|
REQUIRE(start_called);
|
||||||
REQUIRE(stop_called);
|
REQUIRE(stop_called);
|
||||||
@ -176,7 +197,9 @@ TEST_CASE("start_stop_clbks2", "[async]") {
|
|||||||
bool start_called = false;
|
bool start_called = false;
|
||||||
bool stop_called = false;
|
bool stop_called = false;
|
||||||
{
|
{
|
||||||
auto sink = std::make_shared<async_sink_mt>([&] { start_called = true; }, nullptr);
|
async_sink::config cfg;
|
||||||
|
cfg.on_thread_start = [&] { start_called = true; };
|
||||||
|
auto sink = std::make_shared<async_sink>(cfg);
|
||||||
}
|
}
|
||||||
REQUIRE(start_called);
|
REQUIRE(start_called);
|
||||||
REQUIRE_FALSE(stop_called);
|
REQUIRE_FALSE(stop_called);
|
||||||
@ -186,7 +209,10 @@ TEST_CASE("start_stop_clbks3", "[async]") {
|
|||||||
bool start_called = false;
|
bool start_called = false;
|
||||||
bool stop_called = false;
|
bool stop_called = false;
|
||||||
{
|
{
|
||||||
auto sink = std::make_shared<async_sink_mt>(nullptr, [&] { stop_called = true; });
|
async_sink::config cfg;
|
||||||
|
cfg.on_thread_start = nullptr;
|
||||||
|
cfg.on_thread_stop = [&] { stop_called = true; };
|
||||||
|
auto sink = std::make_shared<async_sink>(cfg);
|
||||||
}
|
}
|
||||||
REQUIRE_FALSE(start_called);
|
REQUIRE_FALSE(start_called);
|
||||||
REQUIRE(stop_called);
|
REQUIRE(stop_called);
|
||||||
@ -196,17 +222,26 @@ TEST_CASE("start_stop_clbks4", "[async]") {
|
|||||||
bool start_called = false;
|
bool start_called = false;
|
||||||
bool stop_called = false;
|
bool stop_called = false;
|
||||||
{
|
{
|
||||||
auto sink = std::make_shared<async_sink_mt>(128, [&] { start_called = true; }, [&] { stop_called = true; });
|
async_sink::config cfg;
|
||||||
|
cfg.on_thread_start = [&] { start_called = true; };
|
||||||
|
cfg.on_thread_stop = [&] { stop_called = true; };
|
||||||
|
cfg.queue_size = 128;
|
||||||
|
auto sink = std::make_shared<async_sink>(cfg);
|
||||||
}
|
}
|
||||||
REQUIRE(start_called);
|
REQUIRE(start_called);
|
||||||
REQUIRE(stop_called);
|
REQUIRE(stop_called);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// should not start threads if queue size is invalid
|
||||||
TEST_CASE("start_stop_clbks5", "[async]") {
|
TEST_CASE("start_stop_clbks5", "[async]") {
|
||||||
bool start_called = false;
|
bool start_called = false;
|
||||||
bool stop_called = false;
|
bool stop_called = false;
|
||||||
{
|
{
|
||||||
REQUIRE_THROWS(std::make_shared<async_sink_mt>(0, [&] { start_called = true; }, [&] { stop_called = true; }));
|
async_sink::config cfg;
|
||||||
|
cfg.on_thread_start = [&] { start_called = true; };
|
||||||
|
cfg.on_thread_stop = [&] { stop_called = true; };
|
||||||
|
cfg.queue_size = 0;
|
||||||
|
REQUIRE_THROWS_AS(std::make_shared<async_sink>(cfg), spdlog::spdlog_ex);
|
||||||
}
|
}
|
||||||
REQUIRE_FALSE(start_called);
|
REQUIRE_FALSE(start_called);
|
||||||
REQUIRE_FALSE(stop_called);
|
REQUIRE_FALSE(stop_called);
|
||||||
@ -214,17 +249,20 @@ TEST_CASE("start_stop_clbks5", "[async]") {
|
|||||||
|
|
||||||
TEST_CASE("multi-sinks", "[async]") {
|
TEST_CASE("multi-sinks", "[async]") {
|
||||||
prepare_logdir();
|
prepare_logdir();
|
||||||
auto test_sink1 = std::make_shared<spdlog::sinks::test_sink_mt>();
|
auto test_sink1 = std::make_shared<test_sink_mt>();
|
||||||
auto test_sink2 = std::make_shared<spdlog::sinks::test_sink_mt>();
|
auto test_sink2 = std::make_shared<test_sink_mt>();
|
||||||
auto test_sink3 = std::make_shared<spdlog::sinks::test_sink_mt>();
|
auto test_sink3 = std::make_shared<test_sink_mt>();
|
||||||
size_t messages = 1024;
|
size_t messages = 1024;
|
||||||
{
|
{
|
||||||
auto [logger, async_sink] = creat_async_logger(messages, test_sink1);
|
async_sink::config cfg;
|
||||||
async_sink->add_sink(test_sink2);
|
cfg.sinks.push_back(test_sink1);
|
||||||
async_sink->add_sink(test_sink3);
|
cfg.sinks.push_back(test_sink2);
|
||||||
|
cfg.sinks.push_back(test_sink3);
|
||||||
|
auto as = std::make_shared<async_sink>(cfg);
|
||||||
|
spdlog::logger l("async_logger", as);
|
||||||
|
|
||||||
for (size_t j = 0; j < messages; j++) {
|
for (size_t j = 0; j < messages; j++) {
|
||||||
logger->info("Hello message #{}", j);
|
l.info("Hello message #{}", j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
REQUIRE(test_sink1->msg_counter() == messages);
|
REQUIRE(test_sink1->msg_counter() == messages);
|
||||||
|
@ -95,13 +95,14 @@ TEST_CASE("clone-logger", "[clone]") {
|
|||||||
|
|
||||||
TEST_CASE("clone async", "[clone]") {
|
TEST_CASE("clone async", "[clone]") {
|
||||||
using spdlog::sinks::test_sink_mt;
|
using spdlog::sinks::test_sink_mt;
|
||||||
auto async_sink = std::make_shared<spdlog::sinks::async_sink_mt>(32);
|
|
||||||
auto test_sink = std::make_shared<test_sink_mt>();
|
auto test_sink = std::make_shared<test_sink_mt>();
|
||||||
async_sink->add_sink(test_sink);
|
{
|
||||||
auto logger = std::make_shared<spdlog::logger>("orig", async_sink);
|
auto cfg = spdlog::sinks::async_sink::config();
|
||||||
logger->set_pattern("%v");
|
cfg.sinks.push_back(test_sink);
|
||||||
|
auto async_sink = spdlog::sinks::async_sink::with_sink<test_sink_mt>();
|
||||||
|
auto logger = spdlog::create<spdlog::sinks::async_sink>("orig", cfg);
|
||||||
|
logger->set_pattern("*** %v ***");
|
||||||
auto cloned = logger->clone("clone");
|
auto cloned = logger->clone("clone");
|
||||||
|
|
||||||
REQUIRE(cloned->name() == "clone");
|
REQUIRE(cloned->name() == "clone");
|
||||||
REQUIRE(logger->sinks() == cloned->sinks());
|
REQUIRE(logger->sinks() == cloned->sinks());
|
||||||
REQUIRE(logger->log_level() == cloned->log_level());
|
REQUIRE(logger->log_level() == cloned->log_level());
|
||||||
@ -109,10 +110,10 @@ TEST_CASE("clone async", "[clone]") {
|
|||||||
|
|
||||||
logger->info("Some message 1");
|
logger->info("Some message 1");
|
||||||
cloned->info("Some message 2");
|
cloned->info("Some message 2");
|
||||||
spdlog::details::os::sleep_for_millis(100);
|
}
|
||||||
REQUIRE(test_sink->lines().size() == 2);
|
REQUIRE(test_sink->lines().size() == 2);
|
||||||
REQUIRE(test_sink->lines()[0] == "Some message 1");
|
REQUIRE(test_sink->lines()[0] == "*** Some message 1 ***");
|
||||||
REQUIRE(test_sink->lines()[1] == "Some message 2");
|
REQUIRE(test_sink->lines()[1] == "*** Some message 2 ***");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("global logger API", "[global logger]") {
|
TEST_CASE("global logger API", "[global logger]") {
|
||||||
|
Loading…
Reference in New Issue
Block a user