Health checks, readiness probes, and other routine requests generate spans that add noise to your traces and increase costs. Use a custom OpenTelemetry sampler to drop these spans before they leave your application.
Managed Service for OpenTelemetry supports span filtering for Java and Node.js applications.
How it works
OpenTelemetry uses samplers to decide whether to record and export each span. A custom sampler inspects span properties -- such as the span name or HTTP target path -- and returns one of two decisions:
DROP (
SamplingDecision.DROP) -- The span is discarded. It is not recorded or exported.RECORD_AND_SAMPLE (
SamplingDecision.RECORD_AND_SAMPLE) -- The span is kept, recorded, and exported normally.
Write a sampler that matches unwanted spans and returns DROP to eliminate noise at the source.
Choose a method
The right approach depends on your instrumentation setup and programming language.
Java
| Method | When to use | Code changes required |
|---|---|---|
| Agent extension | Automatic instrumentation with OpenTelemetry Java Agent; no application code changes needed | Build a separate JAR |
| SDK sampler | Manual instrumentation with OpenTelemetry SDK for Java | Add the sampler class to your application code |
Node.js
| Method | When to use | Code changes required |
|---|---|---|
| Filter at creation | Prevent spans from being created for specific HTTP requests | Configure ignoreIncomingRequestHook in HttpInstrumentation |
| Filter at export | Drop spans based on attributes evaluated after span creation | Implement a custom sampler class |
Java
Create an agent extension for OpenTelemetry Java Agent
Build a custom sampler as a Java Agent extension. This approach keeps filtering logic separate from your application code.
Prerequisites
Before you begin, make sure that you have:
An application automatically instrumented with OpenTelemetry Java Agent. For setup instructions, see Use OpenTelemetry to submit the trace data of Java applications
Apache Maven installed for building the extension JAR
Step 1: Create a Maven project
Create a new Maven project to build the agent extension.
Step 2: Add dependencies
Add the following dependencies to your pom.xml file.
All OpenTelemetry dependencies must match the version of the OpenTelemetry Java Agent you use. The following example uses version 1.28.0.
<dependency>
<groupId>com.google.auto.service</groupId>
<artifactId>auto-service</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>io.opentelemetry.javaagent</groupId>
<artifactId>opentelemetry-javaagent</artifactId>
<version>1.28.0</version>
<!--Set the scope to compile.-->
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-trace</artifactId>
<version>1.28.0</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId>
<version>1.28.0</version>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-semconv</artifactId>
<version>1.28.0-alpha</version>
</dependency>Step 3: Implement the sampler
Create a class that implements the io.opentelemetry.sdk.trace.samplers.Sampler interface. Define your filtering rules in the shouldSample method and return a sampler name from getDescription.
shouldSample-- Evaluate each span and returnSamplingResult.create(SamplingDecision.DROP)for spans to discard, orSamplingResult.create(SamplingDecision.RECORD_AND_SAMPLE)for spans to keep.getDescription-- Return the sampler name.
The following example drops spans named spanName1 or spanName2, and spans with an http.target attribute of /api/checkHealth or /health/checks:
package org.example;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.context.Context;
import io.opentelemetry.sdk.trace.data.LinkData;
import io.opentelemetry.sdk.trace.samplers.Sampler;
import io.opentelemetry.sdk.trace.samplers.SamplingDecision;
import io.opentelemetry.sdk.trace.samplers.SamplingResult;
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
import java.util.*;
public class SpanFilterSampler implements Sampler {
// Span names to drop
private static List<String> EXCLUDED_SPAN_NAMES = Collections.unmodifiableList(
Arrays.asList("spanName1", "spanName2")
);
// HTTP target paths to drop
private static List<String> EXCLUDED_HTTP_REQUEST_TARGETS = Collections.unmodifiableList(
Arrays.asList("/api/checkHealth", "/health/checks")
);
@Override
public SamplingResult shouldSample(Context parentContext, String traceId, String name,
SpanKind spanKind, Attributes attributes, List<LinkData> parentLinks) {
String httpTarget = attributes.get(SemanticAttributes.HTTP_TARGET) != null
? attributes.get(SemanticAttributes.HTTP_TARGET) : "";
if (EXCLUDED_SPAN_NAMES.contains(name)
|| EXCLUDED_HTTP_REQUEST_TARGETS.contains(httpTarget)) {
return SamplingResult.create(SamplingDecision.DROP);
}
return SamplingResult.create(SamplingDecision.RECORD_AND_SAMPLE);
}
@Override
public String getDescription() {
return "SpanFilterSampler";
}
}Step 4: Implement the sampler provider
Create a class that implements io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider. This provider registers your sampler with the Java Agent's auto-configuration mechanism.
createSampler-- Return an instance of your sampler.getName-- Return the sampler name. The Java Agent uses this name to locate the sampler.
package org.example;
import com.google.auto.service.AutoService;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider;
import io.opentelemetry.sdk.trace.samplers.Sampler;
@AutoService(ConfigurableSamplerProvider.class)
public class SpanFilterSamplerProvider implements ConfigurableSamplerProvider {
@Override
public Sampler createSampler(ConfigProperties configProperties) {
return new SpanFilterSampler();
}
@Override
public String getName() {
return "SpanFilterSampler";
}
}Step 5: Build the extension JAR
Package the project into a JAR file:
mvn clean packageThe JAR file is generated in the target directory.
Step 6: Load the extension at startup
Specify your custom sampler when starting the application. Use either a JVM system property or an environment variable.
Option A: JVM system property
Add -Dotel.traces.sampler=<your-sampler-name> to the JVM startup parameters. Replace <your-sampler-name> with the value returned by the getName method.
Option B: Environment variable
Set the OTEL_TRACES_SAMPLER environment variable to your sampler name:
Replace the following placeholders with your actual values:
| Placeholder | Description |
|---|---|
<your-sampler-name> | Sampler name returned by getName |
<token> | Authentication token for the OTLP endpoint |
<endpoint> | OTLP exporter endpoint URL |
Register a custom sampler with OpenTelemetry SDK for Java
For applications with manual instrumentation, add the sampler directly to your application code.
Prerequisites
Before you begin, make sure that you have:
An application manually instrumented with OpenTelemetry SDK for Java. For setup instructions, see Use OpenTelemetry to submit the trace data of Java applications
Step 1: Create the sampler class
Create a SpanFilterSampler class that implements the Sampler interface. The implementation is the same as the agent extension sampler in Step 3: Implement the sampler. Adapt the EXCLUDED_SPAN_NAMES and EXCLUDED_HTTP_REQUEST_TARGETS lists to match the spans you want to drop.
Step 2: Register the sampler with SdkTracerProvider
Call .setSampler(new SpanFilterSampler()) when building the SdkTracerProvider instance:
SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()
.setSampler(new SpanFilterSampler()) // Register the custom sampler
.addSpanProcessor(BatchSpanProcessor.builder(OtlpGrpcSpanExporter.builder()
.setEndpoint("<endpoint>")
.addHeader("Authentication", "<token>")
.build()).build())
.setResource(resource)
.build();Step 3: Restart the application
Restart your application. The sampler evaluates every new span and drops those matching the filter rules.
Node.js
Demo project: opentelemetry-nodejs-demo
Prerequisites
Before you begin, make sure that you have:
An application instrumented with the Managed Service for OpenTelemetry API for JavaScript. For setup instructions, see Use OpenTelemetry to submit the trace data of a Node.js application
Filter spans at creation time
Prevent specific HTTP requests from generating spans by configuring ignoreIncomingRequestHook in HttpInstrumentation. The hook runs before request processing and only controls whether a span is created -- the request itself is handled normally.
The following example skips span creation for requests to /api/checkHealth:
const httpInstrumentation = new HttpInstrumentation({
ignoreIncomingRequestHook: (request) => {
// Skip span creation for health check requests
if (request.url === '/api/checkHealth') {
return true;
}
return false;
},
});
registerInstrumentations({
tracerProvider: provider,
instrumentations: [httpInstrumentation, ExpressInstrumentation],
});Start the application after updating the instrumentation configuration.
Filter spans at export time
Drop spans based on their attributes after creation by implementing a custom sampler.
Step 1: Create a sampler class
Create a class that implements the Sampler interface. Define your filtering logic in shouldSample:
const opentelemetry = require('@opentelemetry/api');
class SpanFilterSampler {
shouldSample(spanContext, parentContext) {
// Implement your custom sampling logic here.
}
}Step 2: Register the sampler with NodeTracerProvider
Pass the sampler instance to the NodeTracerProvider constructor:
const provider = new NodeTracerProvider({
sampler: new SpanFilterSampler(), // Register the custom sampler
resource: new Resource({
[SemanticResourceAttributes.HOST_NAME]: require("os").hostname(),
[SemanticResourceAttributes.SERVICE_NAME]: "<your-service-name>",
}),
});Replace <your-service-name> with the name of your service, for example, my-node-app.
Start the application after updating the provider configuration.
Verify that filtering works
After you configure and deploy a custom sampler, verify that spans are being filtered correctly:
Generate traffic that matches your filter rules. For example, send requests to
/api/checkHealthor other filtered endpoints.Open the Managed Service for OpenTelemetry console. Filtered spans should no longer appear in your traces.
Compare the span count before and after filtering. A reduction in span volume for the filtered endpoints confirms that the sampler is working.
If filtered spans still appear, check the following:
| Symptom | Possible cause | Resolution |
|---|---|---|
| Spans still exported | Sampler not registered | Verify that OTEL_TRACES_SAMPLER or -Dotel.traces.sampler is set to your sampler name |
| Sampler not found error on startup | Name mismatch | Make sure getName() in the provider returns the same name used in the startup parameter |
| Extension JAR not loaded | Path incorrect | Verify the path in OTEL_JAVAAGENT_EXTENSIONS or -Dotel.javaagent.extensions points to the correct JAR file |
| Wrong spans filtered | Filter rule logic error | Review the conditions in shouldSample and confirm the span names and attribute values match your expectations |