mirror of
https://github.com/gabime/spdlog.git
synced 2025-09-29 01:29:35 +08:00
@@ -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"
|
||||
|
@@ -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());
|
||||
}
|
||||
|
@@ -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"
|
||||
|
||||
|
@@ -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"
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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();
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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");
|
||||
|
@@ -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]") {
|
||||
|
@@ -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;
|
||||
|
@@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@@ -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>;
|
||||
|
@@ -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");
|
||||
|
@@ -1,5 +1,4 @@
|
||||
#include "includes.h"
|
||||
#include "spdlog/async.h"
|
||||
#include "test_sink.h"
|
||||
|
||||
TEST_CASE("time_point1", "[time_point log_msg]") {
|
||||
|
@@ -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);
|
||||
|
@@ -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);
|
||||
|
Reference in New Issue
Block a user