@@ -2,9 +2,11 @@ package main
22
33import (
44 "flag"
5+ "io"
56 "os"
67 "path/filepath"
78 "runtime/debug"
9+ "strings"
810
911 "github.com/Unity-Technologies/multiplay-examples/simple-game-server-go/internal/game"
1012 "github.com/sirupsen/logrus"
@@ -15,29 +17,89 @@ func parseFlags(args []string) (string, string, string, error) {
1517 dir , _ := os .UserHomeDir ()
1618 f := flag .NewFlagSet ("simple-game-server-go" , flag .ContinueOnError )
1719
18- var log , logFile , tracebackLevel string
19- f .StringVar (& log , "log" , filepath .Join (dir , "logs" ), "path to the log directory to write to " )
20+ var logTargets , logFile , tracebackLevel string
21+ f .StringVar (& logTargets , "log" , "stdout," + filepath .Join (dir , "logs" ), "comma-separated log targets: 'stdout', file path, or directory " )
2022 f .StringVar (& logFile , "logFile" , "" , "path to the log file to write to" )
21- f .StringVar (
22- & tracebackLevel ,
23- "tracebackLevel" ,
24- "" ,
25- "the amount of detail printed by the runtime prints before exiting due to an unrecovered panic" ,
26- )
23+ f .StringVar (& tracebackLevel , "tracebackLevel" , "none" , "the amount of detail printed by the runtime prints before exiting due to an unrecovered panic" )
2724
2825 // Flags which are not used, but must be present to satisfy the default parameters in the Unity Dashboard.
2926 var port , queryPort uint
3027 f .UintVar (& port , "port" , 8000 , "port for the game server to bind to" )
3128 f .UintVar (& queryPort , "queryport" , 8001 , "port for the query endpoint to bind to" )
3229
33- return log , logFile , tracebackLevel , f .Parse (args )
30+ return logTargets , logFile , tracebackLevel , f .Parse (args )
31+ }
32+
33+ // logWritersFromTargets creates a multi-writer from the specified log targets and log file.
34+ // If no valid targets are provided, it defaults to writing to stdout.
35+ func logWritersFromTargets (logTargets string , logFile string , logger * logrus.Logger ) io.Writer {
36+ targets := make ([]io.Writer , 0 )
37+ for _ , t := range splitAndTrim (logTargets ) {
38+ switch t {
39+ case "stdout" :
40+ targets = append (targets , os .Stdout )
41+ case "stderr" :
42+ targets = append (targets , os .Stderr )
43+ default :
44+ // If it's a directory, use server.log inside it
45+ info , err := os .Stat (t )
46+ if err == nil && info .IsDir () {
47+ t = filepath .Join (t , "server.log" )
48+ }
49+ f , err := os .OpenFile (t , os .O_CREATE | os .O_WRONLY | os .O_APPEND , 0o666 )
50+ if err != nil {
51+ logger .WithError (err ).Warningf ("could not open log target %s for writing" , t )
52+ continue
53+ }
54+ targets = append (targets , f )
55+ }
56+ }
57+ // logFile takes precedence
58+ if logFile != "" {
59+ f , err := os .OpenFile (logFile , os .O_CREATE | os .O_WRONLY | os .O_APPEND , 0o666 )
60+ if err == nil {
61+ targets = append (targets , f )
62+ } else {
63+ logger .WithError (err ).Warning ("could not open log file for writing" )
64+ }
65+ }
66+ if len (targets ) == 0 {
67+ return os .Stdout
68+ }
69+ return io .MultiWriter (targets ... )
70+ }
71+
72+ // splitAndTrim splits a string by the OS-specific path list separator and trims each part.
73+ func splitAndTrim (s string ) []string {
74+ parts := make ([]string , 0 )
75+ for _ , p := range filepath .SplitList (s ) {
76+ for _ , t := range splitComma (p ) {
77+ trimmed := filepath .Clean (t )
78+ if trimmed != "" && trimmed != "." {
79+ parts = append (parts , trimmed )
80+ }
81+ }
82+ }
83+ return parts
84+ }
85+
86+ // splitComma splits a string by commas and trims each part, returning a slice of non-empty strings.
87+ func splitComma (s string ) []string {
88+ res := make ([]string , 0 )
89+ for _ , t := range strings .Split (s , "," ) {
90+ trimmed := strings .TrimSpace (t )
91+ if trimmed != "" && trimmed != "." {
92+ res = append (res , trimmed )
93+ }
94+ }
95+ return res
3496}
3597
3698func main () {
3799 logger := logrus .New ()
38100 logger .SetFormatter (& logrus.JSONFormatter {})
39101
40- log , logFile , tracebackLevel , err := parseFlags (os .Args [1 :])
102+ logTargets , logFile , tracebackLevel , err := parseFlags (os .Args [1 :])
41103 if err != nil {
42104 logger .WithError (err ).Fatal ("error parsing flags" )
43105 }
@@ -47,20 +109,7 @@ func main() {
47109 debug .SetTraceback (tracebackLevel )
48110 }
49111
50- // Let -logFile take precedence over -log
51- if logFile == "" && log != "" {
52- logFile = filepath .Join (log , "server.log" )
53- }
54-
55- if logFile != "" {
56- f , err := os .OpenFile (logFile , os .O_CREATE | os .O_WRONLY | os .O_APPEND , 0o666 )
57- if err == nil {
58- defer f .Close ()
59- logger .Out = f
60- } else {
61- logger .WithError (err ).Warning ("could not open log file for writing" )
62- }
63- }
112+ logger .Out = logWritersFromTargets (logTargets , logFile , logger )
64113
65114 g , err := game .New (logger )
66115 if err != nil {
0 commit comments