iceberg-cpp
Loading...
Searching...
No Matches
logger.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
28
29#include <concepts>
30#include <cstdlib>
31#include <format>
32#include <functional>
33#include <memory>
34#include <source_location>
35#include <string>
36#include <string_view>
37#include <type_traits>
38#include <unordered_map>
39#include <utility>
40#include <vector>
41
44#include "iceberg/result.h"
45
46namespace iceberg {
47
53struct ICEBERG_EXPORT LogAttribute {
54 std::string key;
55 std::string value;
56};
57
65struct ICEBERG_EXPORT LogMessage {
66 LogLevel level = LogLevel::kOff;
67 std::string message;
68 std::source_location location = std::source_location::current();
69 std::vector<LogAttribute> attributes;
70
71 class Builder;
72};
73
88class ICEBERG_EXPORT LogMessage::Builder {
89 public:
90 explicit Builder(LogLevel level,
91 std::source_location location = std::source_location::current())
92 : level_(level), location_(location) {}
93
95 Builder& Message(std::string message) {
96 message_ = std::move(message);
97 return *this;
98 }
99
101 Builder& Attribute(std::string key, std::string value) {
102 attributes_.push_back(LogAttribute{.key = std::move(key), .value = std::move(value)});
103 return *this;
104 }
105
107 Builder& Location(std::source_location location) {
108 location_ = location;
109 return *this;
110 }
111
114 return LogMessage{.level = level_,
115 .message = std::move(message_),
116 .location = location_,
117 .attributes = std::move(attributes_)};
118 }
119
120 private:
121 LogLevel level_;
122 std::string message_;
123 // `location_` is a trivially copyable members no need to move.
124 std::source_location location_;
125 std::vector<LogAttribute> attributes_;
126};
127
133inline constexpr std::string_view kLevelProperty = "level";
134inline constexpr std::string_view kPatternProperty = "pattern";
135
146class ICEBERG_EXPORT Logger {
147 public:
148 virtual ~Logger() = default;
149
156 virtual Status Initialize(
157 const std::unordered_map<std::string, std::string>& properties) {
158 if (auto it = properties.find(std::string(kLevelProperty)); it != properties.end()) {
159 auto parsed = LogLevelFromString(it->second);
160 if (!parsed) return std::unexpected(parsed.error());
161 SetLevel(*parsed);
162 }
163 return {};
164 }
165
167 virtual bool ShouldLog(LogLevel level) const noexcept = 0;
168
170 virtual void Log(LogMessage&& message) noexcept = 0;
171
173 virtual void SetLevel(LogLevel level) noexcept = 0;
174
176 virtual LogLevel level() const noexcept = 0;
177
179 virtual void Flush() noexcept {}
180
182 virtual bool IsNoop() const { return false; }
183
185 static std::shared_ptr<Logger> Noop();
186};
187
192ICEBERG_EXPORT std::shared_ptr<Logger> GetDefaultLogger();
193
200ICEBERG_EXPORT std::shared_ptr<Logger> GetCurrentLogger();
201
206ICEBERG_EXPORT void SetDefaultLogger(std::shared_ptr<Logger> logger);
207
213ICEBERG_EXPORT void SetDefaultLevel(LogLevel level);
214
224 std::function<void(const std::source_location&, std::string_view message)>;
225
229ICEBERG_EXPORT void SetFatalHandler(FatalHandler handler);
230
234
256class ICEBERG_EXPORT ScopedLogger {
257 public:
258 explicit ScopedLogger(std::shared_ptr<Logger> logger) noexcept;
260
261 ScopedLogger(const ScopedLogger&) = delete;
262 ScopedLogger& operator=(const ScopedLogger&) = delete;
263 ScopedLogger(ScopedLogger&&) = delete;
264 ScopedLogger& operator=(ScopedLogger&&) = delete;
265
266 private:
267 std::shared_ptr<Logger> previous_;
268};
269
270// ---------------------------------------------------------------------------
271// Using the API directly (the ICEBERG_LOG_* macros that wrap this live in
272// log_macros.h). Example: a custom sink, installed as the process default.
273//
274// class MySink : public Logger {
275// public:
276// bool ShouldLog(LogLevel level) const noexcept override { return level >= level_; }
277// void Log(LogMessage&& m) noexcept override { write_line(m.message); }
278// void SetLevel(LogLevel level) noexcept override { level_ = level; }
279// LogLevel level() const noexcept override { return level_; }
280// private:
281// std::atomic<LogLevel> level_{LogLevel::kInfo};
282// };
283//
284// SetDefaultLogger(std::make_shared<MySink>()); // install process-wide
285// SetDefaultLevel(LogLevel::kDebug); // adjust the threshold
286//
287// auto logger = GetDefaultLogger(); // borrow the current default
288// if (logger->ShouldLog(LogLevel::kInfo)) {
289// logger->Log(LogMessage{.level = LogLevel::kInfo, .message = "scan ready"});
290// }
291//
292// // Or configure from catalog-style properties (applies the "level" key):
293// auto sink = std::make_shared<MySink>();
294// auto status = sink->Initialize({{std::string(kLevelProperty), "warn"}}); // -> kWarn
295// ---------------------------------------------------------------------------
296
297namespace internal {
298
301ICEBERG_EXPORT std::unique_ptr<Logger> MakeNoopLogger();
302
309ICEBERG_EXPORT const std::shared_ptr<Logger>& CurrentLogger() noexcept;
310
315ICEBERG_EXPORT void Emit(Logger& logger, LogLevel level,
316 const std::source_location& location, std::string&& message);
317
323ICEBERG_EXPORT void EmitFormatError(Logger& logger, LogLevel level,
324 const std::source_location& location) noexcept;
325
332template <typename... Args>
333struct FmtWithLoc {
334 std::format_string<Args...> fmt;
335 std::source_location loc;
336
337 template <typename T>
338 requires std::convertible_to<const T&, std::format_string<Args...>>
339 consteval FmtWithLoc( // NOLINT(google-explicit-constructor): mirrors
340 // std::format_string
341 const T& s, std::source_location loc = std::source_location::current())
342 : fmt(s), loc(loc) {}
343};
344
349template <typename... Args>
350void FormatAndEmit(Logger& logger, LogLevel level, const std::source_location& loc,
351 std::format_string<Args...> fmt, Args&&... args) noexcept {
352 if (!logger.ShouldLog(level)) return;
353 try {
354 Emit(logger, level, loc, std::format(fmt, std::forward<Args>(args)...));
355 } catch (...) {
356 // Catch-all upholds the noexcept "logging never throws" guarantee: a
357 // user-defined std::formatter may throw a non-std::exception type, and this
358 // function is noexcept, so anything escaping here would call std::terminate.
359 EmitFormatError(logger, level, loc);
360 }
361}
362
363} // namespace internal
364
369template <typename... Args>
370void Log(LogLevel level, internal::FmtWithLoc<std::type_identity_t<Args>...> fmt,
371 Args&&... args) noexcept {
372 const std::shared_ptr<Logger>& logger = internal::CurrentLogger();
373 if (logger) {
374 internal::FormatAndEmit(*logger, level, fmt.loc, fmt.fmt,
375 std::forward<Args>(args)...);
376 }
377}
378
382template <typename... Args>
383void Log(Logger& logger, LogLevel level,
384 internal::FmtWithLoc<std::type_identity_t<Args>...> fmt,
385 Args&&... args) noexcept {
386 internal::FormatAndEmit(logger, level, fmt.loc, fmt.fmt, std::forward<Args>(args)...);
387}
388
389} // namespace iceberg
Pluggable logging sink.
Definition logger.h:146
virtual void Log(LogMessage &&message) noexcept=0
Emit one (already-formatted) record, taking ownership. Must not throw.
virtual LogLevel level() const noexcept=0
Return the minimum level this logger emits.
static std::shared_ptr< Logger > Noop()
Return a shared, immortal no-op logger singleton.
virtual void SetLevel(LogLevel level) noexcept=0
Set the minimum level this logger emits.
virtual bool ShouldLog(LogLevel level) const noexcept=0
Cheap check whether a record at level would be emitted.
virtual bool IsNoop() const
Return true if this logger is a no-op.
Definition logger.h:182
virtual Status Initialize(const std::unordered_map< std::string, std::string > &properties)
Property-based setup, called by Loggers::Load() before first use.
Definition logger.h:156
Bind a logger for the current thread until this object leaves scope.
Definition logger.h:256
Define symbol visibility macros for core Iceberg APIs.
Severity levels for the logging system.
Core Apache Iceberg C++ APIs.
Definition arrow_io_util.h:33
ICEBERG_EXPORT FatalHandler GetFatalHandler()
Return the installed fatal handler (empty if none). Used by the fatal logging path; thread-safe.
ICEBERG_EXPORT void SetDefaultLevel(LogLevel level)
Set the minimum level of the current default logger.
void Log(LogLevel level, internal::FmtWithLoc< std::type_identity_t< Args >... > fmt, Args &&... args) noexcept
Log to the process-default logger, std::format style. Formats only if the level is enabled; never thr...
Definition logger.h:370
ICEBERG_EXPORT void SetDefaultLogger(std::shared_ptr< Logger > logger)
Install a new process-global default logger.
constexpr std::string_view kLevelProperty
Well-known Logger::Initialize() property keys.
Definition logger.h:133
ICEBERG_EXPORT void SetFatalHandler(FatalHandler handler)
Install (or clear, with nullptr) the process-global fatal handler.
std::function< void(const std::source_location &, std::string_view message)> FatalHandler
A hook invoked on the fatal-log path just before std::abort().
Definition logger.h:224
ICEBERG_EXPORT std::shared_ptr< Logger > GetCurrentLogger()
Return the effective logger for this thread (never null): the active ScopedLogger binding if any,...
Result< LogLevel > LogLevelFromString(std::string_view s)
Parse a LogLevel from a string (case-insensitive).
Definition log_level.h:77
LogLevel
Logging severity level, ordered from most to least verbose.
Definition log_level.h:38
ICEBERG_EXPORT std::shared_ptr< Logger > GetDefaultLogger()
Return the process-global default logger (never null).
STL namespace.
Define Result, Status, and error helpers.
A structured key/value attribute attached to a log record.
Definition logger.h:53
A single log record handed to a Logger.
Definition logger.h:65
Builder & Location(std::source_location location)
Override the record's source location (defaults to the build site).
Definition logger.h:107
LogMessage Build()
Materialize the LogMessage, moving the accumulated state out.
Definition logger.h:113
Builder & Attribute(std::string key, std::string value)
Append a structured key/value attribute.
Definition logger.h:101
Builder & Message(std::string message)
Set the already-formatted message text.
Definition logger.h:95