diff --git a/BigBaseV2/src/logger.hpp b/BigBaseV2/src/logger.hpp index 17f2f6fd..e1ef332c 100644 --- a/BigBaseV2/src/logger.hpp +++ b/BigBaseV2/src/logger.hpp @@ -1,6 +1,8 @@ #pragma once #include "common.hpp" +#include "file_manager.hpp" #include +#include #include namespace big @@ -14,202 +16,206 @@ namespace big return system_clock::to_time_t(sctp); } - enum class log_color : std::uint16_t - { - red = FOREGROUND_RED, - green = FOREGROUND_GREEN, - blue = FOREGROUND_BLUE, - intensify = FOREGROUND_INTENSITY - }; - - enum LOG_FLAGS - { - FLAG_NO_DISK = (1 << 0), - FLAG_NO_CONSOLE = (1 << 1) - }; - - static const int kEventValue = 400; - static const int kRawValue = 600; - inline constexpr auto max_padding_length = 26; - inline constexpr auto level_padding_length = 8; - - const LEVELS INFO_TO_FILE{ INFO.value | FLAG_NO_CONSOLE, {"INFO"} }, - HACKER{ INFO.value, {"HACKER"} }, - EVENT{ kEventValue | FLAG_NO_CONSOLE, {"EVENT"} }, - RAW_GREEN_TO_CONSOLE{ kRawValue | FLAG_NO_DISK, {"RAW_GREEN_TO_CONSOLE"} }, - RAW_RED{ kRawValue, {"RAW_RED"} }; - - inline log_color operator|(log_color a, log_color b) - { - return static_cast(static_cast>(a) | static_cast>(b)); - } - class logger; - inline logger* g_logger{}; + inline logger* g_log{}; - class logger + enum class LogColor { + RESET, + WHITE = 97, + CYAN = 36, + MAGENTA = 35, + BLUE = 34, + GREEN = 32, + YELLOW = 33, + RED = 31, + BLACK = 30 + }; + +#define AddColorToStream(color) "\x1b[" << int(color) << "m" +#define ResetStreamColor "\x1b[" << int(LogColor::RESET) << "m" +#define HEX_TO_UPPER(value) "0x" << std::hex << std::uppercase << (DWORD64)value << std::dec << std::nouppercase + + class logger final + { + public: - explicit logger() : - m_file_path(std::getenv("appdata")), - m_worker(g3::LogWorker::createLogWorker()) + logger(std::string_view console_title, file file, bool attach_console = true) + : m_attach_console(attach_console), m_did_console_exist(false), + m_console_title(console_title), m_original_console_mode(0), + m_console_handle(nullptr), m_file(file), + m_worker(g3::LogWorker::createLogWorker()) { - m_file_path /= "BigBaseV2"; - std::filesystem::path m_backup_path = m_file_path; - m_backup_path /= "Backup"; - try - { - if (!std::filesystem::exists(m_file_path)) - { - std::filesystem::create_directory(m_file_path); - } - else if (!std::filesystem::is_directory(m_file_path)) - { - std::filesystem::remove(m_file_path); - std::filesystem::create_directory(m_file_path); - } - if (!std::filesystem::exists(m_backup_path)) - { - std::filesystem::create_directory(m_backup_path); - } - else if (!std::filesystem::is_directory(m_backup_path)) - { - std::filesystem::remove(m_backup_path); - std::filesystem::create_directory(m_backup_path); - } + initialize(); - m_event_file_path = m_file_path; - m_file_path /= "BigBaseV2.log"; - m_event_file_path /= "GTAEvents.log"; - - if (std::filesystem::exists(m_file_path)) - { - auto file_time = std::filesystem::last_write_time(m_file_path); - auto timet = to_time_t(file_time); - auto local_time = std::localtime(&timet); - - auto bigbase_timestamp = fmt::format("{:0>2}-{:0>2}-{}-{:0>2}-{:0>2}-{:0>2} BigBaseV2.log", local_time->tm_mon + 1, local_time->tm_mday, local_time->tm_year + 1900, local_time->tm_hour, local_time->tm_min, local_time->tm_sec); - auto gta_events_timestamp = fmt::format("{:0>2}-{:0>2}-{}-{:0>2}-{:0>2}-{:0>2} GTAEvents.log", local_time->tm_mon + 1, local_time->tm_mday, local_time->tm_year + 1900, local_time->tm_hour, local_time->tm_min, local_time->tm_sec); - - std::filesystem::copy_file(m_file_path, m_backup_path / bigbase_timestamp); - if (std::filesystem::exists(m_event_file_path) && !std::filesystem::is_empty(m_event_file_path)) - std::filesystem::copy_file(m_event_file_path, m_backup_path / gta_events_timestamp); - } - - m_file_out.open(m_file_path, std::ios_base::out | std::ios_base::trunc); - m_gta_event_file_out.open(m_event_file_path, std::ios_base::out | std::ios_base::trunc); - - m_worker->addSink(std::make_unique(), &log_sink::callback); - g3::initializeLogging(m_worker.get()); - } - catch (std::filesystem::filesystem_error const& error) - { - m_console_out << error.what(); - } - - g_logger = this; + g_log = this; } - ~logger() { - m_worker.reset(); - if (!m_did_console_exist) - FreeConsole(); - - g_logger = nullptr; + g_log = nullptr; } - std::pair*, std::size_t> get_messages() + void initialize() { - return std::make_pair(m_messages.data(), m_messages.size()); - } - std::vector> m_messages; - std::mutex m_mutex; - - struct log_sink - { - std::map log_colors = { - {INFO.text, log_color::blue | log_color::intensify}, - {WARNING.text, log_color::red}, - {HACKER.text, log_color::green | log_color::intensify}, - {FATAL.text, log_color::red | log_color::intensify}, - {G3LOG_DEBUG.text, log_color::blue}, - {RAW_RED.text, log_color::red}, - {RAW_GREEN_TO_CONSOLE.text, log_color::green | log_color::intensify} - }; - - void callback(g3::LogMessageMover log) + if (m_attach_console) { - g3::LogMessage log_message = log.get(); - int level_value = log_message._level.value; + if (m_did_console_exist = ::AttachConsole(GetCurrentProcessId()); !m_did_console_exist) + AllocConsole(); - bool is_raw = level_value == RAW_RED.value || level_value == RAW_GREEN_TO_CONSOLE.value; - - if (!(level_value & FLAG_NO_CONSOLE)) + if (m_console_handle = GetStdHandle(STD_OUTPUT_HANDLE); m_console_handle != nullptr) { - std::lock_guard lock(g_logger->m_mutex); + SetConsoleTitleA(m_console_title.data()); + SetConsoleOutputCP(CP_UTF8); - const std::string msg = log_message.toString(is_raw ? format_raw : format_console); + DWORD console_mode; + GetConsoleMode(m_console_handle, &console_mode); + m_original_console_mode = console_mode; - std::size_t size = msg.size() + 1; - auto message = std::make_unique(size); - std::uninitialized_fill_n(message.get(), size, '\0'); - strcpy(message.get(), msg.c_str()); - g_logger->m_messages.push_back( - std::move(message) - ); - } - - if (!(level_value & FLAG_NO_DISK)) - { - if (level_value == EVENT.value) - g_logger->m_gta_event_file_out << log_message.toString(format_file) << std::flush; - else - g_logger->m_file_out << log_message.toString(is_raw ? format_raw : format_file) << std::flush; + // terminal like behaviour enable full color support + console_mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING | DISABLE_NEWLINE_AUTO_RETURN; + // prevent clicking in terminal from suspending our main thread + //console_mode &= ~(ENABLE_QUICK_EDIT_MODE); + + SetConsoleMode(m_console_handle, console_mode); } } + create_backup(); + open_outstreams(); + initialize_g3log(); + } + void destroy() + { + destroy_g3log(); + close_outstreams(); - static std::string format_file(const g3::LogMessage& msg) + if (m_did_console_exist) + SetConsoleMode(m_console_handle, m_original_console_mode); + + if (!m_did_console_exist && m_attach_console) + FreeConsole(); + } + + private: + void create_backup() + { + if (m_file.exists()) { - std::string file_name_with_line = "[" + msg.file() + ":" + msg.line() + "]"; - std::stringstream out; - out << "[" << msg.timestamp("%H:%M:%S") << "] [" << std::left << std::setw(level_padding_length) << msg.level().append("]") << std::setw(max_padding_length) << file_name_with_line; - return out.str(); + auto file_time = std::filesystem::last_write_time(m_file.get_path()); + auto time_t = to_time_t(file_time); + auto local_time = std::localtime(&time_t); + + m_file.move( + fmt::format( + "./backup/{:0>2}-{:0>2}-{}-{:0>2}-{:0>2}-{:0>2}_{}", + local_time->tm_mon + 1, + local_time->tm_mday, + local_time->tm_year + 1900, + local_time->tm_hour, + local_time->tm_min, + local_time->tm_sec, + m_file.get_path().filename().string().c_str() + ) + ); + } + } + + void initialize_g3log() + { + m_worker->addSink(std::make_unique(), &log_sink::callback); + g3::initializeLogging(m_worker.get()); + } + void destroy_g3log() + { + m_worker->removeAllSinks(); + m_worker.reset(); + } + + void open_outstreams() + { + if (m_attach_console) + m_console_out.open("CONOUT$", std::ios_base::out | std::ios_base::app); + + m_file_out.open(m_file.get_path(), std::ios_base::out | std::ios_base::trunc); + } + void close_outstreams() + { + if (m_attach_console) + m_console_out.close(); + + m_file_out.close(); + } + + private: + struct log_sink + { + void callback(g3::LogMessageMover log) + { + if (g_log->m_console_out.is_open()) + g_log->m_console_out << log.get().toString(log_sink::format_console) << std::flush; + + g_log->m_file_out << log.get().toString(log_sink::format_file) << std::flush; + } + + static LogColor get_color(const LEVELS level) + { + switch (level.value) + { + case g3::kDebugValue: + return LogColor::BLUE; + case g3::kInfoValue: + return LogColor::GREEN; + case g3::kWarningValue: + return LogColor::YELLOW; + } + return g3::internal::wasFatal(level) ? LogColor::RED : LogColor::WHITE; } static std::string format_console(const g3::LogMessage& msg) { + LogColor color = log_sink::get_color(msg._level); std::stringstream out; - out << "[" << msg.timestamp("%H:%M:%S") << "] "; + + out + << "[" << msg.timestamp("%H:%M:%S") << "]" + << AddColorToStream(color) + << "[" << std::setw(7) << msg.level() << "/" + << msg.file() << ":" << msg.line() << "]" + << ResetStreamColor + << ": "; + return out.str(); } - - static std::string format_raw(const g3::LogMessage& msg) + static std::string format_file(const g3::LogMessage& msg) { - return ""; + LogColor color = log_sink::get_color(msg._level); + std::stringstream out; + + out + << "[" << msg.timestamp("%H:%M:%S") << "]" + << "[" << std::setw(7) << msg.level() << "/" + << msg.file() << ":" << msg.line() << "]" + << ": "; + + return out.str(); } }; - template - inline void log_now(std::string_view format, Args const& ...args) - { - auto message = fmt::format(format, args...); - if (m_file_out) - m_file_out << message << std::endl << std::flush; - } - private: - bool m_did_console_exist{}; - HANDLE m_console_handle{}; + friend struct log_sink; + + bool m_attach_console; + bool m_did_console_exist; + + std::string_view m_console_title; + DWORD m_original_console_mode; + HANDLE m_console_handle; + std::ofstream m_console_out; - std::filesystem::path m_file_path; - std::filesystem::path m_event_file_path; std::ofstream m_file_out; - std::ofstream m_gta_event_file_out; + + file m_file; + std::unique_ptr m_worker; + }; - - -#define LOG_NOW(format, ...) g_logger->log_now(format, __VA_ARGS__) -#define HEX_TO_UPPER(value) "0x" << std::hex << std::uppercase << (DWORD64)value << std::dec << std::nouppercase -} +} \ No newline at end of file