@@ -371,3 +371,179 @@ void Log(Logger& logger, LogLevel level,
371371}
372372
373373} // namespace iceberg
374+
375+ // ---------------------------------------------------------------------------
376+ // Logging macros.
377+ //
378+ // Every macro takes a std::format string followed by its arguments. The
379+ // rendered line depends on the active backend (see cerr_logger.h for the
380+ // std::cerr layout, or the spdlog pattern); the examples below show the call
381+ // site and, for the default CerrLogger, the line it produces.
382+ //
383+ // ICEBERG_LOG_TRACE("entering scan for {}", table);
384+ // 2026-06-16T10:59:41.186Z trace [12345] table_scan.cc:88] entering scan for db.t
385+ // ICEBERG_LOG_DEBUG("cache miss key={}", key);
386+ // 2026-06-16T10:59:41.186Z debug [12345] cache.cc:42] cache miss key=manifest-7
387+ // ICEBERG_LOG_INFO("loaded {} manifests in {} ms", n, ms);
388+ // 2026-06-16T10:59:41.186Z info [12345] table_scan.cc:91] loaded 5 manifests in 12 ms
389+ // ICEBERG_LOG_WARN("retry {} after {}", attempt, err);
390+ // 2026-06-16T10:59:41.186Z warn [12345] io.cc:51] retry 2 after timeout
391+ // ICEBERG_LOG_ERROR("commit failed: {}", status);
392+ // 2026-06-16T10:59:41.186Z error [12345] txn.cc:77] commit failed: conflict
393+ // ICEBERG_LOG_CRITICAL("metadata unreadable at {}", path);
394+ // 2026-06-16T10:59:41.186Z critical [12345] meta.cc:30] metadata unreadable at
395+ // s3://b/m.json
396+ // ICEBERG_LOG_FATAL("unrecoverable: {}", reason); // emits, flushes, then
397+ // std::abort()
398+ // 2026-06-16T10:59:41.186Z fatal [12345] boot.cc:19] unrecoverable: bad config
399+ //
400+ // Less common forms:
401+ // ICEBERG_LOG(level, "level chosen at runtime: {}", x); // runtime severity
402+ // ICEBERG_LOG_TO(logger, level, "to an explicit logger {}", y);
403+ // ICEBERG_LOG_RUNTIME_FMT(level, fmt_string, args...); // non-literal format
404+ //
405+ // With ICEBERG_LOG_SHORT_MACROS defined, bare aliases (LOG_INFO, ...) are also
406+ // available. A format string is mandatory; zero extra args is fine
407+ // (ICEBERG_LOG_INFO("done")).
408+ // ---------------------------------------------------------------------------
409+
410+ // / \brief Compile-time severity floor: statements below this level are removed
411+ // / entirely from the build (their format call sites and source_location literals
412+ // / are never emitted). Defaults to keeping everything. ICEBERG_LOG_FATAL is never
413+ // / gated by this floor -- its abort is always compiled in.
414+ #ifndef ICEBERG_LOG_ACTIVE_LEVEL
415+ # define ICEBERG_LOG_ACTIVE_LEVEL ::iceberg::LogLevel::kTrace
416+ #endif
417+
418+ // Internal: fixed-severity emit with compile-time floor then the authoritative
419+ // Logger::ShouldLog (the single source of truth for runtime filtering), with
420+ // formatting only on the taken path, never throwing.
421+ #define ICEBERG_INTERNAL_LOG (level_, FMT_ , ...) \
422+ do { \
423+ if constexpr ((level_) >= ICEBERG_LOG_ACTIVE_LEVEL ) { \
424+ const auto & _ib_logger = ::iceberg::internal::CurrentLogger (); \
425+ if (_ib_logger && _ib_logger->ShouldLog (level_)) { \
426+ try { \
427+ ::iceberg::internal::Emit (*_ib_logger, (level_), \
428+ ::std::source_location::current(), \
429+ ::std::format(FMT_ __VA_OPT__ (, ) __VA_ARGS__)); \
430+ } catch (...) { \
431+ ::iceberg::internal::EmitFormatError (*_ib_logger, (level_), \
432+ ::std::source_location::current()); \
433+ } \
434+ } \
435+ } \
436+ } while (0 )
437+
438+ #define ICEBERG_LOG_TRACE (...) \
439+ ICEBERG_INTERNAL_LOG (::iceberg::LogLevel::kTrace , __VA_ARGS__)
440+ #define ICEBERG_LOG_DEBUG (...) \
441+ ICEBERG_INTERNAL_LOG (::iceberg::LogLevel::kDebug , __VA_ARGS__)
442+ #define ICEBERG_LOG_INFO (...) \
443+ ICEBERG_INTERNAL_LOG (::iceberg::LogLevel::kInfo , __VA_ARGS__)
444+ #define ICEBERG_LOG_WARN (...) \
445+ ICEBERG_INTERNAL_LOG (::iceberg::LogLevel::kWarn , __VA_ARGS__)
446+ #define ICEBERG_LOG_ERROR (...) \
447+ ICEBERG_INTERNAL_LOG (::iceberg::LogLevel::kError , __VA_ARGS__)
448+ #define ICEBERG_LOG_CRITICAL (...) \
449+ ICEBERG_INTERNAL_LOG (::iceberg::LogLevel::kCritical , __VA_ARGS__)
450+
451+ // FATAL: emit if enabled (never compile-stripped), then ALWAYS flush + abort.
452+ // Acquires the default logger ONCE and uses the same instance for emit and flush
453+ // so a concurrent SetDefaultLogger cannot flush a different logger than it emitted to.
454+ #define ICEBERG_LOG_FATAL (FMT_ , ...) \
455+ do { \
456+ auto _ib_logger = ::iceberg::GetDefaultLogger (); \
457+ if (_ib_logger && _ib_logger->ShouldLog (::iceberg::LogLevel::kFatal )) { \
458+ try { \
459+ ::iceberg::internal::Emit (*_ib_logger, ::iceberg::LogLevel::kFatal , \
460+ ::std::source_location::current (), \
461+ ::std::format(FMT_ __VA_OPT__ (, ) __VA_ARGS__)); \
462+ } catch (...) { \
463+ ::iceberg::internal::EmitFormatError (*_ib_logger, ::iceberg::LogLevel::kFatal , \
464+ ::std::source_location::current ()); \
465+ } \
466+ } \
467+ if (_ib_logger) _ib_logger->Flush(); \
468+ ::std::abort (); \
469+ } while (0 )
470+
471+ // Generic, runtime-level form against the default logger. No compile-time floor
472+ // (the level is not a constant). Acquires the logger once; aborts when level == kFatal
473+ // (flushing that same logger first).
474+ #define ICEBERG_LOG (level_, FMT_ , ...) \
475+ do { \
476+ const ::iceberg::LogLevel _ib_lvl = (level_); \
477+ const auto & _ib_logger = ::iceberg::internal::CurrentLogger (); \
478+ if (_ib_logger && _ib_logger->ShouldLog (_ib_lvl)) { \
479+ try { \
480+ ::iceberg::internal::Emit (*_ib_logger, _ib_lvl, \
481+ ::std::source_location::current (), \
482+ ::std::format(FMT_ __VA_OPT__ (, ) __VA_ARGS__)); \
483+ } catch (...) { \
484+ ::iceberg::internal::EmitFormatError (*_ib_logger, _ib_lvl, \
485+ ::std::source_location::current ()); \
486+ } \
487+ } \
488+ if (_ib_lvl == ::iceberg::LogLevel::kFatal ) { \
489+ if (_ib_logger) _ib_logger->Flush (); \
490+ ::std::abort (); \
491+ } \
492+ } while (0 )
493+
494+ // Generic form targeting an EXPLICIT logger (must be an lvalue Logger&). Honors
495+ // only that logger's ShouldLog. Aborts when level == kFatal.
496+ #define ICEBERG_LOG_TO (logger_, level_, FMT_ , ...) \
497+ do { \
498+ ::iceberg::Logger& _ib_logger = (logger_); \
499+ const ::iceberg::LogLevel _ib_lvl = (level_); \
500+ if (_ib_logger.ShouldLog (_ib_lvl)) { \
501+ try { \
502+ ::iceberg::internal::Emit (_ib_logger, _ib_lvl, \
503+ ::std::source_location::current (), \
504+ ::std::format(FMT_ __VA_OPT__ (, ) __VA_ARGS__)); \
505+ } catch (...) { \
506+ ::iceberg::internal::EmitFormatError (_ib_logger, _ib_lvl, \
507+ ::std::source_location::current ()); \
508+ } \
509+ } \
510+ if (_ib_lvl == ::iceberg::LogLevel::kFatal ) { \
511+ _ib_logger.Flush (); \
512+ ::std::abort (); \
513+ } \
514+ } while (0 )
515+
516+ // Runtime (non-literal) format string against the default logger. Acquires the
517+ // logger once; aborts when level == kFatal (flushing that same logger first).
518+ #define ICEBERG_LOG_RUNTIME_FMT (level_, FMT_ , ...) \
519+ do { \
520+ const ::iceberg::LogLevel _ib_lvl = (level_); \
521+ const auto & _ib_logger = ::iceberg::internal::CurrentLogger (); \
522+ if (_ib_logger && _ib_logger->ShouldLog (_ib_lvl)) { \
523+ try { \
524+ ::iceberg::internal::Emit ( \
525+ *_ib_logger, _ib_lvl, ::std::source_location::current(), \
526+ ::iceberg::internal::VFormat((FMT_ )__VA_OPT__(, ) __VA_ARGS__)); \
527+ } catch (...) { \
528+ ::iceberg::internal::EmitFormatError (*_ib_logger, _ib_lvl, \
529+ ::std::source_location::current ()); \
530+ } \
531+ } \
532+ if (_ib_lvl == ::iceberg::LogLevel::kFatal ) { \
533+ if (_ib_logger) _ib_logger->Flush (); \
534+ ::std::abort (); \
535+ } \
536+ } while (0 )
537+
538+ // Bare, Java-style aliases. Opt-IN only (define ICEBERG_LOG_SHORT_MACROS before
539+ // including this header) to avoid colliding with glog/abseil/windows.h in
540+ // consumer translation units. No bare LOG(level) is provided.
541+ #ifdef ICEBERG_LOG_SHORT_MACROS
542+ # define LOG_TRACE (...) ICEBERG_LOG_TRACE (__VA_ARGS__)
543+ # define LOG_DEBUG (...) ICEBERG_LOG_DEBUG (__VA_ARGS__)
544+ # define LOG_INFO (...) ICEBERG_LOG_INFO (__VA_ARGS__)
545+ # define LOG_WARN (...) ICEBERG_LOG_WARN (__VA_ARGS__)
546+ # define LOG_ERROR (...) ICEBERG_LOG_ERROR (__VA_ARGS__)
547+ # define LOG_CRITICAL (...) ICEBERG_LOG_CRITICAL (__VA_ARGS__)
548+ # define LOG_FATAL (...) ICEBERG_LOG_FATAL (__VA_ARGS__)
549+ #endif // ICEBERG_LOG_SHORT_MACROS
0 commit comments