vastly improve logger
This commit is contained in:
parent
22796b69bf
commit
58ee03c249
|
@ -13,8 +13,9 @@ IndentPPDirectives: BeforeHash
|
||||||
|
|
||||||
AllowAllParametersOfDeclarationOnNextLine: true
|
AllowAllParametersOfDeclarationOnNextLine: true
|
||||||
AllowShortBlocksOnASingleLine: false
|
AllowShortBlocksOnASingleLine: false
|
||||||
AllowShortFunctionsOnASingleLine: None
|
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||||
AllowShortIfStatementsOnASingleLine: Never
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
|
||||||
BinPackArguments: true
|
BinPackArguments: true
|
||||||
BinPackParameters: true
|
BinPackParameters: true
|
||||||
|
|
|
@ -1,75 +1,76 @@
|
||||||
//! Logging utilities for Lucore
|
//! Logging utilities for Lucore
|
||||||
//! Using Standard C++ <format>
|
//! Using Standard C++ <format>
|
||||||
|
|
||||||
#include <format>
|
#include <chrono>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <format>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace lucore {
|
namespace lucore {
|
||||||
|
|
||||||
/// The Lucore logger.
|
/// The Lucore logger.
|
||||||
struct Logger {
|
struct Logger {
|
||||||
enum class MessageSeverity {
|
enum class MessageSeverity { Debug, Info, Warning, Error, Fatal };
|
||||||
Debug,
|
|
||||||
Info,
|
|
||||||
Warning,
|
|
||||||
Error,
|
|
||||||
Fatal
|
|
||||||
};
|
|
||||||
|
|
||||||
static constexpr std::string_view SeverityToString(MessageSeverity sev) {
|
static constexpr std::string_view SeverityToString(MessageSeverity sev) {
|
||||||
const char* table[] = {
|
// This must match order of Logger::MessageSeverity.
|
||||||
"Deb",
|
const char* MessageSeverityStringTable[] = { "Deb", "Inf", "Wrn", "Err", "Ftl" };
|
||||||
"Inf",
|
return MessageSeverityStringTable[static_cast<std::size_t>(sev)];
|
||||||
"Wrn",
|
|
||||||
"Err",
|
|
||||||
"Ftl"
|
|
||||||
};
|
|
||||||
return table[static_cast<std::size_t>(sev)];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Message data. This is only used by logger sinks.
|
||||||
|
struct MessageData {
|
||||||
|
std::chrono::system_clock::time_point time;
|
||||||
|
MessageSeverity severity;
|
||||||
|
std::string_view format;
|
||||||
|
std::format_args args;
|
||||||
|
};
|
||||||
|
|
||||||
/// A sink.
|
/// A sink.
|
||||||
struct Sink {
|
struct Sink {
|
||||||
virtual void OutputMessage(MessageSeverity severity, std::string_view format, std::format_args args) = 0;
|
/// Output a message. This is called by the logger in Logger::VOut().
|
||||||
|
virtual void OutputMessage(const MessageData& data) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Get the single instance of the logger.
|
||||||
static Logger& The();
|
static Logger& The();
|
||||||
|
|
||||||
Logger() = default;
|
Logger() = default;
|
||||||
Logger(const Logger&) = delete;
|
Logger(const Logger&) = delete;
|
||||||
Logger(Logger&&) = delete;
|
Logger(Logger&&) = delete;
|
||||||
|
|
||||||
|
/// Attach a sink to the logger.
|
||||||
|
///
|
||||||
|
/// Attaching a sink will allow it to output log messages.
|
||||||
void AttachSink(Sink& sink);
|
void AttachSink(Sink& sink);
|
||||||
|
|
||||||
MessageSeverity GetLogLevel() const {
|
/// Get the current log level.
|
||||||
return logLevel;
|
MessageSeverity GetLogLevel() const { return logLevel; }
|
||||||
}
|
|
||||||
|
|
||||||
void SetLogLevel(MessageSeverity newLogLevel) {
|
/// Set the current log level.
|
||||||
logLevel = newLogLevel;
|
void SetLogLevel(MessageSeverity newLogLevel) { logLevel = newLogLevel; }
|
||||||
}
|
|
||||||
|
|
||||||
template<class... Args>
|
template <class... Args>
|
||||||
inline void Debug(std::string_view fmt, Args... args) {
|
inline void Debug(std::string_view fmt, Args... args) {
|
||||||
VOut(MessageSeverity::Debug, fmt, std::make_format_args(std::forward<Args>(args)...));
|
VOut(MessageSeverity::Debug, fmt, std::make_format_args(std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class... Args>
|
template <class... Args>
|
||||||
inline void Info(std::string_view fmt, Args... args) {
|
inline void Info(std::string_view fmt, Args... args) {
|
||||||
VOut(MessageSeverity::Info, fmt, std::make_format_args(std::forward<Args>(args)...));
|
VOut(MessageSeverity::Info, fmt, std::make_format_args(std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class... Args>
|
template <class... Args>
|
||||||
inline void Warning(std::string_view fmt, Args... args) {
|
inline void Warning(std::string_view fmt, Args... args) {
|
||||||
VOut(MessageSeverity::Warning, fmt, std::make_format_args(std::forward<Args>(args)...));
|
VOut(MessageSeverity::Warning, fmt, std::make_format_args(std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class... Args>
|
template <class... Args>
|
||||||
inline void Error(std::string_view fmt, Args... args) {
|
inline void Error(std::string_view fmt, Args... args) {
|
||||||
VOut(MessageSeverity::Error, fmt, std::make_format_args(std::forward<Args>(args)...));
|
VOut(MessageSeverity::Error, fmt, std::make_format_args(std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class... Args>
|
template <class... Args>
|
||||||
inline void Fatal(std::string_view fmt, Args... args) {
|
inline void Fatal(std::string_view fmt, Args... args) {
|
||||||
VOut(MessageSeverity::Fatal, fmt, std::make_format_args(std::forward<Args>(args)...));
|
VOut(MessageSeverity::Fatal, fmt, std::make_format_args(std::forward<Args>(args)...));
|
||||||
}
|
}
|
||||||
|
@ -77,20 +78,18 @@ namespace lucore {
|
||||||
private:
|
private:
|
||||||
void VOut(MessageSeverity severity, std::string_view format, std::format_args args);
|
void VOut(MessageSeverity severity, std::string_view format, std::format_args args);
|
||||||
|
|
||||||
MessageSeverity logLevel{MessageSeverity::Info};
|
MessageSeverity logLevel { MessageSeverity::Info };
|
||||||
|
std::vector<Sink*> sinks;
|
||||||
Sink* sinks[4];
|
|
||||||
std::uint8_t sinkCount;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A logger sink implementation that prints to standard output.
|
/// A logger sink implementation that prints to standard output.
|
||||||
struct StdoutSink : public Logger::Sink {
|
struct StdoutSink : public Logger::Sink {
|
||||||
static StdoutSink& The();
|
static StdoutSink& The();
|
||||||
|
|
||||||
virtual void OutputMessage(Logger::MessageSeverity severity, std::string_view format, std::format_args args) override;
|
virtual void OutputMessage(const Logger::MessageData& data) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Attach stdout to the logger.
|
/// Attach the stdout logger sink to the logger.
|
||||||
void LoggerAttachStdout();
|
void LoggerAttachStdout();
|
||||||
|
|
||||||
}
|
} // namespace lucore
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
namespace lucore {
|
namespace lucore {
|
||||||
|
|
||||||
[[noreturn]] void ExitMsg(const char* fileName, int fileLine, const char* message) {
|
[[noreturn]] void ExitMsg(const char* fileName, int fileLine, const char* message) {
|
||||||
Logger::The().Fatal("{}", message);
|
Logger::The().Fatal(message);
|
||||||
std::quick_exit(0xAF);
|
std::quick_exit(0xAF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
#include <format>
|
#include <iostream>
|
||||||
#include <lucore/Logger.hpp>
|
#include <lucore/Logger.hpp>
|
||||||
|
|
||||||
|
|
||||||
namespace lucore {
|
namespace lucore {
|
||||||
|
|
||||||
Logger& Logger::The() {
|
Logger& Logger::The() {
|
||||||
|
@ -10,15 +9,24 @@ namespace lucore {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::AttachSink(Sink& sink) {
|
void Logger::AttachSink(Sink& sink) {
|
||||||
sinks[sinkCount++] = &sink;
|
sinks.push_back(&sink);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Logger::VOut(MessageSeverity severity, std::string_view format, std::format_args args) {
|
void Logger::VOut(MessageSeverity severity, std::string_view format, std::format_args args) {
|
||||||
|
// give up early if no sinks are attached
|
||||||
|
if(sinks.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
if(severity < logLevel)
|
if(severity < logLevel)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for(int i = 0; i < sinkCount; ++i)
|
MessageData data { .time = std::chrono::system_clock::now(),
|
||||||
sinks[i]->OutputMessage(severity, format, args);
|
.severity = severity,
|
||||||
|
.format = format,
|
||||||
|
.args = args };
|
||||||
|
|
||||||
|
for(auto sink : sinks)
|
||||||
|
sink->OutputMessage(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
StdoutSink& StdoutSink::The() {
|
StdoutSink& StdoutSink::The() {
|
||||||
|
@ -26,13 +34,16 @@ namespace lucore {
|
||||||
return sink;
|
return sink;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StdoutSink::OutputMessage(Logger::MessageSeverity severity, std::string_view format, std::format_args args) {
|
void StdoutSink::OutputMessage(const Logger::MessageData& data) {
|
||||||
auto message = std::vformat(format, args);
|
// This is very nasty, but required until more standard libraries support the C++23 <print>
|
||||||
std::printf("[Lucore Stdout/%s] %s\n", Logger::SeverityToString(severity).data(), message.c_str());
|
// header.
|
||||||
|
std::puts(std::format("[Lucore/{}] [{}] {}", Logger::SeverityToString(data.severity),
|
||||||
|
data.time, std::vformat(data.format, data.args))
|
||||||
|
.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void LoggerAttachStdout() {
|
void LoggerAttachStdout() {
|
||||||
Logger::The().AttachSink(StdoutSink::The());
|
Logger::The().AttachSink(StdoutSink::The());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
} // namespace lucore
|
||||||
|
|
|
@ -2,27 +2,14 @@
|
||||||
#include <lucore/Assert.hpp>
|
#include <lucore/Assert.hpp>
|
||||||
#include <lucore/Logger.hpp>
|
#include <lucore/Logger.hpp>
|
||||||
|
|
||||||
/// Sample implementation of lucore logger sink.
|
|
||||||
struct MySink : public lucore::Logger::Sink {
|
|
||||||
void OutputMessage(lucore::Logger::MessageSeverity sev, std::string_view fmt, std::format_args args) override {
|
|
||||||
std::printf("[My Sink] [%s] %s\n", lucore::Logger::SeverityToString(sev).data(), std::vformat(fmt, args).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
static MySink& The() {
|
|
||||||
static MySink sink;
|
|
||||||
return sink;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
lucore::LoggerAttachStdout();
|
lucore::LoggerAttachStdout();
|
||||||
auto& logger = lucore::Logger::The();
|
auto& logger = lucore::Logger::The();
|
||||||
|
|
||||||
logger.AttachSink(MySink::The()); // attach our sink
|
|
||||||
|
|
||||||
logger.Info("Hello World {}", 123.456);
|
logger.Info("Hello World {}", 123.456);
|
||||||
logger.Warning("sample warning");
|
logger.Warning("sample warning");
|
||||||
logger.Error("Smaple Error");
|
logger.Error("Smaple Error");
|
||||||
|
|
||||||
|
//LUCORE_ASSERT(false, "expr should assert");
|
||||||
LUCORE_CHECK(false, "should appear");
|
LUCORE_CHECK(false, "should appear");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue