记录每个请求的 apollo-server GraphQL 查询和变量

Log apollo-server GraphQL query and variables per request

当使用 apollo-server 2.2.1 或更高版本时,如何记录每个请求、查询和变量?

这似乎是一个简单的要求和常见用例,但文档 very vague, and the query object passed to formatResponse no longer 具有 queryStringvariables 属性。

如果我必须记录查询和变量,我可能会使用 apollo-server-express,而不是 apollo-server,这样我就可以在 graphql 之前添加一个单独的快速中间件为我记录的:

const express = require('express')
const { ApolloServer } = require('apollo-server-express')
const { typeDefs, resolvers } = require('./graphql')

const server = new ApolloServer({ typeDefs, resolvers })
const app = express()

app.use(bodyParser.json())
app.use('/graphql', (req, res, next) => {
  console.log(req.body.query)
  console.log(req.body.variables)
  return next()
})

server.applyMiddleware({ app })

app.listen({ port: 4000}, () => {
  console.log(` Server ready at http://localhost:4000${server.graphqlPath}`)
})

Dan 的解决方案主要解决了问题,但如果您想在不使用 express 的情况下记录它, 您可以在下面示例中显示的上下文中捕获它。

const server = new ApolloServer({
schema,
context: params => () => {
    console.log(params.req.body.query);
    console.log(params.req.body.variables);
}
});

Amit 的回答(今天)有效,但恕我直言,它有点老套,将来可能无法按预期工作,或者在某些情况下可能无法正常工作。

比如,我看到它的第一反应是:"that may not work if the query is invalid",结果今天它在查询无效时确实有效。因为在当前的实现中,在验证查询之前先评估上下文。但是,这是将来可能会更改的实现细节。例如,如果有一天 apollo 团队决定只有在查询被解析和验证后才评估上下文才能获得性能胜利怎么办?这正是我所期待的:-)

我想说的是,如果您只是想快速记录一些内容以便在您的 dev 环境中调试某些内容,那么 Amit 的解决方案绝对是正确的选择。

但是,如果您想要为生产环境注册日志,那么使用 context 功能可能不是最好的主意。在这种情况下,我会安装 graphql-extensions 并将它们用于日志记录,例如:

const { print } = require('graphql');

class BasicLogging {
  requestDidStart({queryString, parsedQuery, variables}) {
    const query = queryString || print(parsedQuery);
    console.log(query);
    console.log(variables);
  }

  willSendResponse({graphqlResponse}) {
    console.log(JSON.stringify(graphqlResponse, null, 2));
  }
}

const server = new ApolloServer({
  typeDefs,
  resolvers,
  extensions: [() => new BasicLogging()]
});

编辑:

正如 Dan 指出的那样,不需要安装 graphql-extensions 包,因为它已集成在 apollo-server-core 包中。

使用 new plugins API,您可以使用与 Josep 的答案非常相似的方法,只是您的代码结构略有不同。

const BASIC_LOGGING = {
    requestDidStart(requestContext) {
        console.log("request started");
        console.log(requestContext.request.query);
        console.log(requestContext.request.variables);
        return {
            didEncounterErrors(requestContext) {
                console.log("an error happened in response to query " + requestContext.request.query);
                console.log(requestContext.errors);
            }
        };
    },

    willSendResponse(requestContext) {
        console.log("response sent", requestContext.response);
    }
};


const server = new ApolloServer(
    {
        schema,
        plugins: [BASIC_LOGGING]
    }
)

server.listen(3003, '0.0.0.0').then(({ url }) => {
    console.log(`GraphQL API ready at ${url}`);
});