Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/reference/setup/opentelemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ esClient.close();

## Capturing the {{es}} cluster name [opentelemetry-cluster-name]

The built-in instrumentation automatically captures the `db.elasticsearch.cluster.name` span attribute whenever {{es}} includes the cluster name in its response headers. No client-side configuration is required.
The built-in instrumentation automatically captures the `db.namespace` span attribute whenever {{es}} includes the cluster name in its response headers. No client-side configuration is required.

The source of the cluster name depends on your deployment:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@

import static io.opentelemetry.semconv.DbAttributes.DB_OPERATION_NAME;
import static io.opentelemetry.semconv.DbAttributes.DB_QUERY_TEXT;
import static io.opentelemetry.semconv.DbAttributes.DB_NAMESPACE;
import static io.opentelemetry.semconv.DbAttributes.DB_SYSTEM_NAME;
import static io.opentelemetry.semconv.HttpAttributes.HTTP_REQUEST_METHOD;
import static io.opentelemetry.semconv.HttpAttributes.HTTP_RESPONSE_STATUS_CODE;
Expand Down Expand Up @@ -107,10 +108,6 @@ public class OpenTelemetryForElasticsearch implements Instrumentation {
* {@code http.headers.cluster_name.enabled} is set; carries the configured {@code cluster.name}.
*/
private static final String ONPREM_CLUSTER_HEADER = "Elastic-Cluster-Name";

private static final AttributeKey<String> DB_ES_CLUSTER_NAME =
AttributeKey.stringKey("db.elasticsearch.cluster.name");

private final Tracer tracer;
private final boolean captureSearchBody;

Expand Down Expand Up @@ -257,7 +254,7 @@ public void afterReceivingHttpResponse(TransportHttpClient.Response httpResponse
span.setAttribute(SERVER_ADDRESS, uri.getHost());
span.setAttribute(HTTP_RESPONSE_STATUS_CODE, httpResponse.statusCode());

// Record the cluster identity as db.elasticsearch.cluster.name from the response headers —
// Record the cluster identity as db.namespace from the response headers —
// address-independent, so it survives load balancers, proxies and node lists. A deployment
// normally sends only one of these headers (Elastic Cloud sends X-Found-Handling-Cluster;
// self-managed sends Elastic-Cluster-Name when enabled). If both are present, the Cloud
Expand All @@ -267,7 +264,7 @@ public void afterReceivingHttpResponse(TransportHttpClient.Response httpResponse
clusterName = httpResponse.header(ONPREM_CLUSTER_HEADER);
}
if (clusterName != null && !clusterName.isEmpty()) {
span.setAttribute(DB_ES_CLUSTER_NAME, clusterName);
span.setAttribute(DB_NAMESPACE, clusterName);
}
}
} catch (RuntimeException e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,12 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import static io.opentelemetry.semconv.DbAttributes.DB_NAMESPACE;
import static io.opentelemetry.semconv.ServiceAttributes.SERVICE_NAME;

public class OpenTelemetryForElasticsearchTest {
private static final String INDEX = "test-index";
private static final String DOC_ID = "1234567";
private static final AttributeKey<String> DB_ES_CLUSTER_NAME =
AttributeKey.stringKey("db.elasticsearch.cluster.name");
private static final String CLOUD_CLUSTER_HEADER = "X-Found-Handling-Cluster";
private static final String ONPREM_CLUSTER_HEADER = "Elastic-Cluster-Name";
private static final String DOC_RESPONSE = "{\n" +
Expand Down Expand Up @@ -266,47 +265,47 @@ public void testClusterNameFromCloudHeader() throws IOException {
// Elastic Cloud proxy header present -> captured directly.
client.get(r -> r.index("cluster-cloud").id(DOC_ID), Object.class);
SpanData span = spanExporter.getSpans().get(0);
Assertions.assertEquals("cloud-cluster-01", span.getAttributes().get(DB_ES_CLUSTER_NAME));
Assertions.assertEquals("cloud-cluster-01", span.getAttributes().get(DB_NAMESPACE));
}

@Test
public void testClusterNameFromOnPremHeader() throws IOException {
// Self-managed header present (no cloud header) -> captured as fallback.
client.get(r -> r.index("cluster-onprem").id(DOC_ID), Object.class);
SpanData span = spanExporter.getSpans().get(0);
Assertions.assertEquals("onprem-cluster-01", span.getAttributes().get(DB_ES_CLUSTER_NAME));
Assertions.assertEquals("onprem-cluster-01", span.getAttributes().get(DB_NAMESPACE));
}

@Test
public void testClusterNamePrefersCloudHeaderWhenBothPresent() throws IOException {
// Both headers present -> the cloud header wins (canonical, globally-unique id).
client.get(r -> r.index("cluster-both").id(DOC_ID), Object.class);
SpanData span = spanExporter.getSpans().get(0);
Assertions.assertEquals("cloud-cluster-01", span.getAttributes().get(DB_ES_CLUSTER_NAME));
Assertions.assertEquals("cloud-cluster-01", span.getAttributes().get(DB_NAMESPACE));
}

@Test
public void testClusterNameAbsentWhenNoHeader() throws IOException {
// Neither header present -> the attribute is not stamped.
client.get(r -> r.index("cluster-none").id(DOC_ID), Object.class);
SpanData span = spanExporter.getSpans().get(0);
Assertions.assertNull(span.getAttributes().get(DB_ES_CLUSTER_NAME));
Assertions.assertNull(span.getAttributes().get(DB_NAMESPACE));
}

@Test
public void testClusterNameFallsBackToOnPremWhenCloudHeaderEmpty() throws IOException {
// Empty cloud header is treated as absent -> fall back to the on-prem header.
client.get(r -> r.index("cluster-empty-cloud").id(DOC_ID), Object.class);
SpanData span = spanExporter.getSpans().get(0);
Assertions.assertEquals("onprem-cluster-01", span.getAttributes().get(DB_ES_CLUSTER_NAME));
Assertions.assertEquals("onprem-cluster-01", span.getAttributes().get(DB_NAMESPACE));
}

@Test
public void testClusterNameAbsentWhenHeadersEmpty() throws IOException {
// Both headers present but empty -> the attribute is not stamped.
client.get(r -> r.index("cluster-empty-both").id(DOC_ID), Object.class);
SpanData span = spanExporter.getSpans().get(0);
Assertions.assertNull(span.getAttributes().get(DB_ES_CLUSTER_NAME));
Assertions.assertNull(span.getAttributes().get(DB_NAMESPACE));
}

private static class MockSpanExporter implements SpanExporter {
Expand Down
Loading