async_graphql/extensions/
logger.rs

1use std::{fmt::Write, sync::Arc};
2
3use crate::{
4    extensions::{Extension, ExtensionContext, ExtensionFactory, NextExecute, NextParseQuery},
5    parser::types::{ExecutableDocument, OperationType, Selection},
6    PathSegment, Response, ServerResult, Variables,
7};
8
9/// Logger extension
10#[cfg_attr(docsrs, doc(cfg(feature = "log")))]
11pub struct Logger;
12
13impl ExtensionFactory for Logger {
14    fn create(&self) -> Arc<dyn Extension> {
15        Arc::new(LoggerExtension)
16    }
17}
18
19struct LoggerExtension;
20
21#[async_trait::async_trait]
22impl Extension for LoggerExtension {
23    async fn parse_query(
24        &self,
25        ctx: &ExtensionContext<'_>,
26        query: &str,
27        variables: &Variables,
28        next: NextParseQuery<'_>,
29    ) -> ServerResult<ExecutableDocument> {
30        let document = next.run(ctx, query, variables).await?;
31        let is_schema = document
32            .operations
33            .iter()
34            .filter(|(_, operation)| operation.node.ty == OperationType::Query)
35            .any(|(_, operation)| operation.node.selection_set.node.items.iter().any(|selection| matches!(&selection.node, Selection::Field(field) if field.node.name.node == "__schema")));
36        if !is_schema {
37            log::info!(
38                target: "async-graphql",
39                "[Execute] {}", ctx.stringify_execute_doc(&document, variables)
40            );
41        }
42        Ok(document)
43    }
44
45    async fn execute(
46        &self,
47        ctx: &ExtensionContext<'_>,
48        operation_name: Option<&str>,
49        next: NextExecute<'_>,
50    ) -> Response {
51        let resp = next.run(ctx, operation_name).await;
52        if resp.is_err() {
53            for err in &resp.errors {
54                if !err.path.is_empty() {
55                    let mut path = String::new();
56                    for (idx, s) in err.path.iter().enumerate() {
57                        if idx > 0 {
58                            path.push('.');
59                        }
60                        match s {
61                            PathSegment::Index(idx) => {
62                                let _ = write!(&mut path, "{}", idx);
63                            }
64                            PathSegment::Field(name) => {
65                                let _ = write!(&mut path, "{}", name);
66                            }
67                        }
68                    }
69
70                    log::info!(
71                        target: "async-graphql",
72                        "[Error] path={} message={}", path, err.message,
73                    );
74                } else {
75                    log::info!(
76                        target: "async-graphql",
77                        "[Error] message={}", err.message,
78                    );
79                }
80            }
81        }
82        resp
83    }
84}