From f91003d077d809810bff1b2545465b34d9808882 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 14 Nov 2024 17:33:27 +0100 Subject: [PATCH] [service] Move logging to the core, remove pkg level logs --- base/api/endpoints_debug.go | 5 +- base/info/version.go | 22 ++++ base/log/flags.go | 13 --- base/log/input.go | 39 ++----- base/log/logging.go | 96 +++++++++-------- base/log/logging_test.go | 5 +- base/log/output.go | 162 ++++++++++++---------------- base/log/slog.go | 41 +++---- base/log/trace.go | 33 +----- base/log/writer.go | 119 ++++++++++++++++++++ cmds/hub/main.go | 5 +- cmds/observation-hub/main.go | 5 +- cmds/portmaster-core/main.go | 18 +++- cmds/portmaster-core/run.go | 16 ++- cmds/portmaster-core/run_linux.go | 7 +- cmds/portmaster-core/run_windows.go | 10 +- cmds/portmaster-core/update.go | 5 +- go.mod | 13 +-- go.sum | 18 ---- service/config.go | 23 +++- service/mgr/worker.go | 5 +- spn/instance.go | 6 ++ 22 files changed, 360 insertions(+), 306 deletions(-) delete mode 100644 base/log/flags.go create mode 100644 base/log/writer.go diff --git a/base/api/endpoints_debug.go b/base/api/endpoints_debug.go index 55865d9c..06de4cd5 100644 --- a/base/api/endpoints_debug.go +++ b/base/api/endpoints_debug.go @@ -12,6 +12,7 @@ import ( "time" "github.com/safing/portmaster/base/info" + "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/utils/debug" ) @@ -152,12 +153,12 @@ func getStack(_ *Request) (data []byte, err error) { // printStack prints the current goroutine stack to stderr. func printStack(_ *Request) (msg string, err error) { - _, err = fmt.Fprint(os.Stderr, "===== PRINTING STACK =====\n") + _, err = fmt.Fprint(log.GlobalWriter, "===== PRINTING STACK =====\n") if err == nil { err = pprof.Lookup("goroutine").WriteTo(os.Stderr, 1) } if err == nil { - _, err = fmt.Fprint(os.Stderr, "===== END OF STACK =====\n") + _, err = fmt.Fprint(log.GlobalWriter, "===== END OF STACK =====\n") } if err != nil { return "", err diff --git a/base/info/version.go b/base/info/version.go index 6140adc2..2c6c3058 100644 --- a/base/info/version.go +++ b/base/info/version.go @@ -153,6 +153,28 @@ func FullVersion() string { return builder.String() } +// CondensedVersion returns the rather complete, but condensed version string. +func CondensedVersion() string { + info := GetInfo() + + cgoInfo := "-cgo" + if info.CGO { + cgoInfo = "+cgo" + } + dirtyInfo := "clean" + if info.Dirty { + dirtyInfo = "dirty" + } + + return fmt.Sprintf( + "%s %s (%s; built with %s [%s %s] from %s [%s] at %s)", + info.Name, version, + runtime.GOOS, + runtime.Version(), runtime.Compiler, cgoInfo, + info.Commit, dirtyInfo, info.CommitTime, + ) +} + // CheckVersion checks if the metadata is ok. func CheckVersion() error { switch { diff --git a/base/log/flags.go b/base/log/flags.go deleted file mode 100644 index eb019297..00000000 --- a/base/log/flags.go +++ /dev/null @@ -1,13 +0,0 @@ -package log - -import "flag" - -var ( - logLevelFlag string - pkgLogLevelsFlag string -) - -func init() { - flag.StringVar(&logLevelFlag, "log", "", "set log level to [trace|debug|info|warning|error|critical]") - flag.StringVar(&pkgLogLevelsFlag, "plog", "", "set log level of packages: database=trace,notifications=debug") -} diff --git a/base/log/input.go b/base/log/input.go index ef8564a9..6dd4aa96 100644 --- a/base/log/input.go +++ b/base/log/input.go @@ -3,7 +3,6 @@ package log import ( "fmt" "runtime" - "strings" "sync/atomic" "time" ) @@ -25,6 +24,11 @@ func log(level Severity, msg string, tracer *ContextTracer) { return } + // Check log level. + if uint32(level) < atomic.LoadUint32(logLevel) { + return + } + // get time now := time.Now() @@ -41,31 +45,6 @@ func log(level Severity, msg string, tracer *ContextTracer) { } } - // check if level is enabled for file or generally - if pkgLevelsActive.IsSet() { - pathSegments := strings.Split(file, "/") - if len(pathSegments) < 2 { - // file too short for package levels - return - } - pkgLevelsLock.Lock() - severity, ok := pkgLevels[pathSegments[len(pathSegments)-2]] - pkgLevelsLock.Unlock() - if ok { - if level < severity { - return - } - } else { - // no package level set, check against global level - if uint32(level) < atomic.LoadUint32(logLevel) { - return - } - } - } else if uint32(level) < atomic.LoadUint32(logLevel) { - // no package levels set, check against global level - return - } - // create log object log := &logLine{ msg: msg, @@ -101,13 +80,7 @@ func log(level Severity, msg string, tracer *ContextTracer) { } func fastcheck(level Severity) bool { - if pkgLevelsActive.IsSet() { - return true - } - if uint32(level) >= atomic.LoadUint32(logLevel) { - return true - } - return false + return uint32(level) >= atomic.LoadUint32(logLevel) } // Trace is used to log tiny steps. Log traces to context if you can! diff --git a/base/log/logging.go b/base/log/logging.go index b859bf11..d9ed43d9 100644 --- a/base/log/logging.go +++ b/base/log/logging.go @@ -2,6 +2,7 @@ package log import ( "fmt" + "log/slog" "os" "strings" "sync" @@ -33,6 +34,26 @@ import ( // Severity describes a log level. type Severity uint32 +func (s Severity) toSLogLevel() slog.Level { + // Convert to slog level. + switch s { + case TraceLevel: + return slog.LevelDebug + case DebugLevel: + return slog.LevelDebug + case InfoLevel: + return slog.LevelInfo + case WarningLevel: + return slog.LevelWarn + case ErrorLevel: + return slog.LevelError + case CriticalLevel: + return slog.LevelError + } + // Failed to convert, return default log level + return slog.LevelWarn +} + // Message describes a log level message and is implemented // by logLine. type Message interface { @@ -105,10 +126,6 @@ var ( logLevelInt = uint32(InfoLevel) logLevel = &logLevelInt - pkgLevelsActive = abool.NewBool(false) - pkgLevels = make(map[string]Severity) - pkgLevelsLock sync.Mutex - logsWaiting = make(chan struct{}, 1) logsWaitingFlag = abool.NewBool(false) @@ -121,19 +138,6 @@ var ( startedSignal = make(chan struct{}) ) -// SetPkgLevels sets individual log levels for packages. Only effective after Start(). -func SetPkgLevels(levels map[string]Severity) { - pkgLevelsLock.Lock() - pkgLevels = levels - pkgLevelsLock.Unlock() - pkgLevelsActive.Set() -} - -// UnSetPkgLevels removes all individual log levels for packages. -func UnSetPkgLevels() { - pkgLevelsActive.UnSet() -} - // GetLogLevel returns the current log level. func GetLogLevel() Severity { return Severity(atomic.LoadUint32(logLevel)) @@ -187,47 +191,36 @@ func ParseLevel(level string) Severity { } // Start starts the logging system. Must be called in order to see logs. -func Start() (err error) { +func Start(level string, logToStdout bool, logDir string) (err error) { if !initializing.SetToIf(false, true) { return nil } - logBuffer = make(chan *logLine, 1024) - - if logLevelFlag != "" { - initialLogLevel := ParseLevel(logLevelFlag) + // Parse log level argument. + initialLogLevel := InfoLevel + if level != "" { + initialLogLevel = ParseLevel(level) if initialLogLevel == 0 { - fmt.Fprintf(os.Stderr, "log warning: invalid log level \"%s\", falling back to level info\n", logLevelFlag) + fmt.Fprintf(os.Stderr, "log warning: invalid log level %q, falling back to level info\n", level) initialLogLevel = InfoLevel } + } - SetLogLevel(initialLogLevel) + // Setup writer. + if logToStdout { + GlobalWriter = NewStdoutWriter() } else { - // Setup slog here for the transition period. - setupSLog(GetLogLevel()) + // Create file log writer. + var err error + GlobalWriter, err = NewFileWriter(logDir) + if err != nil { + return fmt.Errorf("failed to initialize log file: %w", err) + } } - // get and set file loglevels - pkgLogLevels := pkgLogLevelsFlag - if len(pkgLogLevels) > 0 { - newPkgLevels := make(map[string]Severity) - for _, pair := range strings.Split(pkgLogLevels, ",") { - splitted := strings.Split(pair, "=") - if len(splitted) != 2 { - err = fmt.Errorf("log warning: invalid file log level \"%s\", ignoring", pair) - fmt.Fprintf(os.Stderr, "%s\n", err.Error()) - break - } - fileLevel := ParseLevel(splitted[1]) - if fileLevel == 0 { - err = fmt.Errorf("log warning: invalid file log level \"%s\", ignoring", pair) - fmt.Fprintf(os.Stderr, "%s\n", err.Error()) - break - } - newPkgLevels[splitted[0]] = fileLevel - } - SetPkgLevels(newPkgLevels) - } + // Init logging systems. + SetLogLevel(initialLogLevel) + logBuffer = make(chan *logLine, 1024) if !schedulingEnabled { close(writeTrigger) @@ -237,6 +230,14 @@ func Start() (err error) { started.Set() close(startedSignal) + // Delete all logs older than one month. + if !logToStdout { + err = CleanOldLogs(logDir, 30*24*time.Hour) + if err != nil { + Errorf("log: failed to clean old log files: %s", err) + } + } + return err } @@ -246,4 +247,5 @@ func Shutdown() { close(shutdownSignal) } shutdownWaitGroup.Wait() + GlobalWriter.Close() } diff --git a/base/log/logging_test.go b/base/log/logging_test.go index 577ee51a..6996389a 100644 --- a/base/log/logging_test.go +++ b/base/log/logging_test.go @@ -7,7 +7,7 @@ import ( ) func init() { - err := Start() + err := Start("info", true, "") if err != nil { panic(fmt.Sprintf("start failed: %s", err)) } @@ -56,9 +56,6 @@ func TestLogging(t *testing.T) { // wait logs to be written time.Sleep(1 * time.Millisecond) - // just for show - UnSetPkgLevels() - // do not really shut down, we may need logging for other tests // ShutdownLogging() } diff --git a/base/log/output.go b/base/log/output.go index a2947dc5..91acd525 100644 --- a/base/log/output.go +++ b/base/log/output.go @@ -2,78 +2,25 @@ package log import ( "fmt" - "os" "runtime/debug" "sync" "time" + + "github.com/safing/portmaster/base/info" ) -type ( - // Adapter is used to write logs. - Adapter interface { - // Write is called for each log message. - Write(msg Message, duplicates uint64) - } - - // AdapterFunc is a convenience type for implementing - // Adapter. - AdapterFunc func(msg Message, duplicates uint64) - - // FormatFunc formats msg into a string. - FormatFunc func(msg Message, duplicates uint64) string - - // SimpleFileAdapter implements Adapter and writes all - // messages to File. - SimpleFileAdapter struct { - Format FormatFunc - File *os.File - } -) +// Adapter is used to write logs. +type Adapter interface { + // Write is called for each log message. + WriteMessage(msg Message, duplicates uint64) +} var ( - // StdoutAdapter is a simple file adapter that writes - // all logs to os.Stdout using a predefined format. - StdoutAdapter = &SimpleFileAdapter{ - File: os.Stdout, - Format: defaultColorFormater, - } - - // StderrAdapter is a simple file adapter that writes - // all logs to os.Stdout using a predefined format. - StderrAdapter = &SimpleFileAdapter{ - File: os.Stderr, - Format: defaultColorFormater, - } -) - -var ( - adapter Adapter = StdoutAdapter - schedulingEnabled = false writeTrigger = make(chan struct{}) ) -// SetAdapter configures the logging adapter to use. -// This must be called before the log package is initialized. -func SetAdapter(a Adapter) { - if initializing.IsSet() || a == nil { - return - } - - adapter = a -} - -// Write implements Adapter and calls fn. -func (fn AdapterFunc) Write(msg Message, duplicates uint64) { - fn(msg, duplicates) -} - -// Write implements Adapter and writes msg the underlying file. -func (fileAdapter *SimpleFileAdapter) Write(msg Message, duplicates uint64) { - fmt.Fprintln(fileAdapter.File, fileAdapter.Format(msg, duplicates)) -} - -// EnableScheduling enables external scheduling of the logger. This will require to manually trigger writes via TriggerWrite whenevery logs should be written. Please note that full buffers will also trigger writing. Must be called before Start() to have an effect. +// EnableScheduling enables external scheduling of the logger. This will require to manually trigger writes via TriggerWrite whenever logs should be written. Please note that full buffers will also trigger writing. Must be called before Start() to have an effect. func EnableScheduling() { if !initializing.IsSet() { schedulingEnabled = true @@ -95,27 +42,47 @@ func TriggerWriterChannel() chan struct{} { return writeTrigger } -func defaultColorFormater(line Message, duplicates uint64) string { - return formatLine(line.(*logLine), duplicates, true) //nolint:forcetypeassert // TODO: improve -} - func startWriter() { - fmt.Printf( - "%s%s%s %sBOF %s%s\n", + if GlobalWriter.isStdout { + fmt.Fprintf(GlobalWriter, + "%s%s%s %sBOF %s%s\n", - dimColor(), - time.Now().Format(timeFormat), - endDimColor(), + dimColor(), + time.Now().Format(timeFormat), + endDimColor(), - blueColor(), - rightArrow, - endColor(), - ) + blueColor(), + rightArrow, + endColor(), + ) + } else { + fmt.Fprintf(GlobalWriter, + "%s BOF %s\n", + time.Now().Format(timeFormat), + rightArrow, + ) + } + writeVersion() shutdownWaitGroup.Add(1) go writerManager() } +func writeVersion() { + if GlobalWriter.isStdout { + fmt.Fprintf(GlobalWriter, "%s%s%s running %s%s%s\n", + dimColor(), + time.Now().Format(timeFormat), + endDimColor(), + + blueColor(), + info.CondensedVersion(), + endColor()) + } else { + fmt.Fprintf(GlobalWriter, "%s running %s\n", time.Now().Format(timeFormat), info.CondensedVersion()) + } +} + func writerManager() { defer shutdownWaitGroup.Done() @@ -129,18 +96,17 @@ func writerManager() { } } -// defer should be able to edit the err. So naked return is required. -// nolint:golint,nakedret -func writer() (err error) { +func writer() error { + var err error defer func() { // recover from panic panicVal := recover() if panicVal != nil { - err = fmt.Errorf("%s", panicVal) + _, err = fmt.Fprintf(GlobalWriter, "%s", panicVal) // write stack to stderr fmt.Fprintf( - os.Stderr, + GlobalWriter, `===== Error Report ===== Message: %s StackTrace: @@ -169,7 +135,7 @@ StackTrace: case <-forceEmptyingOfBuffer: // log buffer is full! case <-shutdownSignal: // shutting down finalizeWriting() - return + return err } // wait for timeslot to log @@ -178,7 +144,7 @@ StackTrace: case <-forceEmptyingOfBuffer: // log buffer is full! case <-shutdownSignal: // shutting down finalizeWriting() - return + return err } // write all the logs! @@ -201,7 +167,7 @@ StackTrace: } // if currentLine and line are _not_ equal, output currentLine - adapter.Write(currentLine, duplicates) + GlobalWriter.WriteMessage(currentLine, duplicates) // add to unexpected logs addUnexpectedLogs(currentLine) // reset duplicate counter @@ -215,7 +181,7 @@ StackTrace: // write final line if currentLine != nil { - adapter.Write(currentLine, duplicates) + GlobalWriter.WriteMessage(currentLine, duplicates) // add to unexpected logs addUnexpectedLogs(currentLine) } @@ -225,7 +191,7 @@ StackTrace: case <-time.After(10 * time.Millisecond): case <-shutdownSignal: finalizeWriting() - return + return err } } @@ -235,19 +201,27 @@ func finalizeWriting() { for { select { case line := <-logBuffer: - adapter.Write(line, 0) + GlobalWriter.WriteMessage(line, 0) case <-time.After(10 * time.Millisecond): - fmt.Printf( - "%s%s%s %sEOF %s%s\n", + if GlobalWriter.isStdout { + fmt.Fprintf(GlobalWriter, + "%s%s%s %sEOF %s%s\n", - dimColor(), - time.Now().Format(timeFormat), - endDimColor(), + dimColor(), + time.Now().Format(timeFormat), + endDimColor(), - blueColor(), - leftArrow, - endColor(), - ) + blueColor(), + leftArrow, + endColor(), + ) + } else { + fmt.Fprintf(GlobalWriter, + "%s EOF %s\n", + time.Now().Format(timeFormat), + leftArrow, + ) + } return } } diff --git a/base/log/slog.go b/base/log/slog.go index d0f09aad..5901c146 100644 --- a/base/log/slog.go +++ b/base/log/slog.go @@ -6,54 +6,36 @@ import ( "runtime" "github.com/lmittmann/tint" - "github.com/mattn/go-colorable" - "github.com/mattn/go-isatty" ) -func setupSLog(logLevel Severity) { - // Convert to slog level. - var level slog.Level - switch logLevel { - case TraceLevel: - level = slog.LevelDebug - case DebugLevel: - level = slog.LevelDebug - case InfoLevel: - level = slog.LevelInfo - case WarningLevel: - level = slog.LevelWarn - case ErrorLevel: - level = slog.LevelError - case CriticalLevel: - level = slog.LevelError - } +func setupSLog(level Severity) { + // Set highest possible level, so it can be changed in runtime. + handlerLogLevel := level.toSLogLevel() - // Setup logging. - // Define output. - logOutput := os.Stdout // Create handler depending on OS. var logHandler slog.Handler switch runtime.GOOS { case "windows": logHandler = tint.NewHandler( - colorable.NewColorable(logOutput), + GlobalWriter, &tint.Options{ AddSource: true, - Level: level, + Level: handlerLogLevel, TimeFormat: timeFormat, + NoColor: !GlobalWriter.IsStdout(), // FIXME: also check for tty. }, ) case "linux": - logHandler = tint.NewHandler(logOutput, &tint.Options{ + logHandler = tint.NewHandler(GlobalWriter, &tint.Options{ AddSource: true, - Level: level, + Level: handlerLogLevel, TimeFormat: timeFormat, - NoColor: !isatty.IsTerminal(logOutput.Fd()), + NoColor: !GlobalWriter.IsStdout(), // FIXME: also check for tty. }) default: logHandler = tint.NewHandler(os.Stdout, &tint.Options{ AddSource: true, - Level: level, + Level: handlerLogLevel, TimeFormat: timeFormat, NoColor: true, }) @@ -61,5 +43,6 @@ func setupSLog(logLevel Severity) { // Set as default logger. slog.SetDefault(slog.New(logHandler)) - slog.SetLogLoggerLevel(level) + // Set actual log level. + slog.SetLogLoggerLevel(handlerLogLevel) } diff --git a/base/log/trace.go b/base/log/trace.go index 640594d4..2226339c 100644 --- a/base/log/trace.go +++ b/base/log/trace.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "runtime" - "strings" "sync" "sync/atomic" "time" @@ -24,36 +23,8 @@ var key = ContextTracerKey{} // AddTracer adds a ContextTracer to the returned Context. Will return a nil ContextTracer if logging level is not set to trace. Will return a nil ContextTracer if one already exists. Will return a nil ContextTracer in case of an error. Will return a nil context if nil. func AddTracer(ctx context.Context) (context.Context, *ContextTracer) { if ctx != nil && fastcheck(TraceLevel) { - // check pkg levels - if pkgLevelsActive.IsSet() { - // get file - _, file, _, ok := runtime.Caller(1) - if !ok { - // cannot get file, ignore - return ctx, nil - } - - pathSegments := strings.Split(file, "/") - if len(pathSegments) < 2 { - // file too short for package levels - return ctx, nil - } - pkgLevelsLock.Lock() - severity, ok := pkgLevels[pathSegments[len(pathSegments)-2]] - pkgLevelsLock.Unlock() - if ok { - // check against package level - if TraceLevel < severity { - return ctx, nil - } - } else { - // no package level set, check against global level - if uint32(TraceLevel) < atomic.LoadUint32(logLevel) { - return ctx, nil - } - } - } else if uint32(TraceLevel) < atomic.LoadUint32(logLevel) { - // no package levels set, check against global level + // Check log level. + if atomic.LoadUint32(logLevel) > uint32(TraceLevel) { return ctx, nil } diff --git a/base/log/writer.go b/base/log/writer.go new file mode 100644 index 00000000..6c10716f --- /dev/null +++ b/base/log/writer.go @@ -0,0 +1,119 @@ +package log + +import ( + "fmt" + "os" + "path/filepath" + "sync" + "time" +) + +// GlobalWriter is the global log writer. +var GlobalWriter *LogWriter = nil + +type LogWriter struct { + writeLock sync.Mutex + isStdout bool + file *os.File +} + +// NewStdoutWriter creates a new log writer thet will write to the stdout. +func NewStdoutWriter() *LogWriter { + return &LogWriter{ + file: os.Stdout, + isStdout: true, + } +} + +// NewFileWriter creates a new log writer that will write to a file. The file path will be /2006-01-02_15-04-05.log (with current date and time) +func NewFileWriter(dir string) (*LogWriter, error) { + // Make sure log dir exists, if not, create with strict permission, as logs can contain sensitive data. + _ = os.MkdirAll(dir, 0o700) + + // Open new log file. + logFile := time.Now().UTC().Format("2006-01-02_15-04-05") + ".log" + file, err := os.Create(filepath.Join(dir, logFile)) + if err != nil { + return nil, err + } + + return &LogWriter{ + file: file, + isStdout: false, + }, nil +} + +// Write writes the buffer to the writer. +func (l *LogWriter) Write(buf []byte) (int, error) { + if l == nil { + return 0, fmt.Errorf("log writer not initialized") + } + + // No need to lock in stdout context. + if !l.isStdout { + l.writeLock.Lock() + defer l.writeLock.Unlock() + } + + return l.file.Write(buf) +} + +// WriteMessage writes the message to the writer. +func (l *LogWriter) WriteMessage(msg Message, duplicates uint64) { + if l == nil { + return + } + + // No need to lock in stdout context. + if !l.isStdout { + l.writeLock.Lock() + defer l.writeLock.Unlock() + } + + fmt.Fprintln(l.file, formatLine(msg.(*logLine), duplicates, l.isStdout)) +} + +// IsStdout returns true if writer was initialized with stdout. +func (l *LogWriter) IsStdout() bool { + return l != nil && l.isStdout +} + +// Close closes the writer. +func (l *LogWriter) Close() { + if l != nil && !l.isStdout { + _ = l.file.Close() + } +} + +// CleanOldLogs deletes all log files in given directory that are older than the given threshold. +func CleanOldLogs(dir string, threshold time.Duration) error { + // Get current log file name. + var currentLogFile string + if GlobalWriter != nil && GlobalWriter.file != nil { + currentLogFile = GlobalWriter.file.Name() + } + + // Read dir entries. + files, err := os.ReadDir(dir) + if err != nil { + return fmt.Errorf("failed to read dir: %w", err) + } + + // Remove files older than threshold + deleteOlderThan := time.Now().Add(-threshold) + for _, f := range files { + // Skip directories and the current log file. + if f.IsDir() || f.Name() == currentLogFile { + continue + } + + // Delete log files. + if fileInfo, err := f.Info(); err == nil { + if fileInfo.ModTime().Before(deleteOlderThan) { + _ = os.Remove(filepath.Join(dir, f.Name())) + } + } + } + + return nil +} diff --git a/cmds/hub/main.go b/cmds/hub/main.go index 9c0379e8..1fdc8809 100644 --- a/cmds/hub/main.go +++ b/cmds/hub/main.go @@ -45,9 +45,8 @@ func main() { // Set SPN public hub mode. conf.EnablePublicHub(true) - // Set default log level. - log.SetLogLevel(log.WarningLevel) - _ = log.Start() + // Start logger with default log level. + _ = log.Start(log.WarningLevel) // FIXME: Use service? diff --git a/cmds/observation-hub/main.go b/cmds/observation-hub/main.go index a6ced810..598680f0 100644 --- a/cmds/observation-hub/main.go +++ b/cmds/observation-hub/main.go @@ -46,9 +46,8 @@ func main() { sluice.EnableListener = false api.EnableServer = false - // Set default log level. - log.SetLogLevel(log.WarningLevel) - _ = log.Start() + // Start logger with default log level. + _ = log.Start(log.WarningLevel) // Create instance. var execCmdLine bool diff --git a/cmds/portmaster-core/main.go b/cmds/portmaster-core/main.go index 0b4d0e01..8cd80864 100644 --- a/cmds/portmaster-core/main.go +++ b/cmds/portmaster-core/main.go @@ -24,6 +24,10 @@ var ( binDir string dataDir string + logToStdout bool + logDir string + logLevel string + svcCfg *service.ServiceConfig ) @@ -35,6 +39,11 @@ func init() { // Add persisent flags for all commands. rootCmd.PersistentFlags().StringVar(&binDir, "bin-dir", "", "set directory for executable binaries (rw/ro)") rootCmd.PersistentFlags().StringVar(&dataDir, "data-dir", "", "set directory for variable data (rw)") + + // Add flags for service only. + rootCmd.Flags().BoolVar(&logToStdout, "log-stdout", false, "log to stdout instead of file") + rootCmd.Flags().StringVar(&logDir, "log-dir", "", "set directory for logs") + rootCmd.Flags().StringVar(&logLevel, "log", "", "set log level to [trace|debug|info|warning|error|critical]") } func main() { @@ -56,8 +65,13 @@ func initializeGlobals(cmd *cobra.Command, args []string) { // Create service config. svcCfg = &service.ServiceConfig{ - BinDir: binDir, - DataDir: dataDir, + BinDir: binDir, + DataDir: dataDir, + + LogToStdout: logToStdout, + LogDir: logDir, + LogLevel: logLevel, + BinariesIndexURLs: service.DefaultStableBinaryIndexURLs, IntelIndexURLs: service.DefaultIntelIndexURLs, VerifyBinaryUpdates: service.BinarySigningTrustStore, diff --git a/cmds/portmaster-core/run.go b/cmds/portmaster-core/run.go index 33702992..6c41bd56 100644 --- a/cmds/portmaster-core/run.go +++ b/cmds/portmaster-core/run.go @@ -75,9 +75,15 @@ func cmdRun(cmd *cobra.Command, args []string) { // START - // Set default log level and start logging. - log.SetLogLevel(log.WarningLevel) - _ = log.Start() + // FIXME: fix color and duplicate level when logging with slog + // FIXME: check for tty for color enabling + + // Start logging. + err = log.Start(svcCfg.LogLevel, svcCfg.LogToStdout, svcCfg.LogDir) + if err != nil { + fmt.Fprintln(os.Stderr, err.Error()) + os.Exit(4) + } // Create system service. service := NewSystemService(instance) @@ -97,10 +103,10 @@ func cmdRun(cmd *cobra.Command, args []string) { case <-instance.ShutdownComplete(): // Print stack on shutdown, if enabled. if printStackOnExit { - printStackTo(os.Stdout, "PRINTING STACK ON EXIT") + printStackTo(log.GlobalWriter, "PRINTING STACK ON EXIT") } case <-time.After(3 * time.Minute): - printStackTo(os.Stderr, "PRINTING STACK - TAKING TOO LONG FOR SHUTDOWN") + printStackTo(log.GlobalWriter, "PRINTING STACK - TAKING TOO LONG FOR SHUTDOWN") } // Check if restart was triggered and send start service command if true. diff --git a/cmds/portmaster-core/run_linux.go b/cmds/portmaster-core/run_linux.go index 9b65b804..858ed022 100644 --- a/cmds/portmaster-core/run_linux.go +++ b/cmds/portmaster-core/run_linux.go @@ -11,6 +11,7 @@ import ( processInfo "github.com/shirou/gopsutil/process" "github.com/spf13/cobra" + "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/service" ) @@ -30,7 +31,7 @@ func (s *LinuxSystemService) Run() { // Print stack on start failure, if enabled. if printStackOnExit { - printStackTo(os.Stderr, "PRINTING STACK ON START FAILURE") + printStackTo(log.GlobalWriter, "PRINTING STACK ON START FAILURE") } os.Exit(1) @@ -57,7 +58,7 @@ wait: case sig := <-signalCh: // Only print and continue to wait if SIGUSR1 if sig == syscall.SIGUSR1 { - printStackTo(os.Stdout, "PRINTING STACK ON REQUEST") + printStackTo(log.GlobalWriter, "PRINTING STACK ON REQUEST") continue wait } else { // Trigger shutdown. @@ -84,7 +85,7 @@ wait: if forceCnt > 0 { fmt.Printf(" again, but already shutting down - %d more to force\n", sig, forceCnt) } else { - printStackTo(os.Stderr, "PRINTING STACK ON FORCED EXIT") + printStackTo(log.GlobalWriter, "PRINTING STACK ON FORCED EXIT") os.Exit(1) } } diff --git a/cmds/portmaster-core/run_windows.go b/cmds/portmaster-core/run_windows.go index 3976745f..09593630 100644 --- a/cmds/portmaster-core/run_windows.go +++ b/cmds/portmaster-core/run_windows.go @@ -13,10 +13,12 @@ import ( "os/signal" "syscall" - "github.com/safing/portmaster/service" "github.com/spf13/cobra" "golang.org/x/sys/windows/svc" "golang.org/x/sys/windows/svc/debug" + + "github.com/safing/portmaster/base/log" + "github.com/safing/portmaster/service" ) const serviceName = "PortmasterCore" @@ -66,7 +68,7 @@ func (s *WindowsSystemService) Execute(args []string, changeRequests <-chan svc. // Print stack on start failure, if enabled. if printStackOnExit { - printStackTo(os.Stderr, "PRINTING STACK ON START FAILURE") + printStackTo(log.GlobalWriter, "PRINTING STACK ON START FAILURE") } // Notify service manager we stopped again. @@ -140,7 +142,7 @@ waitShutdown: if forceCnt > 0 { fmt.Printf(" but already shutting down - %d more to force\n", sig, forceCnt) } else { - printStackTo(os.Stderr, "PRINTING STACK ON FORCED EXIT") + printStackTo(log.GlobalWriter, "PRINTING STACK ON FORCED EXIT") os.Exit(1) } @@ -154,7 +156,7 @@ waitShutdown: if forceCnt > 0 { fmt.Printf(" but already shutting down - %d more to force\n", serviceCmdName(c.Cmd), forceCnt) } else { - printStackTo(os.Stderr, "PRINTING STACK ON FORCED EXIT") + printStackTo(log.GlobalWriter, "PRINTING STACK ON FORCED EXIT") os.Exit(1) } diff --git a/cmds/portmaster-core/update.go b/cmds/portmaster-core/update.go index 31fcb881..866aab61 100644 --- a/cmds/portmaster-core/update.go +++ b/cmds/portmaster-core/update.go @@ -28,10 +28,11 @@ func update(cmd *cobra.Command, args []string) error { if err != nil { return fmt.Errorf("internal configuration error: %w", err) } + // Force logging to stdout. + svcCfg.LogToStdout = true // Start logging. - log.SetLogLevel(log.InfoLevel) - _ = log.Start() + _ = log.Start(svcCfg.LogLevel, svcCfg.LogToStdout, svcCfg.LogDir) defer log.Shutdown() // Create updaters. diff --git a/go.mod b/go.mod index a97838af..eb548caa 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,6 @@ go 1.22.0 replace github.com/tc-hib/winres => github.com/dhaavi/winres v0.2.2 require ( - fyne.io/systray v1.11.0 github.com/VictoriaMetrics/metrics v1.35.1 github.com/Xuanwo/go-locale v1.1.1 github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6 @@ -19,11 +18,11 @@ require ( github.com/coreos/go-iptables v0.7.0 github.com/davecgh/go-spew v1.1.1 github.com/dgraph-io/badger v1.6.2 - github.com/dhaavi/go-notify v0.0.0-20190209221809-c404b1f22435 github.com/florianl/go-conntrack v0.4.0 github.com/florianl/go-nfqueue v1.3.2 github.com/fogleman/gg v1.3.0 github.com/ghodss/yaml v1.0.0 + github.com/gobwas/glob v0.2.3 github.com/godbus/dbus/v5 v5.1.0 github.com/gofrs/uuid v4.4.0+incompatible github.com/google/gopacket v1.1.19 @@ -34,9 +33,8 @@ require ( github.com/hashicorp/go-version v1.7.0 github.com/jackc/puddle/v2 v2.2.1 github.com/lmittmann/tint v1.0.5 + github.com/maruel/panicparse/v2 v2.3.1 github.com/mat/besticon v3.12.0+incompatible - github.com/mattn/go-colorable v0.1.13 - github.com/mattn/go-isatty v0.0.20 github.com/miekg/dns v1.1.62 github.com/mitchellh/copystructure v1.2.0 github.com/mitchellh/go-server-timing v1.0.1 @@ -71,16 +69,12 @@ require ( require ( github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect github.com/aead/ecdh v0.2.0 // indirect - github.com/alessio/shellescape v1.4.2 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/danieljoos/wincred v1.2.1 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-ole/go-ole v1.3.0 // indirect - github.com/gobwas/glob v0.2.3 // indirect - github.com/godbus/dbus v4.1.0+incompatible // indirect github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect github.com/golang/gddo v0.0.0-20210115222349-20d68f94ee1f // indirect github.com/golang/glog v1.2.1 // indirect @@ -91,7 +85,7 @@ require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/native v1.1.0 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect - github.com/maruel/panicparse/v2 v2.3.1 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mdlayher/netlink v1.7.2 // indirect github.com/mdlayher/socket v0.5.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect @@ -113,7 +107,6 @@ require ( github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect - github.com/zalando/go-keyring v0.2.5 // indirect github.com/zeebo/blake3 v0.2.4 // indirect golang.org/x/crypto v0.26.0 // indirect golang.org/x/mod v0.20.0 // indirect diff --git a/go.sum b/go.sum index 8f65cf10..ef8feec4 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,4 @@ cloud.google.com/go v0.16.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -fyne.io/systray v1.11.0 h1:D9HISlxSkx+jHSniMBR6fCFOUjk1x/OOOJLa9lJYAKg= -fyne.io/systray v1.11.0/go.mod h1:RVwqP9nYMo7h5zViCBHri2FgjXF7H2cub7MAq4NSoLs= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M= github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -16,8 +14,6 @@ github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6 h1:5L8Mj9Co9sJVgW3TpY github.com/aead/serpent v0.0.0-20160714141033-fba169763ea6/go.mod h1:3HgLJ9d18kXMLQlJvIY3+FszZYMxCz8WfE2MQ7hDY0w= github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= -github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0= -github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= @@ -43,8 +39,6 @@ github.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFE github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= -github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8GQs= -github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -55,8 +49,6 @@ github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWa github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= -github.com/dhaavi/go-notify v0.0.0-20190209221809-c404b1f22435 h1:AnwbdEI8eV3GzLM3SlrJlYmYa6OB5X8RwY4A8QJOCP0= -github.com/dhaavi/go-notify v0.0.0-20190209221809-c404b1f22435/go.mod h1:EMJ8XWTopp8OLRBMUm9vHE8Wn48CNpU21HM817OKNrc= github.com/dhaavi/winres v0.2.2 h1:SUago7FwhgLSMyDdeuV6enBZ+ZQSl0KwcnbWzvlfBls= github.com/dhaavi/winres v0.2.2/go.mod h1:1NTs+/DtKP1BplIL1+XQSoq4X1PUfLczexS7gf3x9T4= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -87,8 +79,6 @@ github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZs github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4= -github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA= @@ -181,11 +171,8 @@ github.com/mat/besticon v3.12.0+incompatible h1:1KTD6wisfjfnX+fk9Kx/6VEZL+MAW1Lh github.com/mat/besticon v3.12.0+incompatible/go.mod h1:mA1auQYHt6CW5e7L9HJLmqVQC8SzNk2gVwouO0AbiEU= github.com/mattn/go-colorable v0.0.10-0.20170816031813-ad5389df28cd/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.2/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mdlayher/ethtool v0.0.0-20210210192532-2b88debcdd43/go.mod h1:+t7E0lkKfbBsebllff1xdTmyJt8lH37niI6kwFk9OTo= @@ -282,8 +269,6 @@ github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DM github.com/spkg/zipfs v0.7.1 h1:+2X5lvNHTybnDMQZAIHgedRXZK1WXdc+94R/P5v2XWE= github.com/spkg/zipfs v0.7.1/go.mod h1:48LW+/Rh1G7aAav1ew1PdlYn52T+LM+ARmSHfDNJvg8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -329,8 +314,6 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -github.com/zalando/go-keyring v0.2.5 h1:Bc2HHpjALryKD62ppdEzaFG6VxL6Bc+5v0LYpN8Lba8= -github.com/zalando/go-keyring v0.2.5/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk= github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= @@ -420,7 +403,6 @@ golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/service/config.go b/service/config.go index 0d613315..7b7e0f08 100644 --- a/service/config.go +++ b/service/config.go @@ -8,12 +8,17 @@ import ( "runtime" "github.com/safing/jess" + "github.com/safing/portmaster/base/log" ) type ServiceConfig struct { BinDir string DataDir string + LogToStdout bool + LogDir string + LogLevel string + BinariesIndexURLs []string IntelIndexURLs []string VerifyBinaryUpdates jess.TrustStore @@ -21,9 +26,10 @@ type ServiceConfig struct { } func (sc *ServiceConfig) Init() error { - // Check directories + // Check directories. switch runtime.GOOS { case "windows": + // Fall back to defaults. if sc.BinDir == "" { exeDir, err := getCurrentBinaryFolder() // Default: C:/Program Files/Portmaster if err != nil { @@ -34,6 +40,9 @@ func (sc *ServiceConfig) Init() error { if sc.DataDir == "" { sc.DataDir = filepath.FromSlash("$ProgramData/Portmaster") } + if sc.LogDir == "" { + sc.LogDir = filepath.Join(sc.DataDir, "logs") + } case "linux": // Fall back to defaults. @@ -43,6 +52,9 @@ func (sc *ServiceConfig) Init() error { if sc.DataDir == "" { sc.DataDir = "/var/lib/portmaster" } + if sc.LogDir == "" { + sc.LogDir = "/var/log/portmaster" + } default: // Fail if not configured on other platforms. @@ -52,11 +64,15 @@ func (sc *ServiceConfig) Init() error { if sc.DataDir == "" { return errors.New("binary directory must be configured - auto-detection not supported on this platform") } + if !sc.LogToStdout && sc.LogDir == "" { + return errors.New("logging directory must be configured - auto-detection not supported on this platform") + } } // Expand path variables. sc.BinDir = os.ExpandEnv(sc.BinDir) sc.DataDir = os.ExpandEnv(sc.DataDir) + sc.LogDir = os.ExpandEnv(sc.LogDir) // Apply defaults for required fields. if len(sc.BinariesIndexURLs) == 0 { @@ -67,6 +83,11 @@ func (sc *ServiceConfig) Init() error { sc.IntelIndexURLs = DefaultIntelIndexURLs } + // Check log level. + if sc.LogLevel != "" && log.ParseLevel(sc.LogLevel) == 0 { + return fmt.Errorf("invalid log level %q", sc.LogLevel) + } + return nil } diff --git a/service/mgr/worker.go b/service/mgr/worker.go index ff06f05a..2151a2c8 100644 --- a/service/mgr/worker.go +++ b/service/mgr/worker.go @@ -5,11 +5,12 @@ import ( "errors" "fmt" "log/slog" - "os" "runtime" "runtime/debug" "strings" "time" + + "github.com/safing/portmaster/base/log" ) // workerContextKey is a key used for the context key/value storage. @@ -303,7 +304,7 @@ func (m *Manager) runWorker(w *WorkerCtx, fn func(w *WorkerCtx) error) (panicInf // Print panic to stderr. stackTrace := string(debug.Stack()) fmt.Fprintf( - os.Stderr, + log.GlobalWriter, "===== PANIC =====\n%s\n\n%s===== END =====\n", panicVal, stackTrace, diff --git a/spn/instance.go b/spn/instance.go index ce555d15..dcfe01aa 100644 --- a/spn/instance.go +++ b/spn/instance.go @@ -9,6 +9,7 @@ import ( "github.com/safing/portmaster/base/api" "github.com/safing/portmaster/base/config" "github.com/safing/portmaster/base/database/dbmodule" + "github.com/safing/portmaster/base/log" "github.com/safing/portmaster/base/metrics" "github.com/safing/portmaster/base/notifications" "github.com/safing/portmaster/base/rng" @@ -92,6 +93,11 @@ func New() (*Instance, error) { // FIXME: fill } + // Initialize log + log.GlobalWriter = log.NewStdoutWriter() + + // FIXME: initialize log file. + var err error // Base modules