Skip to content

Commit a7a8034

Browse files
authored
Merge pull request #584 from elbeno/coloured-logs
2 parents 1736e1c + b45158b commit a7a8034

File tree

4 files changed

+134
-27
lines changed

4 files changed

+134
-27
lines changed

docs/logging.adoc

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@ CIB_ERROR(...);
4848
CIB_FATAL(...);
4949
----
5050

51-
Or `CIB_LOG(level, ...)` can be used equivalently to these where the level is
52-
explicit. `CIB_FATAL` causes a call to
51+
`CIB_FATAL` causes a call to
5352
https://intel.github.io/cpp-std-extensions/#_panic_hpp[`stdx::panic`], and
5453
`CIB_ASSERT(expression)` is equivalent to `CIB_FATAL` in the case where the
5554
expression evaluates to `false`.
@@ -132,6 +131,30 @@ template <>
132131
inline auto logging::config<> = my_logger_config{};
133132
----
134133

134+
=== Flavored logs
135+
136+
There is not always just one logging backend in an application. For example, you
137+
might want regular logs and secure logs. Providing more backends is possible by specializing
138+
`logging::config` with custom types.
139+
140+
[source,cpp]
141+
----
142+
struct secure_tag;
143+
144+
template <>
145+
inline auto logging::config<secure_tag> = my_logger_config{};
146+
----
147+
148+
And this backend can be most easily used by defining macros in terms of the
149+
`CIB_LOG` macro:
150+
151+
[source,cpp]
152+
----
153+
#define SECURE_TRACE(...) CIB_LOG(secure_tag, logging::level::TRACE, __VA_ARGS__)
154+
#define SECURE_INFO(...) CIB_LOG(secure_tag, logging::level::INFO, __VA_ARGS__)
155+
// etc
156+
----
157+
135158
=== Modules
136159

