mirror of
https://gitea.com/gitea/runner.git
synced 2026-11-04 22:37:55 +00:00
Log through `log/slog`, with a console `slog.Handler` that writes the text the runner has always written (sorted `key=value` fields on a plain stream, a colored prefix on a terminal), so nothing changes for users. I compared its output byte for byte with logrus's `TextFormatter` over plain, colored, timestamped and caller cases. Why not `slog.TextHandler`: its output differs from what users and their log parsers see today, and `ReplaceAttr` cannot close the gap. - level is `WARN` where logrus wrote `warning`, and time has milliseconds and no quotes - fields keep call order instead of being sorted - the caller is one `source=` key, where the runner writes `func=` and `file=` (reshaped by `callPrettyfier`), and `ReplaceAttr` returns a single attribute - no colored prefix on a terminal - no hook to rewrite a whole formatted line, which the process-wide secret masking relies on Libraries that log through logrus's global logger (the workflow model, the Docker client) are forwarded to the process-wide logger, so they keep following its level, format and secret masking. This is why logrus stays in `go.mod`. Stacked on https://gitea.com/gitea/runner/pulls/1303. Assisted-by: Codet Co-authored-by: bircni <[email protected]> Reviewed-on: https://gitea.com/gitea/runner/pulls/1304 Reviewed-by: bircni <[email protected]>
41 lines
949 B
Go
41 lines
949 B
Go
// Copyright 2026 The Gitea Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package logging
|
|
|
|
import (
|
|
"context"
|
|
"log/slog"
|
|
)
|
|
|
|
type slogHandler struct {
|
|
h slog.Handler
|
|
}
|
|
|
|
// NewSlogHandler writes to h. Levels map one to one, as Level uses slog's values.
|
|
func NewSlogHandler(h slog.Handler) Handler {
|
|
return slogHandler{h: h}
|
|
}
|
|
|
|
func (s slogHandler) Enabled(ctx context.Context, level Level) bool {
|
|
return s.h.Enabled(ctx, slog.Level(level))
|
|
}
|
|
|
|
func (s slogHandler) Handle(ctx context.Context, r Record) error {
|
|
rec := slog.NewRecord(r.Time, slog.Level(r.Level), r.Message, r.PC)
|
|
rec.AddAttrs(toSlogAttrs(r.Attrs)...)
|
|
return s.h.Handle(ctx, rec)
|
|
}
|
|
|
|
func (s slogHandler) WithAttrs(attrs []Attr) Handler {
|
|
return slogHandler{h: s.h.WithAttrs(toSlogAttrs(attrs))}
|
|
}
|
|
|
|
func toSlogAttrs(attrs []Attr) []slog.Attr {
|
|
out := make([]slog.Attr, len(attrs))
|
|
for i, a := range attrs {
|
|
out[i] = slog.Any(a.Key, a.Value)
|
|
}
|
|
return out
|
|
}
|