From 7aa0cedaa0cd96d8ebec025f8c018369d57c0811 Mon Sep 17 00:00:00 2001 From: Bailey Chittle Date: Mon, 30 Jan 2023 12:13:08 -0500 Subject: [PATCH] exception safety tests --- tests/test_attributes.cpp | 48 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/tests/test_attributes.cpp b/tests/test_attributes.cpp index 42f7828a..4282240a 100644 --- a/tests/test_attributes.cpp +++ b/tests/test_attributes.cpp @@ -7,6 +7,51 @@ #define TEST_FILENAME "test_logs/attr_test.log" +// helper classes for exception-safe attribute push/pop +// these use RAII to ensure that the context is popped even if an exception is thrown +class scoped_context { + std::shared_ptr logger_; + std::size_t push_counter_ = 0; + +public: + scoped_context(std::shared_ptr logger) : logger_(logger) {} + scoped_context(std::shared_ptr logger, spdlog::attribute_list attrs) : scoped_context(logger) { + push(attrs); + } + + void push(spdlog::attribute_list attrs) { + logger_->push_context(attrs); + ++push_counter_; + } + + ~scoped_context() { + for (std::size_t i = 0; i < push_counter_; ++i) { + logger_->pop_context(); + } + } +}; + +TEST_CASE("attributes test with exceptions ", "[attributes]") +{ + auto test_sink = std::make_shared(); + auto logger = std::make_shared("attr_logger", test_sink); + logger->set_pattern("%v%( %K=%V%)"); + + // push and pop context are not guaranteed to be exception safe + // so we use the helper class + try { + auto ctx = scoped_context(logger, {{"key1", "val1"}}); + logger->info("testing"); + throw std::runtime_error("test exception"); + } catch (const std::exception& e) { + logger->info("caught exception: {}", e.what()); + } + + REQUIRE(test_sink->msg_counter() == 2); + REQUIRE(test_sink->lines()[0] == "testing key1=val1"); // has kv pair in this msg + REQUIRE(test_sink->lines()[1] == "caught exception: test exception"); // doesn't have kv pair in error msg +} + // see if multiple async logs to a single file is thread-safe, i.e. produces coherent structured logs TEST_CASE("async attributes test with threads ", "[attributes]") { @@ -28,8 +73,7 @@ TEST_CASE("async attributes test with threads ", "[attributes]") for (auto lg : loggers) { threads.emplace_back([=](){ // push and pop context are not guaranteed to be thread safe - // therefore, messages from the same logger object have to be in the same thread - // to guarantee thread safety, use a different logger object for each thread + // to guarantee thread safety, use a different logger object for each thread when using attributes for (int i = 0; i < num_msgs; ++i) { lg->push_context({{"key"+std::to_string(i), "val"+std::to_string(i)}}); lg->info("testing {}", i);