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 <memory>
33#include <source_location>
34#include <string>
35#include <string_view>
36#include <type_traits>
37#include <unordered_map>
38#include <utility>
39#include <vector>
40
41#include "iceberg/iceberg_export.h"
43#include "iceberg/result.h"
44
45namespace iceberg {
46
52struct ICEBERG_EXPORT LogAttribute {
53 std::string key;
54 std::string value;
55};
56
64struct ICEBERG_EXPORT LogMessage {
65 LogLevel level = LogLevel::kOff;
66 std::string message;
67 std::source_location location = std::source_location::current();
68 std::vector<LogAttribute> attributes;
69
70 class Builder;
71};
72
87class ICEBERG_EXPORT LogMessage::Builder {
88 public:
89 explicit Builder(LogLevel level,
90 std::source_location location = std::source_location::current())
91 : level_(level), location_(location) {}
92
94 Builder& Message(std::string message) {
95 message_ = std::move(message);
96 return *this;
97 }
98
100 Builder& Attribute(std::string key, std::string value) {
101 attributes_.push_back(LogAttribute{.key = std::move(key), .value = std::move(value)});
102 return *this;
103 }
104
106 Builder& Location(std::source_location location) {
107 location_ = location;
108 return *this;
109 }
110
113 return LogMessage{.level = level_,
114 .message = std::move(message_),
115 .location = location_,
116 .attributes = std::move(attributes_)};
117 }
118
119 private:
120 LogLevel level_;
121 std::string message_;
122 // `location_` is a trivially copyable members no need to move.
123 std::source_location location_;
124 std::vector<LogAttribute> attributes_;
125};
126
132inline constexpr std::string_view kLevelProperty = "level";
133inline constexpr std::string_view kPatternProperty = "pattern";
134
145class ICEBERG_EXPORT Logger {
146 public:
147 virtual ~Logger() = default;
148
155 virtual Status Initialize(
156 const std::unordered_map<std::string, std::string>& properties) {
157 if (auto it = properties.find(std::string(kLevelProperty)); it != properties.end()) {
158 auto parsed = LogLevelFromString(it->second);
159 if (!parsed) return std::unexpected(parsed.error());
160 SetLevel(*parsed);
161 }
162 return {};
163 }
164
166 virtual bool ShouldLog(LogLevel level) const noexcept = 0;
167
169 virtual void Log(LogMessage&& message) noexcept = 0;
170
172 virtual void SetLevel(LogLevel level) noexcept = 0;
173
175 virtual LogLevel level() const noexcept = 0;
176
178 virtual void Flush() noexcept {}
179
181 virtual bool IsNoop() const { return false; }
182
184 static std::shared_ptr<Logger> Noop();
185};
186
191ICEBERG_EXPORT std::shared_ptr<Logger> GetDefaultLogger();
192
199ICEBERG_EXPORT std::shared_ptr<Logger> GetCurrentLogger();
200
205ICEBERG_EXPORT void SetDefaultLogger(std::shared_ptr<Logger> logger);
206
212ICEBERG_EXPORT void SetDefaultLevel(LogLevel level);
213
235class ICEBERG_EXPORT ScopedLogger {
236 public:
237 explicit ScopedLogger(std::shared_ptr<Logger> logger) noexcept;
239
240 ScopedLogger(const ScopedLogger&) = delete;
241 ScopedLogger& operator=(const ScopedLogger&) = delete;
242 ScopedLogger(ScopedLogger&&) = delete;
243 ScopedLogger& operator=(ScopedLogger&&) = delete;
244
245 private:
246 std::shared_ptr<Logger> previous_;
247};
248
249// ---------------------------------------------------------------------------
250// Using the API directly (the LOG_* macros that wrap this are added later in
251// the stack). Example: a custom sink, installed as the process default.
252//
253// class MySink : public Logger {
254// public:
255// bool ShouldLog(LogLevel level) const noexcept override { return level >= level_; }
256// void Log(LogMessage&& m) noexcept override { write_line(m.message); }
257// void SetLevel(LogLevel level) noexcept override { level_ = level; }
258// LogLevel level() const noexcept override { return level_; }
259// private:
260// std::atomic<LogLevel> level_{LogLevel::kInfo};
261// };
262//
263// SetDefaultLogger(std::make_shared<MySink>()); // install process-wide
264// SetDefaultLevel(LogLevel::kDebug); // adjust the threshold
265//
266// auto logger = GetDefaultLogger(); // borrow the current default
267// if (logger->ShouldLog(LogLevel::kInfo)) {
268// logger->Log(LogMessage{.level = LogLevel::kInfo, .message = "scan ready"});
269// }
270//
271// // Or configure from catalog-style properties (applies the "level" key):
272// auto sink = std::make_shared<MySink>();
273// auto status = sink->Initialize({{std::string(kLevelProperty), "warn"}}); // -> kWarn
274// ---------------------------------------------------------------------------
275
276namespace internal {
277
284ICEBERG_EXPORT const std::shared_ptr<Logger>& CurrentLogger() noexcept;
285
290ICEBERG_EXPORT void Emit(Logger& logger, LogLevel level,
291 const std::source_location& location, std::string&& message);
292
298ICEBERG_EXPORT void EmitFormatError(Logger& logger, LogLevel level,
299 const std::source_location& location) noexcept;
300
307template <typename... Args>
308std::string VFormat(std::string_view fmt, Args&&... args) {
309 auto store = std::make_format_args(args...);
310 return std::vformat(fmt, store);
311}
312
319template <typename... Args>
320struct FmtWithLoc {
321 std::format_string<Args...> fmt;
322 std::source_location loc;
323
324 template <typename T>
325 requires std::convertible_to<const T&, std::format_string<Args...>>
326 consteval FmtWithLoc( // NOLINT(google-explicit-constructor): mirrors
327 // std::format_string
328 const T& s, std::source_location loc = std::source_location::current())
329 : fmt(s), loc(loc) {}
330};
331
336template <typename... Args>
337void FormatAndEmit(Logger& logger, LogLevel level, const std::source_location& loc,
338 std::format_string<Args...> fmt, Args&&... args) noexcept {
339 if (!logger.ShouldLog(level)) return;
340 try {
341 Emit(logger, level, loc, std::format(fmt, std::forward<Args>(args)...));
342 } catch (...) {
343 EmitFormatError(logger, level, loc);
344 }
345}
346
347} // namespace internal
348
353template <typename... Args>
354void Log(LogLevel level, internal::FmtWithLoc<std::type_identity_t<Args>...> fmt,
355 Args&&... args) noexcept {
356 const std::shared_ptr<Logger>& logger = internal::CurrentLogger();
357 if (logger) {
358 internal::FormatAndEmit(*logger, level, fmt.loc, fmt.fmt,
359 std::forward<Args>(args)...);
360 }
361}
362
366template <typename... Args>
367void Log(Logger& logger, LogLevel level,
368 internal::FmtWithLoc<std::type_identity_t<Args>...> fmt,
369 Args&&... args) noexcept {
370 internal::FormatAndEmit(logger, level, fmt.loc, fmt.fmt, std::forward<Args>(args)...);
371}
372
373} // namespace iceberg
Pluggable logging sink.
Definition logger.h:145
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.
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:181
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:155
Bind a logger for the current thread until this object leaves scope.
Definition logger.h:235
Severity levels for the logging system.
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
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:354
constexpr std::string_view kLevelProperty
Well-known Logger::Initialize() property keys.
Definition logger.h:132
STL namespace.
A structured key/value attribute attached to a log record.
Definition logger.h:52
A single log record handed to a Logger.
Definition logger.h:64
Builder & Location(std::source_location location)
Override the record's source location (defaults to the build site).
Definition logger.h:106
LogMessage Build()
Materialize the LogMessage, moving the accumulated state out.
Definition logger.h:112
Builder & Attribute(std::string key, std::string value)
Append a structured key/value attribute.
Definition logger.h:100
Builder & Message(std::string message)
Set the already-formatted message text.
Definition logger.h:94