137160
It can be helpful to scope or filter log messages by associating them with
@@ -170,3 +193,46 @@ struct my_struct {
170193
On a constrained system, space for text can be at a premium. The `sc` library
171194
and the MIPI Sys-T logger combine to
172195
xref:sc.adoc#_efficient_logging_with_mipi_sys_t[solve this problem].
196+
197+
=== Version logging
198+
199+
To provide version information in a log, specialize the `version::config`
200+
variable template. The configuration should provide a `build_id` and a
201+
`version_string`.
202+
203+
[source,cpp]
204+
----
205+
struct my_version_config {
206+
constexpr static auto build_id = std::uint64_t{1234};
207+
constexpr static auto version_string = stdx::ct_string{"version"};
208+
};
209+
210+
template <> inline auto version::config<> = my_version_config{};
211+
----
212+
213+
Then use `CIB_LOG_VERSION()` to log the version. If the logging config provides
214+
a `log_build` function, that will be used. Otherwise a text string will be
215+
logged.
216+
217+
[source,cpp]
218+
----
219+
struct my_logger_config {
220+
struct {
221+
template <auto Version, stdx::ct_string S = ""> auto log_build() -> void {
222+
// log the build version according to my mechanism
223+
}
224+
} logger;
225+
};
226+
template <>
227+
inline auto logging::config<> = my_logger_config{};
228+
229+
CIB_LOG_VERSION(); // calls my_logger_config::log_build
230+
----
231+
232+
The easiest way to flavor the version logging is to define a macro in terms of
233+
`CIB_LOG_V`:
234+
235+
[source,cpp]
236+
----
237+
#define LOG_SECURE_VERSION(...) CIB_LOG_V(secure_tag)
238+
----

include/log/log.hpp

Lines changed: 37 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,21 @@ concept loggable = requires(T const &t) {
3737
t.apply([]<typename StringType>(StringType, auto const &...) {});
3838
};
3939

40-
template <level L, typename ModuleId, typename... Ts, typename... TArgs>
40+
struct default_flavor_t;
41+
42+
template <typename Flavor, typename... Ts>
43+
constexpr static auto get_config() -> auto & {
44+
if constexpr (std::same_as<Flavor, default_flavor_t>) {
45+
return config<Ts...>;
46+
} else {
47+
return config<Flavor, Ts...>;
48+
}
49+
}
50+
51+
template <typename Flavor, level L, typename ModuleId, typename... Ts,
52+
typename... TArgs>
4153
static auto log(TArgs &&...args) -> void {
42-
auto &cfg = config<Ts...>;
54+
auto &cfg = get_config<Flavor, Ts...>();
4355
cfg.logger.template log<L, ModuleId>(std::forward<TArgs>(args)...);
4456
}
4557

@@ -66,35 +78,43 @@ using cib_log_module_id_t = typename logging::module_id_t<"default">::type;
6678
typename logging::module_id_t<S>::type CIB_PRAGMA_SEMI CIB_PRAGMA( \
6779
diagnostic pop)
6880

69-
#define CIB_LOG(LEVEL, MSG, ...) \
70-
logging::log<LEVEL, cib_log_module_id_t>( \
71-
__FILE__, __LINE__, sc::formatter{MSG##_sc}(__VA_ARGS__))
72-
73-
#define CIB_TRACE(...) CIB_LOG(logging::level::TRACE, __VA_ARGS__)
74-
#define CIB_INFO(...) CIB_LOG(logging::level::INFO, __VA_ARGS__)
75-
#define CIB_WARN(...) CIB_LOG(logging::level::WARN, __VA_ARGS__)
76-
#define CIB_ERROR(...) CIB_LOG(logging::level::ERROR, __VA_ARGS__)
81+
#define CIB_LOG(FLAVOR, LEVEL, MSG, ...) \
82+
logging::log<FLAVOR, LEVEL, cib_log_module_id_t>( \
83+
__FILE__, __LINE__, sc::format(MSG##_sc __VA_OPT__(, ) __VA_ARGS__))
84+
85+
#define CIB_TRACE(...) \
86+
CIB_LOG(logging::default_flavor_t, logging::level::TRACE, __VA_ARGS__)
87+
#define CIB_INFO(...) \
88+
CIB_LOG(logging::default_flavor_t, logging::level::INFO, __VA_ARGS__)
89+
#define CIB_WARN(...) \
90+
CIB_LOG(logging::default_flavor_t, logging::level::WARN, __VA_ARGS__)
91+
#define CIB_ERROR(...) \
92+
CIB_LOG(logging::default_flavor_t, logging::level::ERROR, __VA_ARGS__)
7793
#define CIB_FATAL(...) \
78-
(CIB_LOG(logging::level::FATAL, __VA_ARGS__), STDX_PANIC(__VA_ARGS__))
94+
(CIB_LOG(logging::default_flavor_t, logging::level::FATAL, __VA_ARGS__), \
95+
STDX_PANIC(__VA_ARGS__))
7996

8097
#define CIB_ASSERT(expr) \
8198
((expr) ? void(0) : CIB_FATAL("Assertion failure: " #expr))
8299

83100
namespace logging {
84-
template <typename... Ts> static auto log_version() -> void {
85-
auto &l_cfg = config<Ts...>;
101+
template <typename Flavor, typename... Ts> static auto log_version() -> void {
102+
auto &l_cfg = get_config<Flavor, Ts...>();
86103
auto &v_cfg = ::version::config<Ts...>;
87104
if constexpr (requires {
88105
l_cfg.logger.template log_build<v_cfg.build_id,
89106
v_cfg.version_string>();
90107
}) {
91108
l_cfg.logger.template log_build<v_cfg.build_id, v_cfg.version_string>();
92109
} else {
93-
CIB_LOG(level::MAX, "Version: {} ({})", sc::uint_<v_cfg.build_id>,
94-
stdx::ct_string_to_type<v_cfg.version_string,
95-
sc::string_constant>());
110+
l_cfg.logger.template log<level::MAX, cib_log_module_id_t>(
111+
"", 0,
112+
sc::format("Version: {} ({})"_sc, sc::uint_<v_cfg.build_id>,
113+
stdx::ct_string_to_type<v_cfg.version_string,
114+
sc::string_constant>()));
96115
}
97116
}
98117
} // namespace logging
99118

100-
#define CIB_LOG_VERSION() logging::log_version()
119+
#define CIB_LOG_V(FLAVOR) logging::log_version<FLAVOR>()
120+
#define CIB_LOG_VERSION() CIB_LOG_V(logging::default_flavor_t)

include/sc/format.hpp

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,4 @@ constexpr auto format(Fmt, Args... args) {
100100
return lazy_string_format{r[0_idx] + r[1_idx], r[2_idx]};
101101
}
102102
}
103-
104-
template <typename T> struct formatter {
105-
constexpr explicit formatter(T) {}
106-
107-
template <typename... Ts> constexpr auto operator()(Ts &&...args) {
108-
return format(T{}, std::forward<Ts>(args)...);
109-
}
110-
};
111103
} // namespace sc

test/log/fmt_logger.cpp

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,3 +150,32 @@ TEST_CASE("log version", "[fmt_logger]") {
150150
CHECK(buffer.find("MAX [default]: Version: 1234 (test version)") !=
151151
std::string::npos);
152152
}
153+
154+
namespace {
155+
struct secure_t;
156+
std::string secure_buffer{};
157+
} // namespace
158+
159+
template <>
160+
inline auto logging::config<secure_t> =
161+
logging::fmt::config{std::back_inserter(secure_buffer)};
162+
163+
TEST_CASE("logging can be flavored", "[fmt_logger]") {
164+
buffer.clear();
165+
secure_buffer.clear();
166+
CIB_LOG(secure_t, logging::level::TRACE, "Hello");
167+
CAPTURE(secure_buffer);
168+
CHECK(secure_buffer.substr(secure_buffer.size() - std::size("Hello")) ==
169+
"Hello\n");
170+
CHECK(buffer.empty());
171+
}
172+
173+
TEST_CASE("log version can be flavored", "[fmt_logger]") {
174+
buffer.clear();
175+
secure_buffer.clear();
176+
CIB_LOG_V(secure_t);
177+
CAPTURE(secure_buffer);
178+
CHECK(secure_buffer.find("MAX [default]: Version: 1234 (test version)") !=
179+
std::string::npos);
180+
CHECK(buffer.empty());
181+
}

0 commit comments

Comments
 (0)