// Copyright Epic Games, Inc. All Rights Reserved. #include "zenutil/logging.h" ZEN_THIRD_PARTY_INCLUDES_START #include #include #include #include #include ZEN_THIRD_PARTY_INCLUDES_END #include #include #include #include #include #include #include #include namespace zen { spdlog::sink_ptr g_FileSink; spdlog::sink_ptr GetFileSink() { return g_FileSink; } void InitializeLogging(const LoggingOptions& LogOptions) { BeginInitializeLogging(LogOptions); FinishInitializeLogging(LogOptions); } void BeginInitializeLogging(const LoggingOptions& LogOptions) { zen::logging::InitializeLogging(); zen::logging::EnableVTMode(); bool IsAsync = true; if (LogOptions.IsDebug) { IsAsync = false; } if (LogOptions.IsTest) { IsAsync = false; } if (IsAsync) { const int QueueSize = 8192; const int ThreadCount = 1; spdlog::init_thread_pool(QueueSize, ThreadCount, [&] { SetCurrentThreadName("spdlog_async"); }); auto AsyncSink = spdlog::create_async("main"); zen::logging::SetDefault("main"); } // Sinks spdlog::sink_ptr FileSink; // spdlog can't create directories that starts with `\\?\` so we make sure the folder exists before creating the logger instance if (!LogOptions.AbsLogFile.empty()) { if (LogOptions.AbsLogFile.has_parent_path()) { zen::CreateDirectories(LogOptions.AbsLogFile.parent_path()); } FileSink = std::make_shared(zen::PathToUtf8(LogOptions.AbsLogFile), /* max size */ 128 * 1024 * 1024, /* max files */ 16, /* rotate on open */ true); } std::set_terminate([]() { ZEN_CRITICAL("Program exited abnormally via std::terminate()"); }); // Default LoggerRef DefaultLogger = zen::logging::Default(); auto& Sinks = DefaultLogger.SpdLogger->sinks(); Sinks.clear(); if (LogOptions.NoConsoleOutput) { zen::logging::SuppressConsoleLog(); } else { auto ConsoleSink = std::make_shared(); Sinks.push_back(ConsoleSink); } if (FileSink) { Sinks.push_back(FileSink); } #if ZEN_PLATFORM_WINDOWS if (zen::IsDebuggerPresent() && LogOptions.IsDebug) { auto DebugSink = std::make_shared(); DebugSink->set_level(spdlog::level::debug); Sinks.push_back(DebugSink); } #endif spdlog::set_error_handler([](const std::string& msg) { if (msg == std::bad_alloc().what()) { // Don't report out of memory in spdlog as we usually log in response to errors which will cause another OOM crashing the // program return; } // Bypass zen logging wrapping to reduce potential other error sources if (auto ErrLogger = zen::logging::ErrorLog()) { try { ErrLogger.SpdLogger->log(spdlog::level::err, msg); } catch (const std::exception&) { // Just ignore any errors when in error handler } } try { Log().SpdLogger->error(msg); } catch (const std::exception&) { // Just ignore any errors when in error handler } }); g_FileSink = std::move(FileSink); } void FinishInitializeLogging(const LoggingOptions& LogOptions) { spdlog::level::level_enum LogLevel = spdlog::level::info; if (LogOptions.IsDebug) { LogLevel = spdlog::level::debug; } if (LogOptions.IsTest) { LogLevel = spdlog::level::trace; } // Configure all registered loggers according to settings spdlog::set_level(LogLevel); spdlog::flush_on(spdlog::level::err); spdlog::flush_every(std::chrono::seconds{2}); spdlog::set_formatter(std::make_unique(LogOptions.LogId, std::chrono::system_clock::now())); if (g_FileSink) { if (LogOptions.AbsLogFile.extension() == ".json") { g_FileSink->set_formatter(std::make_unique(LogOptions.LogId)); } else { g_FileSink->set_pattern("[%C-%m-%d.%e %T] [%n] [%l] %v"); } } const std::string StartLogTime = zen::DateTime::Now().ToIso8601(); spdlog::apply_all([&](auto Logger) { Logger->info("log starting at {}", StartLogTime); }); } void ShutdownLogging() { g_FileSink.reset(); auto DefaultLogger = zen::logging::Default(); ZEN_LOG_INFO(DefaultLogger, "log ending at {}", zen::DateTime::Now().ToIso8601()); zen::logging::ShutdownLogging(); } } // namespace zen