Skip to content

Commit 2ffdf6b

Browse files
committed
sonarqube: Fix duplicated code blocks for grpcio and flask (vanilla and with_blinker) instrumentations
Signed-off-by: Cagri Yonca <[email protected]>
1 parent 9598863 commit 2ffdf6b

File tree

4 files changed

+209
-405
lines changed

4 files changed

+209
-405
lines changed

src/instana/instrumentation/flask/common.py

Lines changed: 97 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,27 @@
22
# (c) Copyright Instana Inc. 2019
33

44

5-
import wrapt
6-
import flask
5+
import re
76
from importlib.metadata import version
8-
from typing import Callable, Tuple, Dict, Any, TYPE_CHECKING, Union
7+
from typing import TYPE_CHECKING, Any, Callable, Dict, Tuple, Type, Union
98

9+
import flask
10+
import wrapt
11+
from opentelemetry import context, trace
1012
from opentelemetry.semconv.trace import SpanAttributes
1113

1214
from instana.log import logger
13-
from instana.singletons import get_tracer
1415
from instana.propagators.format import Format
15-
16+
from instana.singletons import agent, get_tracer
17+
from instana.util.secrets import strip_secrets_from_query
18+
from instana.util.traceutils import extract_custom_headers
1619

1720
if TYPE_CHECKING:
18-
from werkzeug.exceptions import HTTPException
1921
from flask.typing import ResponseReturnValue
2022
from jinja2.environment import Template
23+
from werkzeug.exceptions import HTTPException
24+
25+
path_tpl_re = re.compile("<.*>")
2126

2227

2328
@wrapt.patch_function_wrapper("flask", "templating._render")
@@ -92,3 +97,89 @@ def handle_user_exception_with_instana(
9297
logger.debug("handle_user_exception_with_instana:", exc_info=True)
9398

9499
return response
100+
101+
102+
def create_span():
103+
env = flask.request.environ
104+
tracer = get_tracer()
105+
span_context = tracer.extract(Format.HTTP_HEADERS, env)
106+
107+
span = tracer.start_span("wsgi", span_context=span_context)
108+
flask.g.span = span
109+
110+
ctx = trace.set_span_in_context(span)
111+
token = context.attach(ctx)
112+
flask.g.token = token
113+
114+
extract_custom_headers(span, env, format=True)
115+
116+
span.set_attribute(SpanAttributes.HTTP_METHOD, flask.request.method)
117+
if "PATH_INFO" in env:
118+
span.set_attribute(SpanAttributes.HTTP_URL, env["PATH_INFO"])
119+
if "QUERY_STRING" in env and len(env["QUERY_STRING"]):
120+
scrubbed_params = strip_secrets_from_query(
121+
env["QUERY_STRING"],
122+
agent.options.secrets_matcher,
123+
agent.options.secrets_list,
124+
)
125+
span.set_attribute("http.params", scrubbed_params)
126+
if "HTTP_HOST" in env:
127+
span.set_attribute("http.host", env["HTTP_HOST"])
128+
129+
if hasattr(flask.request.url_rule, "rule") and path_tpl_re.search(
130+
flask.request.url_rule.rule
131+
):
132+
path_tpl = flask.request.url_rule.rule.replace("<", "{")
133+
path_tpl = path_tpl.replace(">", "}")
134+
span.set_attribute("http.path_tpl", path_tpl)
135+
136+
137+
def inject_span(
138+
response: flask.wrappers.Response,
139+
error_message: str,
140+
set_flask_g_none: bool = False,
141+
):
142+
span = None
143+
try:
144+
# If we're not tracing, just return
145+
if not hasattr(flask.g, "span"):
146+
return response
147+
148+
span = flask.g.span
149+
if span:
150+
if 500 <= response.status_code:
151+
span.mark_as_errored()
152+
153+
span.set_attribute(
154+
SpanAttributes.HTTP_STATUS_CODE, int(response.status_code)
155+
)
156+
extract_custom_headers(span, response.headers, format=False)
157+
tracer = get_tracer()
158+
tracer.inject(span.context, Format.HTTP_HEADERS, response.headers)
159+
except Exception:
160+
logger.debug(error_message, exc_info=True)
161+
finally:
162+
if span and span.is_recording():
163+
span.end()
164+
if set_flask_g_none:
165+
flask.g.span = None
166+
167+
168+
def teardown_request_with_instana(*argv: Union[Exception, Type[Exception]]) -> None:
169+
"""
170+
In the case of exceptions, after_request_with_instana isn't called
171+
so we capture those cases here.
172+
"""
173+
if hasattr(flask.g, "span") and flask.g.span:
174+
if len(argv) > 0 and argv[0]:
175+
span = flask.g.span
176+
span.record_exception(argv[0])
177+
if SpanAttributes.HTTP_STATUS_CODE not in span.attributes:
178+
span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, 500)
179+
if flask.g.span.is_recording():
180+
flask.g.span.end()
181+
flask.g.span = None
182+
183+
if hasattr(flask.g, "token") and flask.g.token:
184+
context.detach(flask.g.token)
185+
flask.g.token = None

