Back to C++20 standard!
Logging is worked (AdcOstreamLogger and AdcSPDLOGLogger classes)
This commit is contained in:
parent
05e0055193
commit
78a9e53d18
@ -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)
|
||||
|
||||
|
||||
@ -24,10 +24,11 @@ public:
|
||||
|
||||
typedef spdlog::level::level_enum loglevel_t;
|
||||
|
||||
AdcSPDLOGLogger(std::shared_ptr<spdlog::logger> logger,
|
||||
const traits::adc_input_char_range auto& pattern = LOGGER_DEFAULT_FORMAT)
|
||||
: _loggerSPtr(logger), _currentLogPattern(pattern)
|
||||
template <traits::adc_input_char_range R = decltype(LOGGER_DEFAULT_FORMAT)>
|
||||
AdcSPDLOGLogger(std::shared_ptr<spdlog::logger> 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<decltype(args)>(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<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:
|
||||
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<spdlog::logger> _loggerSPtr;
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
#include <mutex>
|
||||
#include <ranges>
|
||||
#include <regex>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#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 <typename CharT = char, typename CharTraitsT = std::char_traits<CharT>>
|
||||
@ -709,43 +722,37 @@ public:
|
||||
return _currentLogLevel;
|
||||
}
|
||||
|
||||
template <traits::formattable... Ts>
|
||||
void logMessage(loglevel_t level, std::string_view fmt, Ts&&... args)
|
||||
void logMessage(loglevel_t level, const std::string& msg)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_logMutex);
|
||||
|
||||
if (_currentLogLevel < level)
|
||||
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());
|
||||
|
||||
// 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 <traits::formattable... Ts>
|
||||
void logError(std::string_view fmt, Ts&&... args)
|
||||
void logError(const std::string& msg)
|
||||
{
|
||||
logMessage(ERROR_LEVEL, fmt, std::forward<Ts>(args)...);
|
||||
logMessage(ERROR_LEVEL, msg);
|
||||
}
|
||||
|
||||
template <traits::formattable... Ts>
|
||||
void logInfo(std::string_view fmt, Ts&&... args)
|
||||
void logInfo(const std::string& msg)
|
||||
{
|
||||
logMessage(INFO_LEVEL, fmt, std::forward<Ts>(args)...);
|
||||
logMessage(INFO_LEVEL, msg);
|
||||
}
|
||||
|
||||
template <traits::formattable... Ts>
|
||||
void logDebug(std::string_view fmt, Ts&&... args)
|
||||
void logDebug(const std::string& msg)
|
||||
{
|
||||
logMessage(DEBUG_LEVEL, fmt, std::forward<Ts>(args)...);
|
||||
logMessage(DEBUG_LEVEL, msg);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <system_error>
|
||||
#include <thread>
|
||||
|
||||
#include "adc_device_netmsg.h"
|
||||
#include "adc_netserver.h"
|
||||
@ -105,11 +106,12 @@ namespace adc
|
||||
{
|
||||
|
||||
|
||||
template <typename IdentT = std::string>
|
||||
class AdcDeviceNetServer : public AdcGenericNetServer<IdentT>
|
||||
template <typename IdentT = std::string, interfaces::adc_logger_c LoggerT = utils::AdcOstreamLogger<char>>
|
||||
class AdcDeviceNetServer : public AdcGenericNetServer<IdentT, LoggerT>
|
||||
{
|
||||
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...)
|
||||
typedef std::vector<char> 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<message_t>(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<IdentT>(id), _devices() {}
|
||||
using AdcGenericNetServer<IdentT, LoggerT>::AdcGenericNetServer;
|
||||
// AdcDeviceNetServer(const server_ident_t& id) : AdcGenericNetServer<IdentT>(id), _devices() {}
|
||||
|
||||
virtual ~AdcDeviceNetServer() = default;
|
||||
|
||||
@ -406,6 +413,18 @@ public:
|
||||
CmdIdDeserialT&& cmd_id_deser_func = {}) // deserializer of command ID
|
||||
{
|
||||
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),
|
||||
std::forward<CmdIdDeserialT>(cmd_id_deser_func));
|
||||
|
||||
@ -415,7 +434,25 @@ public:
|
||||
template <interfaces::adc_device_c DeviceT>
|
||||
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;
|
||||
}
|
||||
|
||||
@ -118,12 +118,14 @@ concept adc_netservice_c = requires(SRVT srv, const SRVT srv_const) {
|
||||
// acceptor type
|
||||
requires std::is_class_v<typename SRVT::acceptor_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>(),
|
||||
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&>());
|
||||
|
||||
{ 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<typename SRVT::async_callback_err_t>(), std::declval<std::string&>());
|
||||
|
||||
@ -224,7 +228,10 @@ concept adc_netsession_proto_c =
|
||||
|
||||
|
||||
|
||||
/* LOGGER */
|
||||
/* LOGGER:
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
template <typename LOGGERT>
|
||||
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>;
|
||||
|
||||
// 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_view>(),
|
||||
std::declval<std::string>());
|
||||
log.logMessage(std::declval<typename LOGGERT::loglevel_t>(), std::declval<std::string>());
|
||||
|
||||
// logging method must accept at least the single argument - formatting string
|
||||
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>());
|
||||
// specialized logging methods signature:
|
||||
|
||||
log.logWarn(std::declval<std::string_view>());
|
||||
log.logWarn(std::declval<std::string_view>(), std::declval<std::string>());
|
||||
|
||||
log.logError(std::declval<std::string_view>());
|
||||
log.logError(std::declval<std::string_view>(), std::declval<std::string>());
|
||||
log.logInfo(std::declval<std::string>());
|
||||
log.logDebug(std::declval<std::string>());
|
||||
log.logError(std::declval<std::string>());
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -8,9 +8,9 @@ ABSTRACT DEVICE COMPONENTS LIBRARY
|
||||
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#if __has_include(<unistd.h>) // 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<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
|
||||
class AdcPosixGenericDaemon
|
||||
{
|
||||
@ -303,25 +224,39 @@ protected:
|
||||
};
|
||||
|
||||
|
||||
static_assert(interfaces::adc_logger_c<utils::AdcOstreamLogger<>>, "!!!!!");
|
||||
|
||||
|
||||
|
||||
/* very generic network server */
|
||||
|
||||
template <typename IdentT = std::string>
|
||||
class AdcGenericNetServer : public AdcPosixGenericDaemon, public AdcNetSessionManager, public AdcTrivialLogger
|
||||
template <typename IdentT = std::string, interfaces::adc_logger_c LoggerT = utils::AdcOstreamLogger<char>>
|
||||
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<char>& log_stream = std::cout)
|
||||
: AdcTrivialLogger(log_stream), _serverIdent(id)
|
||||
template <typename... LoggerCtorArgTs>
|
||||
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(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<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=(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<typename SessionT::netsession_ident_t>
|
||||
{
|
||||
logDebug("Call {}", __PRETTY_FUNCTION__);
|
||||
|
||||
if (!_isListening<SessionT>[this][id]) {
|
||||
auto acceptor = std::make_shared<typename SessionT::netservice_t::acceptor_t>(
|
||||
std::forward<AcceptorCtorArgTs>(ctor_args)...);
|
||||
@ -380,6 +328,16 @@ public:
|
||||
_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));
|
||||
}
|
||||
|
||||
@ -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:
|
||||
// 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](
|
||||
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<SessionT>(id, std::move(srv), sess_ctx);
|
||||
startSession(sess);
|
||||
|
||||
_isListening<SessionT>[this][id] = true;
|
||||
doAccept<SessionT>(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<SessionT>[this][id] = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// virtual void errorMessage(const std::string&) {};
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -14,20 +14,26 @@
|
||||
namespace adc::impl
|
||||
{
|
||||
|
||||
template <typename IdentT = std::string>
|
||||
class AdcDeviceNetServerASIO : public AdcDeviceNetServer<IdentT>
|
||||
template <typename IdentT = std::string, interfaces::adc_logger_c LoggerT = utils::AdcOstreamLogger<char>>
|
||||
class AdcDeviceNetServerASIO : public AdcDeviceNetServer<IdentT, LoggerT>
|
||||
{
|
||||
typedef AdcDeviceNetServer<IdentT> base_t;
|
||||
typedef AdcDeviceNetServer<IdentT, LoggerT> base_t;
|
||||
|
||||
public:
|
||||
using typename base_t::logger_t;
|
||||
using typename base_t::server_ident_t;
|
||||
|
||||
typedef std::string session_ident_t;
|
||||
|
||||
template <typename ServiceT>
|
||||
using Session = typename base_t::template Session<ServiceT, session_ident_t>;
|
||||
|
||||
AdcDeviceNetServerASIO(const server_ident_t& id, asio::io_context& io_context)
|
||||
: base_t(id), _ioContext(io_context), _stopSignal(io_context), _restartSignal(io_context)
|
||||
template <typename... LoggerCtorArgTs>
|
||||
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> &&
|
||||
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) {
|
||||
_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) {
|
||||
|
||||
@ -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 "<local>";
|
||||
}
|
||||
|
||||
return st.str();
|
||||
}
|
||||
|
||||
static std::string formattableError(std::error_code ec)
|
||||
{
|
||||
|
||||
@ -8,6 +8,11 @@
|
||||
#include "../net/adc_netproto.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::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);
|
||||
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.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);
|
||||
|
||||
std::cout << "\tOK\n";
|
||||
// std::cout << "\tOK\n";
|
||||
} else {
|
||||
std::cerr << "Unrecognized endpoint: '" << ep << "'! Ignore!\n";
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user