diff --git a/.gitignore b/.gitignore index ecfce688..ca2aa0b9 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,4 @@ frontend/node_modules frontend/dist frontend/build UIMod/onboard_bundled/v2 +UIMod/logs/* \ No newline at end of file diff --git a/UIMod/onboard_bundled/localization/en-US.json b/UIMod/onboard_bundled/localization/en-US.json index bda9e741..9426d739 100644 --- a/UIMod/onboard_bundled/localization/en-US.json +++ b/UIMod/onboard_bundled/localization/en-US.json @@ -60,7 +60,7 @@ "UIText_UPNPEnabled": "UPNP Enabled", "UIText_UPNPEnabledInfo": "Enable automatic UPNP port forwarding", "UIText_LocalIpAddress": "Local IP Address", - "UIText_LocalIpAddressInfo": "IP address to bind to", + "UIText_LocalIpAddressInfo": "IP address to bind to. Recommended to leave at 0.0.0.0 unless you really know what you're doing.", "UIText_StartLocalHost": "Start Local Host", "UIText_StartLocalHostInfo": "Keep this true. This is required for the server to work.", "UIText_ServerVisible": "Server Visible", @@ -84,7 +84,12 @@ "UIText_AllowAutoGameServerUpdates": "Enable Auto Game Server Updates", "UIText_AllowAutoGameServerUpdatesInfo": "Allow the gameserver to automatically query for and update to the latest version. Attention: Restarts the server when a new version was found and installed. Will send multiple warning messages to the sever with SAY commands 60-10 seconds before the restart.", "UIText_AutoStartServerOnStartup": "Auto Start Server on Startup", - "UIText_AutoStartServerOnStartupInfo": "Automatically start the gameserver when the SSUI is started. Defaults to false." + "UIText_AutoStartServerOnStartupInfo": "Automatically start the gameserver when the SSUI is started. Defaults to false.", + "UIText_CreateSSUILogFile": "Create SSUI Log Files", + "UIText_CreateSSUILogFileInfo": "Create log files for the SSUI log in UIMod/logs. Defaults to false.", + "UIText_CreateGameServerLogFile": "Create Game Server Log File", + "UIText_CreateGameServerLogFileInfo": "Create a unique log file per server start for the gameserver in UIMod/logs. Defaults to false." + }, "terrain": { "UIText_TerrainSettingsHeader": "Optional world generation settings", diff --git a/UIMod/onboard_bundled/ui/config.html b/UIMod/onboard_bundled/ui/config.html index b080d61c..b6cd90cc 100644 --- a/UIMod/onboard_bundled/ui/config.html +++ b/UIMod/onboard_bundled/ui/config.html @@ -256,6 +256,24 @@

{{.UIText_AdvancedConfiguration}}

{{.UIText_AllowAutoGameServerUpdatesInfo}}
+ +
+ + +
{{.UIText_CreateSSUILogFileInfo}}
+
+ +
+ + +
{{.UIText_CreateGameServerLogFileInfo}}
+
diff --git a/src/config/config.go b/src/config/config.go index b4d1d1a9..c7ffdb01 100644 --- a/src/config/config.go +++ b/src/config/config.go @@ -51,11 +51,12 @@ type JsonConfig struct { StartLocation string `json:"StartLocation"` // Logging and debug settings - Debug *bool `json:"Debug"` - CreateSSUILogFile *bool `json:"CreateSSUILogFile"` - LogLevel int `json:"LogLevel"` - SubsystemFilters []string `json:"subsystemFilters"` - AdvertiserOverride string `json:"AdvertiserOverride"` + Debug *bool `json:"Debug"` + CreateSSUILogFile *bool `json:"CreateSSUILogFile"` + CreateGameServerLogFile *bool `json:"CreateGameServerLogFile"` + LogLevel int `json:"LogLevel"` + SubsystemFilters []string `json:"subsystemFilters"` + AdvertiserOverride string `json:"AdvertiserOverride"` // Authentication Settings Users map[string]string `json:"users"` // Map of username to hashed password @@ -230,6 +231,10 @@ func applyConfig(cfg *JsonConfig) { CreateSSUILogFile = createSSUILogFileVal cfg.CreateSSUILogFile = &createSSUILogFileVal + createGameServerLogFileVal := getBool(cfg.CreateGameServerLogFile, "CREATE_GAMESERVER_LOGFILE", false) + CreateGameServerLogFile = createGameServerLogFileVal + cfg.CreateGameServerLogFile = &createGameServerLogFileVal + LogLevel = getInt(cfg.LogLevel, "LOG_LEVEL", 20) isUpdateEnabledVal := getBool(cfg.IsUpdateEnabled, "IS_UPDATE_ENABLED", true) @@ -369,6 +374,7 @@ func safeSaveConfig() error { AuthTokenLifetime: AuthTokenLifetime, Debug: &IsDebugMode, CreateSSUILogFile: &CreateSSUILogFile, + CreateGameServerLogFile: &CreateGameServerLogFile, LogLevel: LogLevel, LogClutterToConsole: &LogClutterToConsole, SubsystemFilters: SubsystemFilters, diff --git a/src/config/getters.go b/src/config/getters.go index a71d7ab0..cd637768 100644 --- a/src/config/getters.go +++ b/src/config/getters.go @@ -297,6 +297,12 @@ func GetCreateSSUILogFile() bool { return CreateSSUILogFile } +func GetCreateGameServerLogFile() bool { + ConfigMu.RLock() + defer ConfigMu.RUnlock() + return CreateGameServerLogFile +} + func GetLogLevel() int { ConfigMu.RLock() defer ConfigMu.RUnlock() diff --git a/src/config/vars.go b/src/config/vars.go index f1e6e192..3aafbbff 100644 --- a/src/config/vars.go +++ b/src/config/vars.go @@ -47,6 +47,7 @@ var ( var ( IsDebugMode bool //only used for pprof server, keep it like this and check the log level instead. Debug = 10 CreateSSUILogFile bool + CreateGameServerLogFile bool LogLevel int IsFirstTimeSetup bool SSEMessageBufferSize = 2000 diff --git a/src/managers/gamemgr/serverlog.go b/src/managers/gamemgr/serverlog.go index fc950cff..31d7f694 100644 --- a/src/managers/gamemgr/serverlog.go +++ b/src/managers/gamemgr/serverlog.go @@ -6,14 +6,20 @@ import ( "io" "os" "os/exec" + "path" "runtime" "strconv" "time" + "github.com/JacksonTheMaster/StationeersServerUI/v5/src/config" "github.com/JacksonTheMaster/StationeersServerUI/v5/src/core/ssestream" "github.com/JacksonTheMaster/StationeersServerUI/v5/src/logger" + "github.com/google/uuid" ) +const defaultLogFolderMode = 0755 +const defaultLogFileMode = 0644 + // readPipe for Windows func readPipe(pipe io.ReadCloser) { scanner := bufio.NewScanner(pipe) @@ -21,6 +27,7 @@ func readPipe(pipe io.ReadCloser) { for scanner.Scan() { output := scanner.Text() ssestream.BroadcastConsoleOutput(output) + logToFile(output) } if err := scanner.Err(); err != nil { logger.Core.Debug("Pipe error: " + err.Error()) @@ -91,6 +98,7 @@ func tailLogFile(logFilePath string) { for scanner.Scan() { output := scanner.Text() ssestream.BroadcastConsoleOutput(output) + logToFile(output) } if err := scanner.Err(); err != nil { @@ -106,3 +114,43 @@ func tailLogFile(logFilePath string) { logger.Core.Debug("Received logDone signal, stopping tail -F") } + +func logToFile(message string) { + if config.GetCreateGameServerLogFile() { + logFileFolder := config.GetLogFolder() + // Check if the log folder exists, if not create it + _, err := os.Stat(logFileFolder) + if os.IsNotExist(err) { + err := os.Mkdir(logFileFolder, defaultLogFolderMode) + if err != nil { + logger.Core.Error("Error creating log folder: " + err.Error()) + return + } + } else if err != nil { + logger.Core.Error("Error checking log folder: " + err.Error()) + return + } + + if GameServerUUID != uuid.Nil { + logFileName := fmt.Sprintf("serverlog_%s_%s.log", time.Now().Format("200601021504"), GameServerUUID.String()) + logFilePath := path.Join(logFileFolder, logFileName) + + // append the log file to the log folder + file, err := os.OpenFile(logFilePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, defaultLogFileMode) + if err != nil { + logger.Core.Error("Error opening log file: " + err.Error()) + return + } + defer file.Close() + + _, err = file.WriteString(message + "\n") + if err != nil { + logger.Core.Error("Error writing to log file: " + err.Error()) + return + } + } else { + logger.Core.Error("Game Server UUID not set, cannot log to file") + return + } + } +} diff --git a/src/web/configpage.go b/src/web/configpage.go index a7e77269..a03598fb 100644 --- a/src/web/configpage.go +++ b/src/web/configpage.go @@ -105,6 +105,22 @@ func ServeConfigPage(w http.ResponseWriter, r *http.Request) { autoGameServerUpdatesFalseSelected = "selected" } + createSSUILogFileTrueSelected := "" + createSSUILogFileFalseSelected := "" + if config.GetCreateSSUILogFile() { + createSSUILogFileTrueSelected = "selected" + } else { + createSSUILogFileFalseSelected = "selected" + } + + createGameServerLogFileTrueSelected := "" + createGameServerLogFileFalseSelected := "" + if config.GetCreateGameServerLogFile() { + createGameServerLogFileTrueSelected = "selected" + } else { + createGameServerLogFileFalseSelected = "selected" + } + data := ConfigTemplateData{ // Config values DiscordToken: config.GetDiscordToken(), @@ -163,6 +179,12 @@ func ServeConfigPage(w http.ResponseWriter, r *http.Request) { AllowAutoGameServerUpdates: fmt.Sprintf("%v", config.GetAllowAutoGameServerUpdates()), AllowAutoGameServerUpdatesTrueSelected: autoGameServerUpdatesTrueSelected, AllowAutoGameServerUpdatesFalseSelected: autoGameServerUpdatesFalseSelected, + CreateSSUILogFile: fmt.Sprintf("%v", config.GetCreateSSUILogFile()), + CreateSSUILogFileTrueSelected: createSSUILogFileTrueSelected, + CreateSSUILogFileFalseSelected: createSSUILogFileFalseSelected, + CreateGameServerLogFile: fmt.Sprintf("%v", config.GetCreateGameServerLogFile()), + CreateGameServerLogFileTrueSelected: createGameServerLogFileTrueSelected, + CreateGameServerLogFileFalseSelected: createGameServerLogFileFalseSelected, // Localized UI text UIText_ServerConfig: localization.GetString("UIText_ServerConfig"), @@ -235,6 +257,10 @@ func ServeConfigPage(w http.ResponseWriter, r *http.Request) { UIText_AutoStartServerOnStartupInfo: localization.GetString("UIText_AutoStartServerOnStartupInfo"), UIText_AllowAutoGameServerUpdates: localization.GetString("UIText_AllowAutoGameServerUpdates"), UIText_AllowAutoGameServerUpdatesInfo: localization.GetString("UIText_AllowAutoGameServerUpdatesInfo"), + UIText_CreateSSUILogFile: localization.GetString("UIText_CreateSSUILogFile"), + UIText_CreateSSUILogFileInfo: localization.GetString("UIText_CreateSSUILogFileInfo"), + UIText_CreateGameServerLogFile: localization.GetString("UIText_CreateGameServerLogFile"), + UIText_CreateGameServerLogFileInfo: localization.GetString("UIText_CreateGameServerLogFileInfo"), UIText_DiscordIntegrationTitle: localization.GetString("UIText_DiscordIntegrationTitle"), UIText_DiscordBotToken: localization.GetString("UIText_DiscordBotToken"), diff --git a/src/web/templatevars.go b/src/web/templatevars.go index 8c0cc2ab..ad5d5a87 100644 --- a/src/web/templatevars.go +++ b/src/web/templatevars.go @@ -80,6 +80,12 @@ type ConfigTemplateData struct { AllowAutoGameServerUpdates string AllowAutoGameServerUpdatesTrueSelected string AllowAutoGameServerUpdatesFalseSelected string + CreateSSUILogFile string + CreateSSUILogFileTrueSelected string + CreateSSUILogFileFalseSelected string + CreateGameServerLogFile string + CreateGameServerLogFileTrueSelected string + CreateGameServerLogFileFalseSelected string UIText_ServerConfig string UIText_DiscordIntegration string @@ -153,6 +159,10 @@ type ConfigTemplateData struct { UIText_AutoStartServerOnStartupInfo string UIText_AllowAutoGameServerUpdates string UIText_AllowAutoGameServerUpdatesInfo string + UIText_CreateSSUILogFile string + UIText_CreateSSUILogFileInfo string + UIText_CreateGameServerLogFile string + UIText_CreateGameServerLogFileInfo string UIText_DiscordIntegrationTitle string UIText_DiscordBotToken string