diff --git a/CMakeLists.txt b/CMakeLists.txt index 084a7f0..ca5f433 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ project(ADC LANGUAGES CXX) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}") -set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) # set(CMAKE_BUILD_TYPE Release) @@ -12,21 +12,21 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) # check compiler version to ensure supporting of # 'deducing this' C++23 feature # -if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14.0) - message(FATAL_ERROR "GCC version must be at least 14.0!") - endif() -elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18.0) - message(FATAL_ERROR "Clang version must be at least 18.0!") - endif() -elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.32") - message(FATAL_ERROR "MSVC version must be at least 19.32") - endif() -else() - message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang and GCC.") -endif() +# if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") +# if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14.0) +# message(FATAL_ERROR "GCC version must be at least 14.0!") +# endif() +# elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") +# if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18.0) +# message(FATAL_ERROR "Clang version must be at least 18.0!") +# endif() +# elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") +# if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.32") +# message(FATAL_ERROR "MSVC version must be at least 19.32") +# endif() +# else() +# message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang and GCC.") +# endif() @@ -85,6 +85,7 @@ option(SPDLOG_LIBRARY "Use of SPDLOG library for logging" ON) if (SPDLOG_LIBRARY) find_package(spdlog REQUIRED) + find_package(fmt REQUIRED) set(ADC_COMMON_HEADERS ${ADC_COMMON_HEADERS} common/adc_spdlog.h @@ -199,6 +200,7 @@ if (BUILD_TESTS) add_test(VALUE_HOLDER ${DEVATTR_TEST_APP}) add_test(NETMSG_TEST ${NETMSG_TEST_APP}) add_test(ASIO_NETSRV_TEST ${ASIO_NETSERVER_TEST_APP}) + target_link_libraries(${ASIO_NETSERVER_TEST_APP} PRIVATE fmt::fmt) enable_testing() endif(BUILD_TESTS) diff --git a/common/adc_spdlog.h b/common/adc_spdlog.h index 09f17df..9247804 100644 --- a/common/adc_spdlog.h +++ b/common/adc_spdlog.h @@ -19,15 +19,16 @@ namespace adc class AdcSPDLOGLogger { public: - // [year-month-day time.millisecs] [log-level]: log-message - constexpr static std::string_view LOGGER_DEFAULT_FORMAT = "[%Y-%m-%d %T.%e] [%l]: %v"; + // [year-month-day time.millisecs][log-level]: log-message + constexpr static std::string_view LOGGER_DEFAULT_FORMAT = "[%Y-%m-%d %T.%e][%l]: %v"; typedef spdlog::level::level_enum loglevel_t; - AdcSPDLOGLogger(std::shared_ptr logger, - const traits::adc_input_char_range auto& pattern = LOGGER_DEFAULT_FORMAT) - : _loggerSPtr(logger), _currentLogPattern(pattern) + template + AdcSPDLOGLogger(std::shared_ptr logger, const R& pattern = LOGGER_DEFAULT_FORMAT) + : _loggerSPtr(logger), _currentLogPattern() { + std::ranges::copy(pattern, std::back_inserter(_currentLogPattern)); _loggerSPtr->set_pattern(_currentLogPattern); } @@ -45,45 +46,81 @@ public: return _loggerSPtr->level(); } - void logMessage(loglevel_t level, std::string_view fmt, traits::formattable auto&&... args) + void logMessage(loglevel_t level, const std::string& msg) { - _loggerSPtr->log(level, fmt, std::forward(args)...); + _loggerSPtr->log(level, msg); } // specialized for given level methods - void logCritical(std::string_view fmt, traits::formattable auto&&... args) + void logCritical(const std::string& msg) { - logMessage(spdlog::level::critical, fmt, std::forward(args)...); + logMessage(spdlog::level::critical, msg); } - void logError(std::string_view fmt, traits::formattable auto&&... args) + void logError(const std::string& msg) { - logMessage(spdlog::level::err, fmt, std::forward(args)...); + logMessage(spdlog::level::err, msg); } - void logWarn(std::string_view fmt, traits::formattable auto&&... args) + void logWarn(const std::string& msg) { - logMessage(spdlog::level::warn, fmt, std::forward(args)...); + logMessage(spdlog::level::warn, msg); } - void logInfo(std::string_view fmt, traits::formattable auto&&... args) + void logInfo(const std::string& msg) { - logMessage(spdlog::level::info, fmt, std::forward(args)...); + logMessage(spdlog::level::info, msg); } - void logDebug(std::string_view fmt, traits::formattable auto&&... args) + void logDebug(const std::string& msg) { - logMessage(spdlog::level::debug, fmt, std::forward(args)...); + logMessage(spdlog::level::debug, msg); } - void logTrace(std::string_view fmt, traits::formattable auto&&... args) + void logTrace(const std::string& msg) { - logMessage(spdlog::level::trace, fmt, std::forward(args)...); + logMessage(spdlog::level::trace, msg); + } + + template + void logCritical(std::format_string fmt, ArgTs&&... args) + { + _loggerSPtr->log(spdlog::level::critical, fmt, std::forward(args)...); + } + + template + void logError(std::format_string fmt, ArgTs&&... args) + { + _loggerSPtr->log(spdlog::level::err, fmt, std::forward(args)...); + } + + template + void logWarn(std::format_string fmt, ArgTs&&... args) + { + _loggerSPtr->log(spdlog::level::warn, fmt, std::forward(args)...); + } + + template + void logInfo(std::format_string fmt, ArgTs&&... args) + { + _loggerSPtr->log(spdlog::level::info, fmt, std::forward(args)...); + } + + template + void logDebug(std::format_string fmt, ArgTs&&... args) + { + _loggerSPtr->log(spdlog::level::debug, fmt, std::forward(args)...); + } + + template + void logTrace(std::format_string fmt, ArgTs&&... args) + { + _loggerSPtr->log(spdlog::level::trace, fmt, std::forward(args)...); } protected: - static constexpr size_t LOGGER_DEFAULT_FORMAT_MARK_POS = 21; + static constexpr size_t LOGGER_DEFAULT_FORMAT_MARK_POS = 20; std::string _currentLogPattern; std::shared_ptr _loggerSPtr; diff --git a/common/adc_utils.h b/common/adc_utils.h index b31e9ee..046a214 100644 --- a/common/adc_utils.h +++ b/common/adc_utils.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "../common/adc_traits.h" @@ -453,6 +454,7 @@ namespace constants { static constexpr char DEFAULT_CONVERTER_DELIMITER[] = " "; +static constexpr char DEFAULT_CONVERTER_DELIMITER_COMA[] = ", "; } // namespace constants @@ -676,6 +678,17 @@ static constexpr size_t AdcFNV1aHash(const R& r) return hash; } + +/* current thread ID std::string representation */ + +static std::string AdcThisThreadId() +{ + std::stringstream st; + st << std::this_thread::get_id(); + + return st.str(); +} + /* std::basic_ostream based multithread-safe simple logger */ template > @@ -709,43 +722,37 @@ public: return _currentLogLevel; } - template - void logMessage(loglevel_t level, std::string_view fmt, Ts&&... args) + void logMessage(loglevel_t level, const std::string& msg) { std::lock_guard lock(_logMutex); if (_currentLogLevel < level) return; - std::string s; - std::format_to(std::back_inserter(s), fmt, std::forward(args)...); - const std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); // format log-message in form: // [YYYY-MM-DD HH:MM:SS][level] log-message // - _logStream << std::put_time(std::localtime(&now), "[%F %T]") << "[" << LOGLEVEL_MARK[_currentLogLevel] << "] " - << s; + _logStream << std::put_time(std::localtime(&now), "[%F %T]") << "[" << LOGLEVEL_MARK[level] << "] " << msg + << "\n" + << std::flush; } - template - void logError(std::string_view fmt, Ts&&... args) + void logError(const std::string& msg) { - logMessage(ERROR_LEVEL, fmt, std::forward(args)...); + logMessage(ERROR_LEVEL, msg); } - template - void logInfo(std::string_view fmt, Ts&&... args) + void logInfo(const std::string& msg) { - logMessage(INFO_LEVEL, fmt, std::forward(args)...); + logMessage(INFO_LEVEL, msg); } - template - void logDebug(std::string_view fmt, Ts&&... args) + void logDebug(const std::string& msg) { - logMessage(DEBUG_LEVEL, fmt, std::forward(args)...); + logMessage(DEBUG_LEVEL, msg); } diff --git a/net/adc_device_netserver.h b/net/adc_device_netserver.h index 6788cf8..3fc3da6 100644 --- a/net/adc_device_netserver.h +++ b/net/adc_device_netserver.h @@ -2,6 +2,7 @@ #include #include +#include #include "adc_device_netmsg.h" #include "adc_netserver.h" @@ -105,11 +106,12 @@ namespace adc { -template -class AdcDeviceNetServer : public AdcGenericNetServer +template > +class AdcDeviceNetServer : public AdcGenericNetServer { public: - using typename AdcGenericNetServer::server_ident_t; + using typename AdcGenericNetServer::server_ident_t; + using typename AdcGenericNetServer::logger_t; // type for serialized data (attr/command ID, attr values etc...) typedef std::vector serialized_t; @@ -232,13 +234,15 @@ public: _recvTimeout(ctx.recvTimeout), _sendTimeout(ctx.sendTimeout) { - _serverPtr->logInfo("Create client session with ID = {} (addr = {})", _ident, (void*)this); + _serverPtr->logInfo("Create client session with ID = {} (addr = {}, thread = {})", _ident, (void*)this, + utils::AdcThisThreadId()); } virtual ~Session() { - _serverPtr->logInfo("Delete client session with ID = {} (addr = {})", _ident, (void*)this); + _serverPtr->logInfo("Delete client session with ID = {} (addr = {}, thread = {})", _ident, (void*)this, + utils::AdcThisThreadId()); } netsession_ident_t ident() const @@ -248,18 +252,22 @@ public: void start() { - _serverPtr->logInfo("Start client session with ID = {} (addr = {})", _ident, (void*)this); + static bool first_time = true; + if (first_time) { + _serverPtr->logInfo("Start client session with ID = {} (addr = {}, thread = {})", _ident, (void*)this, + utils::AdcThisThreadId()); + first_time = false; + } + auto self(this->shared_from_this()); _netService.asyncReceive( [self, this](netservice_t::async_callback_err_t ec, message_t msg) { if (ec) { - // std::string str("asyncReceive operation completed with error: "); - // netservice_t::formatError(ec, str); - // _serverPtr->errorMessage(str); - _serverPtr->logError("asyncReceive operation completed with error: {}", - netservice_t::formattableError(ec)); + _serverPtr->logError( + "asyncReceive operation completed with error: {} (session addr = {}, thread = {})", + netservice_t::formattableError(ec), (void*)this, utils::AdcThisThreadId()); stop(); } else { auto msg_sptr = std::make_shared(std::move(msg)); @@ -272,11 +280,9 @@ public: *msg_sptr, [self, msg_sptr, this](netservice_t::async_callback_err_t ec) { if (ec) { - // std::string str("asyncSend operation completed with error: "); - // netservice_t::formatError(ec, str); - // _serverPtr->errorMessage(str); - _serverPtr->logError("asyncSend operation completed with error: {}", - netservice_t::formattableError(ec)); + _serverPtr->logError( + "asyncSend operation completed with error: {} (session addr = {}, thread = {})", + netservice_t::formattableError(ec), (void*)this, utils::AdcThisThreadId()); stop(); } else { start(); @@ -290,7 +296,8 @@ public: void stop() { - _serverPtr->logInfo("Stop client session with ID = {} (addr = {})", _ident, (void*)this); + _serverPtr->logInfo("Stop client session with ID = {} (addr = {}, thread = {})", _ident, (void*)this, + utils::AdcThisThreadId()); _netService.close(); } @@ -391,8 +398,8 @@ public: }; - // using AdcGenericNetServer::AdcGenericNetServer; - AdcDeviceNetServer(const server_ident_t& id) : AdcGenericNetServer(id), _devices() {} + using AdcGenericNetServer::AdcGenericNetServer; + // AdcDeviceNetServer(const server_ident_t& id) : AdcGenericNetServer(id), _devices() {} virtual ~AdcDeviceNetServer() = default; @@ -406,6 +413,18 @@ public: CmdIdDeserialT&& cmd_id_deser_func = {}) // deserializer of command ID { auto id = std::forward(id_ser_func)(dev_ptr->ident()); + + if constexpr (traits::formattable) { + this->logInfo("Add ADC device with ID: {} (addr = {}, thread = {})", id, (void*)dev_ptr, + utils::AdcThisThreadId()); + } else { + std::string s; + std::ranges::copy(id, std::back_inserter(s)); + + this->logInfo("Add ADC device with ID: {} (addr = {}, thread = {})", s, (void*)dev_ptr, + utils::AdcThisThreadId()); + } + _devices.try_emplace(dev_ptr, dev_ptr, id, std::forward(attr_id_deser_func), std::forward(cmd_id_deser_func)); @@ -415,7 +434,25 @@ public: template AdcDeviceNetServer& delDevice(DeviceT* dev_ptr) { - _devices.erase(dev_ptr); + auto it = _devices.find(dev_ptr); + if (it == _devices.end()) { + this->logError("Invalid ADC device pointer ({})! It seems the device was not added!", (void*)dev_ptr); + return *this; + } + + if constexpr (traits::formattable) { + this->logInfo("Delete ADC device with ID: {} (addr = {}, thread = {})", it->ident(), (void*)dev_ptr, + utils::AdcThisThreadId()); + } else { + std::string s; + std::ranges::copy(it->ident(), std::back_inserter(s)); + + this->logInfo("Delete ADC device with ID: {} (addr = {}, thread = {})", s, (void*)dev_ptr, + utils::AdcThisThreadId()); + } + + _devices.erase(it); + // _devices.erase(dev_ptr); return *this; } diff --git a/net/adc_net_concepts.h b/net/adc_net_concepts.h index f5a6eda..1b03353 100644 --- a/net/adc_net_concepts.h +++ b/net/adc_net_concepts.h @@ -118,12 +118,14 @@ concept adc_netservice_c = requires(SRVT srv, const SRVT srv_const) { // acceptor type requires std::is_class_v; requires adc_async_callback_t; - requires requires(typename SRVT::acceptor_t acc) { + requires requires(typename SRVT::acceptor_t acc, const typename SRVT::acceptor_t acc_const) { acc.asyncAccept(std::declval(), std::declval()); // { acc.accept(std::declval()) } -> std::same_as; acc.accept(std::declval()); + + { acc_const.localEndpoint() } -> traits::formattable; }; @@ -160,6 +162,8 @@ concept adc_netservice_c = requires(SRVT srv, const SRVT srv_const) { srv.close(); + { srv_const.remoteEndpoint() } -> traits::formattable; + // // static method // SRVT::formatError(std::declval(), std::declval()); @@ -224,7 +228,10 @@ concept adc_netsession_proto_c = -/* LOGGER */ +/* LOGGER: + * + * + */ template concept adc_logger_c = requires(LOGGERT log, const LOGGERT log_const) { @@ -234,22 +241,15 @@ concept adc_logger_c = requires(LOGGERT log, const LOGGERT log_const) { { log_const.getLogLevel() } -> std::same_as; // logging method signature: - // void method(std::string_view fmt, traits::formattable auto&& args...) + // void method(loglevel_t level, traits::formattable auto&& args...) - log.logMessage(std::declval(), std::declval()); - log.logMessage(std::declval(), std::declval(), - std::declval()); + log.logMessage(std::declval(), std::declval()); - // logging method must accept at least the single argument - formatting string - log.logInfo(std::declval()); - // method must be defined at least for std::string as its argument - log.logInfo(std::declval(), std::declval()); + // specialized logging methods signature: - log.logWarn(std::declval()); - log.logWarn(std::declval(), std::declval()); - - log.logError(std::declval()); - log.logError(std::declval(), std::declval()); + log.logInfo(std::declval()); + log.logDebug(std::declval()); + log.logError(std::declval()); }; diff --git a/net/adc_netserver.h b/net/adc_netserver.h index 4f9884d..f6044de 100644 --- a/net/adc_netserver.h +++ b/net/adc_netserver.h @@ -8,9 +8,9 @@ ABSTRACT DEVICE COMPONENTS LIBRARY #include #include -#include #include #include +#include #include #if __has_include() // POSIX @@ -21,8 +21,10 @@ ABSTRACT DEVICE COMPONENTS LIBRARY #endif +#include "../common/adc_utils.h" #include "adc_net_concepts.h" + namespace adc { @@ -30,87 +32,6 @@ namespace adc /* SOME USEFULL PRIVITIVES */ -// A simple std::ostream logger -class AdcTrivialLogger -{ -public: - virtual ~AdcTrivialLogger() = default; - - // protected: - static constexpr std::string_view errorLevelMark{"error"}; - static constexpr std::string_view warnLevelMark{"warning"}; - static constexpr std::string_view infoLevelMark{"info"}; - - std::basic_ostream& _stream; - - AdcTrivialLogger(std::basic_ostream& stream = std::cout) : _stream(stream) {} - - template - std::string logMsgFormat(std::string_view level_mark, std::string_view fmt, ArgTs&&... args) - { - std::string s; - std::format_to(std::back_inserter(s), fmt, std::forward(args)...); - - std::stringstream st; - - const std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); - - // format log-message in form: - // [YYYY-MM-DD HH:MM:SS][level] log-message - // - st << std::put_time(std::localtime(&now), "[%F %T]") << "[" << level_mark << "] " << s; - - return st.str(); - } - - - // define default logging function of ERROR, WARNING and INFO levels - template - void logError(this auto&& self, std::string_view fmt, ArgTs&&... args) - { - using obj_t = decltype(self); - - if constexpr (std::same_as, AdcTrivialLogger>) { - std::forward(self)._stream - << std::forward(self).logMsgFormat(errorLevelMark, fmt, std::forward(args)...) << "\n" - << std::flush; - } else { - std::forward(self).logError(fmt, std::forward(args)...); - } - } - - template - void logWarn(this auto&& self, std::string_view fmt, ArgTs&&... args) - { - using obj_t = decltype(self); - - if constexpr (std::same_as) { - std::forward(self)._stream - << std::forward(self).logMsgFormat(warnLevelMark, fmt, std::forward(args)...) << "\n" - << std::flush; - } else { - std::forward(self).logWarn(fmt, std::forward(args)...); - } - } - - template - void logInfo(this auto&& self, std::string_view fmt, ArgTs&&... args) - { - using obj_t = decltype(self); - - if constexpr (std::same_as) { - std::forward(self)._stream - << std::forward(self).logMsgFormat(infoLevelMark, fmt, std::forward(args)...) << "\n" - << std::flush; - } else { - std::forward(self).logInfo(fmt, std::forward(args)...); - } - } -}; - - -static_assert(interfaces::adc_logger_c, "!!!!!!!!!!!"); - // A generic implementation of POSIX OS daemon class AdcPosixGenericDaemon { @@ -303,25 +224,39 @@ protected: }; +static_assert(interfaces::adc_logger_c>, "!!!!!"); + + /* very generic network server */ -template -class AdcGenericNetServer : public AdcPosixGenericDaemon, public AdcNetSessionManager, public AdcTrivialLogger +template > +class AdcGenericNetServer : public AdcPosixGenericDaemon, public AdcNetSessionManager, public LoggerT { public: typedef IdentT server_ident_t; + typedef LoggerT logger_t; - AdcGenericNetServer(const server_ident_t& id, std::basic_ostream& log_stream = std::cout) - : AdcTrivialLogger(log_stream), _serverIdent(id) + template + AdcGenericNetServer(const server_ident_t& id, LoggerCtorArgTs&&... ctor_args) + : _serverIdent(id), LoggerT(std::forward(ctor_args)...) { + if constexpr (traits::formattable) { + logInfo("Create ADC generic network server with ID: {} (addr = {}, thread = {})", id, (void*)this, + utils::AdcThisThreadId()); + } else { + logInfo("Create ADC generic network server (addr = {}, thread = {})", (void*)this, + utils::AdcThisThreadId()); + } } AdcGenericNetServer(const AdcGenericNetServer&) = delete; AdcGenericNetServer(AdcGenericNetServer&& other) - : AdcPosixGenericDaemon(std::move(other)), AdcNetSessionManager(std::move(other)) + : AdcPosixGenericDaemon(std::move(other)), AdcNetSessionManager(std::move(other)), LoggerT(std::move(other)) { + logDebug("Move ADC server class: this = {}, target = {}", (void*)this, (void*)&other); + if (this == &other) { return; } @@ -336,11 +271,22 @@ public: _moveCtorFunc = std::move(other._moveCtorFunc); } - virtual ~AdcGenericNetServer() = default; + virtual ~AdcGenericNetServer() + { + if constexpr (traits::formattable) { + logInfo("Delete ADC generic network server with ID: {} (addr = {}, thread = {})", _serverIdent, (void*)this, + utils::AdcThisThreadId()); + } else { + logInfo("Delete ADC generic network server (addr = {}, thread = {})", (void*)this, + utils::AdcThisThreadId()); + } + }; AdcGenericNetServer& operator=(const AdcGenericNetServer&) = delete; AdcGenericNetServer& operator=(AdcGenericNetServer&& other) { + logDebug("Assign-move ADC server class: this = {}, target = {}", (void*)this, (void*)&other); + if (this != &other) { AdcPosixGenericDaemon::operator=(std::move(other)); AdcNetSessionManager::operator=(std::move(other)); @@ -369,6 +315,8 @@ public: void start(SessionT::netsession_ident_t id, SessionT::netsession_ctx_t sess_ctx, AcceptorCtorArgTs&&... ctor_args) requires traits::adc_hashable_c { + logDebug("Call {}", __PRETTY_FUNCTION__); + if (!_isListening[this][id]) { auto acceptor = std::make_shared( std::forward(ctor_args)...); @@ -380,6 +328,16 @@ public: _isListening[inst][id] = false; }); + if constexpr (traits::formattable) { + logInfo( + "Start listening for client connections at <{}> endpoint (session ID: {}, server addr = {}, thread " + "= {})", + acceptor->localEndpoint(), id, (void*)this, utils::AdcThisThreadId()); + } else { + logInfo("Start listening for client connections at <{}> endpoint (server addr = {}, thread = {})", + acceptor->localEndpoint(), (void*)this, utils::AdcThisThreadId()); + } + doAccept(acceptor, std::move(id), std::move(sess_ctx)); } @@ -413,6 +371,30 @@ public: }; + // helper methods for logging + template + void logMessage(LoggerT::loglevel_t level, std::format_string fmt, Ts&&... args) + { + LoggerT::logMessage(level, std::format(fmt, std::forward(args)...)); + } + + template + void logInfo(std::format_string fmt, Ts&&... args) + { + LoggerT::logInfo(std::format(fmt, std::forward(args)...)); + } + + template + void logDebug(std::format_string fmt, Ts&&... args) + { + LoggerT::logDebug(std::format(fmt, std::forward(args)...)); + } + + template + void logError(std::format_string fmt, Ts&&... args) + { + LoggerT::logError(std::format(fmt, std::forward(args)...)); + } protected: // template @@ -433,24 +415,22 @@ protected: acceptor->asyncAccept([acceptor, id = std::move(id), sess_ctx = std::move(sess_ctx), this]( auto ec, typename SessionT::netservice_t srv) mutable { if (!ec) { + logInfo( + "Client connection is succesfully accepted! Client endpoint: {} (server addr = {}, thread = {})", + srv.remoteEndpoint(), (void*)this, utils::AdcThisThreadId()); + auto sess = std::make_shared(id, std::move(srv), sess_ctx); startSession(sess); _isListening[this][id] = true; doAccept(acceptor, std::move(id), std::move(sess_ctx)); } else { - // std::string str{"Cannot start accepting connection: "}; - // SessionT::netservice_t::formatError(ec, str); - // errorMessage(str); - this->logError("Cannot start accepting connection: {}", SessionT::netservice_t::formattableError(ec)); _isListening[this][id] = false; } }); } - - // virtual void errorMessage(const std::string&) {}; }; diff --git a/net/asio/adc_device_netserver_asio.h b/net/asio/adc_device_netserver_asio.h index 9dd4ace..dd9f8fd 100644 --- a/net/asio/adc_device_netserver_asio.h +++ b/net/asio/adc_device_netserver_asio.h @@ -14,20 +14,26 @@ namespace adc::impl { -template -class AdcDeviceNetServerASIO : public AdcDeviceNetServer +template > +class AdcDeviceNetServerASIO : public AdcDeviceNetServer { - typedef AdcDeviceNetServer base_t; + typedef AdcDeviceNetServer base_t; public: + using typename base_t::logger_t; using typename base_t::server_ident_t; + typedef std::string session_ident_t; template using Session = typename base_t::template Session; - AdcDeviceNetServerASIO(const server_ident_t& id, asio::io_context& io_context) - : base_t(id), _ioContext(io_context), _stopSignal(io_context), _restartSignal(io_context) + template + AdcDeviceNetServerASIO(const server_ident_t& id, asio::io_context& io_context, LoggerCtorArgTs&&... ctor_args) + : base_t(id, std::forward(ctor_args)...), + _ioContext(io_context), + _stopSignal(io_context), + _restartSignal(io_context) { } @@ -93,6 +99,26 @@ public: requires(std::convertible_to, int> && std::convertible_to, int>) { + auto sig_list = [](const auto& sig_range) { + std::string sgs; +#ifdef _GNU_SOURCE + std::vector vsg; + + std::ranges::transform(sig_range, std::back_inserter(vsg), + [](auto s) { return std::format("'{}'", sigdescr_np(s)); }); + + utils::AdcJoinRange(vsg, std::string_view(", "), sgs); +#else + sgs = utils::AdcDefaultValueConverter::serialize< + std::string>(sig_range); +#endif + return sgs; + }; + + this->logDebug("Setup 'stop-server' signal to: {}", sig_list(stop_sig_num)); + this->logDebug("Setup 'restart-server' signal to: {}", sig_list(restart_sig_num)); + + for (const int sig : stop_sig_num) { _stopSignal.add(sig); } @@ -119,6 +145,9 @@ public: { _sessionRecvTimeout = recv_timeout; _sessionSendTimeout = send_timeout; + + this->logDebug("Set session timeouts: recv = {} msec, send = {} msec", _sessionRecvTimeout.count(), + _sessionSendTimeout.count()); } protected: @@ -129,25 +158,41 @@ protected: std::chrono::milliseconds _sessionRecvTimeout = std::chrono::hours(12); std::chrono::milliseconds _sessionSendTimeout = std::chrono::seconds(5); + void daemonize() + { + this->logInfo("Daemonize server process (server addr: {})", (void*)this); + + base_t::daemonize(); + } // demonizing ASIO-related methods virtual void daemonizePrepare() { + this->logDebug("ASIO-related call of daemonizePrepare()"); + _ioContext.notify_fork(asio::execution_context::fork_prepare); } virtual void daemonizeFinalize() { + this->logDebug("ASIO-related call of daemonizeFinalize()"); + _ioContext.notify_fork(asio::io_context::fork_child); } - virtual void signalReceived(std::error_code, int signo) + virtual void signalReceived(std::error_code ec, int signo) { - std::cout << "SIGNAL: " << signo << "\n"; +#ifdef _GNU_SOURCE + this->logInfo("The server received the signal: '{}' (ec = {})", sigdescr_np(signo), ec.message()); +#else + this->logInfo("The server received the signal: {} (ec = {})", signo, ec.message()); +#endif }; virtual void restart() { + this->logInfo("Restart server (server addr: {})", (void*)this); + this->stopAllSessions(); _restartSignal.async_wait([this](std::error_code ec, int signo) { diff --git a/net/asio/adc_netservice_asio.h b/net/asio/adc_netservice_asio.h index 22f9e67..36e8454 100644 --- a/net/asio/adc_netservice_asio.h +++ b/net/asio/adc_netservice_asio.h @@ -296,6 +296,15 @@ public: _acceptor.close(ec); } + + std::string localEndpoint() const + { + std::stringstream st; + st << _acceptor.local_endpoint(); + + return st.str(); + } + private: asio::io_context& _ioContext; srv_acceptor_t _acceptor; @@ -758,11 +767,17 @@ public: } - // static void formatError(std::error_code err, std::string& result_str) - // { - // std::format_to(std::back_inserter(result_str), "{} (Err category: {}) (Err msg: {})", err.value(), - // err.category().name(), err.message()); - // } + std::string remoteEndpoint() const + { + std::stringstream st; + st << _socket.remote_endpoint(); + + if (st.str().empty()) { + return ""; + } + + return st.str(); + } static std::string formattableError(std::error_code ec) { diff --git a/tests/adc_asio_netserver_test.cpp b/tests/adc_asio_netserver_test.cpp index f6919eb..1d44a14 100644 --- a/tests/adc_asio_netserver_test.cpp +++ b/tests/adc_asio_netserver_test.cpp @@ -8,6 +8,11 @@ #include "../net/adc_netproto.h" #include "../net/asio/adc_device_netserver_asio.h" +#ifdef USE_SPDLOG_LIBRARY +#include +#include "../common/adc_spdlog.h" +#endif + typedef adc::impl::AdcDeviceNetServerASIO server_t; typedef adc::AdcDeviceAttribute attr_t; @@ -132,7 +137,17 @@ int main(int argc, char* argv[]) asio::signal_set signals(io_ctx, SIGINT, SIGTERM); signals.async_wait([&](std::error_code, int) { io_ctx.stop(); }); - adc::impl::AdcDeviceNetServerASIO server("TEST SRV", io_ctx); + using server_t = adc::impl::AdcDeviceNetServerASIO; + std::shared_ptr logger = spdlog::stdout_color_mt("console"); + logger->set_level(spdlog::level::debug); + // server_t server("TEST SRV", io_ctx, logger, "[%Y-%m-%d %T.%e][%l]: %v"); + server_t server("TEST SRV", io_ctx, logger); + + // using server_t = adc::impl::AdcDeviceNetServerASIO<>; + // server_t server("TEST SRV", io_ctx); + // server.setLogLevel(server_t::logger_t::DEBUG_LEVEL); + + server.setupSignals(); server.addDevice(&dev1); @@ -154,11 +169,11 @@ int main(int argc, char* argv[]) } } - std::cout << "try to start listenning at '" << ep << "' ..."; + // std::cout << "try to start listenning at '" << ep << "' ..."; server.start>(epn); - std::cout << "\tOK\n"; + // std::cout << "\tOK\n"; } else { std::cerr << "Unrecognized endpoint: '" << ep << "'! Ignore!\n"; }