Files
KytyPS5/src/common/logging/log.cpp
T
nmzik 8cecaf4e30 Merge PR #716: Fix Unicode paths on Windows
Preserve native Windows paths for filesystem operations, command-line options,
and game icons. Complete Unicode handling in recursive file listings and file
logging, restore unmounting by host directory, and extend regression coverage.

Original implementation by Augusto Domingos (@AghastyGD); retain all five of
his PR commits and include the reviewed follow-up fixes.

Closes #714

Co-authored-by: Augusto Domingos <augustodomingosvasco@gmail.com>
2026-09-20 02:14:22 +04:00

196 lines
4.8 KiB
C++

#include "common/logging/log.h"
#include "common/assert.h"
#include "common/emulatorConfig.h"
#include <cstdio>
#include <filesystem>
#include <fmt/format.h>
#include <memory>
#include <mutex>
#include <spdlog/formatter.h>
#include <spdlog/logger.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/null_sink.h>
#include <spdlog/sinks/stdout_sinks.h>
#include <string_view>
namespace {
class RawFormatter final: public spdlog::formatter {
public:
void format(const spdlog::details::log_msg& msg, spdlog::memory_buf_t& dest) override {
const std::string_view payload(msg.payload.data(), msg.payload.size());
dest.append(payload.data(), payload.data() + payload.size());
}
std::unique_ptr<spdlog::formatter> clone() const override {
return std::make_unique<RawFormatter>();
}
};
std::shared_ptr<spdlog::logger> MakeLogger(std::string name, spdlog::sink_ptr sink) {
sink->set_formatter(std::make_unique<RawFormatter>());
auto logger = std::make_shared<spdlog::logger>(std::move(name), std::move(sink));
logger->set_level(spdlog::level::trace);
return logger;
}
std::shared_ptr<spdlog::logger> MakeFileLogger(std::string name,
const std::filesystem::path& path) {
const auto parent = path.parent_path();
if (!parent.empty()) {
std::filesystem::create_directories(parent);
}
auto sink =
std::make_shared<spdlog::sinks::basic_file_sink_mt>(path.native(), true);
return MakeLogger(std::move(name), std::move(sink));
}
static bool HasStyle(fmt::text_style style) {
return style != fmt::text_style {};
}
void WriteStdout(std::string_view text, fmt::text_style style = {}) {
if (HasStyle(style)) {
fmt::print(stdout, style, "{}", text);
} else {
std::fwrite(text.data(), 1, text.size(), stdout);
}
std::fflush(stdout);
}
} // namespace
namespace Log {
static bool g_initialized = false;
static Direction g_direction = Direction::Console;
static std::filesystem::path g_output_file;
static std::mutex g_logger_mutex;
static std::shared_ptr<spdlog::logger> g_logger;
void Flush() {
if (g_logger != nullptr) {
g_logger->flush();
}
}
static void SetupLogger() {
std::lock_guard lock(g_logger_mutex);
Flush();
g_logger.reset();
switch (g_direction) {
case Direction::Silent:
g_logger = MakeLogger("kyty", std::make_shared<spdlog::sinks::null_sink_mt>());
break;
case Direction::Console:
g_logger = MakeLogger("kyty", std::make_shared<spdlog::sinks::stdout_sink_mt>());
break;
case Direction::File:
if (!g_output_file.empty()) {
g_logger = MakeFileLogger("kyty", g_output_file);
}
break;
}
}
static std::shared_ptr<spdlog::logger> GetLogger() {
std::lock_guard lock(g_logger_mutex);
return g_logger;
}
static void WriteImpl(std::string_view text, fmt::text_style style = {}) {
if (text.empty()) {
return;
}
if (!g_initialized) {
WriteStdout(text, style);
return;
}
if (g_direction == Direction::Silent) {
return;
}
if (auto logger = GetLogger()) {
if (g_direction == Direction::Console && HasStyle(style)) {
const auto styled = fmt::format(style, "{}", text);
logger->log(spdlog::level::info, spdlog::string_view_t(styled.data(), styled.size()));
} else {
logger->log(spdlog::level::info, spdlog::string_view_t(text.data(), text.size()));
}
}
}
void WriteToConsoleAndLog(std::string_view text) {
WriteImpl(text);
if (g_initialized && g_direction != Direction::Console) {
WriteStdout(text);
}
Flush();
}
void WriteFatal(std::string_view text) {
if (g_direction == Direction::Silent || !g_initialized) {
WriteStdout(text);
} else {
WriteStdout(text);
WriteImpl(text);
}
Flush();
}
void WriteFatal(fmt::text_style style, std::string_view text) {
if (g_direction == Direction::Silent || !g_initialized) {
WriteStdout(text, style);
} else {
WriteStdout(text, style);
WriteImpl(text, style);
}
Flush();
}
void Initialize() {
g_initialized = true;
switch (Config::GetPrintfDirection()) {
case Config::LogDirection::Silent: g_direction = Direction::Silent; break;
case Config::LogDirection::Console: g_direction = Direction::Console; break;
case Config::LogDirection::File: g_direction = Direction::File; break;
}
g_output_file =
(g_direction == Direction::File ? Config::GetPrintfOutputFile() : std::filesystem::path {});
SetupLogger();
}
void Shutdown() {
Flush();
std::lock_guard lock(g_logger_mutex);
g_logger.reset();
}
Direction GetDirection() {
EXIT_IF(!g_initialized);
return g_direction;
}
bool IsSilent() {
// Before init LOGF must keep writing to stdout, so report non-silent.
return g_initialized && g_direction == Direction::Silent;
}
void Write(std::string_view text) {
WriteImpl(text);
}
void Write(fmt::text_style style, std::string_view text) {
WriteImpl(text, style);
}
} // namespace Log