diff --git a/CMakeLists.txt b/CMakeLists.txt index 43f42400..dd9e51ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -181,7 +181,8 @@ set(SPDLOG_HEADERS "include/spdlog/sinks/syslog_sink.h" "include/spdlog/sinks/systemd_sink.h" "include/spdlog/sinks/tcp_sink.h" - "include/spdlog/sinks/udp_sink.h") + "include/spdlog/sinks/udp_sink.h" + "include/spdlog/sinks/async_sink.h") set(SPDLOG_SRCS "src/common.cpp" @@ -196,7 +197,8 @@ set(SPDLOG_SRCS "src/sinks/base_sink.cpp" "src/sinks/basic_file_sink.cpp" "src/sinks/rotating_file_sink.cpp" - "src/sinks/stdout_sinks.cpp") + "src/sinks/stdout_sinks.cpp" + "src/sinks/async_sink.cpp") if(WIN32) list(APPEND SPDLOG_SRCS diff --git a/example/example.cpp b/example/example.cpp index 59aeaffb..4a6e0951 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -119,12 +119,11 @@ void callback_example() { #include "spdlog/sinks/async_sink.h" void async_example() { - auto async_sink = std::make_shared(); + auto logger = spdlog::create_async("some_logger"); auto file_sink = std::make_shared("logs/async_log.txt", true); - async_sink->add_sink(std::move(file_sink)); - spdlog::logger logger("async_logger", async_sink); + logger->sinks().push_back(file_sink); for (int i = 1; i < 101; ++i) { - logger.info("Async message #{}", i); + logger->info("Async message #{}", i); } } diff --git a/include/spdlog/sinks/async_sink.h b/include/spdlog/sinks/async_sink.h index 343fb140..0de171cf 100644 --- a/include/spdlog/sinks/async_sink.h +++ b/include/spdlog/sinks/async_sink.h @@ -3,7 +3,6 @@ #include #include #include -#include #include #include "../details/async_log_msg.h" @@ -32,33 +31,12 @@ public: discard_new // Discard the log message if the queue is full }; - async_sink(size_t queue_size, std::function on_thread_start, std::function on_thread_stop) { - if (queue_size == 0 || queue_size > max_queue_size) { - throw spdlog_ex("async_sink: invalid queue size"); - } - // printf("........... Allocating queue: slot: %zu X %zu bytes ====> %lld KB ..............\n", - // queue_size, sizeof(details::async_log_msg), (sizeof(details::async_log_msg) * queue_size)/1024); - q_ = std::make_unique(queue_size); - - worker_thread_ = std::thread([this, on_thread_start, on_thread_stop] { - if (on_thread_start) on_thread_start(); - this->worker_loop(); - if (on_thread_stop) on_thread_stop(); - }); - } - ~async_sink() override { - try { - q_->enqueue(async_log_msg(async_log_msg::type::terminate)); - worker_thread_.join(); - } catch (...) { - } - }; - - async_sink(): async_sink(default_queue_size, nullptr, nullptr) {} - explicit async_sink(size_t queue_size): async_sink(queue_size, nullptr, nullptr) {} - async_sink(std::function on_thread_start, std::function on_thread_stop): - async_sink(default_queue_size, on_thread_start, on_thread_stop) {} + async_sink(size_t queue_size, std::function on_thread_start, std::function on_thread_stop); + ~async_sink() override; + async_sink(); + explicit async_sink(size_t queue_size); + async_sink(std::function on_thread_start, std::function on_thread_stop); async_sink(const async_sink &) = delete; async_sink &operator=(const async_sink &) = delete; async_sink(async_sink &&) = default; @@ -68,57 +46,16 @@ public: [[nodiscard]] overflow_policy get_overflow_policy() const { return overflow_policy_; } [[nodiscard]] size_t get_overrun_counter() const { return q_->overrun_counter(); } - void reset_overrun_counter() { q_->reset_overrun_counter(); } + void reset_overrun_counter() const { q_->reset_overrun_counter(); } [[nodiscard]] size_t get_discard_counter() const { return q_->discard_counter(); } - void reset_discard_counter() { q_->reset_discard_counter(); } + void reset_discard_counter() const { q_->reset_discard_counter(); } private: - void sink_it_(const details::log_msg &msg) override { - send_message_(async_log_msg::type::log, msg); - } - - void flush_() override { - send_message_(async_log_msg::type::flush, details::log_msg()); - } - - // asynchronously send the log message to the worker thread using the queue. - // take into account the configured overflow policy. - void send_message_(const async_log_msg::type msg_type, const details::log_msg &msg) { - switch (overflow_policy_) { - case overflow_policy::block: - q_->enqueue(async_log_msg(msg_type, msg)); - break; - case overflow_policy::overrun_oldest: - q_->enqueue_nowait(async_log_msg(msg_type, msg)); - break; - case overflow_policy::discard_new: - q_->enqueue_if_have_room(async_log_msg(msg_type, msg)); - break; - default: - assert(false); - throw spdlog_ex("async_sink: invalid overflow policy"); - } - } - - void worker_loop () { - details::async_log_msg incoming_msg; - for (;;) { - q_->dequeue(incoming_msg); - switch (incoming_msg.message_type()) { - case async_log_msg::type::log: - base_t::sink_it_(incoming_msg); - break; - case async_log_msg::type::flush: - base_t::flush_(); - break; - case async_log_msg::type::terminate: - return; - default: - assert(false); - } - } - } + void sink_it_(const details::log_msg &msg) override; + void flush_() override; + void send_message_(const async_log_msg::type msg_type, const details::log_msg &msg); + void worker_loop(); std::atomic overflow_policy_ = overflow_policy::block; std::unique_ptr q_; @@ -129,4 +66,11 @@ using async_sink_mt = async_sink; using async_sink_st = async_sink; } // namespace sinks + +class logger; +template +std::shared_ptr create_async(std::string logger_name, SinkArgs &&...sink_args) { + auto async_sink = std::make_shared(std::forward(sink_args)...); + return std::make_shared(std::move(logger_name), std::move(async_sink)); +} } // namespace spdlog diff --git a/src/sinks/async_sink.cpp b/src/sinks/async_sink.cpp new file mode 100644 index 00000000..9736c620 --- /dev/null +++ b/src/sinks/async_sink.cpp @@ -0,0 +1,108 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#include "spdlog/sinks/async_sink.h" + +#include +#include +#include + +#include "spdlog/common.h" +#include "spdlog/pattern_formatter.h" + +namespace spdlog { +namespace sinks { + +template +async_sink::async_sink(size_t queue_size, std::function on_thread_start, std::function on_thread_stop) + : base_t() { + if (queue_size == 0 || queue_size > max_queue_size) { + throw spdlog_ex("async_sink: invalid queue size"); + } + // printf("........... Allocating queue: slot: %zu X %zu bytes ====> %lld KB ..............\n", + // queue_size, sizeof(details::async_log_msg), (sizeof(details::async_log_msg) * queue_size)/1024); + q_ = std::make_unique(queue_size); + + worker_thread_ = std::thread([this, on_thread_start, on_thread_stop] { + if (on_thread_start) on_thread_start(); + this->worker_loop(); + if (on_thread_stop) on_thread_stop(); + }); +} + +template +async_sink::~async_sink() { + try { + q_->enqueue(async_log_msg(async_log_msg::type::terminate)); + worker_thread_.join(); + } catch (...) { + } +}; + +template +async_sink::async_sink() + : async_sink(default_queue_size, nullptr, nullptr) {} + +template +async_sink::async_sink(size_t queue_size) + : async_sink(queue_size, nullptr, nullptr) {} + +template +async_sink::async_sink(std::function on_thread_start, std::function on_thread_stop) + : async_sink(default_queue_size, on_thread_start, on_thread_stop) {} + +template +void async_sink::sink_it_(const details::log_msg &msg) { + send_message_(async_log_msg::type::log, msg); +} + +template +void async_sink::flush_() { + send_message_(async_log_msg::type::flush, details::log_msg()); +} + +template +void async_sink::send_message_(const async_log_msg::type msg_type, const details::log_msg &msg) { + switch (overflow_policy_) { + case overflow_policy::block: + q_->enqueue(async_log_msg(msg_type, msg)); + break; + case overflow_policy::overrun_oldest: + q_->enqueue_nowait(async_log_msg(msg_type, msg)); + break; + case overflow_policy::discard_new: + q_->enqueue_if_have_room(async_log_msg(msg_type, msg)); + break; + default: + assert(false); + throw spdlog_ex("async_sink: invalid overflow policy"); + } +} + +template +void async_sink::worker_loop() { + details::async_log_msg incoming_msg; + for (;;) { + q_->dequeue(incoming_msg); + switch (incoming_msg.message_type()) { + case async_log_msg::type::log: + base_t::sink_it_(incoming_msg); + break; + case async_log_msg::type::flush: + base_t::flush_(); + break; + case async_log_msg::type::terminate: + return; + default: + assert(false); + } + } +} + +} // namespace sinks +} // namespace spdlog + +// template instantiations +#include "spdlog/details/null_mutex.h" +template class SPDLOG_API spdlog::sinks::async_sink; +template class SPDLOG_API spdlog::sinks::async_sink;