Skip to content

Commit

Permalink
Add http.server.request.size for ASGI metric implementation (#1867)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>
  • Loading branch information
imanshafiei540 and shalevr authored Jun 21, 2023
1 parent 32ae65e commit 256d8ce
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 5 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,11 @@ def __init__(
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",
Expand Down Expand Up @@ -603,6 +608,16 @@ async def __call__(self, scope, receive, send):
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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,19 @@
"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(
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -592,6 +596,8 @@ def test_basic_metric_success(self):
)
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,
Expand Down Expand Up @@ -630,7 +636,7 @@ async def target_asgi(scope, receive, send):
expected_target,
)
assertions += 1
self.assertEqual(assertions, 2)
self.assertEqual(assertions, 3)

def test_no_metric_for_websockets(self):
self.scope = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
"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,
Expand All @@ -53,6 +54,10 @@
*_duration_attrs,
SpanAttributes.HTTP_TARGET,
},
"http.server.request.size": {
*_duration_attrs,
SpanAttributes.HTTP_TARGET,
},
}


Expand Down Expand Up @@ -251,16 +256,26 @@ def test_basic_metric_success(self):

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
):
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)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,13 @@
"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,
}


Expand Down Expand Up @@ -165,19 +167,29 @@ def test_basic_post_request_metric_success(self):
"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
):
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,
Expand Down

0 comments on commit 256d8ce

Please sign in to comment.