From 3cab260814613b0a7e6889d2a030b2992c916d1a Mon Sep 17 00:00:00 2001 From: Mohammad Ali Date: Thu, 19 Jan 2023 21:16:34 +0330 Subject: [PATCH] Add a trivial callback sink (#2610) Add a trivial callback sink --- README.md | 22 ++++++++++ example/example.cpp | 11 +++++ include/spdlog/sinks/callback_sink.h | 61 ++++++++++++++++++++++++++++ tests/CMakeLists.txt | 1 + tests/test_custom_callbacks.cpp | 34 ++++++++++++++++ 5 files changed, 129 insertions(+) create mode 100644 include/spdlog/sinks/callback_sink.h create mode 100644 tests/test_custom_callbacks.cpp diff --git a/README.md b/README.md index b98a920be..d650b3ed2 100644 --- a/README.md +++ b/README.md @@ -232,6 +232,28 @@ void multi_sink_example() } ``` +--- +#### Logger with a custom callback function that receives the logs +```c++ + +// create logger with a lambda function callback, the callback will be called +// each time something is logged to the logger +void callback_example() +{ + auto callback_sink = std::make_shared([](const spdlog::details::log_msg &msg) { + // for example you can be notified by sending an email to yourself + }); + callback_sink->set_level(spdlog::level::err); + + auto console_sink = std::make_shared(); + spdlog::logger logger("custom_callback_logger", {console_sink, callback_sink}); + + logger.info("some info log"); + logger.debug("some debug log"); + logger.error("critical issue"); // will notify you +} +``` + --- #### Asynchronous logging ```c++ diff --git a/example/example.cpp b/example/example.cpp index ccfdcf2f9..316a22b1a 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -12,6 +12,7 @@ void stdout_logger_example(); void basic_example(); void rotating_example(); void daily_example(); +void callback_example(); void async_example(); void binary_example(); void vector_example(); @@ -72,6 +73,7 @@ int main(int, char *[]) basic_example(); rotating_example(); daily_example(); + callback_example(); async_example(); binary_example(); vector_example(); @@ -136,6 +138,15 @@ void daily_example() auto daily_logger = spdlog::daily_logger_mt("daily_logger", "logs/daily.txt", 2, 30); } +#include "spdlog/sinks/callback_sink.h" +void callback_example() +{ + // Create the logger + auto logger = spdlog::callback_logger_mt("custom_callback_logger", [](const spdlog::details::log_msg &/*msg*/) { + // do what you need to do with msg + }); +} + #include "spdlog/cfg/env.h" void load_levels_example() { diff --git a/include/spdlog/sinks/callback_sink.h b/include/spdlog/sinks/callback_sink.h new file mode 100644 index 000000000..bcd31383b --- /dev/null +++ b/include/spdlog/sinks/callback_sink.h @@ -0,0 +1,61 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#include +#include +#include + +#include +#include + +namespace spdlog { + +// callbacks type +typedef std::function custom_log_callback; + +namespace sinks { +/* + * Trivial callback sink, gets a callback function and calls it on each log + */ +template +class callback_sink final : public base_sink +{ +public: + explicit callback_sink(const custom_log_callback &callback) + : callback_{callback} + {} + +protected: + void sink_it_(const details::log_msg &msg) override + { + callback_(msg); + } + void flush_() override{}; + +private: + custom_log_callback callback_; +}; + +using callback_sink_mt = callback_sink; +using callback_sink_st = callback_sink; + +} // namespace sinks + +// +// factory functions +// +template +inline std::shared_ptr callback_logger_mt(const std::string &logger_name, const custom_log_callback &callback) +{ + return Factory::template create(logger_name, callback); +} + +template +inline std::shared_ptr callback_logger_st(const std::string &logger_name, const custom_log_callback &callback) +{ + return Factory::template create(logger_name, callback); +} + +} // namespace spdlog diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7fe4791eb..12204e2b2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -31,6 +31,7 @@ set(SPDLOG_UTESTS_SOURCES test_stdout_api.cpp test_backtrace.cpp test_create_dir.cpp + test_custom_callbacks.cpp test_cfg.cpp test_time_point.cpp test_stopwatch.cpp) diff --git a/tests/test_custom_callbacks.cpp b/tests/test_custom_callbacks.cpp new file mode 100644 index 000000000..877e16083 --- /dev/null +++ b/tests/test_custom_callbacks.cpp @@ -0,0 +1,34 @@ +/* + * This content is released under the MIT License as specified in https://raw.githubusercontent.com/gabime/spdlog/master/LICENSE + */ +#include "includes.h" +#include "test_sink.h" +#include "spdlog/sinks/callback_sink.h" +#include "spdlog/async.h" +#include "spdlog/common.h" + +TEST_CASE("custom_callback_logger", "[custom_callback_logger]]") +{ + std::vector lines; + spdlog::pattern_formatter formatter; + auto callback_logger = std::make_shared([&](const spdlog::details::log_msg &msg) { + spdlog::memory_buf_t formatted; + formatter.format(msg, formatted); + auto eol_len = strlen(spdlog::details::os::default_eol); + lines.emplace_back(formatted.begin(), formatted.end() - eol_len); + }); + std::shared_ptr test_sink(new spdlog::sinks::test_sink_st); + + spdlog::logger logger("test-callback", {callback_logger, test_sink}); + + logger.info("test message 1"); + logger.info("test message 2"); + logger.info("test message 3"); + + std::vector ref_lines = test_sink->lines(); + + REQUIRE(lines[0] == ref_lines[0]); + REQUIRE(lines[1] == ref_lines[1]); + REQUIRE(lines[2] == ref_lines[2]); + spdlog::drop_all(); +}