shadPS4/src/common/logging/log.cpp
georgemoralis aff387e8a9
First ShadNet integration (#4607)
* initial shadnet client (based on my previous prs)

* some np_handler work mostly based on our previous pr

* improved login/logout event of shadnet

* added np_score support from previous pr

* friends and notifications ui

* clang is not my friend

* clang fix again

* added webapi support for shadnet

* applied metrik's fix

* clang

* fixed some wrong LOG_ERRORS

* updated emulator settings

* trying to fix npcommid bugs

* fixed npcommid

* improved NPcommid detection so it won't write null values to database

* metrik's suggestion

* removed one line

* more command types

* fixed duplicated log print

* fixed compile

* improved reconnection retries

* simplify error code

* stephen suggestion

* made friends hotkey

* fixup

* more proper handling of reconnecting

* fixed return code
2026-06-24 23:57:43 +03:00

320 lines
11 KiB
C++

// SPDX-FileCopyrightText: Copyright 2025-2026 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <cstdlib>
#include <iostream>
#include <string>
#include <fmt/std.h>
#include <spdlog/sinks/async_sink.h>
#include <spdlog/sinks/dup_filter_sink.h>
#ifdef _WIN32
#include <Windows.h>
#endif
#include "common/assert.h"
#include "common/logging/log.h"
#include "common/logging/log_file_sink.h"
#include "common/types.h"
#include "core/emulator_settings.h"
// return codes above 'standard'
// https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes
enum class ShadPs4ReturnCode : u32 {
TERMINATE_WITHOUT_EXCEPTION = 20'000,
TERMINATE_WITH_EXCEPTION = 20'001,
TERMINATE_WITH_UNKNOWN_EXCEPTION = 20'002,
};
namespace Common::Log {
bool g_should_append = false;
static std::shared_ptr<spdlog_stdout> g_console_sink;
static std::shared_ptr<LogFileSink> g_shad_file_sink;
std::unordered_map<std::string_view, std::shared_ptr<spdlog::logger>> ALL_LOGGERS{
{Class::Common, nullptr},
{Class::Common_Filesystem, nullptr},
{Class::Common_Memory, nullptr},
{Class::Config, nullptr},
{Class::Core, nullptr},
{Class::Core_Devices, nullptr},
{Class::Core_Linker, nullptr},
{Class::Debug, nullptr},
{Class::Frontend, nullptr},
{Class::IPC, nullptr},
{Class::ImGui, nullptr},
{Class::Input, nullptr},
{Class::Kernel, nullptr},
{Class::Kernel_Event, nullptr},
{Class::Kernel_Fs, nullptr},
{Class::Kernel_Pthread, nullptr},
{Class::Kernel_Sce, nullptr},
{Class::Kernel_Vmm, nullptr},
{Class::KeyManager, nullptr},
{Class::Lib, nullptr},
{Class::Lib_Ajm, nullptr},
{Class::Lib_AppContent, nullptr},
{Class::Lib_Audio3d, nullptr},
{Class::Lib_AudioIn, nullptr},
{Class::Lib_AudioOut, nullptr},
{Class::Lib_AvPlayer, nullptr},
{Class::Lib_Camera, nullptr},
{Class::Lib_CommonDlg, nullptr},
{Class::Lib_CompanionHttpd, nullptr},
{Class::Lib_CompanionUtil, nullptr},
{Class::Lib_ContentExport, nullptr},
{Class::Lib_DiscMap, nullptr},
{Class::Lib_ErrorDialog, nullptr},
{Class::Lib_Fiber, nullptr},
{Class::Lib_Font, nullptr},
{Class::Lib_FontFt, nullptr},
{Class::Lib_GameLiveStreaming, nullptr},
{Class::Lib_GnmDriver, nullptr},
{Class::Lib_Hmd, nullptr},
{Class::Lib_HmdSetupDialog, nullptr},
{Class::Lib_Http, nullptr},
{Class::Lib_Http2, nullptr},
{Class::Lib_Ime, nullptr},
{Class::Lib_ImeDialog, nullptr},
{Class::Lib_Jpeg, nullptr},
{Class::Lib_Kernel, nullptr},
{Class::Lib_LibcInternal, nullptr},
{Class::Lib_Mouse, nullptr},
{Class::Lib_Move, nullptr},
{Class::Lib_MsgDlg, nullptr},
{Class::Lib_Net, nullptr},
{Class::Lib_NetCtl, nullptr},
{Class::Lib_Ngs2, nullptr},
{Class::Lib_NpAuth, nullptr},
{Class::Lib_NpCommerce, nullptr},
{Class::Lib_NpCommon, nullptr},
{Class::Lib_NpManager, nullptr},
{Class::Lib_NpMatching2, nullptr},
{Class::Lib_NpSignaling, nullptr},
{Class::Lib_NpPartner, nullptr},
{Class::Lib_NpParty, nullptr},
{Class::Lib_NpProfileDialog, nullptr},
{Class::Lib_NpScore, nullptr},
{Class::Lib_NpSnsFacebookDialog, nullptr},
{Class::Lib_NpTrophy, nullptr},
{Class::Lib_NpTus, nullptr},
{Class::Lib_NpWebApi, nullptr},
{Class::Lib_NpWebApi2, nullptr},
{Class::Lib_Pad, nullptr},
{Class::Lib_PlayGo, nullptr},
{Class::Lib_PlayGoDialog, nullptr},
{Class::Lib_Png, nullptr},
{Class::Lib_Random, nullptr},
{Class::Lib_RazorCpu, nullptr},
{Class::Lib_Remoteplay, nullptr},
{Class::Lib_Rtc, nullptr},
{Class::Lib_Rudp, nullptr},
{Class::Lib_SaveData, nullptr},
{Class::Lib_SaveDataDialog, nullptr},
{Class::Lib_Screenshot, nullptr},
{Class::Lib_SharePlay, nullptr},
{Class::Lib_SigninDialog, nullptr},
{Class::Lib_Ssl, nullptr},
{Class::Lib_Ssl2, nullptr},
{Class::Lib_SysModule, nullptr},
{Class::Lib_SystemGesture, nullptr},
{Class::Lib_SystemService, nullptr},
{Class::Lib_Usbd, nullptr},
{Class::Lib_UserService, nullptr},
{Class::Lib_Vdec2, nullptr},
{Class::Lib_VideoOut, nullptr},
{Class::Lib_Videodec, nullptr},
{Class::Lib_VideoRecording, nullptr},
{Class::Lib_Voice, nullptr},
{Class::Lib_VrTracker, nullptr},
{Class::Lib_WebBrowserDialog, nullptr},
{Class::Lib_Zlib, nullptr},
{Class::Loader, nullptr},
{Class::Log, nullptr},
{Class::NpHandler, nullptr},
{Class::Render, nullptr},
{Class::Render_Recompiler, nullptr},
{Class::Render_Vulkan, nullptr},
{Class::ShadNet, nullptr},
{Class::Tty, nullptr},
};
template <typename T>
static auto UpdateColorLevels(T sink) {
#ifdef _WIN32
using LogColor = std::uint16_t;
const auto Grey = FOREGROUND_INTENSITY;
const auto Cyan = FOREGROUND_GREEN | FOREGROUND_BLUE;
const auto Bright_gray = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
const auto Bright_yellow = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
const auto Bright_red = FOREGROUND_RED | FOREGROUND_INTENSITY;
const auto Bright_magenta = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
#else
using LogColor = std::string_view;
#define ESC "\x1b"
const auto Grey = ESC "[1;30m";
const auto Cyan = ESC "[0;36m";
const auto Bright_gray = ESC "[0;37m";
const auto Bright_yellow = ESC "[1;33m";
const auto Bright_red = ESC "[1;31m";
const auto Bright_magenta = ESC "[1;35m";
#undef ESC
#endif
const std::unordered_map<spdlog::level, LogColor> colors{
{spdlog::level::trace, Grey}, {spdlog::level::debug, Cyan},
{spdlog::level::info, Bright_gray}, {spdlog::level::warn, Bright_yellow},
{spdlog::level::err, Bright_red}, {spdlog::level::critical, Bright_magenta}};
for (const auto& [level, color] : colors) {
sink->set_color(level, color);
}
return sink;
}
void Setup(std::string_view shadps4_filename) {
static std::once_flag already_registered;
std::call_once(already_registered, []() {
std::atexit(Shutdown);
std::at_quick_exit(Flush);
std::set_terminate(Terminate);
});
for (auto& [name, logger] : ALL_LOGGERS) {
logger = std::make_shared<spdlog::logger>(std::string(name));
logger->set_level(spdlog::level::trace);
}
// Setup console
#ifdef _WIN32
if (EmulatorSettings.GetLogType() == "wincolor") {
g_console_sink = std::make_shared<spdlog::sinks::wincolor_stdout_sink_mt>();
} else {
g_console_sink = std::make_shared<spdlog::sinks::msvc_sink_mt>();
}
#else
g_console_sink = UpdateColorLevels(std::make_shared<spdlog_stdout>(spdlog::color_mode::always));
#endif
g_console_sink->set_pattern("%^%v%$");
// Setup file
g_shad_file_sink = std::make_shared<LogFileSink>(
(GetUserPath(Common::FS::PathType::LogDir) / shadps4_filename).string(), false,
EmulatorSettings.GetLogSizeLimit());
g_shad_file_sink->set_pattern("%^%v%$");
UpdateSinks();
}
void Switch(std::string_view game_filename) {
UpdateSinks();
UpdateLogLevels(EmulatorSettings.GetLogFilter());
g_shad_file_sink->_size_limit = EmulatorSettings.GetLogSizeLimit();
g_shad_file_sink->session_file_helper_.open(
(GetUserPath(Common::FS::PathType::LogDir) / game_filename).string(), !g_should_append);
}
void Shutdown() {
for (auto& logger : ALL_LOGGERS | std::views::values) {
logger.reset();
}
g_shad_file_sink.reset();
g_console_sink.reset();
}
void Flush() {
if (g_shad_file_sink != nullptr) {
g_shad_file_sink->flush();
}
if (g_console_sink != nullptr) {
g_console_sink->flush();
}
}
void Terminate() {
try {
if (std::exception_ptr eptr{std::current_exception()}) {
std::rethrow_exception(eptr);
}
LOG_CRITICAL(Debug, "Exiting without exception");
std::quick_exit(std::to_underlying(ShadPs4ReturnCode::TERMINATE_WITHOUT_EXCEPTION));
} catch (const std::exception& exception) {
LOG_CRITICAL(Debug, "Exception: {}", exception);
std::quick_exit(std::to_underlying(ShadPs4ReturnCode::TERMINATE_WITH_EXCEPTION));
} catch (...) {
LOG_CRITICAL(Debug, "Unknown exception caught");
std::quick_exit(std::to_underlying(ShadPs4ReturnCode::TERMINATE_WITH_UNKNOWN_EXCEPTION));
}
}
void UpdateSinks() {
std::initializer_list<spdlog::sink_ptr> sinks{g_console_sink, g_shad_file_sink};
std::initializer_list<spdlog::sink_ptr> async_sink{std::make_shared<spdlog::sinks::async_sink>(
spdlog::sinks::async_sink::config{.sinks = sinks})};
std::initializer_list<spdlog::sink_ptr> dup_filter{
std::make_shared<spdlog::sinks::dup_filter_sink_mt>(
std::chrono::milliseconds(EmulatorSettings.GetLogMaxSkipDuration()),
EmulatorSettings.IsLogSync() ? sinks : async_sink)};
for (auto& [name, logger] : ALL_LOGGERS) {
logger->sinks() = EmulatorSettings.IsLogSkipDuplicate()
? dup_filter
: (EmulatorSettings.IsLogSync() ? sinks : async_sink);
}
}
void UpdateLogLevels(std::string_view log_filter) {
spdlog::level default_log_level = spdlog::level::info;
std::unordered_map<std::string, spdlog::level> log_level_per_class;
if (EmulatorSettings.IsLogEnable()) {
for (const auto class_level : std::views::split(log_filter, ' ')) {
const auto class_level_pair =
std::views::split(class_level, ':') | std::ranges::to<std::vector<std::string>>();
if (class_level_pair.size() != 2) {
LOG_ERROR(Config, "bad log filter provided");
continue;
}
if (class_level_pair.front()[0] == '*') {
default_log_level = spdlog::level_from_str(class_level_pair.back() |
std::ranges::to<std::string>());
} else {
log_level_per_class[class_level_pair.front() | std::ranges::to<std::string>()] =
spdlog::level_from_str(class_level_pair.back() |
std::ranges::to<std::string>());
}
}
}
for (auto& [name, logger] : ALL_LOGGERS) {
if (EmulatorSettings.IsLogEnable()) {
const auto level_it = log_level_per_class.find(std::string(name));
logger->set_level(level_it != log_level_per_class.end() ? level_it->second
: default_log_level);
} else {
logger->set_level(spdlog::level::off);
}
}
}
} // namespace Common::Log