OpenTelemetry bridgeedit

The Elastic APM OpenTelemetry bridge allows creating Elastic APM Transactions and Spans, using the OpenTelemetry API. In other words, it translates the calls to the OpenTelemetry API to Elastic APM and thus allows for reusing existing instrumentation.

While manual instrumentations using the OpenTelemetry API can be adapted to the Elastic APM Java agent, it’s not possible to use the instrumentations from opentelemetry-java-instrumentation in the context of the Elastic APM Java agent.
However, you can use opentelemetry-java-instrumentation (aka the OpenTelemetry Java agent) and send the data to APM Server. See the OpenTelemetry integration docs for more details.

The first span of a service will be converted to an Elastic APM Transaction, subsequent spans are mapped to Elastic APM Span.

Getting startededit

The first step in getting started with the OpenTelemetry API bridge is to declare a dependency to the API:




compile "io.opentelemetry:opentelemetry-api:$openTelemetryVersion"

The minimum required OpenTelemetry version is 1.0.1.

Initialize traceredit

There’s no separate dependency needed for the bridge itself. The Java agent hooks into GlobalOpenTelemetry to return it’s own implementation of OpenTelemetry that is connected to the internal tracer of the agent.

import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.trace.Tracer;

OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
Tracer tracer = openTelemetry.getTracer("");

To disable that behavior, and to rely on the standard discovery mechanism of GlobalOpenTelemetry, you can set disable_instrumentations to opentelemetry.

Add custom metadata to a spanedit

If you like the spans created by the Elastic APM Java agent’s auto-instrumentation, but you want to add a custom label, you can use the OpenTelemetry API to get ahold of the current span and call setAttribute:

Span.current().setAttribute("foo", "bar");

Customize span tracingedit

We utilize the setAttribute() API not only to add custom metadata, but also as a way to customize some special tracing features through corresponding custom attributes listed below. Such attributes are not added to span metadata. For example:

Span.current().setAttribute("co.elastic.discardable", false);

By default, spans may be discarded, for example if span_min_duration ( [1.16.0] Added in 1.16.0. ) is set and the span does not exceed the configured threshold. Use this attribute to make a span non-discardable by setting it to false.

making a span non-discardable implicitly makes the entire stack of active spans non-discardable as well. Child spans can still be discarded.

Key Value type Default




Create a child of the active spanedit

This is an example for adding a custom span to the span created by the Java agent’s auto-instrumentation.

// if there's an active span, it will implicitly be the parent
// in case there's no parent, the custom span will become a Elastic APM transaction
Span custom = tracer.spanBuilder("my custom span").startSpan();
// making your child the current one makes the Java agent aware of this span
// if the agent creates spans in the context of myTracedMethod() (such as outgoing requests),
// they'll be added as a child of your custom span
try (Scope scope = custom.makeCurrent()) {
} catch (Exception e) {
    throw e;
} finally {

To learn more about the OpenTelemetry API, head over do their documentation.


Not all features of the OpenTelemetry API are supported.


This bridge only supports the tracing API. The Metrics API is currently not supported.

In process context propagationedit

Entries that are added to the current context, Context.current().with(...).makeCurrent() cannot be retrieved via Context.current().get(...).

Span Referencesedit

The SpanBuilder#addLink method is currently not supported. Spans can only have a single parent (SpanBuilder#setParent)


Propagating baggage within or outside the process is not supported. Baggage items are silently dropped.


Events are silently dropped, for example Span.current().addEvent("my event").