src/instana/instrumentation/flask/vanilla.py

Lines changed: 10 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,23 @@
1-
# (c) Copyright IBM Corp. 2021
1+
# (c) Copyright IBM Corp. 2025
22
# (c) Copyright Instana Inc. 2019
33

44

5-
import re
5+
from typing import Callable, Dict, Tuple
6+
67
import flask
78
import wrapt
8-
from typing import Callable, Tuple, Dict, Type, Union
9-
10-
from opentelemetry.semconv.trace import SpanAttributes
11-
from opentelemetry import context, trace
129

10+
from instana.instrumentation.flask.common import (
11+
create_span,
12+
inject_span,
13+
teardown_request_with_instana,
14+
)
1315
from instana.log import logger
14-
from instana.singletons import agent, get_tracer
15-
from instana.util.secrets import strip_secrets_from_query
16-
from instana.util.traceutils import extract_custom_headers
17-
from instana.propagators.format import Format
18-
19-
path_tpl_re = re.compile("<.*>")
2016

2117

2218
def before_request_with_instana() -> None:
2319
try:
24-
env = flask.request.environ
25-
tracer = get_tracer()
26-
span_context = tracer.extract(Format.HTTP_HEADERS, env)
27-
28-
span = tracer.start_span("wsgi", span_context=span_context)
29-
flask.g.span = span
30-
31-
ctx = trace.set_span_in_context(span)
32-
token = context.attach(ctx)
33-
flask.g.token = token
34-
35-
extract_custom_headers(span, env, format=True)
36-
37-
span.set_attribute(SpanAttributes.HTTP_METHOD, flask.request.method)
38-
if "PATH_INFO" in env:
39-
span.set_attribute(SpanAttributes.HTTP_URL, env["PATH_INFO"])
40-
if "QUERY_STRING" in env and len(env["QUERY_STRING"]):
41-
scrubbed_params = strip_secrets_from_query(
42-
env["QUERY_STRING"],
43-
agent.options.secrets_matcher,
44-
agent.options.secrets_list,
45-
)
46-
span.set_attribute("http.params", scrubbed_params)
47-
if "HTTP_HOST" in env:
48-
span.set_attribute("http.host", env["HTTP_HOST"])
49-
50-
if hasattr(flask.request.url_rule, "rule") and path_tpl_re.search(
51-
flask.request.url_rule.rule
52-
):
53-
path_tpl = flask.request.url_rule.rule.replace("<", "{")
54-
path_tpl = path_tpl.replace(">", "}")
55-
span.set_attribute("http.path_tpl", path_tpl)
20+
create_span()
5621
except Exception:
5722
logger.debug("Flask before_request", exc_info=True)
5823

@@ -62,52 +27,10 @@ def before_request_with_instana() -> None:
6227
def after_request_with_instana(
6328
response: flask.wrappers.Response,
6429
) -> flask.wrappers.Response:
65-
span = None
66-
try:
67-
# If we're not tracing, just return
68-
if not hasattr(flask.g, "span"):
69-
return response
70-
71-
span = flask.g.span
72-
if span:
73-
if 500 <= response.status_code:
74-
span.mark_as_errored()
75-
76-
span.set_attribute(
77-
SpanAttributes.HTTP_STATUS_CODE, int(response.status_code)
78-
)
79-
extract_custom_headers(span, response.headers, format=False)
80-
tracer = get_tracer()
81-
tracer.inject(span.context, Format.HTTP_HEADERS, response.headers)
82-
except Exception:
83-
logger.debug("Flask after_request", exc_info=True)
84-
finally:
85-
if span and span.is_recording():
86-
span.end()
87-
flask.g.span = None
30+
inject_span(response, "Flask after_request", set_flask_g_none=True)
8831
return response
8932

9033

