64 lines
1.4 KiB
Go
64 lines
1.4 KiB
Go
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
|
|
}
|