2021-01-06 12:16:39 -05:00
|
|
|
#include <cstdio>
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <cassert>
|
|
|
|
#include <mutex>
|
|
|
|
#include "third-party/fmt/color.h"
|
|
|
|
#include "log.h"
|
2021-01-10 15:55:21 -05:00
|
|
|
#ifdef _WIN32 // see lg::initialize
|
|
|
|
#include <Windows.h>
|
|
|
|
#endif
|
2021-01-06 12:16:39 -05:00
|
|
|
|
|
|
|
namespace lg {
|
|
|
|
struct Logger {
|
|
|
|
Logger() = default;
|
|
|
|
|
|
|
|
bool initialized = false;
|
|
|
|
FILE* fp = nullptr;
|
|
|
|
level stdout_log_level = level::trace;
|
|
|
|
level file_log_level = level::trace;
|
|
|
|
level flush_level = level::trace;
|
|
|
|
std::mutex mutex;
|
|
|
|
|
|
|
|
~Logger() {
|
|
|
|
// will run when program exits.
|
|
|
|
if (fp) {
|
|
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
Logger gLogger;
|
|
|
|
|
|
|
|
namespace internal {
|
|
|
|
const char* log_level_names[] = {"trace", "debug", "info", "warn", "error", "die"};
|
|
|
|
const fmt::color log_colors[] = {fmt::color::gray, fmt::color::turquoise, fmt::color::light_green,
|
|
|
|
fmt::color::yellow, fmt::color::red, fmt::color::hot_pink};
|
|
|
|
|
|
|
|
void log_message(level log_level, LogTime& now, const char* message) {
|
|
|
|
#ifdef __linux__
|
|
|
|
char date_time_buffer[128];
|
|
|
|
time_t now_seconds = now.tv.tv_sec;
|
|
|
|
auto now_milliseconds = now.tv.tv_usec / 1000;
|
|
|
|
strftime(date_time_buffer, 128, "%Y-%m-%d %H:%M:%S", localtime(&now_seconds));
|
|
|
|
std::string date_string = fmt::format("[{}:{:03d}]", date_time_buffer, now_milliseconds);
|
|
|
|
#else
|
|
|
|
char date_time_buffer[128];
|
|
|
|
strftime(date_time_buffer, 128, "%Y-%m-%d %H:%M:%S", localtime(&now.tim));
|
|
|
|
std::string date_string = fmt::format("[{}]", date_time_buffer);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(gLogger.mutex);
|
|
|
|
if (gLogger.fp && log_level >= gLogger.file_log_level) {
|
|
|
|
// log to file
|
|
|
|
std::string file_string =
|
|
|
|
fmt::format("{} [{}] {}\n", date_string, log_level_names[int(log_level)], message);
|
|
|
|
fwrite(file_string.c_str(), file_string.length(), 1, gLogger.fp);
|
|
|
|
if (log_level >= gLogger.flush_level) {
|
|
|
|
fflush(gLogger.fp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (log_level >= gLogger.stdout_log_level) {
|
|
|
|
fmt::print("{} [", date_string);
|
|
|
|
fmt::print(fg(log_colors[int(log_level)]), "{}", log_level_names[int(log_level)]);
|
|
|
|
fmt::print("] {}\n", message);
|
|
|
|
if (log_level >= gLogger.flush_level) {
|
|
|
|
fflush(stdout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (log_level == level::die) {
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace internal
|
|
|
|
|
|
|
|
void set_file(const std::string& filename) {
|
|
|
|
assert(!gLogger.fp);
|
|
|
|
gLogger.fp = fopen(filename.c_str(), "w");
|
|
|
|
assert(gLogger.fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_flush_level(level log_level) {
|
|
|
|
gLogger.flush_level = log_level;
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_file_level(level log_level) {
|
|
|
|
gLogger.file_log_level = log_level;
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_stdout_level(level log_level) {
|
|
|
|
gLogger.stdout_log_level = log_level;
|
|
|
|
}
|
|
|
|
|
|
|
|
void set_max_debug_levels() {
|
|
|
|
gLogger.flush_level = level::trace;
|
|
|
|
gLogger.stdout_log_level = level::trace;
|
|
|
|
gLogger.file_log_level = level::trace;
|
|
|
|
}
|
|
|
|
|
|
|
|
void initialize() {
|
|
|
|
assert(!gLogger.initialized);
|
2021-01-10 15:55:21 -05:00
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
// Always enable VIRTUAL_TERMINAL_PROCESSING, this console mode allows the console (stdout) to
|
|
|
|
// support ANSI colors in the outputted text, which are used by the logging tool.
|
|
|
|
// This mode may not be enabled by default, and changing that involves modifying the registry,
|
|
|
|
// so it seems like a better solution would be enabling it ourselves.
|
|
|
|
|
|
|
|
// Get handle to stdout
|
|
|
|
HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
|
|
|
|
// get current stdout mode
|
|
|
|
DWORD modeStdOut;
|
|
|
|
GetConsoleMode(hStdOut, &modeStdOut);
|
|
|
|
// enable VIRTUAL_TERMINAL_PROCESSING. As a bitwise OR it will not do anything if it is
|
|
|
|
// already set
|
|
|
|
modeStdOut |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
|
|
|
|
SetConsoleMode(hStdOut, modeStdOut);
|
|
|
|
#endif
|
|
|
|
|
2021-01-06 12:16:39 -05:00
|
|
|
gLogger.initialized = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void finish() {
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(gLogger.mutex);
|
|
|
|
if (gLogger.fp) {
|
|
|
|
fclose(gLogger.fp);
|
|
|
|
gLogger.fp = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace lg
|