update: implement enhanced logging functionality with configurable log levels

This commit is contained in:
kuwacom 2026-03-05 07:43:37 +09:00
parent 6d44e40b4e
commit 1d9dccb7bd
4 changed files with 302 additions and 30 deletions

View file

@ -3,9 +3,184 @@
#include "ServerLogger.h"
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
namespace ServerRuntime
{
static volatile LONG g_minLogLevel = (LONG)eServerLogLevel_Info;
static const char *NormalizeCategory(const char *category)
{
if (category == NULL || category[0] == 0)
{
return "server";
}
return category;
}
static const char *LogLevelToString(EServerLogLevel level)
{
switch (level)
{
case eServerLogLevel_Debug:
return "DEBUG";
case eServerLogLevel_Info:
return "INFO";
case eServerLogLevel_Warn:
return "WARN";
case eServerLogLevel_Error:
return "ERROR";
default:
return "INFO";
}
}
static WORD LogLevelToColor(EServerLogLevel level)
{
switch (level)
{
case eServerLogLevel_Debug:
return FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
case eServerLogLevel_Warn:
return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
case eServerLogLevel_Error:
return FOREGROUND_RED | FOREGROUND_INTENSITY;
case eServerLogLevel_Info:
default:
return FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY;
}
}
static void BuildTimestamp(char *buffer, size_t bufferSize)
{
if (buffer == NULL || bufferSize == 0)
{
return;
}
SYSTEMTIME localTime;
GetLocalTime(&localTime);
sprintf_s(
buffer,
bufferSize,
"%04u-%02u-%02u %02u:%02u:%02u.%03u",
(unsigned)localTime.wYear,
(unsigned)localTime.wMonth,
(unsigned)localTime.wDay,
(unsigned)localTime.wHour,
(unsigned)localTime.wMinute,
(unsigned)localTime.wSecond,
(unsigned)localTime.wMilliseconds);
}
static bool ShouldLog(EServerLogLevel level)
{
return ((LONG)level >= g_minLogLevel);
}
static void WriteLogLine(EServerLogLevel level, const char *category, const char *message)
{
if (!ShouldLog(level))
{
return;
}
const char *safeCategory = NormalizeCategory(category);
const char *safeMessage = (message != NULL) ? message : "";
char timestamp[32] = {};
BuildTimestamp(timestamp, sizeof(timestamp));
HANDLE stdoutHandle = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO originalInfo;
bool hasColorConsole = false;
if (stdoutHandle != INVALID_HANDLE_VALUE && stdoutHandle != NULL)
{
if (GetConsoleScreenBufferInfo(stdoutHandle, &originalInfo))
{
hasColorConsole = true;
SetConsoleTextAttribute(stdoutHandle, LogLevelToColor(level));
}
}
printf(
"[%s][%s][%s] %s\n",
timestamp,
LogLevelToString(level),
safeCategory,
safeMessage);
fflush(stdout);
if (hasColorConsole)
{
SetConsoleTextAttribute(stdoutHandle, originalInfo.wAttributes);
}
}
static void WriteLogLineV(EServerLogLevel level, const char *category, const char *format, va_list args)
{
char messageBuffer[2048] = {};
if (format == NULL)
{
WriteLogLine(level, category, "");
return;
}
vsnprintf_s(messageBuffer, sizeof(messageBuffer), _TRUNCATE, format, args);
WriteLogLine(level, category, messageBuffer);
}
bool TryParseServerLogLevel(const char *value, EServerLogLevel *outLevel)
{
if (value == NULL || outLevel == NULL)
{
return false;
}
if (_stricmp(value, "debug") == 0)
{
*outLevel = eServerLogLevel_Debug;
return true;
}
if (_stricmp(value, "info") == 0)
{
*outLevel = eServerLogLevel_Info;
return true;
}
if (_stricmp(value, "warn") == 0 || _stricmp(value, "warning") == 0)
{
*outLevel = eServerLogLevel_Warn;
return true;
}
if (_stricmp(value, "error") == 0)
{
*outLevel = eServerLogLevel_Error;
return true;
}
return false;
}
void SetServerLogLevel(EServerLogLevel level)
{
if (level < eServerLogLevel_Debug)
{
level = eServerLogLevel_Debug;
}
else if (level > eServerLogLevel_Error)
{
level = eServerLogLevel_Error;
}
g_minLogLevel = (LONG)level;
}
EServerLogLevel GetServerLogLevel()
{
return (EServerLogLevel)g_minLogLevel;
}
std::string WideToUtf8(const std::wstring &value)
{
if (value.empty())
@ -53,22 +228,71 @@ std::wstring Utf8ToWide(const char *value)
return wide;
}
void LogDebug(const char *category, const char *message)
{
WriteLogLine(eServerLogLevel_Debug, category, message);
}
void LogInfo(const char *category, const char *message)
{
WriteLogLine(eServerLogLevel_Info, category, message);
}
void LogWarn(const char *category, const char *message)
{
WriteLogLine(eServerLogLevel_Warn, category, message);
}
void LogError(const char *category, const char *message)
{
WriteLogLine(eServerLogLevel_Error, category, message);
}
void LogDebugf(const char *category, const char *format, ...)
{
va_list args;
va_start(args, format);
WriteLogLineV(eServerLogLevel_Debug, category, format, args);
va_end(args);
}
void LogInfof(const char *category, const char *format, ...)
{
va_list args;
va_start(args, format);
WriteLogLineV(eServerLogLevel_Info, category, format, args);
va_end(args);
}
void LogWarnf(const char *category, const char *format, ...)
{
va_list args;
va_start(args, format);
WriteLogLineV(eServerLogLevel_Warn, category, format, args);
va_end(args);
}
void LogErrorf(const char *category, const char *format, ...)
{
va_list args;
va_start(args, format);
WriteLogLineV(eServerLogLevel_Error, category, format, args);
va_end(args);
}
void LogStartupStep(const char *message)
{
printf("[startup] %s\n", message);
fflush(stdout);
LogInfo("startup", message);
}
void LogWorldIO(const char *message)
{
printf("[world-io] %s\n", message);
fflush(stdout);
LogInfo("world-io", message);
}
void LogWorldName(const char *prefix, const std::wstring &name)
{
std::string utf8 = WideToUtf8(name);
printf("[world-io] %s: %s\n", prefix, utf8.c_str());
fflush(stdout);
LogInfof("world-io", "%s: %s", (prefix != NULL) ? prefix : "name", utf8.c_str());
}
}

