diff --git a/include/flow/impl.hpp b/include/flow/impl.hpp index 8e0b86d4..15d9827e 100644 --- a/include/flow/impl.hpp +++ b/include/flow/impl.hpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include @@ -20,9 +22,9 @@ constexpr auto run_func() -> void { if constexpr (not FlowName.empty()) { using log_spec_t = decltype(get_log_spec>()); - CIB_LOG(typename log_spec_t::flavor, log_spec_t::level, - "flow.{}({})", typename CTNode::type_t{}, - typename CTNode::name_t{}); + CIB_LOG_ENV(logging::get_level, log_spec_t::level); + CIB_LOG(typename log_spec_t::flavor, "flow.{}({})", + typename CTNode::type_t{}, typename CTNode::name_t{}); } typename CTNode::func_t{}(); } @@ -60,16 +62,16 @@ template struct inlined_func_list { if constexpr (loggingEnabled) { using log_spec_t = decltype(get_log_spec()); - CIB_LOG(typename log_spec_t::flavor, log_spec_t::level, - "flow.start({})", name); + CIB_LOG_ENV(logging::get_level, log_spec_t::level); + CIB_LOG(typename log_spec_t::flavor, "flow.start({})", name); } (FuncPtrs(), ...); if constexpr (loggingEnabled) { using log_spec_t = decltype(get_log_spec()); - CIB_LOG(typename log_spec_t::flavor, log_spec_t::level, - "flow.end({})", name); + CIB_LOG_ENV(logging::get_level, log_spec_t::level); + CIB_LOG(typename log_spec_t::flavor, "flow.end({})", name); } } }; diff --git a/include/log/catalog/mipi_encoder.hpp b/include/log/catalog/mipi_encoder.hpp index 4a761bcc..93e046ed 100644 --- a/include/log/catalog/mipi_encoder.hpp +++ b/include/log/catalog/mipi_encoder.hpp @@ -20,13 +20,13 @@ namespace logging::mipi { namespace detail { -template -constexpr auto to_message() { +template constexpr auto to_message() { constexpr auto s = S::value; using char_t = typename std::remove_cv_t::value_type; return [&](std::integer_sequence) { return sc::message< - L, sc::undefined, char_t, s[Is]...>>{}; + static_cast(L), + sc::undefined, char_t, s[Is]...>>{}; }(std::make_integer_sequence{}); } @@ -103,21 +103,22 @@ using catalog_msg_t = template struct log_handler { constexpr explicit log_handler(TDestinations &&ds) : dests{std::move(ds)} {} - template ALWAYS_INLINE auto log(FilenameStringType, LineNumberType, MsgType const &msg) -> void { - log_msg(msg); + log_msg(msg); } - template + template ALWAYS_INLINE auto log_msg(Msg msg) -> void { msg.apply([&](S, Args... args) { - using Message = decltype(detail::to_message()); + constexpr auto L = stdx::to_underlying(get_level(Env{}).value); + using Message = decltype(detail::to_message()); using Module = decltype(detail::to_module()); - dispatch_message(catalog(), module(), - static_cast(args)...); + dispatch_message(catalog(), module(), + static_cast(args)...); }); } @@ -173,7 +174,7 @@ template struct log_handler { dests); } - template ... MsgDataTypes> + template ... MsgDataTypes> ALWAYS_INLINE auto dispatch_message(string_id id, [[maybe_unused]] module_id m, MsgDataTypes... msg_data) -> void { diff --git a/include/log/env.hpp b/include/log/env.hpp index 4e49a60f..822ed0da 100644 --- a/include/log/env.hpp +++ b/include/log/env.hpp @@ -69,6 +69,16 @@ template struct for_each_pair> { 2 * Is + 1>::value.value>()>...>; }; } // namespace detail + +template > +constexpr auto make_env = [] { + using new_env_t = typename detail::for_each_pair< + std::make_index_sequence>::template type; + return boost::mp11::mp_append{}; +}; + +template +using make_env_t = decltype(make_env<>.template operator()()); } // namespace logging using cib_log_env_t = logging::env<>; @@ -82,10 +92,11 @@ using cib_log_env_t = logging::env<>; #endif #define CIB_LOG_ENV_DECL(...) \ - [[maybe_unused]] typedef decltype([] { \ + [[maybe_unused]] typedef decltype([] { \ using new_env_t = \ typename logging::detail::for_each_pair>::template type; \ + sizeof...(_env_args) / 2>>::template type<_env_args...>; \ return boost::mp11::mp_append{}; \ }.template operator()<__VA_ARGS__>()) cib_log_env_t diff --git a/include/log/fmt/logger.hpp b/include/log/fmt/logger.hpp index 255f4f57..644c076d 100644 --- a/include/log/fmt/logger.hpp +++ b/include/log/fmt/logger.hpp @@ -1,24 +1,29 @@ #pragma once +#include #include #include #include #include #include +#include #include #include +#include #include -template struct fmt::formatter> { +template +struct fmt::formatter> { constexpr static auto parse(format_parse_context &ctx) { return ctx.begin(); } template - auto format(logging::level_constant, FormatContext &ctx) const { + auto format(std::integral_constant, + FormatContext &ctx) const { return ::fmt::format_to(ctx.out(), logging::to_text()); } }; @@ -27,7 +32,7 @@ namespace logging::fmt { template struct log_handler { constexpr explicit log_handler(TDestinations &&ds) : dests{std::move(ds)} {} - template auto log(FilenameStringType, LineNumberType, MsgType const &msg) -> void { auto const currentTime = @@ -38,7 +43,7 @@ template struct log_handler { stdx::for_each( [&](auto &out) { ::fmt::format_to(out, "{:>8}us {} [{}]: ", currentTime, - level_constant{}, get_module(Env{}).value); + get_level(Env{}), get_module(Env{}).value); msg.apply( [&](StringType, auto const &...args) { ::fmt::format_to(out, StringType::value, args...); diff --git a/include/log/level.hpp b/include/log/level.hpp index 79b3a126..8552918f 100644 --- a/include/log/level.hpp +++ b/include/log/level.hpp @@ -1,11 +1,14 @@ #pragma once +#include + #include #include #include #include #include +#include namespace logging { // enum assignment is according to Mipi_Sys-T Severity definition @@ -30,5 +33,12 @@ template return level_text[stdx::to_underlying(L)]; } -template struct level_constant : std::integral_constant {}; +[[maybe_unused]] constexpr inline struct get_level_t { + template + CONSTEVAL auto operator()(T &&t) const noexcept( + noexcept(std::forward(t).query(std::declval()))) + -> decltype(std::forward(t).query(*this)) { + return std::forward(t).query(*this); + } +} get_level; } // namespace logging diff --git a/include/log/log.hpp b/include/log/log.hpp index 56e6d14a..1a0a6075 100644 --- a/include/log/log.hpp +++ b/include/log/log.hpp @@ -27,7 +27,7 @@ namespace logging { namespace null { struct config { struct { - template + template constexpr auto log(auto &&...) const noexcept -> void {} } logger; }; @@ -46,33 +46,39 @@ ALWAYS_INLINE constexpr static auto get_config() -> auto & { } } -template +template ALWAYS_INLINE static auto log(TArgs &&...args) -> void { auto &cfg = get_config(); - cfg.logger.template log(std::forward(args)...); + cfg.logger.template log(std::forward(args)...); } } // namespace logging // NOLINTBEGIN(cppcoreguidelines-macro-usage) -#define CIB_LOG(FLAVOR, LEVEL, MSG, ...) \ - logging::log( \ +#define CIB_LOG(FLAVOR, MSG, ...) \ + logging::log( \ __FILE__, __LINE__, sc::format(MSG##_sc __VA_OPT__(, ) __VA_ARGS__)) +#define CIB_LOG_WITH_LEVEL(LEVEL, ...) \ + do { \ + CIB_LOG_ENV(logging::get_level, LEVEL); \ + CIB_LOG(logging::default_flavor_t __VA_OPT__(, ) __VA_ARGS__); \ + } while (false) + #define CIB_TRACE(...) \ - CIB_LOG(logging::default_flavor_t, logging::level::TRACE, __VA_ARGS__) + CIB_LOG_WITH_LEVEL(logging::level::TRACE __VA_OPT__(, ) __VA_ARGS__) #define CIB_INFO(...) \ - CIB_LOG(logging::default_flavor_t, logging::level::INFO, __VA_ARGS__) + CIB_LOG_WITH_LEVEL(logging::level::INFO __VA_OPT__(, ) __VA_ARGS__) #define CIB_WARN(...) \ - CIB_LOG(logging::default_flavor_t, logging::level::WARN, __VA_ARGS__) + CIB_LOG_WITH_LEVEL(logging::level::WARN __VA_OPT__(, ) __VA_ARGS__) #define CIB_ERROR(...) \ - CIB_LOG(logging::default_flavor_t, logging::level::ERROR, __VA_ARGS__) + CIB_LOG_WITH_LEVEL(logging::level::ERROR __VA_OPT__(, ) __VA_ARGS__) #define CIB_FATAL(MSG, ...) \ [](auto &&str) { \ - logging::log(__FILE__, __LINE__, str); \ + CIB_LOG_ENV(logging::get_level, logging::level::FATAL); \ + logging::log(__FILE__, \ + __LINE__, str); \ FWD(str).apply([](S s, Args... args) { \ constexpr auto cts = stdx::ct_string_from_type(s); \ stdx::panic(args...); \ @@ -93,7 +99,8 @@ ALWAYS_INLINE static auto log_version() -> void { }) { l_cfg.logger.template log_build(); } else { - l_cfg.logger.template log( + CIB_LOG_ENV(logging::get_level, logging::level::MAX); + l_cfg.logger.template log( "", 0, sc::format("Version: {} ({})"_sc, sc::uint_, stdx::ct_string_to_type #include +#include #include @@ -37,11 +38,10 @@ constexpr auto msB = flow::milestone<"msB">(); struct log_config { struct log_handler { - template + template auto log(FilenameStringType, LineNumberType, MsgType const &) -> void { - log_calls.push_back(Level); + log_calls.push_back(logging::get_level(Env{}).value); } }; log_handler logger; diff --git a/test/flow/log_levels.cpp b/test/flow/log_levels.cpp index b2465934..82037067 100644 --- a/test/flow/log_levels.cpp +++ b/test/flow/log_levels.cpp @@ -1,5 +1,6 @@ #include #include +#include #include @@ -34,11 +35,10 @@ constexpr auto ms = flow::milestone<"ms">(); struct log_config { struct log_handler { - template + template auto log(FilenameStringType, LineNumberType, MsgType const &) -> void { - log_calls.push_back(Level); + log_calls.push_back(logging::get_level(Env{}).value); } }; log_handler logger; diff --git a/test/log/CMakeLists.txt b/test/log/CMakeLists.txt index 216f9acc..084af1a3 100644 --- a/test/log/CMakeLists.txt +++ b/test/log/CMakeLists.txt @@ -1,4 +1,11 @@ -add_tests(FILES log module_id env LIBRARIES cib_log) +add_tests( + FILES + level + log + module_id + env + LIBRARIES + cib_log) add_tests(FILES fmt_logger LIBRARIES cib_log_fmt) add_tests(FILES mipi_encoder mipi_logger LIBRARIES cib_log_mipi) diff --git a/test/log/catalog1_lib.cpp b/test/log/catalog1_lib.cpp index 1243ca7c..5f99cdb9 100644 --- a/test/log/catalog1_lib.cpp +++ b/test/log/catalog1_lib.cpp @@ -17,6 +17,8 @@ struct test_log_args_destination { last_header = hdr; } }; + +using log_env1 = logging::make_env_t; } // namespace auto log_zero_args() -> void; @@ -27,32 +29,34 @@ auto log_with_fixed_module_id() -> void; auto log_zero_args() -> void { auto cfg = logging::mipi::config{test_log_args_destination{}}; - cfg.logger.log_msg( - "A string with no placeholders"_sc); + cfg.logger.log_msg("A string with no placeholders"_sc); } auto log_one_ct_arg() -> void { auto cfg = logging::mipi::config{test_log_args_destination{}}; - cfg.logger.log_msg( + cfg.logger.log_msg( format("B string with {} placeholder"_sc, "one"_sc)); } auto log_one_rt_arg() -> void { auto cfg = logging::mipi::config{test_log_args_destination{}}; - cfg.logger.log_msg( - format("C string with {} placeholder"_sc, 1)); + cfg.logger.log_msg(format("C string with {} placeholder"_sc, 1)); } auto log_with_non_default_module_id() -> void { - CIB_LOG_MODULE("not default"); - auto cfg = logging::mipi::config{test_log_args_destination{}}; - cfg.logger.log_msg( - format("ModuleID string with {} placeholder"_sc, 1)); + CIB_WITH_LOG_ENV(logging::get_level, logging::level::TRACE, + logging::get_module, "not default") { + auto cfg = logging::mipi::config{test_log_args_destination{}}; + cfg.logger.log_msg( + format("ModuleID string with {} placeholder"_sc, 1)); + } } auto log_with_fixed_module_id() -> void { - CIB_LOG_MODULE("fixed"); - auto cfg = logging::mipi::config{test_log_args_destination{}}; - cfg.logger.log_msg( - format("Fixed ModuleID string with {} placeholder"_sc, 1)); + CIB_WITH_LOG_ENV(logging::get_level, logging::level::TRACE, + logging::get_module, "fixed") { + auto cfg = logging::mipi::config{test_log_args_destination{}}; + cfg.logger.log_msg( + format("Fixed ModuleID string with {} placeholder"_sc, 1)); + } } diff --git a/test/log/catalog2a_lib.cpp b/test/log/catalog2a_lib.cpp index 16be2a65..b19b5f05 100644 --- a/test/log/catalog2a_lib.cpp +++ b/test/log/catalog2a_lib.cpp @@ -14,12 +14,15 @@ namespace { struct test_log_args_destination { auto log_by_args(std::uint32_t, auto...) -> void { ++log_calls; } }; + +using log_env2a = + logging::make_env_t; } // namespace auto log_two_rt_args() -> void; auto log_two_rt_args() -> void { auto cfg = logging::mipi::config{test_log_args_destination{}}; - cfg.logger.log_msg( + cfg.logger.log_msg( format("D string with {} and {} placeholder"_sc, 1, 2)); } diff --git a/test/log/catalog2b_lib.cpp b/test/log/catalog2b_lib.cpp index 2887d076..573303dc 100644 --- a/test/log/catalog2b_lib.cpp +++ b/test/log/catalog2b_lib.cpp @@ -14,6 +14,9 @@ namespace { struct test_log_args_destination { auto log_by_args(std::uint32_t, auto...) -> void { ++log_calls; } }; + +using log_env2b = + logging::make_env_t; } // namespace auto log_rt_enum_arg() -> void; @@ -21,6 +24,6 @@ auto log_rt_enum_arg() -> void; auto log_rt_enum_arg() -> void { auto cfg = logging::mipi::config{test_log_args_destination{}}; using namespace ns; - cfg.logger.log_msg( + cfg.logger.log_msg( format("E string with {} placeholder"_sc, E::value)); } diff --git a/test/log/env.cpp b/test/log/env.cpp index 402ad4cc..539067c2 100644 --- a/test/log/env.cpp +++ b/test/log/env.cpp @@ -59,7 +59,7 @@ namespace { auto logged_modules = std::vector{}; struct log_handler { - template auto log(FilenameStringType, LineNumberType, MsgType const &) -> void { using namespace stdx::literals; diff --git a/test/log/fmt_logger.cpp b/test/log/fmt_logger.cpp index 714c499c..428b8258 100644 --- a/test/log/fmt_logger.cpp +++ b/test/log/fmt_logger.cpp @@ -1,6 +1,7 @@ #include #include +#include #include @@ -61,49 +62,49 @@ TEST_CASE("log levels are properly represented", "[fmt_logger]") { { std::string level{}; fmt::format_to(std::back_inserter(level), "{}", - logging::level_constant{}); + stdx::ct()); CHECK(level == "TRACE"); } { std::string level{}; fmt::format_to(std::back_inserter(level), "{}", - logging::level_constant{}); + stdx::ct()); CHECK(level == "INFO"); } { std::string level{}; fmt::format_to(std::back_inserter(level), "{}", - logging::level_constant{}); + stdx::ct()); CHECK(level == "WARN"); } { std::string level{}; fmt::format_to(std::back_inserter(level), "{}", - logging::level_constant{}); + stdx::ct()); CHECK(level == "ERROR"); } { std::string level{}; fmt::format_to(std::back_inserter(level), "{}", - logging::level_constant{}); + stdx::ct()); CHECK(level == "FATAL"); } { std::string level{}; fmt::format_to(std::back_inserter(level), "{}", - logging::level_constant{}); + stdx::ct()); CHECK(level == "MAX"); } { std::string level{}; fmt::format_to(std::back_inserter(level), "{}", - logging::level_constant{}); + stdx::ct()); CHECK(level == "USER1"); } { std::string level{}; fmt::format_to(std::back_inserter(level), "{}", - logging::level_constant{}); + stdx::ct()); CHECK(level == "USER2"); } } @@ -161,9 +162,10 @@ inline auto logging::config = logging::fmt::config{std::back_inserter(secure_buffer)}; TEST_CASE("logging can be flavored", "[fmt_logger]") { + CIB_LOG_ENV(logging::get_level, logging::level::TRACE); buffer.clear(); secure_buffer.clear(); - CIB_LOG(secure_t, logging::level::TRACE, "Hello"); + CIB_LOG(secure_t, "Hello"); CAPTURE(secure_buffer); CHECK(secure_buffer.substr(secure_buffer.size() - std::size("Hello")) == "Hello\n"); diff --git a/test/log/level.cpp b/test/log/level.cpp new file mode 100644 index 00000000..b29ada8f --- /dev/null +++ b/test/log/level.cpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include + +namespace { +enum struct custom_level { THE_ONE_LEVEL = 5 }; + +std::string buffer{}; +} // namespace + +template <> +inline auto logging::config<> = + logging::fmt::config{std::back_inserter(buffer)}; + +template +struct fmt::formatter> { + constexpr static auto parse(format_parse_context &ctx) { + return ctx.begin(); + } + + template + auto format(std::integral_constant, + FormatContext &ctx) const { + return ::fmt::format_to(ctx.out(), stdx::enum_as_string()); + } +}; + +TEST_CASE("fmt logger works with custom level", "[level]") { + CIB_LOG_ENV(logging::get_level, custom_level::THE_ONE_LEVEL); + buffer.clear(); + CIB_LOG(logging::default_flavor_t, "Hello"); + CAPTURE(buffer); + CHECK(buffer.find("THE_ONE_LEVEL [default]:") != std::string::npos); +} + +template auto catalog() -> string_id { return 0xdeadbeef; } +template auto module() -> module_id { return 0x5a; } + +namespace { +int log_calls{}; + +struct test_destination { + auto log_by_args(std::uint32_t header, auto id, auto &&...) { + CHECK(header == 0x01'5a'00'53); + CHECK(id == 0xdeadbeef); + ++log_calls; + } +}; +} // namespace + +TEST_CASE("mipi logger works with custom level", "[level]") { + log_calls = 0; + CIB_LOG_ENV(logging::get_level, custom_level::THE_ONE_LEVEL); + auto cfg = logging::mipi::config{test_destination{}}; + cfg.logger.log_msg(sc::format("Hello {} {}"_sc, 17, 42)); + CHECK(log_calls == 1); +} diff --git a/test/log/mipi_encoder.cpp b/test/log/mipi_encoder.cpp index cefb2b7a..cd04479a 100644 --- a/test/log/mipi_encoder.cpp +++ b/test/log/mipi_encoder.cpp @@ -61,12 +61,6 @@ expected_msg_header(logging::level level, module_id m, : expected_short32_header(); } -template struct test_log_id_destination { - template static auto log_by_args(Id id) { - CHECK(id == ((ExpectedId << 4u) | 1u)); - } -}; - int num_log_args_calls{}; constexpr auto check = [](auto value, auto expected) { @@ -126,69 +120,72 @@ struct test_log_version_destination { ++num_log_args_calls; } }; + +using log_env = logging::make_env_t; } // namespace template <> inline auto conc::injected_policy<> = test_conc_policy{}; TEST_CASE("log zero arguments", "[mipi]") { + CIB_LOG_ENV(logging::get_level, logging::level::TRACE); test_critical_section::count = 0; auto cfg = logging::mipi::config{ - test_log_args_destination{}}; - cfg.logger.log_msg("Hello"_sc); + test_log_args_destination{}}; + cfg.logger.log_msg("Hello"_sc); CHECK(test_critical_section::count == 2); } TEST_CASE("log one argument", "[mipi]") { + CIB_LOG_ENV(logging::get_level, logging::level::TRACE); test_critical_section::count = 0; auto cfg = logging::mipi::config{ - test_log_args_destination{}}; - cfg.logger.log_msg( - format("{}"_sc, 17u)); + test_log_args_destination{}}; + cfg.logger.log_msg(format("{}"_sc, 17u)); CHECK(test_critical_section::count == 2); } TEST_CASE("log two arguments", "[mipi]") { + CIB_LOG_ENV(logging::get_level, logging::level::TRACE); test_critical_section::count = 0; auto cfg = logging::mipi::config{ - test_log_args_destination{}}; - cfg.logger.log_msg( - format("{} {}"_sc, 17u, 18u)); + test_log_args_destination{}}; + cfg.logger.log_msg(format("{} {}"_sc, 17u, 18u)); CHECK(test_critical_section::count == 2); } TEST_CASE("log more than two arguments", "[mipi]") { + CIB_LOG_ENV(logging::get_level, logging::level::TRACE); { test_critical_section::count = 0; auto cfg = logging::mipi::config{ - test_log_buf_destination{}}; - cfg.logger.log_msg( - format("{} {} {}"_sc, 17u, 18u, 19u)); + test_log_buf_destination{}}; + cfg.logger.log_msg(format("{} {} {}"_sc, 17u, 18u, 19u)); CHECK(test_critical_section::count == 2); } { test_critical_section::count = 0; auto cfg = logging::mipi::config{ - test_log_buf_destination{}}; - cfg.logger.log_msg( + test_log_buf_destination{}}; + cfg.logger.log_msg( format("{} {} {} {}"_sc, 17u, 18u, 19u, 20u)); CHECK(test_critical_section::count == 2); } } TEST_CASE("log to multiple destinations", "[mipi]") { + CIB_LOG_ENV(logging::get_level, logging::level::TRACE); test_critical_section::count = 0; num_log_args_calls = 0; auto cfg = logging::mipi::config{ - test_log_args_destination{}, - test_log_args_destination{}}; - cfg.logger.log_msg( - format("{} {}"_sc, 17u, 18u)); + test_log_args_destination{}, + test_log_args_destination{}}; + + cfg.logger.log_msg(format("{} {}"_sc, 17u, 18u)); CHECK(test_critical_section::count == 4); CHECK(num_log_args_calls == 2); } @@ -230,3 +227,13 @@ TEST_CASE("log version information (long with string)", "[mipi]") { CHECK(test_critical_section::count == 2); CHECK(num_log_args_calls == 1); } + +template <> +inline auto logging::config<> = logging::mipi::config{ + test_log_args_destination{}}; + +TEST_CASE("injection", "[mipi]") { + test_critical_section::count = 0; + CIB_TRACE("Hello"); + CHECK(test_critical_section::count == 2); +}