Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .changesets/feat_aguilarjaf_add_response_errors_selector.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
### add ResponseErrors selector to router response ([PR #7882](https://github.com/apollographql/router/pull/7882))

Introducing the `ResponseErrors` selector in telemetry configurations to capture router response errors, allowing users to capture and log errors encountered at the router service layer. This new selector enhances logging for the router service, as it allows users the option to only log router errors instead of the entire router response body to reduce noise.

``` yaml
telemetry:
instrumentation:
events:
router:
router.error:
attributes:
"my_attribute":
response_errors: "$.[0]"
# Examples: "$.[0].message", "$.[0].locations", "$.[0].extensions", etc.
```

By [@Aguilarjaf](https://github.com/Aguilarjaf) in https://github.com/apollographql/router/pull/7882
Original file line number Diff line number Diff line change
Expand Up @@ -6056,6 +6056,20 @@ snapshot_kind: text
],
"type": "object"
},
{
"additionalProperties": false,
"description": "The body response errors",
"properties": {
"response_errors": {
"description": "The router response body json path of the chunks.",
"type": "string"
}
},
"required": [
"response_errors"
],
"type": "object"
},
{
"additionalProperties": false,
"description": "A header from the response",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
use derivative::Derivative;
use schemars::JsonSchema;
use serde::Deserialize;
use serde_json::from_str;
use serde_json_bytes::path::JsonPathInst;
use sha2::Digest;

use super::events::DisplayRouterResponse;
use crate::Context;
use crate::context::CONTAINS_GRAPHQL_ERROR;
use crate::context::OPERATION_NAME;
use crate::plugin::serde::deserialize_jsonpath;
use crate::plugins::telemetry::config::AttributeValue;
use crate::plugins::telemetry::config::TraceIdFormat;
use crate::plugins::telemetry::config_new::Selector;
Expand Down Expand Up @@ -38,8 +42,9 @@ impl From<&RouterValue> for InstrumentValue<RouterSelector> {
}
}

