#include "common/logging/log.h" #include "common/assert.h" #include "common/emulatorConfig.h" #include #include #include #include #include #include #include #include #include #include #include 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 clone() const override { return std::make_unique(); } }; std::shared_ptr MakeLogger(std::string name, spdlog::sink_ptr sink) { sink->set_formatter(std::make_unique()); auto logger = std::make_shared(std::move(name), std::move(sink)); logger->set_level(spdlog::level::trace); return logger; } std::shared_ptr 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(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 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()); break; case Direction::Console: g_logger = MakeLogger("kyty", std::make_shared()); break; case Direction::File: if (!g_output_file.empty()) { g_logger = MakeFileLogger("kyty", g_output_file); } break; } } static std::shared_ptr 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