aboutsummaryrefslogtreecommitdiff
path: root/src/zencore
diff options
context:
space:
mode:
authorStefan Boberg <[email protected]>2023-11-06 20:36:30 +0100
committerGitHub <[email protected]>2023-11-06 20:36:30 +0100
commit07f288d6e119fc6bb524fb634bc9094109a2ab05 (patch)
tree8531bd38d0d8c3567ba3d0a5603f549faf99632b /src/zencore
parentgc v2 tests (#512) (diff)
downloadzen-07f288d6e119fc6bb524fb634bc9094109a2ab05.tar.xz
zen-07f288d6e119fc6bb524fb634bc9094109a2ab05.zip
spdlog implementation hiding (#498)
this change aims to hide logging internals from client code, in order to make it easier to extend and take more control over the logging process in the future. As a bonus side effect, the generated code is much tighter (net delta around 2.5% on the resulting executable which includes lots of thirdparty code) and should take less time to compile and link. Client usage via macros is pretty much unchanged. The main exposure client code had to spdlog internals before was the use of custom loggers per subsystem, where it would be common to have `spdlog::logger` references to keep a reference to a logger within a class. This is now replaced by `zen::LoggerRef` which currently simply encapsulates an actual `spdlog::logger` instance, but this is intended to be an implementation detail which will change in the future. The way the change works is that we now handle any formatting of log messages in the zencore logging subsystem instead of relying on `spdlog` to manage this. We use the `fmt` library to do the formatting which means the client usage is identical to using `spdlog`. The formatted message is then forwarded onto any sinks etc which are still implememted via `spdlog`.
Diffstat (limited to 'src/zencore')
-rw-r--r--src/zencore/include/zencore/fmtutils.h23
-rw-r--r--src/zencore/include/zencore/logbase.h96
-rw-r--r--src/zencore/include/zencore/logging.h269
-rw-r--r--src/zencore/include/zencore/string.h22
-rw-r--r--src/zencore/include/zencore/zencore.h2
-rw-r--r--src/zencore/logging.cpp241
-rw-r--r--src/zencore/testing.cpp4
7 files changed, 488 insertions, 169 deletions
diff --git a/src/zencore/include/zencore/fmtutils.h b/src/zencore/include/zencore/fmtutils.h
index 3d29625be..9c5bdae66 100644
--- a/src/zencore/include/zencore/fmtutils.h
+++ b/src/zencore/include/zencore/fmtutils.h
@@ -16,6 +16,26 @@ ZEN_THIRD_PARTY_INCLUDES_END
// Custom formatting for some zencore types
+template<typename T>
+requires DerivedFrom<T, zen::StringBuilderBase>
+struct fmt::formatter<T> : fmt::formatter<std::string_view>
+{
+ auto format(const zen::StringBuilderBase& a, format_context& ctx) const
+ {
+ return fmt::formatter<std::string_view>::format(a.ToView(), ctx);
+ }
+};
+
+template<typename T>
+requires DerivedFrom<T, zen::NiceBase>
+struct fmt::formatter<T> : fmt::formatter<std::string_view>
+{
+ auto format(const zen::NiceBase& a, format_context& ctx) const
+ {
+ return fmt::formatter<std::string_view>::format(std::string_view(a), ctx);
+ }
+};
+
template<>
struct fmt::formatter<zen::IoHash> : formatter<string_view>
{
@@ -74,7 +94,8 @@ struct fmt::formatter<std::filesystem::path> : formatter<string_view>
};
template<typename T>
-struct fmt::formatter<T, std::enable_if_t<std::is_base_of<zen::PathBuilderBase, T>::value, char>> : fmt::formatter<std::string_view>
+requires DerivedFrom<T, zen::PathBuilderBase>
+struct fmt::formatter<T> : fmt::formatter<std::string_view>
{
auto format(const zen::PathBuilderBase& a, format_context& ctx) const
{
diff --git a/src/zencore/include/zencore/logbase.h b/src/zencore/include/zencore/logbase.h
new file mode 100644
index 000000000..ad873aa51
--- /dev/null
+++ b/src/zencore/include/zencore/logbase.h
@@ -0,0 +1,96 @@
+// Copyright Epic Games, Inc. All Rights Reserved.
+
+#pragma once
+
+#include <string_view>
+
+#define ZEN_LOG_LEVEL_TRACE 0
+#define ZEN_LOG_LEVEL_DEBUG 1
+#define ZEN_LOG_LEVEL_INFO 2
+#define ZEN_LOG_LEVEL_WARN 3
+#define ZEN_LOG_LEVEL_ERROR 4
+#define ZEN_LOG_LEVEL_CRITICAL 5
+#define ZEN_LOG_LEVEL_OFF 6
+
+#define ZEN_LEVEL_NAME_TRACE std::string_view("trace", 5)
+#define ZEN_LEVEL_NAME_DEBUG std::string_view("debug", 5)
+#define ZEN_LEVEL_NAME_INFO std::string_view("info", 4)
+#define ZEN_LEVEL_NAME_WARNING std::string_view("warning", 7)
+#define ZEN_LEVEL_NAME_ERROR std::string_view("error", 5)
+#define ZEN_LEVEL_NAME_CRITICAL std::string_view("critical", 8)
+#define ZEN_LEVEL_NAME_OFF std::string_view("off", 3)
+
+namespace zen::logging::level {
+
+enum LogLevel : int
+{
+ Trace = ZEN_LOG_LEVEL_TRACE,
+ Debug = ZEN_LOG_LEVEL_DEBUG,
+ Info = ZEN_LOG_LEVEL_INFO,
+ Warn = ZEN_LOG_LEVEL_WARN,
+ Err = ZEN_LOG_LEVEL_ERROR,
+ Critical = ZEN_LOG_LEVEL_CRITICAL,
+ Off = ZEN_LOG_LEVEL_OFF,
+ LogLevelCount
+};
+
+LogLevel ParseLogLevelString(std::string_view String);
+std::string_view ToStringView(LogLevel Level);
+
+} // namespace zen::logging::level
+
+namespace zen::logging {
+
+void SetLogLevel(level::LogLevel NewLogLevel);
+level::LogLevel GetLogLevel();
+
+} // namespace zen::logging
+
+namespace spdlog {
+class logger;
+}
+
+namespace zen::logging {
+
+struct SourceLocation
+{
+ constexpr SourceLocation() = default;
+ constexpr SourceLocation(const char* filename_in, int line_in, const char* funcname_in)
+ : filename(filename_in)
+ , line(line_in)
+ , funcname(funcname_in)
+ {
+ }
+
+ constexpr bool empty() const noexcept { return line == 0; }
+
+ // IMPORTANT NOTE: the layout of this class must match the spdlog::source_loc class
+ // since we currently pass a pointer to it into spdlog after casting it to
+ // spdlog::source_loc*
+ //
+ // This is intended to be an intermediate state, before we (probably) transition off
+ // spdlog entirely
+
+ const char* filename{nullptr};
+ int line{0};
+ const char* funcname{nullptr};
+};
+
+} // namespace zen::logging
+
+namespace zen {
+
+struct LoggerRef
+{
+ LoggerRef() = default;
+ LoggerRef(spdlog::logger& InLogger) : SpdLogger(&InLogger) {}
+
+ LoggerRef Logger() { return *this; }
+
+ bool ShouldLog(int Level) const;
+ inline operator bool() const { return SpdLogger != nullptr; }
+
+ spdlog::logger* SpdLogger = nullptr;
+};
+
+} // namespace zen
diff --git a/src/zencore/include/zencore/logging.h b/src/zencore/include/zencore/logging.h
index c0d966475..e3eb524e9 100644
--- a/src/zencore/include/zencore/logging.h
+++ b/src/zencore/include/zencore/logging.h
@@ -2,138 +2,179 @@
#pragma once
+#include <zencore/logbase.h>
#include <zencore/zencore.h>
-ZEN_THIRD_PARTY_INCLUDES_START
-#include <spdlog/spdlog.h>
-#undef GetObject
-ZEN_THIRD_PARTY_INCLUDES_END
-
-#include <string_view>
+#include <fmt/args.h>
+
+#if ZEN_PLATFORM_WINDOWS
+# define ZEN_LOG_SECTION(Id) ZEN_DATA_SECTION(Id)
+# pragma section(".zlog$f", read)
+# pragma section(".zlog$l", read)
+# pragma section(".zlog$m", read)
+# pragma section(".zlog$s", read)
+# define ZEN_DECLARE_FUNCTION static constinit ZEN_LOG_SECTION(".zlog$f") char FuncName[] = __FUNCTION__;
+# define ZEN_LOG_FUNCNAME FuncName
+#else
+# define ZEN_LOG_SECTION(Id)
+# define ZEN_DECLARE_FUNCTION
+# define ZEN_LOG_FUNCNAME static_cast<const char*>(__func__)
+#endif
namespace zen::logging {
-spdlog::logger& Default();
-void SetDefault(std::shared_ptr<spdlog::logger> NewDefaultLogger);
-spdlog::logger& ConsoleLog();
-void SuppressConsoleLog();
-spdlog::logger& Get(std::string_view Name);
-spdlog::logger* ErrorLog();
-void SetErrorLog(std::shared_ptr<spdlog::logger>&& NewErrorLogger);
-
void InitializeLogging();
void ShutdownLogging();
-
bool EnableVTMode();
+LoggerRef Default();
+void SetDefault(std::string_view NewDefaultLoggerId);
+LoggerRef ConsoleLog();
+void SuppressConsoleLog();
+LoggerRef ErrorLog();
+void SetErrorLog(std::string_view LoggerId);
+LoggerRef Get(std::string_view Name);
+
+struct LogCategory
+{
+ inline LogCategory(std::string_view InCategory) : CategoryName(InCategory) {}
+
+ inline zen::LoggerRef Logger()
+ {
+ if (LoggerRef)
+ {
+ return LoggerRef;
+ }
+
+ LoggerRef = zen::logging::Get(CategoryName);
+ return LoggerRef;
+ }
+
+ std::string CategoryName;
+ zen::LoggerRef LoggerRef;
+};
+
+void EmitConsoleLogMessage(int LogLevel, std::string_view Message);
+void EmitConsoleLogMessage(int LogLevel, std::string_view Format, fmt::format_args Args);
+void EmitLogMessage(LoggerRef& Logger, int LogLevel, std::string_view Message);
+void EmitLogMessage(LoggerRef& Logger, const SourceLocation& Location, int LogLevel, std::string_view Message);
+void EmitLogMessage(LoggerRef& Logger, int LogLevel, std::string_view Format, fmt::format_args Args);
+void EmitLogMessage(LoggerRef& Logger, const SourceLocation& Location, int LogLevel, std::string_view Format, fmt::format_args Args);
+
} // namespace zen::logging
namespace zen {
-extern spdlog::logger* TheDefaultLogger;
-inline spdlog::logger&
+extern LoggerRef TheDefaultLogger;
+
+inline LoggerRef
Log()
{
- if (TheDefaultLogger == nullptr)
+ if (TheDefaultLogger)
{
- return zen::logging::ConsoleLog();
+ return TheDefaultLogger;
}
- return *TheDefaultLogger;
+ return zen::logging::ConsoleLog();
}
using logging::ConsoleLog;
using logging::ErrorLog;
+
} // namespace zen
using zen::ConsoleLog;
using zen::ErrorLog;
using zen::Log;
-struct LogCategory
-{
- LogCategory(std::string_view InCategory) : Category(InCategory) {}
-
- spdlog::logger& Logger()
- {
- static spdlog::logger& Inst = zen::logging::Get(Category);
- return Inst;
- }
-
- std::string Category;
-};
-
inline consteval bool
-LogIsErrorLevel(int level)
+LogIsErrorLevel(int LogLevel)
{
- return (level == spdlog::level::err || level == spdlog::level::critical);
+ return (LogLevel == zen::logging::level::Err || LogLevel == zen::logging::level::Critical);
};
-#define ZEN_LOG_WITH_LOCATION(logger, loc, level, fmtstr, ...) \
- do \
- { \
- using namespace std::literals; \
- if (logger.should_log(level)) \
- { \
- logger.log(loc, level, fmtstr, ##__VA_ARGS__); \
- if (LogIsErrorLevel(level)) \
- { \
- if (auto ErrLogger = zen::logging::ErrorLog(); ErrLogger != nullptr) \
- { \
- ErrLogger->log(loc, level, fmtstr, ##__VA_ARGS__); \
- } \
- } \
- } \
+#if ZEN_BUILD_DEBUG
+# define ZEN_CHECK_FORMAT_STRING(fmtstr, ...) \
+ while (false) \
+ { \
+ (void)fmt::format(fmtstr, ##__VA_ARGS__); \
+ }
+#else
+# define ZEN_CHECK_FORMAT_STRING(fmtstr, ...) \
+ while (false) \
+ { \
+ }
+#endif
+
+#define ZEN_LOG_WITH_LOCATION(InLogger, InLevel, fmtstr, ...) \
+ do \
+ { \
+ using namespace std::literals; \
+ ZEN_DECLARE_FUNCTION \
+ static constinit ZEN_LOG_SECTION(".zlog$s") char FileName[] = __FILE__; \
+ static constinit ZEN_LOG_SECTION(".zlog$m") char FormatString[] = fmtstr; \
+ static constinit ZEN_LOG_SECTION(".zlog$l") zen::logging::SourceLocation Location{FileName, __LINE__, ZEN_LOG_FUNCNAME}; \
+ zen::LoggerRef Logger = InLogger; \
+ ZEN_CHECK_FORMAT_STRING(fmtstr##sv, ##__VA_ARGS__); \
+ if (Logger.ShouldLog(InLevel)) \
+ { \
+ zen::logging::EmitLogMessage(Logger, \
+ Location, \
+ InLevel, \
+ std::string_view(FormatString, sizeof FormatString - 1), \
+ fmt::make_format_args(__VA_ARGS__)); \
+ } \
} while (false);
-#define ZEN_LOG(logger, level, fmtstr, ...) ZEN_LOG_WITH_LOCATION(logger, spdlog::source_loc{}, level, fmtstr, ##__VA_ARGS__)
-
-#define ZEN_DEFINE_LOG_CATEGORY_STATIC(Category, Name) \
- static struct LogCategory##Category : public LogCategory \
- { \
- LogCategory##Category() : LogCategory(Name) {} \
- } Category;
-
-#define ZEN_LOG_TRACE(Category, fmtstr, ...) ZEN_LOG(Category.Logger(), spdlog::level::trace, fmtstr##sv, ##__VA_ARGS__)
-#define ZEN_LOG_DEBUG(Category, fmtstr, ...) ZEN_LOG(Category.Logger(), spdlog::level::debug, fmtstr##sv, ##__VA_ARGS__)
-#define ZEN_LOG_INFO(Category, fmtstr, ...) ZEN_LOG(Category.Logger(), spdlog::level::info, fmtstr##sv, ##__VA_ARGS__)
-#define ZEN_LOG_WARN(Category, fmtstr, ...) ZEN_LOG(Category.Logger(), spdlog::level::warn, fmtstr##sv, ##__VA_ARGS__)
-#define ZEN_LOG_ERROR(Category, fmtstr, ...) \
- ZEN_LOG_WITH_LOCATION(Category.Logger(), \
- spdlog::source_loc(__FILE__, __LINE__, SPDLOG_FUNCTION), \
- spdlog::level::err, \
- fmtstr##sv, \
- ##__VA_ARGS__)
-
-#define ZEN_LOG_CRITICAL(Category, fmtstr, ...) \
- ZEN_LOG_WITH_LOCATION(Category.Logger(), \
- spdlog::source_loc(__FILE__, __LINE__, SPDLOG_FUNCTION), \
- spdlog::level::critical, \
- fmtstr##sv, \
- ##__VA_ARGS__)
-
- // Helper macros for logging
-
-#define ZEN_TRACE(fmtstr, ...) ZEN_LOG(Log(), spdlog::level::trace, fmtstr##sv, ##__VA_ARGS__)
-#define ZEN_DEBUG(fmtstr, ...) ZEN_LOG(Log(), spdlog::level::debug, fmtstr##sv, ##__VA_ARGS__)
-#define ZEN_INFO(fmtstr, ...) ZEN_LOG(Log(), spdlog::level::info, fmtstr##sv, ##__VA_ARGS__)
-#define ZEN_WARN(fmtstr, ...) ZEN_LOG(Log(), spdlog::level::warn, fmtstr##sv, ##__VA_ARGS__)
-#define ZEN_ERROR(fmtstr, ...) \
- ZEN_LOG_WITH_LOCATION(Log(), spdlog::source_loc(__FILE__, __LINE__, SPDLOG_FUNCTION), spdlog::level::err, fmtstr##sv, ##__VA_ARGS__)
-
-#define ZEN_CRITICAL(fmtstr, ...) \
- ZEN_LOG_WITH_LOCATION(Log(), \
- spdlog::source_loc(__FILE__, __LINE__, SPDLOG_FUNCTION), \
- spdlog::level::critical, \
- fmtstr##sv, \
- ##__VA_ARGS__)
-
-#define ZEN_CONSOLE(fmtstr, ...) \
- do \
- { \
- using namespace std::literals; \
- ConsoleLog().info(fmtstr##sv, ##__VA_ARGS__); \
+#define ZEN_LOG(InLogger, InLevel, fmtstr, ...) \
+ do \
+ { \
+ using namespace std::literals; \
+ static constinit ZEN_LOG_SECTION(".zlog$m") char FormatString[] = fmtstr; \
+ zen::LoggerRef Logger = InLogger; \
+ ZEN_CHECK_FORMAT_STRING(fmtstr##sv, ##__VA_ARGS__); \
+ if (Logger.ShouldLog(InLevel)) \
+ { \
+ zen::logging::EmitLogMessage(Logger, \
+ InLevel, \
+ std::string_view(FormatString, sizeof FormatString - 1), \
+ fmt::make_format_args(__VA_ARGS__)); \
+ } \
+ } while (false);
+
+#define ZEN_DEFINE_LOG_CATEGORY_STATIC(Category, Name) \
+ static zen::logging::LogCategory Category { Name }
+
+#define ZEN_LOG_TRACE(Category, fmtstr, ...) ZEN_LOG(Category.Logger(), zen::logging::level::Trace, fmtstr, ##__VA_ARGS__)
+#define ZEN_LOG_DEBUG(Category, fmtstr, ...) ZEN_LOG(Category.Logger(), zen::logging::level::Debug, fmtstr, ##__VA_ARGS__)
+#define ZEN_LOG_INFO(Category, fmtstr, ...) ZEN_LOG(Category.Logger(), zen::logging::level::Info, fmtstr, ##__VA_ARGS__)
+#define ZEN_LOG_WARN(Category, fmtstr, ...) ZEN_LOG(Category.Logger(), zen::logging::level::Warn, fmtstr, ##__VA_ARGS__)
+#define ZEN_LOG_ERROR(Category, fmtstr, ...) ZEN_LOG_WITH_LOCATION(Category.Logger(), zen::logging::level::Err, fmtstr, ##__VA_ARGS__)
+#define ZEN_LOG_CRITICAL(Category, fmtstr, ...) \
+ ZEN_LOG_WITH_LOCATION(Category.Logger(), zen::logging::level::Critical, fmtstr, ##__VA_ARGS__)
+
+#define ZEN_TRACE(fmtstr, ...) ZEN_LOG(Log(), zen::logging::level::Trace, fmtstr, ##__VA_ARGS__)
+#define ZEN_DEBUG(fmtstr, ...) ZEN_LOG(Log(), zen::logging::level::Debug, fmtstr, ##__VA_ARGS__)
+#define ZEN_INFO(fmtstr, ...) ZEN_LOG(Log(), zen::logging::level::Info, fmtstr, ##__VA_ARGS__)
+#define ZEN_WARN(fmtstr, ...) ZEN_LOG(Log(), zen::logging::level::Warn, fmtstr, ##__VA_ARGS__)
+#define ZEN_ERROR(fmtstr, ...) ZEN_LOG_WITH_LOCATION(Log(), zen::logging::level::Err, fmtstr, ##__VA_ARGS__)
+#define ZEN_CRITICAL(fmtstr, ...) ZEN_LOG_WITH_LOCATION(Log(), zen::logging::level::Critical, fmtstr, ##__VA_ARGS__)
+
+#define ZEN_CONSOLE_LOG(InLevel, fmtstr, ...) \
+ do \
+ { \
+ using namespace std::literals; \
+ ZEN_CHECK_FORMAT_STRING(fmtstr##sv, ##__VA_ARGS__); \
+ zen::logging::EmitConsoleLogMessage(InLevel, fmtstr, fmt::make_format_args(__VA_ARGS__)); \
} while (false)
+#define ZEN_CONSOLE(fmtstr, ...) ZEN_CONSOLE_LOG(zen::logging::level::Info, fmtstr, ##__VA_ARGS__)
+#define ZEN_CONSOLE_TRACE(fmtstr, ...) ZEN_CONSOLE_LOG(zen::logging::level::Trace, fmtstr, ##__VA_ARGS__)
+#define ZEN_CONSOLE_DEBUG(fmtstr, ...) ZEN_CONSOLE_LOG(zen::logging::level::Debug, fmtstr, ##__VA_ARGS__)
+#define ZEN_CONSOLE_INFO(fmtstr, ...) ZEN_CONSOLE_LOG(zen::logging::level::Info, fmtstr, ##__VA_ARGS__)
+#define ZEN_CONSOLE_WARN(fmtstr, ...) ZEN_CONSOLE_LOG(zen::logging::level::Warn, fmtstr, ##__VA_ARGS__)
+#define ZEN_CONSOLE_ERROR(fmtstr, ...) ZEN_CONSOLE_LOG(zen::logging::level::Err, fmtstr, ##__VA_ARGS__)
+#define ZEN_CONSOLE_CRITICAL(fmtstr, ...) ZEN_CONSOLE_LOG(zen::logging::level::Critical, fmtstr, ##__VA_ARGS__)
+
//////////////////////////////////////////////////////////////////////////
namespace zen {
@@ -187,30 +228,20 @@ std::string_view EmitActivitiesForLogging(StringBuilderBase& OutString);
#define ZEN_LOG_SCOPE(...) ScopedLazyActivity $Activity##__LINE__([&](StringBuilderBase& Out) { Out << fmt::format(__VA_ARGS__); })
-#define ZEN_SCOPED_ERROR(fmtstr, ...) \
- do \
- { \
- ExtendableStringBuilder<256> ScopeString; \
- const std::string_view Scopes = EmitActivitiesForLogging(ScopeString); \
- ZEN_LOG_WITH_LOCATION(Log(), \
- spdlog::source_loc(__FILE__, __LINE__, SPDLOG_FUNCTION), \
- spdlog::level::err, \
- fmtstr "{}"sv, \
- ##__VA_ARGS__, \
- Scopes); \
+#define ZEN_SCOPED_ERROR(fmtstr, ...) \
+ do \
+ { \
+ ExtendableStringBuilder<256> ScopeString; \
+ const std::string_view Scopes = EmitActivitiesForLogging(ScopeString); \
+ ZEN_LOG_WITH_LOCATION(Log(), zen::logging::level::Err, fmtstr "{}", ##__VA_ARGS__, Scopes); \
} while (false)
-#define ZEN_SCOPED_CRITICAL(fmtstr, ...) \
- do \
- { \
- ExtendableStringBuilder<256> ScopeString; \
- const std::string_view Scopes = EmitActivitiesForLogging(ScopeString); \
- ZEN_LOG_WITH_LOCATION(Log(), \
- spdlog::source_loc(__FILE__, __LINE__, SPDLOG_FUNCTION), \
- spdlog::level::critical, \
- fmtstr "{}"sv, \
- ##__VA_ARGS__, \
- Scopes); \
+#define ZEN_SCOPED_CRITICAL(fmtstr, ...) \
+ do \
+ { \
+ ExtendableStringBuilder<256> ScopeString; \
+ const std::string_view Scopes = EmitActivitiesForLogging(ScopeString); \
+ ZEN_LOG_WITH_LOCATION(Log(), zen::logging::level::Critical, fmtstr "{}", ##__VA_ARGS__, Scopes); \
} while (false)
ScopedActivityBase* GetThreadActivity();
diff --git a/src/zencore/include/zencore/string.h b/src/zencore/include/zencore/string.h
index d0bd38acc..e3de2224c 100644
--- a/src/zencore/include/zencore/string.h
+++ b/src/zencore/include/zencore/string.h
@@ -17,10 +17,6 @@
#include <type_traits>
-ZEN_THIRD_PARTY_INCLUDES_START
-#include <fmt/format.h>
-ZEN_THIRD_PARTY_INCLUDES_END
-
namespace zen {
//////////////////////////////////////////////////////////////////////////
@@ -1141,21 +1137,3 @@ private:
void string_forcelink(); // internal
} // namespace zen
-
-template<typename T>
-struct fmt::formatter<T, std::enable_if_t<std::is_base_of<zen::StringBuilderBase, T>::value, char>> : fmt::formatter<std::string_view>
-{
- auto format(const zen::StringBuilderBase& a, format_context& ctx) const
- {
- return fmt::formatter<std::string_view>::format(a.ToView(), ctx);
- }
-};
-
-template<typename T>
-struct fmt::formatter<T, std::enable_if_t<std::is_base_of<zen::NiceBase, T>::value, char>> : fmt::formatter<std::string_view>
-{
- auto format(const zen::NiceBase& a, format_context& ctx) const
- {
- return fmt::formatter<std::string_view>::format(std::string_view(a), ctx);
- }
-};
diff --git a/src/zencore/include/zencore/zencore.h b/src/zencore/include/zencore/zencore.h
index 299fa2c31..562376c95 100644
--- a/src/zencore/include/zencore/zencore.h
+++ b/src/zencore/include/zencore/zencore.h
@@ -229,10 +229,12 @@ static_assert(sizeof(wchar_t) == 2, "wchar_t is expected to be two bytes in size
#if ZEN_PLATFORM_WINDOWS
// Tells the compiler to put the decorated function in a certain section (aka. segment) of the executable.
# define ZEN_CODE_SECTION(Name) __declspec(code_seg(Name))
+# define ZEN_DATA_SECTION(Name) __declspec(allocate(Name))
# define ZEN_FORCENOINLINE __declspec(noinline) /* Force code to NOT be inline */
# define LINE_TERMINATOR_ANSI "\r\n"
#else
# define ZEN_CODE_SECTION(Name)
+# define ZEN_DATA_SECTION(Name)
# define ZEN_FORCENOINLINE
# define LINE_TERMINATOR_ANSI "\n"
#endif
diff --git a/src/zencore/logging.cpp b/src/zencore/logging.cpp
index c366df812..0d34372a9 100644
--- a/src/zencore/logging.cpp
+++ b/src/zencore/logging.cpp
@@ -7,39 +7,239 @@
#include <spdlog/sinks/null_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
+#include <spdlog/spdlog.h>
+
+#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$z", read)
+#endif
namespace zen {
// We shadow the underlying spdlog default logger, in order to avoid a bunch of overhead
-spdlog::logger* TheDefaultLogger;
+LoggerRef TheDefaultLogger;
} // namespace zen
namespace zen::logging {
-spdlog::logger&
+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()
+{
+}
+
+LoggingContext::~LoggingContext()
+{
+}
+
+static inline bool
+IsErrorLevel(int LogLevel)
+{
+ return (LogLevel == zen::logging::level::Err || LogLevel == zen::logging::level::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)
+{
+ 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::logging::LoggingContext LogCtx;
+ fmt::vformat_to(fmt::appender(LogCtx.MessageBuffer), Format, Args);
+ zen::logging::EmitLogMessage(Logger, LogLevel, LogCtx.Message());
+}
+
+void
+EmitLogMessage(LoggerRef& Logger, const SourceLocation& InLocation, int LogLevel, const std::string_view Message)
+{
+ 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 (LoggerRef ErrLogger = zen::logging::ErrorLog())
+ {
+ ErrLogger.SpdLogger->log(Location, InLevel, Message);
+ }
+ }
+}
+
+void
+EmitLogMessage(LoggerRef& Logger, const SourceLocation& InLocation, int LogLevel, std::string_view Format, fmt::format_args Args)
+{
+ 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)
+{
+ const spdlog::level::level_enum InLevel = (spdlog::level::level_enum)LogLevel;
+ ConsoleLog().SpdLogger->log(InLevel, Message);
+}
+
+void
+EmitConsoleLogMessage(int LogLevel, std::string_view Format, fmt::format_args Args)
+{
+ zen::logging::LoggingContext LogCtx;
+ fmt::vformat_to(fmt::appender(LogCtx.MessageBuffer), Format, Args);
+ zen::logging::EmitConsoleLogMessage(LogLevel, LogCtx.Message());
+}
+
+} // namespace zen::logging
+
+namespace zen::logging::level {
+
+spdlog::level::level_enum
+to_spdlog_level(LogLevel NewLogLevel)
+{
+ return static_cast<spdlog::level::level_enum>((int)NewLogLevel);
+}
+
+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)
+ {
+ if (LevelNames[Level] == Name)
+ return static_cast<level::LogLevel>(Level);
+ }
+
+ if (Name == "warn")
+ {
+ return level::Warn;
+ }
+
+ if (Name == "err")
+ {
+ return level::Err;
+ }
+
+ return level::Off;
+}
+
+std::string_view
+ToStringView(level::LogLevel Level)
+{
+ if (int(Level) < level::LogLevelCount)
+ {
+ return LevelNames[int(Level)];
+ }
+
+ return "None";
+}
+
+} // namespace zen::logging::level
+
+namespace zen::logging {
+
+void
+SetLogLevel(level::LogLevel NewLogLevel)
+{
+ spdlog::set_level(to_spdlog_level(NewLogLevel));
+}
+
+level::LogLevel
+GetLogLevel()
+{
+ return level::to_logging_level(spdlog::get_level());
+}
+
+LoggerRef
Default()
{
ZEN_ASSERT(TheDefaultLogger);
- return *TheDefaultLogger;
+ return TheDefaultLogger;
}
void
-SetDefault(std::shared_ptr<spdlog::logger> NewDefaultLogger)
+SetDefault(std::string_view NewDefaultLoggerId)
{
+ auto NewDefaultLogger = spdlog::get(std::string(NewDefaultLoggerId));
ZEN_ASSERT(NewDefaultLogger);
+
spdlog::set_default_logger(NewDefaultLogger);
- TheDefaultLogger = spdlog::default_logger_raw();
+ TheDefaultLogger = LoggerRef(*NewDefaultLogger);
+}
+
+LoggerRef TheErrorLogger;
+
+LoggerRef
+ErrorLog()
+{
+ return TheErrorLogger;
}
-spdlog::logger&
+void
+SetErrorLog(std::string_view NewErrorLoggerId)
+{
+ if (NewErrorLoggerId.empty())
+ {
+ TheErrorLogger = {};
+ }
+ else
+ {
+ auto NewErrorLogger = spdlog::get(std::string(NewErrorLoggerId));
+
+ ZEN_ASSERT(NewErrorLogger);
+
+ TheErrorLogger = LoggerRef(*NewErrorLogger.get());
+ }
+}
+
+LoggerRef
Get(std::string_view Name)
{
std::shared_ptr<spdlog::logger> Logger = spdlog::get(std::string(Name));
if (!Logger)
{
- Logger = Default().clone(std::string(Name));
+ Logger = Default().SpdLogger->clone(std::string(Name));
spdlog::register_logger(Logger);
}
@@ -55,7 +255,7 @@ SuppressConsoleLog()
ConLogger = spdlog::null_logger_mt("console");
}
-spdlog::logger&
+LoggerRef
ConsoleLog()
{
std::call_once(ConsoleInitFlag, [&] {
@@ -70,25 +270,10 @@ ConsoleLog()
return *ConLogger;
}
-std::shared_ptr<spdlog::logger> TheErrorLogger;
-
-spdlog::logger*
-ErrorLog()
-{
- // This may return nullptr
- return TheErrorLogger.get();
-}
-
-void
-SetErrorLog(std::shared_ptr<spdlog::logger>&& NewErrorLogger)
-{
- TheErrorLogger = std::move(NewErrorLogger);
-}
-
void
InitializeLogging()
{
- TheDefaultLogger = spdlog::default_logger_raw();
+ TheDefaultLogger = *spdlog::default_logger_raw();
}
void
@@ -96,7 +281,7 @@ ShutdownLogging()
{
spdlog::drop_all();
spdlog::shutdown();
- TheDefaultLogger = nullptr;
+ TheDefaultLogger = {};
}
bool
@@ -130,6 +315,12 @@ EnableVTMode()
namespace zen {
+bool
+LoggerRef::ShouldLog(int Level) const
+{
+ return SpdLogger->should_log(static_cast<spdlog::level::level_enum>(Level));
+}
+
thread_local ScopedActivityBase* t_ScopeStack = nullptr;
ScopedActivityBase*
diff --git a/src/zencore/testing.cpp b/src/zencore/testing.cpp
index 6e1f55eda..54d89ded2 100644
--- a/src/zencore/testing.cpp
+++ b/src/zencore/testing.cpp
@@ -32,11 +32,11 @@ TestRunner::ApplyCommandLine(int argc, char const* const* argv)
{
if (argv[i] == "--debug"sv)
{
- spdlog::set_level(spdlog::level::debug);
+ zen::logging::SetLogLevel(zen::logging::level::Debug);
}
else if (argv[i] == "--verbose"sv)
{
- spdlog::set_level(spdlog::level::trace);
+ zen::logging::SetLogLevel(zen::logging::level::Trace);
}
}