parselink-old/include/parselink/logging.h
Kurt Sassenrath 2a4c819f4f Fix approach to include paths.
This accommodates bazel's best practices a bit better.
2023-10-04 21:23:36 -07:00

148 lines
4.7 KiB
C++

//-----------------------------------------------------------------------------
// ___ __ _ _
// / _ \__ _ _ __ ___ ___ / /(_)_ __ | | __
// / /_)/ _` | '__/ __|/ _ \/ / | | '_ \| |/ /
// / ___/ (_| | | \__ \ __/ /__| | | | | <
// \/ \__,_|_| |___/\___\____/_|_| |_|_|\_\ .
//
//-----------------------------------------------------------------------------
// Author: Kurt Sassenrath
// Module: Logging
//
// Logging infrastructure.
//
// Copyright (c) 2023 Kurt Sassenrath.
//
// License TBD.
//-----------------------------------------------------------------------------
#ifndef logging_982a89e400976f59
#define logging_982a89e400976f59
#include <chrono>
#include <memory>
#include <span>
#include <string_view>
#include <vector>
#include "logging/formatters.h"
#include "logging/theme.h"
#include "logging/traits.h"
namespace parselink {
namespace logging {
// Any log levels higher (lower severity) than static_threshold cannot not be
// enabled in the library, but the compiler should be able to optimize away
// some/most of the calls.
constexpr inline auto static_threshold = level::trace;
constexpr inline auto default_threshold = level::info;
// Structure for holding a message. Note: message is a view over some buffer,
// not it's own string. It will either be a static buffer supplied by the
// endpoint implementation, or it will be dynamically created by the logger
// before calling endpoint::write()
struct message {
level lvl;
std::chrono::system_clock::time_point time;
std::string_view name;
std::string_view message;
};
struct endpoint {
virtual ~endpoint() = default;
virtual std::span<char> buffer() { return {}; }
virtual void write(message const& msg) = 0;
virtual bool colored() const noexcept = 0;
level threshold{default_threshold};
};
class logger {
public:
// This constructor utilizes the default logging endpoint.
logger(std::string_view name);
// This constructor allows for an arbitrary number of logging endpoints
// to be passed in.
logger(std::string_view name,
std::vector<std::shared_ptr<endpoint>> eps);
template <typename... Endpoints>
explicit logger(std::string_view name, Endpoints&&... eps)
: logger(name, {std::forward<Endpoints>(eps)...}) {}
template<level Level, typename... Args>
requires (Level <= static_threshold)
void log(fmt::format_string<Args...> format, Args&&... args) const {
try_write(Level, format.get(), std::forward<Args>(args)...);
}
template<level Level, typename... Args>
requires (Level > static_threshold)
[[gnu::flatten]] void log(fmt::format_string<Args...>, Args&&...) const {}
#define LOG_API(lvl) \
template <typename... Args> \
[[gnu::always_inline]] void lvl(fmt::format_string<Args...>&& format, Args&&... args) const { \
log<level::lvl>(std::forward<decltype(format)>(format), std::forward<Args>(args)...); \
}
LOG_API(critical);
LOG_API(error);
LOG_API(warning);
LOG_API(info);
LOG_API(verbose);
LOG_API(debug);
LOG_API(trace);
#undef LOG_API
void set_threshold(level new_threshold) noexcept;
private:
template<typename... Args>
void write_endpoint(std::shared_ptr<endpoint> const& endpoint, message msg,
fmt::string_view format, Args&&... args) const {
auto buff = endpoint->buffer();
if (buff.empty()) {
// Allocate memory.
auto buff = fmt::memory_buffer();
fmt::vformat_to(std::back_inserter(buff), format,
fmt::make_format_args(args...));
msg.message = std::string_view{buff.data(), buff.size()};
endpoint->write(msg);
} else {
// Fill the static buffer.
fmt::vformat_to(buff.begin(), format,
fmt::make_format_args(args...));
msg.message = std::string_view{buff.data(), buff.size()};
endpoint->write(msg);
}
}
template<typename... Args>
void try_write(level level, fmt::string_view format,
Args&&... args) const {
message msg{level, std::chrono::system_clock::now(), name_};
for (auto const& ep : endpoints_) {
if (level > ep->threshold) continue;
if (ep->colored()) {
write_endpoint(ep, msg, format,
themed_arg<Args>{std::forward<Args>(args)}...);
} else {
write_endpoint(ep, msg, format,
format_arg<Args>{std::forward<Args>(args)}...);
}
}
}
std::string name_;
std::vector<std::shared_ptr<endpoint>> endpoints_;
};
} // namespace logging
} // namespace parselink
#endif // logging_982a89e400976f59