Skip to content

Commit d2223d9

Browse files
committed
Fix after CR and change the test format
1 parent d70acc2 commit d2223d9

File tree

3 files changed

+149
-95
lines changed

3 files changed

+149
-95
lines changed

instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/__init__.py

+66-35
Original file line numberDiff line numberDiff line change
@@ -292,14 +292,14 @@ def _uninstrument(self, **kwargs):
292292
self.patched_handlers = []
293293

294294

295-
def patch_handler_class(self, cls, request_hook=None):
295+
def patch_handler_class(instrumentation, cls, request_hook=None):
296296
if getattr(cls, _OTEL_PATCHED_KEY, False):
297297
return False
298298

299299
setattr(cls, _OTEL_PATCHED_KEY, True)
300-
_wrap(cls, "prepare", partial(_prepare, self, request_hook))
301-
_wrap(cls, "on_finish", partial(_on_finish, self))
302-
_wrap(cls, "log_exception", partial(_log_exception, self))
300+
_wrap(cls, "prepare", partial(_prepare, instrumentation, request_hook))
301+
_wrap(cls, "on_finish", partial(_on_finish, instrumentation))
302+
_wrap(cls, "log_exception", partial(_log_exception, instrumentation))
303303
return True
304304

305305

@@ -319,28 +319,36 @@ def _wrap(cls, method_name, wrapper):
319319
wrapt.apply_patch(cls, method_name, wrapper)
320320

321321

322-
def _prepare(self, request_hook, func, handler, args, kwargs):
322+
def _prepare(instrumentation, request_hook, func, handler, args, kwargs):
323+
instrumentation.start_time = default_timer()
323324
request = handler.request
324325
if _excluded_urls.url_disabled(request.uri):
325326
return func(*args, **kwargs)
326-
ctx = _start_span(self, handler)
327+
328+
_record_prepare_metrics(instrumentation, handler)
329+
330+
ctx = _start_span(instrumentation.tracer, handler)
327331
if request_hook:
328332
request_hook(ctx.span, handler)
329333
return func(*args, **kwargs)
330334

331335

332-
def _on_finish(self, func, handler, args, kwargs):
336+
def _on_finish(instrumentation, func, handler, args, kwargs):
333337
response = func(*args, **kwargs)
334-
_finish_span(self, handler)
338+
339+
_record_on_finish_metrics(instrumentation, handler)
340+
341+
_finish_span(instrumentation.tracer, handler)
342+
335343
return response
336344

337345

338-
def _log_exception(self, func, handler, args, kwargs):
346+
def _log_exception(instrumentation, func, handler, args, kwargs):
339347
error = None
340348
if len(args) == 3:
341349
error = args[1]
342350

343-
_finish_span(self, handler, error)
351+
_finish_span(instrumentation, handler, error)
344352
return func(*args, **kwargs)
345353

346354

@@ -406,11 +414,9 @@ def _get_full_handler_name(handler):
406414
return f"{klass.__module__}.{klass.__qualname__}"
407415

408416

