Skip to content

Commit a85ca3d

Browse files
committed
server: Log graphql panics
1 parent 8d70a6f commit a85ca3d

File tree

4 files changed

+91
-26
lines changed

4 files changed

+91
-26
lines changed

graph/src/data/query/query.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,4 +133,14 @@ impl Query {
133133
_force_use_of_new: (),
134134
}
135135
}
136+
137+
pub fn query_text(&self) -> String {
138+
self.document
139+
.format(&graphql_parser::Style::default().indent(0))
140+
.replace('\n', " ")
141+
}
142+
143+
pub fn variables_text(&self) -> String {
144+
serde_json::to_string(&self.variables).unwrap_or_default()
145+
}
136146
}

graphql/src/execution/query.rs

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
use graphql_parser::{query as q, schema as s, Style};
1+
use graphql_parser::{query as q, schema as s};
22
use std::collections::HashMap;
33
use std::sync::Arc;
44

55
use graph::data::graphql::ext::TypeExt;
66
use graph::data::query::{Query as GraphDataQuery, QueryVariables};
77
use graph::data::schema::Schema;
8-
use graph::prelude::{serde_json, QueryExecutionError};
8+
use graph::prelude::QueryExecutionError;
99

1010
use crate::execution::{get_field, get_named_type};
1111
use crate::introspection::introspection_schema;
@@ -45,8 +45,8 @@ pub struct Query {
4545

4646
/// Used only for logging; if logging is configured off, these will
4747
/// have dummy values
48-
pub(crate) query_text: Arc<String>,
49-
pub(crate) variables_text: Arc<String>,
48+
pub query_text: Arc<String>,
49+
pub variables_text: Arc<String>,
5050
pub(crate) complexity: u64,
5151
}
5252

@@ -60,16 +60,9 @@ impl Query {
6060
max_complexity: Option<u64>,
6161
max_depth: u8,
6262
) -> Result<Arc<Self>, Vec<QueryExecutionError>> {
63-
let (query_text, variables_text) = if *graph::log::LOG_GQL_TIMING {
64-
(
65-
query
66-
.document
67-
.format(&Style::default().indent(0))
68-
.replace('\n', " "),
69-
serde_json::to_string(&query.variables).unwrap_or_default(),
70-
)
71-
} else {
72-
("(gql logging turned off)".to_owned(), "".to_owned())
63+
let (query_text, variables_text) = match *graph::log::LOG_GQL_TIMING {
64+
true => (query.query_text(), query.variables_text()),
65+
false => ("(gql logging turned off)".to_owned(), "".to_owned()),
7366
};
7467
let query_text = Arc::new(query_text);
7568
let variables_text = Arc::new(variables_text);

server/http/src/service.rs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -272,9 +272,41 @@ where
272272
let query = GraphQLRequest::new(body, schema, network).compat().await;
273273

274274
let result = match query {
275-
Ok(query) => graph::spawn_blocking_allow_panic(service.graphql_runner.run_query(query))
276-
.await
277-
.unwrap(),
275+
Ok(query) => {
276+
// Not great that we serialize the query here only for the panic case.
277+
let query_text = query.query_text();
278+
let variables_text = query.variables_text();
279+
280+
let result =
281+
graph::spawn_blocking_allow_panic(service.graphql_runner.run_query(query))
282+
.await;
283+
284+
match result {
285+
Ok(res) => res,
286+
287+
// `JoinError` means a panic.
288+
Err(e) => {
289+
let e = e.into_panic();
290+
let e = match e
291+
.downcast_ref::<String>()
292+
.map(|s| s.as_str())
293+
.or(e.downcast_ref::<&'static str>().map(|&s| s))
294+
{
295+
Some(e) => e.to_string(),
296+
None => "panic is not a string".to_string(),
297+
};
298+
let err = QueryExecutionError::Panic(e);
299+
error!(
300+
self.logger,
301+
"panic when processing graphql query";
302+
"panic" => err.to_string(),
303+
"query" => query_text,
304+
"variables" => variables_text,
305+
);
306+
Arc::new(QueryResult::from(err))
307+
}
308+
}
309+
}
278310
Err(GraphQLServerError::QueryError(e)) => Arc::new(QueryResult::from(e)),
279311
Err(e) => return Err(e),
280312
};

server/index-node/src/service.rs

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,6 @@ where
7979
&self,
8080
request_body: Body,
8181
) -> Result<Response<Body>, GraphQLServerError> {
82-
let logger = self.logger.clone();
8382
let store = self.store.clone();
8483
let graphql_runner = self.graphql_runner.clone();
8584

@@ -91,28 +90,59 @@ where
9190
.await?;
9291

9392
let query = IndexNodeRequest::new(body, schema).compat().await?;
93+
let query = match PreparedQuery::new(query, None, 100) {
94+
Ok(query) => query,
95+
Err(e) => return Ok(QueryResult::from(e).as_http_response()),
96+
};
9497

95-
let logger = logger.clone();
9698
let graphql_runner = graphql_runner.clone();
9799
let load_manager = graphql_runner.load_manager();
98100

99101
// Run the query using the index node resolver
102+
let query_clone = query.cheap_clone();
103+
let logger = self.logger.cheap_clone();
100104
let result = tokio::task::spawn_blocking(move || {
101105
let options = QueryExecutionOptions {
102-
logger: logger.clone(),
103106
resolver: IndexNodeResolver::new(&logger, graphql_runner, store),
107+
logger,
104108
deadline: None,
105109
max_first: std::u32::MAX,
106110
load_manager,
107111
};
108-
QueryResult::from(PreparedQuery::new(query, None, 100).map(|query| {
112+
QueryResult::from(
109113
// Index status queries are not cacheable, so we may unwrap this.
110-
Arc::try_unwrap(execute_query(query, None, None, options)).unwrap()
111-
}))
114+
Arc::try_unwrap(execute_query(query_clone, None, None, options)).unwrap(),
115+
)
112116
})
113-
.await
114-
.unwrap();
115-
Ok(result.as_http_response())
117+
.await;
118+
119+
let query_result = match result {
120+
Ok(res) => res,
121+
122+
// `JoinError` means a panic.
123+
Err(e) => {
124+
let e = e.into_panic();
125+
let e = match e
126+
.downcast_ref::<String>()
127+
.map(|s| s.as_str())
128+
.or(e.downcast_ref::<&'static str>().map(|&s| s))
129+
{
130+
Some(e) => e.to_string(),
131+
None => "panic is not a string".to_string(),
132+
};
133+
let err = QueryExecutionError::Panic(e);
134+
error!(
135+
self.logger,
136+
"panic when processing graphql query";
137+
"panic" => err.to_string(),
138+
"query" => &query.query_text,
139+
"variables" => &query.variables_text,
140+
);
141+
QueryResult::from(err)
142+
}
143+
};
144+
145+
Ok(query_result.as_http_response())
116146
}
117147

118148
// Handles OPTIONS requests

0 commit comments

Comments
 (0)