All Products
Search
Document Center

Application Real-Time Monitoring Service:Filter specific spans with custom samplers

Last Updated:Mar 11, 2026

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

MethodWhen to useCode changes required
Agent extensionAutomatic instrumentation with OpenTelemetry Java Agent; no application code changes neededBuild a separate JAR
SDK samplerManual instrumentation with OpenTelemetry SDK for JavaAdd the sampler class to your application code

Node.js

MethodWhen to useCode changes required
Filter at creationPrevent spans from being created for specific HTTP requestsConfigure ignoreIncomingRequestHook in HttpInstrumentation
Filter at exportDrop spans based on attributes evaluated after span creationImplement 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:

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.

Important

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 return SamplingResult.create(SamplingDecision.DROP) for spans to discard, or SamplingResult.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 package

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

Full startup command:

java -javaagent:path/to/opentelemetry-javaagent.jar \
  -Dotel.javaagent.extensions=path/to/opentelemetry-java-agent-extension.jar \
  -Dotel.traces.sampler=<your-sampler-name> \
  -Dotel.exporter.otlp.headers=Authentication=<token> \
  -Dotel.exporter.otlp.endpoint=<endpoint> \
  -Dotel.metrics.exporter=none \
  -jar yourapp.jar

Option B: Environment variable

Set the OTEL_TRACES_SAMPLER environment variable to your sampler name:

Click to view a complete example of the start command

export OTEL_JAVAAGENT_EXTENSIONS="path/to/opentelemetry-java-agent-extension.jar"
export OTEL_TRACES_SAMPLER="<your-sampler-name>"
export OTEL_EXPORTER_OTLP_HEADERS="Authentication=<token>"
export OTEL_EXPORTER_OTLP_ENDPOINT="<endpoint>"
export OTEL_METRICS_EXPORTER="none"

java -javaagent:path/to/opentelemetry-javaagent.jar \
  -jar yourapp.jar

Replace the following placeholders with your actual values:

PlaceholderDescription
<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:

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:

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:

  1. Generate traffic that matches your filter rules. For example, send requests to /api/checkHealth or other filtered endpoints.

  2. Open the Managed Service for OpenTelemetry console. Filtered spans should no longer appear in your traces.

  3. 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:

SymptomPossible causeResolution
Spans still exportedSampler not registeredVerify that OTEL_TRACES_SAMPLER or -Dotel.traces.sampler is set to your sampler name
Sampler not found error on startupName mismatchMake sure getName() in the provider returns the same name used in the startup parameter
Extension JAR not loadedPath incorrectVerify the path in OTEL_JAVAAGENT_EXTENSIONS or -Dotel.javaagent.extensions points to the correct JAR file
Wrong spans filteredFilter rule logic errorReview the conditions in shouldSample and confirm the span names and attribute values match your expectations