Skip to content

Commit 3ab38f0

Browse files
author
sroda
committed
Add client instrumentation
1 parent 340b296 commit 3ab38f0

File tree

3 files changed

+165
-36
lines changed

3 files changed

+165
-36
lines changed

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

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -236,29 +236,45 @@ def _instrument(self, **kwargs):
236236
self.tracer = trace.get_tracer(__name__, __version__, tracer_provider)
237237

238238
meter_provider = kwargs.get("meter_provider")
239-
self.meter = get_meter(__name__, __version__, meter_provider)
239+
meter = get_meter(__name__, __version__, meter_provider)
240240

241-
self.duration_histogram = self.meter.create_histogram(
241+
self.duration_histogram = meter.create_histogram(
242242
name="http.server.duration",
243243
unit="ms",
244244
description="measures the duration outbound HTTP requests",
245245
)
246-
self.request_size_histogram = self.meter.create_histogram(
246+
self.request_size_histogram = meter.create_histogram(
247247
name="http.server.request.size",
248248
unit="By",
249249
description="measures the size of HTTP request messages (compressed)",
250250
)
251-
self.response_size_histogram = self.meter.create_histogram(
251+
self.response_size_histogram = meter.create_histogram(
252252
name="http.server.response.size",
253253
unit="By",
254254
description="measures the size of HTTP response messages (compressed)",
255255
)
256-
self.active_requests_histogram = self.meter.create_up_down_counter(
256+
self.active_requests_histogram = meter.create_up_down_counter(
257257
name="http.server.active_requests",
258258
unit="requests",
259259
description="measures the number of concurrent HTTP requests that are currently in-flight",
260260
)
261261

262+
client_duration_histogram = meter.create_histogram(
263+
name="http.client.duration",
264+
unit="ms",
265+
description="measures the duration outbound HTTP requests",
266+
)
267+
client_request_size_histogram = meter.create_histogram(
268+
name="http.client.request.size",
269+
unit="By",
270+
description="measures the size of HTTP request messages (compressed)",
271+
)
272+
client_response_size_histogram = meter.create_histogram(
273+
name="http.client.response.size",
274+
unit="By",
275+
description="measures the size of HTTP response messages (compressed)",
276+
)
277+
262278
client_request_hook = kwargs.get("client_request_hook", None)
263279
client_response_hook = kwargs.get("client_response_hook", None)
264280
server_request_hook = kwargs.get("server_request_hook", None)
@@ -280,7 +296,9 @@ def handler_init(init, handler, args, kwargs):
280296
self.tracer,
281297
client_request_hook,
282298
client_response_hook,
283-
self.response_size_histogram,
299+
client_duration_histogram,
300+
client_request_size_histogram,
301+
client_response_size_histogram,
284302
),
285303
)
286304

@@ -520,7 +538,12 @@ def _record_prepare_metrics(instrumentation, handler):
520538
def _record_on_finish_metrics(instrumentation, handler):
521539
elapsed_time = round((default_timer() - instrumentation.start_time) * 1000)
522540

541+
response_size = int(handler._headers.get("Content-Length", 0))
523542
metric_attributes = _create_metric_attributes(handler)
543+
instrumentation.response_size_histogram.record(
544+
response_size, attributes=metric_attributes
545+
)
546+
524547
instrumentation.duration_histogram.record(
525548
elapsed_time, attributes=metric_attributes
526549
)
@@ -539,6 +562,7 @@ def _create_active_requests_attributes(request):
539562
SpanAttributes.HTTP_SCHEME: request.protocol,
540563
SpanAttributes.HTTP_FLAVOR: request.version,
541564
SpanAttributes.HTTP_HOST: request.host,
565+
SpanAttributes.HTTP_TARGET: request.path,
542566
}
543567

544568
return metric_attributes

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ def fetch_async(
4545
tracer,
4646
request_hook,
4747
response_hook,
48+
duration_histogram,
49+
request_size_histogram,
4850
response_size_histogram,
4951
func,
5052
_,
@@ -87,19 +89,28 @@ def fetch_async(
8789
_finish_tracing_callback,
8890
span=span,
8991
response_hook=response_hook,
92+
duration_histogram=duration_histogram,
93+
request_size_histogram=request_size_histogram,
9094
response_size_histogram=response_size_histogram,
9195
)
9296
)
9397
return future
9498

9599

96100
def _finish_tracing_callback(
97-
future, span, response_hook, response_size_histogram
101+
future,
102+
span,
103+
response_hook,
104+
duration_histogram,
105+
request_size_histogram,
106+
response_size_histogram,
98107
):
99108
status_code = None
100109
description = None
101110
exc = future.exception()
111+
102112
response = future.result()
113+
103114
if span.is_recording() and exc:
104115
if isinstance(exc, HTTPError):
105116
status_code = exc.code
@@ -115,9 +126,15 @@ def _finish_tracing_callback(
115126
description=description,
116127
)
117128
)
129+
118130
metric_attributes = _create_metric_attributes(response)
119-
response_size = int(response.headers["Content-Length"])
131+
request_size = int(response.request.headers.get("Content-Length", 0))
132+
response_size = int(response.headers.get("Content-Length", 0))
120133

134+
duration_histogram.record(
135+
response.request_time, attributes=metric_attributes
136+
)
137+
request_size_histogram.record(request_size, attributes=metric_attributes)
121138
response_size_histogram.record(response_size, attributes=metric_attributes)
122139

123140
if response_hook:

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

Lines changed: 116 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919

