diff --git a/bench/async_bench.cpp b/bench/async_bench.cpp index e5732725..26836da0 100644 --- a/bench/async_bench.cpp +++ b/bench/async_bench.cpp @@ -115,6 +115,8 @@ int main(int argc, char *argv[]) auto file_sink = std::make_shared(filename, true); auto logger = std::make_shared("async_logger", std::move(file_sink), std::move(tp), async_overflow_policy::block); bench_mt(howmany, std::move(logger), threads); + + verify_file(filename, howmany); #ifdef SPDLOG_ASYNC_BENCH_VERIFY verify_file(filename, howmany); #endif // SPDLOG_ASYNC_BENCH_VERIFY diff --git a/include/spdlog/details/log_msg.h b/include/spdlog/details/log_msg.h index 30190f63..ccd41ecb 100644 --- a/include/spdlog/details/log_msg.h +++ b/include/spdlog/details/log_msg.h @@ -4,16 +4,18 @@ #pragma once #include "spdlog/common.h" +#include namespace spdlog { namespace details { struct log_msg { + log_msg() = default; log_msg(source_loc loc, string_view_t logger_name, level::level_enum lvl, string_view_t msg); log_msg(string_view_t logger_name, level::level_enum lvl, string_view_t msg); log_msg(const log_msg &other) = default; - const string_view_t logger_name; + string_view_t logger_name; level::level_enum level{level::off}; log_clock::time_point time; size_t thread_id{0}; @@ -23,7 +25,7 @@ struct log_msg mutable size_t color_range_end{0}; source_loc source; - const string_view_t payload; + string_view_t payload; }; } // namespace details } // namespace spdlog diff --git a/include/spdlog/details/log_msg_buffer.h b/include/spdlog/details/log_msg_buffer.h new file mode 100644 index 00000000..fa5675ce --- /dev/null +++ b/include/spdlog/details/log_msg_buffer.h @@ -0,0 +1,62 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#include "spdlog/details/log_msg.h" +#include "spdlog/fmt/bundled/core.h" + +namespace spdlog { +namespace details { + +// regular log_msgs only holds string_views to stack data - so the cannot be stored for later use. +// this one can, since it contains and owns the payload buffer. +struct log_msg_buffer : log_msg +{ + fmt::basic_memory_buffer loggername_buf; + fmt::basic_memory_buffer payload_buf; + log_msg_buffer() = default; + + log_msg_buffer(const log_msg &orig_msg): log_msg(orig_msg) + { + update_buffers(); + } + + log_msg_buffer(const log_msg_buffer& other):log_msg(other) + { + update_buffers(); + } + + log_msg_buffer(const log_msg_buffer&& other):log_msg(std::move(other)) + { + update_buffers(); + } + + log_msg_buffer& operator=(log_msg_buffer &other) SPDLOG_NOEXCEPT + { + *static_cast(this) = other; + update_buffers(); + return *this; + } + + log_msg_buffer& operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT + { + *static_cast(this) = std::move(other); + update_buffers(); + return *this; + } + + void update_buffers() SPDLOG_NOEXCEPT + { + loggername_buf.clear(); + loggername_buf.append(logger_name.data(), logger_name.data() + logger_name.size()); + logger_name = string_view_t{loggername_buf.data(), loggername_buf.size()}; + + payload_buf.clear(); + payload_buf.append(payload.data(),payload.data() + payload.size()); + payload = string_view_t{payload_buf.data(), payload_buf.size()}; + } +}; + +} +} \ No newline at end of file diff --git a/include/spdlog/details/thread_pool-inl.h b/include/spdlog/details/thread_pool-inl.h index eecfa45c..1f3837d5 100644 --- a/include/spdlog/details/thread_pool-inl.h +++ b/include/spdlog/details/thread_pool-inl.h @@ -100,8 +100,7 @@ bool SPDLOG_INLINE thread_pool::process_next_msg_() { case async_msg_type::log: { - auto msg = incoming_async_msg.to_log_msg(); - incoming_async_msg.worker_ptr->backend_log_(msg); + incoming_async_msg.worker_ptr->backend_log_(incoming_async_msg); return true; } case async_msg_type::flush: diff --git a/include/spdlog/details/thread_pool.h b/include/spdlog/details/thread_pool.h index ff022082..e934c510 100644 --- a/include/spdlog/details/thread_pool.h +++ b/include/spdlog/details/thread_pool.h @@ -3,10 +3,11 @@ #pragma once -#include "spdlog/details/log_msg.h" +#include "spdlog/details/log_msg_buffer.h" #include "spdlog/details/mpmc_blocking_q.h" #include "spdlog/details/os.h" + #include #include #include @@ -27,17 +28,12 @@ enum class async_msg_type terminate }; +#include "spdlog/details/log_msg_buffer.h" // Async msg to move to/from the queue // Movable only. should never be copied -struct async_msg +struct async_msg:log_msg_buffer { async_msg_type msg_type {async_msg_type::log}; - level::level_enum level {level::info}; - log_clock::time_point time; - size_t thread_id {0}; - fmt::basic_memory_buffer raw; - - source_loc source; async_logger_ptr worker_ptr; async_msg() = default; @@ -49,51 +45,35 @@ struct async_msg // support for vs2013 move #if defined(_MSC_VER) && _MSC_VER <= 1800 async_msg(async_msg &&other) - : msg_type(other.msg_type) - , level(other.level) - , time(other.time) - , thread_id(other.thread_id) - , raw(move(other.raw)) - , msg_id(other.msg_id) - , source(other.source) + : log_msg_buffer(std::move(other)) + , msg_type(other.msg_type) , worker_ptr(std::move(other.worker_ptr)) {} async_msg &operator=(async_msg &&other) + { + *static_cast(this) = std::move(other); msg_type = other.msg_type; - level = other.level; - time = other.time; - thread_id = other.thread_id; - raw = std::move(other.raw); - msg_id = other.msg_id; - source = other.source; worker_ptr = std::move(other.worker_ptr); return *this; } #else // (_MSC_VER) && _MSC_VER <= 1800 - async_msg(async_msg &&) = default; - async_msg &operator=(async_msg &&) = default; + async_msg(async_msg &&) SPDLOG_NOEXCEPT = default; + async_msg &operator=(async_msg &&) SPDLOG_NOEXCEPT = default; #endif // construct from log_msg with given type async_msg(async_logger_ptr &&worker, async_msg_type the_type, details::log_msg &m) - : msg_type(the_type) - , level(m.level) - , time(m.time) - , thread_id(m.thread_id) - , source(m.source) + : log_msg_buffer(m) + , msg_type(the_type) , worker_ptr(std::move(worker)) { - raw.append(m.payload.data(), m.payload.data() + m.payload.size()); } async_msg(async_logger_ptr &&worker, async_msg_type the_type) - : msg_type(the_type) - , level(level::off) - , time() - , thread_id(0) - , source() + : log_msg_buffer() + , msg_type(the_type) , worker_ptr(std::move(worker)) {} @@ -101,17 +81,6 @@ struct async_msg : async_msg(nullptr, the_type) {} - // copy into log_msg - log_msg to_log_msg() - { - log_msg msg(string_view_t(worker_ptr->name()), level, string_view_t(raw.data(), raw.size())); - msg.time = time; - msg.thread_id = thread_id; - msg.source = source; - msg.color_range_start = 0; - msg.color_range_end = 0; - return msg; - } }; class thread_pool diff --git a/include/spdlog/sinks/backtrace-sink.h b/include/spdlog/sinks/backtrace-sink.h new file mode 100644 index 00000000..470763b8 --- /dev/null +++ b/include/spdlog/sinks/backtrace-sink.h @@ -0,0 +1,98 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#include "dist_sink.h" +#include "spdlog/details/null_mutex.h" +#include "spdlog/details/log_msg.h" +#include "spdlog/details/log_msg_buffer.h" +#include "spdlog/details/circular_q.h" + +#include +#include +#include + +// Store log messages in circular buffer +// If it encounters a message with high enough level, it will send all pervious message to its child sinks +// Useful for storing debug data in case of error/warning happens + +// +// Example: +// +// #include "spdlog/spdlog.h" +// #include "spdlog/sinks/backtrace_sink.h +// +// int main() { +// auto backtrace_sink = std::make_shared(); +// backtrace_sink ->add_sink(std::make_shared()); +// spdlog::logger l("logger", backtrace_sink); +// logger.set_level(spdlog::level::trace); +// l.trace("Hello"); +// l.debug("Hello"); +// l.info("Hello"); +// l.warn("This will trigger the log of all prev messages in the queue"); +// } + + +namespace spdlog { +namespace sinks { +template +class backtrace_sink : public dist_sink +{ +public: + explicit backtrace_sink(level::level_enum trigger_level = spdlog::level::warn, size_t n_messages = 32) + : trigger_level_{trigger_level}, traceback_msgs_{n_messages} + {} + +protected: + level::level_enum trigger_level_; + + details::circular_q traceback_msgs_; + + // if current message is high enough, trigger a backtrace log, + // otherwise save the message in the queue for future trigger. + void sink_it_(const details::log_msg &msg) override + { + if(msg.level < trigger_level_) + { + traceback_msgs_.push_back(details::log_msg_buffer(msg)); + } + if(msg.level > level::debug) + { + dist_sink::sink_it_(msg); + } + if(msg.level >= trigger_level_) + { + log_backtrace_(msg.logger_name); + } + } + + void log_backtrace_(const string_view_t& logger_name) + { + if(traceback_msgs_.empty()) + { + return; + } + + dist_sink::sink_it_(details::log_msg{ + logger_name,level::info,"********************* [Backtrace Start] *********************"}); + + do + { + details::log_msg_buffer popped; + traceback_msgs_.pop_front(popped); + dist_sink::sink_it_(popped); + } + while (!traceback_msgs_.empty()); + + dist_sink::sink_it_(details::log_msg{ + logger_name,level::info,"********************* [Backtrace End] ***********************"}); + } +}; + +using backtrace_sink_mt = backtrace_sink; +using backtrace_sink_st = backtrace_sink; + +} // namespace sinks +} // namespace spdlog