opentelemetry-instrumentation-tornado 0.57b0__tar.gz → 0.58b0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {opentelemetry_instrumentation_tornado-0.57b0 → opentelemetry_instrumentation_tornado-0.58b0}/PKG-INFO +4 -4
- {opentelemetry_instrumentation_tornado-0.57b0 → opentelemetry_instrumentation_tornado-0.58b0}/pyproject.toml +3 -3
- {opentelemetry_instrumentation_tornado-0.57b0 → opentelemetry_instrumentation_tornado-0.58b0}/src/opentelemetry/instrumentation/tornado/__init__.py +13 -6
- {opentelemetry_instrumentation_tornado-0.57b0 → opentelemetry_instrumentation_tornado-0.58b0}/src/opentelemetry/instrumentation/tornado/version.py +1 -1
- {opentelemetry_instrumentation_tornado-0.57b0 → opentelemetry_instrumentation_tornado-0.58b0}/tests/test_metrics_instrumentation.py +88 -0
- {opentelemetry_instrumentation_tornado-0.57b0 → opentelemetry_instrumentation_tornado-0.58b0}/tests/tornado_test_app.py +9 -0
- {opentelemetry_instrumentation_tornado-0.57b0 → opentelemetry_instrumentation_tornado-0.58b0}/.gitignore +0 -0
- {opentelemetry_instrumentation_tornado-0.57b0 → opentelemetry_instrumentation_tornado-0.58b0}/LICENSE +0 -0
- {opentelemetry_instrumentation_tornado-0.57b0 → opentelemetry_instrumentation_tornado-0.58b0}/README.rst +0 -0
- {opentelemetry_instrumentation_tornado-0.57b0 → opentelemetry_instrumentation_tornado-0.58b0}/src/opentelemetry/instrumentation/tornado/client.py +0 -0
- {opentelemetry_instrumentation_tornado-0.57b0 → opentelemetry_instrumentation_tornado-0.58b0}/src/opentelemetry/instrumentation/tornado/package.py +0 -0
- {opentelemetry_instrumentation_tornado-0.57b0 → opentelemetry_instrumentation_tornado-0.58b0}/tests/__init__.py +0 -0
- {opentelemetry_instrumentation_tornado-0.57b0 → opentelemetry_instrumentation_tornado-0.58b0}/tests/test_instrumentation.py +0 -0
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: opentelemetry-instrumentation-tornado
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.58b0
|
4
4
|
Summary: Tornado instrumentation for OpenTelemetry
|
5
5
|
Project-URL: Homepage, https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-tornado
|
6
6
|
Project-URL: Repository, https://github.com/open-telemetry/opentelemetry-python-contrib
|
@@ -18,9 +18,9 @@ Classifier: Programming Language :: Python :: 3.12
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.13
|
19
19
|
Requires-Python: >=3.9
|
20
20
|
Requires-Dist: opentelemetry-api~=1.12
|
21
|
-
Requires-Dist: opentelemetry-instrumentation==0.
|
22
|
-
Requires-Dist: opentelemetry-semantic-conventions==0.
|
23
|
-
Requires-Dist: opentelemetry-util-http==0.
|
21
|
+
Requires-Dist: opentelemetry-instrumentation==0.58b0
|
22
|
+
Requires-Dist: opentelemetry-semantic-conventions==0.58b0
|
23
|
+
Requires-Dist: opentelemetry-util-http==0.58b0
|
24
24
|
Provides-Extra: instruments
|
25
25
|
Requires-Dist: tornado>=5.1.1; extra == 'instruments'
|
26
26
|
Description-Content-Type: text/x-rst
|
@@ -25,9 +25,9 @@ classifiers = [
|
|
25
25
|
]
|
26
26
|
dependencies = [
|
27
27
|
"opentelemetry-api ~= 1.12",
|
28
|
-
"opentelemetry-instrumentation == 0.
|
29
|
-
"opentelemetry-semantic-conventions == 0.
|
30
|
-
"opentelemetry-util-http == 0.
|
28
|
+
"opentelemetry-instrumentation == 0.58b0",
|
29
|
+
"opentelemetry-semantic-conventions == 0.58b0",
|
30
|
+
"opentelemetry-util-http == 0.58b0",
|
31
31
|
]
|
32
32
|
|
33
33
|
[project.optional-dependencies]
|
@@ -211,6 +211,7 @@ from .client import fetch_async # pylint: disable=E0401
|
|
211
211
|
|
212
212
|
_logger = getLogger(__name__)
|
213
213
|
_TraceContext = namedtuple("TraceContext", ["activation", "span", "token"])
|
214
|
+
_HANDLER_STATE_KEY = "_otel_state_key"
|
214
215
|
_HANDLER_CONTEXT_KEY = "_otel_trace_context_key"
|
215
216
|
_OTEL_PATCHED_KEY = "_otel_patched_key"
|
216
217
|
|
@@ -402,10 +403,14 @@ def _wrap(cls, method_name, wrapper):
|
|
402
403
|
def _prepare(
|
403
404
|
tracer, server_histograms, request_hook, func, handler, args, kwargs
|
404
405
|
):
|
405
|
-
server_histograms[_START_TIME] = default_timer()
|
406
|
-
|
407
406
|
request = handler.request
|
408
|
-
|
407
|
+
otel_handler_state = {
|
408
|
+
_START_TIME: default_timer(),
|
409
|
+
"exclude_request": _excluded_urls.url_disabled(request.uri),
|
410
|
+
}
|
411
|
+
setattr(handler, _HANDLER_STATE_KEY, otel_handler_state)
|
412
|
+
|
413
|
+
if otel_handler_state["exclude_request"]:
|
409
414
|
return func(*args, **kwargs)
|
410
415
|
|
411
416
|
_record_prepare_metrics(server_histograms, handler)
|
@@ -622,9 +627,11 @@ def _record_prepare_metrics(server_histograms, handler):
|
|
622
627
|
|
623
628
|
|
624
629
|
def _record_on_finish_metrics(server_histograms, handler, error=None):
|
625
|
-
|
626
|
-
|
627
|
-
|
630
|
+
otel_handler_state = getattr(handler, _HANDLER_STATE_KEY, None) or {}
|
631
|
+
if otel_handler_state.get("exclude_request"):
|
632
|
+
return
|
633
|
+
start_time = otel_handler_state.get(_START_TIME, None) or default_timer()
|
634
|
+
elapsed_time = round((default_timer() - start_time) * 1000)
|
628
635
|
|
629
636
|
response_size = int(handler._headers.get("Content-Length", 0))
|
630
637
|
metric_attributes = _create_metric_attributes(handler)
|
@@ -13,8 +13,11 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
|
15
15
|
|
16
|
+
import asyncio
|
16
17
|
from timeit import default_timer
|
17
18
|
|
19
|
+
import tornado.testing
|
20
|
+
|
18
21
|
from opentelemetry.instrumentation.tornado import TornadoInstrumentor
|
19
22
|
from opentelemetry.sdk.metrics.export import HistogramDataPoint
|
20
23
|
|
@@ -165,6 +168,78 @@ class TestTornadoMetricsInstrumentation(TornadoTest):
|
|
165
168
|
),
|
166
169
|
)
|
167
170
|
|
171
|
+
@tornado.testing.gen_test
|
172
|
+
async def test_metrics_concurrent_requests(self):
|
173
|
+
"""
|
174
|
+
Test that metrics can handle concurrent requests and calculate in an async-safe way.
|
175
|
+
"""
|
176
|
+
req1 = self.http_client.fetch(self.get_url("/slow?duration=1.0"))
|
177
|
+
req2 = self.http_client.fetch(self.get_url("/async"))
|
178
|
+
await asyncio.gather(req1, req2)
|
179
|
+
|
180
|
+
metrics = self.get_sorted_metrics()
|
181
|
+
self.assertEqual(len(metrics), 7)
|
182
|
+
|
183
|
+
client_duration = metrics[0]
|
184
|
+
server_duration = metrics[4]
|
185
|
+
self.assertEqual(client_duration.name, "http.client.duration")
|
186
|
+
self.assertEqual(server_duration.name, "http.server.duration")
|
187
|
+
|
188
|
+
# Calculating duration requires tracking state via `_HANDLER_STATE_KEY`, so we want to make sure
|
189
|
+
# duration is calculated properly per request, and doesn't affect concurrent requests.
|
190
|
+
req1_client_duration_data_point = next(
|
191
|
+
dp
|
192
|
+
for dp in client_duration.data.data_points
|
193
|
+
if "/slow" in dp.attributes.get("http.url")
|
194
|
+
)
|
195
|
+
req1_server_duration_data_point = next(
|
196
|
+
dp
|
197
|
+
for dp in server_duration.data.data_points
|
198
|
+
if "/slow" in dp.attributes.get("http.target")
|
199
|
+
)
|
200
|
+
req2_client_duration_data_point = next(
|
201
|
+
dp
|
202
|
+
for dp in client_duration.data.data_points
|
203
|
+
if "/async" in dp.attributes.get("http.url")
|
204
|
+
)
|
205
|
+
req2_server_duration_data_point = next(
|
206
|
+
dp
|
207
|
+
for dp in server_duration.data.data_points
|
208
|
+
if "/async" in dp.attributes.get("http.target")
|
209
|
+
)
|
210
|
+
|
211
|
+
# Server and client durations should be similar (adjusting for msecs vs secs)
|
212
|
+
self.assertAlmostEqual(
|
213
|
+
abs(
|
214
|
+
req1_server_duration_data_point.sum / 1000.0
|
215
|
+
- req1_client_duration_data_point.sum
|
216
|
+
),
|
217
|
+
0.0,
|
218
|
+
delta=0.01,
|
219
|
+
)
|
220
|
+
self.assertAlmostEqual(
|
221
|
+
abs(
|
222
|
+
req2_server_duration_data_point.sum / 1000.0
|
223
|
+
- req2_client_duration_data_point.sum
|
224
|
+
),
|
225
|
+
0.0,
|
226
|
+
delta=0.01,
|
227
|
+
)
|
228
|
+
|
229
|
+
# Make sure duration is roughly equivalent to expected (req1/slow) should be around 1 second
|
230
|
+
self.assertAlmostEqual(
|
231
|
+
req1_server_duration_data_point.sum / 1000.0,
|
232
|
+
1.0,
|
233
|
+
delta=0.1,
|
234
|
+
msg="Should have been about 1 second",
|
235
|
+
)
|
236
|
+
self.assertAlmostEqual(
|
237
|
+
req2_server_duration_data_point.sum / 1000.0,
|
238
|
+
0.0,
|
239
|
+
delta=0.1,
|
240
|
+
msg="Should have been really short",
|
241
|
+
)
|
242
|
+
|
168
243
|
def test_metric_uninstrument(self):
|
169
244
|
self.fetch("/")
|
170
245
|
TornadoInstrumentor().uninstrument()
|
@@ -177,3 +252,16 @@ class TestTornadoMetricsInstrumentation(TornadoTest):
|
|
177
252
|
for point in list(metric.data.data_points):
|
178
253
|
if isinstance(point, HistogramDataPoint):
|
179
254
|
self.assertEqual(point.count, 1)
|
255
|
+
|
256
|
+
def test_exclude_lists(self):
|
257
|
+
def test_excluded(path):
|
258
|
+
self.fetch(path)
|
259
|
+
|
260
|
+
# Verify no server metrics written (only client ones should exist)
|
261
|
+
metrics = self.get_sorted_metrics()
|
262
|
+
for metric in metrics:
|
263
|
+
self.assertTrue("http.server" not in metric.name, metric)
|
264
|
+
self.assertEqual(len(metrics), 3, metrics)
|
265
|
+
|
266
|
+
test_excluded("/healthz")
|
267
|
+
test_excluded("/ping")
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# pylint: disable=W0223,R0201
|
2
|
+
import asyncio
|
2
3
|
import time
|
3
4
|
|
4
5
|
import tornado.web
|
@@ -106,6 +107,13 @@ class CustomResponseHeaderHandler(tornado.web.RequestHandler):
|
|
106
107
|
self.set_status(200)
|
107
108
|
|
108
109
|
|
110
|
+
class SlowHandler(tornado.web.RequestHandler):
|
111
|
+
async def get(self):
|
112
|
+
with self.application.tracer.start_as_current_span("slow"):
|
113
|
+
duration = float(self.get_argument("duration", "1.0"))
|
114
|
+
await asyncio.sleep(duration)
|
115
|
+
|
116
|
+
|
109
117
|
class RaiseHTTPErrorHandler(tornado.web.RequestHandler):
|
110
118
|
def get(self):
|
111
119
|
raise tornado.web.HTTPError(403)
|
@@ -133,6 +141,7 @@ def make_app(tracer):
|
|
133
141
|
(r"/ping", HealthCheckHandler),
|
134
142
|
(r"/test_custom_response_headers", CustomResponseHeaderHandler),
|
135
143
|
(r"/raise_403", RaiseHTTPErrorHandler),
|
144
|
+
(r"/slow", SlowHandler),
|
136
145
|
(r"/echo_socket", EchoWebSocketHandler),
|
137
146
|
]
|
138
147
|
)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|