Skip to content

feat(output): fan out one stream to multiple outputs (PIPE-1446) - #315

Open
Dylan-M wants to merge 1 commit into
dylanmyers/pipe-1492-output-signal-interface-reshape-writer-base-optionalfrom
dylanmyers/pipe-1446-support-multiple-simultaneous-outputs-fan-out
Open

Dylan-M wants to merge 1 commit into
dylanmyers/pipe-1492-output-signal-interface-reshape-writer-base-optionalfrom
dylanmyers/pipe-1446-support-multiple-simultaneous-outputs-fan-out

Conversation

@Dylan-M

@Dylan-M Dylan-M commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

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
  • Changes are tested
  • CI has passed

@Dylan-M
Dylan-M requested review from a team as code owners September 22, 2026 21:24
@Dylan-M
Dylan-M force-pushed the dylanmyers/pipe-1446-support-multiple-simultaneous-outputs-fan-out branch from 8495fa9 to 1efedbd Compare September 22, 2026 22:04
Comment thread internal/config/config.go
Comment on lines +102 to +112
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() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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".

Comment thread output/multi.go
Comment on lines +100 to +107
func supports(o Output, t telemetry.Type) bool {
for _, s := range o.SupportedTelemetry() {
if s == t {
return true
}
}
return false
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

supports() calls SupportedTelemetry() on every write, which costs one extra allocation per child per record. Precomputing per-signal child lists at construction avoids it.

Comment thread output/multi.go
Comment on lines +14 to +21
type MultiOutput struct {
outputs []Output
}

// NewMultiOutput wraps the given outputs.
func NewMultiOutput(outputs ...Output) *MultiOutput {
return &MultiOutput{outputs: outputs}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants