Back to C++20 standard!

Logging is worked (AdcOstreamLogger and AdcSPDLOGLogger classes)
This commit is contained in:
Timur A. Fatkhullin 2024-11-14 18:33:07 +03:00
parent 05e0055193
commit 78a9e53d18
9 changed files with 334 additions and 196 deletions

View File

@ -4,7 +4,7 @@ project(ADC LANGUAGES CXX)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}") 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_CXX_STANDARD_REQUIRED ON)
# set(CMAKE_BUILD_TYPE Release) # set(CMAKE_BUILD_TYPE Release)
@ -12,21 +12,21 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
# check compiler version to ensure supporting of # check compiler version to ensure supporting of
# 'deducing this' C++23 feature # 'deducing this' C++23 feature
# #
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14.0) # if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 14.0)
message(FATAL_ERROR "GCC version must be at least 14.0!") # message(FATAL_ERROR "GCC version must be at least 14.0!")
endif() # endif()
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18.0) # if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 18.0)
message(FATAL_ERROR "Clang version must be at least 18.0!") # message(FATAL_ERROR "Clang version must be at least 18.0!")
endif() # endif()
elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") # elseif(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.32") # if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.32")
message(FATAL_ERROR "MSVC version must be at least 19.32") # message(FATAL_ERROR "MSVC version must be at least 19.32")
endif() # endif()
else() # else()
message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang and GCC.") # message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang and GCC.")
endif() # endif()
@ -85,6 +85,7 @@ option(SPDLOG_LIBRARY "Use of SPDLOG library for logging" ON)
if (SPDLOG_LIBRARY) if (SPDLOG_LIBRARY)
find_package(spdlog REQUIRED) find_package(spdlog REQUIRED)
find_package(fmt REQUIRED)
set(ADC_COMMON_HEADERS ${ADC_COMMON_HEADERS} set(ADC_COMMON_HEADERS ${ADC_COMMON_HEADERS}
common/adc_spdlog.h common/adc_spdlog.h
@ -199,6 +200,7 @@ if (BUILD_TESTS)
add_test(VALUE_HOLDER ${DEVATTR_TEST_APP}) add_test(VALUE_HOLDER ${DEVATTR_TEST_APP})
add_test(NETMSG_TEST ${NETMSG_TEST_APP}) add_test(NETMSG_TEST ${NETMSG_TEST_APP})
add_test(ASIO_NETSRV_TEST ${ASIO_NETSERVER_TEST_APP}) add_test(ASIO_NETSRV_TEST ${ASIO_NETSERVER_TEST_APP})
target_link_libraries(${ASIO_NETSERVER_TEST_APP} PRIVATE fmt::fmt)
enable_testing() enable_testing()
endif(BUILD_TESTS) endif(BUILD_TESTS)

View File

@ -24,10 +24,11 @@ public:
typedef spdlog::level::level_enum loglevel_t; typedef spdlog::level::level_enum loglevel_t;
AdcSPDLOGLogger(std::shared_ptr<spdlog::logger> logger, template <traits::adc_input_char_range R = decltype(LOGGER_DEFAULT_FORMAT)>
const traits::adc_input_char_range auto& pattern = LOGGER_DEFAULT_FORMAT) AdcSPDLOGLogger(std::shared_ptr<spdlog::logger> logger, const R& pattern = LOGGER_DEFAULT_FORMAT)
: _loggerSPtr(logger), _currentLogPattern(pattern) : _loggerSPtr(logger), _currentLogPattern()
{ {
std::ranges::copy(pattern, std::back_inserter(_currentLogPattern));
_loggerSPtr->set_pattern(_currentLogPattern); _loggerSPtr->set_pattern(_currentLogPattern);
} }
@ -45,45 +46,81 @@ public:
return _loggerSPtr->level(); 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<decltype(args)>(args)...); _loggerSPtr->log(level, msg);
} }
// specialized for given level methods // 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<decltype(args)>(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<decltype(args)>(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<decltype(args)>(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<decltype(args)>(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<decltype(args)>(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<decltype(args)>(args)...); logMessage(spdlog::level::trace, msg);
}
template <traits::formattable... ArgTs>
void logCritical(std::format_string<ArgTs...> fmt, ArgTs&&... args)
{
_loggerSPtr->log(spdlog::level::critical, fmt, std::forward<ArgTs>(args)...);
}
template <traits::formattable... ArgTs>
void logError(std::format_string<ArgTs...> fmt, ArgTs&&... args)
{
_loggerSPtr->log(spdlog::level::err, fmt, std::forward<ArgTs>(args)...);
}
template <traits::formattable... ArgTs>
void logWarn(std::format_string<ArgTs...> fmt, ArgTs&&... args)
{
_loggerSPtr->log(spdlog::level::warn, fmt, std::forward<ArgTs>(args)...);
}
template <traits::formattable... ArgTs>
void logInfo(std::format_string<ArgTs...> fmt, ArgTs&&... args)
{
_loggerSPtr->log(spdlog::level::info, fmt, std::forward<ArgTs>(args)...);
}
template <traits::formattable... ArgTs>
void logDebug(std::format_string<ArgTs...> fmt, ArgTs&&... args)
{
_loggerSPtr->log(spdlog::level::debug, fmt, std::forward<ArgTs>(args)...);
}
template <traits::formattable... ArgTs>
void logTrace(std::format_string<ArgTs...> fmt, ArgTs&&... args)
{
_loggerSPtr->log(spdlog::level::trace, fmt, std::forward<ArgTs>(args)...);
} }
protected: 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::string _currentLogPattern;
std::shared_ptr<spdlog::logger> _loggerSPtr; std::shared_ptr<spdlog::logger> _loggerSPtr;

