Skip to content

Commit da7d8f8

Browse files
Add FlaskInstrumentor custom attrs tests
1 parent 570e994 commit da7d8f8

File tree

1 file changed

+189
-0
lines changed

1 file changed

+189
-0
lines changed
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from timeit import default_timer
16+
from unittest.mock import patch
17+
18+
import flask
19+
20+
from opentelemetry.instrumentation._labeler import (
21+
clear_labeler,
22+
get_labeler,
23+
)
24+
from opentelemetry.instrumentation._semconv import (
25+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
26+
OTEL_SEMCONV_STABILITY_OPT_IN,
27+
_OpenTelemetrySemanticConventionStability,
28+
_server_active_requests_count_attrs_new,
29+
_server_active_requests_count_attrs_old,
30+
_server_duration_attrs_new,
31+
_server_duration_attrs_old,
32+
)
33+
from opentelemetry.instrumentation.flask import FlaskInstrumentor
34+
from opentelemetry.sdk.metrics.export import (
35+
HistogramDataPoint,
36+
NumberDataPoint,
37+
)
38+
from opentelemetry.test.wsgitestutil import WsgiTestBase
39+
40+
# pylint: disable=import-error
41+
from .base_test import InstrumentationTest
42+
43+
44+
_expected_metric_names_old = [
45+
"http.server.active_requests",
46+
"http.server.duration",
47+
]
48+
_expected_metric_names_new = [
49+
"http.server.active_requests",
50+
"http.server.request.duration",
51+
]
52+
53+
_custom_attributes = [
54+
"custom_attr", "endpoint_type", "feature_flag"
55+
]
56+
57+
_server_duration_attrs_old_with_custom = _server_duration_attrs_old.copy()
58+
_server_duration_attrs_old_with_custom.append("http.target")
59+
_server_duration_attrs_old_with_custom.extend(_custom_attributes)
60+
_server_duration_attrs_new_with_custom = _server_duration_attrs_new.copy()
61+
_server_duration_attrs_new_with_custom.append("http.route")
62+
_server_duration_attrs_new_with_custom.extend(_custom_attributes)
63+
64+
_recommended_metrics_attrs_old_with_custom = {
65+
"http.server.active_requests": _server_active_requests_count_attrs_old,
66+
"http.server.duration": _server_duration_attrs_old_with_custom,
67+
}
68+
_recommended_metrics_attrs_new_with_custom = {
69+
"http.server.active_requests": _server_active_requests_count_attrs_new,
70+
"http.server.request.duration": _server_duration_attrs_new_with_custom,
71+
}
72+
73+
74+
class TestFlaskLabeler(InstrumentationTest, WsgiTestBase):
75+
def setUp(self):
76+
super().setUp()
77+
78+
test_name = ""
79+
if hasattr(self, "_testMethodName"):
80+
test_name = self._testMethodName
81+
sem_conv_mode = "default"
82+
if "new_semconv" in test_name:
83+
sem_conv_mode = "http"
84+
self.env_patch = patch.dict(
85+
"os.environ",
86+
{
87+
OTEL_SEMCONV_STABILITY_OPT_IN: sem_conv_mode,
88+
},
89+
)
90+
_OpenTelemetrySemanticConventionStability._initialized = False
91+
self.env_patch.start()
92+
93+
clear_labeler()
94+
self.app = flask.Flask(__name__)
95+
96+
@self.app.route("/test_labeler")
97+
def test_labeler_route():
98+
labeler = get_labeler()
99+
labeler.add("custom_attr", "test_value")
100+
labeler.add_attributes({
101+
"endpoint_type": "test",
102+
"feature_flag": True
103+
})
104+
return "OK"
105+
106+
@self.app.route("/no_labeler")
107+
def test_no_labeler_route():
108+
return "No labeler"
109+
110+
FlaskInstrumentor().instrument_app(self.app)
111+
self._common_initialization()
112+
113+
def tearDown(self):
114+
super().tearDown()
115+
clear_labeler()
116+
with self.disable_logging():
117+
FlaskInstrumentor().uninstrument_app(self.app)
118+
119+
def test_flask_metrics_custom_attributes(self):
120+
start = default_timer()
121+
self.client.get("/test_labeler")
122+
self.client.get("/test_labeler")
123+
self.client.get("/test_labeler")
124+
duration = max(round((default_timer() - start) * 1000), 0)
125+
metrics_list = self.memory_metrics_reader.get_metrics_data()
126+
number_data_point_seen = False
127+
histogram_data_point_seen = False
128+
self.assertTrue(len(metrics_list.resource_metrics) != 0)
129+
for resource_metric in metrics_list.resource_metrics:
130+
self.assertTrue(len(resource_metric.scope_metrics) != 0)
131+
for scope_metric in resource_metric.scope_metrics:
132+
self.assertTrue(len(scope_metric.metrics) != 0)
133+
for metric in scope_metric.metrics:
134+
self.assertIn(metric.name, _expected_metric_names_old)
135+
data_points = list(metric.data.data_points)
136+
self.assertEqual(len(data_points), 1)
137+
for point in data_points:
138+
if isinstance(point, HistogramDataPoint):
139+
self.assertEqual(point.count, 3)
140+
self.assertAlmostEqual(
141+
duration, point.sum, delta=10
142+
)
143+
histogram_data_point_seen = True
144+
if isinstance(point, NumberDataPoint):
145+
number_data_point_seen = True
146+
for attr in point.attributes:
147+
self.assertIn(
148+
attr,
149+
_recommended_metrics_attrs_old_with_custom[metric.name],
150+
)
151+
self.assertTrue(number_data_point_seen and histogram_data_point_seen)
152+
153+
def test_flask_metrics_custom_attributes_new_semconv(self):
154+
start = default_timer()
155+
self.client.get("/test_labeler")
156+
self.client.get("/test_labeler")
157+
self.client.get("/test_labeler")
158+
duration_s = max(default_timer() - start, 0)
159+
metrics_list = self.memory_metrics_reader.get_metrics_data()
160+
number_data_point_seen = False
161+
histogram_data_point_seen = False
162+
self.assertTrue(len(metrics_list.resource_metrics) != 0)
163+
for resource_metric in metrics_list.resource_metrics:
164+
self.assertTrue(len(resource_metric.scope_metrics) != 0)
165+
for scope_metric in resource_metric.scope_metrics:
166+
self.assertTrue(len(scope_metric.metrics) != 0)
167+
for metric in scope_metric.metrics:
168+
self.assertIn(metric.name, _expected_metric_names_new)
169+
data_points = list(metric.data.data_points)
170+
self.assertEqual(len(data_points), 1)
171+
for point in data_points:
172+
if isinstance(point, HistogramDataPoint):
173+
self.assertEqual(point.count, 3)
174+
self.assertAlmostEqual(
175+
duration_s, point.sum, places=1
176+
)
177+
self.assertEqual(
178+
point.explicit_bounds,
179+
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
180+
)
181+
histogram_data_point_seen = True
182+
if isinstance(point, NumberDataPoint):
183+
number_data_point_seen = True
184+
for attr in point.attributes:
185+
self.assertIn(
186+
attr,
187+
_recommended_metrics_attrs_new_with_custom[metric.name],
188+
)
189+
self.assertTrue(number_data_point_seen and histogram_data_point_seen)

0 commit comments

Comments
 (0)