mirror of
https://github.com/gabime/spdlog.git
synced 2024-11-15 16:35:45 +08:00
backtrace sink and refactoring
This commit is contained in:
parent
74df115fc1
commit
6b527a50dd
@ -115,6 +115,8 @@ int main(int argc, char *argv[])
|
|||||||
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
|
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(filename, true);
|
||||||
auto logger = std::make_shared<async_logger>("async_logger", std::move(file_sink), std::move(tp), async_overflow_policy::block);
|
auto logger = std::make_shared<async_logger>("async_logger", std::move(file_sink), std::move(tp), async_overflow_policy::block);
|
||||||
bench_mt(howmany, std::move(logger), threads);
|
bench_mt(howmany, std::move(logger), threads);
|
||||||
|
|
||||||
|
verify_file(filename, howmany);
|
||||||
#ifdef SPDLOG_ASYNC_BENCH_VERIFY
|
#ifdef SPDLOG_ASYNC_BENCH_VERIFY
|
||||||
verify_file(filename, howmany);
|
verify_file(filename, howmany);
|
||||||
#endif // SPDLOG_ASYNC_BENCH_VERIFY
|
#endif // SPDLOG_ASYNC_BENCH_VERIFY
|
||||||
|
@ -4,16 +4,18 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "spdlog/common.h"
|
#include "spdlog/common.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace spdlog {
|
namespace spdlog {
|
||||||
namespace details {
|
namespace details {
|
||||||
struct log_msg
|
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(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(string_view_t logger_name, level::level_enum lvl, string_view_t msg);
|
||||||
log_msg(const log_msg &other) = default;
|
log_msg(const log_msg &other) = default;
|
||||||
|
|
||||||
const string_view_t logger_name;
|
string_view_t logger_name;
|
||||||
level::level_enum level{level::off};
|
level::level_enum level{level::off};
|
||||||
log_clock::time_point time;
|
log_clock::time_point time;
|
||||||
size_t thread_id{0};
|
size_t thread_id{0};
|
||||||
@ -23,7 +25,7 @@ struct log_msg
|
|||||||
mutable size_t color_range_end{0};
|
mutable size_t color_range_end{0};
|
||||||
|
|
||||||
source_loc source;
|
source_loc source;
|
||||||
const string_view_t payload;
|
string_view_t payload;
|
||||||
};
|
};
|
||||||
} // namespace details
|
} // namespace details
|
||||||
} // namespace spdlog
|
} // namespace spdlog
|
||||||
|
62
include/spdlog/details/log_msg_buffer.h
Normal file
62
include/spdlog/details/log_msg_buffer.h
Normal file
@ -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<char, 40> loggername_buf;
|
||||||
|
fmt::basic_memory_buffer<char, 200> 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<log_msg*>(this) = other;
|
||||||
|
update_buffers();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
log_msg_buffer& operator=(log_msg_buffer &&other) SPDLOG_NOEXCEPT
|
||||||
|
{
|
||||||
|
*static_cast<log_msg*>(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()};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -100,8 +100,7 @@ bool SPDLOG_INLINE thread_pool::process_next_msg_()
|
|||||||
{
|
{
|
||||||
case async_msg_type::log:
|
case async_msg_type::log:
|
||||||
{
|
{
|
||||||
auto msg = incoming_async_msg.to_log_msg();
|
incoming_async_msg.worker_ptr->backend_log_(incoming_async_msg);
|
||||||
incoming_async_msg.worker_ptr->backend_log_(msg);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case async_msg_type::flush:
|
case async_msg_type::flush:
|
||||||
|
@ -3,10 +3,11 @@
|
|||||||
|
|
||||||
#pragma once
|
#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/mpmc_blocking_q.h"
|
||||||
#include "spdlog/details/os.h"
|
#include "spdlog/details/os.h"
|
||||||
|
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@ -27,17 +28,12 @@ enum class async_msg_type
|
|||||||
terminate
|
terminate
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#include "spdlog/details/log_msg_buffer.h"
|
||||||
// Async msg to move to/from the queue
|
// Async msg to move to/from the queue
|
||||||
// Movable only. should never be copied
|
// Movable only. should never be copied
|
||||||
struct async_msg
|
struct async_msg:log_msg_buffer
|
||||||
{
|
{
|
||||||
async_msg_type msg_type {async_msg_type::log};
|
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<char, 176> raw;
|
|
||||||
|
|
||||||
source_loc source;
|
|
||||||
async_logger_ptr worker_ptr;
|
async_logger_ptr worker_ptr;
|
||||||
|
|
||||||
async_msg() = default;
|
async_msg() = default;
|
||||||
@ -49,51 +45,35 @@ struct async_msg
|
|||||||
// support for vs2013 move
|
// support for vs2013 move
|
||||||
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||||
async_msg(async_msg &&other)
|
async_msg(async_msg &&other)
|
||||||
: msg_type(other.msg_type)
|
: log_msg_buffer(std::move(other))
|
||||||
, level(other.level)
|
, msg_type(other.msg_type)
|
||||||
, time(other.time)
|
|
||||||
, thread_id(other.thread_id)
|
|
||||||
, raw(move(other.raw))
|
|
||||||
, msg_id(other.msg_id)
|
|
||||||
, source(other.source)
|
|
||||||
, worker_ptr(std::move(other.worker_ptr))
|
, worker_ptr(std::move(other.worker_ptr))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
async_msg &operator=(async_msg &&other)
|
async_msg &operator=(async_msg &&other)
|
||||||
|
|
||||||
{
|
{
|
||||||
|
*static_cast<log_msg_buffer*>(this) = std::move(other);
|
||||||
msg_type = other.msg_type;
|
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);
|
worker_ptr = std::move(other.worker_ptr);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
#else // (_MSC_VER) && _MSC_VER <= 1800
|
#else // (_MSC_VER) && _MSC_VER <= 1800
|
||||||
async_msg(async_msg &&) = default;
|
async_msg(async_msg &&) SPDLOG_NOEXCEPT = default;
|
||||||
async_msg &operator=(async_msg &&) = default;
|
async_msg &operator=(async_msg &&) SPDLOG_NOEXCEPT = default;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// construct from log_msg with given type
|
// construct from log_msg with given type
|
||||||
async_msg(async_logger_ptr &&worker, async_msg_type the_type, details::log_msg &m)
|
async_msg(async_logger_ptr &&worker, async_msg_type the_type, details::log_msg &m)
|
||||||
: msg_type(the_type)
|
: log_msg_buffer(m)
|
||||||
, level(m.level)
|
, msg_type(the_type)
|
||||||
, time(m.time)
|
|
||||||
, thread_id(m.thread_id)
|
|
||||||
, source(m.source)
|
|
||||||
, worker_ptr(std::move(worker))
|
, 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)
|
async_msg(async_logger_ptr &&worker, async_msg_type the_type)
|
||||||
: msg_type(the_type)
|
: log_msg_buffer()
|
||||||
, level(level::off)
|
, msg_type(the_type)
|
||||||
, time()
|
|
||||||
, thread_id(0)
|
|
||||||
, source()
|
|
||||||
, worker_ptr(std::move(worker))
|
, worker_ptr(std::move(worker))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
@ -101,17 +81,6 @@ struct async_msg
|
|||||||
: async_msg(nullptr, the_type)
|
: 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
|
class thread_pool
|
||||||
|
98
include/spdlog/sinks/backtrace-sink.h
Normal file
98
include/spdlog/sinks/backtrace-sink.h
Normal file
@ -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 <mutex>
|
||||||
|
#include <string>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
// 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>();
|
||||||
|
// backtrace_sink ->add_sink(std::make_shared<stdout_color_sink_mt>());
|
||||||
|
// 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<typename Mutex>
|
||||||
|
class backtrace_sink : public dist_sink<Mutex>
|
||||||
|
{
|
||||||
|
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<details::log_msg_buffer> 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<Mutex>::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<Mutex>::sink_it_(details::log_msg{
|
||||||
|
logger_name,level::info,"********************* [Backtrace Start] *********************"});
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
details::log_msg_buffer popped;
|
||||||
|
traceback_msgs_.pop_front(popped);
|
||||||
|
dist_sink<Mutex>::sink_it_(popped);
|
||||||
|
}
|
||||||
|
while (!traceback_msgs_.empty());
|
||||||
|
|
||||||
|
dist_sink<Mutex>::sink_it_(details::log_msg{
|
||||||
|
logger_name,level::info,"********************* [Backtrace End] ***********************"});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using backtrace_sink_mt = backtrace_sink<std::mutex>;
|
||||||
|
using backtrace_sink_st = backtrace_sink<details::null_mutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
Loading…
Reference in New Issue
Block a user