View File

@ -7,6 +7,7 @@
#include <mutex> #include <mutex>
#include <ranges> #include <ranges>
#include <regex> #include <regex>
#include <thread>
#include <utility> #include <utility>
#include "../common/adc_traits.h" #include "../common/adc_traits.h"
@ -453,6 +454,7 @@ namespace constants
{ {
static constexpr char DEFAULT_CONVERTER_DELIMITER[] = " "; static constexpr char DEFAULT_CONVERTER_DELIMITER[] = " ";
static constexpr char DEFAULT_CONVERTER_DELIMITER_COMA[] = ", ";
} // namespace constants } // namespace constants
@ -676,6 +678,17 @@ static constexpr size_t AdcFNV1aHash(const R& r)
return hash; 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 */ /* std::basic_ostream based multithread-safe simple logger */
template <typename CharT = char, typename CharTraitsT = std::char_traits<CharT>> template <typename CharT = char, typename CharTraitsT = std::char_traits<CharT>>
@ -709,43 +722,37 @@ public:
return _currentLogLevel; return _currentLogLevel;
} }
template <traits::formattable... Ts> void logMessage(loglevel_t level, const std::string& msg)
void logMessage(loglevel_t level, std::string_view fmt, Ts&&... args)
{ {
std::lock_guard<std::mutex> lock(_logMutex); std::lock_guard<std::mutex> lock(_logMutex);
if (_currentLogLevel < level) if (_currentLogLevel < level)
return; return;
std::string s;
std::format_to(std::back_inserter(s), fmt, std::forward<Ts>(args)...);
const std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); const std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
// format log-message in form: // format log-message in form:
// [YYYY-MM-DD HH:MM:SS][level] log-message // [YYYY-MM-DD HH:MM:SS][level] log-message
// //
_logStream << std::put_time(std::localtime(&now), "[%F %T]") << "[" << LOGLEVEL_MARK[_currentLogLevel] << "] " _logStream << std::put_time(std::localtime(&now), "[%F %T]") << "[" << LOGLEVEL_MARK[level] << "] " << msg
<< s; << "\n"
<< std::flush;
} }
template <traits::formattable... Ts> void logError(const std::string& msg)
void logError(std::string_view fmt, Ts&&... args)
{ {
logMessage(ERROR_LEVEL, fmt, std::forward<Ts>(args)...); logMessage(ERROR_LEVEL, msg);
} }
template <traits::formattable... Ts> void logInfo(const std::string& msg)
void logInfo(std::string_view fmt, Ts&&... args)
{ {
logMessage(INFO_LEVEL, fmt, std::forward<Ts>(args)...); logMessage(INFO_LEVEL, msg);
} }
template <traits::formattable... Ts> void logDebug(const std::string& msg)
void logDebug(std::string_view fmt, Ts&&... args)
{ {
logMessage(DEBUG_LEVEL, fmt, std::forward<Ts>(args)...); logMessage(DEBUG_LEVEL, msg);
} }

