mirror of
https://github.com/ddnet/ddnet.git
synced 2024-11-14 20:18:19 +00:00
419 lines
9.5 KiB
C++
419 lines
9.5 KiB
C++
|
#include "logger.h"
|
||
|
|
||
|
#include "color.h"
|
||
|
#include "system.h"
|
||
|
|
||
|
#include <atomic>
|
||
|
#include <cstdio>
|
||
|
|
||
|
#if defined(CONF_FAMILY_WINDOWS)
|
||
|
#define WIN32_LEAN_AND_MEAN
|
||
|
#undef _WIN32_WINNT
|
||
|
#define _WIN32_WINNT 0x0501 /* required for mingw to get getaddrinfo to work */
|
||
|
#include <windows.h>
|
||
|
#endif
|
||
|
|
||
|
extern "C" {
|
||
|
|
||
|
std::atomic<ILogger *> global_logger = nullptr;
|
||
|
thread_local ILogger *scope_logger = nullptr;
|
||
|
thread_local bool in_logger = false;
|
||
|
|
||
|
void log_set_global_logger(ILogger *logger)
|
||
|
{
|
||
|
ILogger *null = nullptr;
|
||
|
if(!global_logger.compare_exchange_strong(null, logger, std::memory_order_acq_rel))
|
||
|
{
|
||
|
dbg_assert(false, "global logger has already been set and can only be set once");
|
||
|
}
|
||
|
atexit(log_global_logger_finish);
|
||
|
}
|
||
|
|
||
|
void log_global_logger_finish()
|
||
|
{
|
||
|
global_logger.load(std::memory_order_acquire)->GlobalFinish();
|
||
|
}
|
||
|
|
||
|
void log_set_global_logger_default()
|
||
|
{
|
||
|
std::unique_ptr<ILogger> logger;
|
||
|
#if defined(CONF_PLATFORM_ANDROID)
|
||
|
logger = log_logger_android();
|
||
|
#else
|
||
|
logger = log_logger_stdout();
|
||
|
#endif
|
||
|
log_set_global_logger(logger.release());
|
||
|
}
|
||
|
|
||
|
ILogger *log_get_scope_logger()
|
||
|
{
|
||
|
if(!scope_logger)
|
||
|
{
|
||
|
scope_logger = global_logger.load(std::memory_order_acquire);
|
||
|
}
|
||
|
return scope_logger;
|
||
|
}
|
||
|
|
||
|
void log_set_scope_logger(ILogger *logger)
|
||
|
{
|
||
|
scope_logger = logger;
|
||
|
if(!scope_logger)
|
||
|
{
|
||
|
scope_logger = global_logger.load(std::memory_order_acquire);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void log_log_impl(LEVEL level, bool have_color, LOG_COLOR color, const char *sys, const char *fmt, va_list args)
|
||
|
{
|
||
|
// Make sure we're not logging recursively.
|
||
|
if(in_logger)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
in_logger = true;
|
||
|
if(!scope_logger)
|
||
|
{
|
||
|
scope_logger = global_logger.load(std::memory_order_acquire);
|
||
|
}
|
||
|
if(!scope_logger)
|
||
|
{
|
||
|
in_logger = false;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
CLogMessage Msg;
|
||
|
Msg.m_Level = level;
|
||
|
Msg.m_HaveColor = have_color;
|
||
|
Msg.m_Color = color;
|
||
|
str_timestamp_format(Msg.m_aTimestamp, sizeof(Msg.m_aTimestamp), FORMAT_SPACE);
|
||
|
Msg.m_TimestampLength = str_length(Msg.m_aTimestamp);
|
||
|
str_copy(Msg.m_aSystem, sys, sizeof(Msg.m_aSystem));
|
||
|
Msg.m_SystemLength = str_length(Msg.m_aSystem);
|
||
|
|
||
|
// TODO: Add level?
|
||
|
str_format(Msg.m_aLine, sizeof(Msg.m_aLine), "[%s][%s]: ", Msg.m_aTimestamp, Msg.m_aSystem);
|
||
|
Msg.m_LineMessageOffset = str_length(Msg.m_aLine);
|
||
|
|
||
|
char *pMessage = Msg.m_aLine + Msg.m_LineMessageOffset;
|
||
|
int MessageSize = sizeof(Msg.m_aLine) - Msg.m_LineMessageOffset;
|
||
|
#ifdef __GNUC__
|
||
|
#pragma GCC diagnostic push
|
||
|
#pragma GCC diagnostic ignored "-Wformat-nonliteral"
|
||
|
#endif
|
||
|
#if defined(CONF_FAMILY_WINDOWS)
|
||
|
_vsnprintf(pMessage, MessageSize, fmt, args);
|
||
|
#else
|
||
|
vsnprintf(pMessage, MessageSize, fmt, args);
|
||
|
#endif
|
||
|
#ifdef __GNUC__
|
||
|
#pragma GCC diagnostic pop
|
||
|
#endif
|
||
|
Msg.m_LineLength = str_length(Msg.m_aLine);
|
||
|
scope_logger->Log(&Msg);
|
||
|
in_logger = false;
|
||
|
}
|
||
|
|
||
|
void log_log_v(LEVEL level, const char *sys, const char *fmt, va_list args)
|
||
|
{
|
||
|
log_log_impl(level, false, LOG_COLOR{0, 0, 0}, sys, fmt, args);
|
||
|
}
|
||
|
|
||
|
void log_log(LEVEL level, const char *sys, const char *fmt, ...)
|
||
|
{
|
||
|
va_list args;
|
||
|
va_start(args, fmt);
|
||
|
log_log_impl(level, false, LOG_COLOR{0, 0, 0}, sys, fmt, args);
|
||
|
va_end(args);
|
||
|
}
|
||
|
|
||
|
void log_log_color_v(LEVEL level, LOG_COLOR color, const char *sys, const char *fmt, va_list args)
|
||
|
{
|
||
|
log_log_impl(level, true, color, sys, fmt, args);
|
||
|
}
|
||
|
|
||
|
void log_log_color(LEVEL level, LOG_COLOR color, const char *sys, const char *fmt, ...)
|
||
|
{
|
||
|
va_list args;
|
||
|
va_start(args, fmt);
|
||
|
log_log_impl(level, true, color, sys, fmt, args);
|
||
|
va_end(args);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if defined(CONF_PLATFORM_ANDROID)
|
||
|
class CLoggerAndroid : public ILogger
|
||
|
{
|
||
|
public:
|
||
|
void Log(const CLogMessage *pMessage) override
|
||
|
{
|
||
|
int AndroidLevel;
|
||
|
switch(Level)
|
||
|
{
|
||
|
case LEVEL_TRACE: AndroidLevel = ANDROID_LOG_VERBOSE; break;
|
||
|
case LEVEL_DEBUG: AndroidLevel = ANDROID_LOG_DEBUG; break;
|
||
|
case LEVEL_INFO: AndroidLevel = ANDROID_LOG_INFO; break;
|
||
|
case LEVEL_WARN: AndroidLevel = ANDROID_LOG_WARN; break;
|
||
|
case LEVEL_ERROR: AndroidLevel = ANDROID_LOG_ERROR; break;
|
||
|
}
|
||
|
__android_log_write(AndroidLevel, pMessage->m_aSystem, pMessage->Message());
|
||
|
}
|
||
|
};
|
||
|
std::unique_ptr<ILogger> log_logger_android()
|
||
|
{
|
||
|
return std::unique_ptr<ILogger>(new CLoggerAndroid());
|
||
|
}
|
||
|
#else
|
||
|
std::unique_ptr<ILogger> log_logger_android()
|
||
|
{
|
||
|
dbg_assert(0, "Android logger on non-Android");
|
||
|
return nullptr;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
class CLoggerCollection : public ILogger
|
||
|
{
|
||
|
std::vector<std::shared_ptr<ILogger>> m_apLoggers;
|
||
|
|
||
|
public:
|
||
|
CLoggerCollection(std::vector<std::shared_ptr<ILogger>> &&apLoggers) :
|
||
|
m_apLoggers(std::move(apLoggers))
|
||
|
{
|
||
|
}
|
||
|
void Log(const CLogMessage *pMessage) override
|
||
|
{
|
||
|
for(auto &pLogger : m_apLoggers)
|
||
|
{
|
||
|
pLogger->Log(pMessage);
|
||
|
}
|
||
|
}
|
||
|
void GlobalFinish() override
|
||
|
{
|
||
|
for(auto &pLogger : m_apLoggers)
|
||
|
{
|
||
|
pLogger->GlobalFinish();
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
std::unique_ptr<ILogger> log_logger_collection(std::vector<std::shared_ptr<ILogger>> &&loggers)
|
||
|
{
|
||
|
return std::unique_ptr<ILogger>(new CLoggerCollection(std::move(loggers)));
|
||
|
}
|
||
|
|
||
|
class CLoggerAsync : public ILogger
|
||
|
{
|
||
|
ASYNCIO *m_pAio;
|
||
|
bool m_AnsiTruecolor;
|
||
|
bool m_Close;
|
||
|
|
||
|
public:
|
||
|
CLoggerAsync(IOHANDLE File, bool AnsiTruecolor, bool Close) :
|
||
|
m_pAio(aio_new(File)),
|
||
|
m_AnsiTruecolor(AnsiTruecolor),
|
||
|
m_Close(Close)
|
||
|
{
|
||
|
}
|
||
|
void Log(const CLogMessage *pMessage) override
|
||
|
{
|
||
|
aio_lock(m_pAio);
|
||
|
if(m_AnsiTruecolor)
|
||
|
{
|
||
|
// https://en.wikipedia.org/w/index.php?title=ANSI_escape_code&oldid=1077146479#24-bit
|
||
|
char aAnsi[32];
|
||
|
if(pMessage->m_HaveColor)
|
||
|
{
|
||
|
str_format(aAnsi, sizeof(aAnsi),
|
||
|
"\x1b[38;2;%d;%d;%dm",
|
||
|
pMessage->m_Color.r,
|
||
|
pMessage->m_Color.g,
|
||
|
pMessage->m_Color.b);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
str_copy(aAnsi, "\x1b[39m", sizeof(aAnsi));
|
||
|
}
|
||
|
aio_write_unlocked(m_pAio, aAnsi, str_length(aAnsi));
|
||
|
}
|
||
|
aio_write_unlocked(m_pAio, pMessage->m_aLine, pMessage->m_LineLength);
|
||
|
aio_write_newline_unlocked(m_pAio);
|
||
|
aio_unlock(m_pAio);
|
||
|
}
|
||
|
~CLoggerAsync()
|
||
|
{
|
||
|
if(m_Close)
|
||
|
{
|
||
|
aio_close(m_pAio);
|
||
|
}
|
||
|
aio_wait(m_pAio);
|
||
|
aio_free(m_pAio);
|
||
|
}
|
||
|
void GlobalFinish() override
|
||
|
{
|
||
|
if(m_Close)
|
||
|
{
|
||
|
aio_close(m_pAio);
|
||
|
}
|
||
|
aio_wait(m_pAio);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
std::unique_ptr<ILogger> log_logger_file(IOHANDLE logfile)
|
||
|
{
|
||
|
return std::unique_ptr<ILogger>(new CLoggerAsync(logfile, false, true));
|
||
|
}
|
||
|
|
||
|
#if defined(CONF_FAMILY_WINDOWS)
|
||
|
static int color_hsv_to_windows_console_color(const ColorHSVA &Hsv)
|
||
|
{
|
||
|
int h = Hsv.h * 255.0f;
|
||
|
int s = Hsv.s * 255.0f;
|
||
|
int v = Hsv.v * 255.0f;
|
||
|
if(s >= 0 && s <= 10)
|
||
|
{
|
||
|
if(v <= 150)
|
||
|
return 8;
|
||
|
return 15;
|
||
|
}
|
||
|
else if(h >= 0 && h < 15)
|
||
|
return 12;
|
||
|
else if(h >= 15 && h < 30)
|
||
|
return 6;
|
||
|
else if(h >= 30 && h < 60)
|
||
|
return 14;
|
||
|
else if(h >= 60 && h < 110)
|
||
|
return 10;
|
||
|
else if(h >= 110 && h < 140)
|
||
|
return 11;
|
||
|
else if(h >= 140 && h < 170)
|
||
|
return 9;
|
||
|
else if(h >= 170 && h < 195)
|
||
|
return 5;
|
||
|
else if(h >= 195 && h < 240)
|
||
|
return 13;
|
||
|
else if(h >= 240)
|
||
|
return 12;
|
||
|
else
|
||
|
return 15;
|
||
|
}
|
||
|
|
||
|
class CWindowsConsoleLogger : public ILogger
|
||
|
{
|
||
|
public:
|
||
|
void Log(const CLogMessage *pMessage) override
|
||
|
{
|
||
|
wchar_t *pWide = (wchar_t *)malloc((pMessage->m_LineLength + 1) * sizeof(*pWide));
|
||
|
const char *p = pMessage->m_aLine;
|
||
|
int WLen = 0;
|
||
|
|
||
|
mem_zero(pWide, pMessage->m_LineLength * sizeof(*pWide));
|
||
|
|
||
|
for(int Codepoint = 0; (Codepoint = str_utf8_decode(&p)); WLen++)
|
||
|
{
|
||
|
char aU16[4] = {0};
|
||
|
|
||
|
if(Codepoint < 0)
|
||
|
{
|
||
|
free(pWide);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(str_utf16le_encode(aU16, Codepoint) != 2)
|
||
|
{
|
||
|
free(pWide);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
mem_copy(&pWide[WLen], aU16, 2);
|
||
|
}
|
||
|
pWide[WLen] = '\n';
|
||
|
HANDLE pConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||
|
int Color = 15;
|
||
|
if(pMessage->m_HaveColor)
|
||
|
{
|
||
|
ColorRGBA Rgba(1.0, 1.0, 1.0, 1.0);
|
||
|
Rgba.r = pMessage->m_Color.r / 255.0;
|
||
|
Rgba.g = pMessage->m_Color.g / 255.0;
|
||
|
Rgba.b = pMessage->m_Color.b / 255.0;
|
||
|
Color = color_hsv_to_windows_console_color(color_cast<ColorHSVA>(Rgba));
|
||
|
}
|
||
|
SetConsoleTextAttribute(pConsole, Color);
|
||
|
WriteConsoleW(pConsole, pWide, WLen + 1, NULL, NULL);
|
||
|
free(pWide);
|
||
|
}
|
||
|
};
|
||
|
#endif
|
||
|
|
||
|
std::unique_ptr<ILogger> log_logger_stdout()
|
||
|
{
|
||
|
#if !defined(CONF_FAMILY_WINDOWS)
|
||
|
// TODO: Only enable true color when COLORTERM contains "truecolor".
|
||
|
// https://github.com/termstandard/colors/tree/65bf0cd1ece7c15fa33a17c17528b02c99f1ae0b#checking-for-colorterm
|
||
|
return std::unique_ptr<ILogger>(new CLoggerAsync(io_stdout(), getenv("NO_COLOR") == nullptr, false));
|
||
|
#else
|
||
|
return std::unique_ptr<ILogger>(new CWindowsConsoleLogger());
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#if defined(CONF_FAMILY_WINDOWS)
|
||
|
class CLoggerWindowsDebugger : public ILogger
|
||
|
{
|
||
|
public:
|
||
|
void Log(const CLogMessage *pMessage) override
|
||
|
{
|
||
|
WCHAR aWBuffer[4096];
|
||
|
MultiByteToWideChar(CP_UTF8, 0, pMessage->m_aLine, -1, aWBuffer, sizeof(aWBuffer) / sizeof(WCHAR));
|
||
|
OutputDebugStringW(aWBuffer);
|
||
|
}
|
||
|
};
|
||
|
std::unique_ptr<ILogger> log_logger_windows_debugger()
|
||
|
{
|
||
|
return std::unique_ptr<ILogger>(new CLoggerWindowsDebugger());
|
||
|
}
|
||
|
#else
|
||
|
std::unique_ptr<ILogger> log_logger_windows_debugger()
|
||
|
{
|
||
|
dbg_assert(0, "Windows Debug logger on non-Windows");
|
||
|
return nullptr;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void CFutureLogger::Set(std::unique_ptr<ILogger> &&pLogger)
|
||
|
{
|
||
|
ILogger *null = nullptr;
|
||
|
m_PendingLock.lock();
|
||
|
ILogger *pLoggerRaw = pLogger.release();
|
||
|
if(!m_pLogger.compare_exchange_strong(null, pLoggerRaw, std::memory_order_acq_rel))
|
||
|
{
|
||
|
dbg_assert(false, "future logger has already been set and can only be set once");
|
||
|
}
|
||
|
for(const auto &Pending : m_aPending)
|
||
|
{
|
||
|
pLoggerRaw->Log(&Pending);
|
||
|
}
|
||
|
m_aPending.clear();
|
||
|
m_aPending.shrink_to_fit();
|
||
|
m_PendingLock.unlock();
|
||
|
}
|
||
|
|
||
|
void CFutureLogger::Log(const CLogMessage *pMessage)
|
||
|
{
|
||
|
ILogger *pLogger = m_pLogger.load(std::memory_order_acquire);
|
||
|
if(pLogger)
|
||
|
{
|
||
|
pLogger->Log(pMessage);
|
||
|
return;
|
||
|
}
|
||
|
m_PendingLock.lock();
|
||
|
m_aPending.push_back(*pMessage);
|
||
|
m_PendingLock.unlock();
|
||
|
}
|
||
|
|
||
|
void CFutureLogger::GlobalFinish()
|
||
|
{
|
||
|
ILogger *pLogger = m_pLogger.load(std::memory_order_acquire);
|
||
|
if(pLogger)
|
||
|
{
|
||
|
pLogger->GlobalFinish();
|
||
|
}
|
||
|
}
|