Implement logging

This commit is contained in:
2025-01-26 22:35:47 +01:00
parent cf7db48c26
commit b3f2336e1b
10 changed files with 171 additions and 7 deletions

View File

@ -10,6 +10,6 @@ git_override(
commit = "4f28899228fb3ad0126897876f147ca15026151e",
)
bazel_dep(name = "rules_python", version = "1.1.0", dev_dependency = True)
bazel_dep(name = "rules_python", version = "1.1.0")
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
python.toolchain(python_version = "3.13", is_default = True)

View File

@ -90,6 +90,9 @@ public:
{
return H::combine(ASL_MOVE(h), *b);
}
template<is_object U, allocator A>
friend constexpr U* leak(box<U, A>&&);
};
template<is_object T, allocator Allocator = DefaultAllocator, typename... Args>
@ -111,5 +114,11 @@ constexpr box<T, Allocator> make_box(Args&&... args)
return box<T>(ptr, ASL_MOVE(allocator));
}
template<is_object T, allocator A>
constexpr T* leak(box<T, A>&& b)
{
return exchange(b.m_ptr, nullptr);
}
} // namespace asl

View File

@ -1,10 +1,10 @@
cc_library(
name = "log",
srcs = [
"log.hpp",
"log.cpp",
],
hdrs = [
"log.cpp",
"log.hpp",
],
deps = [
"//asl",

View File

@ -0,0 +1,52 @@
#include "asl/log/log.hpp"
#include "asl/print.hpp"
#include "asl/string_builder.hpp"
// @Todo Don't use internal get_stdout_writer, make console module
static asl::log::DefaultLogger g_default_logger{asl::print_internals::get_stdout_writer()};
static asl::log::Logger* g_head = &g_default_logger;
static constexpr asl::string_view kLevelName[] = {
" DEBUG ",
" INFO ",
" WARNING ",
" ERROR ",
};
void asl::log::register_logger(box<Logger> logger_box)
{
auto* logger = leak(ASL_MOVE(logger_box));
logger->m_next = exchange(g_head, logger);
}
void asl::log::DefaultLogger::log(const message& msg)
{
asl::format(m_writer, "[{}] {}:{}: {}\n",
kLevelName[msg.level], // NOLINT
msg.location.file,
msg.location.line,
msg.message);
}
void asl::log::log_inner(
level l,
string_view fmt, span<const format_internals::type_erased_arg> args,
const source_location& sl)
{
// @Todo Use temporary allocator
StringWriter msg_writer{};
asl::format_internals::format(&msg_writer, fmt, args);
message m{
.level = l,
.message = msg_writer.as_string_view(),
.location = sl,
};
for (auto* it = g_head; it != nullptr; it = it->next_logger())
{
it->log(m);
}
}

View File

@ -0,0 +1,83 @@
#pragma once
#include <asl/format.hpp>
#include <asl/utility.hpp>
#include <asl/box.hpp>
namespace asl::log
{
enum level : uint8_t
{
kDebug = 0,
kInfo,
kWarning,
kError,
};
struct message
{
level level;
string_view message;
source_location location;
};
// @Todo Write and use an intrusive doubly-linked list
class Logger
{
Logger* m_next{};
public:
Logger() = default;
ASL_DEFAULT_COPY_MOVE(Logger);
virtual ~Logger() = default;
virtual void log(const message&) = 0;
friend void register_logger(box<Logger>);
constexpr Logger* next_logger() const { return m_next; }
};
// @Todo Make a deref_as trait & deref utility
// @Todo Accept writer as box, pointer, reference, or value
class DefaultLogger : public Logger
{
Writer* m_writer;
public:
explicit constexpr DefaultLogger(Writer* writer) : m_writer{writer} {}
void log(const message&) override;
};
void register_logger(box<Logger>);
// @Todo Add a way to remove loggers (including all)
template<typename T, typename... Args>
requires constructible_from<T, Args&&...> && convertible_from<Logger*, T*>
void register_logger(Args&&... args)
{
register_logger(make_box<T>(ASL_FWD(args)...));
}
void log_inner(level l, string_view fmt, span<const format_internals::type_erased_arg> args, const source_location& sl);
template<formattable... Args>
void log(level l, const source_location& sl, string_view fmt, const Args&... args)
{
format_internals::type_erased_arg type_erased_args[] = {
format_internals::type_erased_arg(args)...
};
log_inner(l, fmt, type_erased_args, sl);
}
} // namespace asl::log
// @Todo Compile-time configuration of logging
#define ASL_LOG_DEBUG(...) ::asl::log::log(::asl::log::kDebug, ::asl::source_location{}, __VA_ARGS__)
#define ASL_LOG_INFO(...) ::asl::log::log(::asl::log::kInfo, ::asl::source_location{}, __VA_ARGS__)
#define ASL_LOG_WARNING(...) ::asl::log::log(::asl::log::kWarning, ::asl::source_location{}, __VA_ARGS__)
#define ASL_LOG_ERROR(...) ::asl::log::log(::asl::log::kError, ::asl::source_location{}, __VA_ARGS__)

View File

@ -0,0 +1,10 @@
#include <asl/log/log.hpp>
#include <asl/testing/testing.hpp>
ASL_TEST(log)
{
asl::log::log(asl::log::kInfo, asl::source_location{}, "Hello, {}!", "world"_sv);
ASL_LOG_ERROR("Oh no! {}", 42);
}

View File

@ -81,4 +81,16 @@ constexpr bool is_pow2(isize_t v)
ASL_DELETE_COPY(T) \
ASL_DELETE_MOVE(T)
#define ASL_DEFAULT_COPY(T) \
T(const T&) = default; \
T& operator=(const T&) = default;
#define ASL_DEFAULT_MOVE(T) \
T(T&&) = default; \
T& operator=(T&&) = default;
#define ASL_DEFAULT_COPY_MOVE(T) \
ASL_DEFAULT_COPY(T) \
ASL_DEFAULT_MOVE(T)
} // namespace asl

View File

@ -1,4 +1 @@
logging
dynlib
warning compare with 0 in format_float.cpp

View File

@ -5,4 +5,5 @@ py_binary(
srcs = [
"fix_line_endings.py",
],
visibility = ["//:__subpackages__"],
)