msgpack::map_view can be used to iterate, pair-wise, over a range of msgpack::token. It will immediately return if the first token is not a map, and will skip over nested map/arrays. Note for the future: It will be handy to be able to get the subspan corresponding to the nested map/array. Will think about how to solve that later. Begin incorporating map_view into the server. Add formatters for std::byte, dynamic theme for bool, and spans thereof. Maybe switch to range?
148 lines
4.7 KiB
C++
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::debug;
|
|
|
|
// 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
|