async sink config param and tests
This commit is contained in:
parent
345af1c0a0
commit
a7298c5b8f
@ -8,10 +8,12 @@
|
||||
//
|
||||
#include <atomic>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <locale>
|
||||
#include <algorithm>
|
||||
|
||||
#include "spdlog/sinks/async_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);
|
||||
|
||||
#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 counter = 0;
|
||||
auto *infile = fopen(filename, "r");
|
||||
int ch = 0;
|
||||
while (EOF != (ch = getc(infile))) {
|
||||
if ('\n' == ch) counter++;
|
||||
}
|
||||
fclose(infile);
|
||||
return counter;
|
||||
std::ifstream ifs(filename);
|
||||
return std::count(std::istreambuf_iterator(ifs), std::istreambuf_iterator<char>(), '\n');
|
||||
}
|
||||
|
||||
void verify_file(const char *filename, int expected_count) {
|
||||
@ -66,10 +57,10 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
try {
|
||||
spdlog::set_pattern("[%^%l%$] %v");
|
||||
// if (argc == 1) {
|
||||
// spdlog::info("Usage: {} <message_count> <threads> <q_size> <iterations>", argv[0]);
|
||||
// return 0;
|
||||
// }
|
||||
if (argc > 1 && (std::string(argv[1]) == "-h" || std::string(argv[1]) == "--help")) {
|
||||
spdlog::info("Usage: {} <message_count> <threads> <q_size> <iterations>", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (argc > 1) howmany = atoi(argv[1]);
|
||||
if (argc > 2) threads = atoi(argv[2]);
|
||||
@ -82,6 +73,11 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
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);
|
||||
spdlog::info("-------------------------------------------------");
|
||||
@ -98,14 +94,17 @@ int main(int argc, char *argv[]) {
|
||||
spdlog::info("Queue Overflow Policy: block");
|
||||
spdlog::info("*********************************");
|
||||
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);
|
||||
async_sink->add_sink(std::move(file_sink));
|
||||
auto logger = std::make_shared<spdlog::logger>("async_logger", std::move(async_sink));
|
||||
bench_mt(howmany, std::move(logger), threads);
|
||||
verify_file(filename, howmany);
|
||||
{
|
||||
auto file_sink = std::make_shared<basic_file_sink_mt>(filename, true);
|
||||
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));
|
||||
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("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
|
||||
filename = "logs/basic_async-overrun.log";
|
||||
for (int i = 0; i < iters; i++) {
|
||||
auto async_sink = std::make_shared<async_sink_mt>(queue_size);
|
||||
async_sink->set_overflow_policy(async_sink_mt::overflow_policy::overrun_oldest);
|
||||
async_sink::config cfg;
|
||||
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);
|
||||
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));
|
||||
bench_mt(howmany, std::move(logger), threads);
|
||||
}
|
||||
|
@ -3,14 +3,16 @@
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#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.
|
||||
// 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
|
||||
template <typename T>
|
||||
@ -20,61 +22,62 @@ class mpmc_blocking_queue;
|
||||
namespace spdlog {
|
||||
namespace sinks {
|
||||
|
||||
template <typename Mutex>
|
||||
class async_sink final : public dist_sink<Mutex> {
|
||||
class async_sink final : public sink {
|
||||
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 {
|
||||
block, // Block until the log message can be enqueued (default).
|
||||
overrun_oldest, // Overrun the oldest message in the queue if 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();
|
||||
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);
|
||||
[[nodiscard]] overflow_policy get_overflow_policy() const;
|
||||
// sink interface implementation
|
||||
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;
|
||||
void reset_overrun_counter() const;
|
||||
|
||||
[[nodiscard]] size_t get_discard_counter() const;
|
||||
void reset_discard_counter() const;
|
||||
[[nodiscard]] const config &get_config() const;
|
||||
|
||||
private:
|
||||
void sink_it_(const details::log_msg &msg) override;
|
||||
void flush_() override;
|
||||
void send_message_(async_log_msg::type msg_type, const details::log_msg &msg);
|
||||
void backend_loop_();
|
||||
using async_log_msg = details::async_log_msg;
|
||||
using queue_t = details::mpmc_blocking_queue<async_log_msg>;
|
||||
|
||||
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::thread worker_thread_;
|
||||
};
|
||||
|
||||
using async_sink_mt = async_sink<std::mutex>;
|
||||
using async_sink_st = async_sink<details::null_mutex>;
|
||||
|
||||
} // 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
|
||||
|
@ -3,99 +3,71 @@
|
||||
|
||||
#include "spdlog/sinks/async_sink.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <cassert>
|
||||
|
||||
#include "spdlog/details/mpmc_blocking_q.h"
|
||||
#include "spdlog/common.h"
|
||||
#include "spdlog/details/mpmc_blocking_q.h"
|
||||
#include "spdlog/pattern_formatter.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
namespace spdlog {
|
||||
namespace sinks {
|
||||
|
||||
template <typename Mutex>
|
||||
async_sink<Mutex>::async_sink(size_t queue_size, std::function<void()> on_thread_start, std::function<void()> on_thread_stop)
|
||||
: base_t() {
|
||||
if (queue_size == 0 || queue_size > max_queue_size) {
|
||||
async_sink::async_sink(config async_config)
|
||||
: config_(std::move(async_config)) {
|
||||
if (config_.queue_size == 0 || config_.queue_size > max_queue_size) {
|
||||
throw spdlog_ex("async_sink: invalid queue size");
|
||||
}
|
||||
// printf("........... Allocating queue: slot: %zu X %zu bytes ====> %lld KB ..............\n",
|
||||
// queue_size, sizeof(details::async_log_msg), (sizeof(details::async_log_msg) * queue_size)/1024);
|
||||
q_ = std::make_unique<queue_t>(queue_size);
|
||||
|
||||
worker_thread_ = std::thread([this, on_thread_start, on_thread_stop] {
|
||||
if (on_thread_start) on_thread_start();
|
||||
q_ = std::make_unique<queue_t>(config_.queue_size);
|
||||
worker_thread_ = std::thread([this] {
|
||||
if (config_.on_thread_start) config_.on_thread_start();
|
||||
this->backend_loop_();
|
||||
if (on_thread_stop) on_thread_stop();
|
||||
if (config_.on_thread_stop) config_.on_thread_stop();
|
||||
});
|
||||
}
|
||||
|
||||
template <typename Mutex>
|
||||
async_sink<Mutex>::~async_sink() {
|
||||
async_sink::~async_sink() {
|
||||
try {
|
||||
q_->enqueue(async_log_msg(async_log_msg::type::terminate));
|
||||
worker_thread_.join();
|
||||
} catch (...) {
|
||||
printf("Exception in ~async_sink()\n");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Mutex>
|
||||
async_sink<Mutex>::async_sink()
|
||||
: async_sink(default_queue_size, nullptr, nullptr) {}
|
||||
void async_sink::log(const details::log_msg &msg) { send_message_(async_log_msg::type::log, msg); }
|
||||
|
||||
template <typename Mutex>
|
||||
async_sink<Mutex>::async_sink(size_t queue_size)
|
||||
: async_sink(queue_size, nullptr, nullptr) {}
|
||||
void async_sink::flush() { send_message_(async_log_msg::type::flush, details::log_msg()); }
|
||||
|
||||
template <typename Mutex>
|
||||
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) {}
|
||||
void async_sink::set_pattern(const std::string &pattern) { set_formatter(std::make_unique<pattern_formatter>(pattern)); }
|
||||
|
||||
template <typename Mutex>
|
||||
void async_sink<Mutex>::sink_it_(const details::log_msg &msg) {
|
||||
send_message_(async_log_msg::type::log, msg);
|
||||
void async_sink::set_formatter(std::unique_ptr<formatter> formatter) {
|
||||
const auto &sinks = config_.sinks;
|
||||
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<Mutex>::set_overflow_policy(overflow_policy policy) {
|
||||
overflow_policy_ = policy;
|
||||
}
|
||||
void async_sink::reset_overrun_counter() const { q_->reset_overrun_counter(); }
|
||||
|
||||
template <typename Mutex>
|
||||
typename async_sink<Mutex>::overflow_policy async_sink<Mutex>::get_overflow_policy() const {
|
||||
return overflow_policy_;
|
||||
}
|
||||
size_t async_sink::get_discard_counter() const { return q_->discard_counter(); }
|
||||
|
||||
template <typename Mutex>
|
||||
size_t async_sink<Mutex>::get_overrun_counter() const {
|
||||
return q_->overrun_counter();
|
||||
}
|
||||
void async_sink::reset_discard_counter() const { q_->reset_discard_counter(); }
|
||||
|
||||
template <typename Mutex>
|
||||
void async_sink<Mutex>::reset_overrun_counter() const {
|
||||
q_->reset_overrun_counter();
|
||||
}
|
||||
const async_sink::config &async_sink::get_config() const { return config_; }
|
||||
|
||||
template <typename Mutex>
|
||||
size_t async_sink<Mutex>::get_discard_counter() const {
|
||||
return q_->discard_counter();
|
||||
}
|
||||
|
||||
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_) {
|
||||
// private methods
|
||||
void async_sink::send_message_(async_log_msg::type msg_type, const details::log_msg &msg) const {
|
||||
switch (config_.policy) {
|
||||
case overflow_policy::block:
|
||||
q_->enqueue(async_log_msg(msg_type, msg));
|
||||
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<Mutex>::backend_loop_() {
|
||||
void async_sink::backend_loop_() const {
|
||||
details::async_log_msg incoming_msg;
|
||||
for (;;) {
|
||||
q_->dequeue(incoming_msg);
|
||||
switch (incoming_msg.message_type()) {
|
||||
case async_log_msg::type::log:
|
||||
base_t::sink_it_(incoming_msg);
|
||||
backend_log_(incoming_msg);
|
||||
break;
|
||||
case async_log_msg::type::flush:
|
||||
base_t::flush_();
|
||||
backend_flush_();
|
||||
break;
|
||||
case async_log_msg::type::terminate:
|
||||
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 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"
|
||||
|
||||
using spdlog::sinks::async_sink_mt;
|
||||
using spdlog::sinks::async_sink;
|
||||
using spdlog::sinks::sink;
|
||||
using spdlog::sinks::test_sink_mt;
|
||||
|
||||
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->add_sink(std::move(backend_sink));
|
||||
auto logger = std::make_shared<spdlog::logger>("async_logger", async_sink);
|
||||
return std::make_tuple(logger, async_sink);
|
||||
async_sink::config cfg;
|
||||
cfg.queue_size = queue_size;
|
||||
cfg.sinks.push_back(std::move(backend_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]") {
|
||||
@ -40,41 +42,46 @@ TEST_CASE("basic async test ", "[async]") {
|
||||
TEST_CASE("discard policy ", "[async]") {
|
||||
auto test_sink = std::make_shared<test_sink_mt>();
|
||||
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;
|
||||
|
||||
auto [logger, async_sink] = creat_async_logger(queue_size, test_sink);
|
||||
async_sink->set_overflow_policy(async_sink_mt::overflow_policy::overrun_oldest);
|
||||
REQUIRE(async_sink->get_overflow_policy() == async_sink_mt::overflow_policy::overrun_oldest);
|
||||
REQUIRE(async_sink->get_discard_counter() == 0);
|
||||
REQUIRE(async_sink->get_overrun_counter() == 0);
|
||||
auto as = std::make_shared<async_sink>(config);
|
||||
auto logger = std::make_shared<spdlog::logger>("async_logger", as);
|
||||
REQUIRE(as->get_discard_counter() == 0);
|
||||
REQUIRE(as->get_overrun_counter() == 0);
|
||||
for (size_t i = 0; i < messages; i++) {
|
||||
logger->info("Hello message");
|
||||
}
|
||||
REQUIRE(test_sink->msg_counter() < messages);
|
||||
REQUIRE(async_sink->get_overrun_counter() > 0);
|
||||
async_sink->reset_overrun_counter();
|
||||
REQUIRE(async_sink->get_overrun_counter() == 0);
|
||||
REQUIRE(as->get_overrun_counter() > 0);
|
||||
as->reset_overrun_counter();
|
||||
REQUIRE(as->get_overrun_counter() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("discard policy discard_new ", "[async]") {
|
||||
auto test_sink = std::make_shared<test_sink_mt>();
|
||||
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;
|
||||
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(async_sink->get_overflow_policy() == async_sink_mt::overflow_policy::discard_new);
|
||||
REQUIRE(async_sink->get_discard_counter() == 0);
|
||||
REQUIRE(async_sink->get_overrun_counter() == 0);
|
||||
|
||||
REQUIRE(as->get_config().policy == async_sink::overflow_policy::discard_new);
|
||||
REQUIRE(as->get_discard_counter() == 0);
|
||||
REQUIRE(as->get_overrun_counter() == 0);
|
||||
for (size_t i = 0; i < messages; i++) {
|
||||
logger->info("Hello message");
|
||||
}
|
||||
REQUIRE(test_sink->msg_counter() < messages);
|
||||
REQUIRE(async_sink->get_discard_counter() > 0);
|
||||
async_sink->reset_discard_counter();
|
||||
REQUIRE(async_sink->get_discard_counter() == 0);
|
||||
REQUIRE(as->get_discard_counter() > 0);
|
||||
as->reset_discard_counter();
|
||||
REQUIRE(as->get_discard_counter() == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("flush", "[async]") {
|
||||
@ -86,7 +93,6 @@ TEST_CASE("flush", "[async]") {
|
||||
for (size_t i = 0; i < messages; i++) {
|
||||
logger->info("Hello message #{}", i);
|
||||
}
|
||||
|
||||
logger->flush();
|
||||
}
|
||||
// std::this_thread::sleep_for(std::chrono::milliseconds(250));
|
||||
@ -95,19 +101,22 @@ TEST_CASE("flush", "[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));
|
||||
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;
|
||||
{
|
||||
auto [logger, async_sink] = creat_async_logger(messages, test_sink);
|
||||
async_sink->set_overflow_policy(async_sink_mt::overflow_policy::block);
|
||||
|
||||
auto as = std::make_shared<async_sink>(config);
|
||||
auto logger = std::make_shared<spdlog::logger>("async_logger", as);
|
||||
for (size_t i = 0; i < messages; i++) {
|
||||
logger->info("Hello message #{}", i);
|
||||
}
|
||||
logger->flush();
|
||||
REQUIRE(async_sink->get_overrun_counter() == 0);
|
||||
REQUIRE(async_sink->get_discard_counter() == 0);
|
||||
REQUIRE(as->get_overrun_counter() == 0);
|
||||
REQUIRE(as->get_discard_counter() == 0);
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
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]") {
|
||||
bool start_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(stop_called);
|
||||
@ -176,7 +197,9 @@ TEST_CASE("start_stop_clbks2", "[async]") {
|
||||
bool start_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_FALSE(stop_called);
|
||||
@ -186,7 +209,10 @@ TEST_CASE("start_stop_clbks3", "[async]") {
|
||||
bool start_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(stop_called);
|
||||
@ -196,17 +222,26 @@ TEST_CASE("start_stop_clbks4", "[async]") {
|
||||
bool start_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(stop_called);
|
||||
}
|
||||
|
||||
// should not start threads if queue size is invalid
|
||||
TEST_CASE("start_stop_clbks5", "[async]") {
|
||||
bool start_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(stop_called);
|
||||
@ -214,17 +249,20 @@ TEST_CASE("start_stop_clbks5", "[async]") {
|
||||
|
||||
TEST_CASE("multi-sinks", "[async]") {
|
||||
prepare_logdir();
|
||||
auto test_sink1 = std::make_shared<spdlog::sinks::test_sink_mt>();
|
||||
auto test_sink2 = std::make_shared<spdlog::sinks::test_sink_mt>();
|
||||
auto test_sink3 = std::make_shared<spdlog::sinks::test_sink_mt>();
|
||||
auto test_sink1 = std::make_shared<test_sink_mt>();
|
||||
auto test_sink2 = std::make_shared<test_sink_mt>();
|
||||
auto test_sink3 = std::make_shared<test_sink_mt>();
|
||||
size_t messages = 1024;
|
||||
{
|
||||
auto [logger, async_sink] = creat_async_logger(messages, test_sink1);
|
||||
async_sink->add_sink(test_sink2);
|
||||
async_sink->add_sink(test_sink3);
|
||||
async_sink::config cfg;
|
||||
cfg.sinks.push_back(test_sink1);
|
||||
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++) {
|
||||
logger->info("Hello message #{}", j);
|
||||
l.info("Hello message #{}", j);
|
||||
}
|
||||
}
|
||||
REQUIRE(test_sink1->msg_counter() == messages);
|
||||
|
@ -95,24 +95,25 @@ TEST_CASE("clone-logger", "[clone]") {
|
||||
|
||||
TEST_CASE("clone async", "[clone]") {
|
||||
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>();
|
||||
async_sink->add_sink(test_sink);
|
||||
auto logger = std::make_shared<spdlog::logger>("orig", async_sink);
|
||||
logger->set_pattern("%v");
|
||||
auto cloned = logger->clone("clone");
|
||||
{
|
||||
auto cfg = spdlog::sinks::async_sink::config();
|
||||
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");
|
||||
REQUIRE(cloned->name() == "clone");
|
||||
REQUIRE(logger->sinks() == cloned->sinks());
|
||||
REQUIRE(logger->log_level() == cloned->log_level());
|
||||
REQUIRE(logger->flush_level() == cloned->flush_level());
|
||||
|
||||
REQUIRE(cloned->name() == "clone");
|
||||
REQUIRE(logger->sinks() == cloned->sinks());
|
||||
REQUIRE(logger->log_level() == cloned->log_level());
|
||||
REQUIRE(logger->flush_level() == cloned->flush_level());
|
||||
|
||||
logger->info("Some message 1");
|
||||
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()[0] == "Some message 1");
|
||||
REQUIRE(test_sink->lines()[1] == "Some message 2");
|
||||
REQUIRE(test_sink->lines()[0] == "*** Some message 1 ***");
|
||||
REQUIRE(test_sink->lines()[1] == "*** Some message 2 ***");
|
||||
}
|
||||
|
||||
TEST_CASE("global logger API", "[global logger]") {
|
||||
|
Loading…
Reference in New Issue
Block a user