91-
def teardown_request_with_instana(*argv: Union[Exception, Type[Exception]]) -> None:
92-
"""
93-
In the case of exceptions, after_request_with_instana isn't called
94-
so we capture those cases here.
95-
"""
96-
if hasattr(flask.g, "span") and flask.g.span:
97-
if len(argv) > 0 and argv[0]:
98-
span = flask.g.span
99-
span.record_exception(argv[0])
100-
if SpanAttributes.HTTP_STATUS_CODE not in span.attributes:
101-
span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, 500)
102-
if flask.g.span.is_recording():
103-
flask.g.span.end()
104-
flask.g.span = None
105-
106-
if hasattr(flask.g, "token") and flask.g.token:
107-
context.detach(flask.g.token)
108-
flask.g.token = None
109-
110-
11134
@wrapt.patch_function_wrapper("flask", "Flask.full_dispatch_request")
11235
def full_dispatch_request_with_instana(
11336
wrapped: Callable[..., flask.wrappers.Response],

src/instana/instrumentation/flask/with_blinker.py

Lines changed: 12 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,33 @@
1-
# (c) Copyright IBM Corp. 2021
1+
# (c) Copyright IBM Corp. 2025
22
# (c) Copyright Instana Inc. 2019
33

44

5-
import re
6-
import wrapt
7-
from typing import Any, Tuple, Dict, Callable
5+
from typing import Any, Callable, Dict, Tuple
86

7+
import flask
8+
import wrapt
9+
from flask import got_request_exception, request_finished, request_started
910
from opentelemetry.semconv.trace import SpanAttributes
10-
from opentelemetry import context, trace
1111

12+
from instana.instrumentation.flask.common import (
13+
create_span,
14+
inject_span,
15+
teardown_request_with_instana,
16+
)
1217
from instana.log import logger
13-
from instana.util.secrets import strip_secrets_from_query
14-
from instana.singletons import agent, get_tracer
15-
from instana.util.traceutils import extract_custom_headers
16-
from instana.propagators.format import Format
17-
18-
import flask
19-
from flask import request_started, request_finished, got_request_exception
20-
21-
path_tpl_re = re.compile("<.*>")
2218

2319

2420
def request_started_with_instana(sender: flask.app.Flask, **extra: Any) -> None:
2521
try:
26-
env = flask.request.environ
27-
tracer = get_tracer()
28-
span_context = tracer.extract(Format.HTTP_HEADERS, env)
29-
30-
span = tracer.start_span("wsgi", span_context=span_context)
31-
flask.g.span = span
32-
33-
ctx = trace.set_span_in_context(span)
34-
token = context.attach(ctx)
35-
flask.g.token = token
36-
37-
extract_custom_headers(span, env, format=True)
38-
39-
span.set_attribute(SpanAttributes.HTTP_METHOD, flask.request.method)
40-
if "PATH_INFO" in env:
41-
span.set_attribute(SpanAttributes.HTTP_URL, env["PATH_INFO"])
42-
if "QUERY_STRING" in env and len(env["QUERY_STRING"]):
43-
scrubbed_params = strip_secrets_from_query(
44-
env["QUERY_STRING"],
45-
agent.options.secrets_matcher,
46-
agent.options.secrets_list,
47-
)
48-
span.set_attribute("http.params", scrubbed_params)
49-
if "HTTP_HOST" in env:
50-
span.set_attribute("http.host", env["HTTP_HOST"])
51-
52-
if hasattr(flask.request.url_rule, "rule") and path_tpl_re.search(
53-
flask.request.url_rule.rule
54-
):
55-
path_tpl = flask.request.url_rule.rule.replace("<", "{")
56-
path_tpl = path_tpl.replace(">", "}")
57-
span.set_attribute("http.path_tpl", path_tpl)
22+
create_span()
5823
except Exception:
5924
logger.debug("Flask request_started_with_instana", exc_info=True)
6025

6126

6227
def request_finished_with_instana(
6328
sender: flask.app.Flask, response: flask.wrappers.Response, **extra: Any
6429
) -> None:
65-
span = None
66-
try:
67-
if not hasattr(flask.g, "span"):
68-
return
69-
70-
span = flask.g.span
71-
if span:
72-
if 500 <= response.status_code:
73-
span.mark_as_errored()
74-
75-
span.set_attribute(
76-
SpanAttributes.HTTP_STATUS_CODE, int(response.status_code)
77-
)
78-
extract_custom_headers(span, response.headers, format=False)
79-
tracer = get_tracer()
80-
tracer.inject(span.context, Format.HTTP_HEADERS, response.headers)
81-
except Exception:
82-
logger.debug("Flask request_finished_with_instana", exc_info=True)
83-
finally:
84-
if span and span.is_recording():
85-
span.end()
30+
inject_span(response, "Flask request_finished_with_instana")
8631

8732

8833
def log_exception_with_instana(
@@ -102,26 +47,6 @@ def log_exception_with_instana(
10247
span.end()
10348

10449

105-
def teardown_request_with_instana(*argv: Any, **kwargs: Any) -> None:
106-
"""
107-
In the case of exceptions, request_finished_with_instana isn't called
108-
so we capture those cases here.
109-
"""
110-
if hasattr(flask.g, "span") and flask.g.span:
111-
if len(argv) > 0 and argv[0]:
112-
span = flask.g.span
113-
span.record_exception(argv[0])
114-
if SpanAttributes.HTTP_STATUS_CODE not in span.attributes:
115-
span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, 500)
116-
if flask.g.span.is_recording():
117-
flask.g.span.end()
118-
flask.g.span = None
119-
120-
if hasattr(flask.g, "token") and flask.g.token:
121-
context.detach(flask.g.token)
122-
flask.g.token = None
123-
124-
12550
@wrapt.patch_function_wrapper("flask", "Flask.full_dispatch_request")
12651
def full_dispatch_request_with_instana(
12752
wrapped: Callable[..., flask.wrappers.Response],

0 commit comments

Comments
 (0)