diff --git a/flatpak.go b/flatpak.go index 65b7652..61edd1b 100644 --- a/flatpak.go +++ b/flatpak.go @@ -1,20 +1,21 @@ package main import ( + "context" + "os" "os/exec" "slices" "strings" "sync" - "github.com/rs/zerolog/log" "gopkg.in/ini.v1" ) -func getApps() ([]string, error) { +func getApps(ctx context.Context) ([]string, error) { cmd := exec.Command("flatpak", "list", "--app") cmd.Dir = "/var/lib/flatpak" - log.Debug().Str("command", cmd.Path+strings.Join(cmd.Args, " ")).Msg("Running command") + log.DebugContext(ctx, "Running command", "command", cmd.Path+" "+strings.Join(cmd.Args, " ")) out, err := cmd.Output() if err != nil { return nil, err @@ -29,29 +30,29 @@ func getApps() ([]string, error) { } data := strings.Split(app, "\t") if len(data) < 2 { - log.Warn().Msgf("Skipping line: %s", app) + log.WarnContext(ctx, "Skipping line", "line", app) continue } appID := strings.TrimSpace(data[1]) if appID == "" { continue } - log.Trace().Msgf("AppID: %s", appID) + log.DebugContext(ctx, "AppID found", "appid", appID) apps = append(apps, appID) } return apps, nil } -func getFlatpakApp(appID string) (Flatpak, error) { +func getFlatpakApp(ctx context.Context, appID string) (Flatpak, error) { cmd := exec.Command("flatpak", "info", "-m", appID) - log.Debug().Str("command", cmd.Path+strings.Join(cmd.Args, " ")).Str("appID", appID).Msg("Running command") + log.DebugContext(ctx, "Running command", "command", cmd.Path+" "+strings.Join(cmd.Args, " "), "appid", appID) out, err := cmd.Output() if err != nil { return Flatpak{}, err } - log.Trace().Str("data", string(out)).Msg("Parsing toml") + log.DebugContext(ctx, "Parsing toml", "data", string(out)) var flatpak Flatpak cfg, err := ini.Load(out) if err != nil { @@ -62,7 +63,7 @@ func getFlatpakApp(appID string) (Flatpak, error) { return Flatpak{}, err } - flatpak.Application.Command, err = getCommand(&flatpak) + flatpak.Application.Command, err = getCommand(ctx, &flatpak) if err != nil { return Flatpak{}, err } @@ -70,7 +71,7 @@ func getFlatpakApp(appID string) (Flatpak, error) { return flatpak, nil } -func removeDuplicates(apps []Flatpak) []Flatpak { +func removeDuplicates(ctx context.Context, apps []Flatpak) []Flatpak { keys := make(map[string]*Flatpak) list := []Flatpak{} duplicates := make(map[string][]Flatpak) @@ -89,35 +90,36 @@ func removeDuplicates(apps []Flatpak) []Flatpak { return slices.DeleteFunc(list, func(app Flatpak) bool { apps, ok := duplicates[app.Application.Command] if ok { - log.Warn().Str("command", app.Application.Command).Interface("apps", apps).Msg("Found duplicates") + log.WarnContext(ctx, "Found duplicates", "command", app.Application.Command, "apps", apps) } return ok }) } -func getAllFlatpakApps() []Flatpak { - appsID, err := getApps() +func getAllFlatpakApps(ctx context.Context) []Flatpak { + appsID, err := getApps(ctx) if err != nil { - log.Fatal().Err(err).Msg("Error getting apps") + log.ErrorContext(ctx, "Error getting apps", "err", err) + os.Exit(1) } apps := make([]Flatpak, len(appsID)) wg := sync.WaitGroup{} for i, appID := range appsID { wg.Add(1) - go func(appID string) { + go func(i int, appID string) { defer wg.Done() - app, err := getFlatpakApp(appID) + app, err := getFlatpakApp(ctx, appID) if err != nil { - log.Error().Err(err).Str("appID", appID).Msg("Error getting app") + log.ErrorContext(ctx, "Error getting app", "err", err, "appid", appID) } // No need to lock since we are writing to different indexes apps[i] = app - }(appID) + }(i, appID) } wg.Wait() - apps = removeDuplicates(apps) + apps = removeDuplicates(ctx, apps) return apps } diff --git a/go.mod b/go.mod index d722e63..3b19f98 100644 --- a/go.mod +++ b/go.mod @@ -4,14 +4,8 @@ go 1.23.4 require ( github.com/hbollon/go-edlib v1.6.0 - github.com/rs/zerolog v1.33.0 gopkg.in/ini.v1 v1.67.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) -require ( - github.com/mattn/go-colorable v0.1.13 // indirect - github.com/mattn/go-isatty v0.0.19 // indirect - github.com/stretchr/testify v1.10.0 // indirect - golang.org/x/sys v0.12.0 // indirect -) +require github.com/stretchr/testify v1.10.0 // indirect diff --git a/go.sum b/go.sum index 1fe61c7..94acf30 100644 --- a/go.sum +++ b/go.sum @@ -1,26 +1,11 @@ -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= 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= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/hbollon/go-edlib v1.6.0 h1:ga7AwwVIvP8mHm9GsPueC0d71cfRU/52hmPJ7Tprv4E= github.com/hbollon/go-edlib v1.6.0/go.mod h1:wnt6o6EIVEzUfgbUZY7BerzQ2uvzp354qmS2xaLkrhM= -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.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= -github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= diff --git a/logger.go b/logger.go new file mode 100644 index 0000000..914cf6a --- /dev/null +++ b/logger.go @@ -0,0 +1,44 @@ +package main + +import ( + "context" + "log/slog" +) + +type multiHandler struct { + handlers []slog.Handler +} + +func (h multiHandler) Enabled(ctx context.Context, level slog.Level) bool { + for _, handler := range h.handlers { + if handler.Enabled(ctx, level) { + return true + } + } + return false +} + +func (h multiHandler) Handle(ctx context.Context, r slog.Record) error { + for _, handler := range h.handlers { + if err := handler.Handle(ctx, r); err != nil { + return err + } + } + return nil +} + +func (h multiHandler) WithAttrs(attrs []slog.Attr) slog.Handler { + hs := make([]slog.Handler, len(h.handlers)) + for i, handler := range h.handlers { + hs[i] = handler.WithAttrs(attrs) + } + return multiHandler{handlers: hs} +} + +func (h multiHandler) WithGroup(name string) slog.Handler { + hs := make([]slog.Handler, len(h.handlers)) + for i, handler := range h.handlers { + hs[i] = handler.WithGroup(name) + } + return multiHandler{handlers: hs} +} diff --git a/main.go b/main.go index e449d11..492a05b 100644 --- a/main.go +++ b/main.go @@ -1,15 +1,15 @@ package main import ( + "context" _ "embed" "os" "path" "text/template" "time" - "github.com/rs/zerolog" - "github.com/rs/zerolog/log" "gopkg.in/natefinch/lumberjack.v2" + "log/slog" ) type Config struct { @@ -27,6 +27,8 @@ type Flatpak struct { //go:embed script.gotmpl var scriptTemplateRaw string +var log *slog.Logger + func parseArgs() Config { config := Config{} if len(os.Args) > 1 { @@ -37,7 +39,7 @@ func parseArgs() Config { return config } -func prepare(config *Config) (*template.Template, *lumberjack.Logger) { +func prepare(ctx context.Context, config *Config) (*template.Template, *lumberjack.Logger) { logFile := &lumberjack.Logger{ Filename: path.Join(config.FlatpakDir, "flatpak-alias.log"), // Path to the log file MaxSize: 1, // Maximum size in MB before rotation @@ -46,36 +48,36 @@ func prepare(config *Config) (*template.Template, *lumberjack.Logger) { Compress: true, // Compress rotated files } - consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC3339} - - multiWriter := zerolog.MultiLevelWriter(consoleWriter, logFile) - - log.Logger = zerolog.New(multiWriter).With().Timestamp().Logger() - - zerolog.SetGlobalLevel(zerolog.InfoLevel) + textHandler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}) + jsonHandler := slog.NewJSONHandler(logFile, &slog.HandlerOptions{Level: slog.LevelInfo}) + log = slog.New(multiHandler{handlers: []slog.Handler{textHandler, jsonHandler}}) + slog.SetDefault(log) scriptTemplate, err := template.New("script").Parse(scriptTemplateRaw) if err != nil { - log.Fatal().Err(err).Msg("Error parsing template") + log.ErrorContext(ctx, "Error parsing template", "err", err) + os.Exit(1) } config.FlatpakDir = path.Join(config.FlatpakDir, "aliases") - log.Info().Str("path", config.FlatpakDir).Msg("Writing script") + log.InfoContext(ctx, "Writing script", "path", config.FlatpakDir) err = os.MkdirAll(config.FlatpakDir, 0755) if err != nil { - log.Fatal().Err(err).Msg("Error creating directory") + log.ErrorContext(ctx, "Error creating directory", "err", err) + os.Exit(1) } return scriptTemplate, logFile } func main() { + ctx := context.Background() config := parseArgs() - scriptTemplate, logFile := prepare(&config) + scriptTemplate, logFile := prepare(ctx, &config) defer logFile.Close() - log.Info().Msg("Starting flatpak-alias") - log.Info().Str("path", config.FlatpakDir).Any("args", os.Args).Msg("Writing script") - apps := getAllFlatpakApps() - removeOldScripts(config) - generateScripts(apps, scriptTemplate, config) + log.InfoContext(ctx, "Starting flatpak-alias") + log.InfoContext(ctx, "Writing script", "path", config.FlatpakDir, "args", os.Args) + apps := getAllFlatpakApps(ctx) + removeOldScripts(ctx, config) + generateScripts(ctx, apps, scriptTemplate, config) } diff --git a/scripts.go b/scripts.go index a881e7f..a44bfa4 100644 --- a/scripts.go +++ b/scripts.go @@ -1,59 +1,59 @@ package main import ( + "context" "os" "path" "text/template" "time" - - "github.com/rs/zerolog/log" ) -func removeOldScripts(config Config) { +func removeOldScripts(ctx context.Context, config Config) { files, err := os.ReadDir(config.FlatpakDir) if err != nil { - log.Fatal().Err(err).Msg("Error reading output directory") + log.ErrorContext(ctx, "Error reading output directory", "err", err) + os.Exit(1) } for _, file := range files { f, err := os.Open(path.Join(config.FlatpakDir, file.Name())) if err != nil { - log.Error().Err(err).Str("file", file.Name()).Msg("Error opening file") + log.ErrorContext(ctx, "Error opening file", "err", err, "file", file.Name()) continue } content := make([]byte, 30) i, err := f.ReadAt(content, 14) if err != nil || i != 30 || string(content) != "**Generated by flatpak-alias**" { - log.Warn().Str("file", file.Name()).Msg("Not a managed file") + log.WarnContext(ctx, "Not a managed file", "file", file.Name()) continue } err = os.Remove(path.Join(config.FlatpakDir, file.Name())) if err != nil { - log.Error().Err(err).Str("file", file.Name()).Msg("Error deleting file") + log.ErrorContext(ctx, "Error deleting file", "err", err, "file", file.Name()) } } } -func generateScripts(apps []Flatpak, scriptTemplate *template.Template, config Config) { +func generateScripts(ctx context.Context, apps []Flatpak, scriptTemplate *template.Template, config Config) { now := time.Now() for _, app := range apps { - log.Info().Str("name", app.Application.Name).Str("command", app.Application.Command).Msg("Found application") + log.InfoContext(ctx, "Found application", "name", app.Application.Name, "command", app.Application.Command) app.Timestamp = now if _, err := os.Stat(path.Join(config.FlatpakDir, app.Application.Command)); err == nil { - log.Warn().Str("command", app.Application.Command).Msg("File already exists") + log.WarnContext(ctx, "File already exists", "command", app.Application.Command) continue } file, err := os.Create(path.Join(config.FlatpakDir, app.Application.Command)) if err != nil { - log.Error().Err(err).Str("command", app.Application.Command).Msg("Error creating file") + log.ErrorContext(ctx, "Error creating file", "err", err, "command", app.Application.Command) continue } err = scriptTemplate.Execute(file, app) if err != nil { - log.Error().Err(err).Str("command", app.Application.Command).Msg("Error writing template") + log.ErrorContext(ctx, "Error writing template", "err", err, "command", app.Application.Command) continue } diff --git a/scrore.go b/scrore.go index bd8c049..356f049 100644 --- a/scrore.go +++ b/scrore.go @@ -1,11 +1,11 @@ package main import ( + "context" "errors" "strings" "github.com/hbollon/go-edlib" - "github.com/rs/zerolog/log" ) const threshold = 0.75 @@ -18,7 +18,7 @@ const ( matchNope = "nope" ) -func toCommand(str string) string { +func toCommand(ctx context.Context, str string) string { res := make([]byte, 0, len(str)) needSpace := false lastUpper := false @@ -50,11 +50,11 @@ func toCommand(str string) string { needSpace = true } } - log.Trace().Str("input", str).Str("output", string(res)).Msg("Converted") + log.DebugContext(ctx, "Converted", "input", str, "output", string(res)) return string(res) } -func getCommand(app *Flatpak) (string, error) { +func getCommand(ctx context.Context, app *Flatpak) (string, error) { var res string var match string command := strings.TrimSpace(app.Application.Command) @@ -78,6 +78,6 @@ func getCommand(app *Flatpak) (string, error) { if res == "" { return "", errors.New("empty command") } - log.Debug().Str("appID", appID).Str("name", name).Str("command", command).Float32("rank", similarity).Str("result", res).Msgf("Match %s", match) - return toCommand(res), nil + log.DebugContext(ctx, "Match", "appid", appID, "name", name, "command", command, "rank", similarity, "result", res, "match", match) + return toCommand(ctx, res), nil }