rename chapters
This commit is contained in:
95
Chapter17/cib/libs/log/README.md
Normal file
95
Chapter17/cib/libs/log/README.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# logging
|
||||
|
||||
Logging in *cib* is in two parts:
|
||||
- the interface, in [log.hpp](log.hpp)
|
||||
- an implementation, which can be specified at the top level
|
||||
|
||||
Three possible logger implementations are provided:
|
||||
- one using libfmt in [fmt/logger.hpp](fmt/logger.hpp)
|
||||
- one using the [MIPI SyS-T spec](https://www.mipi.org/specifications/sys-t), in [catalog/mipi_encoder.hpp](catalog/mipi_encoder.hpp)
|
||||
- the null logger (accepts everything, never produces output)
|
||||
|
||||
## log levels
|
||||
|
||||
*cib* offers 6 well-known and 2 user-defined log levels, according to the [MIPI SyS-T spec](https://www.mipi.org/specifications/sys-t).
|
||||
```c++
|
||||
namespace logging {
|
||||
enum level {
|
||||
MAX = 0,
|
||||
FATAL = 1,
|
||||
ERROR = 2,
|
||||
WARN = 3,
|
||||
INFO = 4,
|
||||
USER1 = 5,
|
||||
USER2 = 6,
|
||||
TRACE = 7
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## interface
|
||||
|
||||
*cib* logging macros follow the log levels available:
|
||||
```cpp
|
||||
CIB_TRACE(...);
|
||||
CIB_INFO(...);
|
||||
CIB_WARN(...);
|
||||
CIB_ERROR(...);
|
||||
CIB_FATAL(...);
|
||||
```
|
||||
These are the same as calling `CIB_LOG` with the `logging::level` as the first argument.
|
||||
|
||||
`CIB_FATAL(...)` also causes a program termination according to the logging implementation.
|
||||
`CIB_ASSERT(expression)` is also available and calls `CIB_FATAL` if the asserted expression is false.
|
||||
|
||||
In order to use logging in a header, it suffices only to include
|
||||
[log.hpp](log.hpp) and use the macros. Header-only clients of logging do not
|
||||
need to know the implementation selected.
|
||||
|
||||
To use logging in a translation unit, the TU needs to see a customization, which brings us to...
|
||||
|
||||
## selecting a logger
|
||||
|
||||
Programs can choose which logger to use by specializing the `logging::config` variable template.
|
||||
Left unspecialized, the null logger will be used.
|
||||
|
||||
```cpp
|
||||
// use libfmt logging to std::cout
|
||||
template <>
|
||||
inline auto logging::config<> = logging::fmt::config{std::ostream_iterator<char>{std::cout}};
|
||||
```
|
||||
|
||||
The provided `libfmt` implementation can output to multiple destinations by constructing
|
||||
`logging::fmt::config` with multiple `ostream` iterators.
|
||||
|
||||
***NOTE:*** Be sure that each translation unit sees the same specialization of
|
||||
`logging::config<>`! Otherwise you will have an [ODR](https://en.cppreference.com/w/cpp/language/definition) violation.
|
||||
|
||||
## implementing a logger
|
||||
|
||||
Each logging implementation (configuration) provides a customization point: a
|
||||
`logger` object, which must implement `log`.
|
||||
Therefore providing a custom implementation is a matter of defining this
|
||||
structure appropriately.
|
||||
|
||||
```cpp
|
||||
namespace my_logger {
|
||||
struct config {
|
||||
struct {
|
||||
template <logging::level L, typename... Ts>
|
||||
auto log(Ts &&...ts) -> void {
|
||||
// log the ts... according to my mechanism
|
||||
}
|
||||
} logger;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
To use the custom implementation, specialize `logging::config`:
|
||||
```cpp
|
||||
// use my logger
|
||||
template <>
|
||||
inline auto logging::config<> = my_logger::config{};
|
||||
```
|
||||
|
||||
*cib* uses a libfmt-inspired convention for logging: the first argument to `log` is a format string with `{}` as format placeholders.
|
||||
19
Chapter17/cib/libs/log/catalog/catalog.hpp
Normal file
19
Chapter17/cib/libs/log/catalog/catalog.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include <log/level.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace sc {
|
||||
template <typename...> struct args;
|
||||
template <typename, typename T, T...> struct undefined;
|
||||
|
||||
template <logging::level, typename> struct message {};
|
||||
template <typename> struct module_string {};
|
||||
} // namespace sc
|
||||
|
||||
using string_id = std::uint32_t;
|
||||
using module_id = std::uint32_t;
|
||||
|
||||
template <typename> extern auto catalog() -> string_id;
|
||||
template <typename> extern auto module() -> module_id;
|
||||
224
Chapter17/cib/libs/log/catalog/mipi_encoder.hpp
Normal file
224
Chapter17/cib/libs/log/catalog/mipi_encoder.hpp
Normal file
@@ -0,0 +1,224 @@
|
||||
#pragma once
|
||||
|
||||
#include <conc/concurrency.hpp>
|
||||
#include <log/catalog/catalog.hpp>
|
||||
#include <log/log.hpp>
|
||||
#include <log/module.hpp>
|
||||
#include <msg/message.hpp>
|
||||
|
||||
#include <stdx/bit.hpp>
|
||||
#include <stdx/compiler.hpp>
|
||||
#include <stdx/ct_string.hpp>
|
||||
#include <stdx/tuple.hpp>
|
||||
#include <stdx/utility.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <concepts>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
|
||||
namespace logging::mipi {
|
||||
namespace detail {
|
||||
template <auto L, typename S, typename... Args> constexpr auto to_message() {
|
||||
constexpr auto s = S::value;
|
||||
using char_t = typename std::remove_cv_t<decltype(s)>::value_type;
|
||||
return [&]<std::size_t... Is>(std::integer_sequence<std::size_t, Is...>) {
|
||||
return sc::message<
|
||||
static_cast<logging::level>(L),
|
||||
sc::undefined<sc::args<Args...>, char_t, s[Is]...>>{};
|
||||
}(std::make_integer_sequence<std::size_t, std::size(s)>{});
|
||||
}
|
||||
|
||||
template <stdx::ct_string S> constexpr auto to_module() {
|
||||
constexpr auto s = std::string_view{S};
|
||||
return [&]<std::size_t... Is>(std::integer_sequence<std::size_t, Is...>) {
|
||||
return sc::module_string<sc::undefined<void, char, s[Is]...>>{};
|
||||
}(std::make_integer_sequence<std::size_t, std::size(s)>{});
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
namespace defn {
|
||||
using msg::at;
|
||||
using msg::dword_index_t;
|
||||
using msg::field;
|
||||
using msg::message;
|
||||
using msg::operator""_msb;
|
||||
using msg::operator""_lsb;
|
||||
|
||||
enum struct type : uint8_t { Build = 0, Short32 = 1, Catalog = 3 };
|
||||
enum struct build_subtype : uint8_t { Compact32 = 0, Compact64 = 1, Long = 2 };
|
||||
enum struct catalog_subtype : uint8_t { Id32_Pack32 = 1 };
|
||||
|
||||
using type_f = field<"type", type>::located<at{dword_index_t{0}, 3_msb, 0_lsb}>;
|
||||
using opt_len_f =
|
||||
field<"opt_len", bool>::located<at{dword_index_t{0}, 9_msb, 9_lsb}>;
|
||||
using payload_len_f =
|
||||
field<"payload_len",
|
||||
std::uint16_t>::located<at{dword_index_t{1}, 15_msb, 0_lsb}>;
|
||||
|
||||
using build_subtype_f =
|
||||
field<"subtype",
|
||||
build_subtype>::located<at{dword_index_t{0}, 29_msb, 24_lsb}>;
|
||||
using compact32_build_id_f = field<"build_id", std::uint32_t>::located<
|
||||
at{dword_index_t{0}, 31_msb, 30_lsb}, at{dword_index_t{0}, 23_msb, 4_lsb}>;
|
||||
using compact64_build_id_f = field<"build_id", std::uint64_t>::located<
|
||||
at{dword_index_t{1}, 31_msb, 0_lsb}, at{dword_index_t{0}, 31_msb, 30_lsb},
|
||||
at{dword_index_t{0}, 23_msb, 4_lsb}>;
|
||||
|
||||
using normal_build_msg_t =
|
||||
message<"normal_build", type_f::with_required<type::Build>,
|
||||
opt_len_f::with_required<true>,
|
||||
build_subtype_f::with_required<build_subtype::Long>, payload_len_f>;
|
||||
using compact32_build_msg_t =
|
||||
message<"compact32_build", type_f::with_required<type::Build>,
|
||||
build_subtype_f::with_required<build_subtype::Compact32>,
|
||||
compact32_build_id_f>;
|
||||
using compact64_build_msg_t =
|
||||
message<"compact64_build", type_f::with_required<type::Build>,
|
||||
build_subtype_f::with_required<build_subtype::Compact64>,
|
||||
compact64_build_id_f>;
|
||||
|
||||
using short32_payload_f =
|
||||
field<"payload",
|
||||
std::uint32_t>::located<at{dword_index_t{0}, 31_msb, 4_lsb}>;
|
||||
using short32_msg_t =
|
||||
message<"short32", type_f::with_required<type::Short32>, short32_payload_f>;
|
||||
|
||||
using catalog_subtype_f =
|
||||
field<"subtype",
|
||||
catalog_subtype>::located<at{dword_index_t{0}, 29_msb, 24_lsb}>;
|
||||
using severity_f = field<"severity", std::uint8_t>::located<at{dword_index_t{0},
|
||||
6_msb, 4_lsb}>;
|
||||
using module_id_f =
|
||||
field<"module_id",
|
||||
std::uint8_t>::located<at{dword_index_t{0}, 22_msb, 16_lsb}>;
|
||||
|
||||
using catalog_msg_t =
|
||||
message<"catalog", type_f::with_required<type::Catalog>, severity_f,
|
||||
module_id_f,
|
||||
catalog_subtype_f::with_required<catalog_subtype::Id32_Pack32>>;
|
||||
} // namespace defn
|
||||
|
||||
template <typename TDestinations> struct log_handler {
|
||||
constexpr explicit log_handler(TDestinations &&ds) : dests{std::move(ds)} {}
|
||||
|
||||
template <typename Env, typename FilenameStringType,
|
||||
typename LineNumberType, typename MsgType>
|
||||
ALWAYS_INLINE auto log(FilenameStringType, LineNumberType,
|
||||
MsgType const &msg) -> void {
|
||||
log_msg<Env>(msg);
|
||||
}
|
||||
|
||||
template <typename Env, typename Msg>
|
||||
ALWAYS_INLINE auto log_msg(Msg msg) -> void {
|
||||
msg.apply([&]<typename S, typename... Args>(S, Args... args) {
|
||||
constexpr auto L = stdx::to_underlying(get_level(Env{}).value);
|
||||
using Message = decltype(detail::to_message<L, S, Args...>());
|
||||
using Module =
|
||||
decltype(detail::to_module<get_module(Env{}).value>());
|
||||
dispatch_message<L>(catalog<Message>(), module<Module>(),
|
||||
static_cast<std::uint32_t>(args)...);
|
||||
});
|
||||
}
|
||||
|
||||
template <auto Version, stdx::ct_string S = ""> auto log_build() -> void {
|
||||
using namespace msg;
|
||||
if constexpr (S.empty() and stdx::bit_width(Version) <= 22) {
|
||||
owning<defn::compact32_build_msg_t> message{"build_id"_field =
|
||||
Version};
|
||||
dispatch_pass_by_args(message.data()[0]);
|
||||
} else if constexpr (S.empty() and stdx::bit_width(Version) <= 54) {
|
||||
owning<defn::compact64_build_msg_t> message{"build_id"_field =
|
||||
Version};
|
||||
dispatch_pass_by_args(message.data()[0], message.data()[1]);
|
||||
} else {
|
||||
constexpr auto header_size =
|
||||
defn::normal_build_msg_t::size<std::uint8_t>::value;
|
||||
constexpr auto payload_len = S.size() + sizeof(std::uint64_t);
|
||||
using storage_t =
|
||||
std::array<std::uint8_t, header_size + payload_len>;
|
||||
|
||||
defn::normal_build_msg_t::owner_t<storage_t> message{
|
||||
"payload_len"_field = payload_len};
|
||||
auto dest = &message.data()[header_size];
|
||||
|
||||
auto const ver = stdx::to_le(static_cast<std::uint64_t>(Version));
|
||||
dest = std::copy_n(stdx::bit_cast<std::uint8_t const *>(&ver),
|
||||
sizeof(std::uint64_t), dest);
|
||||
std::copy_n(std::cbegin(S.value), S.size(), dest);
|
||||
dispatch_pass_by_buffer(message.data());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename... MsgDataTypes>
|
||||
NEVER_INLINE auto
|
||||
dispatch_pass_by_args(MsgDataTypes &&...msg_data) -> void {
|
||||
stdx::for_each(
|
||||
[&]<typename Dest>(Dest &dest) {
|
||||
conc::call_in_critical_section<Dest>([&] {
|
||||
dest.log_by_args(std::forward<MsgDataTypes>(msg_data)...);
|
||||
});
|
||||
},
|
||||
dests);
|
||||
}
|
||||
|
||||
NEVER_INLINE auto
|
||||
dispatch_pass_by_buffer(stdx::span<std::uint8_t const> msg) -> void {
|
||||
stdx::for_each(
|
||||
[&]<typename Dest>(Dest &dest) {
|
||||
conc::call_in_critical_section<Dest>(
|
||||
[&] { dest.log_by_buf(msg); });
|
||||
},
|
||||
dests);
|
||||
}
|
||||
|
||||
template <auto Level, std::same_as<std::uint32_t>... MsgDataTypes>
|
||||
ALWAYS_INLINE auto dispatch_message(string_id id,
|
||||
[[maybe_unused]] module_id m,
|
||||
MsgDataTypes... msg_data) -> void {
|
||||
using namespace msg;
|
||||
if constexpr (sizeof...(msg_data) == 0u) {
|
||||
owning<defn::short32_msg_t> message{"payload"_field = id};
|
||||
dispatch_pass_by_args(message.data()[0]);
|
||||
} else if constexpr (sizeof...(MsgDataTypes) <= 2u) {
|
||||
owning<defn::catalog_msg_t> message{"severity"_field = Level,
|
||||
"module_id"_field = m};
|
||||
dispatch_pass_by_args(
|
||||
message.data()[0], stdx::to_le(id),
|
||||
stdx::to_le(std::forward<MsgDataTypes>(msg_data))...);
|
||||
} else {
|
||||
constexpr auto header_size =
|
||||
defn::catalog_msg_t::size<std::uint8_t>::value;
|
||||
constexpr auto payload_len =
|
||||
(sizeof(id) + ... + sizeof(MsgDataTypes));
|
||||
using storage_t =
|
||||
std::array<std::uint8_t, header_size + payload_len>;
|
||||
|
||||
defn::catalog_msg_t::owner_t<storage_t> message{
|
||||
"severity"_field = Level, "module_id"_field = m};
|
||||
|
||||
constexpr auto copy_arg = [](std::uint32_t arg, auto &dest) {
|
||||
std::memcpy(dest, &arg, sizeof(std::uint32_t));
|
||||
dest += sizeof(std::uint32_t);
|
||||
};
|
||||
auto dest = &message.data()[header_size];
|
||||
copy_arg(stdx::to_le(id), dest);
|
||||
(copy_arg(stdx::to_le(msg_data), dest), ...);
|
||||
dispatch_pass_by_buffer(message.data());
|
||||
}
|
||||
}
|
||||
|
||||
TDestinations dests;
|
||||
};
|
||||
|
||||
template <typename... TDestinations> struct config {
|
||||
using destinations_tuple_t = stdx::tuple<TDestinations...>;
|
||||
constexpr explicit config(TDestinations... dests)
|
||||
: logger{stdx::tuple{std::move(dests)...}} {}
|
||||
|
||||
log_handler<destinations_tuple_t> logger;
|
||||
};
|
||||
template <typename... Ts> config(Ts...) -> config<Ts...>;
|
||||
} // namespace logging::mipi
|
||||
116
Chapter17/cib/libs/log/env.hpp
Normal file
116
Chapter17/cib/libs/log/env.hpp
Normal file
@@ -0,0 +1,116 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdx/compiler.hpp>
|
||||
#include <stdx/ct_string.hpp>
|
||||
#include <stdx/utility.hpp>
|
||||
|
||||
#include <boost/mp11/algorithm.hpp>
|
||||
|
||||
namespace logging {
|
||||
template <auto Query, auto Value> struct prop {
|
||||
[[nodiscard]] CONSTEVAL static auto query(decltype(Query)) noexcept {
|
||||
return Value;
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename Q, typename Env>
|
||||
concept valid_query_for = requires { Env::query(Q{}); };
|
||||
|
||||
template <typename Q, typename... Envs>
|
||||
concept valid_query_over = (... or valid_query_for<Q, Envs>);
|
||||
|
||||
template <typename Q> struct has_query {
|
||||
template <typename Env>
|
||||
using fn = std::bool_constant<valid_query_for<Q, Env>>;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename... Envs> struct env {
|
||||
template <detail::valid_query_over<Envs...> Q>
|
||||
CONSTEVAL static auto query(Q) noexcept {
|
||||
using I = boost::mp11::mp_find_if_q<boost::mp11::mp_list<Envs...>,
|
||||
detail::has_query<Q>>;
|
||||
using E = boost::mp11::mp_at<boost::mp11::mp_list<Envs...>, I>;
|
||||
return Q{}(E{});
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename T> struct autowrap {
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
CONSTEVAL autowrap(T t) : value(t) {}
|
||||
T value;
|
||||
};
|
||||
|
||||
// NOLINTNEXTLINE(modernize-avoid-c-arrays)
|
||||
template <std::size_t N> using str_lit_t = char const (&)[N];
|
||||
|
||||
template <std::size_t N> struct autowrap<str_lit_t<N>> {
|
||||
// NOLINTNEXTLINE(google-explicit-constructor)
|
||||
CONSTEVAL autowrap(str_lit_t<N> str) : value(str) {}
|
||||
stdx::ct_string<N> value;
|
||||
};
|
||||
|
||||
template <typename T> autowrap(T) -> autowrap<T>;
|
||||
template <std::size_t N> autowrap(str_lit_t<N>) -> autowrap<str_lit_t<N>>;
|
||||
|
||||
template <auto V> struct wrap {
|
||||
constexpr static auto value = V;
|
||||
};
|
||||
|
||||
template <typename> struct for_each_pair;
|
||||
template <std::size_t... Is> struct for_each_pair<std::index_sequence<Is...>> {
|
||||
template <auto... Args>
|
||||
using type = env<
|
||||
prop<boost::mp11::mp_at_c<boost::mp11::mp_list<wrap<Args>...>,
|
||||
2 * Is>::value.value,
|
||||
stdx::ct<boost::mp11::mp_at_c<boost::mp11::mp_list<wrap<Args>...>,
|
||||
2 * Is + 1>::value.value>()>...>;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename Env = env<>>
|
||||
constexpr auto make_env = []<detail::autowrap... Args> {
|
||||
using new_env_t = typename detail::for_each_pair<
|
||||
std::make_index_sequence<sizeof...(Args) / 2>>::template type<Args...>;
|
||||
return boost::mp11::mp_append<new_env_t, Env>{};
|
||||
};
|
||||
|
||||
template <detail::autowrap... Args>
|
||||
using make_env_t = decltype(make_env<>.template operator()<Args...>());
|
||||
} // namespace logging
|
||||
|
||||
using cib_log_env_t = logging::env<>;
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
|
||||
|
||||
#ifdef __clang__
|
||||
#define CIB_PRAGMA_SEMI
|
||||
#else
|
||||
#define CIB_PRAGMA_SEMI ;
|
||||
#endif
|
||||
|
||||
#define CIB_LOG_ENV_DECL(...) \
|
||||
[[maybe_unused]] typedef decltype([]<logging::detail:: \
|
||||
autowrap... _env_args> { \
|
||||
using new_env_t = \
|
||||
typename logging::detail::for_each_pair<std::make_index_sequence< \
|
||||
sizeof...(_env_args) / 2>>::template type<_env_args...>; \
|
||||
return boost::mp11::mp_append<new_env_t, cib_log_env_t>{}; \
|
||||
}.template operator()<__VA_ARGS__>()) cib_log_env_t
|
||||
|
||||
#define CIB_LOG_ENV(...) \
|
||||
STDX_PRAGMA(diagnostic push) \
|
||||
STDX_PRAGMA(diagnostic ignored "-Wshadow") \
|
||||
CIB_LOG_ENV_DECL(__VA_ARGS__) \
|
||||
CIB_PRAGMA_SEMI \
|
||||
STDX_PRAGMA(diagnostic pop)
|
||||
|
||||
#define CIB_WITH_LOG_ENV(...) \
|
||||
STDX_PRAGMA(diagnostic push) \
|
||||
STDX_PRAGMA(diagnostic ignored "-Wshadow") \
|
||||
if constexpr (CIB_LOG_ENV_DECL(__VA_ARGS__); true) \
|
||||
STDX_PRAGMA(diagnostic pop)
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-macro-usage)
|
||||
68
Chapter17/cib/libs/log/fmt/logger.hpp
Normal file
68
Chapter17/cib/libs/log/fmt/logger.hpp
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include <log/level.hpp>
|
||||
#include <log/log.hpp>
|
||||
#include <log/module.hpp>
|
||||
|
||||
#include <stdx/ct_format.hpp>
|
||||
#include <stdx/tuple.hpp>
|
||||
#include <stdx/tuple_algorithms.hpp>
|
||||
#include <stdx/utility.hpp>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
template <logging::level L>
|
||||
struct fmt::formatter<std::integral_constant<logging::level, L>> {
|
||||
constexpr static auto parse(format_parse_context &ctx) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(std::integral_constant<logging::level, L>,
|
||||
FormatContext &ctx) const {
|
||||
return ::fmt::format_to(ctx.out(), logging::to_text<L>());
|
||||
}
|
||||
};
|
||||
|
||||
namespace logging::fmt {
|
||||
template <typename TDestinations> struct log_handler {
|
||||
constexpr explicit log_handler(TDestinations &&ds) : dests{std::move(ds)} {}
|
||||
|
||||
template <typename Env, typename FilenameStringType,
|
||||
typename LineNumberType, typename MsgType>
|
||||
auto log(FilenameStringType, LineNumberType, MsgType const &msg) -> void {
|
||||
auto const currentTime =
|
||||
std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::steady_clock::now() - start_time)
|
||||
.count();
|
||||
|
||||
stdx::for_each(
|
||||
[&](auto &out) {
|
||||
::fmt::format_to(out, "{:>8}us {} [{}]: ", currentTime,
|
||||
get_level(Env{}), get_module(Env{}).value);
|
||||
msg.apply(
|
||||
[&]<typename StringType>(StringType, auto const &...args) {
|
||||
::fmt::format_to(out, StringType::value, args...);
|
||||
});
|
||||
*out = '\n';
|
||||
},
|
||||
dests);
|
||||
}
|
||||
|
||||
private:
|
||||
static inline auto const start_time = std::chrono::steady_clock::now();
|
||||
TDestinations dests;
|
||||
};
|
||||
|
||||
template <typename... TDestinations> struct config {
|
||||
using destinations_tuple_t = stdx::tuple<TDestinations...>;
|
||||
constexpr explicit config(TDestinations... dests)
|
||||
: logger{stdx::tuple{std::move(dests)...}} {}
|
||||
|
||||
log_handler<destinations_tuple_t> logger;
|
||||
};
|
||||
} // namespace logging::fmt
|
||||
44
Chapter17/cib/libs/log/level.hpp
Normal file
44
Chapter17/cib/libs/log/level.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include <log/env.hpp>
|
||||
|
||||
#include <stdx/type_traits.hpp>
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string_view>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace logging {
|
||||
// enum assignment is according to Mipi_Sys-T Severity definition
|
||||
enum struct level : std::uint8_t {
|
||||
MAX = 0,
|
||||
FATAL = 1,
|
||||
ERROR = 2,
|
||||
WARN = 3,
|
||||
INFO = 4,
|
||||
USER1 = 5,
|
||||
USER2 = 6,
|
||||
TRACE = 7
|
||||
};
|
||||
|
||||
template <level L>
|
||||
[[nodiscard]] constexpr auto to_text() -> std::string_view
|
||||
requires(L <= level::TRACE)
|
||||
{
|
||||
using namespace std::string_view_literals;
|
||||
constexpr std::array level_text{"MAX"sv, "FATAL"sv, "ERROR"sv, "WARN"sv,
|
||||
"INFO"sv, "USER1"sv, "USER2"sv, "TRACE"sv};
|
||||
return level_text[stdx::to_underlying(L)];
|
||||
}
|
||||
|
||||
[[maybe_unused]] constexpr inline struct get_level_t {
|
||||
template <typename T>
|
||||
CONSTEVAL auto operator()(T &&t) const noexcept(
|
||||
noexcept(std::forward<T>(t).query(std::declval<get_level_t>())))
|
||||
-> decltype(std::forward<T>(t).query(*this)) {
|
||||
return std::forward<T>(t).query(*this);
|
||||
}
|
||||
} get_level;
|
||||
} // namespace logging
|
||||
115
Chapter17/cib/libs/log/log.hpp
Normal file
115
Chapter17/cib/libs/log/log.hpp
Normal file
@@ -0,0 +1,115 @@
|
||||
#pragma once
|
||||
|
||||
#include <log/env.hpp>
|
||||
#include <log/level.hpp>
|
||||
#include <log/module.hpp>
|
||||
#include <sc/format.hpp>
|
||||
#include <sc/fwd.hpp>
|
||||
|
||||
#include <stdx/compiler.hpp>
|
||||
#include <stdx/ct_string.hpp>
|
||||
#include <stdx/panic.hpp>
|
||||
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
namespace version {
|
||||
namespace null {
|
||||
struct config {
|
||||
constexpr static auto build_id = std::uint64_t{};
|
||||
constexpr static auto version_string = stdx::ct_string{""};
|
||||
};
|
||||
} // namespace null
|
||||
template <typename...> inline auto config = null::config{};
|
||||
} // namespace version
|
||||
|
||||
namespace logging {
|
||||
namespace null {
|
||||
struct config {
|
||||
struct {
|
||||
template <typename>
|
||||
constexpr auto log(auto &&...) const noexcept -> void {}
|
||||
} logger;
|
||||
};
|
||||
} // namespace null
|
||||
|
||||
template <typename...> inline auto config = null::config{};
|
||||
|
||||
struct default_flavor_t;
|
||||
|
||||
template <typename Flavor, typename... Ts>
|
||||
ALWAYS_INLINE constexpr static auto get_config() -> auto & {
|
||||
if constexpr (std::same_as<Flavor, default_flavor_t>) {
|
||||
return config<Ts...>;
|
||||
} else {
|
||||
return config<Flavor, Ts...>;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Flavor, typename Env, typename... Ts, typename... TArgs>
|
||||
ALWAYS_INLINE static auto log(TArgs &&...args) -> void {
|
||||
auto &cfg = get_config<Flavor, Ts...>();
|
||||
cfg.logger.template log<Env>(std::forward<TArgs>(args)...);
|
||||
}
|
||||
} // namespace logging
|
||||
|
||||
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
|
||||
|
||||
#define CIB_LOG(FLAVOR, MSG, ...) \
|
||||
logging::log<FLAVOR, cib_log_env_t>( \
|
||||
__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_WITH_LEVEL(logging::level::TRACE __VA_OPT__(, ) __VA_ARGS__)
|
||||
#define CIB_INFO(...) \
|
||||
CIB_LOG_WITH_LEVEL(logging::level::INFO __VA_OPT__(, ) __VA_ARGS__)
|
||||
#define CIB_WARN(...) \
|
||||
CIB_LOG_WITH_LEVEL(logging::level::WARN __VA_OPT__(, ) __VA_ARGS__)
|
||||
#define CIB_ERROR(...) \
|
||||
CIB_LOG_WITH_LEVEL(logging::level::ERROR __VA_OPT__(, ) __VA_ARGS__)
|
||||
|
||||
#define CIB_FATAL(MSG, ...) \
|
||||
[](auto &&str) { \
|
||||
CIB_LOG_ENV(logging::get_level, logging::level::FATAL); \
|
||||
logging::log<logging::default_flavor_t, cib_log_env_t>(__FILE__, \
|
||||
__LINE__, str); \
|
||||
FWD(str).apply([]<typename S, typename... Args>(S s, Args... args) { \
|
||||
constexpr auto cts = stdx::ct_string_from_type(s); \
|
||||
stdx::panic<cts>(args...); \
|
||||
}); \
|
||||
}(sc::format(MSG##_sc __VA_OPT__(, ) __VA_ARGS__))
|
||||
|
||||
#define CIB_ASSERT(expr) \
|
||||
((expr) ? void(0) : CIB_FATAL("Assertion failure: " #expr))
|
||||
|
||||
namespace logging {
|
||||
template <typename Flavor, typename... Ts>
|
||||
ALWAYS_INLINE static auto log_version() -> void {
|
||||
auto &l_cfg = get_config<Flavor, Ts...>();
|
||||
auto &v_cfg = ::version::config<Ts...>;
|
||||
if constexpr (requires {
|
||||
l_cfg.logger.template log_build<v_cfg.build_id,
|
||||
v_cfg.version_string>();
|
||||
}) {
|
||||
l_cfg.logger.template log_build<v_cfg.build_id, v_cfg.version_string>();
|
||||
} else {
|
||||
CIB_LOG_ENV(logging::get_level, logging::level::MAX);
|
||||
l_cfg.logger.template log<cib_log_env_t>(
|
||||
"", 0,
|
||||
sc::format("Version: {} ({})"_sc, sc::uint_<v_cfg.build_id>,
|
||||
stdx::ct_string_to_type<v_cfg.version_string,
|
||||
sc::string_constant>()));
|
||||
}
|
||||
}
|
||||
} // namespace logging
|
||||
|
||||
#define CIB_LOG_V(FLAVOR) logging::log_version<FLAVOR>()
|
||||
#define CIB_LOG_VERSION() CIB_LOG_V(logging::default_flavor_t)
|
||||
|
||||
// NOLINTEND(cppcoreguidelines-macro-usage)
|
||||
27
Chapter17/cib/libs/log/module.hpp
Normal file
27
Chapter17/cib/libs/log/module.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include <log/env.hpp>
|
||||
|
||||
#include <stdx/ct_string.hpp>
|
||||
|
||||
#include <utility>
|
||||
|
||||
namespace logging {
|
||||
[[maybe_unused]] constexpr inline struct get_module_t {
|
||||
template <typename T>
|
||||
requires true // more constrained
|
||||
CONSTEVAL auto operator()(T &&t) const noexcept(
|
||||
noexcept(std::forward<T>(t).query(std::declval<get_module_t>())))
|
||||
-> decltype(std::forward<T>(t).query(*this)) {
|
||||
return std::forward<T>(t).query(*this);
|
||||
}
|
||||
|
||||
CONSTEVAL auto operator()(auto &&) const {
|
||||
using namespace stdx::literals;
|
||||
return "default"_ctst;
|
||||
}
|
||||
} get_module;
|
||||
} // namespace logging
|
||||
|
||||
// NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
|
||||
#define CIB_LOG_MODULE(S) CIB_LOG_ENV(logging::get_module, S)
|
||||
Reference in New Issue
Block a user