aboutsummaryrefslogtreecommitdiff
path: root/src/zencore/logging.cpp
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2026-03-09 10:50:47 +0100
committerGitHub Enterprise <[email protected]>2026-03-09 10:50:47 +0100
commit19a117889c2db6b817af9458c04c04f324162e75 (patch)
treefbbd0d01c5bf40be90cec88e1d02c6a3c529a2f5 /src/zencore/logging.cpp
parentzenstore bug-fixes from static analysis pass (#815) (diff)
downloadzen-19a117889c2db6b817af9458c04c04f324162e75.tar.xz
zen-19a117889c2db6b817af9458c04c04f324162e75.zip
Eliminate spdlog dependency (#773)
Removes the vendored spdlog library (~12,000 lines) and replaces it with a purpose-built logging system in zencore (~1,800 lines). The new implementation provides the same functionality with fewer abstractions, no shared_ptr overhead, and full control over the logging pipeline. ### What changed **New logging core in zencore/logging/:** - LogMessage, Formatter, Sink, Logger, Registry - core abstractions matching spdlog's model but simplified - AnsiColorStdoutSink - ANSI color console output (replaces spdlog stdout_color_sink) - MsvcSink - OutputDebugString on Windows (replaces spdlog msvc_sink) - AsyncSink - async logging via BlockingQueue worker thread (replaces spdlog async_logger) - NullSink, MessageOnlyFormatter - utility types - Thread-safe timestamp caching in formatters using RwLock **Moved to zenutil/logging/:** - FullFormatter - full log formatting with timestamp, logger name, level, source location, multiline alignment - JsonFormatter - structured JSON log output - RotatingFileSink - rotating file sink with atomic size tracking **API changes:** - Log levels are now an enum (LogLevel) instead of int, eliminating the zen::logging::level namespace - LoggerRef no longer wraps shared_ptr - it holds a raw pointer with the registry owning lifetime - Logger error handler is wired through Registry and propagated to all loggers on registration - Logger::Log() now populates ThreadId on every message **Cleanup:** - Deleted thirdparty/spdlog/ entirely (110+ files) - Deleted full_test_formatter (was ~80% duplicate of FullFormatter) - Renamed snake_case classes to PascalCase (full_formatter -> FullFormatter, json_formatter -> JsonFormatter, sentry_sink -> SentrySink) - Removed spdlog from xmake dependency graph ### Build / test impact - zencore no longer depends on spdlog - zenutil and zenvfs xmake.lua updated to drop spdlog dep - zentelemetry xmake.lua updated to drop spdlog dep - All existing tests pass, no test changes required beyond formatter class renames
Diffstat (limited to 'src/zencore/logging.cpp')
-rw-r--r--src/zencore/logging.cpp328
1 files changed, 143 insertions, 185 deletions
diff --git a/src/zencore/logging.cpp b/src/zencore/logging.cpp
index ebd68de09..099518637 100644
--- a/src/zencore/logging.cpp
+++ b/src/zencore/logging.cpp
@@ -2,208 +2,128 @@
#include "zencore/logging.h"
+#include <zencore/logging/ansicolorsink.h>
+#include <zencore/logging/logger.h>
+#include <zencore/logging/messageonlyformatter.h>
+#include <zencore/logging/nullsink.h>
+#include <zencore/logging/registry.h>
#include <zencore/string.h>
#include <zencore/testing.h>
#include <zencore/thread.h>
#include <zencore/memory/llm.h>
-ZEN_THIRD_PARTY_INCLUDES_START
-#include <spdlog/details/registry.h>
-#include <spdlog/sinks/null_sink.h>
-#include <spdlog/sinks/stdout_color_sinks.h>
-#include <spdlog/spdlog.h>
-ZEN_THIRD_PARTY_INCLUDES_END
+#include <mutex>
#if ZEN_PLATFORM_WINDOWS
# pragma section(".zlog$a", read)
-# pragma section(".zlog$f", read)
-# pragma section(".zlog$m", read)
-# pragma section(".zlog$s", read)
+# pragma section(".zlog$l", read)
# pragma section(".zlog$z", read)
#endif
namespace zen {
-// We shadow the underlying spdlog default logger, in order to avoid a bunch of overhead
LoggerRef TheDefaultLogger;
+LoggerRef
+Log()
+{
+ if (TheDefaultLogger)
+ {
+ return TheDefaultLogger;
+ }
+ return zen::logging::ConsoleLog();
+}
+
} // namespace zen
namespace zen::logging {
-using MemoryBuffer_t = fmt::basic_memory_buffer<char, 250>;
-
-struct LoggingContext
-{
- inline LoggingContext();
- inline ~LoggingContext();
-
- zen::logging::MemoryBuffer_t MessageBuffer;
-
- inline std::string_view Message() const { return std::string_view(MessageBuffer.data(), MessageBuffer.size()); }
-};
+//////////////////////////////////////////////////////////////////////////
-LoggingContext::LoggingContext()
+LoggerRef
+LogCategory::Logger()
{
-}
+ // This should be thread safe since zen::logging::Get() will return
+ // the same logger instance for the same category name. Also the
+ // LoggerRef is simply a pointer.
+ if (!m_LoggerRef)
+ {
+ m_LoggerRef = zen::logging::Get(m_CategoryName);
+ }
-LoggingContext::~LoggingContext()
-{
+ return m_LoggerRef;
}
-//////////////////////////////////////////////////////////////////////////
-
static inline bool
-IsErrorLevel(int LogLevel)
+IsErrorLevel(LogLevel InLevel)
{
- return (LogLevel == zen::logging::level::Err || LogLevel == zen::logging::level::Critical);
+ return (InLevel == Err || InLevel == Critical);
};
-static_assert(sizeof(spdlog::source_loc) == sizeof(SourceLocation));
-static_assert(offsetof(spdlog::source_loc, filename) == offsetof(SourceLocation, filename));
-static_assert(offsetof(spdlog::source_loc, line) == offsetof(SourceLocation, line));
-static_assert(offsetof(spdlog::source_loc, funcname) == offsetof(SourceLocation, funcname));
-
void
-EmitLogMessage(LoggerRef& Logger, int LogLevel, const std::string_view Message)
+EmitLogMessage(LoggerRef& Logger, const LogPoint& Lp, fmt::format_args Args)
{
ZEN_MEMSCOPE(ELLMTag::Logging);
- const spdlog::level::level_enum InLevel = (spdlog::level::level_enum)LogLevel;
- Logger.SpdLogger->log(InLevel, Message);
- if (IsErrorLevel(LogLevel))
- {
- if (LoggerRef ErrLogger = zen::logging::ErrorLog())
- {
- ErrLogger.SpdLogger->log(InLevel, Message);
- }
- }
-}
-void
-EmitLogMessage(LoggerRef& Logger, int LogLevel, std::string_view Format, fmt::format_args Args)
-{
- ZEN_MEMSCOPE(ELLMTag::Logging);
- zen::logging::LoggingContext LogCtx;
- fmt::vformat_to(fmt::appender(LogCtx.MessageBuffer), Format, Args);
- zen::logging::EmitLogMessage(Logger, LogLevel, LogCtx.Message());
-}
+ Logger->Log(Lp, Args);
-void
-EmitLogMessage(LoggerRef& Logger, const SourceLocation& InLocation, int LogLevel, const std::string_view Message)
-{
- ZEN_MEMSCOPE(ELLMTag::Logging);
- const spdlog::source_loc& Location = *reinterpret_cast<const spdlog::source_loc*>(&InLocation);
- const spdlog::level::level_enum InLevel = (spdlog::level::level_enum)LogLevel;
- Logger.SpdLogger->log(Location, InLevel, Message);
- if (IsErrorLevel(LogLevel))
+ if (IsErrorLevel(Lp.Level))
{
if (LoggerRef ErrLogger = zen::logging::ErrorLog())
{
- ErrLogger.SpdLogger->log(Location, InLevel, Message);
+ ErrLogger->Log(Lp, Args);
}
}
}
void
-EmitLogMessage(LoggerRef& Logger, const SourceLocation& InLocation, int LogLevel, std::string_view Format, fmt::format_args Args)
-{
- ZEN_MEMSCOPE(ELLMTag::Logging);
- zen::logging::LoggingContext LogCtx;
- fmt::vformat_to(fmt::appender(LogCtx.MessageBuffer), Format, Args);
- zen::logging::EmitLogMessage(Logger, InLocation, LogLevel, LogCtx.Message());
-}
-
-void
-EmitConsoleLogMessage(int LogLevel, const std::string_view Message)
-{
- ZEN_MEMSCOPE(ELLMTag::Logging);
- const spdlog::level::level_enum InLevel = (spdlog::level::level_enum)LogLevel;
- ConsoleLog().SpdLogger->log(InLevel, Message);
-}
-
-#define ZEN_COLOR_YELLOW "\033[0;33m"
-#define ZEN_COLOR_RED "\033[0;31m"
-#define ZEN_BRIGHT_COLOR_RED "\033[1;31m"
-#define ZEN_COLOR_RESET "\033[0m"
-
-void
-EmitConsoleLogMessage(int LogLevel, std::string_view Format, fmt::format_args Args)
+EmitConsoleLogMessage(const LogPoint& Lp, fmt::format_args Args)
{
ZEN_MEMSCOPE(ELLMTag::Logging);
- zen::logging::LoggingContext LogCtx;
-
- // We are not using a format option for console which include log level since it would interfere with normal console output
-
- const spdlog::level::level_enum InLevel = (spdlog::level::level_enum)LogLevel;
- switch (InLevel)
- {
- case spdlog::level::level_enum::warn:
- fmt::format_to(fmt::appender(LogCtx.MessageBuffer), ZEN_COLOR_YELLOW "Warning: " ZEN_COLOR_RESET);
- break;
- case spdlog::level::level_enum::err:
- fmt::format_to(fmt::appender(LogCtx.MessageBuffer), ZEN_BRIGHT_COLOR_RED "Error: " ZEN_COLOR_RESET);
- break;
- case spdlog::level::level_enum::critical:
- fmt::format_to(fmt::appender(LogCtx.MessageBuffer), ZEN_COLOR_RED "Critical: " ZEN_COLOR_RESET);
- break;
- default:
- break;
- }
- fmt::vformat_to(fmt::appender(LogCtx.MessageBuffer), Format, Args);
- zen::logging::EmitConsoleLogMessage(LogLevel, LogCtx.Message());
+ ConsoleLog()->Log(Lp, Args);
}
} // namespace zen::logging
-namespace zen::logging::level {
+namespace zen::logging {
-spdlog::level::level_enum
-to_spdlog_level(LogLevel NewLogLevel)
-{
- return static_cast<spdlog::level::level_enum>((int)NewLogLevel);
-}
+constinit std::string_view LevelNames[] = {std::string_view("trace", 5),
+ std::string_view("debug", 5),
+ std::string_view("info", 4),
+ std::string_view("warning", 7),
+ std::string_view("error", 5),
+ std::string_view("critical", 8),
+ std::string_view("off", 3)};
LogLevel
-to_logging_level(spdlog::level::level_enum NewLogLevel)
-{
- return static_cast<LogLevel>((int)NewLogLevel);
-}
-
-constinit std::string_view LevelNames[] = {ZEN_LEVEL_NAME_TRACE,
- ZEN_LEVEL_NAME_DEBUG,
- ZEN_LEVEL_NAME_INFO,
- ZEN_LEVEL_NAME_WARNING,
- ZEN_LEVEL_NAME_ERROR,
- ZEN_LEVEL_NAME_CRITICAL,
- ZEN_LEVEL_NAME_OFF};
-
-level::LogLevel
ParseLogLevelString(std::string_view Name)
{
- for (int Level = 0; Level < level::LogLevelCount; ++Level)
+ for (int Level = 0; Level < LogLevelCount; ++Level)
{
if (LevelNames[Level] == Name)
- return static_cast<level::LogLevel>(Level);
+ {
+ return static_cast<LogLevel>(Level);
+ }
}
if (Name == "warn")
{
- return level::Warn;
+ return Warn;
}
if (Name == "err")
{
- return level::Err;
+ return Err;
}
- return level::Off;
+ return Off;
}
std::string_view
-ToStringView(level::LogLevel Level)
+ToStringView(LogLevel Level)
{
- if (int(Level) < level::LogLevelCount)
+ if (int(Level) < LogLevelCount)
{
return LevelNames[int(Level)];
}
@@ -211,17 +131,17 @@ ToStringView(level::LogLevel Level)
return "None";
}
-} // namespace zen::logging::level
+} // namespace zen::logging
//////////////////////////////////////////////////////////////////////////
namespace zen::logging {
RwLock LogLevelsLock;
-std::string LogLevels[level::LogLevelCount];
+std::string LogLevels[LogLevelCount];
void
-ConfigureLogLevels(level::LogLevel Level, std::string_view Loggers)
+ConfigureLogLevels(LogLevel Level, std::string_view Loggers)
{
ZEN_MEMSCOPE(ELLMTag::Logging);
@@ -230,18 +150,18 @@ ConfigureLogLevels(level::LogLevel Level, std::string_view Loggers)
}
void
-RefreshLogLevels(level::LogLevel* DefaultLevel)
+RefreshLogLevels(LogLevel* DefaultLevel)
{
ZEN_MEMSCOPE(ELLMTag::Logging);
- spdlog::details::registry::log_levels Levels;
+ std::vector<std::pair<std::string, LogLevel>> Levels;
{
RwLock::SharedLockScope _(LogLevelsLock);
- for (int i = 0; i < level::LogLevelCount; ++i)
+ for (int i = 0; i < LogLevelCount; ++i)
{
- level::LogLevel CurrentLevel{i};
+ LogLevel CurrentLevel{i};
std::string_view Spec = LogLevels[i];
@@ -260,24 +180,16 @@ RefreshLogLevels(level::LogLevel* DefaultLevel)
Spec = {};
}
- Levels[LoggerName] = to_spdlog_level(CurrentLevel);
+ Levels.emplace_back(std::move(LoggerName), CurrentLevel);
}
}
}
- if (DefaultLevel)
- {
- spdlog::level::level_enum SpdDefaultLevel = to_spdlog_level(*DefaultLevel);
- spdlog::details::registry::instance().set_levels(Levels, &SpdDefaultLevel);
- }
- else
- {
- spdlog::details::registry::instance().set_levels(Levels, nullptr);
- }
+ Registry::Instance().SetLevels(Levels, DefaultLevel);
}
void
-RefreshLogLevels(level::LogLevel DefaultLevel)
+RefreshLogLevels(LogLevel DefaultLevel)
{
RefreshLogLevels(&DefaultLevel);
}
@@ -289,15 +201,15 @@ RefreshLogLevels()
}
void
-SetLogLevel(level::LogLevel NewLogLevel)
+SetLogLevel(LogLevel NewLogLevel)
{
- spdlog::set_level(to_spdlog_level(NewLogLevel));
+ Registry::Instance().SetGlobalLevel(NewLogLevel);
}
-level::LogLevel
+LogLevel
GetLogLevel()
{
- return level::to_logging_level(spdlog::get_level());
+ return Registry::Instance().GetGlobalLevel();
}
LoggerRef
@@ -312,10 +224,10 @@ SetDefault(std::string_view NewDefaultLoggerId)
{
ZEN_MEMSCOPE(ELLMTag::Logging);
- auto NewDefaultLogger = spdlog::get(std::string(NewDefaultLoggerId));
+ Ref<Logger> NewDefaultLogger = Registry::Instance().Get(std::string(NewDefaultLoggerId));
ZEN_ASSERT(NewDefaultLogger);
- spdlog::set_default_logger(NewDefaultLogger);
+ Registry::Instance().SetDefaultLogger(NewDefaultLogger);
TheDefaultLogger = LoggerRef(*NewDefaultLogger);
}
@@ -338,11 +250,11 @@ SetErrorLog(std::string_view NewErrorLoggerId)
}
else
{
- auto NewErrorLogger = spdlog::get(std::string(NewErrorLoggerId));
+ Ref<Logger> NewErrorLogger = Registry::Instance().Get(std::string(NewErrorLoggerId));
ZEN_ASSERT(NewErrorLogger);
- TheErrorLogger = LoggerRef(*NewErrorLogger.get());
+ TheErrorLogger = LoggerRef(*NewErrorLogger.Get());
}
}
@@ -353,39 +265,75 @@ Get(std::string_view Name)
{
ZEN_MEMSCOPE(ELLMTag::Logging);
- std::shared_ptr<spdlog::logger> Logger = spdlog::get(std::string(Name));
+ Ref<Logger> FoundLogger = Registry::Instance().Get(std::string(Name));
- if (!Logger)
+ if (!FoundLogger)
{
g_LoggerMutex.WithExclusiveLock([&] {
- Logger = spdlog::get(std::string(Name));
+ FoundLogger = Registry::Instance().Get(std::string(Name));
- if (!Logger)
+ if (!FoundLogger)
{
- Logger = Default().SpdLogger->clone(std::string(Name));
- spdlog::apply_logger_env_levels(Logger);
- spdlog::register_logger(Logger);
+ FoundLogger = Default()->Clone(std::string(Name));
+ Registry::Instance().Register(FoundLogger);
}
});
}
- return *Logger;
+ return *FoundLogger;
}
-std::once_flag ConsoleInitFlag;
-std::shared_ptr<spdlog::logger> ConLogger;
+std::once_flag ConsoleInitFlag;
+Ref<Logger> ConLogger;
void
SuppressConsoleLog()
{
+ ZEN_MEMSCOPE(ELLMTag::Logging);
+
if (ConLogger)
{
- spdlog::drop("console");
+ Registry::Instance().Drop("console");
ConLogger = {};
}
- ConLogger = spdlog::null_logger_mt("console");
+
+ SinkPtr NullSinkPtr(new NullSink());
+ ConLogger = Ref<Logger>(new Logger("console", std::vector<SinkPtr>{NullSinkPtr}));
+ Registry::Instance().Register(ConLogger);
}
+#define ZEN_COLOR_YELLOW "\033[0;33m"
+#define ZEN_COLOR_RED "\033[0;31m"
+#define ZEN_BRIGHT_COLOR_RED "\033[1;31m"
+#define ZEN_COLOR_RESET "\033[0m"
+
+class ConsoleFormatter : public Formatter
+{
+public:
+ void Format(const LogMessage& Msg, MemoryBuffer& Dest) override
+ {
+ switch (Msg.GetLevel())
+ {
+ case Warn:
+ fmt::format_to(fmt::appender(Dest), ZEN_COLOR_YELLOW "Warning: " ZEN_COLOR_RESET);
+ break;
+ case Err:
+ fmt::format_to(fmt::appender(Dest), ZEN_BRIGHT_COLOR_RED "Error: " ZEN_COLOR_RESET);
+ break;
+ case Critical:
+ fmt::format_to(fmt::appender(Dest), ZEN_COLOR_RED "Critical: " ZEN_COLOR_RESET);
+ break;
+ default:
+ break;
+ }
+
+ helpers::AppendStringView(Msg.GetPayload(), Dest);
+ Dest.push_back('\n');
+ }
+
+ std::unique_ptr<Formatter> Clone() const override { return std::make_unique<ConsoleFormatter>(); }
+};
+
LoggerRef
ConsoleLog()
{
@@ -394,10 +342,10 @@ ConsoleLog()
std::call_once(ConsoleInitFlag, [&] {
if (!ConLogger)
{
- ConLogger = spdlog::stdout_color_mt("console");
- spdlog::apply_logger_env_levels(ConLogger);
-
- ConLogger->set_pattern("%v");
+ SinkPtr ConsoleSink(new AnsiColorStdoutSink());
+ ConsoleSink->SetFormatter(std::make_unique<ConsoleFormatter>());
+ ConLogger = Ref<Logger>(new Logger("console", std::vector<SinkPtr>{ConsoleSink}));
+ Registry::Instance().Register(ConLogger);
}
});
@@ -407,9 +355,11 @@ ConsoleLog()
void
ResetConsoleLog()
{
+ ZEN_MEMSCOPE(ELLMTag::Logging);
+
LoggerRef ConLog = ConsoleLog();
- ConLog.SpdLogger->set_pattern("%v");
+ ConLog->SetFormatter(std::make_unique<ConsoleFormatter>());
}
void
@@ -417,13 +367,15 @@ InitializeLogging()
{
ZEN_MEMSCOPE(ELLMTag::Logging);
- TheDefaultLogger = *spdlog::default_logger_raw();
+ TheDefaultLogger = *Registry::Instance().DefaultLoggerRaw();
}
void
ShutdownLogging()
{
- spdlog::shutdown();
+ ZEN_MEMSCOPE(ELLMTag::Logging);
+
+ Registry::Instance().Shutdown();
TheDefaultLogger = {};
}
@@ -457,7 +409,7 @@ EnableVTMode()
void
FlushLogging()
{
- spdlog::details::registry::instance().flush_all();
+ Registry::Instance().FlushAll();
}
} // namespace zen::logging
@@ -465,21 +417,27 @@ FlushLogging()
namespace zen {
bool
-LoggerRef::ShouldLog(int Level) const
+LoggerRef::ShouldLog(logging::LogLevel Level) const
{
- return SpdLogger->should_log(static_cast<spdlog::level::level_enum>(Level));
+ return m_Logger->ShouldLog(Level);
}
void
-LoggerRef::SetLogLevel(logging::level::LogLevel NewLogLevel)
+LoggerRef::SetLogLevel(logging::LogLevel NewLogLevel)
{
- SpdLogger->set_level(to_spdlog_level(NewLogLevel));
+ m_Logger->SetLevel(NewLogLevel);
}
-logging::level::LogLevel
+logging::LogLevel
LoggerRef::GetLogLevel()
{
- return logging::level::to_logging_level(SpdLogger->level());
+ return m_Logger->GetLevel();
+}
+
+void
+LoggerRef::Flush()
+{
+ m_Logger->Flush();
}
thread_local ScopedActivityBase* t_ScopeStack = nullptr;