From e7bedfc3398adefe6d7eda6f8d6f464d35bc0185 Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Tue, 13 May 2025 21:47:55 -0600 Subject: [PATCH] :sparkles: Add fixed string IDs to logs Problem: - Some hardware expects certain string IDs. When logging, it would be useful to be able to fix string IDs in code and have the tooling understand that. Solution: - Add the ability to specify string IDs in the logging environment. --- include/log/catalog/catalog.hpp | 2 +- include/log/catalog/encoder.hpp | 16 ++++++++++------ include/log/string_id.hpp | 19 +++++++++++++++++++ test/log/catalog1_lib.cpp | 10 ++++++++++ test/log/catalog_app.cpp | 11 +++++++++++ tools/gen_str_catalog.py | 21 ++++++++++++++++----- 6 files changed, 67 insertions(+), 12 deletions(-) create mode 100644 include/log/string_id.hpp diff --git a/include/log/catalog/catalog.hpp b/include/log/catalog/catalog.hpp index 87703fe6..8f47f333 100644 --- a/include/log/catalog/catalog.hpp +++ b/include/log/catalog/catalog.hpp @@ -4,7 +4,7 @@ namespace sc { template struct args; -template struct undefined; +template struct undefined; template struct message {}; template struct module_string {}; diff --git a/include/log/catalog/encoder.hpp b/include/log/catalog/encoder.hpp index 2a49b64b..eeac89ee 100644 --- a/include/log/catalog/encoder.hpp +++ b/include/log/catalog/encoder.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -20,24 +21,26 @@ namespace logging::binary { namespace detail { -template constexpr static auto to_message() { +template +constexpr static auto to_message() { constexpr auto s = std::string_view{S::value}; using char_t = typename std::remove_cv_t::value_type; return [&](std::integer_sequence) { return sc::message< - sc::undefined, char_t, s[Is]...>>{}; + sc::undefined, Id, char_t, s[Is]...>>{}; }(std::make_integer_sequence{}); } template constexpr static auto to_module() { constexpr auto s = std::string_view{S}; return [&](std::integer_sequence) { - return sc::module_string>{}; + return sc::module_string>{}; }(std::make_integer_sequence{}); } -template struct to_message_t { - template using fn = decltype(to_message()); +template struct to_message_t { + template + using fn = decltype(to_message()); }; } // namespace detail @@ -85,7 +88,8 @@ template struct log_handler { auto builder = get_builder(Env{}); constexpr auto L = stdx::to_underlying(get_level(Env{})); using Message = typename decltype(builder)::template convert_args< - detail::to_message_t::template fn, + detail::to_message_t::template fn, std::remove_cvref_t...>; using Module = decltype(detail::to_module()); w(builder.template build(catalog(), module(), diff --git a/include/log/string_id.hpp b/include/log/string_id.hpp new file mode 100644 index 00000000..c1e284c9 --- /dev/null +++ b/include/log/string_id.hpp @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include + +namespace logging { +[[maybe_unused]] constexpr inline struct get_string_id_t { + template + requires true // more constrained + 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); + } + + CONSTEVAL auto operator()(auto &&) const -> int { return -1; } +} get_string_id; +} // namespace logging diff --git a/test/log/catalog1_lib.cpp b/test/log/catalog1_lib.cpp index 6447f2d0..8048a68d 100644 --- a/test/log/catalog1_lib.cpp +++ b/test/log/catalog1_lib.cpp @@ -32,6 +32,7 @@ auto log_one_64bit_rt_arg() -> void; auto log_one_formatted_rt_arg() -> void; auto log_with_non_default_module_id() -> void; auto log_with_fixed_module_id() -> void; +auto log_with_fixed_string_id() -> void; auto log_zero_args() -> void { auto cfg = logging::binary::config{test_log_args_destination{}}; @@ -81,3 +82,12 @@ auto log_with_fixed_module_id() -> void { stdx::ct_format<"Fixed ModuleID string with {} placeholder">(1)); } } + +auto log_with_fixed_string_id() -> void { + CIB_WITH_LOG_ENV(logging::get_level, logging::level::TRACE, + logging::get_string_id, 1337) { + auto cfg = logging::binary::config{test_log_args_destination{}}; + cfg.logger.log_msg( + stdx::ct_format<"Fixed StringID string">()); + } +} diff --git a/test/log/catalog_app.cpp b/test/log/catalog_app.cpp index 105d9dc9..2ec740b1 100644 --- a/test/log/catalog_app.cpp +++ b/test/log/catalog_app.cpp @@ -19,6 +19,7 @@ extern auto log_two_rt_args() -> void; extern auto log_rt_enum_arg() -> void; extern auto log_with_non_default_module_id() -> void; extern auto log_with_fixed_module_id() -> void; +extern auto log_with_fixed_string_id() -> void; TEST_CASE("log zero arguments", "[catalog]") { test_critical_section::count = 0; @@ -99,3 +100,13 @@ TEST_CASE("log with fixed module id", "[catalog]") { CHECK((last_header & expected_static) == expected_static); CHECK((last_header & ~expected_static) == (17u << 16u)); } + +TEST_CASE("log with fixed string id", "[catalog]") { + test_critical_section::count = 0; + log_calls = 0; + log_with_fixed_string_id(); + CHECK(test_critical_section::count == 2); + CHECK(log_calls == 1); + // string ID 1337 is fixed by environment + CHECK(last_header == ((1337u << 4u) | 1u)); +} diff --git a/tools/gen_str_catalog.py b/tools/gen_str_catalog.py index 485e667c..aa114f04 100755 --- a/tools/gen_str_catalog.py +++ b/tools/gen_str_catalog.py @@ -32,7 +32,7 @@ def split_args(s: str) -> list[str]: string_re = re.compile( - r"sc::message, char, (.*)>\s*>" + r"sc::message, (.*), char, (.*)>\s*>" ) @@ -40,7 +40,8 @@ def extract_string_id(line_m): catalog_type = line_m.group(1) string_m = string_re.match(line_m.group(3)) arg_tuple = string_m.group(1) - string_tuple = string_m.group(2).replace("(char)", "") + string_id = int(string_m.group(2)) + string_tuple = string_m.group(3).replace("(char)", "") string_value = "".join((chr(int(c)) for c in re.split(r"\s*,\s*", string_tuple))) args = split_args(arg_tuple) @@ -51,11 +52,12 @@ def extract_string_id(line_m): type="flow" if string_value.startswith("flow.") else "msg", arg_types=args, arg_count=len(args), + id=string_id ), ) -module_re = re.compile(r"sc::module_string\s?>") +module_re = re.compile(r"sc::module_string\s?>") def module_string(module) -> str: @@ -110,8 +112,18 @@ def get_id(stable_ids, key_fn, gen, obj): else: return next(gen) + unique_strings = {i[0][0]: i for i in strings}.values() + stable_msg_ids, stable_module_ids = stable_ids + for _, msg in filter(lambda u: u[1]["id"] != -1, unique_strings): + stable_msg_ids[stable_msg_key(msg)] = msg["id"] + + if len(stable_msg_ids) != len(set(stable_msg_ids.values())): + import collections + dupes = [item for item, count in collections.Counter(stable_msg_ids.values()).items() if count > 1] + raise Exception(f"Duplicate string IDs found: {dupes}.") + old_msg_ids = set(stable_msg_ids.values()) msg_id_gen = itertools.filterfalse(old_msg_ids.__contains__, itertools.count(0)) get_msg_id = partial(get_id, stable_msg_ids, stable_msg_key, msg_id_gen) @@ -122,7 +134,6 @@ def get_id(stable_ids, key_fn, gen, obj): ) get_module_id = partial(get_id, stable_module_ids, stable_module_key, module_id_gen) - unique_strings = {i[0][0]: i for i in strings}.values() return ( {m: {"string": module_string(m), "id": get_module_id(m)} for m in sorted(set(modules))}, {item[0]: {**item[1], "id": get_msg_id(item[1])} for item in unique_strings}, @@ -144,7 +155,7 @@ def make_cpp_module_defn(m: str, text: str, n: int) -> str: return f"""/* "{text}" */ -template<> unsigned int module>>() {{ +template<> unsigned int module>>() {{ return {n}; }}"""