From 7473132932b085a6375ece1f0400539e5034f440 Mon Sep 17 00:00:00 2001 From: Christoph Haas Date: Sun, 2 Mar 2025 08:51:13 +0100 Subject: [PATCH] chore: replace logrus with standard lib log/slog --- cmd/wg-portal/main.go | 45 +---- go.mod | 2 - go.sum | 5 - internal/adapters/database.go | 52 +++--- internal/adapters/filesystem.go | 5 +- internal/adapters/metrics.go | 10 +- internal/adapters/wgquick.go | 9 +- internal/app/api/core/server.go | 46 +++-- internal/app/app.go | 18 +- internal/app/audit/recorder.go | 6 +- internal/app/auth/auth.go | 16 +- internal/app/auth/auth_ldap.go | 7 +- internal/app/auth/auth_oauth.go | 6 +- internal/app/auth/auth_oidc.go | 6 +- internal/app/configfile/manager.go | 18 +- internal/app/mail/manager.go | 16 +- internal/app/migrate_v1.go | 15 +- internal/app/route/routes.go | 26 +-- internal/app/users/user_manager.go | 14 +- internal/app/wireguard/statistics.go | 47 ++--- internal/app/wireguard/wireguard.go | 76 +++++--- .../app/wireguard/wireguard_interfaces.go | 11 +- internal/app/wireguard/wireguard_peers.go | 7 +- internal/config/auth.go | 8 +- internal/config/config.go | 47 ++--- internal/domain/context.go | 11 +- internal/domain/interface.go | 13 +- internal/ldap_utils.go | 4 +- internal/logger.go | 168 ++++++++++++++++++ internal/util.go | 12 +- 30 files changed, 479 insertions(+), 247 deletions(-) create mode 100644 internal/logger.go diff --git a/cmd/wg-portal/main.go b/cmd/wg-portal/main.go index 5c13b28..d8f8d33 100644 --- a/cmd/wg-portal/main.go +++ b/cmd/wg-portal/main.go @@ -2,12 +2,11 @@ package main import ( "context" + "log/slog" "os" - "strings" "syscall" "time" - "github.com/sirupsen/logrus" evbus "github.com/vardius/message-bus" "github.com/h44z/wg-portal/internal" @@ -31,12 +30,11 @@ import ( func main() { ctx := internal.SignalAwareContext(context.Background(), syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) - logrus.Infof("Starting WireGuard Portal V2...") - logrus.Infof("WireGuard Portal version: %s", internal.Version) + slog.Info("Starting WireGuard Portal V2...", "version", internal.Version) cfg, err := config.GetConfig() internal.AssertNoError(err) - setupLogging(cfg) + internal.SetupLogging(cfg.Advanced.LogLevel, cfg.Advanced.LogPretty, cfg.Advanced.LogJson) cfg.LogStartupValues() @@ -62,7 +60,7 @@ func main() { case shouldExit && err == nil: return case shouldExit: - logrus.Errorf("Failed to process program args: %v", err) + slog.Error("Failed to process program args", "error", err) os.Exit(1) default: internal.AssertNoError(err) @@ -131,41 +129,14 @@ func main() { go metricsServer.Run(ctx) go webSrv.Run(ctx, cfg.Web.ListeningAddress) + slog.Info("Application startup complete") + // wait until context gets cancelled <-ctx.Done() - logrus.Infof("Stopping WireGuard Portal") + slog.Info("Stopping WireGuard Portal") time.Sleep(5 * time.Second) // wait for (most) goroutines to finish gracefully - logrus.Infof("Stopped WireGuard Portal") -} - -func setupLogging(cfg *config.Config) { - switch strings.ToLower(cfg.Advanced.LogLevel) { - case "trace": - logrus.SetLevel(logrus.TraceLevel) - case "debug": - logrus.SetLevel(logrus.DebugLevel) - case "info", "information": - logrus.SetLevel(logrus.InfoLevel) - case "warn", "warning": - logrus.SetLevel(logrus.WarnLevel) - case "error": - logrus.SetLevel(logrus.ErrorLevel) - default: - logrus.SetLevel(logrus.InfoLevel) - } - - switch { - case cfg.Advanced.LogJson: - logrus.SetFormatter(&logrus.JSONFormatter{ - PrettyPrint: cfg.Advanced.LogPretty, - }) - case cfg.Advanced.LogPretty: - logrus.SetFormatter(&logrus.TextFormatter{ - ForceColors: true, - DisableColors: false, - }) - } + slog.Info("Stopped WireGuard Portal") } diff --git a/go.mod b/go.mod index 8625fe8..4e331db 100644 --- a/go.mod +++ b/go.mod @@ -13,10 +13,8 @@ require ( github.com/google/uuid v1.6.0 github.com/prometheus-community/pro-bing v0.6.1 github.com/prometheus/client_golang v1.21.0 - github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.10.0 github.com/swaggo/swag v1.16.4 - github.com/toorop/gin-logrus v0.0.0-20210225092905-2c785434f26f github.com/utrack/gin-csrf v0.0.0-20190424104817-40fb8d2c8fca github.com/vardius/message-bus v1.1.5 github.com/vishvananda/netlink v1.3.0 diff --git a/go.sum b/go.sum index 0528d30..575af42 100644 --- a/go.sum +++ b/go.sum @@ -243,8 +243,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94 github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -261,8 +259,6 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A= github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg= -github.com/toorop/gin-logrus v0.0.0-20210225092905-2c785434f26f h1:oqdnd6OGlOUu1InG37hWcCB3a+Jy3fwjylyVboaNMwY= -github.com/toorop/gin-logrus v0.0.0-20210225092905-2c785434f26f/go.mod h1:X3Dd1SB8Gt1V968NTzpKFjMM6O8ccta2NPC6MprOxZQ= github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208/go.mod h1:BzWtXXrXzZUvMacR0oF/fbDDgUPO8L36tDMmRAf14ns= github.com/toorop/go-dkim v0.0.0-20250226130143-9025cce95817 h1:q0hKh5a5FRkhuTb5JNfgjzpzvYLHjH0QOgPZPYnRWGA= github.com/toorop/go-dkim v0.0.0-20250226130143-9025cce95817/go.mod h1:BzWtXXrXzZUvMacR0oF/fbDDgUPO8L36tDMmRAf14ns= @@ -353,7 +349,6 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/internal/adapters/database.go b/internal/adapters/database.go index a37d83f..62b9280 100644 --- a/internal/adapters/database.go +++ b/internal/adapters/database.go @@ -4,14 +4,14 @@ import ( "context" "errors" "fmt" + "log/slog" "os" "path/filepath" "strings" "time" "github.com/glebarez/sqlite" - "github.com/sirupsen/logrus" - gormMySQL "gorm.io/driver/mysql" + "gorm.io/driver/mysql" "gorm.io/driver/postgres" "gorm.io/driver/sqlserver" "gorm.io/gorm" @@ -32,13 +32,15 @@ type SysStat struct { SchemaVersion uint64 `gorm:"primaryKey,column:schema_version"` } -// GormLogger is a custom logger for Gorm, making it use logrus. +// GormLogger is a custom logger for Gorm, making it use slog type GormLogger struct { SlowThreshold time.Duration SourceField string IgnoreErrRecordNotFound bool Debug bool Silent bool + + prefix string } func NewLogger(slowThreshold time.Duration, debug bool) *GormLogger { @@ -48,6 +50,7 @@ func NewLogger(slowThreshold time.Duration, debug bool) *GormLogger { IgnoreErrRecordNotFound: true, Silent: false, SourceField: "src", + prefix: "GORM-SQL: ", } } @@ -64,21 +67,21 @@ func (l *GormLogger) Info(ctx context.Context, s string, args ...any) { if l.Silent { return } - logrus.WithContext(ctx).Infof(s, args...) + slog.InfoContext(ctx, l.prefix+s, args...) } func (l *GormLogger) Warn(ctx context.Context, s string, args ...any) { if l.Silent { return } - logrus.WithContext(ctx).Warnf(s, args...) + slog.WarnContext(ctx, l.prefix+s, args...) } func (l *GormLogger) Error(ctx context.Context, s string, args ...any) { if l.Silent { return } - logrus.WithContext(ctx).Errorf(s, args...) + slog.ErrorContext(ctx, l.prefix+s, args...) } func (l *GormLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) { @@ -88,26 +91,29 @@ func (l *GormLogger) Trace(ctx context.Context, begin time.Time, fc func() (stri elapsed := time.Since(begin) sql, rows := fc() - fields := logrus.Fields{ - "rows": rows, - "duration": elapsed, + + attrs := []any{ + "rows", rows, + "duration", elapsed, } + if l.SourceField != "" { - fields[l.SourceField] = utils.FileWithLineNum() + attrs = append(attrs, l.SourceField, utils.FileWithLineNum()) } + if err != nil && !(errors.Is(err, gorm.ErrRecordNotFound) && l.IgnoreErrRecordNotFound) { - fields[logrus.ErrorKey] = err - logrus.WithContext(ctx).WithFields(fields).Errorf("%s", sql) + attrs = append(attrs, "error", err) + slog.ErrorContext(ctx, l.prefix+sql, attrs...) return } if l.SlowThreshold != 0 && elapsed > l.SlowThreshold { - logrus.WithContext(ctx).WithFields(fields).Warnf("%s", sql) + slog.WarnContext(ctx, l.prefix+sql, attrs...) return } if l.Debug { - logrus.WithContext(ctx).WithFields(fields).Tracef("%s", sql) + slog.DebugContext(ctx, l.prefix+sql, attrs...) } } @@ -118,7 +124,7 @@ func NewDatabase(cfg config.DatabaseConfig) (*gorm.DB, error) { switch cfg.Type { case config.DatabaseMySQL: - gormDb, err = gorm.Open(gormMySQL.Open(cfg.DSN), &gorm.Config{ + gormDb, err = gorm.Open(mysql.Open(cfg.DSN), &gorm.Config{ Logger: NewLogger(cfg.SlowQueryThreshold, cfg.Debug), }) if err != nil { @@ -212,13 +218,13 @@ func (r *SqlRepo) preCheck() error { } func (r *SqlRepo) migrate() error { - logrus.Tracef("sysstat migration: %v", r.db.AutoMigrate(&SysStat{})) - logrus.Tracef("user migration: %v", r.db.AutoMigrate(&domain.User{})) - logrus.Tracef("interface migration: %v", r.db.AutoMigrate(&domain.Interface{})) - logrus.Tracef("peer migration: %v", r.db.AutoMigrate(&domain.Peer{})) - logrus.Tracef("peer status migration: %v", r.db.AutoMigrate(&domain.PeerStatus{})) - logrus.Tracef("interface status migration: %v", r.db.AutoMigrate(&domain.InterfaceStatus{})) - logrus.Tracef("audit data migration: %v", r.db.AutoMigrate(&domain.AuditEntry{})) + slog.Debug("running migration: sys-stat", "result", r.db.AutoMigrate(&SysStat{})) + slog.Debug("running migration: user", "result", r.db.AutoMigrate(&domain.User{})) + slog.Debug("running migration: interface", "result", r.db.AutoMigrate(&domain.Interface{})) + slog.Debug("running migration: peer", "result", r.db.AutoMigrate(&domain.Peer{})) + slog.Debug("running migration: peer status", "result", r.db.AutoMigrate(&domain.PeerStatus{})) + slog.Debug("running migration: interface status", "result", r.db.AutoMigrate(&domain.InterfaceStatus{})) + slog.Debug("running migration: audit data", "result", r.db.AutoMigrate(&domain.AuditEntry{})) existingSysStat := SysStat{} r.db.Where("schema_version = ?", SchemaVersion).First(&existingSysStat) @@ -230,7 +236,7 @@ func (r *SqlRepo) migrate() error { if err := r.db.Create(&sysStat).Error; err != nil { return fmt.Errorf("failed to write sysstat entry for schema version %d: %w", SchemaVersion, err) } - logrus.Debugf("sysstat entry for schema version %d written", SchemaVersion) + slog.Debug("sys-stat entry written", "schema_version", SchemaVersion) } return nil diff --git a/internal/adapters/filesystem.go b/internal/adapters/filesystem.go index 11bdba9..34221fd 100644 --- a/internal/adapters/filesystem.go +++ b/internal/adapters/filesystem.go @@ -3,10 +3,9 @@ package adapters import ( "fmt" "io" + "log/slog" "os" "path/filepath" - - "github.com/sirupsen/logrus" ) type FilesystemRepo struct { @@ -46,7 +45,7 @@ func (r *FilesystemRepo) WriteFile(path string, contents io.Reader) error { } defer func(file *os.File) { if err := file.Close(); err != nil { - logrus.Errorf("failed to close file %s: %v", file.Name(), err) + slog.Error("failed to close file", "file", file.Name(), "error", err) } }(file) diff --git a/internal/adapters/metrics.go b/internal/adapters/metrics.go index 15980d3..d9bea6e 100644 --- a/internal/adapters/metrics.go +++ b/internal/adapters/metrics.go @@ -3,13 +3,13 @@ package adapters import ( "context" "errors" + "log/slog" "net/http" "time" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promauto" "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/sirupsen/logrus" "github.com/h44z/wg-portal/internal" "github.com/h44z/wg-portal/internal/config" @@ -91,11 +91,11 @@ func (m *MetricsServer) Run(ctx context.Context) { // Run the metrics server in a goroutine go func() { if err := m.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { - logrus.Errorf("metrics service on %s exited: %v", m.Addr, err) + slog.Error("metrics service exited", "address", m.Addr, "error", err) } }() - logrus.Infof("started metrics service on %s", m.Addr) + slog.Info("started metrics service", "address", m.Addr) // Wait for the context to be done <-ctx.Done() @@ -106,9 +106,9 @@ func (m *MetricsServer) Run(ctx context.Context) { // Attempt to gracefully shut down the metrics server if err := m.Shutdown(shutdownCtx); err != nil { - logrus.Errorf("metrics service on %s shutdown failed: %v", m.Addr, err) + slog.Error("metrics service shutdown failed", "address", m.Addr, "error", err) } else { - logrus.Infof("metrics service on %s shutdown gracefully", m.Addr) + slog.Info("metrics service shutdown gracefully", "address", m.Addr) } } diff --git a/internal/adapters/wgquick.go b/internal/adapters/wgquick.go index 43fe445..992e69a 100644 --- a/internal/adapters/wgquick.go +++ b/internal/adapters/wgquick.go @@ -3,11 +3,10 @@ package adapters import ( "bytes" "fmt" + "log/slog" "os/exec" "strings" - "github.com/sirupsen/logrus" - "github.com/h44z/wg-portal/internal" "github.com/h44z/wg-portal/internal/domain" ) @@ -35,7 +34,7 @@ func (r *WgQuickRepo) ExecuteInterfaceHook(id domain.InterfaceIdentifier, hookCm return nil } - logrus.Tracef("interface %s: executing hook %s", id, hookCmd) + slog.Debug("executing interface hook", "interface", id, "hook", hookCmd) err := r.exec(hookCmd, id) if err != nil { return fmt.Errorf("failed to exec hook: %w", err) @@ -107,6 +106,8 @@ func (r *WgQuickRepo) exec(command string, interfaceId domain.InterfaceIdentifie if err != nil { return fmt.Errorf("failed to exexute shell command %s: %w", commandWithInterfaceName, err) } - logrus.Tracef("executed shell command %s, with output: %s", commandWithInterfaceName, string(out)) + slog.Debug("executed shell command", + "command", commandWithInterfaceName, + "output", string(out)) return nil } diff --git a/internal/app/api/core/server.go b/internal/app/api/core/server.go index aea7e7c..441011e 100644 --- a/internal/app/api/core/server.go +++ b/internal/app/api/core/server.go @@ -7,14 +7,13 @@ import ( "html/template" "io" "io/fs" + "log/slog" "math/rand" "net/http" "os" "time" "github.com/gin-gonic/gin" - "github.com/sirupsen/logrus" - ginlogrus "github.com/toorop/gin-logrus" "github.com/h44z/wg-portal/internal" "github.com/h44z/wg-portal/internal/config" @@ -56,14 +55,39 @@ func NewServer(cfg *config.Config, endpoints ...ApiEndpointSetupFunc) (*Server, gin.SetMode(gin.ReleaseMode) gin.DefaultWriter = io.Discard s.server = gin.New() + if cfg.Web.RequestLogging { - if logrus.GetLevel() == logrus.TraceLevel { + if cfg.Advanced.LogLevel == "trace" { gin.SetMode(gin.DebugMode) - s.server.Use(ginlogrus.Logger(logrus.StandardLogger())) - } else { - s.server.Use(ginlogrus.Logger(logrus.StandardLogger())) } + s.server.Use(func(c *gin.Context) { + start := time.Now() + path := c.Request.URL.Path + raw := c.Request.URL.RawQuery + + c.Next() + + if raw != "" { + path = path + "?" + raw + } + + latency := time.Since(start) + status := c.Writer.Status() + clientIP := c.ClientIP() + method := c.Request.Method + errorMsg := c.Errors.ByType(gin.ErrorTypePrivate).String() + + slog.Debug("HTTP Request", + "status", status, + "latency", latency, + "client", clientIP, + "method", method, + "path", path, + "error", errorMsg, + ) + }) } + s.server.Use(gin.Recovery()).Use(func(c *gin.Context) { c.Writer.Header().Set("X-Served-By", hostname) c.Next() @@ -112,22 +136,24 @@ func (s *Server) Run(ctx context.Context, listenAddress string) { err = srv.ListenAndServe() } if err != nil { - logrus.Infof("web service on %s exited: %v", listenAddress, err) + slog.Info("web service exited", + "address", listenAddress, + "error", err) cancelFn() } }() - logrus.Infof("started web service on %s", listenAddress) + slog.Info("started web service", "address", listenAddress) // Wait for the main context to end <-srvContext.Done() - logrus.Debug("web service shutting down, grace period: 5 seconds...") + slog.Debug("web service shutting down, grace period: 5 seconds") shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() _ = srv.Shutdown(shutdownCtx) - logrus.Debug("web service shut down") + slog.Debug("web service shut down") } func (s *Server) setupRoutes(endpoints ...ApiEndpointSetupFunc) { diff --git a/internal/app/app.go b/internal/app/app.go index 977553f..c9d596e 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -4,9 +4,9 @@ import ( "context" "errors" "fmt" + "log/slog" "time" - "github.com/sirupsen/logrus" evbus "github.com/vardius/message-bus" "github.com/h44z/wg-portal/internal/config" @@ -81,7 +81,7 @@ func (a *App) Startup(ctx context.Context) error { func (a *App) importNewInterfaces(ctx context.Context) error { if !a.Config.Core.ImportExisting { - logrus.Trace("skipping interface import - feature disabled") + slog.Debug("skipping interface import - feature disabled") return nil // feature disabled } @@ -91,14 +91,14 @@ func (a *App) importNewInterfaces(ctx context.Context) error { } if importedCount > 0 { - logrus.Infof("%d new interfaces imported", importedCount) + slog.Info("new interfaces imported", "count", importedCount) } return nil } func (a *App) restoreInterfaceState(ctx context.Context) error { if !a.Config.Core.RestoreState { - logrus.Trace("skipping interface state restore - feature disabled") + slog.Debug("skipping interface state restore - feature disabled") return nil // feature disabled } @@ -107,14 +107,14 @@ func (a *App) restoreInterfaceState(ctx context.Context) error { return err } - logrus.Info("interface state restored") + slog.Info("interface state restored") return nil } func (a *App) createDefaultUser(ctx context.Context) error { adminUserId := domain.UserIdentifier(a.Config.Core.AdminUser) if adminUserId == "" { - logrus.Trace("skipping default user creation - admin user is blank") + slog.Debug("skipping default user creation - admin user is blank") return nil // empty admin user - do not create } @@ -123,7 +123,7 @@ func (a *App) createDefaultUser(ctx context.Context) error { return err } if err == nil { - logrus.Trace("skipping default user creation - admin user already exists") + slog.Debug("skipping default user creation - admin user already exists") return nil // admin user already exists } @@ -154,7 +154,7 @@ func (a *App) createDefaultUser(ctx context.Context) error { } if a.Config.Core.AdminApiToken != "" { if len(a.Config.Core.AdminApiToken) < 18 { - logrus.Warnf("[SECURITY WARNING] admin API token is too short, should be at least 18 characters long") + slog.Warn("admin API token is too short, should be at least 18 characters long") } defaultAdmin.ApiToken = a.Config.Core.AdminApiToken defaultAdmin.ApiTokenCreated = &now @@ -165,7 +165,7 @@ func (a *App) createDefaultUser(ctx context.Context) error { return err } - logrus.Infof("admin user %s created", admin.Identifier) + slog.Info("admin user created", "identifier", admin.Identifier) return nil } diff --git a/internal/app/audit/recorder.go b/internal/app/audit/recorder.go index 50decbc..abcb4c3 100644 --- a/internal/app/audit/recorder.go +++ b/internal/app/audit/recorder.go @@ -3,9 +3,9 @@ package audit import ( "context" "fmt" + "log/slog" "time" - "github.com/sirupsen/logrus" evbus "github.com/vardius/message-bus" "github.com/h44z/wg-portal/internal/app" @@ -52,7 +52,7 @@ func (r *Recorder) StartBackgroundJobs(ctx context.Context) { // select blocks until one of the cases evaluate to true } - logrus.Tracef("registered %d audit message within the last hour", 0) // TODO: implement + slog.Debug("audit status", "registered_messages", 0) // TODO: implement } }() } @@ -77,7 +77,7 @@ func (r *Recorder) authLoginEvent(userIdentifier domain.UserIdentifier) { Message: fmt.Sprintf("user %s logged in", userIdentifier), }) if err != nil { - logrus.Errorf("failed to create audit entry for handleAuthLoginEvent: %v", err) + slog.Error("failed to create audit entry for handleAuthLoginEvent", "error", err) return } } diff --git a/internal/app/auth/auth.go b/internal/app/auth/auth.go index 6076237..d06b00f 100644 --- a/internal/app/auth/auth.go +++ b/internal/app/auth/auth.go @@ -7,13 +7,13 @@ import ( "errors" "fmt" "io" + "log/slog" "net/url" "path" "strings" "time" "github.com/coreos/go-oidc/v3/oidc" - "github.com/sirupsen/logrus" evbus "github.com/vardius/message-bus" "github.com/h44z/wg-portal/internal/app" @@ -227,7 +227,7 @@ func (a *Authenticator) passwordAuthentication( rawUserInfo, err := ldapAuth.GetUserInfo(context.Background(), identifier) if err != nil { if !errors.Is(err, domain.ErrNotFound) { - logrus.Warnf("failed to fetch ldap user info for %s: %v", identifier, err) + slog.Warn("failed to fetch ldap user info", "identifier", identifier, "error", err) } continue // user not found / other ldap error } @@ -407,8 +407,10 @@ func (a *Authenticator) registerNewUser( return nil, fmt.Errorf("failed to register new user: %w", err) } - logrus.Tracef("registered user %s from external authentication provider, admin user: %t", - user.Identifier, user.IsAdmin) + slog.Debug("registered user from external authentication provider", + "user", user.Identifier, + "isAdmin", user.IsAdmin, + "provider", source) return user, nil } @@ -483,8 +485,10 @@ func (a *Authenticator) updateExternalUser( return fmt.Errorf("failed to update user: %w", err) } - logrus.Tracef("updated user %s with data from external authentication provider, admin user: %t", - existingUser.Identifier, existingUser.IsAdmin) + slog.Debug("updated user with data from external authentication provider", + "user", existingUser.Identifier, + "isAdmin", existingUser.IsAdmin, + "provider", source) return nil } diff --git a/internal/app/auth/auth_ldap.go b/internal/app/auth/auth_ldap.go index 1e9d144..3fa2fe4 100644 --- a/internal/app/auth/auth_ldap.go +++ b/internal/app/auth/auth_ldap.go @@ -4,10 +4,10 @@ import ( "context" "encoding/json" "fmt" + "log/slog" "strings" "github.com/go-ldap/ldap/v3" - "github.com/sirupsen/logrus" "github.com/h44z/wg-portal/internal" "github.com/h44z/wg-portal/internal/config" @@ -117,7 +117,10 @@ func (l LdapAuthenticator) GetUserInfo(_ context.Context, userId domain.UserIden if l.cfg.LogUserInfo { contents, _ := json.Marshal(users[0]) - logrus.Tracef("User info from LDAP source %s for %s: %v", l.GetName(), userId, string(contents)) + slog.Debug("LDAP user info", + "source", l.GetName(), + "userId", userId, + "info", string(contents)) } return users[0], nil diff --git a/internal/app/auth/auth_oauth.go b/internal/app/auth/auth_oauth.go index 1604a23..e2d141f 100644 --- a/internal/app/auth/auth_oauth.go +++ b/internal/app/auth/auth_oauth.go @@ -5,10 +5,10 @@ import ( "encoding/json" "fmt" "io" + "log/slog" "net/http" "time" - "github.com/sirupsen/logrus" "golang.org/x/oauth2" "github.com/h44z/wg-portal/internal" @@ -111,7 +111,9 @@ func (p PlainOauthAuthenticator) GetUserInfo( } if p.userInfoLogging { - logrus.Tracef("User info from OAuth source %s: %v", p.name, string(contents)) + slog.Debug("OAuth user info", + "source", p.name, + "info", string(contents)) } return userFields, nil diff --git a/internal/app/auth/auth_oidc.go b/internal/app/auth/auth_oidc.go index 03210ba..d213959 100644 --- a/internal/app/auth/auth_oidc.go +++ b/internal/app/auth/auth_oidc.go @@ -5,9 +5,9 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "github.com/coreos/go-oidc/v3/oidc" - "github.com/sirupsen/logrus" "golang.org/x/oauth2" "github.com/h44z/wg-portal/internal/config" @@ -106,7 +106,9 @@ func (o OidcAuthenticator) GetUserInfo(ctx context.Context, token *oauth2.Token, if o.userInfoLogging { contents, _ := json.Marshal(tokenFields) - logrus.Tracef("User info from OIDC source %s: %v", o.name, string(contents)) + slog.Debug("OIDC user info", + "source", o.name, + "info", string(contents)) } return tokenFields, nil diff --git a/internal/app/configfile/manager.go b/internal/app/configfile/manager.go index e5c624f..9f9a4f7 100644 --- a/internal/app/configfile/manager.go +++ b/internal/app/configfile/manager.go @@ -6,10 +6,10 @@ import ( "context" "fmt" "io" + "log/slog" "os" "strings" - "github.com/sirupsen/logrus" evbus "github.com/vardius/message-bus" "github.com/yeqown/go-qrcode/v2" "github.com/yeqown/go-qrcode/writer/compressed" @@ -82,18 +82,22 @@ func (m Manager) handleInterfaceUpdatedEvent(iface *domain.Interface) { return } - logrus.Debugf("handling interface updated event for %s", iface.Identifier) + slog.Debug("handling interface updated event", "interface", iface.Identifier) err := m.PersistInterfaceConfig(context.Background(), iface.Identifier) if err != nil { - logrus.Errorf("failed to automatically persist interface config for %s: %v", iface.Identifier, err) + slog.Error("failed to automatically persist interface config", + "interface", iface.Identifier, + "error", err) } } func (m Manager) handlePeerInterfaceUpdatedEvent(id domain.InterfaceIdentifier) { peerInterface, err := m.wg.GetInterface(context.Background(), id) if err != nil { - logrus.Errorf("failed to load interface %s: %v", id, err) + slog.Error("failed to load interface", + "interface", id, + "error", err) return } @@ -101,11 +105,13 @@ func (m Manager) handlePeerInterfaceUpdatedEvent(id domain.InterfaceIdentifier) return } - logrus.Debugf("handling peer interface updated event for %s", id) + slog.Debug("handling peer interface updated event", "interface", id) err = m.PersistInterfaceConfig(context.Background(), peerInterface.Identifier) if err != nil { - logrus.Errorf("failed to automatically persist interface config for %s: %v", peerInterface.Identifier, err) + slog.Error("failed to automatically persist interface config", + "interface", peerInterface.Identifier, + "error", err) } } diff --git a/internal/app/mail/manager.go b/internal/app/mail/manager.go index 6ef868f..fb3e28c 100644 --- a/internal/app/mail/manager.go +++ b/internal/app/mail/manager.go @@ -4,8 +4,7 @@ import ( "context" "fmt" "io" - - "github.com/sirupsen/logrus" + "log/slog" "github.com/h44z/wg-portal/internal/config" "github.com/h44z/wg-portal/internal/domain" @@ -57,18 +56,25 @@ func (m Manager) SendPeerEmail(ctx context.Context, linkOnly bool, peers ...doma } if peer.UserIdentifier == "" { - logrus.Debugf("skipping peer email for %s, no user linked", peerId) + slog.Debug("skipping peer email", + "peer", peerId, + "reason", "no user linked") continue } user, err := m.users.GetUser(ctx, peer.UserIdentifier) if err != nil { - logrus.Debugf("skipping peer email for %s, unable to fetch user: %v", peerId, err) + slog.Debug("skipping peer email", + "peer", peerId, + "reason", "unable to fetch user", + "error", err) continue } if user.Email == "" { - logrus.Debugf("skipping peer email for %s, user has no mail address", peerId) + slog.Debug("skipping peer email", + "peer", peerId, + "reason", "user has no mail address") continue } diff --git a/internal/app/migrate_v1.go b/internal/app/migrate_v1.go index 807a4d7..9eb323e 100644 --- a/internal/app/migrate_v1.go +++ b/internal/app/migrate_v1.go @@ -3,10 +3,10 @@ package app import ( "errors" "fmt" + "log/slog" "os" "time" - "github.com/sirupsen/logrus" "gorm.io/gorm" "github.com/h44z/wg-portal/internal/adapters" @@ -50,7 +50,7 @@ func migrateFromV1(db *gorm.DB, source, typ string) error { return fmt.Errorf("unsupported old version, update to database version %s first: %w", latestVersion, err) } - logrus.Infof("Found valid V1 database with version: %s", lastVersion.Version) + slog.Info("found valid V1 database", "version", lastVersion.Version) if err := migrateV1Users(oldDb, db); err != nil { return fmt.Errorf("user migration failed: %w", err) @@ -64,7 +64,8 @@ func migrateFromV1(db *gorm.DB, source, typ string) error { return fmt.Errorf("peer migration failed: %w", err) } - logrus.Infof("Migrated V1 database with version %s, please restart WireGuard Portal", lastVersion.Version) + slog.Info("migrated V1 database successfully, please restart WireGuard Portal", + "version", lastVersion.Version) return nil } @@ -126,7 +127,7 @@ func migrateV1Users(oldDb, newDb *gorm.DB) error { return fmt.Errorf("failed to migrate user %s: %w", oldUser.Email, err) } - logrus.Debugf(" - User %s migrated", newUser.Identifier) + slog.Debug("user migrated successfully", "identifier", newUser.Identifier) } return nil @@ -220,7 +221,7 @@ func migrateV1Interfaces(oldDb, newDb *gorm.DB) error { return fmt.Errorf("failed to migrate device %s: %w", oldDevice.DeviceName, err) } - logrus.Debugf(" - Interface %s migrated", newInterface.Identifier) + slog.Debug("interface migrated successfully", "identifier", newInterface.Identifier) } return nil @@ -319,7 +320,7 @@ func migrateV1Peers(oldDb, newDb *gorm.DB) error { return fmt.Errorf("failed to migrate dummy user %s: %w", oldPeer.Email, err) } - logrus.Debugf(" - Dummy User %s migrated", user.Identifier) + slog.Debug("dummy user migrated successfully", "identifier", user.Identifier) } newPeer := domain.Peer{ BaseModel: domain.BaseModel{ @@ -365,7 +366,7 @@ func migrateV1Peers(oldDb, newDb *gorm.DB) error { return fmt.Errorf("failed to migrate peer %s (%s): %w", oldPeer.Identifier, oldPeer.PublicKey, err) } - logrus.Debugf(" - Peer %s migrated", newPeer.Identifier) + slog.Debug("peer migrated successfully", "identifier", newPeer.Identifier) } return nil diff --git a/internal/app/route/routes.go b/internal/app/route/routes.go index b8e51fb..864f9a1 100644 --- a/internal/app/route/routes.go +++ b/internal/app/route/routes.go @@ -3,8 +3,8 @@ package route import ( "context" "fmt" + "log/slog" - "github.com/sirupsen/logrus" evbus "github.com/vardius/message-bus" "github.com/vishvananda/netlink" "golang.org/x/sys/unix" @@ -67,31 +67,33 @@ func (m Manager) StartBackgroundJobs(_ context.Context) { } func (m Manager) handleRouteUpdateEvent(srcDescription string) { - logrus.Debugf("handling route update event: %s", srcDescription) + slog.Debug("handling route update event", "source", srcDescription) err := m.syncRoutes(context.Background()) if err != nil { - logrus.Errorf("failed to synchronize routes for event %s: %v", srcDescription, err) + slog.Error("failed to synchronize routes", + "source", srcDescription, + "error", err) } - logrus.Debugf("routes synchronized, event: %s", srcDescription) + slog.Debug("routes synchronized", "source", srcDescription) } func (m Manager) handleRouteRemoveEvent(info domain.RoutingTableInfo) { - logrus.Debugf("handling route remove event for: %s", info.String()) + slog.Debug("handling route remove event", "info", info.String()) if !info.ManagementEnabled() { return // route management disabled } if err := m.removeFwMarkRules(info.FwMark, info.GetRoutingTable(), netlink.FAMILY_V4); err != nil { - logrus.Errorf("failed to remove v4 fwmark rules: %v", err) + slog.Error("failed to remove v4 fwmark rules", "error", err) } if err := m.removeFwMarkRules(info.FwMark, info.GetRoutingTable(), netlink.FAMILY_V6); err != nil { - logrus.Errorf("failed to remove v6 fwmark rules: %v", err) + slog.Error("failed to remove v6 fwmark rules", "error", err) } - logrus.Debugf("routes removed, table: %s", info.String()) + slog.Debug("routes removed", "table", info.String()) } func (m Manager) syncRoutes(ctx context.Context) error { @@ -437,14 +439,18 @@ func (m Manager) getRoutingTableAndFwMark(iface *domain.Interface, link netlink. if fwmark == 0 { // generate a new (temporary) firewall mark based on the interface index fwmark = uint32(m.cfg.Advanced.RouteTableOffset + link.Attrs().Index) - logrus.Debugf("%s: using fwmark %d to handle routes", iface.Identifier, table) + slog.Debug("using fwmark to handle routes", + "interface", iface.Identifier, + "fwmark", fwmark) // apply the temporary fwmark to the wireguard interface err = m.setFwMark(iface.Identifier, int(fwmark)) } if table == 0 { table = int(fwmark) // generate a new routing table base on interface index - logrus.Debugf("%s: using routing table %d to handle default routes", iface.Identifier, table) + slog.Debug("using routing table to handle default routes", + "interface", iface.Identifier, + "table", table) } return } diff --git a/internal/app/users/user_manager.go b/internal/app/users/user_manager.go index 3db15bb..85c50cc 100644 --- a/internal/app/users/user_manager.go +++ b/internal/app/users/user_manager.go @@ -4,13 +4,13 @@ import ( "context" "errors" "fmt" + "log/slog" "math" "sync" "time" "github.com/go-ldap/ldap/v3" "github.com/google/uuid" - "github.com/sirupsen/logrus" evbus "github.com/vardius/message-bus" "github.com/h44z/wg-portal/internal" @@ -419,7 +419,7 @@ func (m Manager) runLdapSynchronizationService(ctx context.Context) { go func(cfg config.LdapProvider) { syncInterval := cfg.SyncInterval if syncInterval == 0 { - logrus.Debugf("sync disabled for LDAP server: %s", cfg.ProviderName) + slog.Debug("sync disabled for LDAP server", "provider", cfg.ProviderName) return } @@ -435,7 +435,7 @@ func (m Manager) runLdapSynchronizationService(ctx context.Context) { err := m.synchronizeLdapUsers(ctx, &cfg) if err != nil { - logrus.Errorf("failed to synchronize LDAP users for %s: %v", cfg.ProviderName, err) + slog.Error("failed to synchronize LDAP users", "provider", cfg.ProviderName, "error", err) } } }(ldapCfg) @@ -443,7 +443,7 @@ func (m Manager) runLdapSynchronizationService(ctx context.Context) { } func (m Manager) synchronizeLdapUsers(ctx context.Context, provider *config.LdapProvider) error { - logrus.Tracef("starting to synchronize users for %s", provider.ProviderName) + slog.Debug("starting to synchronize users", "provider", provider.ProviderName) dn, err := ldap.ParseDN(provider.AdminGroupDN) if err != nil { @@ -462,7 +462,7 @@ func (m Manager) synchronizeLdapUsers(ctx context.Context, provider *config.Ldap return err } - logrus.Tracef("fetched %d raw ldap users from provider %s...", len(rawUsers), provider.ProviderName) + slog.Debug("fetched raw ldap users", "count", len(rawUsers), "provider", provider.ProviderName) // Update existing LDAP users err = m.updateLdapUsers(ctx, provider, rawUsers, &provider.FieldMap, provider.ParsedAdminGroupDN) @@ -504,7 +504,7 @@ func (m Manager) updateLdapUsers( if existingUser == nil { // create new user - logrus.Tracef("creating new user %s from provider %s...", user.Identifier, provider.ProviderName) + slog.Debug("creating new user from provider", "user", user.Identifier, "provider", provider.ProviderName) err := m.NewUser(tctx, user) if err != nil { @@ -588,7 +588,7 @@ func (m Manager) disableMissingLdapUsers( continue } - logrus.Tracef("user %s is missing in ldap provider %s, disabling", user.Identifier, providerName) + slog.Debug("user is missing in ldap provider, disabling", "user", user.Identifier, "provider", providerName) now := time.Now() user.Disabled = &now diff --git a/internal/app/wireguard/statistics.go b/internal/app/wireguard/statistics.go index c891741..ed9bf75 100644 --- a/internal/app/wireguard/statistics.go +++ b/internal/app/wireguard/statistics.go @@ -2,11 +2,11 @@ package wireguard import ( "context" + "log/slog" "sync" "time" probing "github.com/prometheus-community/pro-bing" - "github.com/sirupsen/logrus" evbus "github.com/vardius/message-bus" "github.com/h44z/wg-portal/internal/app" @@ -60,7 +60,7 @@ func (c *StatisticsCollector) startInterfaceDataFetcher(ctx context.Context) { go c.collectInterfaceData(ctx) - logrus.Tracef("started interface data fetcher") + slog.Debug("started interface data fetcher") } func (c *StatisticsCollector) collectInterfaceData(ctx context.Context) { @@ -74,14 +74,15 @@ func (c *StatisticsCollector) collectInterfaceData(ctx context.Context) { case <-ticker.C: interfaces, err := c.db.GetAllInterfaces(ctx) if err != nil { - logrus.Warnf("failed to fetch all interfaces for data collection: %v", err) + slog.Warn("failed to fetch all interfaces for data collection", "error", err) continue } for _, in := range interfaces { physicalInterface, err := c.wg.GetInterface(ctx, in.Identifier) if err != nil { - logrus.Warnf("failed to load physical interface %s for data collection: %v", in.Identifier, err) + slog.Warn("failed to load physical interface for data collection", "interface", in.Identifier, + "error", err) continue } err = c.db.UpdateInterfaceStatus(ctx, in.Identifier, @@ -96,9 +97,9 @@ func (c *StatisticsCollector) collectInterfaceData(ctx context.Context) { return i, nil }) if err != nil { - logrus.Warnf("failed to update interface status for %s: %v", in.Identifier, err) + slog.Warn("failed to update interface status", "interface", in.Identifier, "error", err) } - logrus.Tracef("updated interface status for %s", in.Identifier) + slog.Debug("updated interface status", "interface", in.Identifier) } } } @@ -111,7 +112,7 @@ func (c *StatisticsCollector) startPeerDataFetcher(ctx context.Context) { go c.collectPeerData(ctx) - logrus.Tracef("started peer data fetcher") + slog.Debug("started peer data fetcher") } func (c *StatisticsCollector) collectPeerData(ctx context.Context) { @@ -125,14 +126,14 @@ func (c *StatisticsCollector) collectPeerData(ctx context.Context) { case <-ticker.C: interfaces, err := c.db.GetAllInterfaces(ctx) if err != nil { - logrus.Warnf("failed to fetch all interfaces for peer data collection: %v", err) + slog.Warn("failed to fetch all interfaces for peer data collection", "error", err) continue } for _, in := range interfaces { peers, err := c.wg.GetPeers(ctx, in.Identifier) if err != nil { - logrus.Warnf("failed to fetch peers for data collection (interface %s): %v", in.Identifier, err) + slog.Warn("failed to fetch peers for data collection", "interface", in.Identifier, "error", err) continue } for _, peer := range peers { @@ -158,9 +159,9 @@ func (c *StatisticsCollector) collectPeerData(ctx context.Context) { return p, nil }) if err != nil { - logrus.Warnf("failed to update peer status for %s: %v", peer.Identifier, err) + slog.Warn("failed to update peer status", "peer", peer.Identifier, "error", err) } else { - logrus.Tracef("updated peer status for %s", peer.Identifier) + slog.Debug("updated peer status", "peer", peer.Identifier) } } } @@ -221,12 +222,12 @@ func (c *StatisticsCollector) startPingWorkers(ctx context.Context) { go func() { c.pingWaitGroup.Wait() - logrus.Tracef("stopped ping checks") + slog.Debug("stopped ping checks") }() go c.enqueuePingChecks(ctx) - logrus.Tracef("started ping checks") + slog.Debug("started ping checks") } func (c *StatisticsCollector) enqueuePingChecks(ctx context.Context) { @@ -242,14 +243,14 @@ func (c *StatisticsCollector) enqueuePingChecks(ctx context.Context) { case <-ticker.C: interfaces, err := c.db.GetAllInterfaces(ctx) if err != nil { - logrus.Warnf("failed to fetch all interfaces for ping checks: %v", err) + slog.Warn("failed to fetch all interfaces for ping checks", "error", err) continue } for _, in := range interfaces { peers, err := c.db.GetInterfacePeers(ctx, in.Identifier) if err != nil { - logrus.Warnf("failed to fetch peers for ping checks (interface %s): %v", in.Identifier, err) + slog.Warn("failed to fetch peers for ping checks", "interface", in.Identifier, "error", err) continue } for _, peer := range peers { @@ -264,7 +265,7 @@ func (c *StatisticsCollector) pingWorker(ctx context.Context) { defer c.pingWaitGroup.Done() for peer := range c.pingJobs { peerPingable := c.isPeerPingable(ctx, peer) - logrus.Tracef("peer %s pingable: %t", peer.Identifier, peerPingable) + slog.Debug("peer ping check completed", "peer", peer.Identifier, "pingable", peerPingable) now := time.Now() err := c.db.UpdatePeerStatus(ctx, peer.Identifier, @@ -283,9 +284,9 @@ func (c *StatisticsCollector) pingWorker(ctx context.Context) { return p, nil }) if err != nil { - logrus.Warnf("failed to update peer ping status for %s: %v", peer.Identifier, err) + slog.Warn("failed to update peer ping status", "peer", peer.Identifier, "error", err) } else { - logrus.Tracef("updated peer ping status for %s", peer.Identifier) + slog.Debug("updated peer ping status", "peer", peer.Identifier) } } } @@ -302,7 +303,7 @@ func (c *StatisticsCollector) isPeerPingable(ctx context.Context, peer domain.Pe pinger, err := probing.NewPinger(checkAddr) if err != nil { - logrus.Tracef("failed to instatiate pinger for %s (%s): %v", peer.Identifier, checkAddr, err) + slog.Debug("failed to instantiate pinger", "peer", peer.Identifier, "address", checkAddr, "error", err) return false } @@ -312,7 +313,7 @@ func (c *StatisticsCollector) isPeerPingable(ctx context.Context, peer domain.Pe pinger.Timeout = 2 * time.Second err = pinger.RunWithContext(ctx) // Blocks until finished. if err != nil { - logrus.Tracef("pinger for peer %s (%s) exited unexpectedly: %v", peer.Identifier, checkAddr, err) + slog.Debug("pinger for peer exited unexpectedly", "peer", peer.Identifier, "address", checkAddr, "error", err) return false } stats := pinger.Statistics() @@ -327,7 +328,7 @@ func (c *StatisticsCollector) updatePeerMetrics(ctx context.Context, status doma // Fetch peer data from the database peer, err := c.db.GetPeer(ctx, status.PeerId) if err != nil { - logrus.Warnf("failed to fetch peer data for metrics %s: %v", status.PeerId, err) + slog.Warn("failed to fetch peer data for metrics", "peer", status.PeerId, "error", err) return } c.ms.UpdatePeerMetrics(peer, status) @@ -343,7 +344,7 @@ func (c *StatisticsCollector) handlePeerIdentifierChangeEvent(oldIdentifier, new // remove potential left-over status data err := c.db.DeletePeerStatus(ctx, oldIdentifier) if err != nil { - logrus.Errorf("failed to delete old peer status for migrated peer, %s -> %s: %v", - oldIdentifier, newIdentifier, err) + slog.Error("failed to delete old peer status for migrated peer", "oldIdentifier", oldIdentifier, + "newIdentifier", newIdentifier, "error", err) } } diff --git a/internal/app/wireguard/wireguard.go b/internal/app/wireguard/wireguard.go index 07f2b09..82fd55a 100644 --- a/internal/app/wireguard/wireguard.go +++ b/internal/app/wireguard/wireguard.go @@ -2,9 +2,9 @@ package wireguard import ( "context" + "log/slog" "time" - "github.com/sirupsen/logrus" evbus "github.com/vardius/message-bus" "github.com/h44z/wg-portal/internal/app" @@ -58,12 +58,12 @@ func (m Manager) handleUserCreationEvent(user *domain.User) { return } - logrus.Tracef("handling new user event for %s", user.Identifier) + slog.Debug("handling new user event", "user", user.Identifier) ctx := domain.SetUserInfo(context.Background(), domain.SystemAdminContextUserInfo()) err := m.CreateDefaultPeer(ctx, user.Identifier) if err != nil { - logrus.Errorf("failed to create default peer for %s: %v", user.Identifier, err) + slog.Error("failed to create default peer", "user", user.Identifier, "error", err) return } } @@ -75,7 +75,9 @@ func (m Manager) handleUserLoginEvent(userId domain.UserIdentifier) { userPeers, err := m.db.GetUserPeers(context.Background(), userId) if err != nil { - logrus.Errorf("failed to retrieve existing peers for %s prior to default peer creation: %v", userId, err) + slog.Error("failed to retrieve existing peers prior to default peer creation", + "user", userId, + "error", err) return } @@ -83,12 +85,12 @@ func (m Manager) handleUserLoginEvent(userId domain.UserIdentifier) { return // user already has peers, skip creation } - logrus.Tracef("handling new user login for %s", userId) + slog.Debug("handling new user login", "user", userId) ctx := domain.SetUserInfo(context.Background(), domain.SystemAdminContextUserInfo()) err = m.CreateDefaultPeer(ctx, userId) if err != nil { - logrus.Errorf("failed to create default peer for %s: %v", userId, err) + slog.Error("failed to create default peer", "user", userId, "error", err) return } } @@ -97,7 +99,9 @@ func (m Manager) handleUserDisabledEvent(user domain.User) { ctx := domain.SetUserInfo(context.Background(), domain.SystemAdminContextUserInfo()) userPeers, err := m.db.GetUserPeers(ctx, user.Identifier) if err != nil { - logrus.Errorf("failed to retrieve peers for disabled user %s: %v", user.Identifier, err) + slog.Error("failed to retrieve peers for disabled user", + "user", user.Identifier, + "error", err) return } @@ -106,15 +110,19 @@ func (m Manager) handleUserDisabledEvent(user domain.User) { continue // peer is already disabled } - logrus.Debugf("disabling peer %s due to user %s being disabled", peer.Identifier, user.Identifier) + slog.Debug("disabling peer due to user being disabled", + "peer", peer.Identifier, + "user", user.Identifier) peer.Disabled = user.Disabled // set to user disabled timestamp peer.DisabledReason = domain.DisabledReasonUserDisabled _, err := m.UpdatePeer(ctx, &peer) if err != nil { - logrus.Errorf("failed to disable peer %s for disabled user %s: %v", - peer.Identifier, user.Identifier, err) + slog.Error("failed to disable peer for disabled user", + "peer", peer.Identifier, + "user", user.Identifier, + "error", err) } } } @@ -127,7 +135,9 @@ func (m Manager) handleUserEnabledEvent(user domain.User) { ctx := domain.SetUserInfo(context.Background(), domain.SystemAdminContextUserInfo()) userPeers, err := m.db.GetUserPeers(ctx, user.Identifier) if err != nil { - logrus.Errorf("failed to retrieve peers for re-enabled user %s: %v", user.Identifier, err) + slog.Error("failed to retrieve peers for re-enabled user", + "user", user.Identifier, + "error", err) return } @@ -140,15 +150,19 @@ func (m Manager) handleUserEnabledEvent(user domain.User) { continue // peer was disabled for another reason } - logrus.Debugf("enabling peer %s due to user %s being enabled", peer.Identifier, user.Identifier) + slog.Debug("enabling peer due to user being enabled", + "peer", peer.Identifier, + "user", user.Identifier) peer.Disabled = nil peer.DisabledReason = "" _, err := m.UpdatePeer(ctx, &peer) if err != nil { - logrus.Errorf("failed to enable peer %s for enabled user %s: %v", - peer.Identifier, user.Identifier, err) + slog.Error("failed to enable peer for enabled user", + "peer", peer.Identifier, + "user", user.Identifier, + "error", err) } } return @@ -158,7 +172,9 @@ func (m Manager) handleUserDeletedEvent(user domain.User) { ctx := domain.SetUserInfo(context.Background(), domain.SystemAdminContextUserInfo()) userPeers, err := m.db.GetUserPeers(ctx, user.Identifier) if err != nil { - logrus.Errorf("failed to retrieve peers for deleted user %s: %v", user.Identifier, err) + slog.Error("failed to retrieve peers for deleted user", + "user", user.Identifier, + "error", err) return } @@ -169,14 +185,20 @@ func (m Manager) handleUserDeletedEvent(user domain.User) { } if m.cfg.Core.DeletePeerAfterUserDeleted { - logrus.Debugf("deleting peer %s due to user %s being deleted", peer.Identifier, user.Identifier) + slog.Debug("deleting peer due to user being deleted", + "peer", peer.Identifier, + "user", user.Identifier) if err := m.DeletePeer(ctx, peer.Identifier); err != nil { - logrus.Errorf("failed to delete peer %s for deleted user %s: %v", - peer.Identifier, user.Identifier, err) + slog.Error("failed to delete peer for deleted user", + "peer", peer.Identifier, + "user", user.Identifier, + "error", err) } } else { - logrus.Debugf("disabling peer %s due to user %s being deleted", peer.Identifier, user.Identifier) + slog.Debug("disabling peer due to user being deleted", + "peer", peer.Identifier, + "user", user.Identifier) peer.UserIdentifier = "" // remove user reference peer.Disabled = &deletionTime @@ -184,8 +206,10 @@ func (m Manager) handleUserDeletedEvent(user domain.User) { _, err := m.UpdatePeer(ctx, &peer) if err != nil { - logrus.Errorf("failed to disable peer %s for deleted user %s: %v", - peer.Identifier, user.Identifier, err) + slog.Error("failed to disable peer for deleted user", + "peer", peer.Identifier, + "user", user.Identifier, + "error", err) } } } @@ -206,14 +230,16 @@ func (m Manager) runExpiredPeersCheck(ctx context.Context) { interfaces, err := m.db.GetAllInterfaces(ctx) if err != nil { - logrus.Errorf("failed to fetch all interfaces for expiry check: %v", err) + slog.Error("failed to fetch all interfaces for expiry check", "error", err) continue } for _, iface := range interfaces { peers, err := m.db.GetInterfacePeers(ctx, iface.Identifier) if err != nil { - logrus.Errorf("failed to fetch all peers from interface %s for expiry check: %v", iface.Identifier, err) + slog.Error("failed to fetch all peers from interface for expiry check", + "interface", iface.Identifier, + "error", err) continue } @@ -227,14 +253,14 @@ func (m Manager) checkExpiredPeers(ctx context.Context, peers []domain.Peer) { for _, peer := range peers { if peer.IsExpired() && !peer.IsDisabled() { - logrus.Infof("peer %s has expired, disabling...", peer.Identifier) + slog.Info("peer has expired, disabling", "peer", peer.Identifier) peer.Disabled = &now peer.DisabledReason = domain.DisabledReasonExpired _, err := m.UpdatePeer(ctx, &peer) if err != nil { - logrus.Errorf("failed to update expired peer %s: %v", peer.Identifier, err) + slog.Error("failed to update expired peer", "peer", peer.Identifier, "error", err) } } } diff --git a/internal/app/wireguard/wireguard_interfaces.go b/internal/app/wireguard/wireguard_interfaces.go index 706be66..ae6f0aa 100644 --- a/internal/app/wireguard/wireguard_interfaces.go +++ b/internal/app/wireguard/wireguard_interfaces.go @@ -4,12 +4,11 @@ import ( "context" "errors" "fmt" + "log/slog" "os" "slices" "time" - "github.com/sirupsen/logrus" - "github.com/h44z/wg-portal/internal/app" "github.com/h44z/wg-portal/internal/domain" ) @@ -130,7 +129,7 @@ func (m Manager) ImportNewInterfaces(ctx context.Context, filter ...domain.Inter continue } - logrus.Infof("importing new interface %s...", physicalInterface.Identifier) + slog.Info("importing new interface", "interface", physicalInterface.Identifier) physicalPeers, err := m.wg.GetPeers(ctx, physicalInterface.Identifier) if err != nil { @@ -142,7 +141,7 @@ func (m Manager) ImportNewInterfaces(ctx context.Context, filter ...domain.Inter return 0, fmt.Errorf("import of %s failed: %w", physicalInterface.Identifier, err) } - logrus.Infof("imported new interface %s and %d peers", physicalInterface.Identifier, len(physicalPeers)) + slog.Info("imported new interface", "interface", physicalInterface.Identifier, "peers", len(physicalPeers)) imported++ } @@ -206,7 +205,7 @@ func (m Manager) RestoreInterfaceState( _, err = m.wg.GetInterface(ctx, iface.Identifier) if err != nil && !iface.IsDisabled() { - logrus.Debugf("creating missing interface %s...", iface.Identifier) + slog.Debug("creating missing interface", "interface", iface.Identifier) // try to create a new interface _, err = m.saveInterface(ctx, &iface) @@ -224,7 +223,7 @@ func (m Manager) RestoreInterfaceState( return fmt.Errorf("failed to create physical interface %s: %w", iface.Identifier, err) } } else { - logrus.Debugf("restoring interface state for %s to disabled=%t", iface.Identifier, iface.IsDisabled()) + slog.Debug("restoring interface state", "interface", iface.Identifier, "disabled", iface.IsDisabled()) // try to move interface to stored state _, err = m.saveInterface(ctx, &iface) diff --git a/internal/app/wireguard/wireguard_peers.go b/internal/app/wireguard/wireguard_peers.go index 4b7fdcf..8418d85 100644 --- a/internal/app/wireguard/wireguard_peers.go +++ b/internal/app/wireguard/wireguard_peers.go @@ -4,10 +4,9 @@ import ( "context" "errors" "fmt" + "log/slog" "time" - "github.com/sirupsen/logrus" - "github.com/h44z/wg-portal/internal/app" "github.com/h44z/wg-portal/internal/domain" ) @@ -49,7 +48,9 @@ func (m Manager) CreateDefaultPeer(ctx context.Context, userId domain.UserIdenti } } - logrus.Infof("created %d default peers for user %s", len(newPeers), userId) + slog.InfoContext(ctx, "created default peers for user", + "user", userId, + "count", len(newPeers)) return nil } diff --git a/internal/config/auth.go b/internal/config/auth.go index fde955c..d115aeb 100644 --- a/internal/config/auth.go +++ b/internal/config/auth.go @@ -1,11 +1,11 @@ package config import ( + "log/slog" "regexp" "time" "github.com/go-ldap/ldap/v3" - "github.com/sirupsen/logrus" ) type Auth struct { @@ -77,7 +77,8 @@ func (o *OauthAdminMapping) GetAdminValueRegex() *regexp.Regexp { adminRegex, err := regexp.Compile(o.AdminValueRegex) if err != nil { - logrus.Fatalf("failed to compile admin_value_regex: %v", err) + slog.Error("failed to compile admin_value_regex", "error", err) + panic("failed to compile admin_value_regex") } o.adminValueRegex = adminRegex @@ -98,7 +99,8 @@ func (o *OauthAdminMapping) GetAdminGroupRegex() *regexp.Regexp { groupRegex, err := regexp.Compile(o.AdminGroupRegex) if err != nil { - logrus.Fatalf("failed to compile admin_group_regex: %v", err) + slog.Error("failed to compile admin_group_regex", "error", err) + panic("failed to compile admin_group_regex") } o.adminGroupRegex = groupRegex diff --git a/internal/config/config.go b/internal/config/config.go index 6f023f5..6c2a819 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -2,11 +2,11 @@ package config import ( "fmt" + "log/slog" "os" "time" "github.com/a8m/envsubst" - "github.com/sirupsen/logrus" "gopkg.in/yaml.v3" ) @@ -65,29 +65,32 @@ type Config struct { // LogStartupValues logs the startup values of the configuration in debug level func (c *Config) LogStartupValues() { - logrus.Infof("Log Level: %s", c.Advanced.LogLevel) + slog.Info("Configuration loaded!", "logLevel", c.Advanced.LogLevel) - logrus.Debug("WireGuard Portal Features:") - logrus.Debugf(" - EditableKeys: %t", c.Core.EditableKeys) - logrus.Debugf(" - CreateDefaultPeerOnCreation: %t", c.Core.CreateDefaultPeerOnCreation) - logrus.Debugf(" - ReEnablePeerAfterUserEnable: %t", c.Core.ReEnablePeerAfterUserEnable) - logrus.Debugf(" - DeletePeerAfterUserDeleted: %t", c.Core.DeletePeerAfterUserDeleted) - logrus.Debugf(" - SelfProvisioningAllowed: %t", c.Core.SelfProvisioningAllowed) - logrus.Debugf(" - ImportExisting: %t", c.Core.ImportExisting) - logrus.Debugf(" - RestoreState: %t", c.Core.RestoreState) - logrus.Debugf(" - UseIpV6: %t", c.Advanced.UseIpV6) - logrus.Debugf(" - CollectInterfaceData: %t", c.Statistics.CollectInterfaceData) - logrus.Debugf(" - CollectPeerData: %t", c.Statistics.CollectPeerData) - logrus.Debugf(" - CollectAuditData: %t", c.Statistics.CollectAuditData) + slog.Debug("Config Features", + "editableKeys", c.Core.EditableKeys, + "createDefaultPeerOnCreation", c.Core.CreateDefaultPeerOnCreation, + "reEnablePeerAfterUserEnable", c.Core.ReEnablePeerAfterUserEnable, + "deletePeerAfterUserDeleted", c.Core.DeletePeerAfterUserDeleted, + "selfProvisioningAllowed", c.Core.SelfProvisioningAllowed, + "importExisting", c.Core.ImportExisting, + "restoreState", c.Core.RestoreState, + "useIpV6", c.Advanced.UseIpV6, + "collectInterfaceData", c.Statistics.CollectInterfaceData, + "collectPeerData", c.Statistics.CollectPeerData, + "collectAuditData", c.Statistics.CollectAuditData, + ) - logrus.Debug("WireGuard Portal Settings:") - logrus.Debugf(" - ConfigStoragePath: %s", c.Advanced.ConfigStoragePath) - logrus.Debugf(" - ExternalUrl: %s", c.Web.ExternalUrl) + slog.Debug("Config Settings", + "configStoragePath", c.Advanced.ConfigStoragePath, + "externalUrl", c.Web.ExternalUrl, + ) - logrus.Debug("WireGuard Portal Authentication:") - logrus.Debugf(" - OIDC Providers: %d", len(c.Auth.OpenIDConnect)) - logrus.Debugf(" - OAuth Providers: %d", len(c.Auth.OAuth)) - logrus.Debugf(" - Ldap Providers: %d", len(c.Auth.Ldap)) + slog.Debug("Config Authentication", + "oidcProviders", len(c.Auth.OpenIDConnect), + "oauthProviders", len(c.Auth.OAuth), + "ldapProviders", len(c.Auth.Ldap), + ) } // defaultConfig returns the default configuration @@ -180,7 +183,7 @@ func loadConfigFile(cfg any, filename string) error { data, err := envsubst.ReadFile(filename) if err != nil { if os.IsNotExist(err) { - logrus.Warnf("Config file %s not found, using default values", filename) + slog.Warn("Config file not found, using default values", "filename", filename) return nil } return fmt.Errorf("envsubst error: %v", err) diff --git a/internal/domain/context.go b/internal/domain/context.go index 4bb9ac1..f36706c 100644 --- a/internal/domain/context.go +++ b/internal/domain/context.go @@ -3,9 +3,9 @@ package domain import ( "context" "fmt" + "log/slog" "github.com/gin-gonic/gin" - "github.com/sirupsen/logrus" ) const CtxUserInfo = "userInfo" @@ -95,7 +95,10 @@ func ValidateUserAccessRights(ctx context.Context, requiredUser UserIdentifier) return nil // User can access own data } - logrus.Warnf("insufficient permissions for %s (want %s), stack: %s", sessionUser.Id, requiredUser, GetStackTrace()) + slog.Warn("insufficient permissions", + "user", sessionUser.Id, + "requiredUser", requiredUser, + "stack", GetStackTrace()) return ErrNoPermission } @@ -107,6 +110,8 @@ func ValidateAdminAccessRights(ctx context.Context) error { return nil } - logrus.Warnf("insufficient admin permissions for %s, stack: %s", sessionUser.Id, GetStackTrace()) + slog.Warn("insufficient admin permissions", + "user", sessionUser.Id, + "stack", GetStackTrace()) return ErrNoPermission } diff --git a/internal/domain/interface.go b/internal/domain/interface.go index 32471d2..0007c9f 100644 --- a/internal/domain/interface.go +++ b/internal/domain/interface.go @@ -2,6 +2,7 @@ package domain import ( "fmt" + "log/slog" "math" "net" "regexp" @@ -9,8 +10,6 @@ import ( "strings" "time" - "github.com/sirupsen/logrus" - "github.com/h44z/wg-portal/internal" ) @@ -166,18 +165,22 @@ func (i *Interface) GetRoutingTable() int { numberStr := strings.ReplaceAll(routingTableStr, "0x", "") routingTable, err := strconv.ParseUint(numberStr, 16, 64) if err != nil { - logrus.Errorf("invalid hex routing table %s: %v", routingTableStr, err) + slog.Error("failed to parse routing table number", "table", routingTableStr, "error", err) return -1 } if routingTable > math.MaxInt32 { - logrus.Errorf("invalid routing table %s, too big", routingTableStr) + slog.Error("routing table number too large", "table", routingTable, "max", math.MaxInt32) return -1 } return int(routingTable) default: routingTable, err := strconv.Atoi(routingTableStr) if err != nil { - logrus.Errorf("invalid routing table %s: %v", routingTableStr, err) + slog.Error("failed to parse routing table number", "table", routingTableStr, "error", err) + return -1 + } + if routingTable > math.MaxInt32 { + slog.Error("routing table number too large", "table", routingTable, "max", math.MaxInt32) return -1 } return routingTable diff --git a/internal/ldap_utils.go b/internal/ldap_utils.go index 26d4b9e..45e1ad4 100644 --- a/internal/ldap_utils.go +++ b/internal/ldap_utils.go @@ -3,10 +3,10 @@ package internal import ( "crypto/tls" "fmt" + "log/slog" "os" "github.com/go-ldap/ldap/v3" - "github.com/sirupsen/logrus" "github.com/h44z/wg-portal/internal/config" ) @@ -75,7 +75,7 @@ func LdapConnect(cfg *config.LdapProvider) (*ldap.Conn, error) { func LdapDisconnect(conn *ldap.Conn) { if conn != nil { if err := conn.Close(); err != nil { - logrus.Errorf("failed to close ldap connection: %v", err) + slog.Error("failed to close ldap connection", "error", err) } } } diff --git a/internal/logger.go b/internal/logger.go new file mode 100644 index 0000000..994c236 --- /dev/null +++ b/internal/logger.go @@ -0,0 +1,168 @@ +package internal + +import ( + "context" + "fmt" + "io" + "log/slog" + "os" + "runtime" + "strconv" + "strings" + "sync" +) + +// SetupLogging initializes the global logger with the given level and format +func SetupLogging(level string, pretty, json bool) { + var logLevel = new(slog.LevelVar) + + switch strings.ToLower(level) { + case "trace", "debug": + logLevel.Set(slog.LevelDebug) + case "info", "information": + logLevel.Set(slog.LevelInfo) + case "warn", "warning": + logLevel.Set(slog.LevelWarn) + case "error": + logLevel.Set(slog.LevelError) + default: + logLevel.Set(slog.LevelInfo) + } + + opts := &slog.HandlerOptions{ + Level: logLevel, + } + + // send everything to stderr as suggested in https://www.gnu.org/software/libc/manual/html_node/Standard-Streams.html + output := os.Stderr + + var handler slog.Handler + switch { + case json: + handler = slog.NewJSONHandler(output, opts) + case pretty: + handler = NewPrettyHandler(output, opts) + default: + handler = slog.NewTextHandler(output, opts) + } + + logger := slog.New(handler) + + slog.SetDefault(logger) +} + +// PrettyHandler is a slog.Handler that formats log records in a human-readable way. +// It mimics the behavior of the slog.Default() handler. +type PrettyHandler struct { + opts slog.HandlerOptions + prefix string // preformatted group names followed by a dot + preformat string // preformatted Attrs, with an initial space + timeFormat string + + mu sync.Mutex + w io.Writer +} + +// NewPrettyHandler creates a new PrettyHandler. +func NewPrettyHandler(w io.Writer, opts *slog.HandlerOptions) *PrettyHandler { + h := &PrettyHandler{w: w} + if opts != nil { + h.opts = *opts + } + if h.opts.ReplaceAttr == nil { + h.opts.ReplaceAttr = func(_ []string, a slog.Attr) slog.Attr { return a } + } + + h.timeFormat = "2006/01/02 15:04:05" + + return h +} + +// Enabled reports whether the handler handles records at the given level. +func (h *PrettyHandler) Enabled(_ context.Context, level slog.Level) bool { + minLevel := slog.LevelInfo + if h.opts.Level != nil { + minLevel = h.opts.Level.Level() + } + return level >= minLevel +} + +// WithGroup returns a new Handler with the given group appended to the handler's +func (h *PrettyHandler) WithGroup(name string) slog.Handler { + return &PrettyHandler{ + w: h.w, + opts: h.opts, + preformat: h.preformat, + prefix: h.prefix + name + ".", + } +} + +// WithAttrs returns a new Handler whose attributes consist of the handler's +func (h *PrettyHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + var buf []byte + for _, a := range attrs { + buf = h.appendAttr(buf, h.prefix, a) + } + return &PrettyHandler{ + w: h.w, + opts: h.opts, + prefix: h.prefix, + preformat: h.preformat + string(buf), + } +} + +// Handle formats its argument Record as a single line of text ending in a newline. +func (h *PrettyHandler) Handle(_ context.Context, r slog.Record) error { + var buf []byte + if !r.Time.IsZero() { + buf = r.Time.AppendFormat(buf, h.timeFormat) + buf = append(buf, ' ') + } + + // Make sure that each level has the same length. + // The shortest level is "INFO", thus we add a space to the end of the level string + levText := (r.Level.String() + " ")[0:5] + + buf = append(buf, levText...) + buf = append(buf, ' ') + if h.opts.AddSource && r.PC != 0 { + fs := runtime.CallersFrames([]uintptr{r.PC}) + f, _ := fs.Next() + buf = append(buf, f.File...) + buf = append(buf, ':') + buf = strconv.AppendInt(buf, int64(f.Line), 10) + buf = append(buf, ' ') + } + buf = append(buf, r.Message...) + buf = append(buf, h.preformat...) + r.Attrs(func(a slog.Attr) bool { + buf = h.appendAttr(buf, h.prefix, a) + return true + }) + buf = append(buf, '\n') + h.mu.Lock() + defer h.mu.Unlock() + _, err := h.w.Write(buf) + return err +} + +func (h *PrettyHandler) appendAttr(buf []byte, prefix string, a slog.Attr) []byte { + if a.Equal(slog.Attr{}) { + return buf + } + if a.Value.Kind() != slog.KindGroup { + buf = append(buf, ' ') + buf = append(buf, prefix...) + buf = append(buf, a.Key...) + buf = append(buf, '=') + return fmt.Appendf(buf, "%v", a.Value.Any()) + } + // Group + if a.Key != "" { + prefix += a.Key + "." + } + for _, a := range a.Value.Group() { + buf = h.appendAttr(buf, prefix, a) + } + return buf +} diff --git a/internal/util.go b/internal/util.go index 358614e..e350f60 100644 --- a/internal/util.go +++ b/internal/util.go @@ -4,18 +4,17 @@ import ( "context" "fmt" "io" + "log/slog" "os" "os/signal" "strings" "syscall" - - "github.com/sirupsen/logrus" ) // LogClose closes the given Closer and logs any error that occurs func LogClose(c io.Closer) { if err := c.Close(); err != nil { - logrus.Errorf("error during Close(): %v", err) + slog.Error("error during Close()", "error", err) } } @@ -28,11 +27,10 @@ func LogError(err error, msg ...string) { } if len(msg) > 0 { - logrus.Errorf("%s: %v", msg[0], err) - return + slog.Error(msg[0], "error", err) + } else { + slog.Error(err.Error()) } - logrus.Errorf("error: %v", err) - } // SignalAwareContext returns a context that gets closed once a given signal is retrieved.