@@ -2,8 +2,11 @@ package client
22
33import (
44 "flag"
5+ "fmt"
56 "io"
67 "os"
8+ "path/filepath"
9+ "regexp"
710 "strconv"
811 "strings"
912
@@ -14,6 +17,33 @@ import (
1417 "github.com/spf13/viper"
1518)
1619
20+ const (
21+ logFileName = "albiondata-client.log"
22+ maxLogFiles = 10
23+ )
24+
25+ // ansiStripWriter wraps an io.Writer and strips ANSI escape codes before writing
26+ type ansiStripWriter struct {
27+ writer io.Writer
28+ regex * regexp.Regexp
29+ }
30+
31+ // newAnsiStripWriter creates a writer that strips ANSI escape codes
32+ func newAnsiStripWriter (w io.Writer ) * ansiStripWriter {
33+ return & ansiStripWriter {
34+ writer : w ,
35+ // Matches ANSI escape sequences like \x1b[0m, \x1b[36m, etc.
36+ regex : regexp .MustCompile (`\x1b\[[0-9;]*m` ),
37+ }
38+ }
39+
40+ func (w * ansiStripWriter ) Write (p []byte ) (n int , err error ) {
41+ stripped := w .regex .ReplaceAll (p , []byte {})
42+ _ , err = w .writer .Write (stripped )
43+ // Return original length to satisfy io.Writer contract
44+ return len (p ), err
45+ }
46+
1747type config struct {
1848 AllowedWSHosts []string
1949 Debug bool
@@ -29,7 +59,6 @@ type config struct {
2959 EnableWebsockets bool
3060 ListenDevices string
3161 LogLevel string
32- LogToFile bool
3362 Minimize bool
3463 Offline bool
3564 OfflinePath string
@@ -179,13 +208,6 @@ func (config *config) setupCommonFlags() {
179208 "Listen on this comma separated devices instead of all available. (Windows: Use MAC-Address, Linux: Use interface name)" ,
180209 )
181210
182- flag .BoolVar (
183- & config .LogToFile ,
184- "output-file" ,
185- false ,
186- "Enable logging to file." ,
187- )
188-
189211 flag .StringVar (
190212 & config .OfflinePath ,
191213 "o" ,
@@ -237,19 +259,53 @@ func (config *config) setupLogs() {
237259
238260 log .SetLevel (level )
239261
240- if config .LogToFile {
241- log .SetFormatter (& logrus.TextFormatter {DisableTimestamp : true , DisableSorting : true , ForceColors : false })
242- f , err := os .OpenFile ("albiondata-client-output.txt" , os .O_WRONLY | os .O_TRUNC | os .O_CREATE , 0755 )
243- if err == nil {
244- multiWriter := io .MultiWriter (os .Stdout , f )
245- log .SetOutput (multiWriter )
246- } else {
247- log .SetOutput (os .Stdout )
248- }
262+ // Rotate existing log files before creating new one
263+ rotateLogFiles ()
264+
265+ // Always log to both file and terminal
266+ // Use colors for terminal, strip ANSI codes for file
267+ log .SetFormatter (& logrus.TextFormatter {FullTimestamp : true , DisableSorting : true , ForceColors : true })
268+ f , err := os .OpenFile (logFileName , os .O_WRONLY | os .O_TRUNC | os .O_CREATE , 0644 )
269+ if err == nil {
270+ // Wrap file writer to strip ANSI codes
271+ strippedFileWriter := newAnsiStripWriter (f )
272+ multiWriter := io .MultiWriter (colorable .NewColorableStdout (), strippedFileWriter )
273+ log .SetOutput (multiWriter )
249274 } else {
250- log .SetFormatter (& logrus.TextFormatter {FullTimestamp : true , DisableSorting : true , ForceColors : true })
251275 log .SetOutput (colorable .NewColorableStdout ())
276+ log .Warnf ("Could not create log file: %v" , err )
277+ }
278+ }
279+
280+ // rotateLogFiles moves the current log file to a numbered backup and removes old backups
281+ func rotateLogFiles () {
282+ // Check if current log file exists
283+ if _ , err := os .Stat (logFileName ); os .IsNotExist (err ) {
284+ return // No log file to rotate
285+ }
286+
287+ // Remove the oldest log file if we're at the limit
288+ oldestLog := fmt .Sprintf ("%s.%d" , logFileName , maxLogFiles )
289+ _ = os .Remove (oldestLog )
290+
291+ // Shift all existing log files up by one number
292+ for i := maxLogFiles - 1 ; i >= 1 ; i -- {
293+ oldName := fmt .Sprintf ("%s.%d" , logFileName , i )
294+ newName := fmt .Sprintf ("%s.%d" , logFileName , i + 1 )
295+ _ = os .Rename (oldName , newName )
296+ }
297+
298+ // Rename current log file to .1
299+ _ = os .Rename (logFileName , fmt .Sprintf ("%s.1" , logFileName ))
300+ }
301+
302+ // GetLogFilePath returns the full path to the current log file
303+ func GetLogFilePath () string {
304+ absPath , err := filepath .Abs (logFileName )
305+ if err != nil {
306+ return logFileName
252307 }
308+ return absPath
253309}
254310
255311func (config * config ) setupDebugEvents () {
0 commit comments