Files
7aa4190841 Emit outcome: failure in obsconsumer (#13234)
#### Description
The last remaining part of #12676 is to implement the `outcome: failure`
part of the Pipeline Component Telemetry RFC ([see
here](https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/rfcs/component-universal-telemetry.md#auto-instrumented-metrics)).
This is done by introducing a `downstream` error wrapper struct, to
distinguish between errors coming from the next component from errors
bubbled from further downstream.

#### Important note
This PR implements things slightly differently from what the text of the
RFC describes.

If a pipeline contains components `A → B` and an error occurs in B, this
PR records:
- `otelcol.component.outcome = failure` in the `otelcol.*.consumed.*`
metric for B
- `otelcol.component.outcome = refused` in the `otelcol.*.produced.*`
metric for A

whereas the RFC would set both `outcome`s to `failure`.

This is programmatically simpler — no need to have different behavior
between the `obsconsumer` around the output of A and the one around the
input of B — but more importantly, I think it is clearer for users as
well: `outcome = failure` only occurs on metrics associated with the
component where the failure actually occurred.

This subtlety wasn't discussed in-depth in #11956 which introduced
`outcome = refused`, so I took the liberty to make this change. If
necessary, I can file another RFC amendment to match, or, if there are
objections, implement the RFC as-is instead.

#### Link to tracking issue
Fixes #12676

#### Testing
I've updated the existing tests in `obsconsumer` to expect a
`downstream`-wrapped error to exit the `obsconsumer` layer. I may add
more tests later.

#### Documentation
None.

---------

Co-authored-by: Alex Boten <[email protected]>
Co-authored-by: Pablo Baeyens <[email protected]>
2025-07-25 10:19:48 +00:00

54 lines
1.8 KiB
Go

// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package obsconsumer // import "go.opentelemetry.io/collector/service/internal/obsconsumer"
import (
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/metric"
)
const (
ComponentOutcome = "otelcol.component.outcome"
)
// Option modifies the consumer behavior.
type Option func(*options)
type options struct {
staticDataPointAttributes []attribute.KeyValue
}
// WithStaticDataPointAttribute returns an Option that adds a static attribute to data points.
func WithStaticDataPointAttribute(attr attribute.KeyValue) Option {
return func(opts *options) {
opts.staticDataPointAttributes = append(opts.staticDataPointAttributes, attr)
}
}
type compiledOptions struct {
withSuccessAttrs metric.AddOption
withFailureAttrs metric.AddOption
withRefusedAttrs metric.AddOption
}
func (o *options) compile() compiledOptions {
successAttrs := make([]attribute.KeyValue, 0, 1+len(o.staticDataPointAttributes))
successAttrs = append(successAttrs, attribute.String(ComponentOutcome, "success"))
successAttrs = append(successAttrs, o.staticDataPointAttributes...)
failureAttrs := make([]attribute.KeyValue, 0, 1+len(o.staticDataPointAttributes))
failureAttrs = append(failureAttrs, attribute.String(ComponentOutcome, "failure"))
failureAttrs = append(failureAttrs, o.staticDataPointAttributes...)
refusedAttrs := make([]attribute.KeyValue, 0, 1+len(o.staticDataPointAttributes))
refusedAttrs = append(refusedAttrs, attribute.String(ComponentOutcome, "refused"))
refusedAttrs = append(refusedAttrs, o.staticDataPointAttributes...)
return compiledOptions{
withSuccessAttrs: metric.WithAttributes(successAttrs...),
withFailureAttrs: metric.WithAttributes(failureAttrs...),
withRefusedAttrs: metric.WithAttributes(refusedAttrs...),
}
}