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 "ServerLogger.h"
#include <stdio.h> #include <stdio.h>
#include <stdarg.h>
#include <string.h>
namespace ServerRuntime 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) std::string WideToUtf8(const std::wstring &value)
{ {
if (value.empty()) if (value.empty())
@ -53,22 +228,71 @@ std::wstring Utf8ToWide(const char *value)
return wide; 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) void LogStartupStep(const char *message)
{ {
printf("[startup] %s\n", message); LogInfo("startup", message);
fflush(stdout);
} }
void LogWorldIO(const char *message) void LogWorldIO(const char *message)
{ {
printf("[world-io] %s\n", message); LogInfo("world-io", message);
fflush(stdout);
} }
void LogWorldName(const char *prefix, const std::wstring &name) void LogWorldName(const char *prefix, const std::wstring &name)
{ {
std::string utf8 = WideToUtf8(name); std::string utf8 = WideToUtf8(name);
printf("[world-io] %s: %s\n", prefix, utf8.c_str()); LogInfof("world-io", "%s: %s", (prefix != NULL) ? prefix : "name", utf8.c_str());
fflush(stdout);
} }
} }

View file

@ -4,9 +4,40 @@
namespace ServerRuntime 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::string WideToUtf8(const std::wstring &value);
std::wstring Utf8ToWide(const char *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 LogStartupStep(const char *message);
void LogWorldIO(const char *message); void LogWorldIO(const char *message);
void LogWorldName(const char *prefix, const std::wstring &name); void LogWorldName(const char *prefix, const std::wstring &name);

View file

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

View file

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