Skip to content

Commit 05942ea

Browse files
committed
Added ability to extract span attributes from falcon request objects.
OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS env var can be set to a command separated list of attributes names that will be extracted from Falcon's request object and set as attributes on spans.
1 parent 2b46d11 commit 05942ea

File tree

4 files changed

+62
-6
lines changed

4 files changed

+62
-6
lines changed

Diff for: instrumentation/opentelemetry-instrumentation-falcon/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Unreleased
44

5+
- Added support for `OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS` ([#1158](https://github.com/open-telemetry/opentelemetry-python/pull/1158))
6+
57
## Version 0.13b0
68

79
Released 2020-09-17

Diff for: instrumentation/opentelemetry-instrumentation-falcon/README.rst

+15
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,21 @@ For example,
3131

3232
will exclude requests such as ``https://site/client/123/info`` and ``https://site/xyz/healthcheck``.
3333

34+
Request attributes
35+
********************
36+
To extract certain attributes from Falcon's request object and use them as span attributes, set the environment variable ``OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS`` to a comma
37+
delimited list of request attribute names.
38+
39+
For example,
40+
41+
::
42+
43+
export OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS='query_string,uri_template'
44+
45+
will extract path_info and content_type attributes from every traced request and add them as span attritbues.
46+
47+
Falcon Request object reference: https://falcon.readthedocs.io/en/stable/api/request_and_response.html#id1
48+
3449
References
3550
----------
3651

Diff for: instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py

+26-6
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ def on_get(self, req, resp):
5050

5151
import opentelemetry.instrumentation.wsgi as otel_wsgi
5252
from opentelemetry import configuration, context, propagators, trace
53+
from opentelemetry.configuration import Configuration
5354
from opentelemetry.instrumentation.falcon.version import __version__
5455
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
5556
from opentelemetry.instrumentation.utils import http_status_to_canonical_code
@@ -92,13 +93,16 @@ def _uninstrument(self, **kwargs):
9293

9394
class _InstrumentedFalconAPI(falcon.API):
9495
def __init__(self, *args, **kwargs):
95-
mw = kwargs.pop("middleware", [])
96-
if not isinstance(mw, (list, tuple)):
97-
mw = [mw]
96+
middlewares = kwargs.pop("middleware", [])
97+
if not isinstance(middlewares, (list, tuple)):
98+
middlewares = [middlewares]
9899

99100
self._tracer = trace.get_tracer(__name__, __version__)
100-
mw.insert(0, _TraceMiddleware(self._tracer))
101-
kwargs["middleware"] = mw
101+
trace_middleware = _TraceMiddleware(
102+
self._tracer, kwargs.get("traced_request_attributes")
103+
)
104+
middlewares.insert(0, trace_middleware)
105+
kwargs["middleware"] = middlewares
102106
super().__init__(*args, **kwargs)
103107

104108
def __call__(self, env, start_response):
@@ -144,8 +148,24 @@ def _start_response(status, response_headers, *args, **kwargs):
144148
class _TraceMiddleware:
145149
# pylint:disable=R0201,W0613
146150

147-
def __init__(self, tracer=None):
151+
def __init__(self, tracer=None, traced_request_attrs=None):
148152
self.tracer = tracer
153+
self._traced_request_attrs = traced_request_attrs or [
154+
attr.strip()
155+
for attr in (
156+
Configuration().FALCON_TRACED_REQUEST_ATTRS or ""
157+
).split(",")
158+
]
159+
160+
def process_request(self, req, resp):
161+
span = req.env.get(_ENVIRON_SPAN_KEY)
162+
if not span:
163+
return
164+
165+
for attr in self._traced_request_attrs:
166+
value = getattr(req, attr, None)
167+
if value is not None:
168+
span.set_attribute(attr, str(value))
149169

150170
def process_resource(self, req, resp, resource, params):
151171
span = req.env.get(_ENVIRON_SPAN_KEY)

Diff for: instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py

+19
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
import os
1516
from unittest.mock import patch
1617

1718
from falcon import testing
1819

20+
from opentelemetry.configuration import Configuration
1921
from opentelemetry.instrumentation.falcon import FalconInstrumentor
2022
from opentelemetry.test.test_base import TestBase
2123
from opentelemetry.trace.status import StatusCanonicalCode
@@ -27,6 +29,8 @@
2729
class TestFalconInstrumentation(TestBase):
2830
def setUp(self):
2931
super().setUp()
32+
print("OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS" in os.environ)
33+
Configuration._reset()
3034
FalconInstrumentor().instrument()
3135
self.app = make_app()
3236

@@ -171,3 +175,18 @@ def test_exclude_lists(self):
171175
self.client().simulate_get(path="/hello")
172176
span_list = self.memory_exporter.get_finished_spans()
173177
self.assertEqual(len(span_list), 1)
178+
179+
def test_traced_request_attributes(self):
180+
self.client().simulate_get(path="/hello?q=abc")
181+
span = self.memory_exporter.get_finished_spans()[0]
182+
self.assertNotIn("query_string", span.attributes)
183+
self.memory_exporter.clear()
184+
185+
middleware = self.app._middleware[0][0].__self__
186+
with patch.object(
187+
middleware, "_traced_request_attrs", ["query_string"]
188+
):
189+
self.client().simulate_get(path="/hello?q=abc")
190+
span = self.memory_exporter.get_finished_spans()[0]
191+
self.assertIn("query_string", span.attributes)
192+
self.assertEqual(span.attributes["query_string"], "q=abc")

0 commit comments

Comments
 (0)