diff --git a/.clang-format b/.clang-format index 3927a2f..d3f1e90 100755 --- a/.clang-format +++ b/.clang-format @@ -13,8 +13,9 @@ IndentPPDirectives: BeforeHash AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false -AllowShortFunctionsOnASingleLine: None +AllowShortFunctionsOnASingleLine: InlineOnly AllowShortIfStatementsOnASingleLine: Never +AllowShortLoopsOnASingleLine: false BinPackArguments: true BinPackParameters: true diff --git a/native/projects/lucore/include/lucore/Logger.hpp b/native/projects/lucore/include/lucore/Logger.hpp index 00b9ab5..6c22168 100644 --- a/native/projects/lucore/include/lucore/Logger.hpp +++ b/native/projects/lucore/include/lucore/Logger.hpp @@ -1,96 +1,95 @@ //! Logging utilities for Lucore //! Using Standard C++ -#include +#include #include +#include +#include namespace lucore { /// The Lucore logger. struct Logger { - enum class MessageSeverity { - Debug, - Info, - Warning, - Error, - Fatal - }; + enum class MessageSeverity { Debug, Info, Warning, Error, Fatal }; static constexpr std::string_view SeverityToString(MessageSeverity sev) { - const char* table[] = { - "Deb", - "Inf", - "Wrn", - "Err", - "Ftl" - }; - return table[static_cast(sev)]; + // This must match order of Logger::MessageSeverity. + const char* MessageSeverityStringTable[] = { "Deb", "Inf", "Wrn", "Err", "Ftl" }; + return MessageSeverityStringTable[static_cast(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. 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(); Logger() = default; Logger(const 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); - MessageSeverity GetLogLevel() const { - return logLevel; - } + /// Get the current log level. + MessageSeverity GetLogLevel() const { return logLevel; } - void SetLogLevel(MessageSeverity newLogLevel) { - logLevel = newLogLevel; - } + /// Set the current log level. + void SetLogLevel(MessageSeverity newLogLevel) { logLevel = newLogLevel; } - template + template inline void Debug(std::string_view fmt, Args... args) { VOut(MessageSeverity::Debug, fmt, std::make_format_args(std::forward(args)...)); } - template + template inline void Info(std::string_view fmt, Args... args) { VOut(MessageSeverity::Info, fmt, std::make_format_args(std::forward(args)...)); } - - template + + template inline void Warning(std::string_view fmt, Args... args) { VOut(MessageSeverity::Warning, fmt, std::make_format_args(std::forward(args)...)); } - template + template inline void Error(std::string_view fmt, Args... args) { VOut(MessageSeverity::Error, fmt, std::make_format_args(std::forward(args)...)); } - template + template inline void Fatal(std::string_view fmt, Args... args) { VOut(MessageSeverity::Fatal, fmt, std::make_format_args(std::forward(args)...)); } - private: + private: void VOut(MessageSeverity severity, std::string_view format, std::format_args args); - MessageSeverity logLevel{MessageSeverity::Info}; - - Sink* sinks[4]; - std::uint8_t sinkCount; + MessageSeverity logLevel { MessageSeverity::Info }; + std::vector sinks; }; /// A logger sink implementation that prints to standard output. struct StdoutSink : public Logger::Sink { 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(); -} +} // namespace lucore diff --git a/native/projects/lucore/src/Assert.cpp b/native/projects/lucore/src/Assert.cpp index f4ebb5f..da8b2fb 100644 --- a/native/projects/lucore/src/Assert.cpp +++ b/native/projects/lucore/src/Assert.cpp @@ -7,7 +7,7 @@ namespace lucore { [[noreturn]] void ExitMsg(const char* fileName, int fileLine, const char* message) { - Logger::The().Fatal("{}", message); + Logger::The().Fatal(message); std::quick_exit(0xAF); } diff --git a/native/projects/lucore/src/Logger.cpp b/native/projects/lucore/src/Logger.cpp index 73a68a1..d075697 100644 --- a/native/projects/lucore/src/Logger.cpp +++ b/native/projects/lucore/src/Logger.cpp @@ -1,7 +1,6 @@ -#include +#include #include - namespace lucore { Logger& Logger::The() { @@ -10,15 +9,24 @@ namespace lucore { } 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) { + // give up early if no sinks are attached + if(sinks.empty()) + return; + if(severity < logLevel) return; - for(int i = 0; i < sinkCount; ++i) - sinks[i]->OutputMessage(severity, format, args); + MessageData data { .time = std::chrono::system_clock::now(), + .severity = severity, + .format = format, + .args = args }; + + for(auto sink : sinks) + sink->OutputMessage(data); } StdoutSink& StdoutSink::The() { @@ -26,13 +34,16 @@ namespace lucore { return sink; } - void StdoutSink::OutputMessage(Logger::MessageSeverity severity, std::string_view format, std::format_args args) { - auto message = std::vformat(format, args); - std::printf("[Lucore Stdout/%s] %s\n", Logger::SeverityToString(severity).data(), message.c_str()); + void StdoutSink::OutputMessage(const Logger::MessageData& data) { + // This is very nasty, but required until more standard libraries support the C++23 + // header. + std::puts(std::format("[Lucore/{}] [{}] {}", Logger::SeverityToString(data.severity), + data.time, std::vformat(data.format, data.args)) + .c_str()); } void LoggerAttachStdout() { Logger::The().AttachSink(StdoutSink::The()); } -} +} // namespace lucore diff --git a/native/projects/lucore_test/main.cpp b/native/projects/lucore_test/main.cpp index 72c6de3..3dc97dc 100644 --- a/native/projects/lucore_test/main.cpp +++ b/native/projects/lucore_test/main.cpp @@ -2,27 +2,14 @@ #include #include -/// 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() { lucore::LoggerAttachStdout(); auto& logger = lucore::Logger::The(); - logger.AttachSink(MySink::The()); // attach our sink - logger.Info("Hello World {}", 123.456); logger.Warning("sample warning"); logger.Error("Smaple Error"); + //LUCORE_ASSERT(false, "expr should assert"); LUCORE_CHECK(false, "should appear"); }