package task import ( "context" "fmt" "strings" "sync" "unicode/utf8" ) type outputKey struct{} // Output collects bounded execution details without changing the method contract. // A timed-out method may still write, so snapshots and writes must be synchronized. type Output struct { mu sync.Mutex text string truncated bool } func WithOutput(ctx context.Context) (context.Context, *Output) { output := &Output{} return context.WithValue(ctx, outputKey{}, output), output } // Logf records operational counts and progress. Do not include credentials or // whole request/response bodies. Outside a task execution it is a no-op. func Logf(ctx context.Context, format string, args ...any) { if ctx == nil { return } output, _ := ctx.Value(outputKey{}).(*Output) if output == nil { return } line := fmt.Sprintf(format, args...) output.mu.Lock() defer output.mu.Unlock() const limit = 3000 // Reserve space for the executor's final status. if output.truncated { return } remaining := limit - len(output.text) if len(line)+1 > remaining { cut := max(0, remaining-1) for cut > 0 && !utf8.RuneStart(line[cut]) { cut-- } line = line[:cut] output.truncated = true } output.text += line + "\n" } func (output *Output) String() string { output.mu.Lock() defer output.mu.Unlock() text := strings.TrimSpace(output.text) if output.truncated { text += "\n...(过程详情已截断)" } return text }