37#include "../microfmt.hpp"
60template <
typename Tag>
62 requires(value_ref<typename extended_log_sink_traits<Tag>::context_type> ctx,
const extended_log_msg &msg) {
63 { extended_log_sink_traits<Tag>::full(ctx, msg) } -> std::convertible_to<bool>;
66template <
typename Tag>
67concept has_extended_flush =
requires(value_ref<typename extended_log_sink_traits<Tag>::context_type> ctx) {
68 extended_log_sink_traits<Tag>::flush(ctx);
84 void (*flush)(
void *ctx)
noexcept =
nullptr;
90 requires(std::is_convertible_v<Context *, typename Traits::context_type *>)
91 constexpr
extended_log_sink(Tag, Context &ctx RELOCO_LIFETIMEBOUND RELOCO_LIFETIME_CAPTURE_BY_THIS,
93 : ctx_(&ctx), vtbl_(&s_vtbl<Tag>), lvl_(lvl) {}
96 template <
typename Tag,
typename Context>
97 requires(!std::is_lvalue_reference_v<Context>)
102 if (vtbl_ !=
nullptr && msg.lvl >= lvl_) {
103 vtbl_->log(ctx_.get(), msg);
108 [[nodiscard]]
constexpr bool can_report_full() const noexcept {
return vtbl_ !=
nullptr && vtbl_->full !=
nullptr; }
116 return can_report_full() && msg.lvl >= lvl_ && vtbl_->full(ctx_.get(), msg);
119 [[nodiscard]]
constexpr bool can_flush() const noexcept {
return vtbl_ !=
nullptr && vtbl_->flush !=
nullptr; }
123 vtbl_->flush(ctx_.get());
129 [[nodiscard]]
constexpr explicit operator bool() const noexcept {
return vtbl_ !=
nullptr; }
134 template <
typename Tag>
static void log_entry(
void *ctx,
const extended_log_msg &msg)
noexcept {
138 template <
typename Tag> [[nodiscard]]
static constexpr auto full_entry() noexcept {
139 if constexpr (detail::has_extended_full<Tag>) {
140 return +[](
void *ctx,
const extended_log_msg &msg)
noexcept ->
bool {
141 return extended_log_sink_traits<Tag>::full(value_ref<ctx_t<Tag>>(*
static_cast<ctx_t<Tag> *
>(ctx)), msg);
144 return static_cast<bool (*)(
void *,
const extended_log_msg &) noexcept
>(
nullptr);
148 template <
typename Tag> [[nodiscard]]
static constexpr auto flush_entry() noexcept {
149 if constexpr (detail::has_extended_flush<Tag>) {
150 return +[](
void *ctx)
noexcept {
151 extended_log_sink_traits<Tag>::flush(value_ref<ctx_t<Tag>>(*
static_cast<ctx_t<Tag> *
>(ctx)));
154 return static_cast<void (*)(
void *) noexcept
>(
nullptr);
158 template <
typename Tag>
static constexpr vtable s_vtbl{&log_entry<Tag>, full_entry<Tag>(), flush_entry<Tag>()};
160 value_ptr<void> ctx_{};
161 const vtable *vtbl_{
nullptr};
162 level lvl_{level::trace};
210template <std::
size_t MaxSinks,
typename Channel = null_wait_channel>
class fanout_log_sink {
211 static_assert(MaxSinks > 0,
"fanout_log_sink needs room for at least one sink");
213 struct RELOCO_POINTER channel_guard {
214 explicit channel_guard(Channel &c) noexcept : c_(c) { c_.lock(); }
215 ~channel_guard()
noexcept { c_.unlock(); }
216 channel_guard(
const channel_guard &) =
delete;
217 channel_guard &operator=(
const channel_guard &) =
delete;
233 channel_guard g(channel_);
234 const std::size_t n = count_.load(std::memory_order_relaxed);
239 count_.store(n + 1, std::memory_order_release);
253 channel_guard g(channel_);
263 [[nodiscard]] std::size_t
dropped() const noexcept {
return dropped_.load(std::memory_order_relaxed); }
266 if (!msg.meta.can_wait()) {
271 bool accepted =
false;
273 channel_guard g(channel_);
274 const std::uint32_t ticket = next_ticket_++;
276 (void)channel_.wait_for(
wait_forever, [
this, ticket] {
return serving_ == ticket; });
277 accepted = !any_full(msg) || channel_.wait_for(timeout_us_.load(std::memory_order_relaxed),
278 [
this, &msg] { return !any_full(msg); });
284 dropped_.fetch_add(1, std::memory_order_relaxed);
287 channel_guard g(channel_);
295 const std::size_t n = count_.load(std::memory_order_acquire);
296 for (std::size_t i = 0; i < n; ++i) {
303 const std::size_t n = count_.load(std::memory_order_acquire);
304 for (std::size_t i = 0; i < n; ++i) {
305 if (sinks_[i].full(msg)) {
312 void write_through(
const extended_log_msg &msg)
noexcept {
313 const std::size_t n = count_.load(std::memory_order_acquire);
314 for (std::size_t i = 0; i < n; ++i) {
320 microfmt::array<extended_log_sink, MaxSinks> sinks_{};
321 std::atomic<std::size_t> count_{0};
322 std::uint32_t next_ticket_{0};
323 std::uint32_t serving_{0};
324 std::atomic<std::uint64_t> timeout_us_{0};
325 std::atomic<std::size_t> dropped_{0};
328template <std::
size_t MaxSinks,
typename Channel>
332 static void log(value_ref<context_type> ctx,
const extended_log_msg &msg)
noexcept { ctx->log_impl(msg); }
333 static bool full(value_ref<context_type> ctx,
const extended_log_msg &msg)
noexcept {
return ctx->full_impl(msg); }
334 static void flush(value_ref<context_type> ctx)
noexcept { ctx->flush_impl(); }
Type-erased handle to an advanced log sink backend, with a severity threshold. Same two-word-plus-vta...
Definition extended_sink.hpp:77
constexpr bool should_log(level l) const noexcept
Definition extended_sink.hpp:128
bool full(const extended_log_msg &msg) noexcept
Whether msg could not be accepted right now without waiting or overwriting. false for backends that c...
Definition extended_sink.hpp:115
void flush() noexcept
Definition extended_sink.hpp:121
constexpr extended_log_sink(Tag, Context &&, level=level::trace)=delete
constexpr bool can_flush() const noexcept
Definition extended_sink.hpp:119
void log(const extended_log_msg &msg) noexcept
Emits msg unless it is below this sink's threshold.
Definition extended_sink.hpp:101
void set_level(level l) noexcept
Definition extended_sink.hpp:127
constexpr bool can_report_full() const noexcept
Reports whether the backend can say it is full.
Definition extended_sink.hpp:108
constexpr extended_log_sink() noexcept=default
Fixed-capacity sink that forwards each record to up to MaxSinks advanced sinks ("pins"),...
Definition extended_sink.hpp:210
extended_log_sink as_sink(level lvl=level::trace) &noexcept RELOCO_LIFETIMEBOUND
Handle for use as a sink (e.g. by extended_logger).
Definition extended_sink.hpp:247
bool add_sink(extended_log_sink s) noexcept
Appends s; returns false when the table is full. Thread context only. Safe against concurrent writers...
Definition extended_sink.hpp:232
void notify_space() noexcept
Reader side: tells waiting writers that space may have been freed. Thread context.
Definition extended_sink.hpp:252
void log_impl(const extended_log_msg &msg) noexcept
Definition extended_sink.hpp:265
std::size_t dropped() const noexcept
Thread records discarded because a pin stayed full for the whole wait.
Definition extended_sink.hpp:263
space_notifier notifier() &noexcept RELOCO_LIFETIMEBOUND
notify_space as a callback for a pin's consumer.
Definition extended_sink.hpp:258
fanout_log_sink() noexcept=default
void set_wait_timeout_us(std::uint64_t us) noexcept
Longest a turn holder sleeps for space, per record.
Definition extended_sink.hpp:244
void flush_impl() noexcept
Definition extended_sink.hpp:294
bool full_impl(const extended_log_msg &msg) noexcept
Definition extended_sink.hpp:292
Type-erased "space was freed" callback handed to the consumer side of a sink (two words,...
Definition sync.hpp:114
#define MICROFMT_API_CLASS
Definition compat.hpp:181
Definition extended_sink.hpp:67
Definition extended_sink.hpp:61
Log record extended with execution-context metadata (pid, tid, CPU index, context flags) for "advance...
Definition default_log_sink.template.hpp:51
constexpr std::uint64_t wait_forever
timeout_us value meaning "wait without a deadline".
Definition sync.hpp:59
level
Shared log severity levels in ascending order.
Definition log_msg.hpp:22
Type-erased structured logging sinks and console output.
log_msg plus log_meta. Derives from the original record, so it converts to const log_msg & anywhere t...
Definition extended_msg.hpp:62
Definition extended_sink.hpp:79
static bool full(value_ref< context_type > ctx, const extended_log_msg &msg) noexcept
Definition extended_sink.hpp:333
static void flush(value_ref< context_type > ctx) noexcept
Definition extended_sink.hpp:334
static void log(value_ref< context_type > ctx, const extended_log_msg &msg) noexcept
Definition extended_sink.hpp:332
Customization point for extended_log_sink backends.
Definition extended_sink.hpp:56
Definition extended_sink.hpp:165
Locking and wait-channel building blocks shared by the logging stack (basic_logger and the extended s...