19#include "fastmm/core/config_macros.hpp"
22#include "fastmm/core/fixed_string.hpp"
23#include "fastmm/core/spsc_ring.hpp"
76using LogRing = SpscRing<LogRecord, kLogRingSize>;
91 return static_cast<std::uint8_t
>(lvl) >=
g_log_level.load(std::memory_order_relaxed);
121 [[nodiscard]]
bool running() const noexcept {
return running_.load(std::memory_order_acquire); }
123 [[nodiscard]]
bool to_file() const noexcept {
return to_file_.load(std::memory_order_acquire); }
125 [[nodiscard]] std::uint64_t
dropped() const noexcept {
126 return dropped_.load(std::memory_order_relaxed);
128 void note_drop() noexcept { dropped_.fetch_add(1, std::memory_order_relaxed); }
129 [[nodiscard]] std::uint64_t
written() const noexcept {
130 return written_.load(std::memory_order_relaxed);
139 std::size_t drain_all();
143 Impl* impl_ =
nullptr;
144 std::atomic<bool> running_{
false};
145 std::atomic<bool> to_file_{
false};
146 std::atomic<std::uint64_t> dropped_{0};
147 std::atomic<std::uint64_t> written_{0};
152inline thread_local LogRing* t_log_ring =
nullptr;
153inline thread_local std::uint32_t t_log_tid = 0;
154inline thread_local std::uint32_t t_log_slot = 0;
155inline thread_local bool t_log_thread_exiting =
false;
159constexpr bool log_compiled(
LogLevel l)
noexcept {
160 const int v =
static_cast<int>(l);
161 const int floor = FASTMM_MIN_LOG_LEVEL;
166concept EnumWithToString = std::is_enum_v<E> &&
requires(E e) {
167 {
to_string(e) } -> std::convertible_to<std::string_view>;
170concept StringLike = std::convertible_to<const T&, std::string_view>;
172struct IsSecret : std::false_type {};
174struct IsSecret<
Secret<T>> : std::true_type {};
176struct IsFixed : std::false_type {};
178struct IsFixed<Fixed<Tag>> : std::true_type {};
182struct LogFmtTypeImpl {
183 using D = std::remove_cvref_t<T>;
184 using type = std::conditional_t<
185 std::is_same_v<D, bool>,
188 std::is_same_v<D, char>,
191 std::is_integral_v<D>,
192 std::conditional_t<std::is_signed_v<D>, std::int64_t, std::uint64_t>,
194 std::is_floating_point_v<D>,
197 IsFixed<D>::value || IsSecret<D>::value || StringLike<D> ||
200 std::conditional_t<std::is_enum_v<D>,
202 std::conditional_t<std::is_same_v<D, Timestamp> ||
203 std::is_same_v<D, Duration>,
206 static_assert(!std::is_void_v<type>,
"unsupported log argument type");
209using LogFmtType =
typename LogFmtTypeImpl<T>::type;
216 FASTMM_FORCE_INLINE
bool put_scalar(
LogArgType t, std::uint64_t bits)
noexcept {
217 if (end - p < 9)
return false;
218 *p++ =
static_cast<std::uint8_t
>(t);
219 std::memcpy(p, &bits, 8);
224 FASTMM_FORCE_INLINE
bool put_str(
LogArgType t, std::string_view s)
noexcept {
225 const std::size_t room =
static_cast<std::size_t
>(end - p);
226 if (room < 2)
return false;
228 if (len > room - 2) len = room - 2;
229 *p++ =
static_cast<std::uint8_t
>(t);
230 *p++ =
static_cast<std::uint8_t
>(len);
231 if (len != 0) std::memcpy(p, s.data(), len);
232 if (len < s.size() && len >= 3) std::memcpy(p + len - 3,
"...", 3);
240FASTMM_FORCE_INLINE
bool pack_arg(ArgPacker& pk,
const T& v)
noexcept {
241 using D = std::remove_cvref_t<T>;
242 if constexpr (std::is_same_v<D, bool>) {
244 }
else if constexpr (std::is_same_v<D, char>) {
246 }
else if constexpr (std::is_integral_v<D>) {
247 if constexpr (std::is_signed_v<D>) {
249 static_cast<std::uint64_t
>(
static_cast<std::int64_t
>(v)));
253 }
else if constexpr (std::is_floating_point_v<D>) {
254 const double d =
static_cast<double>(v);
255 std::uint64_t bits = 0;
256 std::memcpy(&bits, &d, 8);
258 }
else if constexpr (std::is_same_v<D, Price>) {
260 }
else if constexpr (std::is_same_v<D, Qty>) {
262 }
else if constexpr (std::is_same_v<D, Notional>) {
264 }
else if constexpr (IsSecret<D>::value) {
266 }
else if constexpr (EnumWithToString<D>) {
268 }
else if constexpr (std::is_enum_v<D>) {
270 static_cast<std::uint64_t
>(
271 static_cast<std::int64_t
>(
static_cast<std::underlying_type_t<D>
>(v))));
272 }
else if constexpr (std::is_same_v<D, Timestamp> || std::is_same_v<D, Duration>) {
273 return pk.put_scalar(
LogArgType::Int,
static_cast<std::uint64_t
>(v.ns));
274 }
else if constexpr (StringLike<D>) {
277 static_assert(
sizeof(D) == 0,
"unsupported log argument type");
282template <
class... Args>
283FASTMM_FORCE_INLINE
void log_emit(
const LogDescriptor& d,
const Args&... args)
noexcept {
285 if (FASTMM_UNLIKELY(ring ==
nullptr)) {
287 if (ring ==
nullptr) {
292 LogRecord* r = ring->try_reserve();
293 if (FASTMM_UNLIKELY(r ==
nullptr)) {
300 ArgPacker pk{r->args, r->args +
sizeof(r->args)};
301 (
static_cast<void>(pack_arg(pk, args)), ...);
303 r->used =
static_cast<std::uint8_t
>(pk.p - r->args);
312#define FASTMM_LOG_IMPL(lvl, fmt_str, ...) \
314 if constexpr (::fastmm::detail::log_compiled(lvl)) { \
315 if (::fastmm::log_enabled(lvl)) { \
316 static constexpr ::fastmm::LogDescriptor fastmm_log_desc_{ \
317 fmt_str, __FILE__, __LINE__, lvl}; \
318 [&](const auto&... fastmm_a_) { \
320 ::fmt::format_string<::fastmm::detail::LogFmtType<decltype(fastmm_a_)>...>( \
322 ::fastmm::detail::log_emit(fastmm_log_desc_, fastmm_a_...); \
328#define FASTMM_LOG_TRACE(fmt_str, ...) \
329 FASTMM_LOG_IMPL(::fastmm::LogLevel::Trace, fmt_str __VA_OPT__(, ) __VA_ARGS__)
330#define FASTMM_LOG_DEBUG(fmt_str, ...) \
331 FASTMM_LOG_IMPL(::fastmm::LogLevel::Debug, fmt_str __VA_OPT__(, ) __VA_ARGS__)
332#define FASTMM_LOG_INFO(fmt_str, ...) \
333 FASTMM_LOG_IMPL(::fastmm::LogLevel::Info, fmt_str __VA_OPT__(, ) __VA_ARGS__)
334#define FASTMM_LOG_WARN(fmt_str, ...) \
335 FASTMM_LOG_IMPL(::fastmm::LogLevel::Warn, fmt_str __VA_OPT__(, ) __VA_ARGS__)
336#define FASTMM_LOG_ERROR(fmt_str, ...) \
337 FASTMM_LOG_IMPL(::fastmm::LogLevel::Error, fmt_str __VA_OPT__(, ) __VA_ARGS__)
Process-wide registry of per-thread rings + the sink thread. Implemented in log_sink....
Definition log.hpp:97
static Logger & instance() noexcept
LogLevel level() const noexcept
Definition log.hpp:102
std::size_t thread_slots() const noexcept
Number of rings allocated so far; reused rings are not counted again.
void start(std::FILE *out=stderr, LogLevel mirror_level=LogLevel::Warn)
Sink control.
void detach_current_thread() noexcept
Returns the calling thread's ring to the pool.
std::uint64_t written() const noexcept
Definition log.hpp:131
void stop()
drains everything then joins the sink thread
std::uint64_t dropped() const noexcept
Definition log.hpp:127
void set_level(LogLevel lvl) noexcept
Definition log.hpp:101
void flush()
Drains all rings and fflush()es. Works with or without a running sink thread.
void note_drop() noexcept
Definition log.hpp:130
bool running() const noexcept
Definition log.hpp:123
LogRing * attach_current_thread()
Gives the calling thread a ring: a drained ring released by an exited thread if one exists,...
bool to_file() const noexcept
The sink writes to a stream other than stderr (a log file).
Definition log.hpp:125
static void format_record(const LogRecord &r, std::string &out)
Formats one record into out (appends). Public for tests/tools.
Fixed<Tag>: int64 fixed point with a global 1e-8 scale (ADR-0001).
Definition strategy.hpp:16
SpscRing< LogRecord, kLogRingSize > LogRing
Definition log.hpp:78
LogLevel
Definition enums.hpp:604
Secret< std::decay_t< T > > secret(T &&v)
Definition log.hpp:86
constexpr std::size_t kLogRecordBytes
Definition log.hpp:62
Timestamp wall_now() noexcept
CLOCK_REALTIME via vDSO (~20 ns). Implemented in src/core/time.cpp.
std::atomic< std::uint8_t > g_log_level
Global level (runtime); FASTMM_MIN_LOG_LEVEL is the compile-time floor.
Definition log.hpp:91
FASTMM_FORCE_INLINE bool log_enabled(LogLevel lvl) noexcept
Definition log.hpp:92
constexpr std::size_t kLogMaxStrBytes
Definition log.hpp:63
LogArgType
Definition log.hpp:49
constexpr std::size_t kLogRingSize
records per thread (2 MiB)
Definition log.hpp:64
constexpr std::string_view to_string(ParamType t) noexcept
Definition params.hpp:63
constexpr std::size_t kLogMaxThreads
Definition log.hpp:65
Strongly typed identifiers.
LogLevel level
Definition log.hpp:46
const char * file
Definition log.hpp:44
const char * fmt
Definition log.hpp:43
std::uint32_t line
Definition log.hpp:45
std::uint8_t args[kLogRecordBytes - 24]
Definition log.hpp:74
std::int64_t ts_ns
Definition log.hpp:69
std::uint32_t tid
Definition log.hpp:70
std::uint8_t used
Definition log.hpp:72
std::uint8_t pad_[2]
Definition log.hpp:73
const LogDescriptor * desc
Definition log.hpp:68
std::uint8_t nargs
Definition log.hpp:71
Wrap a value to have it rendered as "***".
Definition log.hpp:82
T value
Definition log.hpp:83
std::int64_t ns
Definition time.hpp:46