mirror of
https://github.com/gabime/spdlog.git
synced 2025-01-28 00:10:21 +08:00
wip async sink
This commit is contained in:
parent
a00229eaa5
commit
07c697f4f5
@ -153,7 +153,7 @@ set(SPDLOG_HEADERS
|
|||||||
"include/spdlog/details/file_helper.h"
|
"include/spdlog/details/file_helper.h"
|
||||||
"include/spdlog/details/fmt_helper.h"
|
"include/spdlog/details/fmt_helper.h"
|
||||||
"include/spdlog/details/log_msg.h"
|
"include/spdlog/details/log_msg.h"
|
||||||
"include/spdlog/details/log_msg_buffer.h"
|
"include/spdlog/details/async_log_msg.h"
|
||||||
"include/spdlog/details/mpmc_blocking_q.h"
|
"include/spdlog/details/mpmc_blocking_q.h"
|
||||||
"include/spdlog/details/null_mutex.h"
|
"include/spdlog/details/null_mutex.h"
|
||||||
"include/spdlog/details/os.h"
|
"include/spdlog/details/os.h"
|
||||||
@ -192,7 +192,7 @@ set(SPDLOG_SRCS
|
|||||||
"src/details/file_helper.cpp"
|
"src/details/file_helper.cpp"
|
||||||
"src/details/os_filesystem.cpp"
|
"src/details/os_filesystem.cpp"
|
||||||
"src/details/log_msg.cpp"
|
"src/details/log_msg.cpp"
|
||||||
"src/details/log_msg_buffer.cpp"
|
"src/details/async_log_msg.cpp"
|
||||||
"src/details/context.cpp"
|
"src/details/context.cpp"
|
||||||
"src/sinks/base_sink.cpp"
|
"src/sinks/base_sink.cpp"
|
||||||
"src/sinks/basic_file_sink.cpp"
|
"src/sinks/basic_file_sink.cpp"
|
||||||
|
@ -5,73 +5,84 @@
|
|||||||
// spdlog usage example
|
// spdlog usage example
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <clocale>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
|
|
||||||
void stdout_logger_example();
|
#include "/home/gabi//spdlog2/tests/test_sink.h"
|
||||||
void basic_example();
|
#include "spdlog/sinks/async_sink.h"
|
||||||
void rotating_example();
|
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||||
void daily_example();
|
|
||||||
void callback_example();
|
|
||||||
void async_example();
|
|
||||||
void binary_example();
|
|
||||||
void vector_example();
|
|
||||||
void stopwatch_example();
|
|
||||||
void trace_example();
|
|
||||||
void multi_sink_example();
|
|
||||||
void user_defined_example();
|
|
||||||
void err_handler_example();
|
|
||||||
void syslog_example();
|
|
||||||
void udp_example();
|
|
||||||
void custom_flags_example();
|
|
||||||
void file_events_example();
|
|
||||||
void replace_global_logger_example();
|
|
||||||
|
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
#include "spdlog/version.h"
|
#include "spdlog/stopwatch.h"
|
||||||
|
|
||||||
using namespace spdlog::sinks;
|
using namespace spdlog::sinks;
|
||||||
|
|
||||||
|
void worker_thread(std::shared_ptr<spdlog::logger> logger, int n_messages) {
|
||||||
|
for (int i = 0; i < n_messages; ++i) {
|
||||||
|
// std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||||
|
logger->info("Worker thread message #{}", i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool async_example2(size_t n_threads, size_t n_messages) {
|
||||||
|
auto test_sink = std::make_shared<test_sink_st>();
|
||||||
|
auto n_total_messages = n_messages * n_threads;
|
||||||
|
{
|
||||||
|
std::vector<std::thread> threads;
|
||||||
|
auto async_sink = std::make_shared<async_sink_mt>();
|
||||||
|
async_sink->add_sink(test_sink);
|
||||||
|
auto logger = std::make_shared<spdlog::logger>("async_logger", async_sink);
|
||||||
|
logger->set_level(spdlog::level::trace);
|
||||||
|
|
||||||
|
spdlog::stopwatch sw;
|
||||||
|
|
||||||
|
for (int i = 0; i < n_threads; ++i) {
|
||||||
|
threads.emplace_back(worker_thread, logger, n_messages);
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for worker threads to finish
|
||||||
|
for (auto &t : threads) {
|
||||||
|
t.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
auto millis = sw.elapsed_ms().count();
|
||||||
|
if (millis == 0) {
|
||||||
|
millis = 1;
|
||||||
|
}
|
||||||
|
spdlog::info("Elapsed {} millis ({:L}/sec)", millis, n_total_messages * 1000 / millis);
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for the worker thread to finish
|
||||||
|
// check that all messages were processed
|
||||||
|
if (test_sink->msg_counter() != n_messages * n_threads) {
|
||||||
|
spdlog::error("Expected: {:L}, Counted: {:L}", n_total_messages,
|
||||||
|
test_sink->msg_counter());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
spdlog::info("OK: {:L}", test_sink->msg_counter());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#include <random>
|
||||||
int main(int, char *[]) {
|
int main(int, char *[]) {
|
||||||
spdlog::info("Welcome to spdlog version {}.{}.{} !", SPDLOG_VER_MAJOR, SPDLOG_VER_MINOR, SPDLOG_VER_PATCH);
|
std::locale::global(std::locale("en_US.UTF-8"));
|
||||||
spdlog::warn("Easy padding in numbers like {:08d}", 12);
|
// constexpr size_t n_messages = 1'00;
|
||||||
spdlog::critical("Support for int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", 42);
|
// constexpr size_t n_threads = 1;
|
||||||
spdlog::info("Support for floats {:03.2f}", 1.23456);
|
|
||||||
spdlog::info("Positional args are {1} {0}..", "too", "supported");
|
|
||||||
spdlog::info("{:>8} aligned, {:<8} aligned", "right", "left");
|
|
||||||
|
|
||||||
// Runtime log levels
|
|
||||||
spdlog::set_level(spdlog::level::info); // Set global log level to info
|
|
||||||
spdlog::debug("This message should not be displayed!");
|
|
||||||
spdlog::set_level(spdlog::level::trace); // Set specific logger's log level
|
|
||||||
spdlog::debug("This message should be displayed..");
|
|
||||||
|
|
||||||
// Customize msg format for all loggers
|
|
||||||
spdlog::set_pattern("[%H:%M:%S %z] [%^%L%$] [thread %t] %v");
|
|
||||||
spdlog::info("This an info message with custom format");
|
|
||||||
spdlog::set_pattern("%+"); // back to default format
|
|
||||||
spdlog::set_level(spdlog::level::info);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
stdout_logger_example();
|
// random n_messages and n_threads
|
||||||
basic_example();
|
std::mt19937 gen(std::random_device{}());
|
||||||
rotating_example();
|
for (int i = 0; i < 100; i++) {
|
||||||
daily_example();
|
auto n_threads = std::uniform_int_distribution<size_t>(1, 10)(gen);
|
||||||
callback_example();
|
auto n_messages = std::uniform_int_distribution<size_t>(1, 1'000'000)(gen);
|
||||||
async_example();
|
|
||||||
binary_example();
|
spdlog::info("***********************************");
|
||||||
vector_example();
|
spdlog::info("Test #{}. Threads: {}, Count: {:L}", i, n_threads, n_messages);
|
||||||
multi_sink_example();
|
if (!async_example2(n_threads, n_messages)) {
|
||||||
user_defined_example();
|
spdlog::error("Stopped");
|
||||||
err_handler_example();
|
break;
|
||||||
trace_example();
|
}
|
||||||
stopwatch_example();
|
}
|
||||||
udp_example();
|
|
||||||
custom_flags_example();
|
|
||||||
file_events_example();
|
|
||||||
replace_global_logger_example();
|
|
||||||
|
|
||||||
// Release all spdlog resources
|
|
||||||
// This is optional (only mandatory if using windows + async log).
|
|
||||||
spdlog::shutdown();
|
spdlog::shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -81,223 +92,3 @@ int main(int, char *[]) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
|
||||||
// or #include "spdlog/sinks/stdout_sinks.h" if no colors needed.
|
|
||||||
void stdout_logger_example() {
|
|
||||||
// Create color multithreading logger.
|
|
||||||
auto console = spdlog::create<stdout_color_sink_mt>("console");
|
|
||||||
// or for stderr:
|
|
||||||
//auto console = spdlog::create<stderr_color_sink_mt>("console");
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "spdlog/sinks/basic_file_sink.h"
|
|
||||||
void basic_example() {
|
|
||||||
// Create basic file logger (not rotated).
|
|
||||||
auto my_logger = spdlog::create<basic_file_sink_mt>("file_logger", "logs/basic-log.txt", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "spdlog/sinks/rotating_file_sink.h"
|
|
||||||
void rotating_example() {
|
|
||||||
// Create a file rotating logger with 5mb size max and 3 rotated files.
|
|
||||||
auto rotating_logger = spdlog::create<rotating_file_sink_mt>("some_logger_name", "logs/rotating.txt", 1048576 * 5, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "spdlog/sinks/daily_file_sink.h"
|
|
||||||
void daily_example() {
|
|
||||||
// Create a daily logger - a new file is created every day on 2:30am.
|
|
||||||
auto daily_logger = spdlog::create<daily_file_format_sink_mt>("daily_logger", "logs/daily.txt", 2, 30);
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "spdlog/sinks/callback_sink.h"
|
|
||||||
void callback_example() {
|
|
||||||
// Create the logger
|
|
||||||
auto logger = spdlog::create<callback_sink_mt>("custom_callback_logger", [](const spdlog::details::log_msg & /*msg*/) {
|
|
||||||
// do what you need to do with msg
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void async_example() {
|
|
||||||
// TODO
|
|
||||||
// // Default thread pool settings can be modified *before* creating the async logger:
|
|
||||||
// // spdlog::init_thread_pool(32768, 1); // queue with max 32k items 1 backing thread.
|
|
||||||
// auto async_file = spdlog::basic_logger_mt<spdlog::async_factory>("async_file_logger", "logs/async_log.txt");
|
|
||||||
// // alternatively:
|
|
||||||
// // auto async_file =
|
|
||||||
// // spdlog::create_async<spdlog::sinks::basic_file_sink_mt>("async_file_logger",
|
|
||||||
// // "logs/async_log.txt");
|
|
||||||
//
|
|
||||||
// for (int i = 1; i < 101; ++i) {
|
|
||||||
// async_file->info("Async message #{}", i);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log binary data as hex.
|
|
||||||
// Many types of std::container<char> types can be used.
|
|
||||||
// Iterator ranges are supported too.
|
|
||||||
// Format flags:
|
|
||||||
// {:X} - print in uppercase.
|
|
||||||
// {:s} - don't separate each byte with space.
|
|
||||||
// {:p} - don't print the position on each line start.
|
|
||||||
// {:n} - don't split the output to lines.
|
|
||||||
|
|
||||||
#include "spdlog/fmt/bin_to_hex.h"
|
|
||||||
void binary_example() {
|
|
||||||
std::vector<char> buf;
|
|
||||||
for (int i = 0; i < 80; i++) {
|
|
||||||
buf.push_back(static_cast<char>(i & 0xff));
|
|
||||||
}
|
|
||||||
spdlog::info("Binary example: {}", spdlog::to_hex(buf));
|
|
||||||
spdlog::info("Another binary example:{:n}", spdlog::to_hex(std::begin(buf), std::begin(buf) + 10));
|
|
||||||
// more examples:
|
|
||||||
// logger->info("uppercase: {:X}", spdlog::to_hex(buf));
|
|
||||||
// logger->info("uppercase, no delimiters: {:Xs}", spdlog::to_hex(buf));
|
|
||||||
// logger->info("uppercase, no delimiters, no position info: {:Xsp}", spdlog::to_hex(buf));
|
|
||||||
// logger->info("hexdump style: {:a}", spdlog::to_hex(buf));
|
|
||||||
// logger->info("hexdump style, 20 chars per line {:a}", spdlog::to_hex(buf, 20));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log a vector of numbers
|
|
||||||
#include "fmt/ranges.h"
|
|
||||||
void vector_example() {
|
|
||||||
std::vector<int> vec = {1, 2, 3};
|
|
||||||
spdlog::info("Vector example: {}", vec);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile time log levels.
|
|
||||||
// define SPDLOG_ACTIVE_LEVEL to required level (e.g. SPDLOG_LEVEL_TRACE)
|
|
||||||
void trace_example() {
|
|
||||||
// trace from global logger
|
|
||||||
SPDLOG_TRACE("Some trace message.. {} ,{}", 1, 3.23);
|
|
||||||
// debug from global logger
|
|
||||||
SPDLOG_DEBUG("Some debug message.. {} ,{}", 1, 3.23);
|
|
||||||
}
|
|
||||||
|
|
||||||
// stopwatch example
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include "spdlog/stopwatch.h"
|
|
||||||
void stopwatch_example() {
|
|
||||||
spdlog::stopwatch sw;
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(123));
|
|
||||||
spdlog::info("Stopwatch: {} seconds", sw);
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "spdlog/sinks/udp_sink.h"
|
|
||||||
void udp_example() {
|
|
||||||
udp_sink_config cfg("127.0.0.1", 11091);
|
|
||||||
auto my_logger = spdlog::create<udp_sink_mt>("udplog", cfg);
|
|
||||||
my_logger->set_level(spdlog::level::debug);
|
|
||||||
my_logger->info("hello world");
|
|
||||||
}
|
|
||||||
|
|
||||||
// A logger with multiple sinks (stdout and file) - each with a different format and log level.
|
|
||||||
void multi_sink_example() {
|
|
||||||
auto console_sink = std::make_shared<stdout_color_sink_mt>();
|
|
||||||
console_sink->set_level(spdlog::level::warn);
|
|
||||||
console_sink->set_pattern("[multi_sink_example] [%^%l%$] %v");
|
|
||||||
|
|
||||||
auto file_sink = std::make_shared<basic_file_sink_mt>("logs/multisink.txt", true);
|
|
||||||
file_sink->set_level(spdlog::level::trace);
|
|
||||||
|
|
||||||
spdlog::logger logger("multi_sink", {console_sink, file_sink});
|
|
||||||
logger.set_level(spdlog::level::debug);
|
|
||||||
logger.warn("this should appear in both console and file");
|
|
||||||
logger.info("this message should not appear in the console, only in the file");
|
|
||||||
}
|
|
||||||
|
|
||||||
// User defined types logging
|
|
||||||
struct my_type {
|
|
||||||
int i = 0;
|
|
||||||
explicit my_type(int i)
|
|
||||||
: i(i) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct fmt::formatter<my_type> : fmt::formatter<std::string> {
|
|
||||||
auto format(my_type my, format_context &ctx) const -> decltype(ctx.out()) {
|
|
||||||
return fmt::format_to(ctx.out(), "[my_type i={}]", my.i);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void user_defined_example() { spdlog::info("user defined type: {}", my_type(14)); }
|
|
||||||
|
|
||||||
// Custom error handler. Will be triggered on log failure.
|
|
||||||
void err_handler_example() {
|
|
||||||
// can be set globally or per logger(logger->set_error_handler(..))
|
|
||||||
spdlog::set_error_handler([](const std::string &msg) { printf("*** Custom log error handler: %s ***\n", msg.c_str()); });
|
|
||||||
}
|
|
||||||
|
|
||||||
// syslog example (linux/osx/freebsd)
|
|
||||||
#ifndef _WIN32
|
|
||||||
#include "spdlog/sinks/syslog_sink.h"
|
|
||||||
void syslog_example() {
|
|
||||||
std::string ident = "spdlog-example";
|
|
||||||
auto syslog_logger = spdlog::create<syslog_sink_mt>("syslog", ident, LOG_PID);
|
|
||||||
syslog_logger->warn("This is warning that will end up in syslog.");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Android example.
|
|
||||||
#if defined(__ANDROID__)
|
|
||||||
#include "spdlog/sinks/android_sink.h"
|
|
||||||
void android_example() {
|
|
||||||
std::string tag = "spdlog-android";
|
|
||||||
auto android_logger = spdlog::android_logger_mt("android", tag);
|
|
||||||
android_logger->critical("Use \"adb shell logcat\" to view this message.");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Log patterns can contain custom flags.
|
|
||||||
// this will add custom flag '%*' which will be bound to a <my_formatter_flag> instance
|
|
||||||
#include "spdlog/pattern_formatter.h"
|
|
||||||
class my_formatter_flag : public spdlog::custom_flag_formatter {
|
|
||||||
public:
|
|
||||||
void format(const spdlog::details::log_msg &, const std::tm &, spdlog::memory_buf_t &dest) override {
|
|
||||||
std::string some_txt = "custom-flag";
|
|
||||||
dest.append(some_txt.data(), some_txt.data() + some_txt.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]]
|
|
||||||
std::unique_ptr<custom_flag_formatter> clone() const override {
|
|
||||||
return std::make_unique<my_formatter_flag>();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void custom_flags_example() {
|
|
||||||
auto formatter = std::make_unique<spdlog::pattern_formatter>();
|
|
||||||
formatter->add_flag<my_formatter_flag>('*').set_pattern("[%n] [%*] [%^%l%$] %v");
|
|
||||||
// set the new formatter using spdlog::set_formatter(formatter) or
|
|
||||||
// logger->set_formatter(formatter) spdlog::set_formatter(std::move(formatter));
|
|
||||||
}
|
|
||||||
|
|
||||||
void file_events_example() {
|
|
||||||
// pass the spdlog::file_event_handlers to file sinks for open/close log file notifications
|
|
||||||
spdlog::file_event_handlers handlers;
|
|
||||||
handlers.before_open = [](spdlog::filename_t) { spdlog::trace("Before opening logfile"); };
|
|
||||||
handlers.after_open = [](spdlog::filename_t, std::FILE *fstream) {
|
|
||||||
spdlog::trace("After opening logfile");
|
|
||||||
fputs("After opening\n", fstream);
|
|
||||||
};
|
|
||||||
handlers.before_close = [](spdlog::filename_t, std::FILE *fstream) {
|
|
||||||
spdlog::trace("Before closing logfile");
|
|
||||||
fputs("Before closing\n", fstream);
|
|
||||||
};
|
|
||||||
handlers.after_close = [](spdlog::filename_t) { spdlog::trace("After closing logfile"); };
|
|
||||||
auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("logs/events-sample.txt", true, handlers);
|
|
||||||
spdlog::logger my_logger("some_logger", file_sink);
|
|
||||||
my_logger.trace("Some log line");
|
|
||||||
}
|
|
||||||
|
|
||||||
void replace_global_logger_example() {
|
|
||||||
// store the old logger so we don't break other examples.
|
|
||||||
auto old_logger = spdlog::global_logger();
|
|
||||||
|
|
||||||
auto new_logger = spdlog::create<basic_file_sink_mt>("new_global_logger", "logs/new-default-log.txt", true);
|
|
||||||
spdlog::set_global_logger(new_logger);
|
|
||||||
spdlog::set_level(spdlog::level::info);
|
|
||||||
spdlog::debug("This message should not be displayed!");
|
|
||||||
spdlog::set_level(spdlog::level::trace);
|
|
||||||
spdlog::debug("This message should be displayed..");
|
|
||||||
spdlog::set_global_logger(old_logger);
|
|
||||||
}
|
|
||||||
|
@ -1,69 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
// Fast asynchronous logger.
|
|
||||||
// Uses pre allocated queue.
|
|
||||||
// Creates a single back thread to pop messages from the queue and log them.
|
|
||||||
//
|
|
||||||
// Upon each log write the logger:
|
|
||||||
// 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)
|
|
||||||
// Upon destruction, logs all remaining messages in the queue before
|
|
||||||
// destructing
|
|
||||||
|
|
||||||
#include "./logger.h"
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
|
|
||||||
// Async overflow policy - block by default.
|
|
||||||
enum class async_overflow_policy {
|
|
||||||
block, // Block until message can be enqueued
|
|
||||||
overrun_oldest, // Discard the oldest message in the queue if full when trying to
|
|
||||||
// add new item.
|
|
||||||
discard_new // Discard new message if the queue is full when trying to add new item.
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace details {
|
|
||||||
class thread_pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
class SPDLOG_API async_logger final : public std::enable_shared_from_this<async_logger>, public logger {
|
|
||||||
friend class details::thread_pool;
|
|
||||||
|
|
||||||
public:
|
|
||||||
template <typename It>
|
|
||||||
async_logger(std::string logger_name,
|
|
||||||
It begin,
|
|
||||||
It end,
|
|
||||||
std::weak_ptr<details::thread_pool> tp,
|
|
||||||
async_overflow_policy overflow_policy = async_overflow_policy::block)
|
|
||||||
: logger(std::move(logger_name), begin, end),
|
|
||||||
thread_pool_(std::move(tp)),
|
|
||||||
overflow_policy_(overflow_policy) {}
|
|
||||||
|
|
||||||
async_logger(std::string logger_name,
|
|
||||||
sinks_init_list sinks_list,
|
|
||||||
std::weak_ptr<details::thread_pool> tp,
|
|
||||||
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
|
||||||
|
|
||||||
async_logger(std::string logger_name,
|
|
||||||
sink_ptr single_sink,
|
|
||||||
std::weak_ptr<details::thread_pool> tp,
|
|
||||||
async_overflow_policy overflow_policy = async_overflow_policy::block);
|
|
||||||
|
|
||||||
std::shared_ptr<logger> clone(std::string new_name) override;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void sink_it_(const details::log_msg &msg) override;
|
|
||||||
void flush_() override;
|
|
||||||
void backend_sink_it_(const details::log_msg &incoming_log_msg);
|
|
||||||
void backend_flush_();
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::weak_ptr<details::thread_pool> thread_pool_;
|
|
||||||
async_overflow_policy overflow_policy_;
|
|
||||||
};
|
|
||||||
} // namespace spdlog
|
|
37
include/spdlog/details/async_log_msg.h
Normal file
37
include/spdlog/details/async_log_msg.h
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include "./log_msg.h"
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
|
||||||
|
// Extend log_msg with internal buffer to store its payload.
|
||||||
|
// This is needed since log_msg holds string_views that points to stack data.
|
||||||
|
|
||||||
|
class SPDLOG_API async_log_msg : public log_msg {
|
||||||
|
public:
|
||||||
|
enum class msg_type:std::uint8_t { log, flush, terminate };
|
||||||
|
async_log_msg() = default;
|
||||||
|
explicit async_log_msg(msg_type type);
|
||||||
|
async_log_msg(msg_type type, const log_msg &orig_msg);
|
||||||
|
|
||||||
|
~async_log_msg() = default;
|
||||||
|
async_log_msg(const async_log_msg &other);
|
||||||
|
async_log_msg(async_log_msg &&other) noexcept;
|
||||||
|
async_log_msg &operator=(const async_log_msg &other);
|
||||||
|
async_log_msg &operator=(async_log_msg &&other) noexcept;
|
||||||
|
|
||||||
|
msg_type message_type() const {return msg_type_;}
|
||||||
|
private:
|
||||||
|
msg_type msg_type_{msg_type::log};
|
||||||
|
memory_buf_t buffer_;
|
||||||
|
void update_string_views();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
@ -1,28 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "./log_msg.h"
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
// Extend log_msg with internal buffer to store its payload.
|
|
||||||
// This is needed since log_msg holds string_views that points to stack data.
|
|
||||||
|
|
||||||
class SPDLOG_API log_msg_buffer : public log_msg {
|
|
||||||
memory_buf_t buffer;
|
|
||||||
void update_string_views();
|
|
||||||
|
|
||||||
public:
|
|
||||||
log_msg_buffer() = default;
|
|
||||||
explicit log_msg_buffer(const log_msg &orig_msg);
|
|
||||||
log_msg_buffer(const log_msg_buffer &other);
|
|
||||||
log_msg_buffer(log_msg_buffer &&other) noexcept;
|
|
||||||
log_msg_buffer &operator=(const log_msg_buffer &other);
|
|
||||||
log_msg_buffer &operator=(log_msg_buffer &&other) noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
63
include/spdlog/sinks/async_sink.h
Normal file
63
include/spdlog/sinks/async_sink.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include "../details/async_log_msg.h"
|
||||||
|
#include "../details/mpmc_blocking_q.h"
|
||||||
|
#include "./dist_sink.h"
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace sinks {
|
||||||
|
|
||||||
|
template <typename Mutex>
|
||||||
|
class async_sink final : public dist_sink<Mutex> {
|
||||||
|
public:
|
||||||
|
using base_t = dist_sink<Mutex>;
|
||||||
|
using queue_t = details::mpmc_blocking_queue<details::async_log_msg>;
|
||||||
|
async_sink() {
|
||||||
|
q_ = std::make_unique<queue_t>(8192);
|
||||||
|
worker_thread_ = std::thread([this] {
|
||||||
|
details::async_log_msg incoming_msg;
|
||||||
|
for (;;) {
|
||||||
|
q_->dequeue(incoming_msg);
|
||||||
|
if (incoming_msg.message_type() == details::async_log_msg::msg_type::terminate) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
base_t::sink_it_(incoming_msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
~async_sink() override {
|
||||||
|
try {
|
||||||
|
q_->enqueue(details::async_log_msg(details::async_log_msg::msg_type::terminate));
|
||||||
|
worker_thread_.join();
|
||||||
|
} catch (...) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
async_sink(const async_sink &) = delete;
|
||||||
|
async_sink &operator=(const async_sink &) = delete;
|
||||||
|
async_sink(async_sink &&) = default;
|
||||||
|
async_sink &operator=(async_sink &&) = default;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
void sink_it_(const details::log_msg &msg) override {
|
||||||
|
// Asynchronously send the log message to the base sink
|
||||||
|
q_->enqueue(details::async_log_msg(details::async_log_msg::msg_type::log, msg));
|
||||||
|
}
|
||||||
|
void flush_() override {
|
||||||
|
// Asynchronously flush the base sink
|
||||||
|
q_->enqueue(details::async_log_msg(details::async_log_msg::msg_type::flush));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::thread worker_thread_;
|
||||||
|
std::unique_ptr<queue_t> q_;
|
||||||
|
};
|
||||||
|
|
||||||
|
using async_sink_mt = async_sink<std::mutex>;
|
||||||
|
using async_sink_st = async_sink<details::null_mutex>;
|
||||||
|
|
||||||
|
} // namespace sinks
|
||||||
|
} // namespace spdlog
|
@ -7,8 +7,8 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
|
#include "../details/async_log_msg.h"
|
||||||
#include "../details/circular_q.h"
|
#include "../details/circular_q.h"
|
||||||
#include "../details/log_msg_buffer.h"
|
|
||||||
#include "../details/null_mutex.h"
|
#include "../details/null_mutex.h"
|
||||||
#include "./base_sink.h"
|
#include "./base_sink.h"
|
||||||
|
|
||||||
|
60
src/details/async_log_msg.cpp
Normal file
60
src/details/async_log_msg.cpp
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||||
|
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||||
|
|
||||||
|
#include "spdlog/details/async_log_msg.h"
|
||||||
|
|
||||||
|
namespace spdlog {
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
|
||||||
|
async_log_msg::async_log_msg(const msg_type type)
|
||||||
|
: msg_type_{type} {}
|
||||||
|
|
||||||
|
// copy logger name and payload to buffer so can be used asynchronously
|
||||||
|
// note: source location pointers are copied without allocation since they
|
||||||
|
// are compiler generated const chars* (__FILE__, __LINE__, __FUNCTION__)
|
||||||
|
// if you pass custom strings to source location, make sure they outlive the async_log_msg
|
||||||
|
async_log_msg::async_log_msg(const msg_type type, const log_msg &orig_msg)
|
||||||
|
: log_msg{orig_msg}, msg_type_(type) {
|
||||||
|
buffer_.append(logger_name);
|
||||||
|
buffer_.append(payload);
|
||||||
|
update_string_views();
|
||||||
|
}
|
||||||
|
|
||||||
|
async_log_msg::async_log_msg(const async_log_msg &other)
|
||||||
|
: log_msg{other}, msg_type_{other.msg_type_} {
|
||||||
|
|
||||||
|
buffer_.append(logger_name);
|
||||||
|
buffer_.append(payload);
|
||||||
|
update_string_views();
|
||||||
|
}
|
||||||
|
|
||||||
|
async_log_msg::async_log_msg(async_log_msg &&other) noexcept
|
||||||
|
: log_msg{other}, msg_type_{other.msg_type_}, buffer_{std::move(other.buffer_)} {
|
||||||
|
update_string_views();
|
||||||
|
}
|
||||||
|
|
||||||
|
async_log_msg &async_log_msg::operator=(const async_log_msg &other) {
|
||||||
|
log_msg::operator=(other);
|
||||||
|
msg_type_ = other.msg_type_;
|
||||||
|
buffer_.clear();
|
||||||
|
buffer_.append(other.buffer_.data(), other.buffer_.data() + other.buffer_.size());
|
||||||
|
update_string_views();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
async_log_msg &async_log_msg::operator=(async_log_msg &&other) noexcept {
|
||||||
|
log_msg::operator=(other);
|
||||||
|
msg_type_ = other.msg_type_;
|
||||||
|
buffer_ = std::move(other.buffer_);
|
||||||
|
update_string_views();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void async_log_msg::update_string_views() {
|
||||||
|
logger_name = string_view_t{buffer_.data(), logger_name.size()};
|
||||||
|
payload = string_view_t{buffer_.data() + logger_name.size(), payload.size()};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
} // namespace spdlog
|
@ -1,54 +0,0 @@
|
|||||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
|
||||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
|
||||||
|
|
||||||
#include "spdlog/details/log_msg_buffer.h"
|
|
||||||
|
|
||||||
namespace spdlog {
|
|
||||||
namespace details {
|
|
||||||
|
|
||||||
// copy logger name and payload to buffer so can be used asynchronously
|
|
||||||
// note: source location pointers are copied without allocation since they
|
|
||||||
// are compiler generated const chars* (__FILE__, __LINE__, __FUNCTION__)
|
|
||||||
// if you pass custom strings to source location, make sure they outlive the log_msg_buffer
|
|
||||||
log_msg_buffer::log_msg_buffer(const log_msg &orig_msg)
|
|
||||||
: log_msg{orig_msg} {
|
|
||||||
buffer.append(logger_name);
|
|
||||||
buffer.append(payload);
|
|
||||||
update_string_views();
|
|
||||||
}
|
|
||||||
|
|
||||||
log_msg_buffer::log_msg_buffer(const log_msg_buffer &other)
|
|
||||||
: log_msg{other} {
|
|
||||||
buffer.append(logger_name);
|
|
||||||
buffer.append(payload);
|
|
||||||
update_string_views();
|
|
||||||
}
|
|
||||||
|
|
||||||
log_msg_buffer::log_msg_buffer(log_msg_buffer &&other) noexcept
|
|
||||||
: log_msg{other},
|
|
||||||
buffer{std::move(other.buffer)} {
|
|
||||||
update_string_views();
|
|
||||||
}
|
|
||||||
|
|
||||||
log_msg_buffer &log_msg_buffer::operator=(const log_msg_buffer &other) {
|
|
||||||
log_msg::operator=(other);
|
|
||||||
buffer.clear();
|
|
||||||
buffer.append(other.buffer.data(), other.buffer.data() + other.buffer.size());
|
|
||||||
update_string_views();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
log_msg_buffer &log_msg_buffer::operator=(log_msg_buffer &&other) noexcept {
|
|
||||||
log_msg::operator=(other);
|
|
||||||
buffer = std::move(other.buffer);
|
|
||||||
update_string_views();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void log_msg_buffer::update_string_views() {
|
|
||||||
logger_name = string_view_t{buffer.data(), logger_name.size()};
|
|
||||||
payload = string_view_t{buffer.data() + logger_name.size(), payload.size()};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace details
|
|
||||||
} // namespace spdlog
|
|
Loading…
Reference in New Issue
Block a user