Implement logging
This commit is contained in:
@ -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)
|
||||
|
@ -6,7 +6,7 @@
|
||||
namespace asl
|
||||
{
|
||||
|
||||
void report_assert_failure( const char* msg, const source_location& sl = source_location{});
|
||||
void report_assert_failure(const char* msg, const source_location& sl = source_location{});
|
||||
|
||||
} // namespace asl
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
cc_library(
|
||||
name = "log",
|
||||
srcs = [
|
||||
"log.hpp",
|
||||
"log.cpp",
|
||||
],
|
||||
hdrs = [
|
||||
"log.cpp",
|
||||
"log.hpp",
|
||||
],
|
||||
deps = [
|
||||
"//asl",
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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__)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -5,4 +5,5 @@ py_binary(
|
||||
srcs = [
|
||||
"fix_line_endings.py",
|
||||
],
|
||||
visibility = ["//:__subpackages__"],
|
||||
)
|
||||
|
Reference in New Issue
Block a user