Skip to content

Commit 4dbdb14

Browse files
committed
🎨 Allow logging to work with custom levels
Problem: - Many logging backends have varying ideas of what a log level is. The MIPI-SysT spec defines two user-defined values. It would be nice to use a strongly-typed enumeration in application code that can re-spell the `USER1` and `USER2` values. Solution: - Make logging level-agnostic. - The MIPI-SysT levels are provided for default use, but they aren't required.
1 parent 8eae4de commit 4dbdb14

File tree

4 files changed

+81
-12
lines changed

4 files changed

+81
-12
lines changed

include/log/catalog/mipi_encoder.hpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,13 @@
2020

2121
namespace logging::mipi {
2222
namespace detail {
23-
template <logging::level L, typename S, typename... Args>
24-
constexpr auto to_message() {
23+
template <auto L, typename S, typename... Args> constexpr auto to_message() {
2524
constexpr auto s = S::value;
2625
using char_t = typename std::remove_cv_t<decltype(s)>::value_type;
2726
return [&]<std::size_t... Is>(std::integer_sequence<std::size_t, Is...>) {
2827
return sc::message<
29-
L, sc::undefined<sc::args<Args...>, char_t, s[Is]...>>{};
28+
static_cast<logging::level>(L),
29+
sc::undefined<sc::args<Args...>, char_t, s[Is]...>>{};
3030
}(std::make_integer_sequence<std::size_t, std::size(s)>{});
3131
}
3232

@@ -113,7 +113,7 @@ template <typename TDestinations> struct log_handler {
113113
template <typename Env, typename Msg>
114114
ALWAYS_INLINE auto log_msg(Msg msg) -> void {
115115
msg.apply([&]<typename S, typename... Args>(S, Args... args) {
116-
constexpr auto L = get_level(Env{}).value;
116+
constexpr auto L = stdx::to_underlying(get_level(Env{}).value);
117117
using Message = decltype(detail::to_message<L, S, Args...>());
118118
using Module =
119119
decltype(detail::to_module<get_module(Env{}).value>());
@@ -174,7 +174,7 @@ template <typename TDestinations> struct log_handler {
174174
dests);
175175
}
176176

177-
template <logging::level Level, std::same_as<std::uint32_t>... MsgDataTypes>
177+
template <auto Level, std::same_as<std::uint32_t>... MsgDataTypes>
178178
ALWAYS_INLINE auto dispatch_message(string_id id,
179179
[[maybe_unused]] module_id m,
180180
MsgDataTypes... msg_data) -> void {

test/log/CMakeLists.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
add_tests(FILES log module_id env LIBRARIES cib_log)
1+
add_tests(
2+
FILES
3+
level
4+
log
5+
module_id
6+
env
7+
LIBRARIES
8+
cib_log)
29
add_tests(FILES fmt_logger LIBRARIES cib_log_fmt)
310
add_tests(FILES mipi_encoder mipi_logger LIBRARIES cib_log_mipi)
411

test/log/level.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
#include <log/catalog/mipi_encoder.hpp>
2+
#include <log/fmt/logger.hpp>
3+
#include <log/level.hpp>
4+
#include <sc/format.hpp>
5+
6+
#include <stdx/ct_conversions.hpp>
7+
#include <stdx/ct_string.hpp>
8+
9+
#include <catch2/catch_test_macros.hpp>
10+
11+
#include <cstdint>
12+
#include <iterator>
13+
#include <string>
14+
#include <type_traits>
15+
16+
namespace {
17+
enum struct custom_level { THE_ONE_LEVEL = 5 };
18+
19+
std::string buffer{};
20+
} // namespace
21+
22+
template <>
23+
inline auto logging::config<> =
24+
logging::fmt::config{std::back_inserter(buffer)};
25+
26+
template <custom_level L>
27+
struct fmt::formatter<std::integral_constant<custom_level, L>> {
28+
constexpr static auto parse(format_parse_context &ctx) {
29+
return ctx.begin();
30+
}
31+
32+
template <typename FormatContext>
33+
auto format(std::integral_constant<custom_level, L>,
34+
FormatContext &ctx) const {
35+
return ::fmt::format_to(ctx.out(), stdx::enum_as_string<L>());
36+
}
37+
};
38+
39+
TEST_CASE("fmt logger works with custom level", "[level]") {
40+
CIB_LOG_ENV(logging::get_level, custom_level::THE_ONE_LEVEL);
41+
buffer.clear();
42+
CIB_LOG(logging::default_flavor_t, "Hello");
43+
CAPTURE(buffer);
44+
CHECK(buffer.find("THE_ONE_LEVEL [default]:") != std::string::npos);
45+
}
46+
47+
template <typename> auto catalog() -> string_id { return 0xdeadbeef; }
48+
template <typename> auto module() -> module_id { return 0x5a; }
49+
50+
namespace {
51+
int log_calls{};
52+
53+
struct test_destination {
54+
auto log_by_args(std::uint32_t header, auto id, auto &&...) {
55+
CHECK(header == 0x01'5a'00'53);
56+
CHECK(id == 0xdeadbeef);
57+
++log_calls;
58+
}
59+
};
60+
} // namespace
61+
62+
TEST_CASE("mipi logger works with custom level", "[level]") {
63+
log_calls = 0;
64+
CIB_LOG_ENV(logging::get_level, custom_level::THE_ONE_LEVEL);
65+
auto cfg = logging::mipi::config{test_destination{}};
66+
cfg.logger.log_msg<cib_log_env_t>(sc::format("Hello {} {}"_sc, 17, 42));
67+
CHECK(log_calls == 1);
68+
}

test/log/mipi_encoder.cpp

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -61,12 +61,6 @@ expected_msg_header(logging::level level, module_id m,
6161
: expected_short32_header();
6262
}
6363

64-
template <auto ExpectedId> struct test_log_id_destination {
65-
template <typename Id> static auto log_by_args(Id id) {
66-
CHECK(id == ((ExpectedId << 4u) | 1u));
67-
}
68-
};
69-
7064
int num_log_args_calls{};
7165

7266
constexpr auto check = [](auto value, auto expected) {

0 commit comments

Comments
 (0)