210 lines
5.9 KiB
C++
210 lines
5.9 KiB
C++
/*
|
|
* 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 <iostream>
|
|
#include <filesystem>
|
|
#include <csignal>
|
|
#include <inttypes.h>
|
|
#include <spdlog/sinks/rotating_file_sink.h>
|
|
#include <spdlog/sinks/null_sink.h>
|
|
#include <spdlog/sinks/stdout_color_sinks.h>
|
|
#include <spdlog/pattern_formatter.h>
|
|
#ifndef NO_CONSOLE_LOG
|
|
#include <spdlog/sinks/stdout_color_sinks.h>
|
|
#endif
|
|
#ifdef _MSC_VER
|
|
#include <spdlog/sinks/msvc_sink.h>
|
|
#endif
|
|
#ifdef _WIN32
|
|
# include <windows.h>
|
|
# include <dbghelp.h>
|
|
#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<spdlog::custom_flag_formatter> clone() const override
|
|
{
|
|
return spdlog::details::make_unique<CustomThreadNameFormatterFlag>();
|
|
}
|
|
};
|
|
}
|
|
|
|
std::vector<spdlog::sink_ptr> Logger::SINKS;
|
|
std::vector<std::weak_ptr<spdlog::logger>> 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<spdlog::sinks::rotating_file_sink_mt>((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<spdlog::sinks::stdout_color_sink_mt>());
|
|
#endif
|
|
#ifdef _MSC_VER // If it was build with msvc in debug we can use the msvc sink
|
|
SINKS.push_back(std::make_shared<spdlog::sinks::msvc_sink_mt>());
|
|
#endif
|
|
// Make sure that there is always a sink for the loggers
|
|
if (SINKS.empty()) SINKS.push_back(std::make_shared<spdlog::sinks::null_sink_mt>());
|
|
|
|
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<spdlog::logger>(name, SINKS.begin(), SINKS.end());
|
|
if (reg) spdlog::register_logger(logger);
|
|
auto formatter = std::make_unique<spdlog::pattern_formatter>();
|
|
formatter->add_flag<CustomThreadNameFormatterFlag>('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);
|
|
}
|
|
}
|