Repository navigation
feat(output): fan out one stream to multiple outputs (PIPE-1446) - #315
Conversation
Assisted-By: Claude Opus 4.8
8495fa9 to
1efedbd
Compare
| func fillZeroFields(dst, tmpl reflect.Value) { | ||
| for i := 0; i < dst.NumField(); i++ { | ||
| df := dst.Field(i) | ||
| if !df.CanSet() { | ||
| continue | ||
| } | ||
| if df.Kind() == reflect.Struct { | ||
| fillZeroFields(df, tmpl.Field(i)) | ||
| continue | ||
| } | ||
| if df.IsZero() { |
There was a problem hiding this comment.
fillZeroFields treats a zero value as "unset", so an explicit false or 0 in an outputs: entry is overwritten by the singular output default. There are two real cases: syslog.facility: 0 (kern, and validation accepts 0–23) comes out as 1, and file.rotation.compress: false comes out as true. YAML decoding produces the same zero values, so real configs hit this. The struct can't tell "unset" from "explicit zero".
| func supports(o Output, t telemetry.Type) bool { | ||
| for _, s := range o.SupportedTelemetry() { | ||
| if s == t { | ||
| return true | ||
| } | ||
| } | ||
| return false | ||
| } |
There was a problem hiding this comment.
supports() calls SupportedTelemetry() on every write, which costs one extra allocation per child per record. Precomputing per-signal child lists at construction avoids it.
| type MultiOutput struct { | ||
| outputs []Output | ||
| } | ||
|
|
||
| // NewMultiOutput wraps the given outputs. | ||
| func NewMultiOutput(outputs ...Output) *MultiOutput { | ||
| return &MultiOutput{outputs: outputs} | ||
| } |
There was a problem hiding this comment.
Fan-out is serial, so a hanging child throttles the others and uses up the shared 5s timeout. The docs only promise that a dead destination doesn't stop the others.
Proposed Change
Add an
outputs:config list that fans one generated stream out to several destinations in a single run. Today blitz sends to exactly one output, so comparing destinations against identical input is impossible. While the Blitz setup and environment are deterministic, the generators randomize per run. So running blitz twice does not reproduce the same records. Worked around previously by sending to an intermediary OTel collector, but that was not ideal.Docs: fan-out section added to
configuration.md.Checklist