/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #include "Math/ByteSize.hpp" #include "Logger.hpp" #include "IO/AppFolders.hpp" #include "Utils.hpp" #include #include #include #include #include #include #include #include #ifndef NO_CONSOLE_LOG #include #endif #ifdef _MSC_VER #include #endif #ifdef _WIN32 # include # include #endif namespace OpenVulkano { namespace { constexpr size_t MAX_LOG_FILE_SIZE = 512_MiB; constexpr size_t MAX_LOG_FILE_COUNT = 6; class CustomThreadNameFormatterFlag : public spdlog::custom_flag_formatter { public: void format(const spdlog::details::log_msg &msg, const std::tm &tmTime, spdlog::memory_buf_t &dest) override { std::string threadName = OpenVulkano::Utils::GetThreadName(); if(threadName.empty()) { uint64_t threadId = OpenVulkano::Utils::GetThreadId(); threadName = fmt::format("{}", threadId); } dest.append(threadName.c_str(), threadName.c_str() + threadName.length()); } std::unique_ptr clone() const override { return spdlog::details::make_unique(); } }; } std::vector Logger::SINKS; std::vector> Logger::LOGGERS; Logger::Ptr Logger::WINDOW = nullptr; Logger::Ptr Logger::MANAGER = nullptr; Logger::Ptr Logger::RENDER = nullptr; Logger::Ptr Logger::PHYSIC = nullptr; Logger::Ptr Logger::AUDIO = nullptr; Logger::Ptr Logger::DATA = nullptr; Logger::Ptr Logger::SCENE = nullptr; Logger::Ptr Logger::INPUT = nullptr; Logger::Ptr Logger::FILESYS = nullptr; Logger::Ptr Logger::AR = nullptr; Logger::Ptr Logger::PERF = nullptr; Logger::Ptr Logger::APP = nullptr; namespace { bool DidLoggerShutdown = false; void SignalHandler(int sigCode) { if(!DidLoggerShutdown) spdlog::error("Received signal {}", sigCode); spdlog::shutdown(); DidLoggerShutdown = true; std::raise(sigCode); // calls default handler } #ifdef _WIN32 LONG WINAPI win32ExceptionHandler(EXCEPTION_POINTERS *exceptionPointers) { // NOTE(vb): We can report some of the exceptionPointers fields to the user... But this is for windows only if(!DidLoggerShutdown) spdlog::error("An exception occured"); spdlog::shutdown(); DidLoggerShutdown = true; return EXCEPTION_CONTINUE_SEARCH; } #endif void SetupSignalHandlers() { std::signal(SIGSEGV, SignalHandler); std::signal(SIGABRT, SignalHandler); std::signal(SIGFPE, SignalHandler); std::signal(SIGILL, SignalHandler); // std::signal(SIGINT, SignalHandler); std::signal(SIGTERM, SignalHandler); #ifdef _WIN32 SetUnhandledExceptionFilter(win32ExceptionHandler); #endif } } void Logger::SetupLogger(std::filesystem::path logFolder, const std::string& logFile, bool handleSignals) { if (logFolder.empty()) logFolder = AppFolders::GetAppDataHomeDir() / "logs"; if (!SINKS.empty()) return; try { try { SINKS.push_back(std::make_shared((logFolder / logFile).string(), MAX_LOG_FILE_SIZE, MAX_LOG_FILE_COUNT, true)); } catch (const spdlog::spdlog_ex& e) { std::cerr << "Failed to create file log sink: " << e.what() << std::endl; } #ifndef NO_CONSOLE_LOG SINKS.push_back(std::make_shared()); #endif #ifdef _MSC_VER // If it was build with msvc in debug we can use the msvc sink SINKS.push_back(std::make_shared()); #endif // Make sure that there is always a sink for the loggers if (SINKS.empty()) SINKS.push_back(std::make_shared()); MANAGER = CreateLogger("manager"); WINDOW = CreateLogger("window"); RENDER = CreateLogger("render"); PHYSIC = CreateLogger("physic"); AUDIO = CreateLogger("audio"); DATA = CreateLogger("data"); SCENE = CreateLogger("scene"); INPUT = CreateLogger("input"); FILESYS = CreateLogger("filesys"); AR = CreateLogger("ar"); PERF = CreateLogger("perf"); APP = CreateLogger("app"); spdlog::set_default_logger(APP); spdlog::flush_every(std::chrono::seconds(5)); if (handleSignals) SetupSignalHandlers(); MANAGER->info("Logger initialized"); } catch (const spdlog::spdlog_ex& e) { std::cerr << "Log initialization failed: " << e.what() << std::endl; } } Logger::Ptr Logger::CreateLogger(const std::string& name, const bool reg) { if (reg) { auto logger = spdlog::get(name); if (logger) return logger; } auto logger = std::make_shared(name, SINKS.begin(), SINKS.end()); if (reg) spdlog::register_logger(logger); auto formatter = std::make_unique(); formatter->add_flag('t'); // overloading t #ifdef LOG_DATE formatter->set_pattern("[%Y-%m-%d %H:%M:%S.%e] [T:%t] [%^%l%$] [%n]: %v"); #else formatter->set_pattern("[%H:%M:%S.%e] [T:%t] [%^%l%$] [%n]: %v"); #endif #ifdef DEBUG logger->set_level(spdlog::level::debug); #else logger->set_level(spdlog::level::info); #endif logger->set_formatter(std::move(formatter)); logger->flush_on(spdlog::level::err); // Flush on error LOGGERS.push_back(logger); return logger; } Logger::Ptr Logger::GetLogger(const std::string& name) { return spdlog::get(name); } void Logger::RegisterSink(const spdlog::sink_ptr& newSink) { auto iter = LOGGERS.begin(); while(iter != LOGGERS.end()) { if (const auto logger = (*iter).lock()) { logger->sinks().push_back(newSink); } else { iter = LOGGERS.erase(iter); } } SINKS.push_back(newSink); } }