// Package applog provides geniusrun's structured JSON logging: a // log/slog-based logger writing to stdout, plus the App helper that tags // records with the mandatory schema fields every geniusrun log line // carries -- "type" ("app" | "http" | "wrapper"), "class" (Go type with // its package, or the package/module name for free functions), and // "method" (the Go or Python function emitting the record). "msg" is // optional: NewLogger's handler drops it when empty. package applog import ( "io" "log/slog" "strings" ) // NewLogger builds a JSON-handler *slog.Logger writing to w at the given // level ("debug"|"info"|"warn"|"error", case-insensitive; anything else // defaults to info). Empty msg values are omitted from the output -- // records whose meaning is fully carried by type/class/method and attrs // (e.g. the HTTP request line) don't need one. func NewLogger(level string, w io.Writer) *slog.Logger { return slog.New(slog.NewJSONHandler(w, &slog.HandlerOptions{ Level: parseLevel(level), ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { if len(groups) == 0 && a.Key == slog.MessageKey && a.Value.String() == "" { return slog.Attr{} } return a }, })) } // App returns the default logger tagged with the mandatory schema fields // for an ordinary application record: type=app, plus the emitting class // and method. The http and wrapper emitters (internal/api's logging // middleware, internal/garmin's execute/forwardWrapperStderr) tag their // own type instead. func App(class, method string) *slog.Logger { return slog.Default().With("type", "app", "class", class, "method", method) } func parseLevel(level string) slog.Level { switch strings.ToLower(level) { case "debug": return slog.LevelDebug case "warn": return slog.LevelWarn case "error": return slog.LevelError default: return slog.LevelInfo } }