diff --git a/.chloggen/tailsampling-default-error-mode-ignore.yaml b/.chloggen/tailsampling-default-error-mode-ignore.yaml new file mode 100644 index 0000000000000..4d72f5000754e --- /dev/null +++ b/.chloggen/tailsampling-default-error-mode-ignore.yaml @@ -0,0 +1,9 @@ +change_type: enhancement +component: processor/tailsampling +note: Add `processor.tailsamplingprocessor.defaultErrorModeIgnore` feature gate to change default `error_mode` to `ignore` for ottl_condition policies +issues: [48420] +subtext: | + When the feature gate is enabled, the processor defaults to `ignore` mode for OTTL condition + evaluations which preserves valid data when an OTTL condition error occurs, improving resiliency. + This is a breaking change rolled out via feature gate to allow gradual adoption. +change_logs: [user] diff --git a/processor/tailsamplingprocessor/config.go b/processor/tailsamplingprocessor/config.go index 26e18f39808e4..0cde7bd916fc4 100644 --- a/processor/tailsamplingprocessor/config.go +++ b/processor/tailsamplingprocessor/config.go @@ -296,6 +296,10 @@ type BooleanAttributeCfg struct { // OTTLConditionCfg holds the configurable setting to create a OTTL condition filter // sampling policy evaluator. type OTTLConditionCfg struct { + // ErrorMode determines how the processor reacts to OTTL condition errors. + // Valid values are `ignore`, `silent`, and `propagate`. + // The default value is `propagate`, but when the `processor.tailsamplingprocessor.defaultErrorModeIgnore` + // feature gate is enabled, the default changes to `ignore`. ErrorMode ottl.ErrorMode `mapstructure:"error_mode"` SpanConditions []string `mapstructure:"span"` SpanEventConditions []string `mapstructure:"spanevent"` diff --git a/processor/tailsamplingprocessor/internal/metadata/generated_feature_gates.go b/processor/tailsamplingprocessor/internal/metadata/generated_feature_gates.go index 300119c6b3a4a..dc126ce4a66d9 100644 --- a/processor/tailsamplingprocessor/internal/metadata/generated_feature_gates.go +++ b/processor/tailsamplingprocessor/internal/metadata/generated_feature_gates.go @@ -38,3 +38,10 @@ var ProcessorTailsamplingprocessorTailstorageextensionFeatureGate = featuregate. featuregate.WithRegisterReferenceURL("https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/47331"), featuregate.WithRegisterFromVersion("v0.150.0"), ) + +var ProcessorTailsamplingprocessorDefaultErrorModeIgnoreFeatureGate = featuregate.GlobalRegistry().MustRegister( + "processor.tailsamplingprocessor.defaultErrorModeIgnore", + featuregate.StageAlpha, + featuregate.WithRegisterDescription("When enabled, the default error_mode for ottl_condition policies is `ignore` instead of `propagate`."), + featuregate.WithRegisterReferenceURL("https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/48420"), +) diff --git a/processor/tailsamplingprocessor/metadata.yaml b/processor/tailsamplingprocessor/metadata.yaml index 77afd7f8cde3a..05da9e266fabb 100644 --- a/processor/tailsamplingprocessor/metadata.yaml +++ b/processor/tailsamplingprocessor/metadata.yaml @@ -37,6 +37,11 @@ feature_gates: from_version: v0.150.0 reference_url: https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/47331 + - id: "processor.tailsamplingprocessor.defaultErrorModeIgnore" + description: When enabled, the default error_mode for ottl_condition policies is `ignore` instead of `propagate`. + stage: alpha + reference_url: https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/48420 + tests: config: diff --git a/processor/tailsamplingprocessor/processor.go b/processor/tailsamplingprocessor/processor.go index b4ccede5d61b7..a297479df4374 100644 --- a/processor/tailsamplingprocessor/processor.go +++ b/processor/tailsamplingprocessor/processor.go @@ -24,6 +24,7 @@ import ( "go.opentelemetry.io/otel/trace" "go.uber.org/zap" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/tailsamplingprocessor/cache" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/tailsamplingprocessor/internal/idbatcher" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/tailsamplingprocessor/internal/metadata" @@ -450,7 +451,15 @@ func getSharedPolicyEvaluator(settings component.TelemetrySettings, cfg *sharedP return sampling.NewBooleanAttributeFilter(settings, bafCfg.Key, bafCfg.Value, bafCfg.InvertMatch), nil case OTTLCondition: ottlfCfg := cfg.OTTLConditionCfg - return sampling.NewOTTLConditionFilter(settings, ottlfCfg.SpanConditions, ottlfCfg.SpanEventConditions, ottlfCfg.ErrorMode) + errorMode := ottlfCfg.ErrorMode + if errorMode == "" { + if metadata.ProcessorTailsamplingprocessorDefaultErrorModeIgnoreFeatureGate.IsEnabled() { + errorMode = ottl.IgnoreError + } else { + errorMode = ottl.PropagateError + } + } + return sampling.NewOTTLConditionFilter(settings, ottlfCfg.SpanConditions, ottlfCfg.SpanEventConditions, errorMode) case TraceFlags: return sampling.NewTraceFlags(settings), nil default: diff --git a/processor/tailsamplingprocessor/processor_test.go b/processor/tailsamplingprocessor/processor_test.go index 397f459a25c4c..c90e2ca46d24d 100644 --- a/processor/tailsamplingprocessor/processor_test.go +++ b/processor/tailsamplingprocessor/processor_test.go @@ -30,6 +30,7 @@ import ( "go.uber.org/zap" "go.uber.org/zap/zaptest/observer" + "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/tailsamplingprocessor/internal/idbatcher" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/tailsamplingprocessor/internal/metadata" "github.com/open-telemetry/opentelemetry-collector-contrib/processor/tailsamplingprocessor/internal/tailstorageextension" @@ -1788,3 +1789,52 @@ func (*nonTailStorageExtension) Start(context.Context, component.Host) error { func (*nonTailStorageExtension) Shutdown(context.Context) error { return nil } + +func TestOTTLConditionDefaultErrorModeFeatureGate(t *testing.T) { + tests := []struct { + name string + featureGateEnabled bool + expectedErrorMode ottl.ErrorMode + }{ + { + name: "feature gate disabled", + featureGateEnabled: false, + expectedErrorMode: ottl.PropagateError, + }, + { + name: "feature gate enabled", + featureGateEnabled: true, + expectedErrorMode: ottl.IgnoreError, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + prev := metadata.ProcessorTailsamplingprocessorDefaultErrorModeIgnoreFeatureGate.IsEnabled() + require.NoError(t, featuregate.GlobalRegistry().Set( + metadata.ProcessorTailsamplingprocessorDefaultErrorModeIgnoreFeatureGate.ID(), + tt.featureGateEnabled, + )) + t.Cleanup(func() { + require.NoError(t, featuregate.GlobalRegistry().Set( + metadata.ProcessorTailsamplingprocessorDefaultErrorModeIgnoreFeatureGate.ID(), + prev, + )) + }) + + cfg := PolicyCfg{ + sharedPolicyCfg: sharedPolicyCfg{ + Name: "ottl-test", + Type: OTTLCondition, + OTTLConditionCfg: OTTLConditionCfg{ + SpanConditions: []string{"true"}, + }, + }, + } + + evaluator, err := getSharedPolicyEvaluator(processortest.NewNopSettings(metadata.Type), &cfg, nil) + require.NoError(t, err) + require.NotNil(t, evaluator) + }) + } +}