// Copyright Epic Games, Inc. All Rights Reserved. #include "zencore/logging.h" #include #include #include namespace zen { // We shadow the underlying spdlog default logger, in order to avoid a bunch of overhead spdlog::logger* TheDefaultLogger; } // namespace zen namespace zen::logging { spdlog::logger& Default() { return *TheDefaultLogger; } void SetDefault(std::shared_ptr NewDefaultLogger) { spdlog::set_default_logger(NewDefaultLogger); TheDefaultLogger = spdlog::default_logger_raw(); } spdlog::logger& Get(std::string_view Name) { std::shared_ptr Logger = spdlog::get(std::string(Name)); if (!Logger) { Logger = Default().clone(std::string(Name)); spdlog::register_logger(Logger); } return *Logger; } std::once_flag ConsoleInitFlag; std::shared_ptr ConLogger; spdlog::logger& ConsoleLog() { std::call_once(ConsoleInitFlag, [&] { ConLogger = spdlog::stdout_color_mt("console"); ConLogger->set_pattern("%v"); }); return *ConLogger; } std::shared_ptr TheErrorLogger; spdlog::logger* ErrorLog() { return TheErrorLogger.get(); } void SetErrorLog(std::shared_ptr&& NewErrorLogger) { TheErrorLogger = std::move(NewErrorLogger); } void InitializeLogging() { TheDefaultLogger = spdlog::default_logger_raw(); } void ShutdownLogging() { spdlog::drop_all(); spdlog::shutdown(); } bool EnableVTMode() { #if ZEN_PLATFORM_WINDOWS // Set output mode to handle virtual terminal sequences HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE); if (hOut == INVALID_HANDLE_VALUE) { return false; } DWORD dwMode = 0; if (!GetConsoleMode(hOut, &dwMode)) { return false; } dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; if (!SetConsoleMode(hOut, dwMode)) { return false; } #endif return true; } } // namespace zen::logging namespace zen { thread_local ScopedActivityBase* t_ScopeStack = nullptr; ScopedActivityBase* GetThreadActivity() { return t_ScopeStack; } ScopedActivityBase::ScopedActivityBase() : m_NextScope{t_ScopeStack} { t_ScopeStack = this; } ScopedActivityBase::~ScopedActivityBase() { if (t_ScopeStack != this) { ZEN_ERROR("invalid t_ScopeStack in ~ScopedActivityBase(). Expected {:#x}, found {:#x}", (uintptr_t)this, (uintptr_t)t_ScopeStack); return; } t_ScopeStack = m_NextScope; } std::string_view EmitActivitiesForLogging(StringBuilderBase& OutString) { OutString.Reset(); for (auto Bread = GetThreadActivity(); Bread; Bread = Bread->GetNext()) { OutString.Append("\n|>|>|> "); Bread->Emit(OutString); } return OutString.ToView(); } ////////////////////////////////////////////////////////////////////////// ScopedActivityString::~ScopedActivityString() { } void ScopedActivityString::Emit(StringBuilderBase& Target) { Target.Append(m_Text); } #if ZEN_WITH_TESTS void logging_forcelink() { } using namespace std::literals; TEST_CASE("simple.bread") { ExtendableStringBuilder<256> Crumbs; auto EmitBreadcrumbs = [&] { Crumbs.Reset(); for (auto Bread = GetThreadActivity(); Bread; Bread = Bread->GetNext()) { Bread->Emit(Crumbs); } return Crumbs.ToView(); }; SUBCASE("single") { ScopedActivityString _{"hello"}; EmitBreadcrumbs(); CHECK_EQ(Crumbs.ToView(), "hello"sv); } SUBCASE("multi") { ScopedActivityString $1{"hello"}; ScopedActivityString $2{"world"}; ScopedActivityString $3{"amaze"}; CHECK_EQ(EmitBreadcrumbs(), "amazeworldhello"sv); } SUBCASE("multi_defer") { int n = 42; ScopedActivityString $1{"hello"}; ScopedActivityString $2{"world"}; ScopedActivityString $3{"amaze"}; ScopedLazyActivity $4{[&](StringBuilderBase& Out) { Out << "plant"; }}; { ScopedLazyActivity $5{[&](StringBuilderBase& Out) { Out << n; }}; ZEN_LOG_SCOPE("{}:{}", "abc", "def"); CHECK_EQ(EmitBreadcrumbs(), "abc:def42plantamazeworldhello"sv); } CHECK_EQ(EmitBreadcrumbs(), "plantamazeworldhello"sv); } } #endif } // namespace zen