VLink  2.1.0
A high-performance communication middleware
logger.h
浏览该文件的文档.
1 /*
2  * Copyright (C) 2026 by Thun Lu. All rights reserved.
3  * Author: Thun Lu <thun.lu@zohomail.cn>
4  * Repo: https://github.com/thun-res/vlink
5  * _ __ __ _ __
6  * | | / / / / (_) ____ / /__
7  * | | / / / / / / / __ \ / //_/
8  * | |/ / / /___ / / / / / / / ,<
9  * |___/ /_____/ /_/ /_/ /_/ /_/|_|
10  *
11  * Licensed under the Apache License, Version 2.0 (the "License");
12  * you may not use this file except in compliance with the License.
13  * You may obtain a copy of the License at
14  *
15  * http://www.apache.org/licenses/LICENSE-2.0
16  *
17  * Unless required by applicable law or agreed to in writing, software
18  * distributed under the License is distributed on an "AS IS" BASIS,
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  * See the License for the specific language governing permissions and
21  * limitations under the License.
22  */
23 
24 /**
25  * @file logger.h
26  * @brief Singleton logger with stream / format / printf / RAII-stream entry points.
27  *
28  * @details
29  * @c vlink::Logger is the central logging facility. A single instance is constructed via
30  * @c Logger::init and reused for the rest of the process; each call routes through a console
31  * sink and an optional file sink whose minimum levels are independently configurable.
32  *
33  * @par Entry point cheat sheet
34  *
35  * | Style | Macro family | Backing API | Argument shape |
36  * | --------------- | --------------- | ----------------------------- | ------------------------------- |
37  * | Stream | @c VLOG_x | @c FastStream operator<< | @c VLOG_I("x=", x, " y=", y) |
38  * | Placeholder | @c MLOG_x | @c vlink::format::format_to_n | @c MLOG_W("x={} y={}", x, y) |
39  * | printf | @c CLOG_x | @c std::snprintf | @c CLOG_E("errno=%d", err) |
40  * | RAII stream | @c SLOG_x | @c WrapperStream | @c SLOG_D @c << "a=" @c << a |
41  *
42  * @par Severity ladder
43  *
44  * | Value | Name | Use case |
45  * | ----- | --------- | ----------------------------------------- |
46  * | 0 | @c kTrace | Verbose internals |
47  * | 1 | @c kDebug | Developer diagnostics |
48  * | 2 | @c kInfo | Normal operational messages |
49  * | 3 | @c kWarn | Unusual but recoverable conditions |
50  * | 4 | @c kError | Errors that may affect operation |
51  * | 5 | @c kFatal | Unrecoverable; throws @c RuntimeError |
52  * | 6 | @c kOff | Disables the corresponding sink |
53  *
54  * @par ASCII priority diagram
55  *
56  * @verbatim
57  * higher severity -> kFatal (throws)
58  * kError
59  * kWarn <- kDetailLevel (default): adds {file:line}
60  * kInfo
61  * kDebug
62  * kTrace
63  * lower severity -> kOff (sink disabled)
64  * @endverbatim
65  *
66  * @par Compile-time gating
67  * - @c VLINK_LOG_LEVEL @c =N strips levels below @c N at compile time (zero overhead).
68  * - @c VLINK_LOG_DETAIL_LEVEL @c =N changes the level at which file/line is appended.
69  * - @c VLINK_LOG_DISABLE_SHORT removes the @c VLOG_* / @c MLOG_* / @c CLOG_* / @c SLOG_* aliases.
70  *
71  * @par Periodic call-site limiting
72  * @c VLOG_x_EVERY_MS(interval_ms, ...) emits immediately, then at most once per interval at that
73  * macro call site. The state is shared by concurrent callers, and suppressed log arguments are
74  * not evaluated. A non-positive interval bypasses limiting without updating the saved timestamp.
75  * Function-template specializations keep separate state. Fatal logs intentionally have no
76  * periodic variant.
77  *
78  * @par Formatting cheat sheet
79  *
80  * | Need | Snippet |
81  * | --------------------------------- | ------------------------------------------------------ |
82  * | Hex with 4 digits | @c VLOG_I(VLINK_LOG_HEX(4), value) |
83  * | Inline hex inside @c SLOG_* | @c SLOG_I @c << VLINK_LOG_HEXSS(4) @c << value |
84  * | Gate around expensive log args | @c if @c (VLINK_LOG_IF_D) @c { ... @c } |
85  *
86  * @par Example
87  * @code
88  * vlink::Logger::init("my_app", "/var/log/my_app.log");
89  * vlink::Logger::set_console_level(vlink::Logger::kInfo);
90  *
91  * VLOG_I("node started, id=", node_id);
92  * MLOG_W("temperature is {} C", temp);
93  * CLOG_E("errno=%d", errno);
94  * SLOG_D << "values: " << a << " " << b;
95  * @endcode
96  *
97  * @note @c kFatal messages call @c Logger::flush and then throw @c Exception::RuntimeError so
98  * the application can perform a controlled shutdown. Backends include spdlog, quill,
99  * DLT, Android logcat, QNX slog2 and kmsg depending on build options.
100  */
101 
102 #pragma once
103 
104 #include <atomic>
105 #include <cstdint>
106 #include <cstdio>
107 #include <iomanip>
108 #include <iostream>
109 #include <memory>
110 #include <string>
111 #include <string_view>
112 #include <type_traits>
113 #include <utility>
114 
115 #include "./exception.h"
116 #include "./fast_stream.h"
117 #include "./format.h"
118 #include "./functional.h"
119 #include "./macros.h"
120 
121 namespace vlink {
122 
123 /**
124  * @class Logger
125  * @brief Global singleton logger with four formatting styles and independently configurable sinks.
126  *
127  * @details
128  * Construct exactly once via @c Logger::init; subsequent calls reconfigure the existing instance.
129  * The console sink is always enabled; the file sink activates when @c log_path is non-empty.
130  * Logging entry points are macros that wrap the public @c print_* templates and forward a
131  * compile-time @c Level so the bodies disappear when the level is below @c kMinimumLevel.
132  */
133 class VLINK_EXPORT Logger final {
134  public:
135  /**
136  * @brief Internal output style tag used by the @c print_* family.
137  *
138  * @details
139  * Callers normally interact with styles via macros; the enum is exposed for completeness.
140  */
141  enum Style : uint8_t {
142  kStreamStyle = 0, ///< Stream composition via @c FastStream operator<<.
143  kFormatStyle = 1, ///< Brace placeholders via @c vlink::format.
144  kCStyle = 2, ///< printf-style formatting via @c std::snprintf.
145  };
146 
147  /**
148  * @brief Message severity level.
149  *
150  * @details
151  * Lower numerical values are less severe. @c kOff is a sentinel that disables a sink.
152  */
153  enum Level : uint8_t {
154  kTrace = 0, ///< Verbose tracing.
155  kDebug = 1, ///< Developer diagnostics.
156  kInfo = 2, ///< Normal operational message.
157  kWarn = 3, ///< Recoverable but unusual condition.
158  kError = 4, ///< Recoverable error.
159  kFatal = 5, ///< Unrecoverable; throws @c Exception::RuntimeError.
160  kOff = 6, ///< Disable sink.
161  };
162 
163  /**
164  * @brief Compile-time minimum severity level; messages below this are stripped.
165  *
166  * @details
167  * Override by defining @c VLINK_LOG_LEVEL before including this header. Defaults to
168  * @c kTrace so every level is emitted by default.
169  */
170 #ifdef VLINK_LOG_LEVEL
171  static constexpr uint8_t kMinimumLevel = VLINK_LOG_LEVEL;
172 #else
173  static constexpr uint8_t kMinimumLevel = kTrace;
174 #endif
175 
176  /**
177  * @brief Severity threshold at and above which @c {file:line} is prepended to messages.
178  *
179  * @details
180  * Override by defining @c VLINK_LOG_DETAIL_LEVEL before including this header. Defaults to
181  * @c kWarn.
182  */
183 #ifdef VLINK_LOG_DETAIL_LEVEL
184  static constexpr uint8_t kDetailLevel = VLINK_LOG_DETAIL_LEVEL;
185 #else
186  static constexpr uint8_t kDetailLevel = kWarn;
187 #endif
188 
189  /**
190  * @brief Size of the thread-local C-style format buffer in bytes.
191  *
192  * @details
193  * Messages longer than @c kLocalBufferSize @c - @c 1 characters are silently truncated.
194  */
195  static constexpr int kLocalBufferSize = 4096;
196 
197  /**
198  * @brief Signature for custom console / file sink callbacks.
199  *
200  * @details
201  * Invoked synchronously from the logging thread; the @c std::string_view is valid only for
202  * the duration of the call.
203  */
204  using Callback = MoveFunction<void(Level, std::string_view)>;
205 
206  /**
207  * @brief Carries the source file name and line number for the detail prefix.
208  *
209  * @details
210  * Built automatically by @c VLINK_LOG_GET_DETAIL when the message level reaches
211  * @c kDetailLevel.
212  */
213  using DetailInfo = std::pair<std::string_view, int>;
214 
215  /**
216  * @brief Sentinel type indicating no detail prefix is attached.
217  *
218  * @details
219  * Used to avoid capturing @c __FILE__ / @c __LINE__ for messages below @c kDetailLevel.
220  */
221  struct NoDetail {};
222 
223  /**
224  * @brief Initialises the logger singleton.
225  *
226  * @details
227  * Must be invoked before any logging macros. Subsequent calls reconfigure the singleton.
228  * Provide a non-empty @p log_path to activate the file sink.
229  *
230  * @param app_name Application name embedded in log output. Default: empty.
231  * @param log_path Absolute path for the file sink. Default: empty (no file sink).
232  */
233  static void init(const std::string& app_name = "", const std::string& log_path = "") noexcept;
234 
235  /**
236  * @brief Returns the global logger instance.
237  *
238  * @return Reference to the singleton.
239  */
240  static Logger& get() noexcept;
241 
242  /**
243  * @brief Flushes every active sink.
244  *
245  * @details
246  * Useful before abnormal termination. Invoked automatically before a @c kFatal message
247  * throws.
248  */
249  static void flush() noexcept;
250 
251  /**
252  * @brief Installs a custom console sink callback replacing the built-in console writer.
253  *
254  * @param callback Handler called with @c (level, @c message_view) for each record.
255  */
256  static void register_console_handler(Callback&& callback) noexcept;
257 
258  /**
259  * @brief Installs a custom file sink callback replacing the built-in file writer.
260  *
261  * @param callback Handler called with @c (level, @c message_view) for each record.
262  */
263  static void register_file_handler(Callback&& callback) noexcept;
264 
265  /**
266  * @brief Sets the minimum severity for the console sink; pass @c kOff to mute it.
267  *
268  * @param level Minimum output level.
269  */
270  static void set_console_level(Level level) noexcept;
271 
272  /**
273  * @brief Sets the minimum severity for the file sink; pass @c kOff to mute it.
274  *
275  * @param level Minimum output level.
276  */
277  static void set_file_level(Level level) noexcept;
278 
279  /**
280  * @brief Enables or disables ANSI colour / formatting on the console sink.
281  *
282  * @param enable @c true to keep ANSI escapes (default), @c false for plain text.
283  */
284  static void set_console_fmt_enable(bool enable) noexcept;
285 
286  /**
287  * @brief Returns the current console sink severity threshold.
288  *
289  * @return Current level.
290  */
291  [[nodiscard]] static Level get_console_level() noexcept;
292 
293  /**
294  * @brief Returns the current file sink severity threshold.
295  *
296  * @return Current level.
297  */
298  [[nodiscard]] static Level get_file_level() noexcept;
299 
300  /**
301  * @brief Returns whether ANSI colour codes are enabled on the console sink.
302  *
303  * @return @c true when ANSI escapes are emitted.
304  */
305  [[nodiscard]] static bool get_console_fmt_enable() noexcept;
306 
307  /**
308  * @brief Sets @c std::ios_base format flags applied to stream-style records.
309  *
310  * @param flags Stream format flags.
311  */
312  static void set_stream_flag(std::ios_base::fmtflags flags) noexcept;
313 
314  /**
315  * @brief Sets the floating-point precision for stream-style records.
316  *
317  * @param precision Precision passed to @c std::setprecision.
318  */
319  static void set_stream_precision(int precision) noexcept;
320 
321  /**
322  * @brief Sets the minimum field width for stream-style records.
323  *
324  * @param width Field width passed to @c std::setw.
325  */
326  static void set_stream_width(int width) noexcept;
327 
328  /**
329  * @brief Returns the stream format flags currently applied to stream-style records.
330  *
331  * @return Format flags.
332  */
333  [[nodiscard]] static std::ios_base::fmtflags get_stream_flag() noexcept;
334 
335  /**
336  * @brief Returns the floating-point precision used for stream-style records.
337  *
338  * @return Precision value.
339  */
340  [[nodiscard]] static int get_stream_precision() noexcept;
341 
342  /**
343  * @brief Returns the field width used for stream-style records.
344  *
345  * @return Width value.
346  */
347  [[nodiscard]] static int get_stream_width() noexcept;
348 
349  /**
350  * @brief Enables a ring-buffer backtrace of the most recent @p size records.
351  *
352  * @param size Capacity of the backtrace ring buffer.
353  */
354  static void enable_backtrace(size_t size) noexcept;
355 
356  /**
357  * @brief Disables backtrace capture and discards the ring buffer.
358  */
359  static void disable_backtrace() noexcept;
360 
361  /**
362  * @brief Flushes the backtrace ring buffer to the active sinks.
363  */
364  static void dump_backtrace() noexcept;
365 
366  /**
367  * @brief Reports whether the logger is currently writing a record.
368  *
369  * @return @c true while a write is in progress.
370  */
371  [[nodiscard]] static bool is_busy() noexcept;
372 
373  /**
374  * @brief Reports whether a record at @p level would currently be emitted.
375  *
376  * @details
377  * Use the result to gate expensive argument computation before a macro call.
378  *
379  * @param level Severity level under test.
380  * @return @c true when the level passes either sink threshold.
381  */
382  [[nodiscard]] static bool is_writable(Level level) noexcept;
383 
384  /**
385  * @brief Attempts to acquire the current period for one limited log call site.
386  *
387  * @param level Severity from @c kTrace through @c kError; other values are rejected.
388  * @param interval_ms Minimum interval in milliseconds; non-positive values disable limiting.
389  * @param last_log_time_ns Per-call-site monotonic timestamp, initially @c 0.
390  * @return @c true when the level is writable and the current period is acquired.
391  */
392  [[nodiscard]] static bool try_acquire_periodic_log(Level level, int64_t interval_ms,
393  std::atomic<uint64_t>& last_log_time_ns) noexcept;
394 
395  /**
396  * @brief Strips a path down to its final filename component at compile time.
397  *
398  * @param path Source path, typically @c __FILE__.
399  * @return View covering the filename portion.
400  */
401  [[nodiscard]] static constexpr std::string_view extract_filename(std::string_view path) noexcept;
402 
403  /**
404  * @brief Stream-style entry point (used by @c VLOG_* / @c print).
405  *
406  * @details
407  * Returns immediately when @c should_log<LevelT>() is @c false. Otherwise streams @p args
408  * into a thread-local @c FastStream and hands the resulting view to the sinks.
409  *
410  * @tparam LevelT Compile-time severity.
411  * @tparam DetailT Either @c DetailInfo or @c NoDetail.
412  * @tparam ArgsT Stream argument types.
413  * @param detail Source location info or @c NoDetail{}.
414  * @param args Values to stream.
415  */
416  template <Level LevelT, typename DetailT, typename... ArgsT>
417  static void print_stream_style(DetailT&& detail, ArgsT&&... args);
418 
419  /**
420  * @brief Placeholder-style entry point (used by @c MLOG_*).
421  *
422  * @tparam LevelT Compile-time severity.
423  * @tparam DetailT Either @c DetailInfo or @c NoDetail.
424  * @tparam ArgsT Format argument types.
425  * @param detail Source location info or @c NoDetail{}.
426  * @param format Format string with @c {} placeholders.
427  * @param args Format arguments.
428  */
429  template <Level LevelT, typename DetailT, typename... ArgsT>
430  static void print_format_style(DetailT&& detail, format::format_string<ArgsT...> format, ArgsT&&... args);
431 
432  /**
433  * @brief printf-style entry point (used by @c CLOG_*).
434  *
435  * @tparam LevelT Compile-time severity.
436  * @tparam DetailT Either @c DetailInfo or @c NoDetail.
437  * @tparam FormatT Format string type, typically @c const @c char*.
438  * @tparam ArgsT printf argument types.
439  * @param detail Source location info or @c NoDetail{}.
440  * @param format printf-style format string.
441  * @param args printf arguments.
442  */
443  template <Logger::Level LevelT, typename DetailT, typename FormatT, typename... ArgsT>
444  static void print_c_style(DetailT&& detail, FormatT&& format, ArgsT&&... args);
445 
446  /**
447  * @brief Convenience stream-style entry point without source location.
448  *
449  * @tparam LevelT Compile-time severity.
450  * @tparam ArgsT Stream argument types.
451  * @param args Values to stream.
452  */
453  template <Level LevelT, typename... ArgsT>
454  static void print(ArgsT&&... args);
455 
456  /**
457  * @class WrapperStream
458  * @brief RAII helper backing @c SLOG_*; collects tokens and flushes on destruction.
459  *
460  * @details
461  * When @c kIsEnabled is @c false at the chosen level the type and its methods compile to
462  * nothing, so disabled-level call sites have zero runtime overhead.
463  *
464  * @tparam LevelT Compile-time severity level.
465  */
466  template <Logger::Level LevelT>
467  class WrapperStream final {
468  public:
469  /**
470  * @brief Static gate indicating whether the wrapper emits at the chosen level.
471  */
472  static constexpr bool kIsEnabled = (LevelT >= kMinimumLevel && LevelT < Logger::kOff);
473 
474  explicit WrapperStream(Logger::NoDetail) noexcept {
475  if constexpr (kIsEnabled) {
476  if (should_log<LevelT>()) {
477  enabled_ = true;
478  stream_ = &Logger::get_local_stream();
479  }
480  }
481  }
482 
483  explicit WrapperStream(DetailInfo&& detail) noexcept {
484  if constexpr (kIsEnabled) {
485  if (should_log<LevelT>()) {
486  enabled_ = true;
487  stream_ = &Logger::get_local_stream();
488 
489  push_detail_to_stream(detail, *stream_);
490  }
491  }
492  }
493 
494  WrapperStream(WrapperStream&& other) noexcept : stream_(other.stream_), enabled_(other.enabled_) {
495  other.stream_ = nullptr;
496  other.enabled_ = false;
497  }
498 
500 
501  ~WrapperStream() noexcept(LevelT != Level::kFatal) {
502  if constexpr (kIsEnabled) {
503  if (enabled_) {
504  finalize_log<LevelT>(stream_->take_view());
505  }
506  }
507  }
508 
509  template <typename T>
510  WrapperStream& operator<<(T&& t) noexcept {
511  if constexpr (kIsEnabled) {
512  if (enabled_) {
513  *stream_ << std::forward<T>(t);
514  }
515  }
516 
517  return *this;
518  }
519 
520  private:
522 
523  FastStream* stream_{nullptr};
524  bool enabled_{false};
525  };
526 
527  private:
528  Logger() noexcept;
529 
530  ~Logger() noexcept;
531 
532  template <Level LevelT>
533  static bool should_log() noexcept;
534 
535  template <Level LevelT>
536  static void finalize_log(std::string_view log_view);
537 
538  template <typename DetailT>
539  static void push_detail_to_stream(DetailT&& detail, FastStream& stream) noexcept;
540 
541  template <typename DetailT>
542  static std::string_view format_with_detail(DetailT&& detail, const char* msg, int len) noexcept;
543 
544  static char* get_local_buffer() noexcept;
545 
546  static FastStream& get_local_stream() noexcept;
547 
548  void write_to_console(Level level, std::string_view log) noexcept;
549 
550  void write_to_file(Level level, std::string_view log) noexcept;
551 
552  struct Impl;
553  std::unique_ptr<Impl> impl_;
554 
555  template <Logger::Level LevelT>
556  friend class WrapperStream;
557 
559 
561 };
562 
563 ////////////////////////////////////////////////////////////////
564 /// Details
565 ////////////////////////////////////////////////////////////////
566 
567 inline constexpr std::string_view Logger::extract_filename(std::string_view path) noexcept {
568  auto pos = path.find_last_of("/\\");
569  return (pos == std::string_view::npos) ? path : path.substr(pos + 1);
570 }
571 
572 template <Logger::Level LevelT, typename DetailT, typename... ArgsT>
573 inline void Logger::print_stream_style([[maybe_unused]] DetailT&& detail, [[maybe_unused]] ArgsT&&... args) {
574  if (!should_log<LevelT>()) {
575  return;
576  }
577 
578  auto& stream = get_local_stream();
579 
580  if constexpr (std::is_same_v<std::decay_t<DetailT>, DetailInfo>) {
581  push_detail_to_stream(detail, stream);
582  }
583 
584  (void)(stream << ... << args);
585 
586  finalize_log<LevelT>(stream.take_view());
587 }
588 
589 template <Logger::Level LevelT, typename DetailT, typename... ArgsT>
590 inline void Logger::print_format_style([[maybe_unused]] DetailT&& detail,
591  [[maybe_unused]] format::format_string<ArgsT...> format,
592  [[maybe_unused]] ArgsT&&... args) {
593  if (!should_log<LevelT>()) {
594  return;
595  }
596 
597  std::string_view log_view;
598 
599  auto* local_buffer = get_local_buffer();
600  auto result = format::format_to_n(local_buffer, kLocalBufferSize - 1, format, std::forward<ArgsT>(args)...);
601  auto written = static_cast<int>(result.out - local_buffer);
602 
603  local_buffer[written] = '\0';
604 
605  log_view = format_with_detail(detail, local_buffer, written);
606 
607  finalize_log<LevelT>(log_view);
608 }
609 
610 template <Logger::Level LevelT, typename DetailT, typename FormatT, typename... ArgsT>
611 inline void Logger::print_c_style([[maybe_unused]] DetailT&& detail, [[maybe_unused]] FormatT&& format,
612  [[maybe_unused]] ArgsT&&... args) {
613  if (!should_log<LevelT>()) {
614  return;
615  }
616 
617  std::string_view log_view;
618 
619  if constexpr (sizeof...(ArgsT) == 0) {
620  auto& stream = get_local_stream();
621 
622  if constexpr (std::is_same_v<std::decay_t<DetailT>, DetailInfo>) {
623  push_detail_to_stream(detail, stream);
624  }
625 
626  stream << format;
627  log_view = stream.take_view();
628  } else {
629  auto* local_buffer = get_local_buffer();
630  auto written = std::snprintf(local_buffer, kLocalBufferSize - 1, format, args...);
631 
632  if VUNLIKELY (written < 0) {
633  written = 0;
634  } else if VUNLIKELY (written > kLocalBufferSize - 1) {
635  written = kLocalBufferSize - 1;
636  }
637 
638  log_view = format_with_detail(detail, local_buffer, written);
639  }
640 
641  finalize_log<LevelT>(log_view);
642 }
643 
644 template <Logger::Level LevelT, typename... ArgsT>
645 inline void Logger::print([[maybe_unused]] ArgsT&&... args) {
646  print_stream_style<LevelT>(NoDetail{}, args...);
647 }
648 
649 template <Logger::Level LevelT>
650 inline bool Logger::should_log() noexcept {
651  if constexpr (LevelT < Logger::kMinimumLevel || LevelT >= Logger::kOff) {
652  return false;
653  } else if constexpr (LevelT == Logger::kFatal) {
654  return true;
655  } else {
656  return Logger::is_writable(LevelT);
657  }
658 }
659 
660 template <Logger::Level LevelT>
661 inline void Logger::finalize_log(std::string_view log_view) {
662  Logger& instance = Logger::get();
663 
664  instance.write_to_console(LevelT, log_view);
665  instance.write_to_file(LevelT, log_view);
666 
667  if constexpr (LevelT == Logger::kFatal) {
668  Logger::flush();
669  throw Exception::RuntimeError(std::string(log_view));
670  }
671 }
672 
673 template <typename DetailT>
674 inline void Logger::push_detail_to_stream(DetailT&& detail, FastStream& stream) noexcept {
675  auto& [file, line] = detail;
676  stream << "{" << file << ":" << line << "} ";
677 }
678 
679 template <typename DetailT>
680 inline std::string_view Logger::format_with_detail(DetailT&& detail, const char* msg, int len) noexcept {
681  if constexpr (std::is_same_v<std::decay_t<DetailT>, Logger::DetailInfo>) {
682  auto& stream = Logger::get_local_stream();
683 
684  push_detail_to_stream(detail, stream);
685 
686  if VLIKELY (len > 0) {
687  stream.write_raw(msg, static_cast<size_t>(len));
688  }
689 
690  return stream.take_view();
691  } else {
692  if VLIKELY (len > 0) {
693  return std::string_view(msg, static_cast<size_t>(len));
694  }
695 
696  return {};
697  }
698 }
699 
700 } // namespace vlink
701 
703 
704 ////////////////////////////////////////////////////////////////
705 /// Macro Definitions
706 ////////////////////////////////////////////////////////////////
707 
708 #define VLINK_LOG_GET_DETAIL(level) \
709  ([]() -> auto { \
710  if constexpr ((level) >= VLinkLogger::kDetailLevel) { \
711  return VLinkLogger::DetailInfo{VLinkLogger::extract_filename(__FILE__), __LINE__}; \
712  } else { \
713  return VLinkLogger::NoDetail{}; \
714  } \
715  })()
716 
717 #define VLINK_LOG_HEX(offset) std::hex, std::uppercase, std::setw(offset), std::setfill('0')
718 
719 #define VLINK_LOG_HEXSS(offset) std::hex << std::uppercase << std::setw(offset) << std::setfill('0')
720 
721 #define VLINK_LOG_IF_T VLinkLogger::is_writable(VLinkLogger::kTrace)
722 
723 #define VLINK_LOG_IF_D VLinkLogger::is_writable(VLinkLogger::kDebug)
724 
725 #define VLINK_LOG_IF_I VLinkLogger::is_writable(VLinkLogger::kInfo)
726 
727 #define VLINK_LOG_IF_W VLinkLogger::is_writable(VLinkLogger::kWarn)
728 
729 #define VLINK_LOG_IF_E VLinkLogger::is_writable(VLinkLogger::kError)
730 
731 #define VLINK_LOG_IF_F VLinkLogger::is_writable(VLinkLogger::kFatal)
732 
733 #define VLINK_LOG_T(...) \
734  VLinkLogger::print_stream_style<VLinkLogger::kTrace>(VLINK_LOG_GET_DETAIL(VLinkLogger::kTrace), __VA_ARGS__)
735 
736 #define VLINK_LOG_D(...) \
737  VLinkLogger::print_stream_style<VLinkLogger::kDebug>(VLINK_LOG_GET_DETAIL(VLinkLogger::kDebug), __VA_ARGS__)
738 
739 #define VLINK_LOG_I(...) \
740  VLinkLogger::print_stream_style<VLinkLogger::kInfo>(VLINK_LOG_GET_DETAIL(VLinkLogger::kInfo), __VA_ARGS__)
741 
742 #define VLINK_LOG_W(...) \
743  VLinkLogger::print_stream_style<VLinkLogger::kWarn>(VLINK_LOG_GET_DETAIL(VLinkLogger::kWarn), __VA_ARGS__)
744 
745 #define VLINK_LOG_E(...) \
746  VLinkLogger::print_stream_style<VLinkLogger::kError>(VLINK_LOG_GET_DETAIL(VLinkLogger::kError), __VA_ARGS__)
747 
748 #define VLINK_LOG_F(...) \
749  VLinkLogger::print_stream_style<VLinkLogger::kFatal>(VLINK_LOG_GET_DETAIL(VLinkLogger::kFatal), __VA_ARGS__)
750 
751 #define VLINK_MLOG_T(...) \
752  VLinkLogger::print_format_style<VLinkLogger::kTrace>(VLINK_LOG_GET_DETAIL(VLinkLogger::kTrace), __VA_ARGS__)
753 
754 #define VLINK_MLOG_D(...) \
755  VLinkLogger::print_format_style<VLinkLogger::kDebug>(VLINK_LOG_GET_DETAIL(VLinkLogger::kDebug), __VA_ARGS__)
756 
757 #define VLINK_MLOG_I(...) \
758  VLinkLogger::print_format_style<VLinkLogger::kInfo>(VLINK_LOG_GET_DETAIL(VLinkLogger::kInfo), __VA_ARGS__)
759 
760 #define VLINK_MLOG_W(...) \
761  VLinkLogger::print_format_style<VLinkLogger::kWarn>(VLINK_LOG_GET_DETAIL(VLinkLogger::kWarn), __VA_ARGS__)
762 
763 #define VLINK_MLOG_E(...) \
764  VLinkLogger::print_format_style<VLinkLogger::kError>(VLINK_LOG_GET_DETAIL(VLinkLogger::kError), __VA_ARGS__)
765 
766 #define VLINK_MLOG_F(...) \
767  VLinkLogger::print_format_style<VLinkLogger::kFatal>(VLINK_LOG_GET_DETAIL(VLinkLogger::kFatal), __VA_ARGS__)
768 
769 #define VLINK_CLOG_T(...) \
770  VLinkLogger::print_c_style<VLinkLogger::kTrace>(VLINK_LOG_GET_DETAIL(VLinkLogger::kTrace), __VA_ARGS__)
771 
772 #define VLINK_CLOG_D(...) \
773  VLinkLogger::print_c_style<VLinkLogger::kDebug>(VLINK_LOG_GET_DETAIL(VLinkLogger::kDebug), __VA_ARGS__)
774 
775 #define VLINK_CLOG_I(...) \
776  VLinkLogger::print_c_style<VLinkLogger::kInfo>(VLINK_LOG_GET_DETAIL(VLinkLogger::kInfo), __VA_ARGS__)
777 
778 #define VLINK_CLOG_W(...) \
779  VLinkLogger::print_c_style<VLinkLogger::kWarn>(VLINK_LOG_GET_DETAIL(VLinkLogger::kWarn), __VA_ARGS__)
780 
781 #define VLINK_CLOG_E(...) \
782  VLinkLogger::print_c_style<VLinkLogger::kError>(VLINK_LOG_GET_DETAIL(VLinkLogger::kError), __VA_ARGS__)
783 
784 #define VLINK_CLOG_F(...) \
785  VLinkLogger::print_c_style<VLinkLogger::kFatal>(VLINK_LOG_GET_DETAIL(VLinkLogger::kFatal), __VA_ARGS__)
786 
787 #define VLINK_SLOG_T VLinkLogger::WrapperStream<VLinkLogger::kTrace>(VLINK_LOG_GET_DETAIL(VLinkLogger::kTrace))
788 
789 #define VLINK_SLOG_D VLinkLogger::WrapperStream<VLinkLogger::kDebug>(VLINK_LOG_GET_DETAIL(VLinkLogger::kDebug))
790 
791 #define VLINK_SLOG_I VLinkLogger::WrapperStream<VLinkLogger::kInfo>(VLINK_LOG_GET_DETAIL(VLinkLogger::kInfo))
792 
793 #define VLINK_SLOG_W VLinkLogger::WrapperStream<VLinkLogger::kWarn>(VLINK_LOG_GET_DETAIL(VLinkLogger::kWarn))
794 
795 #define VLINK_SLOG_E VLinkLogger::WrapperStream<VLinkLogger::kError>(VLINK_LOG_GET_DETAIL(VLinkLogger::kError))
796 
797 #define VLINK_SLOG_F VLinkLogger::WrapperStream<VLinkLogger::kFatal>(VLINK_LOG_GET_DETAIL(VLinkLogger::kFatal))
798 
799 #define VLINK_LOG_EVERY_MS_IMPL(level, interval_ms, ...) \
800  do { \
801  if constexpr ((level) >= VLinkLogger::kMinimumLevel && (level) < VLinkLogger::kFatal) { \
802  if VUNLIKELY ([](int64_t vlink_interval_ms) noexcept { \
803  static std::atomic<uint64_t> vlink_last_log_time_ns{0}; \
804  return VLinkLogger::try_acquire_periodic_log(level, vlink_interval_ms, vlink_last_log_time_ns); \
805  }(static_cast<int64_t>(interval_ms))) { \
806  VLinkLogger::print_stream_style<level>(VLINK_LOG_GET_DETAIL(level), __VA_ARGS__); \
807  } \
808  } \
809  } while (false)
810 
811 #define VLINK_LOG_T_EVERY_MS(interval_ms, ...) VLINK_LOG_EVERY_MS_IMPL(VLinkLogger::kTrace, interval_ms, __VA_ARGS__)
812 
813 #define VLINK_LOG_D_EVERY_MS(interval_ms, ...) VLINK_LOG_EVERY_MS_IMPL(VLinkLogger::kDebug, interval_ms, __VA_ARGS__)
814 
815 #define VLINK_LOG_I_EVERY_MS(interval_ms, ...) VLINK_LOG_EVERY_MS_IMPL(VLinkLogger::kInfo, interval_ms, __VA_ARGS__)
816 
817 #define VLINK_LOG_W_EVERY_MS(interval_ms, ...) VLINK_LOG_EVERY_MS_IMPL(VLinkLogger::kWarn, interval_ms, __VA_ARGS__)
818 
819 #define VLINK_LOG_E_EVERY_MS(interval_ms, ...) VLINK_LOG_EVERY_MS_IMPL(VLinkLogger::kError, interval_ms, __VA_ARGS__)
820 
821 #ifndef VLINK_LOG_DISABLE_SHORT
822 
823 #define VLOG_T_EVERY_MS(interval_ms, ...) VLINK_LOG_T_EVERY_MS(interval_ms, __VA_ARGS__)
824 
825 #define VLOG_D_EVERY_MS(interval_ms, ...) VLINK_LOG_D_EVERY_MS(interval_ms, __VA_ARGS__)
826 
827 #define VLOG_I_EVERY_MS(interval_ms, ...) VLINK_LOG_I_EVERY_MS(interval_ms, __VA_ARGS__)
828 
829 #define VLOG_W_EVERY_MS(interval_ms, ...) VLINK_LOG_W_EVERY_MS(interval_ms, __VA_ARGS__)
830 
831 #define VLOG_E_EVERY_MS(interval_ms, ...) VLINK_LOG_E_EVERY_MS(interval_ms, __VA_ARGS__)
832 
833 #define VLOG_T(...) VLINK_LOG_T(__VA_ARGS__)
834 
835 #define VLOG_D(...) VLINK_LOG_D(__VA_ARGS__)
836 
837 #define VLOG_I(...) VLINK_LOG_I(__VA_ARGS__)
838 
839 #define VLOG_W(...) VLINK_LOG_W(__VA_ARGS__)
840 
841 #define VLOG_E(...) VLINK_LOG_E(__VA_ARGS__)
842 
843 #define VLOG_F(...) VLINK_LOG_F(__VA_ARGS__)
844 
845 #define CLOG_T(...) VLINK_CLOG_T(__VA_ARGS__)
846 
847 #define CLOG_D(...) VLINK_CLOG_D(__VA_ARGS__)
848 
849 #define CLOG_I(...) VLINK_CLOG_I(__VA_ARGS__)
850 
851 #define CLOG_W(...) VLINK_CLOG_W(__VA_ARGS__)
852 
853 #define CLOG_E(...) VLINK_CLOG_E(__VA_ARGS__)
854 
855 #define CLOG_F(...) VLINK_CLOG_F(__VA_ARGS__)
856 
857 #define MLOG_T(...) VLINK_MLOG_T(__VA_ARGS__)
858 
859 #define MLOG_D(...) VLINK_MLOG_D(__VA_ARGS__)
860 
861 #define MLOG_I(...) VLINK_MLOG_I(__VA_ARGS__)
862 
863 #define MLOG_W(...) VLINK_MLOG_W(__VA_ARGS__)
864 
865 #define MLOG_E(...) VLINK_MLOG_E(__VA_ARGS__)
866 
867 #define MLOG_F(...) VLINK_MLOG_F(__VA_ARGS__)
868 
869 #define SLOG_T VLINK_SLOG_T
870 
871 #define SLOG_D VLINK_SLOG_D
872 
873 #define SLOG_I VLINK_SLOG_I
874 
875 #define SLOG_W VLINK_SLOG_W
876 
877 #define SLOG_E VLINK_SLOG_E
878 
879 #define SLOG_F VLINK_SLOG_F
880 
881 #endif
Thin final wrappers around the standard exception hierarchy used by VLink.
Allocation-light std::ostream backed by a growable string buffer.
Minimal heap-free {} placeholder formatter for the logger hot path.
Pool-backed type-erased callables: copyable vlink::Function and move-only vlink::MoveFunction.
Cross-platform macros for visibility, branch hints, copy prevention, singletons and string helpers.
#define VUNLIKELY(...)
Short alias for VLINK_UNLIKELY.
Definition: macros.h:289
#define VLINK_SINGLETON_CHECK(classname)
Embeds a static check that enforces one-instance-per-process for classname.
Definition: macros.h:192
#define VLINK_EXPORT
Definition: macros.h:81
#define VLIKELY(...)
Short alias for VLINK_LIKELY.
Definition: macros.h:284
#define VLINK_DISALLOW_COPY_AND_ASSIGN(classname)
Deletes the copy constructor and copy-assignment operator of classname.
Definition: macros.h:174