|
1 | 1 | #pragma once |
2 | 2 |
|
3 | 3 | #include <string> |
4 | | -#include <string_view> |
5 | | -#include <unordered_map> |
6 | | -#include <utility> |
7 | | -#include <variant> |
8 | 4 |
|
9 | 5 | #include <spdlog/spdlog.h> |
10 | 6 |
|
11 | | -#include "print_utils.h" |
| 7 | +#include "print_utils.h" // any translation unit that needs logging probably also wants pretty-printing |
| 8 | +#include "utils.h" |
12 | 9 |
|
13 | | -#define CELERITY_LOG_SET_SCOPED_CTX(ctx) CELERITY_DETAIL_LOG_SET_SCOPED_CTX(ctx) |
14 | 10 |
|
15 | 11 | #define CELERITY_LOG(level, ...) \ |
16 | 12 | (::spdlog::should_log(level) \ |
17 | | - ? SPDLOG_LOGGER_CALL(::spdlog::default_logger_raw(), level, "{}{}", *::celerity::detail::active_log_ctx, ::fmt::format(__VA_ARGS__)) \ |
| 13 | + ? SPDLOG_LOGGER_CALL(::spdlog::default_logger_raw(), level, "{}{}", ::celerity::detail::active_log_ctx->repr, ::fmt::format(__VA_ARGS__)) \ |
18 | 14 | : (void)0) |
19 | 15 |
|
20 | 16 | // TODO Add a macro similar to SPDLOG_ACTIVE_LEVEL, configurable through CMake |
|
25 | 21 | #define CELERITY_ERROR(...) CELERITY_LOG(::celerity::detail::log_level::err, __VA_ARGS__) |
26 | 22 | #define CELERITY_CRITICAL(...) CELERITY_LOG(::celerity::detail::log_level::critical, __VA_ARGS__) |
27 | 23 |
|
28 | | -namespace celerity { |
29 | | -namespace detail { |
| 24 | +namespace celerity::detail { |
30 | 25 |
|
31 | | - using log_level = spdlog::level::level_enum; |
| 26 | +using log_level = spdlog::level::level_enum; |
32 | 27 |
|
33 | | - template <typename... Es> |
34 | | - struct log_map { |
35 | | - const std::tuple<Es...>& entries; |
36 | | - log_map(const std::tuple<Es...>& entries) : entries(entries) {} |
37 | | - }; |
38 | | - |
39 | | - struct log_context { |
40 | | - std::string value; |
41 | | - log_context() = default; |
42 | | - template <typename... Es> |
43 | | - explicit log_context(const std::tuple<Es...>& entries) { |
44 | | - static_assert(sizeof...(Es) % 2 == 0, "log_context requires key/value pairs"); |
45 | | - value = fmt::format("[{}] ", log_map{entries}); |
46 | | - } |
47 | | - }; |
48 | | - |
49 | | - inline const std::string null_log_ctx; |
50 | | - inline thread_local const std::string* active_log_ctx = &null_log_ctx; |
| 28 | +struct log_context { |
| 29 | + std::string repr; |
51 | 30 |
|
52 | | - struct log_ctx_setter { |
53 | | - log_ctx_setter(log_context& ctx) { celerity::detail::active_log_ctx = &ctx.value; } |
54 | | - ~log_ctx_setter() { celerity::detail::active_log_ctx = &celerity::detail::null_log_ctx; } |
55 | | - }; |
| 31 | + log_context() = default; |
56 | 32 |
|
57 | | -#define CELERITY_DETAIL_LOG_SET_SCOPED_CTX(ctx) \ |
58 | | - log_ctx_setter _set_log_ctx_##__COUNTER__ { ctx } |
59 | | - |
60 | | - template <typename Tuple, typename Callback> |
61 | | - constexpr void tuple_for_each_pair_impl(const Tuple&, Callback&&, std::index_sequence<>) {} |
62 | | - |
63 | | - template <typename Tuple, size_t I1, size_t I2, size_t... Is, typename Callback> |
64 | | - constexpr void tuple_for_each_pair_impl(const Tuple& tuple, const Callback& cb, std::index_sequence<I1, I2, Is...>) { |
65 | | - cb(std::get<I1>(tuple), std::get<I2>(tuple)); |
66 | | - tuple_for_each_pair_impl(tuple, cb, std::index_sequence<Is...>{}); |
67 | | - } |
68 | | - |
69 | | - template <typename Tuple, typename Callback> |
70 | | - constexpr void tuple_for_each_pair(const Tuple& tuple, const Callback& cb) { |
71 | | - static_assert(std::tuple_size_v<Tuple> % 2 == 0, "an even number of entries is required"); |
72 | | - tuple_for_each_pair_impl(tuple, cb, std::make_index_sequence<std::tuple_size_v<Tuple>>{}); |
| 33 | + template <typename... Es> |
| 34 | + explicit log_context(const std::tuple<Es...>& entries) { |
| 35 | + static_assert(sizeof...(Es) % 2 == 0, "log_context requires key/value pairs"); |
| 36 | + if constexpr(sizeof...(Es) > 0) { |
| 37 | + repr += "["; |
| 38 | + int i = 0; |
| 39 | + celerity::detail::utils::tuple_for_each_pair(entries, [&](const auto& a, const auto& b) { |
| 40 | + if(i++ > 0) { repr += ", "; } |
| 41 | + fmt::format_to(std::back_inserter(repr), "{}={}", a, b); |
| 42 | + }); |
| 43 | + repr += "] "; |
| 44 | + } |
73 | 45 | } |
| 46 | +}; |
74 | 47 |
|
75 | | -} // namespace detail |
76 | | -} // namespace celerity |
| 48 | +inline const log_context null_log_ctx; |
| 49 | +inline thread_local const log_context* active_log_ctx = &null_log_ctx; |
77 | 50 |
|
78 | | -template <typename... Es> |
79 | | -struct fmt::formatter<celerity::detail::log_map<Es...>> { |
80 | | - constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } |
| 51 | +class set_log_context_guard { |
| 52 | + public: |
| 53 | + set_log_context_guard(const log_context& ctx) : m_ctx_before(celerity::detail::active_log_ctx) { celerity::detail::active_log_ctx = &ctx; } |
| 54 | + set_log_context_guard(const set_log_context_guard&) = delete; |
| 55 | + set_log_context_guard(set_log_context_guard&&) = delete; |
| 56 | + set_log_context_guard& operator=(const set_log_context_guard&) = delete; |
| 57 | + set_log_context_guard& operator=(set_log_context_guard&&) = delete; |
| 58 | + ~set_log_context_guard() { celerity::detail::active_log_ctx = m_ctx_before; } |
81 | 59 |
|
82 | | - template <typename FormatContext> |
83 | | - auto format(const celerity::detail::log_map<Es...>& map, FormatContext& ctx) { |
84 | | - auto&& out = ctx.out(); |
85 | | - int i = 0; |
86 | | - tuple_for_each_pair(map.entries, [&i, &out](auto& a, auto& b) { |
87 | | - if(i++ > 0) { fmt::format_to(out, ", "); } |
88 | | - fmt::format_to(out, "{}={}", a, b); |
89 | | - }); |
90 | | - return out; |
91 | | - } |
| 60 | + private: |
| 61 | + const log_context* m_ctx_before; |
92 | 62 | }; |
| 63 | + |
| 64 | +} // namespace celerity::detail |
0 commit comments