After playing with it some more: This also does the trick
h := NewHandler(os.Stdout, &MyOptions{Level: slog.LevelDebug, TimeFormat: time.DateTime}) l := slog.New(h) slog.SetDefault(l) slog.Debug("hello") slog.Info("hello") slog.Warn("hello") slog.Error("hello") type MyOptions struct { // Enable source code location (Default: false) AddSource bool // Minimum level to log (Default: slog.LevelInfo) Level slog.Leveler // ReplaceAttr is called to rewrite each non-group attribute before it is logged. // See https://pkg.go.dev/log/slog#HandlerOptions for details. ReplaceAttr func(groups []string, attr slog.Attr) slog.Attr // Time format (Default: time.StampMilli) TimeFormat string } type MyHandler struct { opts MyOptions 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 } func NewHandler(w io.Writer, opts *MyOptions) *MyHandler { h := &MyHandler{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 } } if opts.TimeFormat != "" { h.timeFormat = opts.TimeFormat } return h } func (h *MyHandler) Enabled(ctx context.Context, level slog.Level) bool { minLevel := slog.LevelInfo if h.opts.Level != nil { minLevel = h.opts.Level.Level() } return level >= minLevel } func (h *MyHandler) WithGroup(name string) slog.Handler { return &MyHandler{ w: h.w, opts: h.opts, preformat: h.preformat, prefix: h.prefix + name + ".", } } func (h *MyHandler) WithAttrs(attrs []slog.Attr) slog.Handler { var buf []byte for _, a := range attrs { buf = h.appendAttr(buf, h.prefix, a) } return &MyHandler{ w: h.w, opts: h.opts, prefix: h.prefix, preformat: h.preformat + string(buf), } } func (h *MyHandler) Handle(ctx context.Context, r slog.Record) error { var buf []byte if !r.Time.IsZero() { buf = r.Time.AppendFormat(buf, h.timeFormat) buf = append(buf, ' ') } 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 *MyHandler) 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 } Op di 29 aug 2023 om 09:16 schreef 'Sean Liao' via golang-nuts < golang-nuts@googlegroups.com>: > cycle: > https://go.dev//issues/61892 > > default handler, copied/exported: > https://pkg.go.dev/github.com/jba/slog/handlers/loghandler > > - sean > > > On Tue, Aug 29, 2023 at 7:53 AM Marcello H <marcel...@gmail.com> wrote: > >> Yesterday, I came up with the same question and found this: >> "github.com/lmittmann/tint" >> >> (This solution still uses os.Stdout, but I think this can do what you >> need.) >> >> An example: >> logOptions := &tint.Options{ >> NoColor: true, >> Level: slog.LevelError, >> TimeFormat: time.DateTime, >> } >> logHandler := tint.NewHandler(os.Stdout, logOptions) >> logger := slog.New(logHandler) >> slog.SetDefault(logger) >> slog.Info("this does not show") >> slog.Debug("this debug info does not show") >> logOptions.Level = slog.LevelInfo >> slog.Info("this is now visible") >> slog.Debug("this debug info still does not show") >> logOptions.Level = slog.LevelDebug >> slog.Info("this is still visible") >> slog.Debug("this debug info also shows") >> Op dinsdag 29 augustus 2023 om 03:00:01 UTC+2 schreef Mike Schinkel: >> >>> Hi Tamás, >>> >>> Have you actually tried that and gotten it to work? It does not compile >>> for me but this does (note method call vs. property reference): >>> >>> slog.SetDefault(slog.New(myHandler{Handler:slog.Default().Handler()})) >>> >>> However, when delegating the Handle() method it seems to cause an >>> infinite >>> loop: >>> >>> func (m MyHandler) Handle(ctx context.Context, r slog.Record) error { >>> return m.Handler.Handle(ctx, r) >>> } >>> >>> See https://goplay.tools/snippet/qw07m0YflLd >>> >>> I know about this because just this past weekend I was trying to write a >>> TeeHandler to output the default to the screen and JSON to a file just >>> this >>> past weekend and ran into an infinite loop problem with the default >>> handler. >>> >>> I tried my best to figure out why it needed to be structured the way it >>> was >>> in that it seems to call itself recursively. I wanted to post a question >>> to >>> this list to see if there was a workaround, or if not to see if there >>> might >>> be interest in allowing it to work, but I could not get my head around >>> it so >>> eventually gave up and just used the TextHandler instead. >>> >>> Shame though. It would be nice to be able to reuse the default handler >>> but >>> AFACT it is not possible (though if I am wrong I would love for someone >>> to >>> show me how to get it to work.) >>> >>> -Mike >>> >>> >>> On Monday, August 28, 2023 at 12:50:50 PM UTC-4 Tamás Gulácsi wrote: >>> >>> slog.SetDefault(slog.New(myHandler{Handler:slog.Default().Handler})) >>> >>> vl...@mailbox.org a következőt írta (2023. augusztus 28., hétfő, >>> 15:06:37 UTC+2): >>> >>> Hi, >>> >>> When reading trough the log/slog documentation, it seems one can create >>> a logger with a different handler, which is either NewTextHandler or >>> NewJSONHandler. >>> >>> Why can't I configure the defaultHandler? Let's say I want my logger to >>> behave exactly like the defaultHandler, but output to a logfile or >>> Stdout instead. >>> >>> The defaultHandler's output is different compared to the NewTextHandler: >>> >>> slog.Info("ok"), gives me: >>> >>> INFO ok >>> >>> The NextTextHandler gives me: >>> >>> level=INFO msg="ok" >>> >>> >>> Regards, >>> >>> -- >> You received this message because you are subscribed to the Google Groups >> "golang-nuts" group. >> To unsubscribe from this group and stop receiving emails from it, send an >> email to golang-nuts+unsubscr...@googlegroups.com. >> To view this discussion on the web visit >> https://groups.google.com/d/msgid/golang-nuts/df136f1e-0283-46fa-a0b7-ce17a0722fd9n%40googlegroups.com >> <https://groups.google.com/d/msgid/golang-nuts/df136f1e-0283-46fa-a0b7-ce17a0722fd9n%40googlegroups.com?utm_medium=email&utm_source=footer> >> . >> > -- > You received this message because you are subscribed to a topic in the > Google Groups "golang-nuts" group. > To unsubscribe from this topic, visit > https://groups.google.com/d/topic/golang-nuts/aJPXT2NF-Lc/unsubscribe. > To unsubscribe from this group and all its topics, send an email to > golang-nuts+unsubscr...@googlegroups.com. > To view this discussion on the web visit > https://groups.google.com/d/msgid/golang-nuts/CAGabyPo9NZJBf%3Dj-W_8tUS%3DqkmwHRWVpjCbFz04jZW_P8KEOow%40mail.gmail.com > <https://groups.google.com/d/msgid/golang-nuts/CAGabyPo9NZJBf%3Dj-W_8tUS%3DqkmwHRWVpjCbFz04jZW_P8KEOow%40mail.gmail.com?utm_medium=email&utm_source=footer> > . > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/CADBSaR28mnPtC-bB-udasPpu%2BdsatFB5mOKoH0KL2sSATpkjMA%40mail.gmail.com.