mirror of
https://gitea.com/gitea/runner.git
synced 2026-11-14 03:43:42 +00:00
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]>
This commit is contained in:
1 parent
ad9eeb668a
commit
de961b91eb
50 files changed
+1241
-438
No files matched your search
@@ -8,11 +8,13 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"gitea.com/gitea/runner/act/common/logging"
|
||||
"gitea.com/gitea/runner/act/runner"
|
||||
"gitea.com/gitea/runner/internal/pkg/client"
|
||||
"gitea.com/gitea/runner/internal/pkg/config"
|
||||
@@ -166,8 +168,30 @@ func (r *Reporter) ResetSteps(l int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reporter) Levels() []log.Level {
|
||||
return log.AllLevels
|
||||
// Enabled makes the Reporter see every record, the job logger's level filter runs before it.
|
||||
func (r *Reporter) Enabled(context.Context, logging.Level) bool { return true }
|
||||
|
||||
// WithAttrs returns a Handler feeding the same Reporter, with attrs added to every record.
|
||||
func (r *Reporter) WithAttrs(attrs []logging.Attr) logging.Handler {
|
||||
return &reporterHandler{r: r, attrs: attrs}
|
||||
}
|
||||
|
||||
type reporterHandler struct {
|
||||
r *Reporter
|
||||
attrs []logging.Attr
|
||||
}
|
||||
|
||||
func (h *reporterHandler) Enabled(ctx context.Context, level logging.Level) bool {
|
||||
return h.r.Enabled(ctx, level)
|
||||
}
|
||||
|
||||
func (h *reporterHandler) Handle(ctx context.Context, rec logging.Record) error {
|
||||
rec.Attrs = slices.Concat(h.attrs, rec.Attrs)
|
||||
return h.r.Handle(ctx, rec)
|
||||
}
|
||||
|
||||
func (h *reporterHandler) WithAttrs(attrs []logging.Attr) logging.Handler {
|
||||
return &reporterHandler{r: h.r, attrs: slices.Concat(h.attrs, attrs)}
|
||||
}
|
||||
|
||||
// appendLogRow masks a row before buffering it for Gitea and the local job.log, the one point
|
||||
@@ -181,11 +205,19 @@ func (r *Reporter) appendLogRow(row *runnerv1.LogRow) {
|
||||
r.jobLog.write(row.Time.AsTime(), row.Content)
|
||||
}
|
||||
|
||||
func attrArgs(attrs []logging.Attr) []any {
|
||||
args := make([]any, len(attrs))
|
||||
for i, a := range attrs {
|
||||
args[i] = a
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
// isJobStepEntry is used to not report composite step results incorrectly as step result
|
||||
// returns true if the logentry is on job level
|
||||
// returns false for composite action step messages
|
||||
func isJobStepEntry(entry *log.Entry) bool {
|
||||
if v, ok := entry.Data["stepID"]; ok {
|
||||
func isJobStepEntry(rec logging.Record) bool {
|
||||
if v, ok := rec.Attr("stepID"); ok {
|
||||
if v, ok := v.([]string); ok && len(v) > 1 {
|
||||
return false
|
||||
}
|
||||
@@ -219,15 +251,15 @@ func (r *Reporter) unlockAndNotify(urgentState bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Reporter) Fire(entry *log.Entry) error {
|
||||
func (r *Reporter) Handle(_ context.Context, entry logging.Record) error {
|
||||
urgentState := false
|
||||
|
||||
r.stateMu.Lock()
|
||||
|
||||
r.stateChanged = true
|
||||
|
||||
if log.IsLevelEnabled(log.TraceLevel) {
|
||||
log.WithFields(entry.Data).Trace(r.mask(entry.Message)) // the process masker has no ::add-mask:: value
|
||||
if logging.Enabled(logging.LevelTrace) {
|
||||
logging.With(attrArgs(entry.Attrs)...).Trace(r.mask(entry.Message)) // the process masker has no ::add-mask:: value
|
||||
}
|
||||
|
||||
timestamp := entry.Time
|
||||
@@ -235,10 +267,10 @@ func (r *Reporter) Fire(entry *log.Entry) error {
|
||||
r.state.StartedAt = timestamppb.New(timestamp)
|
||||
}
|
||||
|
||||
stage := entry.Data["stage"]
|
||||
stage, _ := entry.Attr("stage")
|
||||
|
||||
if stage != "Main" {
|
||||
if v, ok := entry.Data["jobResult"]; ok {
|
||||
if v, ok := entry.Attr("jobResult"); ok {
|
||||
if jobResult, ok := r.parseResult(v); ok {
|
||||
// We need to ensure log upload before this upload
|
||||
r.state.Result = jobResult
|
||||
@@ -262,7 +294,7 @@ func (r *Reporter) Fire(entry *log.Entry) error {
|
||||
}
|
||||
|
||||
var step *runnerv1.StepState
|
||||
if v, ok := entry.Data["stepNumber"]; ok {
|
||||
if v, ok := entry.Attr("stepNumber"); ok {
|
||||
if v, ok := v.(int); ok && len(r.state.Steps) > v {
|
||||
step = r.state.Steps[v]
|
||||
}
|
||||
@@ -284,11 +316,12 @@ func (r *Reporter) Fire(entry *log.Entry) error {
|
||||
}
|
||||
|
||||
// Force reporting log errors as raw output to prevent silent failures
|
||||
if entry.Level == log.ErrorLevel {
|
||||
entry.Data["raw_output"] = true
|
||||
v, ok := entry.Attr("raw_output")
|
||||
if entry.Level >= logging.LevelError {
|
||||
v, ok = true, true
|
||||
}
|
||||
|
||||
if v, ok := entry.Data["raw_output"]; ok {
|
||||
if ok {
|
||||
if rawOutput, ok := v.(bool); ok && rawOutput {
|
||||
if row := r.parseLogRow(entry); row != nil {
|
||||
if step.LogLength == 0 {
|
||||
@@ -301,7 +334,7 @@ func (r *Reporter) Fire(entry *log.Entry) error {
|
||||
} else if r.shouldAppendLogRow(entry) {
|
||||
r.appendLogRow(r.parseLogRow(entry))
|
||||
}
|
||||
if v, ok := entry.Data["stepResult"]; ok && isJobStepEntry(entry) {
|
||||
if v, ok := entry.Attr("stepResult"); ok && isJobStepEntry(entry) {
|
||||
if stepResult, ok := r.parseResult(v); ok {
|
||||
if step.LogLength == 0 {
|
||||
step.LogIndex = int64(r.logOffset + len(r.logRows))
|
||||
@@ -671,7 +704,7 @@ func (r *Reporter) ReportState(reportResult bool) error {
|
||||
r.outputsMu.Unlock()
|
||||
|
||||
// Consume stateChanged atomically with the snapshot; restored on error
|
||||
// below so a concurrent Fire() during UpdateTask isn't silently lost.
|
||||
// below so a log record handled during UpdateTask isn't silently lost.
|
||||
// Heartbeat at stateReportInterval even when nothing changed, so the server
|
||||
// doesn't time out long-running silent jobs as orphaned (#826).
|
||||
last := r.lastReportedAtNanos.Load()
|
||||
@@ -753,8 +786,8 @@ func (r *Reporter) duringSteps() bool {
|
||||
// shouldAppendLogRow reports whether a non-raw_output entry should be written
|
||||
// to the job log: only when we are between steps and the entry's level is
|
||||
// within the globally configured log level.
|
||||
func (r *Reporter) shouldAppendLogRow(entry *log.Entry) bool {
|
||||
return !r.duringSteps() && entry.Level <= log.GetLevel()
|
||||
func (r *Reporter) shouldAppendLogRow(entry logging.Record) bool {
|
||||
return !r.duringSteps() && entry.Level >= logging.GetLevel()
|
||||
}
|
||||
|
||||
var stringToResult = map[string]runnerv1.Result{
|
||||
@@ -879,7 +912,7 @@ func cutPrefixFold(s, prefix string) (string, bool) {
|
||||
return s[len(prefix):], true
|
||||
}
|
||||
|
||||
func (r *Reporter) parseLogRow(entry *log.Entry) *runnerv1.LogRow {
|
||||
func (r *Reporter) parseLogRow(entry logging.Record) *runnerv1.LogRow {
|
||||
content := strings.TrimRight(entry.Message, "\r\n")
|
||||
|
||||
// cmdRegex only covers the ::cmd:: form, so the ##[add-mask] one would otherwise reach
|
||||
|
||||
Reference in new issue
Block a user