2020
from opentelemetry import trace
2121
from opentelemetry.instrumentation.tornado import TornadoInstrumentor
22-
from opentelemetry.sdk.metrics.export import HistogramDataPoint
22+
from opentelemetry.sdk.metrics.export import (
23+
HistogramDataPoint,
24+
NumberDataPoint,
25+
)
2326
from opentelemetry.test.test_base import TestBase
2427

2528
from .tornado_test_app import make_app
@@ -51,13 +54,43 @@ def sorted_metrics(metrics):
5154
key=lambda m: m.name,
5255
)
5356

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),
57+
def assertMetricExpected(
58+
self, metric, expected_value, expected_attributes
59+
):
60+
data_point = next(metric.data.data_points)
61+
62+
if isinstance(data_point, HistogramDataPoint):
63+
self.assertEqual(
64+
data_point.sum,
65+
expected_value,
66+
)
67+
elif isinstance(data_point, NumberDataPoint):
68+
self.assertEqual(
69+
data_point.value,
70+
expected_value,
5971
)
6072

73+
self.assertDictEqual(
74+
expected_attributes,
75+
dict(data_point.attributes),
76+
)
77+
78+
def assertDurationMetricExpected(
79+
self, metric, duration_estimated, expected_attributes
80+
):
81+
data_point = next(metric.data.data_points)
82+
83+
self.assertAlmostEqual(
84+
data_point.sum,
85+
duration_estimated,
86+
delta=200,
87+
)
88+
89+
self.assertDictEqual(
90+
expected_attributes,
91+
dict(data_point.attributes),
92+
)
93+
6194
def setUp(self):
6295
super().setUp()
6396
TornadoInstrumentor().instrument(
@@ -79,57 +112,112 @@ def test_basic_metrics(self):
79112
client_duration_estimated = (default_timer() - start_time) * 1000
80113

81114
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,
115+
self.assertEqual(len(metrics), 7)
116+
117+
(
118+
client_duration,
119+
client_request_size,
120+
client_response_size,
121+
) = metrics[:3]
122+
123+
(
124+
server_active_request,
125+
server_duration,
126+
server_request_size,
127+
server_response_size,
128+
) = metrics[3:]
129+
130+
self.assertEqual(
131+
server_active_request.name, "http.server.active_requests"
132+
)
133+
self.assertMetricExpected(
134+
server_active_request,
135+
0,
88136
{
89137
"http.method": "GET",
90138
"http.flavor": "HTTP/1.1",
91139
"http.scheme": "http",
140+
"http.target": "/",
92141
"http.host": response.request.headers["host"],
93142
},
94143
)
95144

96-
self.assertEqual(duration.name, "http.server.duration")
97-
data_point = list(duration.data.data_points)[0]
98-
self.assertAlmostEqual(
99-
data_point.sum,
145+
self.assertEqual(server_duration.name, "http.server.duration")
146+
self.assertDurationMetricExpected(
147+
server_duration,
100148
client_duration_estimated,
101-
delta=200,
102-
)
103-
self.assertMetricHasAttributes(
104-
duration,
105149
{
106-
"http.status_code": 201,
150+
"http.status_code": response.code,
107151
"http.method": "GET",
108152
"http.flavor": "HTTP/1.1",
109153
"http.scheme": "http",
154+
"http.target": "/",
110155
"http.host": response.request.headers["host"],
111156
},
112157
)
113158

114-
self.assertEqual(request_size.name, "http.server.request.size")
115-
self.assertMetricHasAttributes(
116-
request_size,
159+
self.assertEqual(server_request_size.name, "http.server.request.size")
160+
self.assertMetricExpected(
161+
server_request_size,
162+
0,
117163
{
118164
"http.status_code": 200,
119165
"http.method": "GET",
120166
"http.flavor": "HTTP/1.1",
121167
"http.scheme": "http",
168+
"http.target": "/",
122169
"http.host": response.request.headers["host"],
123170
},
124171
)
125172

126-
self.assertEqual(response_size.name, "http.server.response.size")
127-
self.assertMetricHasAttributes(
128-
response_size,
173+
self.assertEqual(
174+
server_response_size.name, "http.server.response.size"
175+
)
176+
self.assertMetricExpected(
177+
server_response_size,
178+
len(response.body),
179+
{
180+
"http.status_code": response.code,
181+
"http.method": "GET",
182+
"http.flavor": "HTTP/1.1",
183+
"http.scheme": "http",
184+
"http.target": "/",
185+
"http.host": response.request.headers["host"],
186+
},
187+
)
188+
189+
self.assertEqual(client_duration.name, "http.client.duration")
190+
self.assertDurationMetricExpected(
191+
client_duration,
192+
client_duration_estimated,
193+
{
194+
"http.status_code": response.code,
195+
"http.method": "GET",
196+
"http.url": response.effective_url,
197+
},
198+
)
199+
200+
self.assertEqual(client_request_size.name, "http.client.request.size")
201+
self.assertMetricExpected(
202+
client_request_size,
203+
0,
204+
{
205+
"http.status_code": response.code,
206+
"http.method": "GET",
207+
"http.url": response.effective_url,
208+
},
209+
)
210+
211+
self.assertEqual(
212+
client_response_size.name, "http.client.response.size"
213+
)
214+
self.assertMetricExpected(
215+
client_response_size,
216+
len(response.body),
129217
{
130218
"http.status_code": response.code,
131219
"http.method": "GET",
132-
"http.url": self.get_url("/"),
220+
"http.url": response.effective_url,
133221
},
134222
)
135223

0 commit comments

Comments
 (0)