diff --git a/CMakeLists.txt b/CMakeLists.txt index 943143c..58533a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,8 +28,22 @@ set(ADC_NETWORK_HEADERS net/adc_netproto.h net/adc_netservice.h net/adc_endpoint.h + net/adc_netserver.h ) + +option(SPDLOG_LIBRARY "Use SPDLOG library for logging" ON) + +if (SPDLOG_LIBRARY) + find_package(spdlog REQUIRED) + + set(ADC_NETWORK_HEADERS ${ADC_NETWORK_HEADERS} + net/adc_netserver_spdlog.h + ) + + add_compile_definitions(PUBLIC USE_SPDLOG_LIBRARY) +endif() + option(BUILD_TESTS "Build tests" ON) if (BUILD_TESTS) diff --git a/common/adc_spdlog.h b/common/adc_spdlog.h new file mode 100644 index 0000000..a817849 --- /dev/null +++ b/common/adc_spdlog.h @@ -0,0 +1,88 @@ +#pragma once + + + +#ifdef USE_SPDLOG_LIBRARY + +#include +#include + + +#include "adc_traits.h" + +namespace adc +{ + +template +class AdcSpdlogGenericDecorator : public BaseT +{ +protected: + std::shared_ptr _logger; + + void* _thisAddress; + +public: + constexpr static char loggerDefaultFormat[] = "[%Y-%m-%d %T.%e] [%l]: [%v]"; + + template + AdcSpdlogGenericDecorator(std::shared_ptr logger, BaseCtorArgTs&&... ctor_args) + : BaseT(std::forward(ctor_args)...), _logger(logger), _thisAddress((void*)std::addressof(*this)) + { + _logger->set_pattern(loggerDefaultFormat); + + logMethodCalling("AdcSpdlogGenericDecorator"); // in debug + logMethodCalling(); // in trace + + _logger->debug("set logger with {}", loggerInfo()); + } + + virtual ~AdcSpdlogGenericDecorator() + { + // + logMethodCalling("AdcSpdlogGenericDecorator"); // in debug + } + + +protected: + // format string must contain two formating specifications: + // 1 - logger name (string) + // 2 - logging level (string) + template + R loggerInfo(std::string_view fmt) const + { + R info; + + std::format_to(std::back_inserter(info), fmt, _logger->name(), spdlog::level::to_string_view(_logger->level())); + + return info; + } + + std::string loggerInfo() const + { + // + return loggerInfo("name [{}] and log-level [{}]"); + } + + void logMethodCalling(spdlog::level::level_enum level = spdlog::level::trace) const + { + _logger->log(level, "call {} (this: {}, thread ID: {})", __PRETTY_FUNCTION__, _thisAddress, + std::this_thread::get_id()); + } + + void logMethodCalling(std::string_view class_name, + spdlog::level::level_enum level = spdlog::level::debug, + std::string_view method_name = "") const + { + if (method_name.empty()) { + method_name = __FUNCTION__; + } + + _logger->log(level, "call {}::{} (this: {}, thread ID: {})", class_name, method_name, _thisAddress, + std::this_thread::get_id()); + } +}; + + +} // namespace adc + +#endif diff --git a/net/adc_netserver.h b/net/adc_netserver.h index f03ffd1..1383146 100644 --- a/net/adc_netserver.h +++ b/net/adc_netserver.h @@ -21,17 +21,40 @@ namespace adc { -/* Server session and its abstract decorator */ +namespace traits +{ -template +// network server session implementation concept +template +concept adc_netserver_session_impl_c = requires(T t, const T t_const) { + typename T::session_ident_t; + + { t_const.sessionIdent() } -> std::same_as; + { t.start() } -> std::same_as; + { t.stop() } -> std::same_as; +}; + +} // namespace traits + + + +/* Server session */ + +template class AdcNetServerSession : std::enable_shared_from_this> { +protected: + ImplT _impl; + public: - using shared_ptr_t = std::shared_ptr; - using weak_ptr_t = std::weak_ptr; + typedef ImplT session_impl_t; + typedef std::shared_ptr shared_ptr_t; + typedef std::weak_ptr weak_ptr_t; + + using typename ImplT::session_ident_t; template - AdcNetServerSession(ImplCtorArgTs&&... ctor_args) : _implUptr(std::forward(ctor_args)...) + AdcNetServerSession(ImplCtorArgTs&&... ctor_args) : _impl(std::forward(ctor_args)...) { } @@ -39,63 +62,72 @@ public: virtual ~AdcNetServerSession() = default; - auto sessionIdent() const + virtual session_ident_t sessionIdent() const { // - return _implUptr->_sessionIdent(); + return _impl._sessionIdent(); } virtual void start() { // - _implUptr->start(); + _impl.start(); } virtual void stop() { // - _implUptr->stop(); + _impl.stop(); } - -protected: - std::unique_ptr _implUptr; }; -template > SessionT> -class AdcNetServerSessionDecorator : public SessionT + +namespace traits { -public: - using SessionT::sessionIdent; - template - AdcNetServerSessionDecorator(ImplCtorArgTs&&... ctor_args) : SessionT(std::forward(ctor_args)...) - { - } +// network server session concept +template +concept adc_netserver_session_c = requires { + typename T::session_impl_t; - virtual ~AdcNetServerSessionDecorator() = default; - - virtual std::invoke_result_t sessionIdent() const = 0; - - virtual void start() = 0; - - virtual void stop() = 0; + std::derived_from>; }; +} // namespace traits -/* Server */ +/* network server */ -template +namespace traits +{ + +template +concept adc_netserver_impl_c = requires(T t, const T t_const) { + typename T::server_ident_t; + + { t_const.serverIdent() } -> std::same_as; + { t.start() } -> std::same_as; + { t.stop() } -> std::same_as; + { t.daemonizePrepare() } -> std::same_as; + { t.daemonizeFinalize() } -> std::same_as; +}; + +} // namespace traits + +template class AdcNetServer { protected: - std::unique_ptr _implUptr; + ImplT _impl; public: + typedef ImplT server_impl_t; + using typename ImplT::server_ident_t; + template - AdcNetServer(ImplCtorArgTs&&... ctor_args) : _implUptr(std::make_unique(ctor_args)...) + AdcNetServer(ImplCtorArgTs&&... ctor_args) : _impl(std::make_unique(ctor_args)...) { } @@ -103,18 +135,27 @@ public: virtual ~AdcNetServer() = default; - virtual void start() = 0; + virtual server_ident_t serverIdent() const + { + // + return _impl.serverIdent(); + } - virtual void stop() = 0; + virtual void start() + { + // + _impl.start(); + }; - template - void start() {}; + virtual void stop() + { + // + _impl.stop(); + }; - template - void stop() {}; // run server as daemon (still only on POSIX OSes) - void daemonize() + virtual void daemonize() { daemonizePrepare(); @@ -161,9 +202,34 @@ public: daemonizeFinalize(); } - virtual void daemonizePrepare() = 0; - virtual void daemonizeFinalize() = 0; +protected: + virtual void daemonizePrepare() + { + // + _impl.daemonizePrepare(); + }; + + virtual void daemonizeFinalize() + { + // + _impl.daemonizeFinalize(); + }; }; + +namespace traits +{ + +// network server concept +template +concept adc_netserver_c = requires { + typename T::server_impl_t; + std::derived_from>; +}; + +} // namespace traits + + + } // namespace adc diff --git a/net/adc_netserver_spdlog.h b/net/adc_netserver_spdlog.h new file mode 100644 index 0000000..9e5c6cb --- /dev/null +++ b/net/adc_netserver_spdlog.h @@ -0,0 +1,151 @@ +#pragma once + +/* + +ABSTRACT DEVICE COMPONENTS LIBRARY + + spdlog-library extensions + +*/ + + +#ifdef USE_SPDLOG_LIBRARY + +#include + +#include "../common/adc_traits.h" +#include "adc_netserver.h" + +namespace adc +{ + +template +class AdcNetServerSessionSpdlogDecorator : public SessionT +{ +protected: + std::shared_ptr _logger; + + void* _thisAddress; + std::string _sessionID; + +public: + // using SessionT::sessionIdent; + using typename SessionT::session_ident_t; + + static_assert(traits::formattable, "NETWORK SESSION IDENT TYPE MUST BE A FORMATTABLE ONE!!!"); + + template + AdcNetServerSessionSpdlogDecorator(std::shared_ptr logger, ImplCtorArgTs&&... ctor_args) + : SessionT(std::forward(ctor_args)...), + _logger(logger), + _thisAddress((void*)std::addressof(this)) + { + fmt::format_to(std::back_inserter(_sessionID), "{}", SessionT::sessionIdent()); + + _logger->trace("Call AdcNetServerSessionSpdlogDecorator::AdcNetServerSessionSpdlogDecorator (this: {})", + _thisAddress); + + _logger->debug("Creating network server session with ID = [{}] (ADDR = {})", _sessionID, _thisAddress); + _logger->debug("use logger with name [{}] and level [{}]", _logger->name(), + spdlog::level::to_string_view(_logger->level())); + } + + virtual ~AdcNetServerSessionSpdlogDecorator() + { + _logger->trace("Call AdcNetServerSessionSpdlogDecorator::~AdcNetServerSessionSpdlogDecorator (this: {})", + _thisAddress); + + _logger->debug("Deleting network server session with ID = [{}] (ADDR = {})", _sessionID, _thisAddress); + + _logger->flush(); + }; + + virtual session_ident_t sessionIdent() const override + { + _logger->trace("Call AdcNetServerSessionSpdlogDecorator::sessionIdent (this: {})", _thisAddress); + + return SessionT::sessionIdent(); + }; + + virtual void start() override + { + _logger->trace("Call AdcNetServerSessionSpdlogDecorator::start (this: {})", _thisAddress); + + _logger->info("Starting network server session with ID = [{}] (ADDR = {})", _sessionID, _thisAddress); + + SessionT::start(); + }; + + virtual void stop() override + { + _logger->trace("Call AdcNetServerSessionSpdlogDecorator::stop (this: {})", _thisAddress); + + _logger->info("Stopping network server session with ID = [{}] (ADDR = {})", _sessionID, _thisAddress); + + SessionT::stop(); + }; +}; + + + +template +class AdcNetServerSpdlogDecorator : public ServerT +{ +protected: + std::shared_ptr _logger; + + void* _thisAddress; + std::string _serverID; + +public: + using typename ServerT::server_ident_t; + + template + AdcNetServerSpdlogDecorator(std::shared_ptr logger, ImplCtorArgTs&&... ctor_args) + : ServerT(std::forward(ctor_args)...), _logger(logger), _thisAddress((void*)std::addressof(this)) + { + fmt::format_to(std::back_inserter(_serverID), "{}", ServerT::serverIdent()); + + _logger->trace("Call AdcNetServerSpdlogDecorator::AdcNetServerSpdlogDecorator (this: {})", _thisAddress); + + _logger->debug("Creating network server session with ID = [{}] (ADDR = {})", _serverID, _thisAddress); + _logger->debug("use logger with name [{}] and level [{}]", _logger->name(), + spdlog::level::to_string_view(_logger->level())); + } + + + virtual ~AdcNetServerSpdlogDecorator() + { + _logger->trace("Call AdcNetServerSpdlogDecorator::~AdcNetServerSpdlogDecorator (this: {})", _thisAddress); + + _logger->debug("Deleting network server with ID = [{}] (ADDR = {})", _serverID, _thisAddress); + + _logger->flush(); + } + + + virtual server_ident_t serverIdent() const + { + // + return ServerT::serverIdent(); + } + + virtual void start() + { + _logger->trace("Call AdcNetServerSpdlogDecorator::start (this: {})", _thisAddress); + + _logger->info("Starting network server with ID = [{}] (ADDR = {})", _serverID, _thisAddress); + + ServerT::start(); + }; + + virtual void stop() + { + // + ServerT::stop(); + }; +}; + +} // namespace adc + +#endif