@@ -68,8 +68,8 @@ struct LogMessage {
6868// / \brief Well-known Logger::Initialize() property keys.
6969// /
7070// / `level` is honored by the base Logger::Initialize (parsed via
71- // / LogLevelFromString). `pattern` is honored by the formatting sinks
72- // / ( CerrLogger, SpdLogger) .
71+ // / LogLevelFromString) on every backend . `pattern` is honored only by the
72+ // / spdlog backend; CerrLogger uses a fixed layout and ignores it .
7373inline constexpr std::string_view kLevelProperty = " level" ;
7474inline constexpr std::string_view kPatternProperty = " pattern" ;
7575
@@ -211,3 +211,175 @@ std::string VFormat(std::string_view fmt, Args&&... args) {
211211} // namespace detail
212212
213213} // namespace iceberg
214+
215+ // ---------------------------------------------------------------------------
216+ // Logging macros.
217+ //
218+ // Every macro takes a std::format string followed by its arguments. The
219+ // rendered line depends on the active backend (see cerr_logger.h for the
220+ // std::cerr layout, or the spdlog pattern); the examples below show the call
221+ // site and, for the default CerrLogger, the line it produces.
222+ //
223+ // ICEBERG_LOG_TRACE("entering scan for {}", table);
224+ // 2026-06-16T10:59:41.186Z trace [12345] table_scan.cc:88] entering scan for db.t
225+ // ICEBERG_LOG_DEBUG("cache miss key={}", key);
226+ // 2026-06-16T10:59:41.186Z debug [12345] cache.cc:42] cache miss key=manifest-7
227+ // ICEBERG_LOG_INFO("loaded {} manifests in {} ms", n, ms);
228+ // 2026-06-16T10:59:41.186Z info [12345] table_scan.cc:91] loaded 5 manifests in 12 ms
229+ // ICEBERG_LOG_WARN("retry {} after {}", attempt, err);
230+ // 2026-06-16T10:59:41.186Z warn [12345] io.cc:51] retry 2 after timeout
231+ // ICEBERG_LOG_ERROR("commit failed: {}", status);
232+ // 2026-06-16T10:59:41.186Z error [12345] txn.cc:77] commit failed: conflict
233+ // ICEBERG_LOG_CRITICAL("metadata unreadable at {}", path);
234+ // 2026-06-16T10:59:41.186Z critical [12345] meta.cc:30] metadata unreadable at s3://b/m.json
235+ // ICEBERG_LOG_FATAL("unrecoverable: {}", reason); // emits, flushes, then std::abort()
236+ // 2026-06-16T10:59:41.186Z fatal [12345] boot.cc:19] unrecoverable: bad config
237+ //
238+ // Less common forms:
239+ // ICEBERG_LOG(level, "level chosen at runtime: {}", x); // runtime severity
240+ // ICEBERG_LOG_TO(logger, level, "to an explicit logger {}", y);
241+ // ICEBERG_LOG_RUNTIME_FMT(level, fmt_string, args...); // non-literal format
242+ //
243+ // With ICEBERG_LOG_SHORT_MACROS defined, bare aliases (LOG_INFO, ...) are also
244+ // available. A format string is mandatory; zero extra args is fine
245+ // (ICEBERG_LOG_INFO("done")).
246+ // ---------------------------------------------------------------------------
247+
248+ // / \brief Compile-time severity floor: statements below this level are removed
249+ // / entirely from the build (their format call sites and source_location literals
250+ // / are never emitted). Defaults to keeping everything. ICEBERG_LOG_FATAL is never
251+ // / gated by this floor -- its abort is always compiled in.
252+ #ifndef ICEBERG_LOG_ACTIVE_LEVEL
253+ # define ICEBERG_LOG_ACTIVE_LEVEL ::iceberg::LogLevel::kTrace
254+ #endif
255+
256+ // Internal: fixed-severity emit with compile-time floor then the authoritative
257+ // Logger::ShouldLog (the single source of truth for runtime filtering), with
258+ // formatting only on the taken path, never throwing.
259+ #define ICEBERG_INTERNAL_LOG (level_, FMT_ , ...) \
260+ do { \
261+ if constexpr ((level_) >= ICEBERG_LOG_ACTIVE_LEVEL ) { \
262+ const auto & _ib_logger = ::iceberg::detail::CurrentLogger (); \
263+ if (_ib_logger && _ib_logger->ShouldLog (level_)) { \
264+ try { \
265+ ::iceberg::detail::Emit (*_ib_logger, (level_), \
266+ ::std::source_location::current(), \
267+ ::std::format(FMT_ __VA_OPT__ (, ) __VA_ARGS__)); \
268+ } catch (...) { \
269+ ::iceberg::detail::EmitFormatError (*_ib_logger, (level_), \
270+ ::std::source_location::current()); \
271+ } \
272+ } \
273+ } \
274+ } while (0 )
275+
276+ #define ICEBERG_LOG_TRACE (...) \
277+ ICEBERG_INTERNAL_LOG (::iceberg::LogLevel::kTrace , __VA_ARGS__)
278+ #define ICEBERG_LOG_DEBUG (...) \
279+ ICEBERG_INTERNAL_LOG (::iceberg::LogLevel::kDebug , __VA_ARGS__)
280+ #define ICEBERG_LOG_INFO (...) \
281+ ICEBERG_INTERNAL_LOG (::iceberg::LogLevel::kInfo , __VA_ARGS__)
282+ #define ICEBERG_LOG_WARN (...) \
283+ ICEBERG_INTERNAL_LOG (::iceberg::LogLevel::kWarn , __VA_ARGS__)
284+ #define ICEBERG_LOG_ERROR (...) \
285+ ICEBERG_INTERNAL_LOG (::iceberg::LogLevel::kError , __VA_ARGS__)
286+ #define ICEBERG_LOG_CRITICAL (...) \
287+ ICEBERG_INTERNAL_LOG (::iceberg::LogLevel::kCritical , __VA_ARGS__)
288+
289+ // FATAL: emit if enabled (never compile-stripped), then ALWAYS flush + abort.
290+ // Acquires the default logger ONCE and uses the same instance for emit and flush
291+ // so a concurrent SetDefaultLogger cannot flush a different logger than it emitted to.
292+ #define ICEBERG_LOG_FATAL (FMT_ , ...) \
293+ do { \
294+ auto _ib_logger = ::iceberg::GetDefaultLogger (); \
295+ if (_ib_logger && _ib_logger->ShouldLog (::iceberg::LogLevel::kFatal )) { \
296+ try { \
297+ ::iceberg::detail::Emit (*_ib_logger, ::iceberg::LogLevel::kFatal , \
298+ ::std::source_location::current (), \
299+ ::std::format(FMT_ __VA_OPT__ (, ) __VA_ARGS__)); \
300+ } catch (...) { \
301+ ::iceberg::detail::EmitFormatError (*_ib_logger, ::iceberg::LogLevel::kFatal , \
302+ ::std::source_location::current ()); \
303+ } \
304+ } \
305+ if (_ib_logger) _ib_logger->Flush(); \
306+ ::std::abort (); \
307+ } while (0 )
308+
309+ // Generic, runtime-level form against the default logger. No compile-time floor
310+ // (the level is not a constant). Acquires the logger once; aborts when level == kFatal
311+ // (flushing that same logger first).
312+ #define ICEBERG_LOG (level_, FMT_ , ...) \
313+ do { \
314+ const ::iceberg::LogLevel _ib_lvl = (level_); \
315+ const auto & _ib_logger = ::iceberg::detail::CurrentLogger (); \
316+ if (_ib_logger && _ib_logger->ShouldLog (_ib_lvl)) { \
317+ try { \
318+ ::iceberg::detail::Emit (*_ib_logger, _ib_lvl, ::std::source_location::current(), \
319+ ::std::format(FMT_ __VA_OPT__ (, ) __VA_ARGS__)); \
320+ } catch (...) { \
321+ ::iceberg::detail::EmitFormatError (*_ib_logger, _ib_lvl, \
322+ ::std::source_location::current ()); \
323+ } \
324+ } \
325+ if (_ib_lvl == ::iceberg::LogLevel::kFatal ) { \
326+ if (_ib_logger) _ib_logger->Flush (); \
327+ ::std::abort (); \
328+ } \
329+ } while (0 )
330+
331+ // Generic form targeting an EXPLICIT logger (must be an lvalue Logger&). Honors
332+ // only that logger's ShouldLog. Aborts when level == kFatal.
333+ #define ICEBERG_LOG_TO (logger_, level_, FMT_ , ...) \
334+ do { \
335+ ::iceberg::Logger& _ib_logger = (logger_); \
336+ const ::iceberg::LogLevel _ib_lvl = (level_); \
337+ if (_ib_logger.ShouldLog (_ib_lvl)) { \
338+ try { \
339+ ::iceberg::detail::Emit (_ib_logger, _ib_lvl, ::std::source_location::current(), \
340+ ::std::format(FMT_ __VA_OPT__ (, ) __VA_ARGS__)); \
341+ } catch (...) { \
342+ ::iceberg::detail::EmitFormatError (_ib_logger, _ib_lvl, \
343+ ::std::source_location::current ()); \
344+ } \
345+ } \
346+ if (_ib_lvl == ::iceberg::LogLevel::kFatal ) { \
347+ _ib_logger.Flush (); \
348+ ::std::abort (); \
349+ } \
350+ } while (0 )
351+
352+ // Runtime (non-literal) format string against the default logger. Acquires the
353+ // logger once; aborts when level == kFatal (flushing that same logger first).
354+ #define ICEBERG_LOG_RUNTIME_FMT (level_, FMT_ , ...) \
355+ do { \
356+ const ::iceberg::LogLevel _ib_lvl = (level_); \
357+ const auto & _ib_logger = ::iceberg::detail::CurrentLogger (); \
358+ if (_ib_logger && _ib_logger->ShouldLog (_ib_lvl)) { \
359+ try { \
360+ ::iceberg::detail::Emit ( \
361+ *_ib_logger, _ib_lvl, ::std::source_location::current(), \
362+ ::iceberg::detail::VFormat((FMT_ )__VA_OPT__(, ) __VA_ARGS__)); \
363+ } catch (...) { \
364+ ::iceberg::detail::EmitFormatError (*_ib_logger, _ib_lvl, \
365+ ::std::source_location::current ()); \
366+ } \
367+ } \
368+ if (_ib_lvl == ::iceberg::LogLevel::kFatal ) { \
369+ if (_ib_logger) _ib_logger->Flush (); \
370+ ::std::abort (); \
371+ } \
372+ } while (0 )
373+
374+ // Bare, Java-style aliases. Opt-IN only (define ICEBERG_LOG_SHORT_MACROS before
375+ // including this header) to avoid colliding with glog/abseil/windows.h in
376+ // consumer translation units. No bare LOG(level) is provided.
377+ #ifdef ICEBERG_LOG_SHORT_MACROS
378+ # define LOG_TRACE (...) ICEBERG_LOG_TRACE (__VA_ARGS__)
379+ # define LOG_DEBUG (...) ICEBERG_LOG_DEBUG (__VA_ARGS__)
380+ # define LOG_INFO (...) ICEBERG_LOG_INFO (__VA_ARGS__)
381+ # define LOG_WARN (...) ICEBERG_LOG_WARN (__VA_ARGS__)
382+ # define LOG_ERROR (...) ICEBERG_LOG_ERROR (__VA_ARGS__)
383+ # define LOG_CRITICAL (...) ICEBERG_LOG_CRITICAL (__VA_ARGS__)
384+ # define LOG_FATAL (...) ICEBERG_LOG_FATAL (__VA_ARGS__)
385+ #endif // ICEBERG_LOG_SHORT_MACROS
0 commit comments