Asink sink (#3309)

Replace async logger with async sink
This commit is contained in:
Gabi Melman
2025-01-05 02:17:31 +02:00
committed by GitHub
parent 166843ff3a
commit 83c9ede9e6
82 changed files with 986 additions and 1789 deletions

View File

@@ -25,7 +25,6 @@
#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
#include "spdlog/async.h"
#include "spdlog/details/fmt_helper.h"
#include "spdlog/pattern_formatter.h"
#include "spdlog/sinks/null_sink.h"

View File

@@ -1,86 +1,98 @@
#include <tuple>
#include "includes.h"
#include "spdlog/async.h"
#include "spdlog/sinks/async_sink.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "test_sink.h"
#define TEST_FILENAME "test_logs/async_test.log"
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) {
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]") {
auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
const auto test_sink = std::make_shared<test_sink_mt>();
size_t overrun_counter = 0;
size_t queue_size = 128;
size_t messages = 256;
{
auto tp = std::make_shared<spdlog::details::thread_pool>(queue_size, 1);
auto logger = std::make_shared<spdlog::async_logger>("as", test_sink, tp, spdlog::async_overflow_policy::block);
constexpr size_t queue_size = 16;
auto [logger, async_sink] = creat_async_logger(queue_size, test_sink);
for (size_t i = 0; i < messages; i++) {
logger->info("Hello message #{}", i);
}
logger->flush();
overrun_counter = tp->overrun_counter();
overrun_counter = async_sink->get_overrun_counter();
}
// logger and async_sink are destroyed here so the queue should be emptied
REQUIRE(test_sink->msg_counter() == messages);
REQUIRE(test_sink->flush_counter() == 1);
REQUIRE(overrun_counter == 0);
}
TEST_CASE("discard policy ", "[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(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 tp = std::make_shared<spdlog::details::thread_pool>(queue_size, 1);
auto logger = std::make_shared<spdlog::async_logger>("as", test_sink, tp, spdlog::async_overflow_policy::overrun_oldest);
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(tp->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<spdlog::sinks::test_sink_mt>();
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 tp = std::make_shared<spdlog::details::thread_pool>(queue_size, 1);
auto logger = std::make_shared<spdlog::async_logger>("as", test_sink, tp, spdlog::async_overflow_policy::discard_new);
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(tp->discard_counter() > 0);
}
TEST_CASE("discard policy using factory ", "[async]") {
size_t queue_size = 4;
size_t messages = 1024;
spdlog::init_thread_pool(queue_size, 1);
auto logger = spdlog::create_async_nb<spdlog::sinks::test_sink_mt>("as2");
auto test_sink = std::static_pointer_cast<spdlog::sinks::test_sink_mt>(logger->sinks()[0]);
test_sink->set_delay(std::chrono::milliseconds(3));
for (size_t i = 0; i < messages; i++) {
logger->info("Hello message");
}
REQUIRE(test_sink->msg_counter() < messages);
REQUIRE(as->get_discard_counter() > 0);
as->reset_discard_counter();
REQUIRE(as->get_discard_counter() == 0);
}
TEST_CASE("flush", "[async]") {
auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
size_t queue_size = 256;
auto test_sink = std::make_shared<test_sink_mt>();
size_t messages = 256;
{
auto tp = std::make_shared<spdlog::details::thread_pool>(queue_size, 1);
auto logger = std::make_shared<spdlog::async_logger>("as", test_sink, tp, spdlog::async_overflow_policy::block);
constexpr size_t queue_size = 256;
auto [logger, async_sink] = creat_async_logger(queue_size, test_sink);
for (size_t i = 0; i < messages; i++) {
logger->info("Hello message #{}", i);
}
logger->flush();
}
// std::this_thread::sleep_for(std::chrono::milliseconds(250));
@@ -88,18 +100,24 @@ TEST_CASE("flush", "[async]") {
REQUIRE(test_sink->flush_counter() == 1);
}
TEST_CASE("tp->wait_empty() ", "[async]") {
auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
TEST_CASE("wait_dtor ", "[async]") {
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 tp = std::make_shared<spdlog::details::thread_pool>(messages, 2);
auto logger = std::make_shared<spdlog::async_logger>("as", test_sink, tp, spdlog::async_overflow_policy::block);
for (size_t i = 0; i < messages; i++) {
logger->info("Hello message #{}", i);
{
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(as->get_overrun_counter() == 0);
REQUIRE(as->get_discard_counter() == 0);
}
logger->flush();
tp.reset();
REQUIRE(test_sink->msg_counter() == messages);
REQUIRE(test_sink->flush_counter() == 1);
@@ -107,18 +125,17 @@ TEST_CASE("tp->wait_empty() ", "[async]") {
TEST_CASE("multi threads", "[async]") {
auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
size_t queue_size = 128;
size_t messages = 256;
size_t n_threads = 10;
{
auto tp = std::make_shared<spdlog::details::thread_pool>(queue_size, 1);
auto logger = std::make_shared<spdlog::async_logger>("as", test_sink, tp, spdlog::async_overflow_policy::block);
constexpr size_t queue_size = 128;
auto [logger, async_sink] = creat_async_logger(queue_size, test_sink);
std::vector<std::thread> threads;
for (size_t i = 0; i < n_threads; i++) {
threads.emplace_back([logger, messages] {
for (size_t j = 0; j < messages; j++) {
logger->info("Hello message #{}", j);
threads.emplace_back([l = logger, msgs = messages] {
for (size_t j = 0; j < msgs; j++) {
l->info("Hello message #{}", j);
}
});
logger->flush();
@@ -128,7 +145,6 @@ TEST_CASE("multi threads", "[async]") {
t.join();
}
}
REQUIRE(test_sink->msg_counter() == messages * n_threads);
REQUIRE(test_sink->flush_counter() == n_threads);
}
@@ -136,45 +152,146 @@ TEST_CASE("multi threads", "[async]") {
TEST_CASE("to_file", "[async]") {
prepare_logdir();
size_t messages = 1024;
size_t tp_threads = 1;
spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME);
{
spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME);
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
auto tp = std::make_shared<spdlog::details::thread_pool>(messages, tp_threads);
auto logger = std::make_shared<spdlog::async_logger>("as", std::move(file_sink), std::move(tp));
auto [logger, async_sink] = creat_async_logger(messages, file_sink);
for (size_t j = 0; j < messages; j++) {
logger->info("Hello message #{}", j);
}
}
require_message_count(TEST_FILENAME, messages);
auto contents = file_contents(TEST_FILENAME);
using spdlog::details::os::default_eol;
REQUIRE(ends_with(contents, spdlog::fmt_lib::format("Hello message #1023{}", default_eol)));
}
TEST_CASE("to_file multi-workers", "[async]") {
prepare_logdir();
size_t messages = 1024 * 10;
size_t tp_threads = 10;
spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME);
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 file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
auto tp = std::make_shared<spdlog::details::thread_pool>(messages, tp_threads);
auto logger = std::make_shared<spdlog::async_logger>("as", std::move(file_sink), std::move(tp));
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);
}
TEST_CASE("start_stop_clbks2", "[async]") {
bool start_called = false;
bool stop_called = false;
{
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);
}
TEST_CASE("start_stop_clbks3", "[async]") {
bool start_called = false;
bool stop_called = false;
{
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);
}
TEST_CASE("start_stop_clbks4", "[async]") {
bool start_called = false;
bool stop_called = false;
{
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;
{
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);
}
TEST_CASE("multi-sinks", "[async]") {
prepare_logdir();
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;
{
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_message_count(TEST_FILENAME, messages);
REQUIRE(test_sink1->msg_counter() == messages);
REQUIRE(test_sink2->msg_counter() == messages);
REQUIRE(test_sink3->msg_counter() == messages);
}
TEST_CASE("bad_tp", "[async]") {
auto test_sink = std::make_shared<spdlog::sinks::test_sink_mt>();
std::shared_ptr<spdlog::details::thread_pool> const empty_tp;
auto logger = std::make_shared<spdlog::async_logger>("as", test_sink, empty_tp);
logger->info("Please throw an exception");
TEST_CASE("level-off", "[async]") {
const auto test_sink = std::make_shared<test_sink_mt>();
test_sink->set_level(spdlog::level::critical);
{
constexpr size_t messages = 256;
constexpr size_t queue_size = 16;
auto [logger, async_sink] = creat_async_logger(queue_size, test_sink);
logger->flush_on(spdlog::level::critical);
for (size_t i = 0; i < messages; i++) {
logger->info("Hello message #{}", i);
}
}
// logger and async_sink are destroyed here so the queue should be emptied
REQUIRE(test_sink->msg_counter() == 0);
REQUIRE(test_sink->flush_counter() == 0);
}
TEST_CASE("backend_ex", "[async]") {
const auto test_sink = std::make_shared<test_sink_mt>();
test_sink->set_exception(std::runtime_error("test backend exception"));
constexpr size_t queue_size = 16;
auto [logger, async_sink] = creat_async_logger(queue_size, test_sink);
REQUIRE_NOTHROW(logger->info("Hello message"));
REQUIRE_NOTHROW(logger->flush());
}

View File

@@ -1,5 +1,6 @@
#include "includes.h"
#include "spdlog/fmt/bin_to_hex.h"
#include "spdlog/bin_to_hex.h"
#include "spdlog/sinks/ostream_sink.h"
#include "test_sink.h"

View File

@@ -3,7 +3,6 @@
* https://raw.githubusercontent.com/gabime/spdlog/v2.x/LICENSE
*/
#include "includes.h"
#include "spdlog/async.h"
#include "spdlog/common.h"
#include "spdlog/sinks/callback_sink.h"
#include "test_sink.h"

View File

@@ -7,94 +7,59 @@
#include "includes.h"
#include "spdlog/sinks/basic_file_sink.h"
#define SIMPLE_LOG "test_logs/simple_log.txt"
#define SIMPLE_ASYNC_LOG "test_logs/simple_async_log.txt"
static spdlog::filename_t log_filename = SPDLOG_FILENAME_T("test_logs/simple_log.txt");
static std::string log_err_msg = "Error during log";
static std::string flush_err_msg = "Error during flush";
class failing_sink final : public spdlog::sinks::base_sink<std::mutex> {
protected:
void sink_it_(const spdlog::details::log_msg &) final { throw std::runtime_error("some error happened during log"); }
void flush_() final { throw std::runtime_error("some error happened during flush"); }
void sink_it_(const spdlog::details::log_msg &) override { throw std::runtime_error(log_err_msg.c_str()); }
void flush_() override { throw std::runtime_error(flush_err_msg.c_str()); }
};
struct custom_ex {};
using namespace spdlog::sinks;
TEST_CASE("default_error_handler", "[errors]") {
prepare_logdir();
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
auto logger = spdlog::basic_logger_mt("test-error", filename);
auto logger = spdlog::create<basic_file_sink_mt>("test-error", log_filename);
logger->set_pattern("%v");
logger->info(SPDLOG_FMT_RUNTIME("Test message {} {}"), 1);
logger->info("Test message {}", 2);
logger->flush();
using spdlog::details::os::default_eol;
REQUIRE(file_contents(SIMPLE_LOG) == spdlog::fmt_lib::format("Test message 2{}", default_eol));
REQUIRE(count_lines(SIMPLE_LOG) == 1);
REQUIRE(file_contents(log_filename) == spdlog::fmt_lib::format("Test message 2{}", default_eol));
REQUIRE(count_lines(log_filename) == 1);
}
TEST_CASE("custom_error_handler", "[errors]") {
prepare_logdir();
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
auto logger = spdlog::basic_logger_mt("test-error", filename);
auto logger = spdlog::create<basic_file_sink_mt>("test-error", log_filename);
logger->flush_on(spdlog::level::info);
logger->set_error_handler([=](const std::string &) { throw custom_ex(); });
logger->set_error_handler([=](const std::string & msg) {
REQUIRE(msg == "argument not found");
throw custom_ex();
});
logger->info("Good message #1");
REQUIRE_THROWS_AS(logger->info(SPDLOG_FMT_RUNTIME("Bad format msg {} {}"), "xxx"), custom_ex);
logger->info("Good message #2");
require_message_count(SIMPLE_LOG, 2);
require_message_count(log_filename, 2);
}
TEST_CASE("default_error_handler2", "[errors]") {
auto logger = std::make_shared<spdlog::logger>("failed_logger", std::make_shared<failing_sink>());
logger->set_error_handler([=](const std::string &) { throw custom_ex(); });
logger->set_error_handler([=](const std::string &msg) {
REQUIRE(msg == log_err_msg);
throw custom_ex();
});
REQUIRE_THROWS_AS(logger->info("Some message"), custom_ex);
}
TEST_CASE("flush_error_handler", "[errors]") {
auto logger = spdlog::create<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string &) { throw custom_ex(); });
logger->set_error_handler([=](const std::string &msg) {
REQUIRE(msg == flush_err_msg);
throw custom_ex();
});
REQUIRE_THROWS_AS(logger->flush(), custom_ex);
}
TEST_CASE("async_error_handler", "[errors]") {
prepare_logdir();
std::string err_msg("log failed with some msg");
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_ASYNC_LOG);
{
spdlog::init_thread_pool(128, 1);
auto logger = spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("logger", filename, true);
logger->set_error_handler([=](const std::string &) {
std::ofstream ofs("test_logs/custom_err.txt");
if (!ofs) {
throw std::runtime_error("Failed open test_logs/custom_err.txt");
}
ofs << err_msg;
});
logger->info("Good message #1");
logger->info(SPDLOG_FMT_RUNTIME("Bad format msg {} {}"), "xxx");
logger->info("Good message #2");
}
spdlog::init_thread_pool(128, 1);
require_message_count(SIMPLE_ASYNC_LOG, 2);
REQUIRE(file_contents("test_logs/custom_err.txt") == err_msg);
}
// Make sure async error handler is executed
TEST_CASE("async_error_handler2", "[errors]") {
prepare_logdir();
std::string err_msg("This is async handler error message");
{
spdlog::details::os::create_dir(SPDLOG_FILENAME_T("test_logs"));
spdlog::init_thread_pool(128, 1);
auto logger = spdlog::create_async<failing_sink>("failed_logger");
logger->set_error_handler([=](const std::string &) {
std::ofstream ofs("test_logs/custom_err2.txt");
if (!ofs) throw std::runtime_error("Failed open test_logs/custom_err2.txt");
ofs << err_msg;
});
logger->info("Hello failure");
}
spdlog::init_thread_pool(128, 1);
REQUIRE(file_contents("test_logs/custom_err2.txt") == err_msg);
}

View File

@@ -136,7 +136,7 @@ TEST_CASE("file_event_handlers", "[file_helper]") {
events.clear();
helper.close();
REQUIRE(events == std::vector<flags>{flags::before_close, flags::after_close});
REQUIRE(file_contents(TEST_FILENAME) == "after_open\nbefore_close\n");
REQUIRE(file_contents(SPDLOG_FILENAME_T(TEST_FILENAME)) == "after_open\nbefore_close\n");
helper.reopen(true);
events.clear();

View File

@@ -9,10 +9,12 @@
#define SIMPLE_LOG "test_logs/simple_log"
#define ROTATING_LOG "test_logs/rotating_log"
using namespace spdlog::sinks;
TEST_CASE("simple_file_logger", "[simple_logger]") {
prepare_logdir();
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
auto logger = spdlog::basic_logger_mt("logger", filename);
auto logger = spdlog::create<basic_file_sink_mt>("test-error", filename);
logger->set_pattern("%v");
logger->info("Test message {}", 1);
logger->info("Test message {}", 2);
@@ -25,7 +27,7 @@ TEST_CASE("simple_file_logger", "[simple_logger]") {
TEST_CASE("flush_on", "[flush_on]") {
prepare_logdir();
spdlog::filename_t filename = SPDLOG_FILENAME_T(SIMPLE_LOG);
auto logger = spdlog::basic_logger_mt("test-error", filename);
auto logger = spdlog::create<basic_file_sink_mt>("test-error", filename);
logger->set_pattern("%v");
logger->set_level(spdlog::level::trace);
logger->flush_on(spdlog::level::info);
@@ -43,8 +45,7 @@ TEST_CASE("rotating_file_logger1", "[rotating_logger]") {
prepare_logdir();
size_t max_size = 1024 * 10;
spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG);
auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 0);
auto logger = spdlog::create<rotating_file_sink_mt>("logger", basename, max_size, 0);
for (int i = 0; i < 10; ++i) {
logger->info("Test message {}", i);
}
@@ -57,16 +58,14 @@ TEST_CASE("rotating_file_logger2", "[rotating_logger]") {
prepare_logdir();
size_t max_size = 1024 * 10;
spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG);
{
// make an initial logger to create the first output file
auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 2, true);
auto logger = spdlog::create<rotating_file_sink_mt>("logger", basename, max_size, 2, true);
for (int i = 0; i < 10; ++i) {
logger->info("Test message {}", i);
}
}
auto logger = spdlog::rotating_logger_mt("logger", basename, max_size, 2, true);
auto logger = spdlog::create<rotating_file_sink_mt>("logger", basename, max_size, 2, true);
for (int i = 0; i < 10; ++i) {
logger->info("Test message {}", i);
}
@@ -89,7 +88,7 @@ TEST_CASE("rotating_file_logger3", "[rotating_logger]") {
prepare_logdir();
size_t max_size = 0;
spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG);
REQUIRE_THROWS_AS(spdlog::rotating_logger_mt("logger", basename, max_size, 0), spdlog::spdlog_ex);
REQUIRE_THROWS_AS(spdlog::create<rotating_file_sink_mt>("logger", basename, max_size, 0), spdlog::spdlog_ex);
}
// test on-demand rotation of logs
@@ -99,15 +98,11 @@ TEST_CASE("rotating_file_logger4", "[rotating_logger]") {
spdlog::filename_t basename = SPDLOG_FILENAME_T(ROTATING_LOG);
auto sink = std::make_shared<spdlog::sinks::rotating_file_sink_st>(basename, max_size, 2);
auto logger = std::make_shared<spdlog::logger>("rotating_sink_logger", sink);
logger->info("Test message - pre-rotation");
logger->flush();
sink->rotate_now();
logger->info("Test message - post-rotation");
logger->flush();
REQUIRE(get_filesize(ROTATING_LOG) > 0);
REQUIRE(get_filesize(ROTATING_LOG ".1") > 0);
}

View File

@@ -15,7 +15,7 @@
TEST_CASE("debug and trace w/o format string", "[macros]") {
prepare_logdir();
spdlog::filename_t filename = SPDLOG_FILENAME_T(TEST_FILENAME);
auto logger = spdlog::basic_logger_mt("logger", filename);
auto logger = spdlog::create<spdlog::sinks::basic_file_sink_mt>("logger", filename);
logger->set_pattern("%v");
logger->set_level(spdlog::level::trace);
@@ -44,7 +44,7 @@ TEST_CASE("disable param evaluation", "[macros]") {
}
TEST_CASE("pass logger pointer", "[macros]") {
auto logger = spdlog::null_logger_mt("refmacro");
auto logger = spdlog::create<spdlog::sinks::null_sink_mt>("refmacro");
auto &ref = *logger;
SPDLOG_LOGGER_TRACE(&ref, "Test message 1");
SPDLOG_LOGGER_DEBUG(&ref, "Test message 2");

View File

@@ -8,6 +8,7 @@
#include "includes.h"
#include "spdlog/details/os.h"
#include "spdlog/sinks/ostream_sink.h"
#include "spdlog/sinks/async_sink.h"
#include "test_sink.h"
template <class T>
@@ -94,25 +95,25 @@ TEST_CASE("clone-logger", "[clone]") {
TEST_CASE("clone async", "[clone]") {
using spdlog::sinks::test_sink_mt;
spdlog::init_thread_pool(4, 1);
auto test_sink = std::make_shared<test_sink_mt>();
auto logger = std::make_shared<spdlog::async_logger>("orig", test_sink, spdlog::thread_pool());
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());
logger->info("Some message 1");
cloned->info("Some message 2");
spdlog::details::os::sleep_for_millis(100);
{
auto cfg = spdlog::sinks::async_sink::config();
cfg.sinks.push_back(test_sink);
auto async_sink = spdlog::sinks::async_sink::with<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());
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]") {

View File

@@ -1,4 +1,5 @@
#include "includes.h"
#include "spdlog/details/mpmc_blocking_q.h"
using std::chrono::milliseconds;
using test_clock = std::chrono::high_resolution_clock;

View File

@@ -37,7 +37,7 @@ TEST_CASE("test_drain_raw", "[ringbuffer_sink]") {
}
int counter = 0;
sink->drain_raw([&](const spdlog::details::log_msg_buffer &buffer) {
sink->drain_raw([&](const spdlog::details::async_log_msg &buffer) {
REQUIRE(buffer.payload.data() == std::to_string(counter + 1));
counter++;
});
@@ -68,4 +68,4 @@ TEST_CASE("test_empty_size", "[ringbuffer_sink]") {
sink->drain([&](std::string_view) {
REQUIRE_FALSE(true); // should not be called since the sink size is 0
});
}
}

View File

@@ -8,10 +8,10 @@
#include <chrono>
#include <mutex>
#include <thread>
#include <exception>
#include "spdlog/details/null_mutex.h"
#include "spdlog/details/os.h"
#include "spdlog/fmt/fmt.h"
#include "spdlog/sinks/base_sink.h"
namespace spdlog {
@@ -37,6 +37,14 @@ public:
delay_ = delay;
}
void set_exception(const std::runtime_error& ex) {
exception_ptr_ = std::make_exception_ptr(ex);
}
void clear_exception() {
exception_ptr_ = nullptr;
}
// return last output without the eol
std::vector<std::string> lines() {
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
@@ -45,6 +53,9 @@ public:
protected:
void sink_it_(const details::log_msg &msg) override {
if (exception_ptr_) {
std::rethrow_exception(exception_ptr_);
}
memory_buf_t formatted;
base_sink<Mutex>::formatter_->format(msg, formatted);
// save the line without the eol
@@ -56,12 +67,18 @@ protected:
std::this_thread::sleep_for(delay_);
}
void flush_() override { flush_counter_++; }
void flush_() override {
if (exception_ptr_) {
std::rethrow_exception(exception_ptr_);
}
flush_counter_++;
}
size_t msg_counter_{0};
size_t flush_counter_{0};
std::chrono::milliseconds delay_{std::chrono::milliseconds::zero()};
std::vector<std::string> lines_;
std::exception_ptr exception_ptr_; // will be thrown on next log or flush if not null
};
using test_sink_mt = test_sink<std::mutex>;

View File

@@ -8,7 +8,7 @@
TEST_CASE("stdout_st", "[stdout]") {
spdlog::set_pattern("%+");
auto l = spdlog::stdout_logger_st("test");
auto l = spdlog::create<spdlog::sinks::stdout_color_sink_st>("test");
l->set_level(spdlog::level::trace);
l->trace("Test stdout_st");
l->debug("Test stdout_st");
@@ -19,7 +19,7 @@ TEST_CASE("stdout_st", "[stdout]") {
}
TEST_CASE("stderr_st", "[stderr]") {
auto l = spdlog::stderr_logger_st("test");
auto l = spdlog::create<spdlog::sinks::stderr_color_sink_st>("test");
l->set_level(spdlog::level::trace);
l->trace("Test stderr_st");
l->debug("Test stderr_st");
@@ -43,7 +43,7 @@ TEST_CASE("stderr_mt", "[stderr]") {
// color loggers
TEST_CASE("stdout_color_st", "[stdout]") {
auto l = spdlog::stdout_color_st("test");
auto l = spdlog::create<spdlog::sinks::stdout_color_sink_st>("test");
l->set_pattern("%+");
l->set_level(spdlog::level::trace);
l->trace("Test stdout_color_st");
@@ -55,7 +55,7 @@ TEST_CASE("stdout_color_st", "[stdout]") {
}
TEST_CASE("stdout_color_mt", "[stdout]") {
auto l = spdlog::stdout_color_mt("test");
auto l = spdlog::create<spdlog::sinks::stdout_color_sink_mt>("test");
l->set_pattern("%+");
l->set_level(spdlog::level::trace);
l->trace("Test stdout_color_mt");
@@ -67,14 +67,14 @@ TEST_CASE("stdout_color_mt", "[stdout]") {
}
TEST_CASE("stderr_color_st", "[stderr]") {
auto l = spdlog::stderr_color_st("test");
auto l = spdlog::create<spdlog::sinks::stderr_color_sink_st>("test");
l->set_pattern("%+");
l->set_level(spdlog::level::debug);
l->debug("Test stderr_color_st");
}
TEST_CASE("stderr_color_mt", "[stderr]") {
auto l = spdlog::stderr_color_mt("test");
auto l = spdlog::create<spdlog::sinks::stderr_color_sink_mt>("test");
l->set_pattern("%+");
l->info("Test stderr_color_mt");
l->warn("Test stderr_color_mt");

View File

@@ -1,5 +1,4 @@
#include "includes.h"
#include "spdlog/async.h"
#include "test_sink.h"
TEST_CASE("time_point1", "[time_point log_msg]") {

View File

@@ -4,7 +4,6 @@
#include <windows.h>
#else
#include <dirent.h>
#include <sys/types.h>
#endif
void prepare_logdir() {
@@ -18,7 +17,7 @@ void prepare_logdir() {
#endif
}
std::string file_contents(const std::string &filename) {
std::string file_contents(const std::filesystem::path &filename) {
std::ifstream ifs(filename, std::ios_base::binary);
if (!ifs) {
throw std::runtime_error("Failed open file ");
@@ -26,7 +25,7 @@ std::string file_contents(const std::string &filename) {
return std::string((std::istreambuf_iterator<char>(ifs)), (std::istreambuf_iterator<char>()));
}
std::size_t count_lines(const spdlog::filename_t &filename) {
std::size_t count_lines(const std::filesystem::path &filename) {
std::ifstream ifs(filename);
if (!ifs) {
throw std::runtime_error("Failed open file ");
@@ -52,7 +51,7 @@ std::size_t get_filesize(const std::string &filename) {
throw std::runtime_error("Failed open file ");
}
return static_cast<std::size_t>(ifs.tellg());
return static_cast<size_t>(ifs.tellg());
}
// source: https://stackoverflow.com/a/2072890/192001
@@ -72,7 +71,7 @@ std::size_t count_files(const std::string &folder) {
// Start iterating over the files in the folder directory.
HANDLE hFind = ::FindFirstFileA((folder + "\\*").c_str(), &ffd);
if (hFind != INVALID_HANDLE_VALUE) {
do // Managed to locate and create an handle to that folder.
do // Managed to locate and create a handle to that folder.
{
if (ffd.cFileName[0] != '.') counter++;
} while (::FindNextFileA(hFind, &ffd) != 0);

View File

@@ -8,7 +8,7 @@ std::size_t count_files(const std::string &folder);
void prepare_logdir();
std::string file_contents(const std::string &filename);
std::string file_contents(const std::filesystem::path &filename);
// std::size_t count_lines(const std::string &filename);
std::size_t count_lines(const std::filesystem::path &filename);
@@ -17,4 +17,4 @@ void require_message_count(const std::filesystem::path &filename, const std::siz
std::size_t get_filesize(const std::string &filename);
bool ends_with(std::string const &value, std::string const &ending);
bool ends_with(std::string const &value, std::string const &ending);