From b04bfab964bbbbb32e915f0988d34c0dd70ab75b Mon Sep 17 00:00:00 2001 From: "Timur A. Fatkhullin" Date: Mon, 6 Jul 2026 18:59:09 +0300 Subject: [PATCH] add loggers --- CMakeLists.txt | 40 ++++ include/snipplib/concepts/snplib_concepts.h | 14 ++ include/snipplib/concepts/snplib_traits.h | 1 + include/snipplib/utils/snplib_logger.h | 222 ++++++++++++++++++++ include/snipplib/utils/snplib_utils.h | 4 +- 5 files changed, 280 insertions(+), 1 deletion(-) create mode 100644 include/snipplib/utils/snplib_logger.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b821a8..543e19a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,12 +3,42 @@ project(snipplib VERSION 0.1.0 LANGUAGES CXX) option(SNPLIB_BUILD_EXAMPLES "Build examples" ON) +option(SNPLIB_USE_SPDLOG "Use of spdlog library" ON) + +if(SNPLIB_USE_SPDLOG) + find_package(spdlog CONFIG) + if(NOT spdlog_FOUND) + FetchContent_Declare( + spdlog + GIT_REPOSITORY "https://github.com/gabime/spdlog.git" + GIT_TAG "v1.17.0" + GIT_SHALLOW TRUE + GIT_SUBMODULES "" + GIT_PROGRESS TRUE + # CMAKE_ARGS + # -DSPDLOG_USE_STD_FORMAT=ON + # -DSPDLOG_FMT_EXTERNAL=OFF + OVERRIDE_FIND_PACKAGE + ) + + set(SPDLOG_USE_STD_FORMAT ON CACHE INTERNAL "Use of C++20 std::format") + set(SPDLOG_FMT_EXTERNAL + OFF + CACHE INTERNAL + "Turn off external fmt library" + ) + set(SPDLOG_INSTALL ON CACHE BOOL "Enable spdlog installation" FORCE) + FetchContent_MakeAvailable(spdlog) + endif() +endif(SNPLIB_USE_SPDLOG) + set(LIB_HEADERS include/snipplib/concepts/snplib_concepts.h include/snipplib/concepts/snplib_traits.h include/snipplib/utils/snplib_hash.h include/snipplib/utils/snplib_string.h include/snipplib/utils/snplib_keyvalue_expr.h + include/snipplib/utils/snplib_logger.h include/snipplib/utils/snplib_utils.h include/snipplib/containers/snplib_hmap.h include/snipplib/containers/snplib_hvec.h @@ -24,6 +54,16 @@ target_include_directories( "$" "$" ) +if(SNPLIB_USE_SPDLOG) + target_compile_definitions( + ${PROJECT_NAME} + INTERFACE + SPDLOG_USE_STD_FORMAT=1 + SPDLOG_FMT_EXTERNAL=0 + SNPLIB_SPDLOGGER=1 + ) + target_link_libraries(${PROJECT_NAME} INTERFACE spdlog::spdlog_header_only) +endif() if(SNPLIB_BUILD_EXAMPLES) set(EXAM_STRING str_exam) diff --git a/include/snipplib/concepts/snplib_concepts.h b/include/snipplib/concepts/snplib_concepts.h index fde448d..42a7c29 100644 --- a/include/snipplib/concepts/snplib_concepts.h +++ b/include/snipplib/concepts/snplib_concepts.h @@ -200,4 +200,18 @@ template concept snplib_async_retval_c = snplib_awaitable_c && snplib_expected_c; + +/* a concept for generic logger class */ + +template +concept snplib_logger_c = requires(T t) { + { t.logInfo(std::declval()) }; + { t.logWarn(std::declval()) }; + { t.logDebug(std::declval()) }; + { t.logError(std::declval()) }; + { t.logTrace(std::declval()) }; +}; + + + } // namespace snplib \ No newline at end of file diff --git a/include/snipplib/concepts/snplib_traits.h b/include/snipplib/concepts/snplib_traits.h index 2e38103..1791b89 100644 --- a/include/snipplib/concepts/snplib_traits.h +++ b/include/snipplib/concepts/snplib_traits.h @@ -81,4 +81,5 @@ struct snplib_func_traits_t : snplib_func_traits_t { template using snplib_func_arg1_t = typename snplib_func_traits_t::arg1_t; + } // namespace snplib \ No newline at end of file diff --git a/include/snipplib/utils/snplib_logger.h b/include/snipplib/utils/snplib_logger.h new file mode 100644 index 0000000..9bd174f --- /dev/null +++ b/include/snipplib/utils/snplib_logger.h @@ -0,0 +1,222 @@ +#pragma once + +#include +#ifdef SNPLIB_SPDLOGGER +#include +#endif + +#include "../concepts/snplib_concepts.h" + +namespace snplib +{ + +/* snplib_logger_c-like class adaptor to use formating */ + +// template +// struct snplib_logger_fmt_adaptor_t : public LoggerT { +// template +// snplib_logger_fmt_adaptor_t(CtorArgTs&&... ctor_args): LoggerT(std::forward(ctor_args)...) {} + +// ~snplib_logger_fmt_adaptor_t() = default; + +// template ... ArgTs> +// auto logInfo(std::format_string fmt, ArgTs&&... args) +// { +// return logInfo(std::format(fmt, std::forward(args)...)); +// } + +// template ... ArgTs> +// auto logWarn(std::format_string fmt, ArgTs&&... args) +// { +// return logWarn(std::format(fmt, std::forward(args)...)); +// } + +// template ... ArgTs> +// auto logDebug(std::format_string fmt, ArgTs&&... args) +// { +// return logDebug(std::format(fmt, std::forward(args)...)); +// } + +// template ... ArgTs> +// auto logError(std::format_string fmt, ArgTs&&... args) +// { +// return logError(std::format(fmt, std::forward(args)...)); +// } + +// template ... ArgTs> +// auto logTrace(std::format_string fmt, ArgTs&&... args) +// { +// return logTrace(std::format(fmt, std::forward(args)...)); +// } +// }; + + + +/* a NULL-logger */ + +struct snplib_null_logger_t { + void logInfo(std::string const&) {} + void logWarn(std::string const&) {} + void logDebug(std::string const&) {} + void logError(std::string const&) {} + void logTrace(std::string const&) {} +}; + + +#ifdef SNPLIB_SPDLOGGER + +/* SPDLOG-library based advanced single/multithreaded logger */ + +class snplib_spdlog_logger_t +{ +protected: + std::shared_ptr _loggerSPtr; + std::list _currentLogPatternRange{}; + std::string _currentLogPattern{}; + +public: + // [year-month-day time.millisecs][log-level]: log-message + constexpr static std::array LOGGER_DEFAULT_FORMAT = {std::string_view{"[%Y-%m-%d %T.%e]"}, std::string_view{"[%l]"}, + std::string_view{": "}, std::string_view{"%v"}}; + + + + template + requires snplib_input_char_range_c> + snplib_spdlog_logger_t(std::shared_ptr logger, R&& pattern_range = LOGGER_DEFAULT_FORMAT) + : _loggerSPtr(logger) + { + if (std::distance(pattern_range.begin(), pattern_range.end())) { + std::ranges::copy( + pattern_range | std::views::transform([](const auto& el) { return std::string(el.begin(), el.end()); }), + std::back_inserter(_currentLogPatternRange)); + } else { + std::ranges::copy(LOGGER_DEFAULT_FORMAT | std::views::transform([](const auto& el) { + return std::string(el.begin(), el.end()); + }), + std::back_inserter(_currentLogPatternRange)); + } + + std::ranges::copy(std::views::join(_currentLogPatternRange), std::back_inserter(_currentLogPattern)); + + _loggerSPtr->set_pattern(_currentLogPattern); + } + + virtual ~snplib_spdlog_logger_t() = default; + + void logMessage(spdlog::level::level_enum level, const std::string& msg) + { + _loggerSPtr->log(level, msg); + } + + // specialized for given level methods + + void logCritical(const std::string& msg) + { + logMessage(spdlog::level::critical, msg); + } + + void logError(const std::string& msg) + { + logMessage(spdlog::level::err, msg); + } + + void logWarn(const std::string& msg) + { + logMessage(spdlog::level::warn, msg); + } + + void logInfo(const std::string& msg) + { + logMessage(spdlog::level::info, msg); + } + + void logDebug(const std::string& msg) + { + logMessage(spdlog::level::debug, msg); + } + + void logTrace(const std::string& msg) + { + logMessage(spdlog::level::trace, msg); + } + + template ... ArgTs> + void logMessage(spdlog::level::level_enum level, spdlog::format_string_t fmt, ArgTs&&... args) + { + _loggerSPtr->log(level, fmt, std::forward(args)...); + } + + template ... ArgTs> + void logCritical(spdlog::format_string_t fmt, ArgTs&&... args) + { + _loggerSPtr->log(spdlog::level::critical, fmt, std::forward(args)...); + } + + template ... ArgTs> + void logError(spdlog::format_string_t fmt, ArgTs&&... args) + { + _loggerSPtr->log(spdlog::level::err, fmt, std::forward(args)...); + } + + template ... ArgTs> + void logWarn(spdlog::format_string_t fmt, ArgTs&&... args) + { + _loggerSPtr->log(spdlog::level::warn, fmt, std::forward(args)...); + } + + template ... ArgTs> + void logInfo(spdlog::format_string_t fmt, ArgTs&&... args) + { + _loggerSPtr->log(spdlog::level::info, fmt, std::forward(args)...); + } + + template ... ArgTs> + void logDebug(spdlog::format_string_t fmt, ArgTs&&... args) + { + _loggerSPtr->log(spdlog::level::debug, fmt, std::forward(args)...); + } + + template ... ArgTs> + void logTrace(spdlog::format_string_t fmt, ArgTs&&... args) + { + _loggerSPtr->log(spdlog::level::trace, fmt, std::forward(args)...); + } + + + // 'after_idx' is 0-based index! + void addMarkToPatternIdx(snplib_input_char_range_c auto&& mark, size_t after_idx = 1) + requires(!std::is_pointer_v>) + { + if constexpr (std::is_pointer_v>) { + addMarkToPatternIdx(std::string_view(mark), after_idx); + } else { + if (!std::distance(mark.begin(), mark.end())) { + return; + } + + auto it = _currentLogPatternRange.begin(); + size_t idx = 0; + while (it != _currentLogPatternRange.end()) { + ++it; + if (idx == after_idx) + break; + + ++idx; + } + + _currentLogPatternRange.emplace(it, mark.begin(), mark.end()); + + _currentLogPattern.clear(); + std::ranges::copy(std::views::join(_currentLogPatternRange), std::back_inserter(_currentLogPattern)); + + _loggerSPtr->set_pattern(_currentLogPattern); + } + } +}; + +static_assert(snplib_logger_c); + +#endif + +} // namespace snplib \ No newline at end of file diff --git a/include/snipplib/utils/snplib_utils.h b/include/snipplib/utils/snplib_utils.h index 632355b..09e40ed 100644 --- a/include/snipplib/utils/snplib_utils.h +++ b/include/snipplib/utils/snplib_utils.h @@ -1,4 +1,6 @@ #pragma once #include "snplib_hash.h" -#include "snplib_string.h" \ No newline at end of file +#include "snplib_keyvalue_expr.h" +#include "snplib_logger.h" +#include "snplib_string.h"