diff options
author | Steven Le Rouzic <steven.lerouzic@gmail.com> | 2025-02-12 23:34:47 +0100 |
---|---|---|
committer | Steven Le Rouzic <steven.lerouzic@gmail.com> | 2025-02-12 23:34:47 +0100 |
commit | cb77cbe9ce4cddad6a460aa190ff70f0c13e4703 (patch) | |
tree | eb63a44f6264893ba803ff27db2faa59d644b0e9 /asl/logging | |
parent | e297b4182b735d97d5251361854acd2fccb03013 (diff) |
Rename log to logging
Diffstat (limited to 'asl/logging')
-rw-r--r-- | asl/logging/BUILD.bazel | 26 | ||||
-rw-r--r-- | asl/logging/logging.cpp | 52 | ||||
-rw-r--r-- | asl/logging/logging.hpp | 98 | ||||
-rw-r--r-- | asl/logging/logging_tests.cpp | 23 |
4 files changed, 199 insertions, 0 deletions
diff --git a/asl/logging/BUILD.bazel b/asl/logging/BUILD.bazel new file mode 100644 index 0000000..396da74 --- /dev/null +++ b/asl/logging/BUILD.bazel @@ -0,0 +1,26 @@ +cc_library( + name = "logging", + srcs = [ + "logging.cpp", + ], + hdrs = [ + "logging.hpp", + ], + deps = [ + "//asl", + ], + visibility = ["//visibility:public"], +) + +cc_test( + name = "tests", + srcs = [ + "logging_tests.cpp" + ], + deps = [ + ":logging", + "//asl/testing", + ], + visibility = ["//visibility:public"], +) + diff --git a/asl/logging/logging.cpp b/asl/logging/logging.cpp new file mode 100644 index 0000000..aa630ac --- /dev/null +++ b/asl/logging/logging.cpp @@ -0,0 +1,52 @@ +#include "asl/logging/logging.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<asl::Writer*> 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::DefaultLoggerBase::log_inner(Writer& writer, const message& msg) +{ + asl::format(&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); + } +} + diff --git a/asl/logging/logging.hpp b/asl/logging/logging.hpp new file mode 100644 index 0000000..81f10e7 --- /dev/null +++ b/asl/logging/logging.hpp @@ -0,0 +1,98 @@ +#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; } +}; + +class DefaultLoggerBase : public Logger +{ +protected: + static void log_inner(Writer&, const message&); +}; + +template<derefs_as<Writer> W> +class DefaultLogger : public DefaultLoggerBase +{ + W m_writer; + +public: + explicit constexpr DefaultLogger(W&& writer) : m_writer{ASL_FWD(writer)} {} + + constexpr void log(const message& m) override + { + log_inner(deref<Writer>(m_writer), m); + } +}; + +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) +{ + if constexpr (sizeof...(Args) == 0) + { + log_inner(l, fmt, {}, sl); + } + else + { + 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__) diff --git a/asl/logging/logging_tests.cpp b/asl/logging/logging_tests.cpp new file mode 100644 index 0000000..ebbf800 --- /dev/null +++ b/asl/logging/logging_tests.cpp @@ -0,0 +1,23 @@ +#include "asl/logging/logging.hpp" +#include "asl/testing/testing.hpp" +#include "asl/string_builder.hpp" + +ASL_TEST(log) +{ + asl::log::log(asl::log::kInfo, asl::source_location{}, "Hello, {}!", "world"_sv); + + ASL_LOG_ERROR("Oh no! {}", 42); +} + +static asl::StringWriter g_string_writer{}; + +ASL_TEST(custom_writer) +{ + asl::log::register_logger<asl::log::DefaultLogger<asl::StringWriter<>&>>(g_string_writer); + + ASL_LOG_INFO("Hello"); + auto sv = g_string_writer.as_string_view(); + + ASL_TEST_EXPECT(sv == "[ INFO ] asl/logging/logging_tests.cpp:18: Hello\n"); +} + |