From 3bc49604d7ae7a14d8a8e33389eceef915c6356d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9C=D0=B0=D1=80=D0=BA=D0=B5=D0=BB=D0=BE=D0=B2=20=D0=9C?= =?UTF-8?q?=D0=B0=D0=BA=D1=81=D0=B8=D0=BC?= <53077323+mmarkeloff@users.noreply.github.com> Date: Mon, 6 Sep 2021 10:55:37 +0300 Subject: [PATCH] Add tcp_syslog_client_sink Support for sending messages to remote syslog server over TCP --- example/example.cpp | 8 ++ include/spdlog/details/syslog_consts.h | 51 ++++++++++++ include/spdlog/sinks/syslog_client_sinks.h | 90 ++++++++++++++++++++++ 3 files changed, 149 insertions(+) create mode 100644 include/spdlog/details/syslog_consts.h create mode 100644 include/spdlog/sinks/syslog_client_sinks.h diff --git a/example/example.cpp b/example/example.cpp index dea76f2f..21408d7a 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -20,6 +20,7 @@ void user_defined_example(); void err_handler_example(); void syslog_example(); void custom_flags_example(); +void tcp_syslog_client_example(); #include "spdlog/spdlog.h" #include "spdlog/cfg/env.h" // support for loading levels from the environment variable @@ -292,3 +293,10 @@ void custom_flags_example() formatter->add_flag('*').set_pattern("[%n] [%*] [%^%l%$] %v"); spdlog::set_formatter(std::move(formatter)); } + +#include "spdlog/sinks/syslog_client_sinks.h" +void tcp_syslog_client_example() +{ + auto syslog_logger = spdlog::tcp_syslog_client_logger_mt("syslog_client", {"127.0.0.1", 514}); + syslog_logger->warn("This message will be sended to syslog server"); +} diff --git a/include/spdlog/details/syslog_consts.h b/include/spdlog/details/syslog_consts.h new file mode 100644 index 00000000..cd52456b --- /dev/null +++ b/include/spdlog/details/syslog_consts.h @@ -0,0 +1,51 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +namespace spdlog { +namespace details { + +// https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1 +struct syslog_consts { + enum level { + SL_EMERG = 0, + SL_ALERT, + SL_CRIT, + SL_ERR, + SL_WARNING, + SL_NOTICE, + SL_INFO, + SL_DEBUG + }; + + enum facility { + SF_KERN = 0, + SF_USER, + SF_MAIL, + SF_DAEMON, + SF_AUTH, + SF_SYSLOG, + SF_LPR, + SF_NEWS, + SF_UUCP, + SF_CRON, + SF_AUTHPRIV, + SF_FTP, + SF_NTP, + SF_AUDIT, + SF_ALERT, + SF_CRON2, + SF_LOCAL0, + SF_LOCAL1, + SF_LOCAL2, + SF_LOCAL3, + SF_LOCAL4, + SF_LOCAL5, + SF_LOCAL6, + SF_LOCAL7 + }; +}; + +} // namespace details +} // namespace spdlog \ No newline at end of file diff --git a/include/spdlog/sinks/syslog_client_sinks.h b/include/spdlog/sinks/syslog_client_sinks.h new file mode 100644 index 00000000..74c79dde --- /dev/null +++ b/include/spdlog/sinks/syslog_client_sinks.h @@ -0,0 +1,90 @@ +// Copyright(c) 2015-present, Gabi Melman & spdlog contributors. +// Distributed under the MIT License (http://opensource.org/licenses/MIT) + +#pragma once + +#include +#include +#include + +namespace spdlog { +namespace sinks { + +/** + * Sink that sends messages to remote syslog server over TCP + */ +template +class tcp_syslog_client_sink : public spdlog::sinks::tcp_sink +{ +private: + using syslog_facility_t = spdlog::details::syslog_consts::facility; +public: + explicit tcp_syslog_client_sink(tcp_sink_config &&sink_config, syslog_facility_t syslog_facility) + : spdlog::sinks::tcp_sink{std::move(sink_config)} + , syslog_levels_{ + /* spdlog::level::trace */ spdlog::details::syslog_consts::SL_DEBUG, + /* spdlog::level::debug */ spdlog::details::syslog_consts::SL_DEBUG, + /* spdlog::level::info */ spdlog::details::syslog_consts::SL_INFO, + /* spdlog::level::warn */ spdlog::details::syslog_consts::SL_WARNING, + /* spdlog::level::err */ spdlog::details::syslog_consts::SL_ERR, + /* spdlog::level::critical */ spdlog::details::syslog_consts::SL_CRIT, + /* spdlog::level::off */ spdlog::details::syslog_consts::SL_INFO} + , syslog_facility_{syslog_facility} + { + + } +protected: + void sink_it_(const spdlog::details::log_msg &msg) override + { + memory_buf_t formatted; + base_sink::formatter_->format(msg, formatted); + + // https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1 + std::string pri{"<" + std::to_string((syslog_facility_ << 3) + syslog_prio_from_level(msg)) + ">"}; + + memory_buf_t syslog_formatted; + syslog_formatted.append(pri); + syslog_formatted.append(formatted); + + if (!spdlog::sinks::tcp_sink::client_.is_connected()) + { + spdlog::sinks::tcp_sink::client_.connect( + spdlog::sinks::tcp_sink::config_.server_host, + spdlog::sinks::tcp_sink::config_.server_port); + } + spdlog::sinks::tcp_sink::client_.send(syslog_formatted.data(), syslog_formatted.size()); + } +private: + using levels_array = std::array; + + levels_array syslog_levels_; + syslog_facility_t syslog_facility_; + + int syslog_prio_from_level(const details::log_msg &msg) const + { + return syslog_levels_.at(static_cast(msg.level)); + } +}; + +using tcp_syslog_client_sink_mt = tcp_syslog_client_sink; +using tcp_syslog_client_sink_st = tcp_syslog_client_sink; + +} // namespace sinks + +template +inline std::shared_ptr tcp_syslog_client_logger_mt(const std::string &logger_name, + spdlog::sinks::tcp_sink_config sink_config, + spdlog::details::syslog_consts::facility syslog_facility = spdlog::details::syslog_consts::facility::SF_USER) +{ + return Factory::template create(logger_name, std::move(sink_config), syslog_facility); +} + +template +inline std::shared_ptr tcp_syslog_client_logger_st(const std::string &logger_name, + spdlog::sinks::tcp_sink_config sink_config, + spdlog::details::syslog_consts::facility syslog_facility = spdlog::details::syslog_consts::facility::SF_USER) +{ + return Factory::template create(logger_name, std::move(sink_config), syslog_facility); +} + +} // namespace spdlog \ No newline at end of file