Files
runner/act/common/logging/logrus.go
T
Lunny Xiaoandbircni de961b91eb refactor(logging): add a logging abstraction and move the job logger onto it (#1295)
Add `act/common/logging`: a printf-style `Logger` on a slog-shaped `Handler`, with a logrus-backed handler as the only backend, plus a `logtest` handler for tests. Move the job logger onto it: `common.Logger` returns `*logging.Logger`, the job log formatters and the `Reporter` hook become handlers, and composite step IDs travel in the context.

Logging is tightly coupled to logrus types (hooks, formatters, `Entry.Data`). Going through our own interface first lets the remaining call sites move over with unchanged behavior, and lets the backend be swapped for `log/slog` in one place afterwards. The global process logger and its masking formatter stay on logrus for now.

Assisted-by: Codet
Co-authored-by: bircni <[email protected]>
Reviewed-on: https://gitea.com/gitea/runner/pulls/1295
Reviewed-by: bircni <[email protected]>
2026-10-11 23:26:00 +00:00

88 lines
2.0 KiB
Go

// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package logging
import (
"context"
"runtime"
"github.com/sirupsen/logrus"
)
type logrusHandler struct {
entry *logrus.Entry
}
// NewLogrusHandler writes to l, so its level, hooks, formatter and output all apply.
func NewLogrusHandler(l *logrus.Logger) Handler {
return &logrusHandler{entry: logrus.NewEntry(l)}
}
func (h *logrusHandler) Enabled(_ context.Context, level Level) bool {
return h.entry.Logger.IsLevelEnabled(toLogrusLevel(level))
}
func (h *logrusHandler) Handle(ctx context.Context, r Record) error {
if ctx == nil {
ctx = context.Background()
}
e := h.entry.WithContext(ctx) // always a copy, so the fields below never reach h.entry
if !r.Time.IsZero() {
e = e.WithTime(r.Time)
}
if len(r.Attrs) > 0 {
e = e.WithFields(attrsToFields(r.Attrs))
}
if r.PC != 0 && e.Logger.ReportCaller {
// logrus only skips its own frames, so without this it would report the logging package
frame, _ := runtime.CallersFrames([]uintptr{r.PC}).Next()
e.Caller = &frame
}
e.Log(toLogrusLevel(r.Level), r.Message)
return nil
}
func (h *logrusHandler) WithAttrs(attrs []Attr) Handler {
return &logrusHandler{entry: h.entry.WithFields(attrsToFields(attrs))}
}
func attrsToFields(attrs []Attr) logrus.Fields {
fields := make(logrus.Fields, len(attrs))
for _, a := range attrs {
fields[a.Key] = a.Value
}
return fields
}
func toLogrusLevel(l Level) logrus.Level {
switch {
case l <= LevelTrace:
return logrus.TraceLevel
case l <= LevelDebug:
return logrus.DebugLevel
case l <= LevelInfo:
return logrus.InfoLevel
case l <= LevelWarn:
return logrus.WarnLevel
default:
return logrus.ErrorLevel
}
}
// fromLogrusLevel maps logrus's Fatal and Panic to LevelError.
func fromLogrusLevel(l logrus.Level) Level {
switch l {
case logrus.TraceLevel:
return LevelTrace
case logrus.DebugLevel:
return LevelDebug
case logrus.InfoLevel:
return LevelInfo
case logrus.WarnLevel:
return LevelWarn
default:
return LevelError
}
}