parselink-old/source/common/include/logging.h
Kurt Sassenrath b0ed20369f Initial commit
* Logging ported from layover project with minor tweaks.
    * Logging test ported over.
    * Boost libraries are available.
2023-09-04 16:03:58 -07:00

131 lines
4.3 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 <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::verbose;
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 {}
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