mirror of
https://github.com/Mr-X-GTA/YimMenu.git
synced 2025-01-09 02:43:38 +08:00
feat(Logger): Complete rewrite
This commit is contained in:
parent
8e960e7c81
commit
4b8826bc43
@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include "common.hpp"
|
||||
#include "file_manager.hpp"
|
||||
#include <g3log/g3log.hpp>
|
||||
#include <g3log/loglevels.hpp>
|
||||
#include <g3log/logworker.hpp>
|
||||
|
||||
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<log_color>(static_cast<std::underlying_type_t<log_color>>(a) | static_cast<std::underlying_type_t<log_color>>(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>(), &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::unique_ptr<char[]>*, std::size_t> get_messages()
|
||||
void initialize()
|
||||
{
|
||||
return std::make_pair(m_messages.data(), m_messages.size());
|
||||
}
|
||||
std::vector<std::unique_ptr<char[]>> m_messages;
|
||||
std::mutex m_mutex;
|
||||
|
||||
struct log_sink
|
||||
{
|
||||
std::map<std::string, log_color> 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<char[]>(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>(), &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 <typename ...Args>
|
||||
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<g3::LogWorker> 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
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user