iceberg-cpp
Loading...
Searching...
No Matches
log_macros.h
Go to the documentation of this file.
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20#pragma once
21
29
30#include <cstdlib>
31#include <format>
32#include <memory>
33#include <source_location>
34#include <string>
35#include <string_view>
36#include <utility>
37
40
41namespace iceberg::internal {
42
49template <typename... Args>
50std::string VFormat(std::string_view fmt, Args&&... args) {
51 auto store = std::make_format_args(args...);
52 return std::vformat(fmt, store);
53}
54
61template <typename MakeMessage>
62void EmitIfEnabled(Logger& logger, LogLevel level, const std::source_location& location,
63 MakeMessage&& make_message) noexcept {
64 if (!logger.ShouldLog(level)) return;
65 try {
66 Emit(logger, level, location, std::forward<MakeMessage>(make_message)());
67 } catch (...) {
68 EmitFormatError(logger, level, location);
69 }
70}
71
73template <typename MakeMessage>
74void LogToCurrent(LogLevel level, const std::source_location& location,
75 MakeMessage&& make_message) noexcept {
76 const std::shared_ptr<Logger>& logger = CurrentLogger();
77 if (logger) {
78 EmitIfEnabled(*logger, level, location, std::forward<MakeMessage>(make_message));
79 }
80}
81
86template <typename MakeMessage>
87[[noreturn]] void DispatchFatal(Logger* logger, const std::source_location& location,
88 MakeMessage&& make_message) noexcept {
89 std::string message;
90 try {
91 message = std::forward<MakeMessage>(make_message)();
92 } catch (...) {
93 message = "<fmt error>";
94 }
95 if (logger != nullptr) {
96 if (logger->ShouldLog(LogLevel::kFatal)) {
97 Emit(*logger, LogLevel::kFatal, location, std::string(message));
98 }
99 logger->Flush();
100 }
101 if (auto handler = GetFatalHandler()) {
102 try {
103 handler(location, message);
104 } catch (...) { // a throwing handler must not prevent the abort
105 }
106 }
107 std::abort();
108}
109
110template <typename MakeMessage>
111void LogToCurrentRuntime(LogLevel level, const std::source_location& location,
112 MakeMessage&& make_message) noexcept {
113 const std::shared_ptr<Logger>& logger = CurrentLogger();
114 if (level == LogLevel::kFatal) {
115 DispatchFatal(logger.get(), location, std::forward<MakeMessage>(make_message));
116 }
117 if (logger) {
118 EmitIfEnabled(*logger, level, location, std::forward<MakeMessage>(make_message));
119 }
120}
121
122template <typename MakeMessage>
123void LogToExplicitRuntime(Logger& logger, LogLevel level,
124 const std::source_location& location,
125 MakeMessage&& make_message) noexcept {
126 if (level == LogLevel::kFatal) {
127 DispatchFatal(&logger, location, std::forward<MakeMessage>(make_message));
128 }
129 EmitIfEnabled(logger, level, location, std::forward<MakeMessage>(make_message));
130}
131
132template <typename MakeMessage>
133[[noreturn]] void LogFatal(const std::source_location& location,
134 MakeMessage&& make_message) noexcept {
135 auto logger = GetCurrentLogger();
136 DispatchFatal(logger.get(), location, std::forward<MakeMessage>(make_message));
137}
138
139} // namespace iceberg::internal
140
141// ---------------------------------------------------------------------------
142// Logging macros.
143//
144// Every macro takes a std::format string followed by its arguments. The
145// rendered line depends on the active backend (see cerr_logger.h for the
146// std::cerr layout, or the spdlog pattern); the examples below show the call
147// site and, for the default CerrLogger, the line it produces.
148//
149// ICEBERG_LOG_TRACE("entering scan for {}", table);
150// 2026-06-16T10:59:41.186Z trace [12345] [table_scan.cc:88] entering scan for db.t
151// ICEBERG_LOG_DEBUG("cache miss key={}", key);
152// 2026-06-16T10:59:41.186Z debug [12345] [cache.cc:42] cache miss key=manifest-7
153// ICEBERG_LOG_INFO("loaded {} manifests in {} ms", n, ms);
154// 2026-06-16T10:59:41.186Z info [12345] [table_scan.cc:91] loaded 5 manifests in 12
155// ms
156// ICEBERG_LOG_WARN("retry {} after {}", attempt, err);
157// 2026-06-16T10:59:41.186Z warn [12345] [io.cc:51] retry 2 after timeout
158// ICEBERG_LOG_ERROR("commit failed: {}", status);
159// 2026-06-16T10:59:41.186Z error [12345] [txn.cc:77] commit failed: conflict
160// ICEBERG_LOG_CRITICAL("metadata unreadable at {}", path);
161// 2026-06-16T10:59:41.186Z critical [12345] [meta.cc:30] metadata unreadable at
162// s3://b/m.json
163// ICEBERG_LOG_FATAL("unrecoverable: {}", reason); // emits, flushes, then
164// std::abort()
165// 2026-06-16T10:59:41.186Z fatal [12345] [boot.cc:19] unrecoverable: bad config
166//
167// Less common forms:
168// ICEBERG_LOG(level, "level chosen at runtime: {}", x); // runtime severity
169// ICEBERG_LOG_TO(logger, level, "to an explicit logger {}", y);
170// ICEBERG_LOG_RUNTIME_FMT(level, fmt_string, args...); // non-literal format
171//
172// Include short_log_macros.h for bare aliases (LOG_INFO, ...). A format string is
173// mandatory; zero extra args is fine (ICEBERG_LOG_INFO("done")).
174// ---------------------------------------------------------------------------
175
183#ifndef ICEBERG_LOG_ACTIVE_LEVEL
184# define ICEBERG_LOG_ACTIVE_LEVEL ::iceberg::LogLevel::kTrace
185#endif
186
187// A message-builder lambda that formats lazily (only invoked past ShouldLog by the
188// EmitIfEnabled helpers), so disabled logs never evaluate their arguments.
189#define ICEBERG_INTERNAL_LOG_MESSAGE(FMT_, ...) \
190 [&]() -> ::std::string { return ::std::format((FMT_)__VA_OPT__(, ) __VA_ARGS__); }
191
192// Fixed-severity emit with a compile-time floor (`if constexpr`) then the shared
193// current-logger path. Formatting happens only on the taken path and never throws.
194#define ICEBERG_INTERNAL_LOG(level_, FMT_, ...) \
195 do { \
196 if constexpr ((level_) >= ICEBERG_LOG_ACTIVE_LEVEL) { \
197 ::iceberg::internal::LogToCurrent( \
198 (level_), ::std::source_location::current(), \
199 ICEBERG_INTERNAL_LOG_MESSAGE(FMT_ __VA_OPT__(, ) __VA_ARGS__)); \
200 } \
201 } while (0)
202
203#define ICEBERG_LOG_TRACE(...) \
204 ICEBERG_INTERNAL_LOG(::iceberg::LogLevel::kTrace, __VA_ARGS__)
205#define ICEBERG_LOG_DEBUG(...) \
206 ICEBERG_INTERNAL_LOG(::iceberg::LogLevel::kDebug, __VA_ARGS__)
207#define ICEBERG_LOG_INFO(...) \
208 ICEBERG_INTERNAL_LOG(::iceberg::LogLevel::kInfo, __VA_ARGS__)
209#define ICEBERG_LOG_WARN(...) \
210 ICEBERG_INTERNAL_LOG(::iceberg::LogLevel::kWarn, __VA_ARGS__)
211#define ICEBERG_LOG_ERROR(...) \
212 ICEBERG_INTERNAL_LOG(::iceberg::LogLevel::kError, __VA_ARGS__)
213#define ICEBERG_LOG_CRITICAL(...) \
214 ICEBERG_INTERNAL_LOG(::iceberg::LogLevel::kCritical, __VA_ARGS__)
215
216// FATAL: emit if enabled (never compile-stripped), then ALWAYS flush + abort.
217// Acquires the effective (scoped-or-default) logger ONCE so a concurrent
218// SetDefaultLogger cannot flush a different logger than it emitted to.
219#define ICEBERG_LOG_FATAL(FMT_, ...) \
220 ::iceberg::internal::LogFatal( \
221 ::std::source_location::current(), \
222 ICEBERG_INTERNAL_LOG_MESSAGE(FMT_ __VA_OPT__(, ) __VA_ARGS__))
223
224// Generic, runtime-level form against the default logger. No compile-time floor
225// (the level is not a constant). Aborts when level == kFatal.
226#define ICEBERG_LOG(level_, FMT_, ...) \
227 ::iceberg::internal::LogToCurrentRuntime( \
228 (level_), ::std::source_location::current(), \
229 ICEBERG_INTERNAL_LOG_MESSAGE(FMT_ __VA_OPT__(, ) __VA_ARGS__))
230
231// Generic form targeting an EXPLICIT logger (must be an lvalue Logger&). Honors
232// only that logger's ShouldLog. Aborts when level == kFatal.
233#define ICEBERG_LOG_TO(logger_, level_, FMT_, ...) \
234 ::iceberg::internal::LogToExplicitRuntime( \
235 (logger_), (level_), ::std::source_location::current(), \
236 ICEBERG_INTERNAL_LOG_MESSAGE(FMT_ __VA_OPT__(, ) __VA_ARGS__))
237
238// Runtime (non-literal) format string against the default logger. Aborts when
239// level == kFatal.
240#define ICEBERG_LOG_RUNTIME_FMT(level_, FMT_, ...) \
241 ::iceberg::internal::LogToCurrentRuntime( \
242 (level_), ::std::source_location::current(), [&]() -> ::std::string { \
243 return ::iceberg::internal::VFormat((FMT_)__VA_OPT__(, ) __VA_ARGS__); \
244 })
Pluggable logging sink.
Definition logger.h:146
Severity levels for the logging system.
void LogToCurrent(LogLevel level, const std::source_location &location, MakeMessage &&make_message) noexcept
Emit to the current (scoped-or-default) logger if enabled.
Definition log_macros.h:74
std::string VFormat(std::string_view fmt, Args &&... args)
Runtime (non-literal) format-string helper for ICEBERG_LOG_RUNTIME_FMT.
Definition log_macros.h:50
void EmitIfEnabled(Logger &logger, LogLevel level, const std::source_location &location, MakeMessage &&make_message) noexcept
Gate on logger.ShouldLog, then format (via make_message) and emit.
Definition log_macros.h:62
void DispatchFatal(Logger *logger, const std::source_location &location, MakeMessage &&make_message) noexcept
Format once, emit if enabled, flush, run the fatal handler, and abort.
Definition log_macros.h:87
Pluggable logging interface and the process-global default logger.
ICEBERG_EXPORT FatalHandler GetFatalHandler()
Return the installed fatal handler (empty if none). Used by the fatal logging path; thread-safe.
ICEBERG_EXPORT std::shared_ptr< Logger > GetCurrentLogger()
Return the effective logger for this thread (never null): the active ScopedLogger binding if any,...
LogLevel
Logging severity level, ordered from most to least verbose.
Definition log_level.h:38