From ee815042dd0649756eb99e5ffe94b34c0d6fed25 Mon Sep 17 00:00:00 2001 From: Philippe Serreault Date: Wed, 11 May 2016 17:22:09 +0200 Subject: [PATCH] In async mode, worker thread can now execute an optional teardown callback upon exit. (Note: this can be helpful when a custom sink invokes a JNI callback, which implies that worker thread was previously attached to JVM, and needs to be cleanly detached upon exit) --- include/spdlog/async_logger.h | 12 +++++++----- include/spdlog/details/async_log_helper.h | 11 +++++++++-- include/spdlog/details/async_logger_impl.h | 15 +++++++++------ include/spdlog/details/registry.h | 6 ++++-- include/spdlog/details/spdlog_impl.h | 5 ++--- include/spdlog/spdlog.h | 5 ++++- 6 files changed, 35 insertions(+), 19 deletions(-) diff --git a/include/spdlog/async_logger.h b/include/spdlog/async_logger.h index be215010..786eb02e 100644 --- a/include/spdlog/async_logger.h +++ b/include/spdlog/async_logger.h @@ -13,7 +13,7 @@ // 1. Checks if its log level is enough to log the message // 2. Push a new copy of the message to a queue (or block the caller until space is available in the queue) // 3. will throw spdlog_ex upon log exceptions -// Upong destruction, logs all remaining messages in the queue before destructing.. +// Upon destruction, logs all remaining messages in the queue before destructing.. #include #include @@ -41,21 +41,24 @@ public: size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, - const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); + const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), + const std::function& worker_teardown_cb = nullptr); async_logger(const std::string& logger_name, sinks_init_list sinks, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, - const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); + const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), + const std::function& worker_teardown_cb = nullptr); async_logger(const std::string& logger_name, sink_ptr single_sink, size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, - const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); + const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), + const std::function& worker_teardown_cb = nullptr); void flush() override; @@ -71,4 +74,3 @@ private: #include - diff --git a/include/spdlog/details/async_log_helper.h b/include/spdlog/details/async_log_helper.h index a7006708..8d7f6cca 100644 --- a/include/spdlog/details/async_log_helper.h +++ b/include/spdlog/details/async_log_helper.h @@ -121,7 +121,8 @@ public: size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, - const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); + const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), + const std::function& worker_teardown_cb = nullptr); void log(const details::log_msg& msg); @@ -157,6 +158,9 @@ private: // auto periodic sink flush parameter const std::chrono::milliseconds _flush_interval_ms; + // worker thread teardown callback + const std::function _worker_teardown_cb; + // worker thread std::thread _worker_thread; @@ -190,7 +194,8 @@ inline spdlog::details::async_log_helper::async_log_helper( size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, - const std::chrono::milliseconds& flush_interval_ms): + const std::chrono::milliseconds& flush_interval_ms, + const std::function& worker_teardown_cb): _formatter(formatter), _sinks(sinks), _q(queue_size), @@ -199,6 +204,7 @@ inline spdlog::details::async_log_helper::async_log_helper( _overflow_policy(overflow_policy), _worker_warmup_cb(worker_warmup_cb), _flush_interval_ms(flush_interval_ms), + _worker_teardown_cb(worker_teardown_cb), _worker_thread(&async_log_helper::worker_loop, this) {} @@ -254,6 +260,7 @@ inline void spdlog::details::async_log_helper::worker_loop() auto last_pop = details::os::now(); auto last_flush = last_pop; while(process_next_msg(last_pop, last_flush)); + if (_worker_teardown_cb) _worker_teardown_cb(); } catch (const std::exception& ex) { diff --git a/include/spdlog/details/async_logger_impl.h b/include/spdlog/details/async_logger_impl.h index 140d45f4..ebacdb59 100644 --- a/include/spdlog/details/async_logger_impl.h +++ b/include/spdlog/details/async_logger_impl.h @@ -23,9 +23,10 @@ inline spdlog::async_logger::async_logger(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, - const std::chrono::milliseconds& flush_interval_ms) : + const std::chrono::milliseconds& flush_interval_ms, + const std::function& worker_teardown_cb) : logger(logger_name, begin, end), - _async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms)) + _async_log_helper(new details::async_log_helper(_formatter, _sinks, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb)) { } @@ -34,19 +35,21 @@ inline spdlog::async_logger::async_logger(const std::string& logger_name, size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, - const std::chrono::milliseconds& flush_interval_ms) : - async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms) {} + const std::chrono::milliseconds& flush_interval_ms, + const std::function& worker_teardown_cb) : + async_logger(logger_name, sinks.begin(), sinks.end(), queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {} inline spdlog::async_logger::async_logger(const std::string& logger_name, sink_ptr single_sink, size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, - const std::chrono::milliseconds& flush_interval_ms) : + const std::chrono::milliseconds& flush_interval_ms, + const std::function& worker_teardown_cb) : async_logger(logger_name, { single_sink -}, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms) {} + }, queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb) {} inline void spdlog::async_logger::flush() diff --git a/include/spdlog/details/registry.h b/include/spdlog/details/registry.h index 0a35451c..7d744f89 100644 --- a/include/spdlog/details/registry.h +++ b/include/spdlog/details/registry.h @@ -53,7 +53,7 @@ public: throw_if_exists(logger_name); std::shared_ptr new_logger; if (_async_mode) - new_logger = std::make_shared(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms); + new_logger = std::make_shared(logger_name, sinks_begin, sinks_end, _async_q_size, _overflow_policy, _worker_warmup_cb, _flush_interval_ms, _worker_teardown_cb); else new_logger = std::make_shared(logger_name, sinks_begin, sinks_end); @@ -112,7 +112,7 @@ public: _level = log_level; } - void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms) + void set_async_mode(size_t q_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function& worker_teardown_cb) { std::lock_guard lock(_mutex); _async_mode = true; @@ -120,6 +120,7 @@ public: _overflow_policy = overflow_policy; _worker_warmup_cb = worker_warmup_cb; _flush_interval_ms = flush_interval_ms; + _worker_teardown_cb = worker_teardown_cb; } void set_sync_mode() @@ -153,6 +154,7 @@ private: async_overflow_policy _overflow_policy = async_overflow_policy::block_retry; std::function _worker_warmup_cb = nullptr; std::chrono::milliseconds _flush_interval_ms; + std::function _worker_teardown_cb = nullptr; }; #ifdef SPDLOG_NO_REGISTRY_MUTEX typedef registry_t registry; diff --git a/include/spdlog/details/spdlog_impl.h b/include/spdlog/details/spdlog_impl.h index 3942e5a7..8337ab47 100644 --- a/include/spdlog/details/spdlog_impl.h +++ b/include/spdlog/details/spdlog_impl.h @@ -132,9 +132,9 @@ inline void spdlog::set_level(level::level_enum log_level) } -inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms) +inline void spdlog::set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy, const std::function& worker_warmup_cb, const std::chrono::milliseconds& flush_interval_ms, const std::function& worker_teardown_cb) { - details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms); + details::registry::instance().set_async_mode(queue_size, overflow_policy, worker_warmup_cb, flush_interval_ms, worker_teardown_cb); } inline void spdlog::set_sync_mode() @@ -146,4 +146,3 @@ inline void spdlog::drop_all() { details::registry::instance().drop_all(); } - diff --git a/include/spdlog/spdlog.h b/include/spdlog/spdlog.h index f12e87ce..2dc02090 100644 --- a/include/spdlog/spdlog.h +++ b/include/spdlog/spdlog.h @@ -53,7 +53,10 @@ void set_level(level::level_enum log_level); // worker_warmup_cb (optional): // callback function that will be called in worker thread upon start (can be used to init stuff like thread affinity) // -void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero()); +// worker_teardown_cb (optional): +// callback function that will be called in worker thread upon exit +// +void set_async_mode(size_t queue_size, const async_overflow_policy overflow_policy = async_overflow_policy::block_retry, const std::function& worker_warmup_cb = nullptr, const std::chrono::milliseconds& flush_interval_ms = std::chrono::milliseconds::zero(), const std::function& worker_teardown_cb = nullptr); // Turn off async mode void set_sync_mode();