vastly improve logger

This commit is contained in:
Lily Tsuru 2023-07-17 16:27:27 -04:00
parent 22796b69bf
commit 58ee03c249
5 changed files with 61 additions and 63 deletions

View File

@ -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

View File

@ -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

View File

@ -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);
} }

View File

@ -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

View File

@ -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");
} }