Skip to content

Commit 4c15f79

Browse files
authored
Support Elastic Cloud OpenTelemetry attributes (elastic#154)
1 parent a13bf6b commit 4c15f79

File tree

4 files changed

+43
-14
lines changed

4 files changed

+43
-14
lines changed

elastic_transport/_async_transport.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,8 @@ async def perform_request( # type: ignore[override]
220220
method,
221221
endpoint_id=resolve_default(endpoint_id, None),
222222
path_parts=resolve_default(path_parts, {}),
223-
):
224-
return await self._perform_request(
223+
) as span:
224+
response = await self._perform_request(
225225
method,
226226
target,
227227
body=body,
@@ -232,6 +232,8 @@ async def perform_request( # type: ignore[override]
232232
request_timeout=request_timeout,
233233
client_meta=client_meta,
234234
)
235+
span.set_elastic_cloud_metadata(response.meta.headers)
236+
return response
235237

236238
async def _perform_request( # type: ignore[override,return]
237239
self,

elastic_transport/_otel.py

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
try:
2525
from opentelemetry import trace
26+
from opentelemetry.trace import Span
2627

2728
_tracer: trace.Tracer | None = trace.get_tracer("elastic-transport")
2829
except ModuleNotFoundError:
@@ -32,6 +33,23 @@
3233
ENABLED_ENV_VAR = "OTEL_PYTHON_INSTRUMENTATION_ELASTICSEARCH_ENABLED"
3334

3435

36+
class OpenTelemetrySpan:
37+
def __init__(self, otel_span: Optional[Span]):
38+
self.otel_span = otel_span
39+
40+
def set_attribute(self, key: str, value: str) -> None:
41+
if self.otel_span is not None:
42+
self.otel_span.set_attribute(key, value)
43+
44+
def set_elastic_cloud_metadata(self, headers: Mapping[str, str]) -> None:
45+
cluster_name = headers.get("X-Found-Handling-Cluster")
46+
if cluster_name is not None:
47+
self.set_attribute("db.elasticsearch.cluster.name", cluster_name)
48+
node_name = headers.get("X-Found-Handling-Instance")
49+
if node_name is not None:
50+
self.set_attribute("db.elasticsearch.node.name", node_name)
51+
52+
3553
class OpenTelemetry:
3654
def __init__(self, enabled: bool | None = None, tracer: trace.Tracer | None = None):
3755
if enabled is None:
@@ -46,17 +64,17 @@ def span(
4664
*,
4765
endpoint_id: Optional[str],
4866
path_parts: Mapping[str, str],
49-
) -> Generator[None, None, None]:
67+
) -> Generator[OpenTelemetrySpan, None, None]:
5068
if not self.enabled or self.tracer is None:
51-
yield
69+
yield OpenTelemetrySpan(None)
5270
return
5371

5472
span_name = endpoint_id or method
55-
with self.tracer.start_as_current_span(span_name) as span:
56-
span.set_attribute("http.request.method", method)
57-
span.set_attribute("db.system", "elasticsearch")
73+
with self.tracer.start_as_current_span(span_name) as otel_span:
74+
otel_span.set_attribute("http.request.method", method)
75+
otel_span.set_attribute("db.system", "elasticsearch")
5876
if endpoint_id is not None:
59-
span.set_attribute("db.operation", endpoint_id)
77+
otel_span.set_attribute("db.operation", endpoint_id)
6078
for key, value in path_parts.items():
61-
span.set_attribute(f"db.elasticsearch.path_parts.{key}", value)
62-
yield
79+
otel_span.set_attribute(f"db.elasticsearch.path_parts.{key}", value)
80+
yield OpenTelemetrySpan(otel_span)

elastic_transport/_transport.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,8 +303,8 @@ def perform_request(
303303
method,
304304
endpoint_id=resolve_default(endpoint_id, None),
305305
path_parts=resolve_default(path_parts, {}),
306-
):
307-
return self._perform_request(
306+
) as span:
307+
api_response = self._perform_request(
308308
method,
309309
target,
310310
body=body,
@@ -315,6 +315,8 @@ def perform_request(
315315
request_timeout=request_timeout,
316316
client_meta=client_meta,
317317
)
318+
span.set_elastic_cloud_metadata(api_response.meta.headers)
319+
return api_response
318320

319321
def _perform_request( # type: ignore[return]
320322
self,

tests/test_otel.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,13 @@ def test_detailed_span():
5353
otel = OpenTelemetry(enabled=True, tracer=tracer)
5454
with otel.span(
5555
"GET", endpoint_id="ml.close_job", path_parts={"job_id": "my-job", "foo": "bar"}
56-
):
57-
pass
56+
) as span:
57+
span.set_elastic_cloud_metadata(
58+
{
59+
"X-Found-Handling-Cluster": "e9106fc68e3044f0b1475b04bf4ffd5f",
60+
"X-Found-Handling-Instance": "instance-0000000001",
61+
}
62+
)
5863

5964
spans = memory_exporter.get_finished_spans()
6065
assert len(spans) == 1
@@ -65,4 +70,6 @@ def test_detailed_span():
6570
"db.operation": "ml.close_job",
6671
"db.elasticsearch.path_parts.job_id": "my-job",
6772
"db.elasticsearch.path_parts.foo": "bar",
73+
"db.elasticsearch.cluster.name": "e9106fc68e3044f0b1475b04bf4ffd5f",
74+
"db.elasticsearch.node.name": "instance-0000000001",
6875
}

0 commit comments

Comments
 (0)