View file

@ -4,9 +4,40 @@
namespace ServerRuntime
{
enum EServerLogLevel
{
eServerLogLevel_Debug = 0,
eServerLogLevel_Info = 1,
eServerLogLevel_Warn = 2,
eServerLogLevel_Error = 3
};
/**
* `debug`/`info`/`warn`/`error`
*
* @param value
* @param outLevel
* @return `true`
*/
bool TryParseServerLogLevel(const char *value, EServerLogLevel *outLevel);
void SetServerLogLevel(EServerLogLevel level);
EServerLogLevel GetServerLogLevel();
std::string WideToUtf8(const std::wstring &value);
std::wstring Utf8ToWide(const char *value);
void LogDebug(const char *category, const char *message);
void LogInfo(const char *category, const char *message);
void LogWarn(const char *category, const char *message);
void LogError(const char *category, const char *message);
/** 指定レベル・カテゴリでフォーマットログを出力する */
void LogDebugf(const char *category, const char *format, ...);
void LogInfof(const char *category, const char *format, ...);
void LogWarnf(const char *category, const char *format, ...);
void LogErrorf(const char *category, const char *format, ...);
void LogStartupStep(const char *message);
void LogWorldIO(const char *message);
void LogWorldName(const char *prefix, const std::wstring &name);

View file

@ -56,6 +56,7 @@ struct DedicatedServerConfig
char name[17];
int maxPlayers;
__int64 seed;
ServerRuntime::EServerLogLevel logLevel;
bool hasSeed;
bool showHelp;
};
@ -96,21 +97,28 @@ static int WaitForServerStoppedThreadProc(void *)
static void PrintUsage()
{
printf("Minecraft.Server.exe [options]\n");
printf(" -port <1-65535> Listen TCP port (default: 25565)\n");
printf(" -ip <addr> Bind address (default: 0.0.0.0)\n");
printf(" -bind <addr> Alias of -ip\n");
printf(" -name <name> Host display name (max 16 chars)\n");
printf(" -maxplayers <1-8> Public slots (default: 8)\n");
printf(" -seed <int64> World seed\n");
printf(" -help Show this help\n");
ServerRuntime::LogInfo("usage", "Minecraft.Server.exe [options]");
ServerRuntime::LogInfo("usage", " -port <1-65535> Listen TCP port (default: 25565)");
ServerRuntime::LogInfo("usage", " -ip <addr> Bind address (default: 0.0.0.0)");
ServerRuntime::LogInfo("usage", " -bind <addr> Alias of -ip");
ServerRuntime::LogInfo("usage", " -name <name> Host display name (max 16 chars)");
ServerRuntime::LogInfo("usage", " -maxplayers <1-8> Public slots (default: 8)");
ServerRuntime::LogInfo("usage", " -seed <int64> World seed");
ServerRuntime::LogInfo("usage", " -loglevel <level> debug|info|warn|error (default: info)");
ServerRuntime::LogInfo("usage", " -help Show this help");
}
using ServerRuntime::LoadServerPropertiesConfig;
using ServerRuntime::LogError;
using ServerRuntime::LogErrorf;
using ServerRuntime::LogInfof;
using ServerRuntime::LogStartupStep;
using ServerRuntime::LogWarn;
using ServerRuntime::LogWorldIO;
using ServerRuntime::SaveServerPropertiesConfig;
using ServerRuntime::SetServerLogLevel;
using ServerRuntime::ServerPropertiesConfig;
using ServerRuntime::TryParseServerLogLevel;
using ServerRuntime::WideToUtf8;
using ServerRuntime::BootstrapWorldForServer;
using ServerRuntime::eWorldBootstrap_Failed;
@ -161,7 +169,7 @@ static bool ParseCommandLine(int argc, char **argv, DedicatedServerConfig *confi
int port = 0;
if (!ParseIntArg(argv[++i], &port) || port <= 0 || port > 65535)
{
printf("Invalid -port value.\n");
LogError("startup", "Invalid -port value.");
return false;
}
config->port = port;
@ -179,7 +187,7 @@ static bool ParseCommandLine(int argc, char **argv, DedicatedServerConfig *confi
int maxPlayers = 0;
if (!ParseIntArg(argv[++i], &maxPlayers) || maxPlayers <= 0 || maxPlayers > MINECRAFT_NET_MAX_PLAYERS)
{
printf("Invalid -maxplayers value.\n");
LogError("startup", "Invalid -maxplayers value.");
return false;
}
config->maxPlayers = maxPlayers;
@ -188,14 +196,22 @@ static bool ParseCommandLine(int argc, char **argv, DedicatedServerConfig *confi
{
if (!ParseInt64Arg(argv[++i], &config->seed))
{
printf("Invalid -seed value.\n");
LogError("startup", "Invalid -seed value.");
return false;
}
config->hasSeed = true;
}
else if ((_stricmp(arg, "-loglevel") == 0) && (i + 1 < argc))
{
if (!TryParseServerLogLevel(argv[++i], &config->logLevel))
{
LogError("startup", "Invalid -loglevel value. Use debug/info/warn/error.");
return false;
}
}
else
{
printf("Unknown or incomplete argument: %s\n", arg);
LogErrorf("startup", "Unknown or incomplete argument: %s", arg);
return false;
}
}
@ -253,6 +269,7 @@ int main(int argc, char **argv)
strncpy_s(config.name, sizeof(config.name), "DedicatedServer", _TRUNCATE);
config.maxPlayers = MINECRAFT_NET_MAX_PLAYERS;
config.seed = 0;
config.logLevel = ServerRuntime::eServerLogLevel_Info;
config.hasSeed = false;
config.showHelp = false;
@ -267,6 +284,7 @@ int main(int argc, char **argv)
return 0;
}
SetServerLogLevel(config.logLevel);
LogStartupStep("initializing process state");
SetConsoleCtrlHandler(ConsoleCtrlHandlerProc, TRUE);
SetExeWorkingDirectory();
@ -289,7 +307,7 @@ int main(int argc, char **argv)
LogStartupStep("creating hidden window");
if (!InitInstance(hInstance, SW_HIDE))
{
printf("Failed to create window instance.\n");
LogError("startup", "Failed to create window instance.");
return 2;
}
ShowWindow(g_hWnd, SW_HIDE);
@ -297,7 +315,7 @@ int main(int argc, char **argv)
LogStartupStep("initializing graphics device wrappers");
if (FAILED(InitDevice()))
{
printf("Failed to initialize D3D device.\n");
LogError("startup", "Failed to initialize D3D device.");
CleanupDevice();
return 2;
}
@ -353,7 +371,7 @@ int main(int argc, char **argv)
Minecraft *minecraft = Minecraft::GetInstance();
if (minecraft == NULL)
{
printf("Minecraft initialization failed.\n");
LogError("startup", "Minecraft initialization failed.");
CleanupDevice();
return 3;
}
@ -411,7 +429,7 @@ int main(int argc, char **argv)
}
else if (worldBootstrap.status == eWorldBootstrap_Failed)
{
printf("Failed to load configured world \"%s\".\n", WideToUtf8(targetWorldName).c_str());
LogErrorf("world-io", "Failed to load configured world \"%s\".", WideToUtf8(targetWorldName).c_str());
WinsockNetLayer::Shutdown();
g_NetworkManager.Terminate();
CleanupDevice();
@ -446,7 +464,7 @@ int main(int argc, char **argv)
if (startupResult != 0)
{
printf("Failed to start dedicated server (code %d).\n", startupResult);
LogErrorf("startup", "Failed to start dedicated server (code %d).", startupResult);
WinsockNetLayer::Shutdown();
g_NetworkManager.Terminate();
CleanupDevice();
@ -454,14 +472,14 @@ int main(int argc, char **argv)
}
LogStartupStep("server startup complete");
printf("Dedicated server listening on %s:%d\n", g_Win64MultiplayerIP, g_Win64MultiplayerPort);
LogInfof("startup", "Dedicated server listening on %s:%d", g_Win64MultiplayerIP, g_Win64MultiplayerPort);
DWORD nextAutosaveTick = GetTickCount() + kAutosaveIntervalMs;
bool autosaveRequested = false;
while (!g_shutdownRequested && !app.m_bShutdown)
{
TickCoreSystems();
app.HandleXuiActions();
HandleXuiActions();
if (autosaveRequested && app.GetXuiServerAction(kServerActionPad) == eXuiServerAction_Idle)
{
@ -489,7 +507,7 @@ int main(int argc, char **argv)
Sleep(10);
}
printf("Stopping dedicated server...\n");
LogStartupStep("stopping dedicated server");
MinecraftServer *server = MinecraftServer::getInstance();
if (server != NULL)
{
@ -503,7 +521,7 @@ int main(int argc, char **argv)
if (!WaitForWorldActionIdle(kServerActionPad, 15000, &TickCoreSystems, &HandleXuiActions))
{
LogWorldIO("shutdown save timed out");
printf("Timed out waiting for shutdown save action to finish.\n");
LogWarn("world-io", "Timed out waiting for shutdown save action to finish.");
}
else
{

View file

@ -68,8 +68,7 @@ static void SetStorageSaveUniqueFilename(const std::string &saveFilename)
static void LogSaveFilename(const char *prefix, const std::string &saveFilename)
{
printf("[world-io] %s: %s\n", prefix, saveFilename.c_str());
fflush(stdout);
LogInfof("world-io", "%s: %s", (prefix != NULL) ? prefix : "save-filename", saveFilename.c_str());
}
static void LogEnumeratedSaveInfo(int index, const SAVE_INFO &saveInfo)
@ -87,7 +86,7 @@ static void LogEnumeratedSaveInfo(int index, const SAVE_INFO &saveInfo)
index,
titleUtf8.c_str(),
filenameUtf8.c_str());
LogWorldIO(logLine);
LogDebug("world-io", logLine);
}
/**