All Products
Search
Document Center

Managed Service for OpenTelemetry:Use Managed Service for OpenTelemetry to filter out specific spans

Last Updated:Jun 08, 2026

Filter out health checks, internal probes, or other noise spans from Java and Node.js applications by writing a custom sampler with Managed Service for OpenTelemetry.

Java application

Choose a method based on how your application reports data:

Scenario

Method

Using OpenTelemetry Java Agent (auto-instrumentation) — no code changes wanted

Method 1: Create an extension for Managed Service for OpenTelemetry Java Agent

Using Managed Service for OpenTelemetry SDK for Java (manual instrumentation)

Method 2: Use Managed Service for OpenTelemetry SDK for Java

Both methods work by implementing a custom Sampler with shouldSample(). Return SamplingDecision.DROP to discard a span, or SamplingDecision.RECORD_AND_SAMPLE to keep and export it.

Method 1: Create an extension for Managed Service for OpenTelemetry Java Agent

Prerequisites

The application is automatically instrumented using OpenTelemetry Java Agent. For more information, see Report trace data from Java applications by using OpenTelemetry.

  1. Create a Maven project.

    This project builds the extension JAR that the agent loads at startup.

  2. Add the following dependencies to pom.xml.

    Important

    Use the same version for all OpenTelemetry dependencies. The version must match the OpenTelemetry Java Agent version you are running.

    <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 value of the scope field 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>
  3. Create the SpanFilterSampler class.

    The class implements io.opentelemetry.sdk.trace.samplers.Sampler and must provide two methods:

    • shouldSample

      Defines the filter rules. Return SamplingResult.create(SamplingDecision.DROP) to discard a span, or SamplingResult.create(SamplingDecision.RECORD_AND_SAMPLE) to keep it.

    • getDescription

      Returns the sampler name. The agent uses this name to look up the sampler.

    The following example filters out spans by name (spanName1, spanName2) and by HTTP target (/api/checkHealth, /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 {
        /*
         * Filter out spans whose name is in EXCLUDED_SPAN_NAMES.
         */
        private static List<String> EXCLUDED_SPAN_NAMES = Collections.unmodifiableList(
            Arrays.asList("spanName1", "spanName2")
        );
    
        /*
         * Filter out spans whose attributes.http.target is in EXCLUDED_HTTP_REQUEST_TARGETS.
         */
        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)) {
                // Drop spans matching the filter rules.
                return SamplingResult.create(SamplingDecision.DROP);
            } else {
                return SamplingResult.create(SamplingDecision.RECORD_AND_SAMPLE);
            }
        }
    
        @Override
        public String getDescription() {
            return "SpanFilterSampler"; // Replace SpanFilterSampler with a custom name if needed.
        }
    }
  4. Create the SpanFilterSamplerProvider class.

    The class implements io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSamplerProvider and must provide two methods:

    • createSampler

      Creates and returns your custom sampler instance.

    • getName

      Returns the sampler name. OpenTelemetry Java Agent uses this name to find the sampler at startup.

    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"; // Replace SpanFilterSampler with a custom name if needed.
        }
    }
  5. Build the extension JAR.

    mvn clean package

    The JAR is saved to the target directory.

  6. Load the extension when starting the application.

    Set the custom sampler name (the value returned by getName) using one of the following approaches:

    • Add a JVM parameter:

      -Dotel.traces.sampler=<your-sampler-name>

      Replace <your-sampler-name> with the value returned by the getName method.

      Show the complete startup command example

      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
    • Set an environment variable:

      export OTEL_TRACES_SAMPLER="<your-sampler-name>"

      Replace <your-sampler-name> with the value returned by the getName method.

      Show the complete startup command example

      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

Method 2: Use Managed Service for OpenTelemetry SDK for Java

Prerequisites

The application is manually instrumented using Managed Service for OpenTelemetry SDK for Java. For more information, see Report trace data from Java applications by using OpenTelemetry.

  1. Create a custom sampler class in your application code.

    The class implements io.opentelemetry.sdk.trace.samplers.Sampler and must provide two methods:

    • shouldSample

      Defines the filter rules. Return SamplingResult.create(SamplingDecision.DROP) to discard a span, or SamplingResult.create(SamplingDecision.RECORD_AND_SAMPLE) to keep it.

    • getDescription

      Returns the sampler name.

    The following example filters out spans by name (spanName1, spanName2) and by HTTP target (/api/checkHealth, /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 {
        /*
         * Filter out spans whose name is in EXCLUDED_SPAN_NAMES.
         */
        private static List<String> EXCLUDED_SPAN_NAMES = Collections.unmodifiableList(
            Arrays.asList("spanName1", "spanName2")
        );
    
        /*
         * Filter out spans whose attributes.http.target is in EXCLUDED_HTTP_REQUEST_TARGETS.
         */
        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)) {
                // Drop spans matching the filter rules.
                return SamplingResult.create(SamplingDecision.DROP);
            } else {
                return SamplingResult.create(SamplingDecision.RECORD_AND_SAMPLE);
            }
        }
    
        @Override
        public String getDescription() {
            return "SpanFilterSampler"; // Replace SpanFilterSampler with a custom name if needed.
        }
    }
  2. Register the custom sampler when creating a SdkTracerProvider instance.

    Call setSampler(new SpanFilterSampler()) in the builder:

    SdkTracerProvider sdkTracerProvider = SdkTracerProvider.builder()
        .setSampler(new SpanFilterSampler()) // Add this line.
        .addSpanProcessor(BatchSpanProcessor.builder(OtlpGrpcSpanExporter.builder()
            .setEndpoint("<endpoint>")
            .addHeader("Authentication", "<token>")
            .build()).build())
        .setResource(resource)
        .build();
  3. Start the application.

Node.js application

Demo: opentelemetry-nodejs-demo.

Prerequisites

The application is instrumented using the Managed Service for OpenTelemetry API for JavaScript. For more information, see Submit trace data from a Node.js application.

Method 1: Filter out spans when they are created

  1. Configure ignoreIncomingRequestHook when constructing HttpInstrumentation.

    ignoreIncomingRequestHook accepts a function that runs before the request is processed. Return true to skip instrumentation for that request — no span is created, and the request completes normally.

    The following example skips instrumentation for requests to /api/checkHealth:

    // Replace the original registration block:
    // registerInstrumentations({
    //   tracerProvider: provider,
    //   instrumentations: [new HttpInstrumentation(), ExpressInstrumentation],
    // });
    
    const httpInstrumentation = new HttpInstrumentation({
        ignoreIncomingRequestHook: (request) => {
            // Skip requests to /api/checkHealth.
            if (request.url === '/api/checkHealth') {
                return true;
            }
            return false;
        },
    });
    
    registerInstrumentations({
        tracerProvider: provider,
        instrumentations: [httpInstrumentation, ExpressInstrumentation],
    });
  2. Start the application.

Method 2: Filter out spans when they are reported

  1. Create a custom sampler class that implements the Sampler interface.

    Define your sampling logic in shouldSample. Example:

    const opentelemetry = require('@opentelemetry/api');
    
    class SpanFilterSampler {
        shouldSample(spanContext, parentContext) {
            // Implement your custom sampling logic here.
        }
    }
  2. Register the custom sampler when creating a NodeTracerProvider instance.

    const provider = new NodeTracerProvider({
        sampler: new SpanFilterSampler(), // Add this line to set the custom sampler.
        resource: new Resource({
            [SemanticResourceAttributes.HOST_NAME]: require("os").hostname(),
            [SemanticResourceAttributes.SERVICE_NAME]: "<your-service-name>",
        }),
    });