Add foundation logging facade

This commit is contained in:
2026-06-01 08:20:58 +02:00
parent 6604f30ef3
commit a7bb04f54b
9 changed files with 262 additions and 6 deletions

93
src/foundation/log.cpp Normal file
View File

@@ -0,0 +1,93 @@
#include "foundation/log.h"
namespace pp::foundation {
namespace {
[[nodiscard]] bool should_write(LogLevel level, LogLevel min_level) noexcept
{
return static_cast<std::uint8_t>(level) >= static_cast<std::uint8_t>(min_level);
}
}
Logger::Logger(ILogSink& sink) noexcept
: sink_(&sink)
{
}
void Logger::set_min_level(LogLevel level) noexcept
{
min_level_ = level;
}
LogLevel Logger::min_level() const noexcept
{
return min_level_;
}
Status Logger::write(
LogLevel level,
std::string_view component,
std::string_view message,
std::uint64_t frame_id,
std::uint64_t stroke_id,
std::uint64_t thread_id) noexcept
{
if (component.empty()) {
return Status::invalid_argument("log component must not be empty");
}
if (message.empty()) {
return Status::invalid_argument("log message must not be empty");
}
if (!should_write(level, min_level_)) {
return Status::success();
}
sink_->write(LogRecord {
.level = level,
.component = std::string(component),
.message = std::string(message),
.frame_id = frame_id,
.stroke_id = stroke_id,
.thread_id = thread_id,
});
return Status::success();
}
void MemoryLogSink::write(const LogRecord& record) noexcept
{
records_.push_back(record);
}
const std::vector<LogRecord>& MemoryLogSink::records() const noexcept
{
return records_;
}
void MemoryLogSink::clear() noexcept
{
records_.clear();
}
const char* log_level_name(LogLevel level) noexcept
{
switch (level) {
case LogLevel::trace:
return "trace";
case LogLevel::debug:
return "debug";
case LogLevel::info:
return "info";
case LogLevel::warning:
return "warning";
case LogLevel::error:
return "error";
}
return "unknown";
}
}

67
src/foundation/log.h Normal file
View File

@@ -0,0 +1,67 @@
#pragma once
#include "foundation/result.h"
#include <cstdint>
#include <string>
#include <string_view>
#include <vector>
namespace pp::foundation {
enum class LogLevel : std::uint8_t {
trace,
debug,
info,
warning,
error,
};
struct LogRecord {
LogLevel level = LogLevel::info;
std::string component;
std::string message;
std::uint64_t frame_id = 0;
std::uint64_t stroke_id = 0;
std::uint64_t thread_id = 0;
};
class ILogSink {
public:
virtual ~ILogSink() = default;
virtual void write(const LogRecord& record) noexcept = 0;
};
class Logger {
public:
explicit Logger(ILogSink& sink) noexcept;
void set_min_level(LogLevel level) noexcept;
[[nodiscard]] LogLevel min_level() const noexcept;
[[nodiscard]] Status write(
LogLevel level,
std::string_view component,
std::string_view message,
std::uint64_t frame_id = 0,
std::uint64_t stroke_id = 0,
std::uint64_t thread_id = 0) noexcept;
private:
ILogSink* sink_ = nullptr;
LogLevel min_level_ = LogLevel::trace;
};
class MemoryLogSink final : public ILogSink {
public:
void write(const LogRecord& record) noexcept override;
[[nodiscard]] const std::vector<LogRecord>& records() const noexcept;
void clear() noexcept;
private:
std::vector<LogRecord> records_;
};
[[nodiscard]] const char* log_level_name(LogLevel level) noexcept;
}