mirror of
https://gitea.com/gitea/runner.git
synced 2026-11-12 10:54:09 +00:00
enhance(report): add runner.log_report_level for the uploaded runner messages (#1310)
The runner's own messages between steps (not step output) were uploaded to the job log at `log.level`, so raising `log.level` for local debugging also changed what workflow authors see in Gitea, and lowering it hid messages from them. Add `runner.log_report_level` to set that independently. Unset, it still follows `log.level`, so existing setups behave as before. An invalid value is rejected when the config loads. Assisted-by: Codet Assisted-by: Claude Code:claude-opus-5-5 Co-authored-by: silverwind <[email protected]> Co-authored-by: silverwind <[email protected]> Reviewed-on: https://gitea.com/gitea/runner/pulls/1310 Co-authored-by: Lunny Xiao <[email protected]>
This commit is contained in:
1 parent
42858c61df
commit
9c4c66e70f
6 files changed
+28
-2
No files matched your search
@@ -40,6 +40,9 @@ fields.
|
|||||||
|
|
||||||
- The process-wide logger masks the secrets of every live task, the job logger masks its own.
|
- The process-wide logger masks the secrets of every live task, the job logger masks its own.
|
||||||
Going around either leaks them.
|
Going around either leaks them.
|
||||||
|
- The job logger's output can reach the job log workflow authors see, the process-wide logger's
|
||||||
|
stays on the runner host, so keep operator-only detail (host paths, internal addresses) off the
|
||||||
|
job logger.
|
||||||
- Libraries that log through logrus's or slog's global logger are forwarded to the process-wide
|
- Libraries that log through logrus's or slog's global logger are forwarded to the process-wide
|
||||||
one, so they follow its level and masking too.
|
one, so they follow its level and masking too.
|
||||||
- That forwarding only works while logrus's global logger is left as `logging` configures it. A
|
- That forwarding only works while logrus's global logger is left as `logging` configures it. A
|
||||||
|
|||||||
@@ -76,6 +76,9 @@ runner:
|
|||||||
# Flush logs immediately when the buffer reaches this many rows.
|
# Flush logs immediately when the buffer reaches this many rows.
|
||||||
# This ensures bursty output (e.g., npm install) is delivered promptly.
|
# This ensures bursty output (e.g., npm install) is delivered promptly.
|
||||||
#log_report_batch_size: 100
|
#log_report_batch_size: 100
|
||||||
|
# The lowest severity of the runner's own messages (not step output) that are sent to the
|
||||||
|
# job log between steps: trace, debug, info, warn, error. Default if omitted: follow log.level.
|
||||||
|
#log_report_level: info
|
||||||
# The interval for reporting task state (step status, timing) to the Gitea instance.
|
# The interval for reporting task state (step status, timing) to the Gitea instance.
|
||||||
# State is also reported on step transitions (start/stop) and after each log flush.
|
# State is also reported on step transitions (start/stop) and after each log flush.
|
||||||
#state_report_interval: 5s
|
#state_report_interval: 5s
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ type Runner struct {
|
|||||||
LogReportInterval time.Duration `yaml:"log_report_interval"` // LogReportInterval specifies the base interval for periodic log flush.
|
LogReportInterval time.Duration `yaml:"log_report_interval"` // LogReportInterval specifies the base interval for periodic log flush.
|
||||||
LogReportMaxLatency time.Duration `yaml:"log_report_max_latency"` // LogReportMaxLatency specifies the max time a log row can wait before being sent.
|
LogReportMaxLatency time.Duration `yaml:"log_report_max_latency"` // LogReportMaxLatency specifies the max time a log row can wait before being sent.
|
||||||
LogReportBatchSize int `yaml:"log_report_batch_size"` // LogReportBatchSize triggers immediate log flush when buffer reaches this size.
|
LogReportBatchSize int `yaml:"log_report_batch_size"` // LogReportBatchSize triggers immediate log flush when buffer reaches this size.
|
||||||
|
LogReportLevel string `yaml:"log_report_level"` // LogReportLevel is the lowest level of the runner's own messages sent to the job log between steps. Empty, the default, follows log.level.
|
||||||
StateReportInterval time.Duration `yaml:"state_report_interval"` // StateReportInterval specifies the interval for state reporting.
|
StateReportInterval time.Duration `yaml:"state_report_interval"` // StateReportInterval specifies the interval for state reporting.
|
||||||
ReportCloseTimeout time.Duration `yaml:"report_close_timeout"` // ReportCloseTimeout caps each RPC attempt when flushing the final logs and task state at job completion, on a detached context so a server cancel can't block the acknowledgement.
|
ReportCloseTimeout time.Duration `yaml:"report_close_timeout"` // ReportCloseTimeout caps each RPC attempt when flushing the final logs and task state at job completion, on a detached context so a server cancel can't block the acknowledgement.
|
||||||
Labels []string `yaml:"labels"` // Labels specify the labels of the runner. Labels are declared on each startup
|
Labels []string `yaml:"labels"` // Labels specify the labels of the runner. Labels are declared on each startup
|
||||||
@@ -348,6 +349,11 @@ func LoadDefault(file string) (*Config, error) {
|
|||||||
if !slices.Contains(ToolCacheModes, cfg.Runner.ToolCacheMode) {
|
if !slices.Contains(ToolCacheModes, cfg.Runner.ToolCacheMode) {
|
||||||
return nil, fmt.Errorf("invalid runner.tool_cache_mode %q: must be one of %q", cfg.Runner.ToolCacheMode, ToolCacheModes)
|
return nil, fmt.Errorf("invalid runner.tool_cache_mode %q: must be one of %q", cfg.Runner.ToolCacheMode, ToolCacheModes)
|
||||||
}
|
}
|
||||||
|
if cfg.Runner.LogReportLevel != "" {
|
||||||
|
if _, err := logging.ParseLevel(cfg.Runner.LogReportLevel); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid runner.log_report_level: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
if err := validateExtraHeaders(cfg.Runner.ExtraHeaders); err != nil {
|
if err := validateExtraHeaders(cfg.Runner.ExtraHeaders); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -172,6 +172,11 @@ func TestLoadDefault_ToolCacheMode(t *testing.T) {
|
|||||||
assert.Contains(t, err.Error(), "tool_cache_mode")
|
assert.Contains(t, err.Error(), "tool_cache_mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoadDefault_RejectsInvalidLogReportLevel(t *testing.T) {
|
||||||
|
_, err := LoadDefault(write(t, "runner:\n log_report_level: loud\n"))
|
||||||
|
require.ErrorContains(t, err, "runner.log_report_level")
|
||||||
|
}
|
||||||
|
|
||||||
func TestLoadDefault_LoadsExtraHeaders(t *testing.T) {
|
func TestLoadDefault_LoadsExtraHeaders(t *testing.T) {
|
||||||
path := filepath.Join(t.TempDir(), "config.yaml")
|
path := filepath.Join(t.TempDir(), "config.yaml")
|
||||||
require.NoError(t, os.WriteFile(path, []byte("runner:\n extra_headers:\n X-Proxy-Token: proxy-token\n"), 0o600))
|
require.NoError(t, os.WriteFile(path, []byte("runner:\n extra_headers:\n X-Proxy-Token: proxy-token\n"), 0o600))
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ type Reporter struct {
|
|||||||
logReportInterval time.Duration
|
logReportInterval time.Duration
|
||||||
logReportMaxLatency time.Duration
|
logReportMaxLatency time.Duration
|
||||||
logBatchSize int
|
logBatchSize int
|
||||||
|
logReportLevel logging.Level
|
||||||
stateReportInterval time.Duration
|
stateReportInterval time.Duration
|
||||||
// closeTimeout bounds each RPC attempt in the final flush, on a context
|
// closeTimeout bounds each RPC attempt in the final flush, on a context
|
||||||
// detached from r.ctx so a server cancel can't abort the acknowledgement.
|
// detached from r.ctx so a server cancel can't abort the acknowledgement.
|
||||||
@@ -133,6 +134,7 @@ func NewReporter(ctx context.Context, cancel context.CancelFunc, client client.C
|
|||||||
logReportInterval: cfg.Runner.LogReportInterval,
|
logReportInterval: cfg.Runner.LogReportInterval,
|
||||||
logReportMaxLatency: cfg.Runner.LogReportMaxLatency,
|
logReportMaxLatency: cfg.Runner.LogReportMaxLatency,
|
||||||
logBatchSize: cfg.Runner.LogReportBatchSize,
|
logBatchSize: cfg.Runner.LogReportBatchSize,
|
||||||
|
logReportLevel: logging.GetLevel(),
|
||||||
stateReportInterval: cfg.Runner.StateReportInterval,
|
stateReportInterval: cfg.Runner.StateReportInterval,
|
||||||
closeTimeout: cfg.Runner.ReportCloseTimeout,
|
closeTimeout: cfg.Runner.ReportCloseTimeout,
|
||||||
logNotify: make(chan struct{}, 1),
|
logNotify: make(chan struct{}, 1),
|
||||||
@@ -146,6 +148,10 @@ func NewReporter(ctx context.Context, cancel context.CancelFunc, client client.C
|
|||||||
jobLog: openJobLog(cfg.Log.Job, task.Id, time.Now()),
|
jobLog: openJobLog(cfg.Log.Job, task.Id, time.Now()),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if level, err := logging.ParseLevel(cfg.Runner.LogReportLevel); err == nil { // unset keeps log.level
|
||||||
|
rv.logReportLevel = level
|
||||||
|
}
|
||||||
|
|
||||||
rv.daemonWait = 6 * rv.effectiveCloseTimeout()
|
rv.daemonWait = 6 * rv.effectiveCloseTimeout()
|
||||||
|
|
||||||
registerGlobalMasks(rv)
|
registerGlobalMasks(rv)
|
||||||
@@ -954,9 +960,9 @@ func (r *Reporter) duringSteps() bool {
|
|||||||
|
|
||||||
// shouldAppendLogRow reports whether a non-raw_output entry should be written
|
// 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
|
// to the job log: only when we are between steps and the entry's level is
|
||||||
// within the globally configured log level.
|
// within runner.log_report_level.
|
||||||
func (r *Reporter) shouldAppendLogRow(entry logging.Record) bool {
|
func (r *Reporter) shouldAppendLogRow(entry logging.Record) bool {
|
||||||
return !r.duringSteps() && entry.Level >= logging.GetLevel()
|
return !r.duringSteps() && entry.Level >= r.logReportLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
var stringToResult = map[string]runnerv1.Result{
|
var stringToResult = map[string]runnerv1.Result{
|
||||||
|
|||||||
@@ -309,7 +309,10 @@ func TestReporter_LogLevelFiltering(t *testing.T) {
|
|||||||
taskCtx, err := structpb.NewStruct(map[string]any{})
|
taskCtx, err := structpb.NewStruct(map[string]any{})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
cfg, _ := config.LoadDefault("")
|
cfg, _ := config.LoadDefault("")
|
||||||
|
cfg.Runner.LogReportLevel = "warn"
|
||||||
reporter := NewReporter(ctx, cancel, client, &runnerv1.Task{Context: taskCtx}, cfg)
|
reporter := NewReporter(ctx, cancel, client, &runnerv1.Task{Context: taskCtx}, cfg)
|
||||||
|
assert.False(t, reporter.shouldAppendLogRow(logging.Record{Level: logging.LevelInfo}))
|
||||||
|
assert.True(t, reporter.shouldAppendLogRow(logging.Record{Level: logging.LevelWarn}))
|
||||||
reporter.RunDaemon()
|
reporter.RunDaemon()
|
||||||
defer func() {
|
defer func() {
|
||||||
require.NoError(t, reporter.Close(""))
|
require.NoError(t, reporter.Close(""))
|
||||||
|
|||||||
Reference in new issue
Block a user