Skip to content

Commit fefb005

Browse files
committed
elasticsearch: don't produce spans if native elasticsearch support is enabled
If elasticsearch is found with native OTel support enabled just call the wrapped function without creating our own spans. Fix #2393
1 parent 460fc33 commit fefb005

File tree

4 files changed

+56
-7
lines changed

4 files changed

+56
-7
lines changed

instrumentation/opentelemetry-instrumentation-elasticsearch/src/opentelemetry/instrumentation/elasticsearch/__init__.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,14 @@
1616
This library allows tracing HTTP elasticsearch made by the
1717
`elasticsearch <https://elasticsearch-py.readthedocs.io/en/master/>`_ library.
1818
19+
.. warning::
20+
The elasticsearch package got native OpenTelemetry supportSince version
21+
`8.13 <https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/release-notes.html#rn-8-13-0>`_.
22+
To avoid duplicated tracing this instrumentation disables itself if it founds an elasticsearch client
23+
that has OpenTelemetry support enabled.
24+
25+
Please be aware that the two libraries may use a different semantic convention.
26+
1927
Usage
2028
-----
2129
@@ -54,7 +62,7 @@ def response_hook(span: Span, response: dict)
5462
5563
for example:
5664
57-
.. code: python
65+
.. code-block: python
5866
5967
from opentelemetry.instrumentation.elasticsearch import ElasticsearchInstrumentor
6068
import elasticsearch
@@ -197,6 +205,11 @@ def _wrap_perform_request(
197205
):
198206
# pylint: disable=R0912,R0914
199207
def wrapper(wrapped, _, args, kwargs):
208+
# if wrapped elasticsearch has native OTel instrumentation just call the wrapped function
209+
otel_span = kwargs.get("otel_span")
210+
if otel_span and otel_span.otel_span:
211+
return wrapped(*args, **kwargs)
212+
200213
method = url = None
201214
try:
202215
method, url, *_ = args
@@ -249,6 +262,11 @@ def normalize_kwargs(k, v):
249262
v = str(v)
250263
elif isinstance(v, elastic_transport.HttpHeaders):
251264
v = dict(v)
265+
elif isinstance(
266+
v, elastic_transport.OpenTelemetrySpan
267+
):
268+
# the transport Span is always a dummy one
269+
v = None
252270
return (k, v)
253271

254272
hook_kwargs = dict(

instrumentation/opentelemetry-instrumentation-elasticsearch/test-requirements-2.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
asgiref==3.7.2
22
attrs==23.2.0
33
Deprecated==1.2.14
4-
elasticsearch==8.12.1
5-
elasticsearch-dsl==8.12.0
6-
elastic-transport==8.12.0
4+
elasticsearch==8.13.1
5+
elasticsearch-dsl==8.13.1
6+
elastic-transport==8.13.0
77
importlib-metadata==6.11.0
88
iniconfig==2.0.0
99
packaging==23.2

instrumentation/opentelemetry-instrumentation-elasticsearch/tests/test_elasticsearch.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import elasticsearch.exceptions
2424
from elasticsearch import Elasticsearch
2525
from elasticsearch_dsl import Search
26+
from pytest import mark
2627

2728
import opentelemetry.instrumentation.elasticsearch
2829
from opentelemetry import trace
@@ -36,7 +37,7 @@
3637

3738
from . import sanitization_queries # pylint: disable=no-name-in-module
3839

39-
major_version = elasticsearch.VERSION[0]
40+
major_version, minor_version = elasticsearch.VERSION[:2]
4041

4142
if major_version == 8:
4243
from . import helpers_es8 as helpers # pylint: disable=no-name-in-module
@@ -70,6 +71,9 @@ def get_elasticsearch_client(*args, **kwargs):
7071

7172

7273
@mock.patch(helpers.perform_request_mock_path)
74+
@mock.patch.dict(
75+
os.environ, {"OTEL_PYTHON_INSTRUMENTATION_ELASTICSEARCH_ENABLED": "false"}
76+
)
7377
class TestElasticsearchIntegration(TestBase):
7478
search_attributes = {
7579
SpanAttributes.DB_SYSTEM: "elasticsearch",
@@ -110,7 +114,6 @@ def test_instrumentor(self, request_mock):
110114
span = spans_list[0]
111115

112116
# Check version and name in span's instrumentation info
113-
# self.assertEqualSpanInstrumentationInfo(span, opentelemetry.instrumentation.elasticsearch)
114117
self.assertEqualSpanInstrumentationInfo(
115118
span, opentelemetry.instrumentation.elasticsearch
116119
)
@@ -475,6 +478,7 @@ def request_hook(span, method, url, kwargs):
475478
"headers": {
476479
"accept": "application/vnd.elasticsearch+json; compatible-with=8"
477480
},
481+
"otel_span": None,
478482
}
479483
elif major_version == 7:
480484
expected_kwargs = {
@@ -607,3 +611,30 @@ def test_bulk(self, request_mock):
607611
self.assertEqualSpanInstrumentationInfo(
608612
span, opentelemetry.instrumentation.elasticsearch
609613
)
614+
615+
@mark.skipif(
616+
(major_version, minor_version) < (8, 13),
617+
reason="Native OTel since elasticsearch 8.13",
618+
)
619+
@mock.patch.dict(
620+
os.environ,
621+
{"OTEL_PYTHON_INSTRUMENTATION_ELASTICSEARCH_ENABLED": "true"},
622+
)
623+
def test_instrumentation_is_disabled_if_native_support_enabled(
624+
self, request_mock
625+
):
626+
request_mock.return_value = helpers.mock_response("{}")
627+
628+
es = get_elasticsearch_client(hosts=["http://localhost:9200"])
629+
es.index(
630+
index="sw",
631+
id=1,
632+
**normalize_arguments(body={"name": "adam"}, doc_type="_doc"),
633+
)
634+
635+
spans_list = self.get_finished_spans()
636+
self.assertEqual(len(spans_list), 1)
637+
span = spans_list[0]
638+
639+
# Check that name in span's instrumentation info is not from this instrumentation
640+
self.assertEqual(span.instrumentation_info.name, "elasticsearch-api")

tox.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ envlist =
9292
; below mean these dependencies are being used:
9393
; 0: elasticsearch-dsl==6.4.0 elasticsearch==6.8.2
9494
; 1: elasticsearch-dsl==7.4.1 elasticsearch==7.17.9
95-
; 2: elasticsearch-dsl>=8.0,<8.13 elasticsearch>=8.0,<8.13
95+
; 2: elasticsearch-dsl==8.13.1 elasticsearch==8.13.1
9696
py3{8,9,10,11}-test-instrumentation-elasticsearch-{0,1,2}
9797
pypy3-test-instrumentation-elasticsearch-{0,1,2}
9898
lint-instrumentation-elasticsearch

0 commit comments

Comments
 (0)