mirror of
https://github.com/open-telemetry/opentelemetry-python-contrib.git
synced 2025-07-28 20:52:57 +08:00
Add http.server.request.size for ASGI metric implementation (#1867)
* Update changelog file. * Update changelog file. * Add new request.size metric for ASGI middleware. * Clean-up. * Refactor try except section. --------- Co-authored-by: Shalev Roda <65566801+shalevr@users.noreply.github.com>
This commit is contained in:
@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## Unreleased
|
||||
- `opentelemetry-instrumentation-asgi` Add `http.server.request.size` metric
|
||||
([#1867](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1867))
|
||||
|
||||
### Fixed
|
||||
|
||||
|
@ -511,6 +511,11 @@ class OpenTelemetryMiddleware:
|
||||
unit="By",
|
||||
description="measures the size of HTTP response messages (compressed).",
|
||||
)
|
||||
self.server_request_size_histogram = self.meter.create_histogram(
|
||||
name=MetricInstruments.HTTP_SERVER_REQUEST_SIZE,
|
||||
unit="By",
|
||||
description="Measures the size of HTTP request messages (compressed).",
|
||||
)
|
||||
self.active_requests_counter = self.meter.create_up_down_counter(
|
||||
name=MetricInstruments.HTTP_SERVER_ACTIVE_REQUESTS,
|
||||
unit="requests",
|
||||
@ -603,6 +608,16 @@ class OpenTelemetryMiddleware:
|
||||
self.server_response_size_histogram.record(
|
||||
self.content_length_header, duration_attrs
|
||||
)
|
||||
request_size = asgi_getter.get(scope, "content-length")
|
||||
if request_size:
|
||||
try:
|
||||
request_size_amount = int(request_size[0])
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
self.server_request_size_histogram.record(
|
||||
request_size_amount, duration_attrs
|
||||
)
|
||||
if token:
|
||||
context.detach(token)
|
||||
|
||||
|
@ -47,16 +47,19 @@ _expected_metric_names = [
|
||||
"http.server.active_requests",
|
||||
"http.server.duration",
|
||||
"http.server.response.size",
|
||||
"http.server.request.size",
|
||||
]
|
||||
_recommended_attrs = {
|
||||
"http.server.active_requests": _active_requests_count_attrs,
|
||||
"http.server.duration": _duration_attrs,
|
||||
"http.server.response.size": _duration_attrs,
|
||||
"http.server.request.size": _duration_attrs,
|
||||
}
|
||||
|
||||
|
||||
async def http_app(scope, receive, send):
|
||||
message = await receive()
|
||||
scope["headers"] = [(b"content-length", b"128")]
|
||||
assert scope["type"] == "http"
|
||||
if message.get("type") == "http.request":
|
||||
await send(
|
||||
@ -99,6 +102,7 @@ async def error_asgi(scope, receive, send):
|
||||
assert isinstance(scope, dict)
|
||||
assert scope["type"] == "http"
|
||||
message = await receive()
|
||||
scope["headers"] = [(b"content-length", b"128")]
|
||||
if message.get("type") == "http.request":
|
||||
try:
|
||||
raise ValueError
|
||||
@ -592,6 +596,8 @@ class TestAsgiApplication(AsgiTestBase):
|
||||
)
|
||||
elif metric.name == "http.server.response.size":
|
||||
self.assertEqual(1024, point.sum)
|
||||
elif metric.name == "http.server.request.size":
|
||||
self.assertEqual(128, point.sum)
|
||||
elif isinstance(point, NumberDataPoint):
|
||||
self.assertDictEqual(
|
||||
expected_requests_count_attributes,
|
||||
@ -630,7 +636,7 @@ class TestAsgiApplication(AsgiTestBase):
|
||||
expected_target,
|
||||
)
|
||||
assertions += 1
|
||||
self.assertEqual(assertions, 2)
|
||||
self.assertEqual(assertions, 3)
|
||||
|
||||
def test_no_metric_for_websockets(self):
|
||||
self.scope = {
|
||||
|
@ -45,6 +45,7 @@ _expected_metric_names = [
|
||||
"http.server.active_requests",
|
||||
"http.server.duration",
|
||||
"http.server.response.size",
|
||||
"http.server.request.size",
|
||||
]
|
||||
_recommended_attrs = {
|
||||
"http.server.active_requests": _active_requests_count_attrs,
|
||||
@ -53,6 +54,10 @@ _recommended_attrs = {
|
||||
*_duration_attrs,
|
||||
SpanAttributes.HTTP_TARGET,
|
||||
},
|
||||
"http.server.request.size": {
|
||||
*_duration_attrs,
|
||||
SpanAttributes.HTTP_TARGET,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -251,8 +256,13 @@ class TestFastAPIManualInstrumentation(TestBase):
|
||||
|
||||
def test_basic_post_request_metric_success(self):
|
||||
start = default_timer()
|
||||
self._client.post("/foobar")
|
||||
response = self._client.post(
|
||||
"/foobar",
|
||||
json={"foo": "bar"},
|
||||
)
|
||||
duration = max(round((default_timer() - start) * 1000), 0)
|
||||
response_size = int(response.headers.get("content-length"))
|
||||
request_size = int(response.request.headers.get("content-length"))
|
||||
metrics_list = self.memory_metrics_reader.get_metrics_data()
|
||||
for metric in (
|
||||
metrics_list.resource_metrics[0].scope_metrics[0].metrics
|
||||
@ -260,7 +270,12 @@ class TestFastAPIManualInstrumentation(TestBase):
|
||||
for point in list(metric.data.data_points):
|
||||
if isinstance(point, HistogramDataPoint):
|
||||
self.assertEqual(point.count, 1)
|
||||
self.assertAlmostEqual(duration, point.sum, delta=30)
|
||||
if metric.name == "http.server.duration":
|
||||
self.assertAlmostEqual(duration, point.sum, delta=30)
|
||||
elif metric.name == "http.server.response.size":
|
||||
self.assertEqual(response_size, point.sum)
|
||||
elif metric.name == "http.server.request.size":
|
||||
self.assertEqual(request_size, point.sum)
|
||||
if isinstance(point, NumberDataPoint):
|
||||
self.assertEqual(point.value, 0)
|
||||
|
||||
|
@ -50,11 +50,13 @@ _expected_metric_names = [
|
||||
"http.server.active_requests",
|
||||
"http.server.duration",
|
||||
"http.server.response.size",
|
||||
"http.server.request.size",
|
||||
]
|
||||
_recommended_attrs = {
|
||||
"http.server.active_requests": _active_requests_count_attrs,
|
||||
"http.server.duration": _duration_attrs,
|
||||
"http.server.response.size": _duration_attrs,
|
||||
"http.server.request.size": _duration_attrs,
|
||||
}
|
||||
|
||||
|
||||
@ -165,8 +167,13 @@ class TestStarletteManualInstrumentation(TestBase):
|
||||
"http.scheme": "http",
|
||||
"http.server_name": "testserver",
|
||||
}
|
||||
self._client.post("/foobar")
|
||||
response = self._client.post(
|
||||
"/foobar",
|
||||
json={"foo": "bar"},
|
||||
)
|
||||
duration = max(round((default_timer() - start) * 1000), 0)
|
||||
response_size = int(response.headers.get("content-length"))
|
||||
request_size = int(response.request.headers.get("content-length"))
|
||||
metrics_list = self.memory_metrics_reader.get_metrics_data()
|
||||
for metric in (
|
||||
metrics_list.resource_metrics[0].scope_metrics[0].metrics
|
||||
@ -174,10 +181,15 @@ class TestStarletteManualInstrumentation(TestBase):
|
||||
for point in list(metric.data.data_points):
|
||||
if isinstance(point, HistogramDataPoint):
|
||||
self.assertEqual(point.count, 1)
|
||||
self.assertAlmostEqual(duration, point.sum, delta=30)
|
||||
self.assertDictEqual(
|
||||
dict(point.attributes), expected_duration_attributes
|
||||
)
|
||||
if metric.name == "http.server.duration":
|
||||
self.assertAlmostEqual(duration, point.sum, delta=30)
|
||||
elif metric.name == "http.server.response.size":
|
||||
self.assertEqual(response_size, point.sum)
|
||||
elif metric.name == "http.server.request.size":
|
||||
self.assertEqual(request_size, point.sum)
|
||||
if isinstance(point, NumberDataPoint):
|
||||
self.assertDictEqual(
|
||||
expected_requests_count_attributes,
|
||||
|
Reference in New Issue
Block a user