View File

@ -2,6 +2,7 @@
#include <memory> #include <memory>
#include <system_error> #include <system_error>
#include <thread>
#include "adc_device_netmsg.h" #include "adc_device_netmsg.h"
#include "adc_netserver.h" #include "adc_netserver.h"
@ -105,11 +106,12 @@ namespace adc
{ {
template <typename IdentT = std::string> template <typename IdentT = std::string, interfaces::adc_logger_c LoggerT = utils::AdcOstreamLogger<char>>
class AdcDeviceNetServer : public AdcGenericNetServer<IdentT> class AdcDeviceNetServer : public AdcGenericNetServer<IdentT, LoggerT>
{ {
public: public:
using typename AdcGenericNetServer<IdentT>::server_ident_t; using typename AdcGenericNetServer<IdentT, LoggerT>::server_ident_t;
using typename AdcGenericNetServer<IdentT, LoggerT>::logger_t;
// type for serialized data (attr/command ID, attr values etc...) // type for serialized data (attr/command ID, attr values etc...)
typedef std::vector<char> serialized_t; typedef std::vector<char> serialized_t;
@ -232,13 +234,15 @@ public:
_recvTimeout(ctx.recvTimeout), _recvTimeout(ctx.recvTimeout),
_sendTimeout(ctx.sendTimeout) _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() 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 netsession_ident_t ident() const
@ -248,18 +252,22 @@ public:
void start() 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()); auto self(this->shared_from_this());
_netService.asyncReceive( _netService.asyncReceive(
[self, this](netservice_t::async_callback_err_t ec, message_t msg) { [self, this](netservice_t::async_callback_err_t ec, message_t msg) {
if (ec) { if (ec) {
// std::string str("asyncReceive operation completed with error: "); _serverPtr->logError(
// netservice_t::formatError(ec, str); "asyncReceive operation completed with error: {} (session addr = {}, thread = {})",
// _serverPtr->errorMessage(str); netservice_t::formattableError(ec), (void*)this, utils::AdcThisThreadId());
_serverPtr->logError("asyncReceive operation completed with error: {}",
netservice_t::formattableError(ec));
stop(); stop();
} else { } else {
auto msg_sptr = std::make_shared<message_t>(std::move(msg)); auto msg_sptr = std::make_shared<message_t>(std::move(msg));
@ -272,11 +280,9 @@ public:
*msg_sptr, *msg_sptr,
[self, msg_sptr, this](netservice_t::async_callback_err_t ec) { [self, msg_sptr, this](netservice_t::async_callback_err_t ec) {
if (ec) { if (ec) {
// std::string str("asyncSend operation completed with error: "); _serverPtr->logError(
// netservice_t::formatError(ec, str); "asyncSend operation completed with error: {} (session addr = {}, thread = {})",
// _serverPtr->errorMessage(str); netservice_t::formattableError(ec), (void*)this, utils::AdcThisThreadId());
_serverPtr->logError("asyncSend operation completed with error: {}",
netservice_t::formattableError(ec));
stop(); stop();
} else { } else {
start(); start();
@ -290,7 +296,8 @@ public:
void stop() 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(); _netService.close();
} }
@ -391,8 +398,8 @@ public:
}; };
// using AdcGenericNetServer::AdcGenericNetServer; using AdcGenericNetServer<IdentT, LoggerT>::AdcGenericNetServer;
AdcDeviceNetServer(const server_ident_t& id) : AdcGenericNetServer<IdentT>(id), _devices() {} // AdcDeviceNetServer(const server_ident_t& id) : AdcGenericNetServer<IdentT>(id), _devices() {}
virtual ~AdcDeviceNetServer() = default; virtual ~AdcDeviceNetServer() = default;
@ -406,6 +413,18 @@ public:
CmdIdDeserialT&& cmd_id_deser_func = {}) // deserializer of command ID CmdIdDeserialT&& cmd_id_deser_func = {}) // deserializer of command ID
{ {
auto id = std::forward<IdSerialT>(id_ser_func)(dev_ptr->ident()); auto id = std::forward<IdSerialT>(id_ser_func)(dev_ptr->ident());
if constexpr (traits::formattable<serialized_t>) {
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<AttrIdDeserialT>(attr_id_deser_func), _devices.try_emplace(dev_ptr, dev_ptr, id, std::forward<AttrIdDeserialT>(attr_id_deser_func),
std::forward<CmdIdDeserialT>(cmd_id_deser_func)); std::forward<CmdIdDeserialT>(cmd_id_deser_func));
@ -415,7 +434,25 @@ public:
template <interfaces::adc_device_c DeviceT> template <interfaces::adc_device_c DeviceT>
AdcDeviceNetServer& delDevice(DeviceT* dev_ptr) 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<serialized_t>) {
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; return *this;
} }

View File

@ -118,12 +118,14 @@ concept adc_netservice_c = requires(SRVT srv, const SRVT srv_const) {
// acceptor type // acceptor type
requires std::is_class_v<typename SRVT::acceptor_t>; requires std::is_class_v<typename SRVT::acceptor_t>;
requires adc_async_callback_t<typename SRVT::acceptor_t::async_accept_callback_t>; requires adc_async_callback_t<typename SRVT::acceptor_t::async_accept_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<typename SRVT::acceptor_t::async_accept_callback_t>(), acc.asyncAccept(std::declval<typename SRVT::acceptor_t::async_accept_callback_t>(),
std::declval<const typename SRVT::timeout_t&>()); std::declval<const typename SRVT::timeout_t&>());
// { acc.accept(std::declval<const typename SRVT::timeout_t&>()) } -> std::same_as<SRVT>; // { acc.accept(std::declval<const typename SRVT::timeout_t&>()) } -> std::same_as<SRVT>;
acc.accept(std::declval<const typename SRVT::timeout_t&>()); acc.accept(std::declval<const typename SRVT::timeout_t&>());
{ acc_const.localEndpoint() } -> traits::formattable;
}; };
@ -160,6 +162,8 @@ concept adc_netservice_c = requires(SRVT srv, const SRVT srv_const) {
srv.close(); srv.close();
{ srv_const.remoteEndpoint() } -> traits::formattable;
// // static method // // static method
// SRVT::formatError(std::declval<typename SRVT::async_callback_err_t>(), std::declval<std::string&>()); // SRVT::formatError(std::declval<typename SRVT::async_callback_err_t>(), std::declval<std::string&>());
@ -224,7 +228,10 @@ concept adc_netsession_proto_c =
/* LOGGER */ /* LOGGER:
*
*
*/
template <typename LOGGERT> template <typename LOGGERT>
concept adc_logger_c = requires(LOGGERT log, const LOGGERT log_const) { 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<typename LOGGERT::loglevel_t>; { log_const.getLogLevel() } -> std::same_as<typename LOGGERT::loglevel_t>;
// logging method signature: // 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<typename LOGGERT::loglevel_t>(), std::declval<std::string_view>()); log.logMessage(std::declval<typename LOGGERT::loglevel_t>(), std::declval<std::string>());
log.logMessage(std::declval<typename LOGGERT::loglevel_t>(), std::declval<std::string_view>(),
std::declval<std::string>());
// logging method must accept at least the single argument - formatting string // specialized logging methods signature:
log.logInfo(std::declval<std::string_view>());
// method must be defined at least for std::string as its argument
log.logInfo(std::declval<std::string_view>(), std::declval<std::string>());
log.logWarn(std::declval<std::string_view>()); log.logInfo(std::declval<std::string>());
log.logWarn(std::declval<std::string_view>(), std::declval<std::string>()); log.logDebug(std::declval<std::string>());
log.logError(std::declval<std::string>());
log.logError(std::declval<std::string_view>());
log.logError(std::declval<std::string_view>(), std::declval<std::string>());
}; };

View File

@ -8,9 +8,9 @@ ABSTRACT DEVICE COMPONENTS LIBRARY
#include <filesystem> #include <filesystem>
#include <functional> #include <functional>
#include <iostream>
#include <list> #include <list>
#include <set> #include <set>
#include <thread>
#include <unordered_map> #include <unordered_map>
#if __has_include(<unistd.h>) // POSIX #if __has_include(<unistd.h>) // POSIX
@ -21,8 +21,10 @@ ABSTRACT DEVICE COMPONENTS LIBRARY
#endif #endif
#include "../common/adc_utils.h"
#include "adc_net_concepts.h" #include "adc_net_concepts.h"
namespace adc namespace adc
{ {
@ -30,87 +32,6 @@ namespace adc
/* SOME USEFULL PRIVITIVES */ /* 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<char>& _stream;
AdcTrivialLogger(std::basic_ostream<char>& stream = std::cout) : _stream(stream) {}
template <traits::formattable... ArgTs>
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<ArgTs>(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 <traits::formattable... ArgTs>
void logError(this auto&& self, std::string_view fmt, ArgTs&&... args)
{
using obj_t = decltype(self);
if constexpr (std::same_as<std::remove_cvref_t<obj_t>, AdcTrivialLogger>) {
std::forward<obj_t>(self)._stream
<< std::forward<obj_t>(self).logMsgFormat(errorLevelMark, fmt, std::forward<ArgTs>(args)...) << "\n"
<< std::flush;
} else {
std::forward<obj_t>(self).logError(fmt, std::forward<ArgTs>(args)...);
}
}
template <traits::formattable... ArgTs>
void logWarn(this auto&& self, std::string_view fmt, ArgTs&&... args)
{
using obj_t = decltype(self);
if constexpr (std::same_as<obj_t, AdcTrivialLogger>) {
std::forward<obj_t>(self)._stream
<< std::forward<obj_t>(self).logMsgFormat(warnLevelMark, fmt, std::forward<ArgTs>(args)...) << "\n"
<< std::flush;
} else {
std::forward<obj_t>(self).logWarn(fmt, std::forward<ArgTs>(args)...);
}
}
template <traits::formattable... ArgTs>
void logInfo(this auto&& self, std::string_view fmt, ArgTs&&... args)
{
using obj_t = decltype(self);
if constexpr (std::same_as<obj_t, AdcTrivialLogger>) {
std::forward<obj_t>(self)._stream
<< std::forward<obj_t>(self).logMsgFormat(infoLevelMark, fmt, std::forward<ArgTs>(args)...) << "\n"
<< std::flush;
} else {
std::forward<obj_t>(self).logInfo(fmt, std::forward<ArgTs>(args)...);
}
}
};
static_assert(interfaces::adc_logger_c<AdcTrivialLogger>, "!!!!!!!!!!!");
// A generic implementation of POSIX OS daemon // A generic implementation of POSIX OS daemon
class AdcPosixGenericDaemon class AdcPosixGenericDaemon
{ {
@ -303,25 +224,39 @@ protected:
}; };
static_assert(interfaces::adc_logger_c<utils::AdcOstreamLogger<>>, "!!!!!");
/* very generic network server */ /* very generic network server */
template <typename IdentT = std::string> template <typename IdentT = std::string, interfaces::adc_logger_c LoggerT = utils::AdcOstreamLogger<char>>
class AdcGenericNetServer : public AdcPosixGenericDaemon, public AdcNetSessionManager, public AdcTrivialLogger class AdcGenericNetServer : public AdcPosixGenericDaemon, public AdcNetSessionManager, public LoggerT
{ {
public: public:
typedef IdentT server_ident_t; typedef IdentT server_ident_t;
typedef LoggerT logger_t;
AdcGenericNetServer(const server_ident_t& id, std::basic_ostream<char>& log_stream = std::cout) template <typename... LoggerCtorArgTs>
: AdcTrivialLogger(log_stream), _serverIdent(id) AdcGenericNetServer(const server_ident_t& id, LoggerCtorArgTs&&... ctor_args)
: _serverIdent(id), LoggerT(std::forward<LoggerCtorArgTs>(ctor_args)...)
{ {
if constexpr (traits::formattable<IdentT>) {
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(const AdcGenericNetServer&) = delete;
AdcGenericNetServer(AdcGenericNetServer&& other) 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) { if (this == &other) {
return; return;
} }
@ -336,11 +271,22 @@ public:
_moveCtorFunc = std::move(other._moveCtorFunc); _moveCtorFunc = std::move(other._moveCtorFunc);
} }
virtual ~AdcGenericNetServer() = default; virtual ~AdcGenericNetServer()
{
if constexpr (traits::formattable<IdentT>) {
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=(const AdcGenericNetServer&) = delete;
AdcGenericNetServer& operator=(AdcGenericNetServer&& other) AdcGenericNetServer& operator=(AdcGenericNetServer&& other)
{ {
logDebug("Assign-move ADC server class: this = {}, target = {}", (void*)this, (void*)&other);
if (this != &other) { if (this != &other) {
AdcPosixGenericDaemon::operator=(std::move(other)); AdcPosixGenericDaemon::operator=(std::move(other));
AdcNetSessionManager::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) void start(SessionT::netsession_ident_t id, SessionT::netsession_ctx_t sess_ctx, AcceptorCtorArgTs&&... ctor_args)
requires traits::adc_hashable_c<typename SessionT::netsession_ident_t> requires traits::adc_hashable_c<typename SessionT::netsession_ident_t>
{ {
logDebug("Call {}", __PRETTY_FUNCTION__);
if (!_isListening<SessionT>[this][id]) { if (!_isListening<SessionT>[this][id]) {
auto acceptor = std::make_shared<typename SessionT::netservice_t::acceptor_t>( auto acceptor = std::make_shared<typename SessionT::netservice_t::acceptor_t>(
std::forward<AcceptorCtorArgTs>(ctor_args)...); std::forward<AcceptorCtorArgTs>(ctor_args)...);
@ -380,6 +328,16 @@ public:
_isListening<SessionT>[inst][id] = false; _isListening<SessionT>[inst][id] = false;
}); });
if constexpr (traits::formattable<typename SessionT::netsession_ident_t>) {
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<SessionT>(acceptor, std::move(id), std::move(sess_ctx)); doAccept<SessionT>(acceptor, std::move(id), std::move(sess_ctx));
} }
@ -413,6 +371,30 @@ public:
}; };
// helper methods for logging
template <traits::formattable... Ts>
void logMessage(LoggerT::loglevel_t level, std::format_string<Ts...> fmt, Ts&&... args)
{
LoggerT::logMessage(level, std::format(fmt, std::forward<Ts>(args)...));
}
template <traits::formattable... Ts>
void logInfo(std::format_string<Ts...> fmt, Ts&&... args)
{
LoggerT::logInfo(std::format(fmt, std::forward<Ts>(args)...));
}
template <traits::formattable... Ts>
void logDebug(std::format_string<Ts...> fmt, Ts&&... args)
{
LoggerT::logDebug(std::format(fmt, std::forward<Ts>(args)...));
}
template <traits::formattable... Ts>
void logError(std::format_string<Ts...> fmt, Ts&&... args)
{
LoggerT::logError(std::format(fmt, std::forward<Ts>(args)...));
}
protected: protected:
// template <interfaces::adc_netsession_c SessionT> // template <interfaces::adc_netsession_c SessionT>
@ -433,24 +415,22 @@ protected:
acceptor->asyncAccept([acceptor, id = std::move(id), sess_ctx = std::move(sess_ctx), this]( acceptor->asyncAccept([acceptor, id = std::move(id), sess_ctx = std::move(sess_ctx), this](
auto ec, typename SessionT::netservice_t srv) mutable { auto ec, typename SessionT::netservice_t srv) mutable {
if (!ec) { if (!ec) {
logInfo(
"Client connection is succesfully accepted! Client endpoint: {} (server addr = {}, thread = {})",
srv.remoteEndpoint(), (void*)this, utils::AdcThisThreadId());
auto sess = std::make_shared<SessionT>(id, std::move(srv), sess_ctx); auto sess = std::make_shared<SessionT>(id, std::move(srv), sess_ctx);
startSession(sess); startSession(sess);
_isListening<SessionT>[this][id] = true; _isListening<SessionT>[this][id] = true;
doAccept<SessionT>(acceptor, std::move(id), std::move(sess_ctx)); doAccept<SessionT>(acceptor, std::move(id), std::move(sess_ctx));
} else { } 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)); this->logError("Cannot start accepting connection: {}", SessionT::netservice_t::formattableError(ec));
_isListening<SessionT>[this][id] = false; _isListening<SessionT>[this][id] = false;
} }
}); });
} }
// virtual void errorMessage(const std::string&) {};
}; };

View File

@ -14,20 +14,26 @@
namespace adc::impl namespace adc::impl
{ {
template <typename IdentT = std::string> template <typename IdentT = std::string, interfaces::adc_logger_c LoggerT = utils::AdcOstreamLogger<char>>
class AdcDeviceNetServerASIO : public AdcDeviceNetServer<IdentT> class AdcDeviceNetServerASIO : public AdcDeviceNetServer<IdentT, LoggerT>
{ {
typedef AdcDeviceNetServer<IdentT> base_t; typedef AdcDeviceNetServer<IdentT, LoggerT> base_t;
public: public:
using typename base_t::logger_t;
using typename base_t::server_ident_t; using typename base_t::server_ident_t;
typedef std::string session_ident_t; typedef std::string session_ident_t;
template <typename ServiceT> template <typename ServiceT>
using Session = typename base_t::template Session<ServiceT, session_ident_t>; using Session = typename base_t::template Session<ServiceT, session_ident_t>;
AdcDeviceNetServerASIO(const server_ident_t& id, asio::io_context& io_context) template <typename... LoggerCtorArgTs>
: base_t(id), _ioContext(io_context), _stopSignal(io_context), _restartSignal(io_context) AdcDeviceNetServerASIO(const server_ident_t& id, asio::io_context& io_context, LoggerCtorArgTs&&... ctor_args)
: base_t(id, std::forward<LoggerCtorArgTs>(ctor_args)...),
_ioContext(io_context),
_stopSignal(io_context),
_restartSignal(io_context)
{ {
} }
@ -93,6 +99,26 @@ public:
requires(std::convertible_to<std::ranges::range_value_t<RST>, int> && requires(std::convertible_to<std::ranges::range_value_t<RST>, int> &&
std::convertible_to<std::ranges::range_value_t<RRT>, int>) std::convertible_to<std::ranges::range_value_t<RRT>, int>)
{ {
auto sig_list = [](const auto& sig_range) {
std::string sgs;
#ifdef _GNU_SOURCE
std::vector<std::string> 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<utils::constants::DEFAULT_CONVERTER_DELIMITER_COMA>::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) { for (const int sig : stop_sig_num) {
_stopSignal.add(sig); _stopSignal.add(sig);
} }
@ -119,6 +145,9 @@ public:
{ {
_sessionRecvTimeout = recv_timeout; _sessionRecvTimeout = recv_timeout;
_sessionSendTimeout = send_timeout; _sessionSendTimeout = send_timeout;
this->logDebug("Set session timeouts: recv = {} msec, send = {} msec", _sessionRecvTimeout.count(),
_sessionSendTimeout.count());
} }
protected: protected:
@ -129,25 +158,41 @@ protected:
std::chrono::milliseconds _sessionRecvTimeout = std::chrono::hours(12); std::chrono::milliseconds _sessionRecvTimeout = std::chrono::hours(12);
std::chrono::milliseconds _sessionSendTimeout = std::chrono::seconds(5); 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 // demonizing ASIO-related methods
virtual void daemonizePrepare() virtual void daemonizePrepare()
{ {
this->logDebug("ASIO-related call of daemonizePrepare()");
_ioContext.notify_fork(asio::execution_context::fork_prepare); _ioContext.notify_fork(asio::execution_context::fork_prepare);
} }
virtual void daemonizeFinalize() virtual void daemonizeFinalize()
{ {
this->logDebug("ASIO-related call of daemonizeFinalize()");
_ioContext.notify_fork(asio::io_context::fork_child); _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() virtual void restart()
{ {
this->logInfo("Restart server (server addr: {})", (void*)this);
this->stopAllSessions(); this->stopAllSessions();
_restartSignal.async_wait([this](std::error_code ec, int signo) { _restartSignal.async_wait([this](std::error_code ec, int signo) {

View File

@ -296,6 +296,15 @@ public:
_acceptor.close(ec); _acceptor.close(ec);
} }
std::string localEndpoint() const
{
std::stringstream st;
st << _acceptor.local_endpoint();
return st.str();
}
private: private:
asio::io_context& _ioContext; asio::io_context& _ioContext;
srv_acceptor_t _acceptor; srv_acceptor_t _acceptor;
@ -758,11 +767,17 @@ public:
} }
// static void formatError(std::error_code err, std::string& result_str) std::string remoteEndpoint() const
// { {
// std::format_to(std::back_inserter(result_str), "{} (Err category: {}) (Err msg: {})", err.value(), std::stringstream st;
// err.category().name(), err.message()); st << _socket.remote_endpoint();
// }
if (st.str().empty()) {
return "<local>";
}
return st.str();
}
static std::string formattableError(std::error_code ec) static std::string formattableError(std::error_code ec)
{ {

View File

@ -8,6 +8,11 @@
#include "../net/adc_netproto.h" #include "../net/adc_netproto.h"
#include "../net/asio/adc_device_netserver_asio.h" #include "../net/asio/adc_device_netserver_asio.h"
#ifdef USE_SPDLOG_LIBRARY
#include <spdlog/sinks/stdout_color_sinks.h>
#include "../common/adc_spdlog.h"
#endif
typedef adc::impl::AdcDeviceNetServerASIO<std::string_view> server_t; typedef adc::impl::AdcDeviceNetServerASIO<std::string_view> server_t;
typedef adc::AdcDeviceAttribute<std::string, server_t::serialized_t> attr_t; typedef adc::AdcDeviceAttribute<std::string, server_t::serialized_t> attr_t;
@ -132,7 +137,17 @@ int main(int argc, char* argv[])
asio::signal_set signals(io_ctx, SIGINT, SIGTERM); asio::signal_set signals(io_ctx, SIGINT, SIGTERM);
signals.async_wait([&](std::error_code, int) { io_ctx.stop(); }); 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::string, adc::AdcSPDLOGLogger>;
std::shared_ptr<spdlog::logger> 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.setupSignals();
server.addDevice(&dev1); 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<adc::AdcStopSeqSessionProto<>>(epn); server.start<adc::AdcStopSeqSessionProto<>>(epn);
std::cout << "\tOK\n"; // std::cout << "\tOK\n";
} else { } else {
std::cerr << "Unrecognized endpoint: '" << ep << "'! Ignore!\n"; std::cerr << "Unrecognized endpoint: '" << ep << "'! Ignore!\n";
} }