Handle exceptions in async sink
This commit is contained in:
parent
0d780b0b58
commit
833bb360a3
@ -209,8 +209,7 @@ private:
|
|||||||
void flush_();
|
void flush_();
|
||||||
[[nodiscard]] bool should_flush_(const details::log_msg &msg) const;
|
[[nodiscard]] bool should_flush_(const details::log_msg &msg) const;
|
||||||
|
|
||||||
// handle errors during logging.
|
// default handler prints the error to stderr
|
||||||
// default handler prints the error to stderr at max rate of 1 message/sec.
|
|
||||||
void err_handler_(const std::string &msg);
|
void err_handler_(const std::string &msg);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -70,9 +70,10 @@ private:
|
|||||||
using queue_t = details::mpmc_blocking_queue<async_log_msg>;
|
using queue_t = details::mpmc_blocking_queue<async_log_msg>;
|
||||||
|
|
||||||
void send_message_(async_log_msg::type msg_type, const details::log_msg &msg) const;
|
void send_message_(async_log_msg::type msg_type, const details::log_msg &msg) const;
|
||||||
void backend_loop_() const;
|
void backend_loop_();
|
||||||
void backend_log_(const details::log_msg &msg) const;
|
void backend_log_(const details::log_msg &msg) ;
|
||||||
void backend_flush_() const;
|
void backend_flush_();
|
||||||
|
void err_handler_(const std::string &msg);
|
||||||
|
|
||||||
config config_;
|
config config_;
|
||||||
std::unique_ptr<queue_t> q_;
|
std::unique_ptr<queue_t> q_;
|
||||||
|
@ -82,7 +82,7 @@ void async_sink::send_message_(async_log_msg::type msg_type, const details::log_
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void async_sink::backend_loop_() const {
|
void async_sink::backend_loop_() {
|
||||||
details::async_log_msg incoming_msg;
|
details::async_log_msg incoming_msg;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
q_->dequeue(incoming_msg);
|
q_->dequeue(incoming_msg);
|
||||||
@ -101,19 +101,41 @@ void async_sink::backend_loop_() const {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void async_sink::backend_log_(const details::log_msg &msg) const {
|
void async_sink::backend_log_(const details::log_msg &msg) {
|
||||||
for (const auto &sink : config_.sinks) {
|
for (const auto &sink : config_.sinks) {
|
||||||
if (sink->should_log(msg.log_level)) {
|
if (sink->should_log(msg.log_level)) {
|
||||||
sink->log(msg);
|
try {
|
||||||
|
sink->log(msg);
|
||||||
|
} catch (const std::exception &ex) {
|
||||||
|
err_handler_(std::string("async log failed: ") + ex.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void async_sink::backend_flush_() const {
|
void async_sink::backend_flush_() {
|
||||||
for (const auto &sink : config_.sinks) {
|
for (const auto &sink : config_.sinks) {
|
||||||
sink->flush();
|
try {
|
||||||
|
sink->flush();
|
||||||
|
} catch (const std::exception &ex) {
|
||||||
|
err_handler_(std::string("async flush failed: ") + ex.what());
|
||||||
|
} catch (...) {
|
||||||
|
err_handler_("Async flush failed with unknown exception");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void async_sink::err_handler_(const std::string &message) {
|
||||||
|
using std::chrono::system_clock;
|
||||||
|
const auto now = system_clock::now();
|
||||||
|
const auto tm_time = details::os::localtime(system_clock::to_time_t(now));
|
||||||
|
char date_buf[64];
|
||||||
|
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
|
||||||
|
#if defined(USING_R) && defined(R_R_H) // if in R environment
|
||||||
|
REprintf("[*** LOG ERROR ***] [%s] [%s] %s\n", date_buf, name().c_str(), message.c_str());
|
||||||
|
#else
|
||||||
|
std::fprintf(stderr, "[*** LOG ERROR ***] [%s] %s\n", date_buf, message.c_str());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace sinks
|
} // namespace sinks
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
@ -286,3 +286,12 @@ TEST_CASE("level-off", "[async]") {
|
|||||||
REQUIRE(test_sink->msg_counter() == 0);
|
REQUIRE(test_sink->msg_counter() == 0);
|
||||||
REQUIRE(test_sink->flush_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());
|
||||||
|
}
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
#include "spdlog/details/null_mutex.h"
|
#include "spdlog/details/null_mutex.h"
|
||||||
#include "spdlog/details/os.h"
|
#include "spdlog/details/os.h"
|
||||||
@ -36,6 +37,14 @@ public:
|
|||||||
delay_ = delay;
|
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
|
// return last output without the eol
|
||||||
std::vector<std::string> lines() {
|
std::vector<std::string> lines() {
|
||||||
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
std::lock_guard<Mutex> lock(base_sink<Mutex>::mutex_);
|
||||||
@ -44,6 +53,9 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void sink_it_(const details::log_msg &msg) override {
|
void sink_it_(const details::log_msg &msg) override {
|
||||||
|
if (exception_ptr_) {
|
||||||
|
std::rethrow_exception(exception_ptr_);
|
||||||
|
}
|
||||||
memory_buf_t formatted;
|
memory_buf_t formatted;
|
||||||
base_sink<Mutex>::formatter_->format(msg, formatted);
|
base_sink<Mutex>::formatter_->format(msg, formatted);
|
||||||
// save the line without the eol
|
// save the line without the eol
|
||||||
@ -55,12 +67,18 @@ protected:
|
|||||||
std::this_thread::sleep_for(delay_);
|
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 msg_counter_{0};
|
||||||
size_t flush_counter_{0};
|
size_t flush_counter_{0};
|
||||||
std::chrono::milliseconds delay_{std::chrono::milliseconds::zero()};
|
std::chrono::milliseconds delay_{std::chrono::milliseconds::zero()};
|
||||||
std::vector<std::string> lines_;
|
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>;
|
using test_sink_mt = test_sink<std::mutex>;
|
||||||
|
Loading…
Reference in New Issue
Block a user