#[derive(Deserialize, JsonSchema, Clone, Debug, PartialEq)]
#[derive(Derivative, Deserialize, JsonSchema, Clone)]
#[serde(deny_unknown_fields, untagged)]
#[derivative(Debug, PartialEq)]
pub(crate) enum RouterSelector {
/// A value from baggage.
Baggage {
Expand Down Expand Up @@ -117,6 +122,14 @@ pub(crate) enum RouterSelector {
/// The response body enabled or not
response_body: bool,
},
/// The body response errors
ResponseErrors {
/// The router response body json path of the chunks.
#[schemars(with = "String")]
#[derivative(Debug = "ignore", PartialEq = "ignore")]
#[serde(deserialize_with = "deserialize_jsonpath")]
response_errors: JsonPathInst,
},
/// A header from the response
ResponseHeader {
/// The name of the request header.
Expand Down Expand Up @@ -168,6 +181,13 @@ impl Selector for RouterSelector {
type EventResponse = ();

fn on_request(&self, request: &router::Request) -> Option<opentelemetry::Value> {
// Helper function to insert DisplayRouterResponse into request context extensions
fn insert_display_router_response(request: &router::Request) {
request.context.extensions().with_lock(|ext| {
ext.insert(DisplayRouterResponse);
});
}

match self {
RouterSelector::RequestMethod { request_method } if *request_method => {
Some(request.router_request.method().to_string().into())
Expand Down Expand Up @@ -217,9 +237,11 @@ impl Selector for RouterSelector {
RouterSelector::Static(val) => Some(val.clone().into()),
RouterSelector::StaticField { r#static } => Some(r#static.clone().into()),
RouterSelector::ResponseBody { response_body } if *response_body => {
request.context.extensions().with_lock(|ext| {
ext.insert(DisplayRouterResponse);
});
insert_display_router_response(request);
None
}
RouterSelector::ResponseErrors { .. } => {
insert_display_router_response(request);
None
}
// Related to Response
Expand All @@ -239,6 +261,24 @@ impl Selector for RouterSelector {
})
.map(|v| opentelemetry::Value::String(v.0.into()))
}
RouterSelector::ResponseErrors { response_errors } => response
.context
.extensions()
.with_lock(|ext| ext.get::<RouterResponseBodyExtensionType>().cloned())
.and_then(|v| {
from_str::<serde_json::Value>(&v.0)
.ok()
.and_then(|body_json| {
let errors = body_json.get("errors");

let data: serde_json_bytes::Value =
serde_json_bytes::to_value(errors).ok()?;

let val = response_errors.find(&data);

val.maybe_to_otel_value()
})
}),
RouterSelector::ResponseHeader {
response_header,
default,
Expand Down Expand Up @@ -418,6 +458,7 @@ mod test {
use opentelemetry::trace::TraceId;
use opentelemetry::trace::TraceState;
use serde_json::json;
use serde_json_bytes::path::JsonPathInst;
use tower::BoxError;
use tracing::span;
use tracing::subscriber;
Expand Down Expand Up @@ -910,4 +951,27 @@ mod test {
r#"{"data":"some data"}"#
);
}

#[test]
fn router_response_body_errors() {
let selector = RouterSelector::ResponseErrors {
response_errors: JsonPathInst::new("$.[0]").unwrap(),
};
let res = &crate::services::RouterResponse::fake_builder()
.status_code(StatusCode::BAD_REQUEST)
.data("some data")
.errors(vec![
crate::graphql::Error::builder()
.message("Something went wrong")
.locations(vec![crate::graphql::Location { line: 1, column: 1 }])
.extension_code("GRAPHQL_VALIDATION_FAILED")
.build(),
])
.build()
.unwrap();
assert_eq!(
selector.on_response(res).unwrap().as_str(),
r#"{"message":"Something went wrong","locations":[{"line":1,"column":1}],"extensions":{"code":"GRAPHQL_VALIDATION_FAILED"}}"#
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
| `response_header` | Yes | | The name of a response header |
| `response_status` | Yes | `code` \| `reason` | The response status |
| `response_context` | Yes | | The name of a response context key |
| `response_body` | Yes | | JSON Path into the router response body data (it might impact performance) |

Check notice on line 45 in docs/source/routing/observability/telemetry/instrumentation/selectors.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/routing/observability/telemetry/instrumentation/selectors.mdx#L45

The parenthetical is less authoritative. A direct statement about the performance impact is clearer and more aligned with an opinionated voice. ```suggestion | <code>response_body</code> | Yes | | JSON Path into the router response body data. Using this selector can impact performance. | ```
| `response_errors` | Yes | | JSON Path into the router response body errors (it might impact performance) |

Check notice on line 46 in docs/source/routing/observability/telemetry/instrumentation/selectors.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/routing/observability/telemetry/instrumentation/selectors.mdx#L46

The parenthetical is less authoritative. A direct statement about the performance impact is clearer and more aligned with an opinionated voice. ```suggestion | <code>response_errors</code> | Yes | | JSON Path into the router response body errors. Using this selector can impact performance. | ```
| `baggage` | Yes | | The name of a baggage item |
| `env` | Yes | | The name of an environment variable |
| `on_graphql_error` | No | `true` \| `false` | Boolean set to true if the response payload contains a GraphQL error |
Expand Down Expand Up @@ -194,6 +196,43 @@
response_errors: "$.[0].extensions.code"
```

### Capturing the Router Response body

Check warning on line 199 in docs/source/routing/observability/telemetry/instrumentation/selectors.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/routing/observability/telemetry/instrumentation/selectors.mdx#L199

For headings in sections that describe how to do something, use imperative verbs instead of gerunds. ```suggestion ### Capture the router response body ```

The `response_body` selector allows for accessing the response body in the telemetry

Check warning on line 201 in docs/source/routing/observability/telemetry/instrumentation/selectors.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/routing/observability/telemetry/instrumentation/selectors.mdx#L201

Avoid using the word 'allow'. Use 'enable' when a feature provides new functionality. ```suggestion The <code>response_body</code> selector enables you to access the response body in the telemetry ```
configurations, enabling more detailed monitoring and logging of response data in the Router.

```yaml
telemetry:
instrumentation:
spans:
router:
attributes:
"my_attribute":
response_body: true
```

### Capturing Router Response Errors

Check warning on line 214 in docs/source/routing/observability/telemetry/instrumentation/selectors.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/routing/observability/telemetry/instrumentation/selectors.mdx#L214

For headings in sections that describe how to do something, use imperative verbs instead of gerunds. ```suggestion ### Capture router response errors ```

The `response_errors` selector allows for a more granular approach towards gathering specific error

Check warning on line 216 in docs/source/routing/observability/telemetry/instrumentation/selectors.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/routing/observability/telemetry/instrumentation/selectors.mdx#L216

Avoid using 'allows for' and 'towards'. The suggested phrasing is more direct and uses 'toward' as per the style guide. ```suggestion The <code>response_errors</code> selector provides a more granular way to gather specific error ```
data from the Router response with JSON paths, avoiding capturing the entire response body
while still providing relevant error information.

Check notice on line 218 in docs/source/routing/observability/telemetry/instrumentation/selectors.mdx

View check run for this annotation

Apollo Librarian / AI Style Review

docs/source/routing/observability/telemetry/instrumentation/selectors.mdx#L218

Avoid using 'while' to mean 'although' or to contrast phrases that are not happening simultaneously. 'But' is clearer in this context. ```suggestion but still provides relevant error information. ```

```yaml
telemetry:
instrumentation:
events:
router:
router.error:
attributes:
"my_attribute":
response_errors: "$.[0]"

# Other examples:
# response_errors: "$.[0].message"
# response_errors: "$.[0].locations"
# response_errors: "$.[0].extensions"
```

### Getting GraphQL operation info

Configuring the `query` selector to get information about GraphQL operations, with an example for a custom view of operation limits:
Expand Down