409-
def _start_span(self, handler) -> _TraceContext:
410-
start_time = default_timer()
411-
417+
def _start_span(tracer, handler) -> _TraceContext:
412418
span, token = _start_internal_or_server_span(
413-
tracer=self.tracer,
419+
tracer=tracer,
414420
span_name=_get_operation_name(handler, handler.request),
415421
start_time=time_ns(),
416422
context_carrier=handler.request.headers,
@@ -429,14 +435,6 @@ def _start_span(self, handler) -> _TraceContext:
429435
if len(custom_attributes) > 0:
430436
span.set_attributes(custom_attributes)
431437

432-
metric_attributes = _create_metric_attributes(handler)
433-
request_size = len(handler.request.body)
434-
435-
self.request_size_histogram.record(
436-
request_size, attributes=metric_attributes
437-
)
438-
self.active_requests_histogram.add(1, attributes=metric_attributes)
439-
440438
activation = trace.use_span(span, end_on_exit=True)
441439
activation.__enter__() # pylint: disable=E1101
442440
ctx = _TraceContext(activation, span, token)
@@ -449,15 +447,10 @@ def _start_span(self, handler) -> _TraceContext:
449447
if propagator:
450448
propagator.inject(handler, setter=response_propagation_setter)
451449

452-
elapsed_time = round((default_timer() - start_time) * 1000)
453-
454-
self.duration_histogram.record(elapsed_time, attributes=metric_attributes)
455-
self.active_requests_histogram.add(-1, attributes=metric_attributes)
456-
457450
return ctx
458451

459452

460-
def _finish_span(self, handler, error=None):
453+
def _finish_span(tracer, handler, error=None):
461454
status_code = handler.get_status()
462455
reason = getattr(handler, "_reason")
463456
finish_args = (None, None, None)
@@ -467,7 +460,7 @@ def _finish_span(self, handler, error=None):
467460
if isinstance(error, tornado.web.HTTPError):
468461
status_code = error.status_code
469462
if not ctx and status_code == 404:
470-
ctx = _start_span(self, handler)
463+
ctx = _start_span(tracer, handler)
471464
else:
472465
status_code = 500
473466
reason = None
@@ -508,13 +501,51 @@ def _finish_span(self, handler, error=None):
508501
delattr(handler, _HANDLER_CONTEXT_KEY)
509502

510503

511-
def _create_metric_attributes(handler):
504+
def _record_prepare_metrics(instrumentation, handler):
505+
request_size = len(handler.request.body)
506+
metric_attributes = _create_metric_attributes(handler)
507+
508+
instrumentation.request_size_histogram.record(
509+
request_size, attributes=metric_attributes
510+
)
511+
512+
active_requests_attributes = _create_active_requests_attributes(
513+
handler.request
514+
)
515+
instrumentation.active_requests_histogram.add(
516+
1, attributes=active_requests_attributes
517+
)
518+
519+
520+
def _record_on_finish_metrics(instrumentation, handler):
521+
elapsed_time = round((default_timer() - instrumentation.start_time) * 1000)
522+
523+
metric_attributes = _create_metric_attributes(handler)
524+
instrumentation.duration_histogram.record(
525+
elapsed_time, attributes=metric_attributes
526+
)
527+
528+
active_requests_attributes = _create_active_requests_attributes(
529+
handler.request
530+
)
531+
instrumentation.active_requests_histogram.add(
532+
-1, attributes=active_requests_attributes
533+
)
534+
535+
536+
def _create_active_requests_attributes(request):
512537
metric_attributes = {
513-
SpanAttributes.HTTP_METHOD: handler.request.method,
514-
SpanAttributes.HTTP_SCHEME: handler.request.protocol,
515-
SpanAttributes.HTTP_STATUS_CODE: handler.get_status(),
516-
SpanAttributes.HTTP_FLAVOR: handler.request.version,
517-
SpanAttributes.HTTP_HOST: handler.request.host,
538+
SpanAttributes.HTTP_METHOD: request.method,
539+
SpanAttributes.HTTP_SCHEME: request.protocol,
540+
SpanAttributes.HTTP_FLAVOR: request.version,
541+
SpanAttributes.HTTP_HOST: request.host,
518542
}
519543

520544
return metric_attributes
545+
546+
547+
def _create_metric_attributes(handler):
548+
metric_attributes = _create_active_requests_attributes(handler.request)
549+
metric_attributes[SpanAttributes.HTTP_STATUS_CODE] = handler.get_status()
550+
551+
return metric_attributes

instrumentation/opentelemetry-instrumentation-tornado/src/opentelemetry/instrumentation/tornado/client.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,13 @@ def _finish_tracing_callback(
9999
status_code = None
100100
description = None
101101
exc = future.exception()
102+
response = future.result()
102103
if span.is_recording() and exc:
103104
if isinstance(exc, HTTPError):
104105
status_code = exc.code
105106
description = f"{type(exc).__name__}: {exc}"
106107
else:
107-
status_code = future.result().code
108+
status_code = response.code
108109

109110
if status_code is not None:
110111
span.set_attribute(SpanAttributes.HTTP_STATUS_CODE, status_code)
@@ -114,7 +115,6 @@ def _finish_tracing_callback(
114115
description=description,
115116
)
116117
)
117-
response = future.result()
118118
metric_attributes = _create_metric_attributes(response)
119119
response_size = int(response.headers["Content-Length"])
120120

instrumentation/opentelemetry-instrumentation-tornado/tests/test_metrics_instrumentation.py

+81-58
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from opentelemetry import trace
2121
from opentelemetry.instrumentation.tornado import TornadoInstrumentor
2222
from opentelemetry.test.test_base import TestBase
23+
from opentelemetry.sdk.metrics.export import HistogramDataPoint
2324

2425
from .tornado_test_app import make_app
2526

@@ -31,6 +32,32 @@ def get_app(self):
3132
app = make_app(tracer)
3233
return app
3334

35+
def get_sorted_metrics(self):
36+
resource_metrics = (
37+
self.memory_metrics_reader.get_metrics_data().resource_metrics
38+
)
39+
for metrics in resource_metrics:
40+
for scope_metrics in metrics.scope_metrics:
41+
all_metrics = [metric for metric in scope_metrics.metrics]
42+
return self.sorted_metrics(all_metrics)
43+
44+
@staticmethod
45+
def sorted_metrics(metrics):
46+
"""
47+
Sorts metrics by metric name.
48+
"""
49+
return sorted(
50+
metrics,
51+
key=lambda m: m.name,
52+
)
53+
54+
def assertMetricHasAttributes(self, metric, expected_attributes):
55+
for data_point in metric.data.data_points:
56+
self.assertDictEqual(
57+
expected_attributes,
58+
dict(data_point.attributes),
59+
)
60+
3461
def setUp(self):
3562
super().setUp()
3663
TornadoInstrumentor().instrument(
@@ -51,64 +78,60 @@ def test_basic_metrics(self):
5178
response = self.fetch("/")
5279
client_duration_estimated = (default_timer() - start_time) * 1000
5380

54-
expected_attributes = {
55-
"http.status_code": 200,
56-
"http.method": "GET",
57-
"http.flavor": "HTTP/1.1",
58-
"http.scheme": "http",
59-
"http.host": response.request.headers["host"],
60-
}
61-
expected_response_attributes = {
62-
"http.status_code": response.code,
63-
"http.method": "GET",
64-
"http.url": self.get_url("/"),
65-
}
66-
expected_data = {
67-
"http.server.request.size": 0,
68-
"http.server.response.size": int(
69-
response.headers["Content-Length"]
70-
),
71-
}
72-
expected_metrics = [
73-
"http.server.duration",
74-
"http.server.request.size",
75-
"http.server.response.size",
76-
"http.server.active_requests",
77-
]
81+
metrics = self.get_sorted_metrics()
82+
self.assertEqual(len(metrics), 4)
83+
active_request, duration, request_size, response_size = metrics
84+
85+
self.assertEqual(active_request.name, "http.server.active_requests")
86+
self.assertMetricHasAttributes(
87+
active_request,
88+
{
89+
"http.method": "GET",
90+
"http.flavor": "HTTP/1.1",
91+
"http.scheme": "http",
92+
"http.host": response.request.headers["host"],
93+
},
94+
)
7895

79-
resource_metrics = (
80-
self.memory_metrics_reader.get_metrics_data().resource_metrics
96+
self.assertEqual(duration.name, "http.server.duration")
97+
for data_point in duration.data.data_points:
98+
self.assertAlmostEqual(
99+
data_point.sum,
100+
client_duration_estimated,
101+
delta=200,
102+
)
103+
self.assertMetricHasAttributes(
104+
duration,
105+
{
106+
"http.status_code": 201,
107+
"http.method": "GET",
108+
"http.flavor": "HTTP/1.1",
109+
"http.scheme": "http",
110+
"http.host": response.request.headers["host"],
111+
},
112+
)
113+
114+
self.assertEqual(request_size.name, "http.server.request.size")
115+
self.assertMetricHasAttributes(
116+
request_size,
117+
{
118+
"http.status_code": 200,
119+
"http.method": "GET",
120+
"http.flavor": "HTTP/1.1",
121+
"http.scheme": "http",
122+
"http.host": response.request.headers["host"],
123+
},
124+
)
125+
126+
self.assertEqual(response_size.name, "http.server.response.size")
127+
self.assertMetricHasAttributes(
128+
response_size,
129+
{
130+
"http.status_code": response.code,
131+
"http.method": "GET",
132+
"http.url": self.get_url("/"),
133+
},
81134
)
82-
for metrics in resource_metrics:
83-
for scope_metrics in metrics.scope_metrics:
84-
self.assertEqual(
85-
len(scope_metrics.metrics), len(expected_metrics)
86-
)
87-
for metric in scope_metrics.metrics:
88-
for data_point in metric.data.data_points:
89-
if metric.name in expected_data:
90-
self.assertEqual(
91-
data_point.sum, expected_data[metric.name]
92-
)
93-
94-
self.assertIn(metric.name, expected_metrics)
95-
if metric.name == "http.server.duration":
96-
self.assertAlmostEqual(
97-
data_point.sum,
98-
client_duration_estimated,
99-
delta=1000,
100-
)
101-
102-
if metric.name == "http.server.response.size":
103-
self.assertDictEqual(
104-
expected_response_attributes,
105-
dict(data_point.attributes),
106-
)
107-
else:
108-
self.assertDictEqual(
109-
expected_attributes,
110-
dict(data_point.attributes),
111-
)
112135

113136
def test_metric_uninstrument(self):
114137
self.fetch("/")
@@ -119,6 +142,6 @@ def test_metric_uninstrument(self):
119142
for resource_metric in metrics_list.resource_metrics:
120143
for scope_metric in resource_metric.scope_metrics:
121144
for metric in scope_metric.metrics:
122-
if metric.name != "http.server.active_requests":
123-
for point in list(metric.data.data_points):
145+
for point in list(metric.data.data_points):
146+
if isinstance(point, HistogramDataPoint):
124147
self.assertEqual(point.count, 1)

0 commit comments

Comments
 (0)