Add FilteringSpanExporter with composable SpanFilter and TraceFilter interfaces#2745
Add FilteringSpanExporter with composable SpanFilter and TraceFilter interfaces#2745udaysagar2177 wants to merge 5 commits intoopen-telemetry:mainfrom
Conversation
…interfaces Adds a trace-level filtering span exporter to the processors module. FilteringSpanExporter wraps a delegate SpanExporter and only forwards spans belonging to traces that match at least one configured filter. Filtering is trace-aware: if any filter matches, all spans sharing that trace ID are exported together. Two filter interfaces enable composable filtering: - SpanFilter: per-span evaluation (e.g., error status, slow duration) - TraceFilter: batch-level evaluation (e.g., overall trace wall-clock duration) Built-in implementations: - ErrorSpanFilter: keeps traces with error spans - DurationSpanFilter: keeps traces with slow individual spans - TraceDurationFilter: keeps traces with long wall-clock duration Optional Meter parameter emits a dropped-span counter with reason attribute.
There was a problem hiding this comment.
Pull request overview
This pull request adds a FilteringSpanExporter with composable SpanFilter and TraceFilter interfaces for trace-level filtering in the OpenTelemetry Java contrib project. The feature enables services with heavy sampling to prioritize exporting traces containing errors or exhibiting unusual latency patterns. The implementation includes a core exporter wrapper that groups spans by trace ID and makes filtering decisions at the trace level, along with three built-in filter implementations for common use cases.
Changes:
- Adds two new filter interfaces (
SpanFilter,TraceFilter) for composable, user-extensible filtering - Implements three built-in filters (
ErrorSpanFilter,DurationSpanFilter,TraceDurationFilter) for common scenarios - Implements
FilteringSpanExporterwith single-pass filtering logic that maintains trace-level grouping - Includes optional metrics emission for observability into dropped spans
- Adds comprehensive test coverage (24 tests total) covering composition, grouping, edge cases, and metrics
- Updates README documentation with usage examples
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
SpanFilter.java |
Interface for per-span filtering evaluation |
TraceFilter.java |
Interface for batch-level trace filtering |
ErrorSpanFilter.java |
Built-in filter for error status detection |
DurationSpanFilter.java |
Built-in filter for individual span duration thresholds |
TraceDurationFilter.java |
Built-in filter for trace-level wall-clock duration thresholds |
FilteringSpanExporter.java |
Core exporter wrapper with trace-aware filtering logic |
*Test.java |
Comprehensive unit tests for all components |
processors/README.md |
Documentation and usage examples |
| if (meter != null) { | ||
| this.droppedSpansCounter = | ||
| meter | ||
| .counterBuilder("filtering.span.exporter.dropped") |
There was a problem hiding this comment.
in the sdk they have metrics that look like otel.sdk.exporter.span.exported and otel.sdk.processor.span.processed, so perhaps we should use something closer to that pattern like otel.contrib.exporter.span.filtered or otel.contrib.processor.span.dropped? Or maybe having contrib doesn't make sense in this context
@trask wdyt?
| .counterBuilder("filtering.span.exporter.dropped") | |
| .counterBuilder("otel.contrib.processor.span.filtered") |
There was a problem hiding this comment.
Used otel.contrib.processor.span.filtered — aligns well with the SDK naming conventions. Happy to adjust if @trask has a different preference.
- Accept Duration instead of long millis in DurationSpanFilter and TraceDurationFilter
- Add negative threshold validation to both duration filters
- Add early return for empty span list in TraceDurationFilter (bug: overflow caused false positive)
- Add Objects.requireNonNull validation in FilteringSpanExporter constructor
- Rename metric to otel.contrib.processor.span.filtered and add unit {span}
- Add tests for empty span list and negative threshold validation
Javadoc and README previously said "keeps traces" which implies full trace-level guarantees. Reworded to make clear that filtering decisions apply only to spans within the same export() call, and that a trace split across batches may be partially exported. Also updated README code examples to use Duration API.
|
@udaysagar2177 looks like there's some formatting issues, could you run spotless to fix that up? |
Summary
Adds a trace-level filtering
SpanExporterto theprocessorsmodule.FilteringSpanExporterwraps a delegate exporter and only forwards spans belonging to traces that match at least one configured filter. Filtering is trace-aware: if any filter matches, all spans sharing that trace ID are exported together.Two filter interfaces enable composable, user-extensible filtering:
SpanFilter— per-span evaluation (e.g., error status, slow duration)TraceFilter— batch-level evaluation over all spans sharing a trace ID (e.g., overall trace wall-clock duration)Built-in implementations:
ErrorSpanFilterSpanFilterStatusCode.ERRORDurationSpanFilterSpanFilterTraceDurationFilterTraceFilterAn optional
Meterparameter emits afiltering.span.exporter.droppedcounter with areasonattribute for observability into dropped spans.Motivation
Services with heavy sampling often want to prioritize exporting traces that contain errors or exhibit unusual latency, without increasing overall sampling rate. This exporter wrapper provides that capability as a composable, upstream component that any OTel Java user can plug into their SDK configuration.
The existing
InterceptableSpanExporterin this module operates per-span via theInterceptorinterface, but has no trace-level grouping.FilteringSpanExporterfills this gap by grouping spans by trace ID and making keep/drop decisions at the trace level.Usage
Changes
SpanFilter.javaTraceFilter.javaErrorSpanFilter.javaDurationSpanFilter.javaTraceDurationFilter.javaFilteringSpanExporter.javaErrorSpanFilterTest.javaDurationSpanFilterTest.javaTraceDurationFilterTest.javaFilteringSpanExporterTest.javaREADME.mdTest plan
ErrorSpanFilterTest— error, OK, UNSET status codesDurationSpanFilterTest— over/at/under threshold boundary casesTraceDurationFilterTest— over/at/under threshold, single-span traceFilteringSpanExporterTest— trace grouping, mixed traces, span-only/trace-only/custom filter composition, dropped-span metrics, flush/shutdown delegation, empty batch