Repository navigation
Description
Is your feature request related to a problem? Please describe.
When you monitor a self-managed Elasticsearch cluster with OpenTelemetry, the tracing spans and the metrics don't currently meet in the middle, and there's no clean way to join them without manual work.
The two signals identify the cluster differently:
- The Collector's
elasticsearchreceiveridentifies a cluster byelasticsearch.cluster.name, because that's what Elasticsearch's monitoring APIs report. - The Java agent produces spans for the operations the application actually performs —
_search,_bulk, and so on. Elasticsearch doesn't return the cluster name in the responses to those operations, so the cluster name never lands on the span.
The result is that the metric has a cluster name and the span doesn't, so there's no shared key to correlate a service's calls with the cluster they hit.
This looks structural rather than a misconfiguration:
- The agent is passive by design — it records the operations the application makes and doesn't issue its own calls to learn about the cluster. That's the correct default, but it means the cluster identity is only ever observed if the application itself happens to query the cluster's info endpoints, which most applications never do.
- Managed/hosted Elasticsearch carries extra metadata on its responses that lets the cluster name reach the span, so hosted users may never see this gap. Self-managed deployments get none of that, and self-managed is likely a large share of real-world usage.
Anyone running self-managed Elasticsearch who wants to see which service is calling which cluster, or to scope dashboards and alerts per cluster, runs into this.
Describe the solution you'd like
I would like to propose introducing opt-in active discovery for the Elasticsearch instrumentation.
Specifically, an explicit, off-by-default configuration flag (e.g., otel.instrumentation.elasticsearch.experimental.active-discovery=true) that permits the Java agent to make a single, lightweight HTTP GET / call at client initialization to retrieve the cluster_name.
The execution flow would look like this:
- Trigger: The application initializes an Elasticsearch client (e.g.,
RestClientorElasticsearchClient). - Discovery Call: If the opt-in flag is enabled, the instrumentation intercepts the initialization and makes a single
GET /request against the configured Elasticsearch endpoint. - Extraction: The agent parses the standard JSON response to extract the
cluster_namefield. - Caching & Injection: The agent caches this string in memory, tethered to that specific client instance. For all subsequent data-plane operations (
_search,_bulk, etc.) made by that client, the agent automatically injectselasticsearch.cluster.name(ordb.elasticsearch.cluster.name) into the span attributes.
Because it is strictly opt-in, it respects the default passive nature of the agent for standard users. For users running self-managed clusters who enable it, it guarantees the cluster identity is present on every span, matching the Collector's metrics perfectly.
Describe alternatives you've considered
- Picking the cluster name up passively from a normal request. In principle, the name could reach a span if the application queried the cluster's root endpoint. In practice, applications mostly just search and write, so if the app never asks the cluster about itself, there is nothing to observe.
- Correlating on network identity (
server.address) in the OpenTelemetry Collector. It is conceptually possible to use a transform to align the metric's host/port with the span'sserver.address. However, this breaks as soon as there is a load balancer, proxy, or container network in the path, which describes most production setups. The IPs and hostnames simply won't match on both sides. - Manual logical tagging (e.g.,
db.namespace). We could manually inject a hardcoded tag via environment variables on both the APM side and the Collector side to force a match. However, APM and Infrastructure are usually managed by entirely different teams. Forcing siloed teams to coordinate and maintain hardcoded strings across their respective configurations is organizational friction and defeats the purpose of "out-of-the-box" observability.
Additional context
This is a case where the passive-instrumentation model and the way a backend's identity is exposed don't line up, and the correlation between an OTel APM agent and the matching OTel Collector receiver quietly degrades for self-managed users. I'd appreciate the maintainers' read on introducing an opt-in GET / initialization call to bridge this gap.
Tip
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.
Metadata
Metadata
Assignees
Labels
Type
Fields
Priority
Activity
laurit commentedon Aug 4, 2026
co.elastic.clients:elasticsearch-java contains built-in instrumentations since 8.10 so for
ElasticsearchClientyou may need to take this up with elastic folks cc @AlexanderWert.trask commentedon Aug 4, 2026
cc @jackshirazi @SylvainJuge @JonasKunz
vagrawal-newrelic commentedon Aug 4, 2026
Thanks @laurit and @trask (and @jackshirazi @SylvainJuge @JonasKunz for taking a look)
I've opened the same request against that repo, with the specifics of what's
currently captured vs. missing: (elastic/elasticsearch-java#1304)
Leaving this issue open/linked in case there's a piece that still belongs here (e.g.
if any coordination with the OTel semantic-conventions side is needed), but the actual
implementation would live in
elasticsearch-java. Will report back here once there'smovement on that side.
SylvainJuge commentedon Sep 14, 2026
We can now close this as there isn't anything left to do in instrumentation
db.namespacedb.elasticsearch.cluster.namesemconv attributedb.namespaceto align with the current state of semconv.