async sink config param and tests

pull/3309/head
gabime 8 months ago
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();
auto logger = std::make_shared<spdlog::logger>("async_logger", std::move(async_sink)); cfg.queue_size = queue_size;
bench_mt(howmany, std::move(logger), threads); cfg.sinks.push_back(std::move(file_sink));
verify_file(filename, howmany); auto async_sink = std::make_shared<sinks::async_sink>(cfg);
auto logger = std::make_shared<spdlog::logger>("async_logger", std::move(async_sink));
bench_mt(howmany, std::move(logger), threads);
}
//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 };
~async_sink() override;
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(); ~async_sink() override;
explicit async_sink(size_t queue_size);
async_sink(std::function<void()> on_thread_start, std::function<void()> on_thread_stop);
async_sink(const async_sink &) = delete;
async_sink &operator=(const async_sink &) = delete;
async_sink(async_sink &&) = default;
async_sink &operator=(async_sink &&) = default;
void set_overflow_policy(overflow_policy policy); // sink interface implementation
[[nodiscard]] overflow_policy get_overflow_policy() const; void log(const details::log_msg &msg) override;
void flush() override;
void set_pattern(const std::string &pattern) override;
void set_formatter(std::unique_ptr<formatter> sink_formatter) override;
// 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>
typename async_sink<Mutex>::overflow_policy async_sink<Mutex>::get_overflow_policy() const {
return overflow_policy_;
}
template <typename Mutex> size_t async_sink::get_discard_counter() const { return q_->discard_counter(); }
size_t async_sink<Mutex>::get_overrun_counter() const {
return q_->overrun_counter();
}
template <typename Mutex> void async_sink::reset_discard_counter() const { q_->reset_discard_counter(); }
void async_sink<Mutex>::reset_overrun_counter() const {
q_->reset_overrun_counter();
}
template <typename Mutex> const async_sink::config &async_sink::get_config() const { return config_; }
size_t async_sink<Mutex>::get_discard_counter() const {
return q_->discard_counter();
}
template <typename Mutex> // private methods
void async_sink<Mutex>::reset_discard_counter() const { void async_sink::send_message_(async_log_msg::type msg_type, const details::log_msg &msg) const {
q_->reset_discard_counter(); switch (config_.policy) {
}
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); REQUIRE(as->get_config().policy == async_sink::overflow_policy::discard_new);
async_sink->set_overflow_policy(async_sink_mt::overflow_policy::discard_new); REQUIRE(as->get_discard_counter() == 0);
REQUIRE(async_sink->get_overflow_policy() == async_sink_mt::overflow_policy::discard_new); 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_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,24 +95,25 @@ 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 cloned = logger->clone("clone"); auto async_sink = spdlog::sinks::async_sink::with_sink<test_sink_mt>();
auto logger = spdlog::create<spdlog::sinks::async_sink>("orig", cfg);
REQUIRE(cloned->name() == "clone"); logger->set_pattern("*** %v ***");
REQUIRE(logger->sinks() == cloned->sinks()); auto cloned = logger->clone("clone");
REQUIRE(logger->log_level() == cloned->log_level()); REQUIRE(cloned->name() == "clone");
REQUIRE(logger->flush_level() == cloned->flush_level()); REQUIRE(logger->sinks() == cloned->sinks());
REQUIRE(logger->log_level() == cloned->log_level());
logger->info("Some message 1"); REQUIRE(logger->flush_level() == cloned->flush_level());
cloned->info("Some message 2");
spdlog::details::os::sleep_for_millis(100); logger->info("Some message 1");
cloned->info("Some message 2");
}
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…
Cancel
Save