Skip to content

feat(logging): add logging macros (4/6)#725

Open
kamcheungting-db wants to merge 4 commits into
apache:mainfrom
kamcheungting-db:logging-block4-macros
Open

feat(logging): add logging macros (4/6)#725
kamcheungting-db wants to merge 4 commits into
apache:mainfrom
kamcheungting-db:logging-block4-macros

Conversation

@kamcheungting-db

@kamcheungting-db kamcheungting-db commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

What

The application-facing logging API — macros over the process default logger. Block 4 of the 6-PR logging stack (base: logging-block3-cerr).

Fixed-severity macros take a std::format string and arguments:
ICEBERG_LOG_INFO("opened {} in {} ms", name, elapsed);
ICEBERG_LOG_WARN("retrying {}/{}", attempt, max);
ICEBERG_LOG_TRACE / _DEBUG / _INFO / _WARN / _ERROR / _CRITICAL. Each is cheap when it won't print:

  • Compile-time floor — ICEBERG_LOG_ACTIVE_LEVEL removes calls below the configured level from the binary entirely (if constexpr).
  • Runtime gate — a lock-free atomic level check short-circuits before arguments are evaluated.
  • Format string is validated at compile time; std::source_location is captured automatically.

@kamcheungting-db kamcheungting-db changed the title feat: [Iceberg Logger] [Part-4] Logging Macros feat(logging): add logging macros Jun 11, 2026
@kamcheungting-db kamcheungting-db changed the title feat(logging): add logging macros feat(logging): add logging macros (4/6) Jun 11, 2026
@kamcheungting-db kamcheungting-db force-pushed the logging-block4-macros branch 2 times, most recently from a908132 to 7e38d7e Compare June 14, 2026 06:16
First block of the iceberg-cpp logging system. Introduces the severity
level type the rest of the stack builds on:

- LogLevel { kTrace, kDebug, kInfo, kWarn, kError, kCritical, kFatal, kOff },
  ordered so `level >= threshold` is the enabled test; kOff is the max
  sentinel (disables all emission as a threshold, never an actual message level).
- constexpr ToString() and case-insensitive LogLevelFromString() -> Result<LogLevel>,
  mirroring the CounterUnit pattern in src/iceberg/metrics/.

Wires the new logging/ subdirectory and a logging_test target into BOTH the
CMake and Meson builds (header install + test), so the two build systems stay
at parity from the first block.

Co-authored-by: Isaac
Second block: the backend-agnostic logging API and the swappable default logger.

- Logger: pure-virtual sink (ShouldLog/Log/SetLevel/level/Flush/IsNoop), with
  ShouldLog() as the single authority for runtime filtering and a documented
  no-reentrancy / thread-safety / noexcept contract. Mirrors MetricsReporter.
- LogMessage owns its formatted text (moved in) so a sink may retain records;
  reserves an owning attributes vector for future structured logging without an
  ABI break. logger.h is backend-agnostic -- it never includes the build config
  header nor references the spdlog feature macro, so consumers see one stable API.
- Process-global default logger: GetDefaultLogger / SetDefaultLogger /
  SetDefaultLevel, plus a lock-free thread-local generation cache on the hot path
  (no lock or refcount churn in steady state; slot mutex only on swap/refresh).
  NoopLogger is the placeholder default here; CerrLogger and the spdlog backend
  arrive in the following blocks.

Builds the source into both CMake and Meson and installs logger.h alongside
log_level.h; logger_test covers the default-logger API, level lowering taking
effect immediately, and concurrent swap/read safety.

Co-authored-by: Isaac
Third block: the first concrete sink, and the process default until the spdlog
backend lands.

- CerrLogger writes to std::cerr with a fixed line layout
  `YYYY-MM-DDThh:mm:ss.mmmZ LEVEL [tid] file:line] message`. Timestamps use UTC
  std::chrono floored to milliseconds (no gmtime/localtime -- thread-unsafe);
  the thread id is the OS-native id, cached per thread.
- Level is a std::atomic<LogLevel>; a mutex guards the whole formatted-line write
  so concurrent lines never interleave. Log()/Flush() wrap stream ops in
  try/catch so the noexcept contract holds even if the stream throws.
- MakeDefaultLogger() now returns CerrLogger.

Wired into both builds and installed; cerr_logger_test covers layout, level
filtering, and concurrent-write safety.

Co-authored-by: Isaac
Fourth block: the application-facing macros, the only part most callers touch.

- ICEBERG_LOG_{TRACE,DEBUG,INFO,WARN,ERROR,CRITICAL,FATAL} plus the generic
  ICEBERG_LOG(level, ...), ICEBERG_LOG_TO(logger, level, ...) for an explicit
  logger, and ICEBERG_LOG_RUNTIME_FMT for a runtime (non-literal) format string.
- ICEBERG_LOG_ACTIVE_LEVEL is a compile-time severity floor: statements below it
  are removed entirely via `if constexpr` (no format call site, no source
  location emitted). ICEBERG_LOG_FATAL is never gated by the floor -- its abort
  is always compiled in; it emits, best-effort Flush()es the same logger it
  emitted to, then std::abort().
- Filtering is decided solely by Logger::ShouldLog(); formatting is wrapped in
  try/catch so logging never throws (a format failure routes to EmitFormatError).
- Bare Java-style aliases (LOG_INFO, ...) are opt-in via ICEBERG_LOG_SHORT_MACROS
  to avoid polluting consumers / colliding with glog/abseil.

Header-only addition to logger.h. macros_test covers injection, the
guard-before-format short-circuit, never-throws, and FATAL aborts;
macros_active_level_test verifies compile-time stripping in a kOff translation unit.

Co-authored-by: Isaac
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant