Skip to content

Commit 535c2e6

Browse files
authored
Use is_recording flag in jinja, celery, esearch, falcon instrumentations (open-telemetry#1241)
1 parent c782f14 commit 535c2e6

File tree

11 files changed

+160
-67
lines changed

11 files changed

+160
-67
lines changed

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

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,11 @@ def _trace_postrun(*args, **kwargs):
148148
return
149149

150150
# request context tags
151-
span.set_attribute(_TASK_TAG_KEY, _TASK_RUN)
152-
utils.set_attributes_from_context(span, kwargs)
153-
utils.set_attributes_from_context(span, task.request)
154-
span.set_attribute(_TASK_NAME_KEY, task.name)
151+
if span.is_recording():
152+
span.set_attribute(_TASK_TAG_KEY, _TASK_RUN)
153+
utils.set_attributes_from_context(span, kwargs)
154+
utils.set_attributes_from_context(span, task.request)
155+
span.set_attribute(_TASK_NAME_KEY, task.name)
155156

156157
activation.__exit__(None, None, None)
157158
utils.detach_span(task, task_id)
@@ -169,10 +170,11 @@ def _trace_before_publish(self, *args, **kwargs):
169170
)
170171

171172
# apply some attributes here because most of the data is not available
172-
span.set_attribute(_TASK_TAG_KEY, _TASK_APPLY_ASYNC)
173-
span.set_attribute(_MESSAGE_ID_ATTRIBUTE_NAME, task_id)
174-
span.set_attribute(_TASK_NAME_KEY, task.name)
175-
utils.set_attributes_from_context(span, kwargs)
173+
if span.is_recording():
174+
span.set_attribute(_TASK_TAG_KEY, _TASK_APPLY_ASYNC)
175+
span.set_attribute(_MESSAGE_ID_ATTRIBUTE_NAME, task_id)
176+
span.set_attribute(_TASK_NAME_KEY, task.name)
177+
utils.set_attributes_from_context(span, kwargs)
176178

177179
activation = self._tracer.use_span(span, end_on_exit=True)
178180
activation.__enter__()
@@ -209,7 +211,7 @@ def _trace_failure(*args, **kwargs):
209211

210212
# retrieve and pass exception info to activation
211213
span, _ = utils.retrieve_span(task, task_id)
212-
if span is None:
214+
if span is None or not span.is_recording():
213215
return
214216

215217
status_kwargs = {"canonical_code": StatusCanonicalCode.UNKNOWN}
@@ -238,7 +240,7 @@ def _trace_retry(*args, **kwargs):
238240
return
239241

240242
span, _ = utils.retrieve_span(task, task_id)
241-
if span is None:
243+
if span is None or not span.is_recording():
242244
return
243245

244246
# Add retry reason metadata to span

instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
# pylint:disable=too-many-branches
4949
def set_attributes_from_context(span, context):
5050
"""Helper to extract meta values from a Celery Context"""
51+
if not span.is_recording():
52+
return
5153
for key in CELERY_CONTEXT_ATTRIBUTES:
5254
value = context.get(key)
5355

instrumentation/opentelemetry-instrumentation-celery/tests/test_utils.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,30 @@ def test_set_attributes_from_context(self):
6969
)
7070
self.assertNotIn("custom_meta", span.attributes)
7171

72+
def test_set_attributes_not_recording(self):
73+
# it should extract only relevant keys
74+
context = {
75+
"correlation_id": "44b7f305",
76+
"delivery_info": {"eager": True},
77+
"eta": "soon",
78+
"expires": "later",
79+
"hostname": "localhost",
80+
"id": "44b7f305",
81+
"reply_to": "44b7f305",
82+
"retries": 4,
83+
"timelimit": ("now", "later"),
84+
"custom_meta": "custom_value",
85+
"routing_key": "celery",
86+
}
87+
88+
mock_span = mock.Mock()
89+
mock_span.is_recording.return_value = False
90+
utils.set_attributes_from_context(mock_span, context)
91+
self.assertFalse(mock_span.is_recording())
92+
self.assertTrue(mock_span.is_recording.called)
93+
self.assertFalse(mock_span.set_attribute.called)
94+
self.assertFalse(mock_span.set_status.called)
95+
7296
def test_set_attributes_from_context_empty_keys(self):
7397
# it should not extract empty keys
7498
context = {

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

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ def _uninstrument(self, **kwargs):
110110

111111

112112
def _wrap_perform_request(tracer, span_name_prefix):
113+
# pylint: disable=R0912
113114
def wrapper(wrapped, _, args, kwargs):
114115
method = url = None
115116
try:
@@ -125,26 +126,27 @@ def wrapper(wrapped, _, args, kwargs):
125126
params = kwargs.get("params", {})
126127
body = kwargs.get("body", None)
127128

128-
attributes = {
129-
"component": "elasticsearch-py",
130-
"db.type": "elasticsearch",
131-
}
132-
133-
if url:
134-
attributes["elasticsearch.url"] = url
135-
if method:
136-
attributes["elasticsearch.method"] = method
137-
if body:
138-
attributes["db.statement"] = str(body)
139-
if params:
140-
attributes["elasticsearch.params"] = str(params)
141-
142129
with tracer.start_as_current_span(
143-
op_name, kind=SpanKind.CLIENT, attributes=attributes
130+
op_name, kind=SpanKind.CLIENT,
144131
) as span:
132+
if span.is_recording():
133+
attributes = {
134+
"component": "elasticsearch-py",
135+
"db.type": "elasticsearch",
136+
}
137+
if url:
138+
attributes["elasticsearch.url"] = url
139+
if method:
140+
attributes["elasticsearch.method"] = method
141+
if body:
142+
attributes["db.statement"] = str(body)
143+
if params:
144+
attributes["elasticsearch.params"] = str(params)
145+
for key, value in attributes.items():
146+
span.set_attribute(key, value)
145147
try:
146148
rv = wrapped(*args, **kwargs)
147-
if isinstance(rv, dict):
149+
if isinstance(rv, dict) and span.is_recording():
148150
for member in _ATTRIBUTES_FROM_RESULT:
149151
if member in rv:
150152
span.set_attribute(
@@ -153,11 +155,12 @@ def wrapper(wrapped, _, args, kwargs):
153155
)
154156
return rv
155157
except Exception as ex: # pylint: disable=broad-except
156-
if isinstance(ex, elasticsearch.exceptions.NotFoundError):
157-
status = StatusCanonicalCode.NOT_FOUND
158-
else:
159-
status = StatusCanonicalCode.UNKNOWN
160-
span.set_status(Status(status, str(ex)))
158+
if span.is_recording():
159+
if isinstance(ex, elasticsearch.exceptions.NotFoundError):
160+
status = StatusCanonicalCode.NOT_FOUND
161+
else:
162+
status = StatusCanonicalCode.UNKNOWN
163+
span.set_status(Status(status, str(ex)))
161164
raise ex
162165

163166
return wrapper

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,24 @@ def test_instrumentor(self, request_mock):
8888
spans_list = self.get_ordered_finished_spans()
8989
self.assertEqual(len(spans_list), 1)
9090

91+
def test_span_not_recording(self, request_mock):
92+
request_mock.return_value = (1, {}, {})
93+
mock_tracer = mock.Mock()
94+
mock_span = mock.Mock()
95+
mock_span.is_recording.return_value = False
96+
mock_tracer.start_span.return_value = mock_span
97+
mock_tracer.use_span.return_value.__enter__ = mock_span
98+
mock_tracer.use_span.return_value.__exit__ = mock_span
99+
with mock.patch("opentelemetry.trace.get_tracer") as tracer:
100+
tracer.return_value = mock_tracer
101+
Elasticsearch()
102+
self.assertFalse(mock_span.is_recording())
103+
self.assertTrue(mock_span.is_recording.called)
104+
self.assertFalse(mock_span.set_attribute.called)
105+
self.assertFalse(mock_span.set_status.called)
106+
107+
ElasticsearchInstrumentor().uninstrument()
108+
91109
def test_prefix_arg(self, request_mock):
92110
prefix = "prefix-from-env"
93111
ElasticsearchInstrumentor().uninstrument()

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

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,13 +117,16 @@ def __call__(self, env, start_response):
117117
token = context.attach(
118118
propagators.extract(otel_wsgi.get_header_from_environ, env)
119119
)
120-
attributes = otel_wsgi.collect_request_attributes(env)
121120
span = self._tracer.start_span(
122121
otel_wsgi.get_default_span_name(env),
123122
kind=trace.SpanKind.SERVER,
124-
attributes=attributes,
125123
start_time=start_time,
126124
)
125+
if span.is_recording():
126+
attributes = otel_wsgi.collect_request_attributes(env)
127+
for key, value in attributes.items():
128+
span.set_attribute(key, value)
129+
127130
activation = self._tracer.use_span(span, end_on_exit=True)
128131
activation.__enter__()
129132
env[_ENVIRON_SPAN_KEY] = span
@@ -162,7 +165,7 @@ def __init__(self, tracer=None, traced_request_attrs=None):
162165

163166
def process_request(self, req, resp):
164167
span = req.env.get(_ENVIRON_SPAN_KEY)
165-
if not span:
168+
if not span or not span.is_recording():
166169
return
167170

168171
attributes = extract_attributes_from_object(
@@ -173,7 +176,7 @@ def process_request(self, req, resp):
173176

174177
def process_resource(self, req, resp, resource, params):
175178
span = req.env.get(_ENVIRON_SPAN_KEY)
176-
if not span:
179+
if not span or not span.is_recording():
177180
return
178181

179182
resource_name = resource.__class__.__name__
@@ -186,7 +189,7 @@ def process_response(
186189
self, req, resp, resource, req_succeeded=None
187190
): # pylint:disable=R0201
188191
span = req.env.get(_ENVIRON_SPAN_KEY)
189-
if not span:
192+
if not span or not span.is_recording():
190193
return
191194

192195
status = resp.status

instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
from unittest.mock import patch
15+
from unittest.mock import Mock, patch
1616

1717
from falcon import testing
1818

@@ -188,3 +188,18 @@ def test_traced_request_attributes(self):
188188
span = self.memory_exporter.get_finished_spans()[0]
189189
self.assertIn("query_string", span.attributes)
190190
self.assertEqual(span.attributes["query_string"], "q=abc")
191+
192+
def test_traced_not_recording(self):
193+
mock_tracer = Mock()
194+
mock_span = Mock()
195+
mock_span.is_recording.return_value = False
196+
mock_tracer.start_span.return_value = mock_span
197+
mock_tracer.use_span.return_value.__enter__ = mock_span
198+
mock_tracer.use_span.return_value.__exit__ = mock_span
199+
with patch("opentelemetry.trace.get_tracer") as tracer:
200+
tracer.return_value = mock_tracer
201+
self.client().simulate_get(path="/hello?q=abc")
202+
self.assertFalse(mock_span.is_recording())
203+
self.assertTrue(mock_span.is_recording.called)
204+
self.assertFalse(mock_span.set_attribute.called)
205+
self.assertFalse(mock_span.set_status.called)

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

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -78,39 +78,44 @@ def wrapper(wrapped, instance, args, kwargs):
7878
def _wrap_render(tracer, wrapped, instance, args, kwargs):
7979
"""Wrap `Template.render()` or `Template.generate()`
8080
"""
81-
template_name = instance.name or DEFAULT_TEMPLATE_NAME
82-
attributes = {ATTRIBUTE_JINJA2_TEMPLATE_NAME: template_name}
8381
with tracer.start_as_current_span(
84-
"jinja2.render", kind=SpanKind.INTERNAL, attributes=attributes
85-
):
82+
"jinja2.render", kind=SpanKind.INTERNAL,
83+
) as span:
84+
if span.is_recording():
85+
template_name = instance.name or DEFAULT_TEMPLATE_NAME
86+
span.set_attribute(ATTRIBUTE_JINJA2_TEMPLATE_NAME, template_name)
8687
return wrapped(*args, **kwargs)
8788

8889

8990
@_with_tracer_wrapper
9091
def _wrap_compile(tracer, wrapped, _, args, kwargs):
91-
template_name = (
92-
args[1] if len(args) > 1 else kwargs.get("name", DEFAULT_TEMPLATE_NAME)
93-
)
94-
attributes = {ATTRIBUTE_JINJA2_TEMPLATE_NAME: template_name}
9592
with tracer.start_as_current_span(
96-
"jinja2.compile", kind=SpanKind.INTERNAL, attributes=attributes
97-
):
93+
"jinja2.compile", kind=SpanKind.INTERNAL,
94+
) as span:
95+
if span.is_recording():
96+
template_name = (
97+
args[1]
98+
if len(args) > 1
99+
else kwargs.get("name", DEFAULT_TEMPLATE_NAME)
100+
)
101+
span.set_attribute(ATTRIBUTE_JINJA2_TEMPLATE_NAME, template_name)
98102
return wrapped(*args, **kwargs)
99103

100104

101105
@_with_tracer_wrapper
102106
def _wrap_load_template(tracer, wrapped, _, args, kwargs):
103-
template_name = kwargs.get("name", args[0])
104-
attributes = {ATTRIBUTE_JINJA2_TEMPLATE_NAME: template_name}
105107
with tracer.start_as_current_span(
106-
"jinja2.load", kind=SpanKind.INTERNAL, attributes=attributes
108+
"jinja2.load", kind=SpanKind.INTERNAL,
107109
) as span:
110+
if span.is_recording():
111+
template_name = kwargs.get("name", args[0])
112+
span.set_attribute(ATTRIBUTE_JINJA2_TEMPLATE_NAME, template_name)
108113
template = None
109114
try:
110115
template = wrapped(*args, **kwargs)
111116
return template
112117
finally:
113-
if template:
118+
if template and span.is_recording():
114119
span.set_attribute(
115120
ATTRIBUTE_JINJA2_TEMPLATE_PATH, template.filename
116121
)

instrumentation/opentelemetry-instrumentation-jinja2/tests/test_jinja2.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414

1515
import os
16+
from unittest import mock
1617

1718
import jinja2
1819

@@ -53,6 +54,21 @@ def test_render_inline_template_with_root(self):
5354
self.assertIs(template.parent, root.get_span_context())
5455
self.assertIsNone(root.parent)
5556

57+
def test_render_not_recording(self):
58+
mock_tracer = mock.Mock()
59+
mock_span = mock.Mock()
60+
mock_span.is_recording.return_value = False
61+
mock_tracer.start_span.return_value = mock_span
62+
mock_tracer.use_span.return_value.__enter__ = mock_span
63+
mock_tracer.use_span.return_value.__exit__ = mock_span
64+
with mock.patch("opentelemetry.trace.get_tracer") as tracer:
65+
tracer.return_value = mock_tracer
66+
jinja2.environment.Template("Hello {{name}}!")
67+
self.assertFalse(mock_span.is_recording())
68+
self.assertTrue(mock_span.is_recording.called)
69+
self.assertFalse(mock_span.set_attribute.called)
70+
self.assertFalse(mock_span.set_status.called)
71+
5672
def test_render_inline_template(self):
5773
template = jinja2.environment.Template("Hello {{name}}!")
5874
self.assertEqual(template.render(name="Jinja"), "Hello Jinja!")

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,15 +118,16 @@ def _wrap_cmd(tracer, cmd, wrapped, instance, args, kwargs):
118118
_CMD, kind=SpanKind.INTERNAL, attributes={}
119119
) as span:
120120
try:
121-
if not args:
122-
vals = ""
123-
else:
124-
vals = _get_query_string(args[0])
121+
if span.is_recording():
122+
if not args:
123+
vals = ""
124+
else:
125+
vals = _get_query_string(args[0])
125126

126-
query = "{}{}{}".format(cmd, " " if vals else "", vals)
127-
span.set_attribute(_RAWCMD, query)
127+
query = "{}{}{}".format(cmd, " " if vals else "", vals)
128+
span.set_attribute(_RAWCMD, query)
128129

129-
_set_connection_attributes(span, instance)
130+
_set_connection_attributes(span, instance)
130131
except Exception as ex: # pylint: disable=broad-except
131132
logger.warning(
132133
"Failed to set attributes for pymemcache span %s", str(ex)

0 commit comments

Comments
 (0)