diff --git a/.changesets/fix_rohan_b99_missing_metrics_including_response_body_size.md b/.changesets/fix_rohan_b99_missing_metrics_including_response_body_size.md new file mode 100644 index 0000000000..885c75cc45 --- /dev/null +++ b/.changesets/fix_rohan_b99_missing_metrics_including_response_body_size.md @@ -0,0 +1,5 @@ +### Ensure `http.server.response.body.size` metric is recorded ([PR #8697](https://github.com/apollographql/router/pull/8697)) + +Previously, the `http.server.response.body.size` metric was not recorded as we attempted to read from the `Content-Length` header of the response before it had been set. We now use the `size_hint` of the body if it is exact. + +By [@rohan-b99](https://github.com/rohan-b99) in https://github.com/apollographql/router/pull/8697 diff --git a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap index 86c7cb6816..4d97276df7 100644 --- a/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap +++ b/apollo-router/src/configuration/snapshots/apollo_router__configuration__tests__schema_generation.snap @@ -8332,6 +8332,20 @@ expression: "&schema" ], "type": "object" }, + { + "additionalProperties": false, + "description": "The size hint of the body.\nThis is used as the Content-Length header has not yet been populated\nwhen selectors are evaluated", + "properties": { + "response_size_hint": { + "description": "Extract response size hint in bytes", + "type": "boolean" + } + }, + "required": [ + "response_size_hint" + ], + "type": "object" + }, { "additionalProperties": false, "description": "Router overhead duration (time not spent in subgraph requests)", diff --git a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_counter/router.yaml b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_counter/router.yaml index dbecbd4ed6..de2e539154 100644 --- a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_counter/router.yaml +++ b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_counter/router.yaml @@ -4,6 +4,7 @@ telemetry: router: http.server.active_requests: false http.server.request.duration: false + http.server.response.body.size: false "custom_counter": description: "count of requests" type: counter diff --git a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_counter_custom_value/router.yaml b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_counter_custom_value/router.yaml index f6a40d1765..e391996dea 100644 --- a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_counter_custom_value/router.yaml +++ b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_counter_custom_value/router.yaml @@ -4,6 +4,7 @@ telemetry: router: http.server.active_requests: false http.server.request.duration: false + http.server.response.body.size: false "custom_counter": description: "count of requests" type: counter diff --git a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_counter_with_attributes/router.yaml b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_counter_with_attributes/router.yaml index ae6977491d..5e97dc2c59 100644 --- a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_counter_with_attributes/router.yaml +++ b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_counter_with_attributes/router.yaml @@ -4,6 +4,7 @@ telemetry: router: http.server.active_requests: false http.server.request.duration: false + http.server.response.body.size: false "custom_counter": description: "count of requests" type: counter diff --git a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_counter_with_conditions/router.yaml b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_counter_with_conditions/router.yaml index 5e1bdc25ca..7db03bc31f 100644 --- a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_counter_with_conditions/router.yaml +++ b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_counter_with_conditions/router.yaml @@ -4,6 +4,7 @@ telemetry: router: http.server.active_requests: false http.server.request.duration: false + http.server.response.body.size: false "custom_counter": description: "count of requests" type: counter diff --git a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_custom_value/router.yaml b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_custom_value/router.yaml index 74d95b1924..25f6f8c01d 100644 --- a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_custom_value/router.yaml +++ b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_custom_value/router.yaml @@ -4,6 +4,7 @@ telemetry: router: http.server.active_requests: false http.server.request.duration: false + http.server.response.body.size: false "custom.histogram": description: "histogram of requests" type: histogram diff --git a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_duration/router.yaml b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_duration/router.yaml index e1796e90b6..bdc783d789 100644 --- a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_duration/router.yaml +++ b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_duration/router.yaml @@ -4,6 +4,7 @@ telemetry: router: http.server.active_requests: false http.server.request.duration: false + http.server.response.body.size: false "custom.histogram.duration": description: "histogram of requests" type: histogram diff --git a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_unit/router.yaml b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_unit/router.yaml index 813abb4615..59991866a0 100644 --- a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_unit/router.yaml +++ b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_unit/router.yaml @@ -4,6 +4,7 @@ telemetry: router: http.server.active_requests: false http.server.request.duration: false + http.server.response.body.size: false "custom.histogram": description: "histogram of requests" type: histogram diff --git a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_with_attributes/router.yaml b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_with_attributes/router.yaml index d37b70d822..841b0b8f0d 100644 --- a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_with_attributes/router.yaml +++ b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_with_attributes/router.yaml @@ -4,6 +4,7 @@ telemetry: router: http.server.active_requests: false http.server.request.duration: false + http.server.response.body.size: false "custom.histogram": description: "histogram of requests" type: histogram diff --git a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_with_conditions/router.yaml b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_with_conditions/router.yaml index faa6a69a28..f7b1c44838 100644 --- a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_with_conditions/router.yaml +++ b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/custom_histogram_with_conditions/router.yaml @@ -4,6 +4,7 @@ telemetry: router: http.server.active_requests: false http.server.request.duration: false + http.server.response.body.size: false "custom.histogram": description: "histogram of requests" type: histogram diff --git a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.active_requests/router.yaml b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.active_requests/router.yaml index e9b3ac1c2d..ae82a62f70 100644 --- a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.active_requests/router.yaml +++ b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.active_requests/router.yaml @@ -4,5 +4,6 @@ telemetry: router: http.server.active_requests: true http.server.request.duration: false + http.server.response.body.size: false subgraph: http.client.request.duration: false \ No newline at end of file diff --git a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.request.body.size/router.yaml b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.request.body.size/router.yaml index 42455f7354..aaeea5f87c 100644 --- a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.request.body.size/router.yaml +++ b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.request.body.size/router.yaml @@ -4,4 +4,5 @@ telemetry: router: http.server.active_requests: false http.server.request.duration: false - http.server.request.body.size: true \ No newline at end of file + http.server.request.body.size: true + http.server.response.body.size: false \ No newline at end of file diff --git a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.request.body.size_with_custom_attributes/router.yaml b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.request.body.size_with_custom_attributes/router.yaml index 42455f7354..aaeea5f87c 100644 --- a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.request.body.size_with_custom_attributes/router.yaml +++ b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.request.body.size_with_custom_attributes/router.yaml @@ -4,4 +4,5 @@ telemetry: router: http.server.active_requests: false http.server.request.duration: false - http.server.request.body.size: true \ No newline at end of file + http.server.request.body.size: true + http.server.response.body.size: false \ No newline at end of file diff --git a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.request.duration/router.yaml b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.request.duration/router.yaml index 5411b485dd..8b117049a2 100644 --- a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.request.duration/router.yaml +++ b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.request.duration/router.yaml @@ -3,4 +3,5 @@ telemetry: instruments: router: http.server.active_requests: false - http.server.request.duration: true \ No newline at end of file + http.server.request.duration: true + http.server.response.body.size: false \ No newline at end of file diff --git a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.request.duration_with_custom_attributes/router.yaml b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.request.duration_with_custom_attributes/router.yaml index ef8332ead1..941203aec4 100644 --- a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.request.duration_with_custom_attributes/router.yaml +++ b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.request.duration_with_custom_attributes/router.yaml @@ -3,6 +3,7 @@ telemetry: instruments: router: http.server.active_requests: false + http.server.response.body.size: false http.server.request.duration: attributes: http.request.method: diff --git a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.response.body.size/metrics.snap b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.response.body.size/metrics.snap index 245131f105..0184468d38 100644 --- a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.response.body.size/metrics.snap +++ b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.response.body.size/metrics.snap @@ -16,7 +16,7 @@ info: unit: By data: datapoints: - - sum: 35 + - sum: 18 count: 1 attributes: http.request.method: GET diff --git a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.response.body.size/test.yaml b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.response.body.size/test.yaml index b0bbb9625a..323075d1fb 100644 --- a/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.response.body.size/test.yaml +++ b/apollo-router/src/plugins/telemetry/config_new/fixtures/router/http.server.response.body.size/test.yaml @@ -6,8 +6,6 @@ events: body: | hello - router_response: - headers: - "content-length": "35" body: | hello status: 200 \ No newline at end of file diff --git a/apollo-router/src/plugins/telemetry/config_new/instruments.rs b/apollo-router/src/plugins/telemetry/config_new/instruments.rs index fd4919cf6f..aafe264f96 100644 --- a/apollo-router/src/plugins/telemetry/config_new/instruments.rs +++ b/apollo-router/src/plugins/telemetry/config_new/instruments.rs @@ -401,11 +401,7 @@ impl InstrumentsConfig { ) ), attributes: Vec::with_capacity(nb_attributes), - selector: Some(Arc::new(RouterSelector::ResponseHeader { - response_header: "content-length".to_string(), - redact: None, - default: None, - })), + selector: Some(Arc::new(RouterSelector::ResponseSizeHint { response_size_hint: true })), selectors, updated: false, _phantom: PhantomData, @@ -3463,7 +3459,6 @@ mod tests { .status_code(StatusCode::BAD_REQUEST) .header("content-type", "application/json") .header("x-my-header", "TEST") - .header("content-length", "35") .data(json!({"errors": [{"message": "nope"}]})) .build() .unwrap(); @@ -3483,7 +3478,7 @@ mod tests { assert_histogram_sum!("http.server.request.body.size", 35.0); assert_histogram_sum!( "http.server.response.body.size", - 35.0, + 40.0, "acme.my_attribute" = "TEST" ); @@ -3500,7 +3495,6 @@ mod tests { .context(router_req.context.clone()) .status_code(StatusCode::BAD_REQUEST) .header("content-type", "application/json") - .header("content-length", "35") .data(json!({"errors": [{"message": "nope"}]})) .build() .unwrap(); @@ -3520,12 +3514,12 @@ mod tests { assert_histogram_sum!("http.server.request.body.size", 70.0); assert_histogram_sum!( "http.server.response.body.size", - 35.0, + 40.0, "acme.my_attribute" = "TEST" ); assert_histogram_sum!( "http.server.response.body.size", - 35.0, + 40.0, "acme.my_attribute" = "unknown" ); @@ -3541,7 +3535,6 @@ mod tests { .context(router_req.context.clone()) .status_code(StatusCode::OK) .header("content-type", "application/json") - .header("content-length", "35") .data(json!({"errors": [{"message": "nope"}]})) .build() .unwrap(); diff --git a/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs b/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs index ecc709e7a8..e2b9f8cee3 100644 --- a/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs +++ b/apollo-router/src/plugins/telemetry/config_new/router/selectors.rs @@ -1,4 +1,5 @@ use derivative::Derivative; +use http_body::Body; use schemars::JsonSchema; use serde::Deserialize; use serde_json::from_str; @@ -159,6 +160,13 @@ pub(crate) enum RouterSelector { /// The http response status code. response_status: ResponseStatus, }, + /// The size hint of the body. + /// This is used as the Content-Length header has not yet been populated + /// when selectors are evaluated + ResponseSizeHint { + /// Extract response size hint in bytes + response_size_hint: bool, + }, /// Router overhead duration (time not spent in subgraph requests) RouterOverhead { /// Extract router overhead duration in seconds @@ -311,6 +319,13 @@ impl Selector for RouterSelector { .canonical_reason() .map(|reason| reason.to_string().into()), }, + RouterSelector::ResponseSizeHint { response_size_hint } if *response_size_hint => { + response + .response + .size_hint() + .exact() + .map(|size| opentelemetry::Value::I64(size as i64)) + } RouterSelector::RouterOverhead { router_overhead } if *router_overhead => response .context .extensions() @@ -459,6 +474,7 @@ impl Selector for RouterSelector { | RouterSelector::ResponseHeader { .. } | RouterSelector::ResponseContext { .. } | RouterSelector::ResponseStatus { .. } + | RouterSelector::ResponseSizeHint { .. } | RouterSelector::RouterOverhead { .. } | RouterSelector::ActiveSubgraphRequests { .. } | RouterSelector::OnGraphQLError { .. } @@ -975,6 +991,25 @@ mod test { ); } + #[test] + fn router_response_size_hint() { + let selector = RouterSelector::ResponseSizeHint { + response_size_hint: true, + }; + assert_eq!( + selector + .on_response( + &crate::services::RouterResponse::fake_builder() + .data("value") + .build() + .unwrap() + ) + .unwrap(), + // The actual response body is {"data":"value"} + opentelemetry::Value::I64(16) + ); + } + #[test] fn router_response_body() { let selector = RouterSelector::ResponseBody { diff --git a/docs/source/routing/observability/router-telemetry-otel/enabling-telemetry/instruments.mdx b/docs/source/routing/observability/router-telemetry-otel/enabling-telemetry/instruments.mdx index 0abcc587e9..c0c5d5ab28 100644 --- a/docs/source/routing/observability/router-telemetry-otel/enabling-telemetry/instruments.mdx +++ b/docs/source/routing/observability/router-telemetry-otel/enabling-telemetry/instruments.mdx @@ -24,6 +24,7 @@ OpenTelemetry specifies multiple [standard metric instruments](https://opentelem * `http.server.active_requests` - The number of active requests in flight. * `http.server.request.body.size` - A histogram of request body sizes for requests handled by the router. * `http.server.request.duration` - A histogram of request durations for requests handled by the router. + * `http.server.response.body.size` - A histogram of response body sizes for requests handled by the router. + In the [subgraph service](#router-request-lifecycle-services): @@ -53,6 +54,7 @@ telemetry: http.server.active_requests: true # (default false) http.server.request.body.size: true # (default false) http.server.request.duration: true # (default false) + http.server.response.body.size: true # (default false) subgraph: http.client.request.body.size: true # (default false) http.client.request.duration: true # (default false)