From 7da21de3f88704669051e5cd94f0aff6c407e3ab Mon Sep 17 00:00:00 2001 From: "anshul.asawa" Date: Fri, 30 Sep 2022 14:25:37 +0530 Subject: [PATCH 01/15] instrument class --- .../instrumentation/falcon/__init__.py | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py index fb0f14a2d5..aa7b237e9d 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py @@ -455,6 +455,46 @@ class FalconInstrumentor(BaseInstrumentor): def instrumentation_dependencies(self) -> Collection[str]: return _instruments + + @staticmethod + def instrument_app( + app, + request_hook=None, + response_hook=None, + tracer_provider=None, + meter_provider=None, + excluded_urls=None, + **kwargs + ): + if not hasattr(app, "_is_instrumented_by_opentelemetry"): + app._is_instrumented_by_opentelemetry = False + + if not getattr(app, "_is_instrumented_by_opentelemetry", False): + app._otel_excluded_urls = excluded_urls if excluded_urls is None else get_excluded_urls("FALCON") + app._otel_tracer = trace.get_tracer(__name__, __version__, tracer_provider) + app._otel_meter = get_meter(__name__, __version__, meter_provider) + app.duration_histogram = self._otel_meter.create_histogram( + name="http.server.duration", + unit="ms", + description="measures the duration of the inbound HTTP request", + ) + app.active_requests_counter = app._otel_meter.create_up_down_counter( + name="http.server.active_requests", + unit="requests", + description="measures the number of concurrent HTTP requests that are currently in-flight", + ) + trace_middleware = _TraceMiddleware( + app._otel_tracer, + kwargs.pop( + "traced_request_attributes", get_traced_request_attrs("FALCON") + ), + request_hook, + response_hook, + ) + # TODO: implement middleware addition logic for falcon 1,2 & 3 + app._is_instrumented_by_opentelemetry = True + app.__class__ = _InstrumentedFalconAPI + # pylint:disable=no-self-use def _remove_instrumented_middleware(self, app): From d01e18e8984e522f7c41da9a642cba05fe75fdbc Mon Sep 17 00:00:00 2001 From: "anshul.asawa" Date: Fri, 30 Sep 2022 22:01:58 +0530 Subject: [PATCH 02/15] add middleware --- .../instrumentation/falcon/__init__.py | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py index aa7b237e9d..87666e1dae 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py @@ -146,6 +146,7 @@ def response_hook(span, req, resp): from time import time_ns from timeit import default_timer from typing import Collection +from xxlimited import new import falcon from packaging import version as package_version @@ -446,6 +447,35 @@ def process_response( self._response_hook(span, req, resp) +def _prepare_middleware_tupple(middleware, trace_middleware, independent_middleware): + request_mw, resource_mw, respose_mw = middleware + + new_request_mw = [] + new_response_mw = [] + new_resource_mw = [] + + process_request = getattr(trace_middleware, 'process_request') + process_resource = getattr(trace_middleware, 'process_resource') + process_response = getattr(trace_middleware, 'process_response') + + if independent_middleware: + new_request_mw.insert(0, process_request) + new_response_mw.append(process_response) + else: + new_request_mw.insert(0, (process_request, process_response)) + + new_resource_mw.insert(0, process_resource) + + for each in request_mw: + new_request_mw.append(each) + for each in respose_mw: + new_response_mw.append(each) + for each in resource_mw: + new_resource_mw.append(each) + + return (tuple(new_request_mw), tuple(new_resource_mw), tuple(new_response_mw)) + + class FalconInstrumentor(BaseInstrumentor): # pylint: disable=protected-access,attribute-defined-outside-init """An instrumentor for falcon.API @@ -473,7 +503,7 @@ def instrument_app( app._otel_excluded_urls = excluded_urls if excluded_urls is None else get_excluded_urls("FALCON") app._otel_tracer = trace.get_tracer(__name__, __version__, tracer_provider) app._otel_meter = get_meter(__name__, __version__, meter_provider) - app.duration_histogram = self._otel_meter.create_histogram( + app.duration_histogram = app._otel_meter.create_histogram( name="http.server.duration", unit="ms", description="measures the duration of the inbound HTTP request", @@ -491,7 +521,14 @@ def instrument_app( request_hook, response_hook, ) - # TODO: implement middleware addition logic for falcon 1,2 & 3 + if _falcon_version == 3: + app._unprepared_middleware.insert(0, trace_middleware) + app._middleware = app._prepare_middleware( + app._unprepared_middleware, + independent_middleware=app._independent_middleware, + ) + else: + app._middleware = _prepare_middleware_tupple(app._middleware, trace_middleware, app._independet_middleware) app._is_instrumented_by_opentelemetry = True app.__class__ = _InstrumentedFalconAPI From 1f177084b124dc2ae9992a35d152b771e836bc2b Mon Sep 17 00:00:00 2001 From: "anshul.asawa" Date: Fri, 30 Sep 2022 22:54:15 +0530 Subject: [PATCH 03/15] remove middleware --- .../instrumentation/falcon/__init__.py | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py index 87666e1dae..93196c6003 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py @@ -146,7 +146,6 @@ def response_hook(span, req, resp): from time import time_ns from timeit import default_timer from typing import Collection -from xxlimited import new import falcon from packaging import version as package_version @@ -447,16 +446,19 @@ def process_response( self._response_hook(span, req, resp) -def _prepare_middleware_tupple(middleware, trace_middleware, independent_middleware): - request_mw, resource_mw, respose_mw = middleware +def _prepare_middleware_tupple(middleware_tupple, trace_middleware, independent_middleware): + request_mw, resource_mw, response_mw = middleware_tupple new_request_mw = [] new_response_mw = [] new_resource_mw = [] process_request = getattr(trace_middleware, 'process_request') + process_request._is_otel_method = True process_resource = getattr(trace_middleware, 'process_resource') + process_resource._is_otel_method = True process_response = getattr(trace_middleware, 'process_response') + process_response._is_otel_method = True if independent_middleware: new_request_mw.insert(0, process_request) @@ -468,12 +470,28 @@ def _prepare_middleware_tupple(middleware, trace_middleware, independent_middlew for each in request_mw: new_request_mw.append(each) - for each in respose_mw: - new_response_mw.append(each) for each in resource_mw: new_resource_mw.append(each) + for each in response_mw: + new_response_mw.append(each) + + return (tuple(new_request_mw), tuple(new_resource_mw), tuple(new_response_mw)) + +def remove_trace_middleware(middleware_tupple): + request_mw, resource_mw, response_mw = middleware_tupple + + new_request_mw = [] + for each in request_mw: + if isinstance(each, tuple) and not getattr(each[0], '_is_otel_method'): + new_request_mw.append(each) + elif not isinstance(each, tuple) and not getattr(each, '_is_otel_method'): + new_request_mw.append(each) + new_response_mw = [x for x in response_mw if not getattr(each, '_is_otel_method')] + new_resource_mw = [x for x in resource_mw if not getattr(each, '_is_otel_method')] + return (tuple(new_request_mw), tuple(new_resource_mw), tuple(new_response_mw)) + class FalconInstrumentor(BaseInstrumentor): @@ -549,7 +567,7 @@ def _remove_instrumented_middleware(self, app): app._unprepared_middleware, independent_middleware=app._independent_middleware, ) - else: + elif hasattr(app, '_middleware_list'): app._middlewares_list = [ x for x in app._middlewares_list @@ -560,6 +578,8 @@ def _remove_instrumented_middleware(self, app): app._middlewares_list, independent_middleware=app._independent_middleware, ) + else: + app._middleware = remove_trace_middleware(app._middleware) app._is_instrumented_by_opentelemetry = False def _instrument(self, **opts): From d6ddd28e19a6e289e0ea978d3c96750a631ffab6 Mon Sep 17 00:00:00 2001 From: "anshul.asawa" Date: Sat, 1 Oct 2022 15:07:54 +0530 Subject: [PATCH 04/15] fix function name --- .../instrumentation/falcon/__init__.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py index 93196c6003..4708f0dc91 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py @@ -482,13 +482,13 @@ def remove_trace_middleware(middleware_tupple): new_request_mw = [] for each in request_mw: - if isinstance(each, tuple) and not getattr(each[0], '_is_otel_method'): + if isinstance(each, tuple) and not hasattr(each[0], '_is_otel_method'): new_request_mw.append(each) - elif not isinstance(each, tuple) and not getattr(each, '_is_otel_method'): + elif not isinstance(each, tuple) and not hasattr(each, '_is_otel_method'): new_request_mw.append(each) - new_response_mw = [x for x in response_mw if not getattr(each, '_is_otel_method')] - new_resource_mw = [x for x in resource_mw if not getattr(each, '_is_otel_method')] + new_response_mw = [x for x in response_mw if not hasattr(each, '_is_otel_method')] + new_resource_mw = [x for x in resource_mw if not hasattr(each, '_is_otel_method')] return (tuple(new_request_mw), tuple(new_resource_mw), tuple(new_response_mw)) @@ -551,8 +551,8 @@ def instrument_app( app.__class__ = _InstrumentedFalconAPI - # pylint:disable=no-self-use - def _remove_instrumented_middleware(self, app): + @staticmethod + def uinstrument_app(app): if ( hasattr(app, "_is_instrumented_by_opentelemetry") and app._is_instrumented_by_opentelemetry @@ -594,6 +594,6 @@ def __init__(self, *args, **kwargs): def _uninstrument(self, **kwargs): for app in _InstrumentedFalconAPI._instrumented_falcon_apps: - self._remove_instrumented_middleware(app) + FalconInstrumentor.uinstrument_app(app) _InstrumentedFalconAPI._instrumented_falcon_apps.clear() setattr(falcon, _instrument_app, self._original_falcon_api) From 4c8bf49340626dd5768373b178066c4d84c1706f Mon Sep 17 00:00:00 2001 From: "anshul.asawa" Date: Sat, 1 Oct 2022 15:35:06 +0530 Subject: [PATCH 05/15] add test for manual instrumentation --- .../instrumentation/falcon/__init__.py | 2 +- .../tests/test_falcon.py | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py index 4708f0dc91..f99c290f43 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py @@ -515,7 +515,7 @@ def instrument_app( **kwargs ): if not hasattr(app, "_is_instrumented_by_opentelemetry"): - app._is_instrumented_by_opentelemetry = False + setattr(app, '_is_instrumented_by_opentelemetry', False) if not getattr(app, "_is_instrumented_by_opentelemetry", False): app._otel_excluded_urls = excluded_urls if excluded_urls is None else get_excluded_urls("FALCON") diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 7e714342a7..62951ffd13 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -353,6 +353,38 @@ def test_metric_uninstrument(self): if isinstance(point, HistogramDataPoint): self.assertEqual(point.count, 1) +class TestFalconManualInstrumentation(TestFalconInstrumentation): + def setUp(self): + TestBase.setUp(self) + self.env_patch = patch.dict( + "os.environ", + { + "OTEL_PYTHON_FALCON_EXCLUDED_URLS": "ping", + "OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS": "query_string", + }, + ) + self.env_patch.start() + + # FalconInstrumentor().instrument( + # request_hook=getattr(self, "request_hook", None), + # response_hook=getattr(self, "response_hook", None), + # ) + self.app = make_app() + + FalconInstrumentor.instrument_app( + self.app, + request_hook=getattr(self, "request_hook", None), + response_hook=getattr(self, "response_hook", None), + ) + + def client(self): + return testing.TestClient(self.app) + + def tearDown(self): + TestBase.tearDown(self) + with self.disable_logging(): + FalconInstrumentor.uninstrument_app(self.app) + self.env_patch.stop() class TestFalconInstrumentationWithTracerProvider(TestBase): def setUp(self): From b6ed177583bd587dad7c0dea0275e85720d6a201 Mon Sep 17 00:00:00 2001 From: "anshul.asawa" Date: Thu, 6 Oct 2022 10:32:32 +0530 Subject: [PATCH 06/15] fix response middleware --- .../opentelemetry/instrumentation/falcon/__init__.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py index f99c290f43..d574fc04bd 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py @@ -448,18 +448,21 @@ def process_response( def _prepare_middleware_tupple(middleware_tupple, trace_middleware, independent_middleware): request_mw, resource_mw, response_mw = middleware_tupple - + new_request_mw = [] new_response_mw = [] new_resource_mw = [] - + process_request = getattr(trace_middleware, 'process_request') process_request._is_otel_method = True process_resource = getattr(trace_middleware, 'process_resource') process_resource._is_otel_method = True process_response = getattr(trace_middleware, 'process_response') process_response._is_otel_method = True - + + for each in response_mw: + new_response_mw.append(each) + if independent_middleware: new_request_mw.insert(0, process_request) new_response_mw.append(process_response) @@ -472,8 +475,6 @@ def _prepare_middleware_tupple(middleware_tupple, trace_middleware, independent_ new_request_mw.append(each) for each in resource_mw: new_resource_mw.append(each) - for each in response_mw: - new_response_mw.append(each) return (tuple(new_request_mw), tuple(new_resource_mw), tuple(new_response_mw)) From 18373fc67a35fac22a270fca528adae4bf6d5339 Mon Sep 17 00:00:00 2001 From: "anshul.asawa" Date: Thu, 6 Oct 2022 16:36:24 +0530 Subject: [PATCH 07/15] fix approach to add attributes --- .../instrumentation/falcon/__init__.py | 60 ++++++++++--------- 1 file changed, 32 insertions(+), 28 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py index d574fc04bd..de9ed52fad 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py @@ -446,19 +446,19 @@ def process_response( self._response_hook(span, req, resp) -def _prepare_middleware_tupple(middleware_tupple, trace_middleware, independent_middleware): - request_mw, resource_mw, response_mw = middleware_tupple +def _prepare_middleware_tuple(middleware_tuple, trace_middleware, independent_middleware): + request_mw, resource_mw, response_mw = middleware_tuple new_request_mw = [] new_response_mw = [] new_resource_mw = [] process_request = getattr(trace_middleware, 'process_request') - process_request._is_otel_method = True + setattr(process_request.__func__, '_is_otel_method', True) process_resource = getattr(trace_middleware, 'process_resource') - process_resource._is_otel_method = True + setattr(process_resource.__func__, '_is_otel_method', True) process_response = getattr(trace_middleware, 'process_response') - process_response._is_otel_method = True + setattr(process_response.__func__, '_is_otel_method', True) for each in response_mw: new_response_mw.append(each) @@ -478,8 +478,8 @@ def _prepare_middleware_tupple(middleware_tupple, trace_middleware, independent_ return (tuple(new_request_mw), tuple(new_resource_mw), tuple(new_response_mw)) -def remove_trace_middleware(middleware_tupple): - request_mw, resource_mw, response_mw = middleware_tupple +def remove_trace_middleware(middleware_tuple): + request_mw, resource_mw, response_mw = middleware_tuple new_request_mw = [] for each in request_mw: @@ -513,28 +513,34 @@ def instrument_app( tracer_provider=None, meter_provider=None, excluded_urls=None, - **kwargs + **opts ): if not hasattr(app, "_is_instrumented_by_opentelemetry"): - setattr(app, '_is_instrumented_by_opentelemetry', False) + class FalconAPI(_InstrumentedFalconAPI): + def __init__(self, *args, ** kwargs): + for attribute in app.__slots__: + setattr(self, attribute, getattr(app, attribute, None)) + self._otel_excluded_urls = excluded_urls if excluded_urls is None else get_excluded_urls("FALCON") + self._otel_tracer = trace.get_tracer(__name__, __version__, tracer_provider) + self._otel_meter = get_meter(__name__, __version__, meter_provider) + self.duration_histogram = self._otel_meter.create_histogram( + name="http.server.duration", + unit="ms", + description="measures the duration of the inbound HTTP request", + ) + self.active_requests_counter = self._otel_meter.create_up_down_counter( + name="http.server.active_requests", + unit="requests", + description="measures the number of concurrent HTTP requests that are currently in-flight", + ) + self._is_instrumented_by_opentelemetry = False + + app = FalconAPI() if not getattr(app, "_is_instrumented_by_opentelemetry", False): - app._otel_excluded_urls = excluded_urls if excluded_urls is None else get_excluded_urls("FALCON") - app._otel_tracer = trace.get_tracer(__name__, __version__, tracer_provider) - app._otel_meter = get_meter(__name__, __version__, meter_provider) - app.duration_histogram = app._otel_meter.create_histogram( - name="http.server.duration", - unit="ms", - description="measures the duration of the inbound HTTP request", - ) - app.active_requests_counter = app._otel_meter.create_up_down_counter( - name="http.server.active_requests", - unit="requests", - description="measures the number of concurrent HTTP requests that are currently in-flight", - ) trace_middleware = _TraceMiddleware( app._otel_tracer, - kwargs.pop( + opts.pop( "traced_request_attributes", get_traced_request_attrs("FALCON") ), request_hook, @@ -547,13 +553,11 @@ def instrument_app( independent_middleware=app._independent_middleware, ) else: - app._middleware = _prepare_middleware_tupple(app._middleware, trace_middleware, app._independet_middleware) + app._middleware = _prepare_middleware_tuple(app._middleware, trace_middleware, app._independent_middleware) app._is_instrumented_by_opentelemetry = True - app.__class__ = _InstrumentedFalconAPI - @staticmethod - def uinstrument_app(app): + def uninstrument_app(app): if ( hasattr(app, "_is_instrumented_by_opentelemetry") and app._is_instrumented_by_opentelemetry @@ -595,6 +599,6 @@ def __init__(self, *args, **kwargs): def _uninstrument(self, **kwargs): for app in _InstrumentedFalconAPI._instrumented_falcon_apps: - FalconInstrumentor.uinstrument_app(app) + FalconInstrumentor.uninstrument_app(app) _InstrumentedFalconAPI._instrumented_falcon_apps.clear() setattr(falcon, _instrument_app, self._original_falcon_api) From a705af77456aab0573c892b768893d626256609c Mon Sep 17 00:00:00 2001 From: "anshul.asawa" Date: Thu, 6 Oct 2022 23:44:18 +0530 Subject: [PATCH 08/15] working manual instrumentation but with returning method --- .../instrumentation/falcon/__init__.py | 3 ++- .../tests/test_falcon.py | 26 ++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py index de9ed52fad..4fb9af530d 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py @@ -520,7 +520,7 @@ class FalconAPI(_InstrumentedFalconAPI): def __init__(self, *args, ** kwargs): for attribute in app.__slots__: setattr(self, attribute, getattr(app, attribute, None)) - self._otel_excluded_urls = excluded_urls if excluded_urls is None else get_excluded_urls("FALCON") + self._otel_excluded_urls = excluded_urls if excluded_urls is not None else get_excluded_urls("FALCON") self._otel_tracer = trace.get_tracer(__name__, __version__, tracer_provider) self._otel_meter = get_meter(__name__, __version__, meter_provider) self.duration_histogram = self._otel_meter.create_histogram( @@ -555,6 +555,7 @@ def __init__(self, *args, ** kwargs): else: app._middleware = _prepare_middleware_tuple(app._middleware, trace_middleware, app._independent_middleware) app._is_instrumented_by_opentelemetry = True + return app @staticmethod def uninstrument_app(app): diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 62951ffd13..49fffb56ce 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -371,7 +371,7 @@ def setUp(self): # ) self.app = make_app() - FalconInstrumentor.instrument_app( + self.app = FalconInstrumentor.instrument_app( self.app, request_hook=getattr(self, "request_hook", None), response_hook=getattr(self, "response_hook", None), @@ -385,6 +385,30 @@ def tearDown(self): with self.disable_logging(): FalconInstrumentor.uninstrument_app(self.app) self.env_patch.stop() + + def test_uninstrument_after_instrument(self): + self.client().simulate_get(path="/hello") + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 1) + + FalconInstrumentor().uninstrument_app(self.app) + self.memory_exporter.clear() + + self.client().simulate_get(path="/hello") + spans = self.memory_exporter.get_finished_spans() + self.assertEqual(len(spans), 0) + + def test_metric_uninstrument(self): + self.client().simulate_request(method="POST", path="/hello/756") + FalconInstrumentor().uninstrument_app(self.app) + self.client().simulate_request(method="POST", path="/hello/756") + metrics_list = self.memory_metrics_reader.get_metrics_data() + for resource_metric in metrics_list.resource_metrics: + for scope_metric in resource_metric.scope_metrics: + for metric in scope_metric.metrics: + for point in list(metric.data.data_points): + if isinstance(point, HistogramDataPoint): + self.assertEqual(point.count, 1) class TestFalconInstrumentationWithTracerProvider(TestBase): def setUp(self): From e430d8e0081689539ffa9f4bb69c68a387f93fe9 Mon Sep 17 00:00:00 2001 From: "anshul.asawa" Date: Fri, 7 Oct 2022 12:12:38 +0530 Subject: [PATCH 09/15] fix lint --- .../instrumentation/falcon/__init__.py | 81 ++++++++++++------- .../tests/test_falcon.py | 11 +-- 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py index 4fb9af530d..31b01e0144 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py @@ -441,7 +441,6 @@ def process_response( propagator = get_global_response_propagator() if propagator: propagator.inject(resp, setter=_response_propagation_setter) - if self._response_hook: self._response_hook(span, req, resp) @@ -453,12 +452,12 @@ def _prepare_middleware_tuple(middleware_tuple, trace_middleware, independent_mi new_response_mw = [] new_resource_mw = [] - process_request = getattr(trace_middleware, 'process_request') - setattr(process_request.__func__, '_is_otel_method', True) - process_resource = getattr(trace_middleware, 'process_resource') - setattr(process_resource.__func__, '_is_otel_method', True) - process_response = getattr(trace_middleware, 'process_response') - setattr(process_response.__func__, '_is_otel_method', True) + process_request = getattr(trace_middleware, "process_request") + setattr(process_request.__func__, "_is_otel_method", True) + process_resource = getattr(trace_middleware, "process_resource") + setattr(process_resource.__func__, "_is_otel_method", True) + process_response = getattr(trace_middleware, "process_response") + setattr(process_response.__func__, "_is_otel_method", True) for each in response_mw: new_response_mw.append(each) @@ -468,31 +467,41 @@ def _prepare_middleware_tuple(middleware_tuple, trace_middleware, independent_mi new_response_mw.append(process_response) else: new_request_mw.insert(0, (process_request, process_response)) - new_resource_mw.insert(0, process_resource) - for each in request_mw: new_request_mw.append(each) for each in resource_mw: new_resource_mw.append(each) - - return (tuple(new_request_mw), tuple(new_resource_mw), tuple(new_response_mw)) + + return ( + tuple(new_request_mw), + tuple(new_resource_mw), + tuple(new_response_mw), + ) def remove_trace_middleware(middleware_tuple): request_mw, resource_mw, response_mw = middleware_tuple - new_request_mw = [] for each in request_mw: - if isinstance(each, tuple) and not hasattr(each[0], '_is_otel_method'): + if isinstance(each, tuple) and not hasattr(each[0], "_is_otel_method"): new_request_mw.append(each) - elif not isinstance(each, tuple) and not hasattr(each, '_is_otel_method'): + elif not isinstance(each, tuple) and not hasattr( + each, "_is_otel_method" + ): new_request_mw.append(each) - - new_response_mw = [x for x in response_mw if not hasattr(each, '_is_otel_method')] - new_resource_mw = [x for x in resource_mw if not hasattr(each, '_is_otel_method')] - return (tuple(new_request_mw), tuple(new_resource_mw), tuple(new_response_mw)) - + new_response_mw = [ + x for x in response_mw if not hasattr(each, "_is_otel_method") + ] + new_resource_mw = [ + x for x in resource_mw if not hasattr(each, "_is_otel_method") + ] + + return ( + tuple(new_request_mw), + tuple(new_resource_mw), + tuple(new_response_mw), + ) class FalconInstrumentor(BaseInstrumentor): @@ -504,7 +513,7 @@ class FalconInstrumentor(BaseInstrumentor): def instrumentation_dependencies(self) -> Collection[str]: return _instruments - + @staticmethod def instrument_app( app, @@ -513,16 +522,25 @@ def instrument_app( tracer_provider=None, meter_provider=None, excluded_urls=None, - **opts + **opts, ): if not hasattr(app, "_is_instrumented_by_opentelemetry"): + class FalconAPI(_InstrumentedFalconAPI): - def __init__(self, *args, ** kwargs): + def __init__(self, *args, **kwargs): for attribute in app.__slots__: setattr(self, attribute, getattr(app, attribute, None)) - self._otel_excluded_urls = excluded_urls if excluded_urls is not None else get_excluded_urls("FALCON") - self._otel_tracer = trace.get_tracer(__name__, __version__, tracer_provider) - self._otel_meter = get_meter(__name__, __version__, meter_provider) + self._otel_excluded_urls = ( + excluded_urls + if excluded_urls is not None + else get_excluded_urls("FALCON") + ) + self._otel_tracer = trace.get_tracer( + __name__, __version__, tracer_provider + ) + self._otel_meter = get_meter( + __name__, __version__, meter_provider + ) self.duration_histogram = self._otel_meter.create_histogram( name="http.server.duration", unit="ms", @@ -536,12 +554,13 @@ def __init__(self, *args, ** kwargs): self._is_instrumented_by_opentelemetry = False app = FalconAPI() - + if not getattr(app, "_is_instrumented_by_opentelemetry", False): trace_middleware = _TraceMiddleware( app._otel_tracer, opts.pop( - "traced_request_attributes", get_traced_request_attrs("FALCON") + "traced_request_attributes", + get_traced_request_attrs("FALCON"), ), request_hook, response_hook, @@ -553,7 +572,11 @@ def __init__(self, *args, ** kwargs): independent_middleware=app._independent_middleware, ) else: - app._middleware = _prepare_middleware_tuple(app._middleware, trace_middleware, app._independent_middleware) + app._middleware = _prepare_middleware_tuple( + app._middleware, + trace_middleware, + app._independent_middleware + ) app._is_instrumented_by_opentelemetry = True return app @@ -573,7 +596,7 @@ def uninstrument_app(app): app._unprepared_middleware, independent_middleware=app._independent_middleware, ) - elif hasattr(app, '_middleware_list'): + elif hasattr(app, "_middleware_list"): app._middlewares_list = [ x for x in app._middlewares_list diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 49fffb56ce..79d3764e24 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -353,6 +353,7 @@ def test_metric_uninstrument(self): if isinstance(point, HistogramDataPoint): self.assertEqual(point.count, 1) + class TestFalconManualInstrumentation(TestFalconInstrumentation): def setUp(self): TestBase.setUp(self) @@ -364,11 +365,6 @@ def setUp(self): }, ) self.env_patch.start() - - # FalconInstrumentor().instrument( - # request_hook=getattr(self, "request_hook", None), - # response_hook=getattr(self, "response_hook", None), - # ) self.app = make_app() self.app = FalconInstrumentor.instrument_app( @@ -385,7 +381,7 @@ def tearDown(self): with self.disable_logging(): FalconInstrumentor.uninstrument_app(self.app) self.env_patch.stop() - + def test_uninstrument_after_instrument(self): self.client().simulate_get(path="/hello") spans = self.memory_exporter.get_finished_spans() @@ -397,7 +393,7 @@ def test_uninstrument_after_instrument(self): self.client().simulate_get(path="/hello") spans = self.memory_exporter.get_finished_spans() self.assertEqual(len(spans), 0) - + def test_metric_uninstrument(self): self.client().simulate_request(method="POST", path="/hello/756") FalconInstrumentor().uninstrument_app(self.app) @@ -410,6 +406,7 @@ def test_metric_uninstrument(self): if isinstance(point, HistogramDataPoint): self.assertEqual(point.count, 1) + class TestFalconInstrumentationWithTracerProvider(TestBase): def setUp(self): super().setUp() From 6fa5f275d7910e7b30c50b2f8e9c3c422b1764cb Mon Sep 17 00:00:00 2001 From: "anshul.asawa" Date: Fri, 7 Oct 2022 12:17:43 +0530 Subject: [PATCH 10/15] add change log --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 53f45790d9..7abc532103 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `opentelemetry-instrumentation-django` Fixed bug where auto-instrumentation fails when django is installed and settings are not configured. ([#1369](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1369)) - `opentelemetry-instrumentation-system-metrics` add supports to collect system thread count. ([#1339](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1339)) +- Manual Instrumentation in Falcon + ([#1365](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1365)) ## [1.13.0-0.34b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.13.0-0.34b0) - 2022-09-26 From 2709724a3b42e27f8b1b06249c44ad9f0e159e77 Mon Sep 17 00:00:00 2001 From: "anshul.asawa" Date: Fri, 7 Oct 2022 13:35:04 +0530 Subject: [PATCH 11/15] fix lint --- .../instrumentation/falcon/__init__.py | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py index 31b01e0144..f6fa9c9cb3 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py @@ -445,7 +445,9 @@ def process_response( self._response_hook(span, req, resp) -def _prepare_middleware_tuple(middleware_tuple, trace_middleware, independent_middleware): +def _prepare_middleware_tuple( + middleware_tuple, trace_middleware, independent_middleware +): request_mw, resource_mw, response_mw = middleware_tuple new_request_mw = [] @@ -479,6 +481,7 @@ def _prepare_middleware_tuple(middleware_tuple, trace_middleware, independent_mi tuple(new_response_mw), ) + def remove_trace_middleware(middleware_tuple): request_mw, resource_mw, response_mw = middleware_tuple new_request_mw = [] @@ -513,14 +516,14 @@ class FalconInstrumentor(BaseInstrumentor): def instrumentation_dependencies(self) -> Collection[str]: return _instruments - + @staticmethod def instrument_app( - app, - request_hook=None, - response_hook=None, - tracer_provider=None, - meter_provider=None, + app, + request_hook=None, + response_hook=None, + tracer_provider=None, + meter_provider=None, excluded_urls=None, **opts, ): @@ -529,7 +532,7 @@ def instrument_app( class FalconAPI(_InstrumentedFalconAPI): def __init__(self, *args, **kwargs): for attribute in app.__slots__: - setattr(self, attribute, getattr(app, attribute, None)) + setattr(self, attribute, getattr(app, attribute, None)) self._otel_excluded_urls = ( excluded_urls if excluded_urls is not None @@ -575,8 +578,10 @@ def __init__(self, *args, **kwargs): app._middleware = _prepare_middleware_tuple( app._middleware, trace_middleware, - app._independent_middleware + app._independent_middleware, ) + if app not in _InstrumentedFalconAPI._instrumented_falcon_apps: + _InstrumentedFalconAPI._instrumented_falcon_apps.add(app) app._is_instrumented_by_opentelemetry = True return app From 4891737071f2ce983a1f03a3bb0ea8bb6283a371 Mon Sep 17 00:00:00 2001 From: "anshul.asawa" Date: Fri, 7 Oct 2022 13:50:03 +0530 Subject: [PATCH 12/15] fix typo --- .../src/opentelemetry/instrumentation/falcon/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py index f6fa9c9cb3..e210ff5ec9 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py @@ -494,10 +494,10 @@ def remove_trace_middleware(middleware_tuple): new_request_mw.append(each) new_response_mw = [ - x for x in response_mw if not hasattr(each, "_is_otel_method") + x for x in response_mw if not hasattr(x, "_is_otel_method") ] new_resource_mw = [ - x for x in resource_mw if not hasattr(each, "_is_otel_method") + x for x in resource_mw if not hasattr(x, "_is_otel_method") ] return ( From d396e2af3c55fe6eb2c61ec0dacf3827a1b30f23 Mon Sep 17 00:00:00 2001 From: "anshul.asawa" Date: Fri, 7 Oct 2022 15:17:06 +0530 Subject: [PATCH 13/15] add tests --- .../tests/test_falcon.py | 116 +++++++++++++++++- 1 file changed, 114 insertions(+), 2 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index 79d3764e24..db24aced47 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -387,7 +387,7 @@ def test_uninstrument_after_instrument(self): spans = self.memory_exporter.get_finished_spans() self.assertEqual(len(spans), 1) - FalconInstrumentor().uninstrument_app(self.app) + FalconInstrumentor.uninstrument_app(self.app) self.memory_exporter.clear() self.client().simulate_get(path="/hello") @@ -396,7 +396,7 @@ def test_uninstrument_after_instrument(self): def test_metric_uninstrument(self): self.client().simulate_request(method="POST", path="/hello/756") - FalconInstrumentor().uninstrument_app(self.app) + FalconInstrumentor.uninstrument_app(self.app) self.client().simulate_request(method="POST", path="/hello/756") metrics_list = self.memory_metrics_reader.get_metrics_data() for resource_metric in metrics_list.resource_metrics: @@ -437,6 +437,29 @@ def test_traced_request(self): self.exporter.clear() +class TestFalconManualInstrumentationWithTracerProvider( + TestFalconInstrumentationWithTracerProvider +): + def setUp(self): + TestBase.setUp(self) + resource = Resource.create({"resource-key": "resource-value"}) + result = self.create_tracer_provider(resource=resource) + tracer_provider, exporter = result + self.exporter = exporter + self.app = make_app() + self.app = FalconInstrumentor.instrument_app( + self.app, tracer_provider=tracer_provider + ) + + def client(self): + return testing.TestClient(self.app) + + def tearDown(self): + TestBase.tearDown(self) + with self.disable_logging(): + FalconInstrumentor.uninstrument_app(self.app) + + class TestFalconInstrumentationHooks(TestFalconBase): # pylint: disable=no-self-use def request_hook(self, span, req): @@ -456,6 +479,35 @@ def test_hooks(self): ) +class TestFalconManualInstrumentationHooks(TestFalconInstrumentationHooks): + def setUp(self): + TestBase.setUp(self) + self.env_patch = patch.dict( + "os.environ", + { + "OTEL_PYTHON_FALCON_EXCLUDED_URLS": "ping", + "OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS": "query_string", + }, + ) + self.env_patch.start() + self.app = make_app() + + self.app = FalconInstrumentor.instrument_app( + self.app, + request_hook=getattr(self, "request_hook", None), + response_hook=getattr(self, "response_hook", None), + ) + + def client(self): + return testing.TestClient(self.app) + + def tearDown(self): + TestBase.tearDown(self) + with self.disable_logging(): + FalconInstrumentor.uninstrument_app(self.app) + self.env_patch.stop() + + class TestFalconInstrumentationWrappedWithOtherFramework(TestFalconBase): def test_mark_span_internal_in_presence_of_span_from_other_framework(self): tracer = trace.get_tracer(__name__) @@ -471,6 +523,36 @@ def test_mark_span_internal_in_presence_of_span_from_other_framework(self): ) +class TestFalconManualInstrumentationWrappedWithOtherFramework( + TestFalconInstrumentationWrappedWithOtherFramework +): + def setUp(self): + TestBase.setUp(self) + self.env_patch = patch.dict( + "os.environ", + { + "OTEL_PYTHON_FALCON_EXCLUDED_URLS": "ping", + "OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS": "query_string", + }, + ) + self.env_patch.start() + self.app = make_app() + + self.app = FalconInstrumentor.instrument_app( + self.app, + request_hook=getattr(self, "request_hook", None), + response_hook=getattr(self, "response_hook", None), + ) + + def client(self): + return testing.TestClient(self.app) + + def tearDown(self): + TestBase.tearDown(self) + with self.disable_logging(): + FalconInstrumentor.uninstrument_app(self.app) + + @patch.dict( "os.environ", { @@ -581,3 +663,33 @@ def test_custom_response_header_not_added_in_internal_span(self): self.assertEqual(span.kind, trace.SpanKind.INTERNAL) for key, _ in not_expected.items(): self.assertNotIn(key, span.attributes) + + +class TestCustomRequestResponseHeadersManualInstrumentation( + TestCustomRequestResponseHeaders +): + def setUp(self): + TestBase.setUp(self) + self.env_patch = patch.dict( + "os.environ", + { + "OTEL_PYTHON_FALCON_EXCLUDED_URLS": "ping", + "OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS": "query_string", + }, + ) + self.env_patch.start() + self.app = make_app() + + self.app = FalconInstrumentor.instrument_app( + self.app, + request_hook=getattr(self, "request_hook", None), + response_hook=getattr(self, "response_hook", None), + ) + + def client(self): + return testing.TestClient(self.app) + + def tearDown(self): + TestBase.tearDown(self) + with self.disable_logging(): + FalconInstrumentor.uninstrument_app(self.app) From eb96e2e5c914771172323e747ddc6d7cb6fa8362 Mon Sep 17 00:00:00 2001 From: "anshul.asawa" Date: Mon, 7 Nov 2022 14:12:39 +0530 Subject: [PATCH 14/15] fix attriibute error --- .../instrumentation/falcon/__init__.py | 81 ++++++++++++------- 1 file changed, 53 insertions(+), 28 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py index e210ff5ec9..a988bf7b9b 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py @@ -529,34 +529,59 @@ def instrument_app( ): if not hasattr(app, "_is_instrumented_by_opentelemetry"): - class FalconAPI(_InstrumentedFalconAPI): - def __init__(self, *args, **kwargs): - for attribute in app.__slots__: - setattr(self, attribute, getattr(app, attribute, None)) - self._otel_excluded_urls = ( - excluded_urls - if excluded_urls is not None - else get_excluded_urls("FALCON") - ) - self._otel_tracer = trace.get_tracer( - __name__, __version__, tracer_provider - ) - self._otel_meter = get_meter( - __name__, __version__, meter_provider - ) - self.duration_histogram = self._otel_meter.create_histogram( - name="http.server.duration", - unit="ms", - description="measures the duration of the inbound HTTP request", - ) - self.active_requests_counter = self._otel_meter.create_up_down_counter( - name="http.server.active_requests", - unit="requests", - description="measures the number of concurrent HTTP requests that are currently in-flight", - ) - self._is_instrumented_by_opentelemetry = False - - app = FalconAPI() + try: + app._otel_excluded_urls = ( + excluded_urls + if excluded_urls is not None + else get_excluded_urls("FALCON") + ) + app._otel_tracer = trace.get_tracer( + __name__, __version__, tracer_provider + ) + app._otel_meter = get_meter( + __name__, __version__, meter_provider + ) + app.duration_histogram = app._otel_meter.create_histogram( + name="http.server.duration", + unit="ms", + description="measures the duration of the inbound HTTP request", + ) + app.active_requests_counter = app._otel_meter.create_up_down_counter( + name="http.server.active_requests", + unit="requests", + description="measures the number of concurrent HTTP requests that are currently in-flight", + ) + app._is_instrumented_by_opentelemetry = False + except AttributeError: + + class FalconAPI(_InstrumentedFalconAPI): + def __init__(self, *args, **kwargs): + for attribute in app.__slots__: + setattr(self, attribute, getattr(app, attribute, None)) + self._otel_excluded_urls = ( + excluded_urls + if excluded_urls is not None + else get_excluded_urls("FALCON") + ) + self._otel_tracer = trace.get_tracer( + __name__, __version__, tracer_provider + ) + self._otel_meter = get_meter( + __name__, __version__, meter_provider + ) + self.duration_histogram = self._otel_meter.create_histogram( + name="http.server.duration", + unit="ms", + description="measures the duration of the inbound HTTP request", + ) + self.active_requests_counter = self._otel_meter.create_up_down_counter( + name="http.server.active_requests", + unit="requests", + description="measures the number of concurrent HTTP requests that are currently in-flight", + ) + self._is_instrumented_by_opentelemetry = False + + app = FalconAPI() if not getattr(app, "_is_instrumented_by_opentelemetry", False): trace_middleware = _TraceMiddleware( From 5a5b85cd46a7a89c5fbb62c23ed381622272d99a Mon Sep 17 00:00:00 2001 From: "anshul.asawa" Date: Mon, 7 Nov 2022 15:20:15 +0530 Subject: [PATCH 15/15] add test for attribute validity --- .../instrumentation/falcon/__init__.py | 6 ++++-- .../tests/test_falcon.py | 20 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py index ba85a51577..14ef99a380 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py @@ -599,11 +599,13 @@ def instrument_app( ) app._is_instrumented_by_opentelemetry = False except AttributeError: - + class FalconAPI(_InstrumentedFalconAPI): def __init__(self, *args, **kwargs): for attribute in app.__slots__: - setattr(self, attribute, getattr(app, attribute, None)) + setattr( + self, attribute, getattr(app, attribute, None) + ) self._otel_excluded_urls = ( excluded_urls if excluded_urls is not None diff --git a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py index df6f77c48d..24b6789143 100644 --- a/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py +++ b/instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py @@ -407,6 +407,26 @@ def test_metric_uninstrument(self): if isinstance(point, HistogramDataPoint): self.assertEqual(point.count, 1) + def test_falcon_attribute_validity(self): + import falcon + _parsed_falcon_version = package_version.parse(falcon.__version__) + if _parsed_falcon_version < package_version.parse("3.0.0"): + # Falcon 1 and Falcon 2 + + class MyAPI(falcon.API): + class_var = "class_var" + + self.app = MyAPI() + else: + # Falcon 3 + class MyAPP(falcon.App): + class_var = "class_var" + + self.app = MyAPP() + + self.app = FalconInstrumentor.instrument_app(self.app) + self.assertEqual(self.app.class_var, "class_var") + class TestFalconInstrumentationWithTracerProvider(TestBase): def setUp(self):