mirror of https://github.com/gabime/spdlog.git
parent
166843ff3a
commit
83c9ede9e6
@ -1,85 +0,0 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
//
|
||||
// Async logging using global thread pool
|
||||
// All loggers created here share same global thread pool.
|
||||
// Each log message is pushed to a queue along with a shared pointer to the
|
||||
// logger.
|
||||
// If a logger deleted while having pending messages in the queue, it's actual
|
||||
// destruction will defer
|
||||
// until all its messages are processed by the thread pool.
|
||||
// This is because each message in the queue holds a shared_ptr to the
|
||||
// originating logger.
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
|
||||
#include "./async_logger.h"
|
||||
#include "./details/context.h"
|
||||
#include "./details/thread_pool.h"
|
||||
#include "spdlog.h"
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
static constexpr size_t default_async_q_size = 8192;
|
||||
}
|
||||
|
||||
// async logger factory - creates async loggers backed with thread pool.
|
||||
// if a global thread pool doesn't already exist, create it with default queue
|
||||
// size of 8192 items and single thread.
|
||||
template <async_overflow_policy OverflowPolicy = async_overflow_policy::block>
|
||||
struct async_factory_impl {
|
||||
template <typename Sink, typename... SinkArgs>
|
||||
static std::shared_ptr<async_logger> create(std::string logger_name, SinkArgs &&...args) {
|
||||
auto context = spdlog::context();
|
||||
// create global thread pool if not already exists
|
||||
auto &mutex = context->tp_mutex();
|
||||
std::lock_guard<std::recursive_mutex> tp_lock(mutex);
|
||||
auto tp = context->get_tp();
|
||||
if (tp == nullptr) {
|
||||
tp = std::make_shared<details::thread_pool>(details::default_async_q_size, 1U);
|
||||
context->set_tp(tp);
|
||||
}
|
||||
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
||||
auto new_logger = std::make_shared<async_logger>(std::move(logger_name), std::move(sink), std::move(tp), OverflowPolicy);
|
||||
return new_logger;
|
||||
}
|
||||
};
|
||||
|
||||
using async_factory = async_factory_impl<async_overflow_policy::block>;
|
||||
using async_factory_nonblock = async_factory_impl<async_overflow_policy::overrun_oldest>;
|
||||
|
||||
template <typename Sink, typename... SinkArgs>
|
||||
std::shared_ptr<spdlog::logger> create_async(std::string logger_name, SinkArgs &&...sink_args) {
|
||||
return async_factory::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
||||
}
|
||||
|
||||
template <typename Sink, typename... SinkArgs>
|
||||
std::shared_ptr<spdlog::logger> create_async_nb(std::string logger_name, SinkArgs &&...sink_args) {
|
||||
return async_factory_nonblock::create<Sink>(std::move(logger_name), std::forward<SinkArgs>(sink_args)...);
|
||||
}
|
||||
|
||||
// set global thread pool.
|
||||
inline void init_thread_pool(size_t q_size,
|
||||
size_t thread_count,
|
||||
std::function<void()> on_thread_start,
|
||||
std::function<void()> on_thread_stop) {
|
||||
auto tp = std::make_shared<details::thread_pool>(q_size, thread_count, on_thread_start, on_thread_stop);
|
||||
spdlog::context()->set_tp(std::move(tp));
|
||||
}
|
||||
|
||||
inline void init_thread_pool(size_t q_size, size_t thread_count, std::function<void()> on_thread_start) {
|
||||
init_thread_pool(q_size, thread_count, on_thread_start, [] {});
|
||||
}
|
||||
|
||||
inline void init_thread_pool(size_t q_size, size_t thread_count) {
|
||||
init_thread_pool(q_size, thread_count, [] {}, [] {});
|
||||
}
|
||||
|
||||
// get the global thread pool.
|
||||
inline std::shared_ptr<spdlog::details::thread_pool> thread_pool() { return spdlog::context()->get_tp(); }
|
||||
} // namespace spdlog
|
||||
@ -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
|
||||
@ -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 type:std::uint8_t { log, flush, terminate };
|
||||
async_log_msg() = default;
|
||||
explicit async_log_msg(type type);
|
||||
async_log_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;
|
||||
|
||||
type message_type() const {return msg_type_;}
|
||||
private:
|
||||
type msg_type_{type::log};
|
||||
memory_buf_t buffer_;
|
||||
void update_string_views();
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@ -1,59 +0,0 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// Loggers registry of unique name->logger pointer
|
||||
// An attempt to create a logger with an already existing name will result with spdlog_ex exception.
|
||||
// If user requests a non-existing logger, nullptr will be returned
|
||||
// This class is thread safe
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
#include "../common.h"
|
||||
#include "./periodic_worker.h"
|
||||
|
||||
namespace spdlog {
|
||||
class logger;
|
||||
|
||||
namespace details {
|
||||
class thread_pool;
|
||||
|
||||
class SPDLOG_API context {
|
||||
public:
|
||||
context() = default;
|
||||
explicit context(std::unique_ptr<logger> global_logger);
|
||||
~context() = default;
|
||||
context(const context &) = delete;
|
||||
context &operator=(const context &) = delete;
|
||||
|
||||
[[nodiscard]] std::shared_ptr<logger> global_logger();
|
||||
|
||||
// Return raw ptr to the global logger.
|
||||
// To be used directly by the spdlog global api (e.g. spdlog::info)
|
||||
// This make the global API faster, but cannot be used concurrently with set_global_logger().
|
||||
// e.g do not call set_global_logger() from one thread while calling spdlog::info() from
|
||||
// another.
|
||||
[[nodiscard]] logger *global_logger_raw() const noexcept;
|
||||
|
||||
// set logger instance.
|
||||
void set_logger(std::shared_ptr<logger> new_logger);
|
||||
|
||||
void set_tp(std::shared_ptr<thread_pool> tp);
|
||||
|
||||
[[nodiscard]] std::shared_ptr<thread_pool> get_tp();
|
||||
|
||||
// clean all resources
|
||||
void shutdown();
|
||||
[[nodiscard]] std::recursive_mutex &tp_mutex();
|
||||
|
||||
private:
|
||||
std::recursive_mutex tp_mutex_;
|
||||
std::shared_ptr<thread_pool> tp_;
|
||||
std::shared_ptr<logger> global_logger_;
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@ -0,0 +1,23 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <mutex>
|
||||
#include <exception>
|
||||
#include "spdlog/common.h"
|
||||
|
||||
// by default, prints the error to stderr, thread safe
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
class SPDLOG_API err_helper {
|
||||
err_handler custom_err_handler_;
|
||||
public:
|
||||
void handle_ex(const std::string& origin, const source_loc& loc, const std::exception& ex) const;
|
||||
void handle_unknown_ex(const std::string& origin, const source_loc& loc) const;
|
||||
void set_err_handler(err_handler handler);
|
||||
};
|
||||
|
||||
|
||||
}} // namespace spdlog::details
|
||||
@ -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
|
||||
@ -1,57 +0,0 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
// periodic worker thread - periodically executes the given callback function.
|
||||
//
|
||||
// RAII over the owned thread:
|
||||
// creates the thread on construction.
|
||||
// stops and joins the thread on destruction (if the thread is executing a callback, wait for it
|
||||
// to finish first).
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include "../common.h"
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
class SPDLOG_API periodic_worker {
|
||||
public:
|
||||
template <typename Rep, typename Period>
|
||||
periodic_worker(const std::function<void()> &callback_fun, std::chrono::duration<Rep, Period> interval) {
|
||||
active_ = (interval > std::chrono::duration<Rep, Period>::zero());
|
||||
if (!active_) {
|
||||
return;
|
||||
}
|
||||
|
||||
worker_thread_ = std::thread([this, callback_fun, interval]() {
|
||||
for (;;) {
|
||||
std::unique_lock<std::mutex> lock(this->mutex_);
|
||||
if (this->cv_.wait_for(lock, interval, [this] { return !this->active_; })) {
|
||||
return; // active_ == false, so exit this thread
|
||||
}
|
||||
callback_fun();
|
||||
}
|
||||
});
|
||||
}
|
||||
std::thread &get_thread() { return worker_thread_; }
|
||||
periodic_worker(const periodic_worker &) = delete;
|
||||
periodic_worker &operator=(const periodic_worker &) = delete;
|
||||
// stop the worker thread and join it
|
||||
~periodic_worker();
|
||||
|
||||
private:
|
||||
bool active_;
|
||||
std::thread worker_thread_;
|
||||
std::mutex mutex_;
|
||||
std::condition_variable cv_;
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@ -1,21 +0,0 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "./context.h"
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
// Default logger factory- creates synchronous loggers
|
||||
class logger;
|
||||
|
||||
struct synchronous_factory {
|
||||
template <typename Sink, typename... SinkArgs>
|
||||
static std::shared_ptr<spdlog::logger> create(std::string logger_name, SinkArgs &&...args) {
|
||||
auto sink = std::make_shared<Sink>(std::forward<SinkArgs>(args)...);
|
||||
auto new_logger = std::make_shared<spdlog::logger>(std::move(logger_name), std::move(sink));
|
||||
return new_logger;
|
||||
}
|
||||
};
|
||||
} // namespace spdlog
|
||||
@ -1,112 +0,0 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "../async.h"
|
||||
#include "./log_msg_buffer.h"
|
||||
#include "./mpmc_blocking_q.h"
|
||||
#include "./os.h"
|
||||
|
||||
namespace spdlog {
|
||||
class async_logger;
|
||||
|
||||
namespace details {
|
||||
|
||||
using async_logger_ptr = std::shared_ptr<spdlog::async_logger>;
|
||||
|
||||
enum class async_msg_type { log, flush, terminate };
|
||||
|
||||
// Async msg to move to/from the queue
|
||||
// Movable only. should never be copied
|
||||
struct async_msg : log_msg_buffer {
|
||||
async_msg_type msg_type{async_msg_type::log};
|
||||
async_logger_ptr worker_ptr;
|
||||
|
||||
async_msg() = default;
|
||||
~async_msg() = default;
|
||||
|
||||
// should only be moved in or out of the queue..
|
||||
async_msg(const async_msg &) = delete;
|
||||
|
||||
// support for vs2013 move
|
||||
#if defined(_MSC_VER) && _MSC_VER <= 1800
|
||||
async_msg(async_msg &&other)
|
||||
: 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<log_msg_buffer *>(this) = std::move(other);
|
||||
msg_type = other.msg_type;
|
||||
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;
|
||||
#endif
|
||||
|
||||
// construct from log_msg with given type
|
||||
async_msg(async_logger_ptr &&worker, async_msg_type the_type, const details::log_msg &m)
|
||||
: log_msg_buffer{m},
|
||||
msg_type{the_type},
|
||||
worker_ptr{std::move(worker)} {}
|
||||
|
||||
async_msg(async_logger_ptr &&worker, async_msg_type the_type)
|
||||
: log_msg_buffer{},
|
||||
msg_type{the_type},
|
||||
worker_ptr{std::move(worker)} {}
|
||||
|
||||
explicit async_msg(async_msg_type the_type)
|
||||
: async_msg{nullptr, the_type} {}
|
||||
};
|
||||
|
||||
class SPDLOG_API thread_pool {
|
||||
public:
|
||||
using item_type = async_msg;
|
||||
using q_type = details::mpmc_blocking_queue<item_type>;
|
||||
|
||||
thread_pool(size_t q_max_items,
|
||||
size_t threads_n,
|
||||
std::function<void()> on_thread_start,
|
||||
std::function<void()> on_thread_stop);
|
||||
thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start);
|
||||
thread_pool(size_t q_max_items, size_t threads_n);
|
||||
|
||||
// message all threads to terminate gracefully and join them
|
||||
~thread_pool();
|
||||
|
||||
thread_pool(const thread_pool &) = delete;
|
||||
thread_pool &operator=(thread_pool &&) = delete;
|
||||
|
||||
void post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy);
|
||||
void post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy);
|
||||
size_t overrun_counter();
|
||||
void reset_overrun_counter();
|
||||
size_t discard_counter();
|
||||
void reset_discard_counter();
|
||||
size_t queue_size();
|
||||
|
||||
private:
|
||||
q_type q_;
|
||||
|
||||
std::vector<std::thread> threads_;
|
||||
|
||||
void post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy);
|
||||
void worker_loop_();
|
||||
|
||||
// process next message in the queue
|
||||
// return true if this thread should still be active (while no terminate msg
|
||||
// was received)
|
||||
bool process_next_msg_();
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@ -1,9 +0,0 @@
|
||||
//
|
||||
// Copyright(c) 2016-2018 Gabi Melman.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "fmt/format.h"
|
||||
#include "fmt/xchar.h"
|
||||
@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "../details/async_log_msg.h"
|
||||
#include "../details/err_helper.h"
|
||||
#include "sink.h"
|
||||
|
||||
// async_sink is a sink that sends log messages to a dist_sink in a separate thread using a queue.
|
||||
// The worker thread dequeues the messages and sends them to the dist_sink to perform the actual logging.
|
||||
// Once the sink is destroyed, the worker thread empties the queue and exits.
|
||||
|
||||
namespace spdlog::details { // forward declaration
|
||||
template <typename T>
|
||||
class mpmc_blocking_queue;
|
||||
}
|
||||
|
||||
namespace spdlog {
|
||||
namespace sinks {
|
||||
|
||||
class SPDLOG_API async_sink final : public sink {
|
||||
public:
|
||||
enum class overflow_policy : std::uint8_t {
|
||||
block, // Block until the log message can be enqueued (default).
|
||||
overrun_oldest, // Overrun the oldest message in the queue if full.
|
||||
discard_new // Discard the log message if the queue is full
|
||||
};
|
||||
|
||||
enum { default_queue_size = 8192, max_queue_size = 10 * 1024 * 1024 };
|
||||
|
||||
struct config {
|
||||
size_t queue_size = default_queue_size;
|
||||
overflow_policy policy = overflow_policy::block;
|
||||
std::vector<std::shared_ptr<sink>> sinks;
|
||||
std::function<void()> on_thread_start = nullptr;
|
||||
std::function<void()> on_thread_stop = nullptr;
|
||||
err_handler custom_err_handler = nullptr;
|
||||
};
|
||||
|
||||
explicit async_sink(config async_config);
|
||||
|
||||
// create an async_sink with one backend sink
|
||||
template <typename Sink, typename... SinkArgs>
|
||||
static std::shared_ptr<async_sink> with(SinkArgs &&...sink_args) {
|
||||
config cfg{};
|
||||
cfg.sinks.emplace_back(std::make_shared<Sink>(std::forward<SinkArgs>(sink_args)...));
|
||||
return std::make_shared<async_sink>(cfg);
|
||||
}
|
||||
|
||||
~async_sink() override;
|
||||
|
||||
// sink interface implementation
|
||||
void log(const details::log_msg &msg) override;
|
||||
void flush() override;
|
||||
void set_pattern(const std::string &pattern) override;
|
||||
void set_formatter(std::unique_ptr<formatter> sink_formatter) override;
|
||||
|
||||
// async sink specific methods
|
||||
[[nodiscard]] size_t get_overrun_counter() const;
|
||||
void reset_overrun_counter() const;
|
||||
[[nodiscard]] size_t get_discard_counter() const;
|
||||
void reset_discard_counter() const;
|
||||
[[nodiscard]] const config &get_config() const;
|
||||
|
||||
private:
|
||||
using async_log_msg = details::async_log_msg;
|
||||
using queue_t = details::mpmc_blocking_queue<async_log_msg>;
|
||||
|
||||
void send_message_(async_log_msg::type msg_type, const details::log_msg &msg) const;
|
||||
void backend_loop_();
|
||||
void backend_log_(const details::log_msg &msg) ;
|
||||
void backend_flush_();
|
||||
|
||||
config config_;
|
||||
std::unique_ptr<queue_t> q_;
|
||||
std::thread worker_thread_;
|
||||
details::err_helper err_helper_;
|
||||
};
|
||||
|
||||
} // namespace sinks
|
||||
} // namespace spdlog
|
||||
@ -1,79 +0,0 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#include "spdlog/async_logger.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "spdlog/details/thread_pool.h"
|
||||
#include "spdlog/sinks/sink.h"
|
||||
|
||||
spdlog::async_logger::async_logger(std::string logger_name,
|
||||
sinks_init_list sinks_list,
|
||||
std::weak_ptr<details::thread_pool> tp,
|
||||
async_overflow_policy overflow_policy)
|
||||
: async_logger(std::move(logger_name), sinks_list.begin(), sinks_list.end(), std::move(tp), overflow_policy) {}
|
||||
|
||||
spdlog::async_logger::async_logger(std::string logger_name,
|
||||
sink_ptr single_sink,
|
||||
std::weak_ptr<details::thread_pool> tp,
|
||||
async_overflow_policy overflow_policy)
|
||||
: async_logger(std::move(logger_name), {std::move(single_sink)}, std::move(tp), overflow_policy) {}
|
||||
|
||||
// send the log message to the thread pool
|
||||
void spdlog::async_logger::sink_it_(const details::log_msg &msg) {
|
||||
try {
|
||||
if (auto pool_ptr = thread_pool_.lock()) {
|
||||
pool_ptr->post_log(shared_from_this(), msg, overflow_policy_);
|
||||
} else {
|
||||
throw_spdlog_ex("async log: thread pool doesn't exist anymore");
|
||||
}
|
||||
}
|
||||
SPDLOG_LOGGER_CATCH(msg.source)
|
||||
}
|
||||
|
||||
// send flush request to the thread pool
|
||||
void spdlog::async_logger::flush_() {
|
||||
try {
|
||||
if (auto pool_ptr = thread_pool_.lock()) {
|
||||
pool_ptr->post_flush(shared_from_this(), overflow_policy_);
|
||||
} else {
|
||||
throw_spdlog_ex("async flush: thread pool doesn't exist anymore");
|
||||
}
|
||||
}
|
||||
SPDLOG_LOGGER_CATCH(source_loc())
|
||||
}
|
||||
|
||||
//
|
||||
// backend functions - called from the thread pool to do the actual job
|
||||
//
|
||||
void spdlog::async_logger::backend_sink_it_(const details::log_msg &msg) {
|
||||
for (auto &sink : sinks_) {
|
||||
if (sink->should_log(msg.log_level)) {
|
||||
try {
|
||||
sink->log(msg);
|
||||
}
|
||||
SPDLOG_LOGGER_CATCH(msg.source)
|
||||
}
|
||||
}
|
||||
|
||||
if (should_flush_(msg)) {
|
||||
backend_flush_();
|
||||
}
|
||||
}
|
||||
|
||||
void spdlog::async_logger::backend_flush_() {
|
||||
for (auto &sink : sinks_) {
|
||||
try {
|
||||
sink->flush();
|
||||
}
|
||||
SPDLOG_LOGGER_CATCH(source_loc())
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<spdlog::logger> spdlog::async_logger::clone(std::string new_name) {
|
||||
auto cloned = std::make_shared<spdlog::async_logger>(*this);
|
||||
cloned->name_ = std::move(new_name);
|
||||
return cloned;
|
||||
}
|
||||
@ -0,0 +1,61 @@
|
||||
// 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 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 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) {
|
||||
if (this == &other) return *this;
|
||||
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 {
|
||||
if (this == &other) return *this;
|
||||
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,49 +0,0 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#include "spdlog/details/context.h"
|
||||
|
||||
#include "spdlog/logger.h"
|
||||
|
||||
#ifndef SPDLOG_DISABLE_GLOBAL_LOGGER
|
||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||
#endif // SPDLOG_DISABLE_GLOBAL_LOGGER
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
context::context(std::unique_ptr<logger> global_logger) { global_logger_ = std::move(global_logger); }
|
||||
|
||||
std::shared_ptr<logger> context::global_logger() { return global_logger_; }
|
||||
|
||||
// Return raw ptr to the global logger.
|
||||
// To be used directly by the spdlog default api (e.g. spdlog::info)
|
||||
// This make the default API faster, but cannot be used concurrently with set_global_logger().
|
||||
// e.g do not call set_global_logger() from one thread while calling spdlog::info() from another.
|
||||
logger *context::global_logger_raw() const noexcept { return global_logger_.get(); }
|
||||
|
||||
// set global logger
|
||||
void context::set_logger(std::shared_ptr<logger> new_global_logger) { global_logger_ = std::move(new_global_logger); }
|
||||
|
||||
void context::set_tp(std::shared_ptr<thread_pool> tp) {
|
||||
std::lock_guard lock(tp_mutex_);
|
||||
tp_ = std::move(tp);
|
||||
}
|
||||
|
||||
std::shared_ptr<thread_pool> context::get_tp() {
|
||||
std::lock_guard lock(tp_mutex_);
|
||||
return tp_;
|
||||
}
|
||||
|
||||
// clean all resources and threads started by the registry
|
||||
void context::shutdown() {
|
||||
std::lock_guard lock(tp_mutex_);
|
||||
tp_.reset();
|
||||
}
|
||||
|
||||
std::recursive_mutex &context::tp_mutex() { return tp_mutex_; }
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@ -0,0 +1,40 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#include "iostream"
|
||||
#include "spdlog/details/err_helper.h"
|
||||
#include "spdlog/details/os.h"
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
// Prints error to stderr with source location (if available). A stderr sink is not used because reaching
|
||||
// this point might indicate a problem with the logging system itself so we use fputs() directly.
|
||||
void err_helper::handle_ex(const std::string &origin, const source_loc &loc, const std::exception &ex) const {
|
||||
if (custom_err_handler_) {
|
||||
custom_err_handler_(ex.what());
|
||||
return;
|
||||
}
|
||||
const auto tm_time = os::localtime();
|
||||
char date_buf[32];
|
||||
std::strftime(date_buf, sizeof(date_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
|
||||
std::string msg;
|
||||
if (loc.empty()) {
|
||||
msg = fmt_lib::format("[*** LOG ERROR ***] [{}] [{}] {}\n", date_buf, origin, ex.what());
|
||||
} else {
|
||||
msg = fmt_lib::format("[*** LOG ERROR ***] [{}({})] [{}] [{}] {}\n", loc.filename, loc.line, date_buf, origin,
|
||||
ex.what());
|
||||
}
|
||||
std::fputs(msg.c_str(), stderr);
|
||||
}
|
||||
|
||||
void err_helper::handle_unknown_ex(const std::string &origin, const source_loc &loc) const {
|
||||
handle_ex(origin, loc, std::runtime_error("unknown exception"));
|
||||
}
|
||||
|
||||
void err_helper::set_err_handler(err_handler handler) {
|
||||
custom_err_handler_ = std::move(handler);
|
||||
}
|
||||
|
||||
} // 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
|
||||
@ -1,117 +0,0 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#include "spdlog/details/thread_pool.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "spdlog/common.h"
|
||||
|
||||
namespace spdlog {
|
||||
namespace details {
|
||||
|
||||
thread_pool::thread_pool(size_t q_max_items,
|
||||
size_t threads_n,
|
||||
std::function<void()> on_thread_start,
|
||||
std::function<void()> on_thread_stop)
|
||||
: q_(q_max_items) {
|
||||
if (threads_n == 0 || threads_n > 1000) {
|
||||
throw_spdlog_ex(
|
||||
"spdlog::thread_pool(): invalid threads_n param (valid "
|
||||
"range is 1-1000)");
|
||||
}
|
||||
for (size_t i = 0; i < threads_n; i++) {
|
||||
threads_.emplace_back([this, on_thread_start, on_thread_stop] {
|
||||
on_thread_start();
|
||||
this->thread_pool::worker_loop_();
|
||||
on_thread_stop();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
thread_pool::thread_pool(size_t q_max_items, size_t threads_n, std::function<void()> on_thread_start)
|
||||
: thread_pool(q_max_items, threads_n, on_thread_start, [] {}) {}
|
||||
|
||||
thread_pool::thread_pool(size_t q_max_items, size_t threads_n)
|
||||
: thread_pool(q_max_items, threads_n, [] {}, [] {}) {}
|
||||
|
||||
// message all threads to terminate gracefully join them
|
||||
thread_pool::~thread_pool() {
|
||||
try {
|
||||
for (size_t i = 0; i < threads_.size(); i++) {
|
||||
post_async_msg_(async_msg(async_msg_type::terminate), async_overflow_policy::block);
|
||||
}
|
||||
|
||||
for (auto &t : threads_) {
|
||||
t.join();
|
||||
}
|
||||
} catch (...) {
|
||||
}
|
||||
}
|
||||
|
||||
void thread_pool::post_log(async_logger_ptr &&worker_ptr, const details::log_msg &msg, async_overflow_policy overflow_policy) {
|
||||
async_msg async_m(std::move(worker_ptr), async_msg_type::log, msg);
|
||||
post_async_msg_(std::move(async_m), overflow_policy);
|
||||
}
|
||||
|
||||
void thread_pool::post_flush(async_logger_ptr &&worker_ptr, async_overflow_policy overflow_policy) {
|
||||
post_async_msg_(async_msg(std::move(worker_ptr), async_msg_type::flush), overflow_policy);
|
||||
}
|
||||
|
||||
size_t thread_pool::overrun_counter() { return q_.overrun_counter(); }
|
||||
|
||||
void thread_pool::reset_overrun_counter() { q_.reset_overrun_counter(); }
|
||||
|
||||
size_t thread_pool::discard_counter() { return q_.discard_counter(); }
|
||||
|
||||
void thread_pool::reset_discard_counter() { q_.reset_discard_counter(); }
|
||||
|
||||
size_t thread_pool::queue_size() { return q_.size(); }
|
||||
|
||||
void thread_pool::post_async_msg_(async_msg &&new_msg, async_overflow_policy overflow_policy) {
|
||||
if (overflow_policy == async_overflow_policy::block) {
|
||||
q_.enqueue(std::move(new_msg));
|
||||
} else if (overflow_policy == async_overflow_policy::overrun_oldest) {
|
||||
q_.enqueue_nowait(std::move(new_msg));
|
||||
} else {
|
||||
assert(overflow_policy == async_overflow_policy::discard_new);
|
||||
q_.enqueue_if_have_room(std::move(new_msg));
|
||||
}
|
||||
}
|
||||
|
||||
void thread_pool::worker_loop_() {
|
||||
while (process_next_msg_()) {
|
||||
}
|
||||
}
|
||||
|
||||
// process next message in the queue
|
||||
// return true if this thread should still be active (while no terminate msg
|
||||
// was received)
|
||||
bool thread_pool::process_next_msg_() {
|
||||
async_msg incoming_async_msg;
|
||||
q_.dequeue(incoming_async_msg);
|
||||
|
||||
switch (incoming_async_msg.msg_type) {
|
||||
case async_msg_type::log: {
|
||||
incoming_async_msg.worker_ptr->backend_sink_it_(incoming_async_msg);
|
||||
return true;
|
||||
}
|
||||
case async_msg_type::flush: {
|
||||
incoming_async_msg.worker_ptr->backend_flush_();
|
||||
return true;
|
||||
}
|
||||
|
||||
case async_msg_type::terminate: {
|
||||
return false;
|
||||
}
|
||||
|
||||
default: {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace details
|
||||
} // namespace spdlog
|
||||
@ -0,0 +1,130 @@
|
||||
// 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 <cassert>
|
||||
#include <memory>
|
||||
|
||||
#include "spdlog/common.h"
|
||||
#include "spdlog/details/mpmc_blocking_q.h"
|
||||
#include "spdlog/pattern_formatter.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
|
||||
namespace spdlog {
|
||||
namespace sinks {
|
||||
|
||||
async_sink::async_sink(config async_config)
|
||||
: config_(std::move(async_config)) {
|
||||
if (config_.queue_size == 0 || config_.queue_size > max_queue_size) {
|
||||
throw spdlog_ex("async_sink: invalid queue size");
|
||||
}
|
||||
q_ = std::make_unique<queue_t>(config_.queue_size);
|
||||
worker_thread_ = std::thread([this] {
|
||||
if (config_.on_thread_start) config_.on_thread_start();
|
||||
this->backend_loop_();
|
||||
if (config_.on_thread_stop) config_.on_thread_stop();
|
||||
});
|
||||
}
|
||||
|
||||
async_sink::~async_sink() {
|
||||
try {
|
||||
q_->enqueue(async_log_msg(async_log_msg::type::terminate));
|
||||
worker_thread_.join();
|
||||
} catch (...) {
|
||||
printf("Exception in ~async_sink()\n");
|
||||
}
|
||||
}
|
||||
|
||||
void async_sink::log(const details::log_msg &msg) { send_message_(async_log_msg::type::log, msg); }
|
||||
|
||||
void async_sink::flush() { send_message_(async_log_msg::type::flush, details::log_msg()); }
|
||||
|
||||
void async_sink::set_pattern(const std::string &pattern) { set_formatter(std::make_unique<pattern_formatter>(pattern)); }
|
||||
|
||||
void async_sink::set_formatter(std::unique_ptr<formatter> formatter) {
|
||||
const auto &sinks = config_.sinks;
|
||||
for (auto it = sinks.begin(); it != sinks.end(); ++it) {
|
||||
if (std::next(it) == sinks.end()) {
|
||||
// last element - we can move it.
|
||||
(*it)->set_formatter(std::move(formatter));
|
||||
break; // to prevent clang-tidy warning
|
||||
}
|
||||
(*it)->set_formatter(formatter->clone());
|
||||
}
|
||||
}
|
||||
|
||||
size_t async_sink::get_overrun_counter() const { return q_->overrun_counter(); }
|
||||
|
||||
void async_sink::reset_overrun_counter() const { q_->reset_overrun_counter(); }
|
||||
|
||||
size_t async_sink::get_discard_counter() const { return q_->discard_counter(); }
|
||||
|
||||
void async_sink::reset_discard_counter() const { q_->reset_discard_counter(); }
|
||||
|
||||
const async_sink::config &async_sink::get_config() const { return config_; }
|
||||
|
||||
// private methods
|
||||
void async_sink::send_message_(async_log_msg::type msg_type, const details::log_msg &msg) const {
|
||||
switch (config_.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 async_sink::backend_loop_() {
|
||||
details::async_log_msg incoming_msg;
|
||||
for (;;) {
|
||||
q_->dequeue(incoming_msg);
|
||||
switch (incoming_msg.message_type()) {
|
||||
case async_log_msg::type::log:
|
||||
backend_log_(incoming_msg);
|
||||
break;
|
||||
case async_log_msg::type::flush:
|
||||
backend_flush_();
|
||||
break;
|
||||
case async_log_msg::type::terminate:
|
||||
return;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void async_sink::backend_log_(const details::log_msg &msg) {
|
||||
for (const auto &sink : config_.sinks) {
|
||||
if (sink->should_log(msg.log_level)) {
|
||||
try {
|
||||
sink->log(msg);
|
||||
} catch (const std::exception &ex) {
|
||||
err_helper_.handle_ex("async log", msg.source, ex);
|
||||
} catch (...) {
|
||||
err_helper_.handle_unknown_ex("async log", source_loc{});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void async_sink::backend_flush_() {
|
||||
for (const auto &sink : config_.sinks) {
|
||||
try {
|
||||
sink->flush();
|
||||
} catch (const std::exception &ex) {
|
||||
err_helper_.handle_ex("async flush", source_loc{}, ex);
|
||||
} catch (...) {
|
||||
err_helper_.handle_unknown_ex("async flush", source_loc{});
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace sinks
|
||||
} // namespace spdlog
|
||||
@ -1,14 +0,0 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#include "spdlog/sinks/sink.h"
|
||||
|
||||
#include "spdlog/common.h"
|
||||
|
||||
bool spdlog::sinks::sink::should_log(spdlog::level msg_level) const {
|
||||
return msg_level >= level_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void spdlog::sinks::sink::set_level(level level) { level_.store(level, std::memory_order_relaxed); }
|
||||
|
||||
spdlog::level spdlog::sinks::sink::log_level() const { return level_.load(std::memory_order_relaxed); }
|
||||
@ -1,57 +0,0 @@
|
||||
// Copyright(c) 2015-present, Gabi Melman & spdlog contributors.
|
||||
// Distributed under the MIT License (http://opensource.org/licenses/MIT)
|
||||
|
||||
#include "spdlog/sinks/stdout_color_sinks.h"
|
||||
|
||||
#include "spdlog/async.h"
|
||||
#include "spdlog/common.h"
|
||||
#include "spdlog/details/synchronous_factory.h"
|
||||
#include "spdlog/logger.h"
|
||||
|
||||
namespace spdlog {
|
||||
|
||||
template <typename Factory>
|
||||
std::shared_ptr<logger> stdout_color_mt(const std::string &logger_name, color_mode mode) {
|
||||
return Factory::template create<sinks::stdout_color_sink_mt>(logger_name, mode);
|
||||
}
|
||||
|
||||
template <typename Factory>
|
||||
std::shared_ptr<logger> stdout_color_st(const std::string &logger_name, color_mode mode) {
|
||||
return Factory::template create<sinks::stdout_color_sink_st>(logger_name, mode);
|
||||
}
|
||||
|
||||
template <typename Factory>
|
||||
std::shared_ptr<logger> stderr_color_mt(const std::string &logger_name, color_mode mode) {
|
||||
return Factory::template create<sinks::stderr_color_sink_mt>(logger_name, mode);
|
||||
}
|
||||
|
||||
template <typename Factory>
|
||||
std::shared_ptr<logger> stderr_color_st(const std::string &logger_name, color_mode mode) {
|
||||
return Factory::template create<sinks::stderr_color_sink_st>(logger_name, mode);
|
||||
}
|
||||
} // namespace spdlog
|
||||
|
||||
// template instantiations
|
||||
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt<spdlog::synchronous_factory>(
|
||||
const std::string &logger_name, color_mode mode);
|
||||
|
||||
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stdout_color_st<spdlog::synchronous_factory>(
|
||||
const std::string &logger_name, color_mode mode);
|
||||
|
||||
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt<spdlog::synchronous_factory>(
|
||||
const std::string &logger_name, color_mode mode);
|
||||
|
||||
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stderr_color_st<spdlog::synchronous_factory>(
|
||||
const std::string &logger_name, color_mode mode);
|
||||
|
||||
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stdout_color_mt<spdlog::async_factory>(const std::string &logger_name,
|
||||
color_mode mode);
|
||||
|
||||
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stdout_color_st<spdlog::async_factory>(const std::string &logger_name,
|
||||
color_mode mode);
|
||||
|
||||
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stderr_color_mt<spdlog::async_factory>(const std::string &logger_name,
|
||||
color_mode mode);
|
||||
|
||||
template SPDLOG_API std::shared_ptr<spdlog::logger> spdlog::stderr_color_st<spdlog::async_factory>(const std::string &logger_name,
|
||||
color_mode mode);
|
||||
Loading…
Reference in New Issue