如何编写 Apollo Server 插件来记录请求及其持续时间
Posted
技术标签:
【中文标题】如何编写 Apollo Server 插件来记录请求及其持续时间【英文标题】:How do I write a Apollo Server plugin to log the request and its duration 【发布时间】:2020-05-16 05:43:48 【问题描述】:我很惊讶我找不到执行以下操作的库或示例:
我想要一个简单的服务器日志,记录对服务器的每个请求,其中将说明请求的查询或突变,以及完成请求所用的时间
我知道有 plugin and extension 框架。但我不确定在两个回调之间保持状态的最佳做法是什么:requestDidStart
和 willSendResponse
会吐出来的东西:
path="createAccountMutation" service=20ms
额外的功劳是显示有效负载的大小
path="createAccountMutation" service=20ms bytes=355
希望看到打字稿中的解决方案
注意:我找到了apollo-log——但它没有请求持续时间
谢谢!
【问题讨论】:
【参考方案1】:requestDidStart
在每个请求中调用一次,并返回请求生命周期钩子的映射,因此您可以初始化在钩子之间持久存在的任何状态。
const LogPlugin =
requestDidStart(requestContext)
const start = Date.now()
let op
return
didResolveOperation (context)
op = context.operationName
,
willSendResponse (context)
const stop = Date.now()
const elapsed = stop - start
const size = JSON.stringify(context.response).length * 2
console.log(
`Operation $op completed in $elapsed ms and returned $size bytes`
)
,
请注意,这仅适用于每个请求。如果您需要更精细的信息,例如跟踪单个字段需要多长时间来解析,您需要使用 directive 或 middleware
【讨论】:
老兄——这看起来很棒。 (如果你把它做成打字稿的话,还有额外的拳头碰撞;-))[但我真的打赌我能弄明白]!谢谢 。 (一旦我证明它有效,我会检查它) 是否可以只在 willSendResponse 中捕获任何“突变”? 不要忘记为 apollo v3 制作 "async requestDidStart" 、 "async didResolveOperation" 和 "async willSendResponse"【参考方案2】:这是打字稿
import
ApolloServerPlugin,
from 'apollo-server-plugin-base';
import GraphQLRequestContext from 'apollo-server-types';
import GraphQLRequestListener from 'apollo-server-plugin-base/src/index'
// https://***.com/questions/59988906/how-do-i-write-a-apollo-server-plugin-to-log-the-request-and-its-duration
export const LogPlugin: ApolloServerPlugin =
requestDidStart<TContext>(_: GraphQLRequestContext<TContext>): GraphQLRequestListener<TContext>
const start = Date.now()
let op: string
return
didResolveOperation (context)
op = context.operationName
,
willSendResponse (context)
const stop = Date.now()
const elapsed = stop - start
const size = JSON.stringify(context.response).length * 2
console.log(
`operataion=$op duration=$elapsedms bytes=$size`
)
,
所有功劳归功于丹尼尔·里尔登
【讨论】:
【参考方案3】:我扩展了 Daniel Rearden 和 Jonathan 的答案,使其对我有用。 ApolloServerPlugin
类型需要异步调用。也许那是因为我使用的是 ApolloClient v.3?此外,操作变量必须键入null
,因为它被初始化为空。
无论如何,这是我所做的一个工作示例。我还冒昧地重命名了插件、操作变量、添加错误记录器并使用performance.now()
而不是Date.now()
。
还要注意MyApolloContext
是您自己定义的上下文(至少在使用apollo-server-express
时)
import MyModels from './models'
import
ApolloServerPlugin,
from 'apollo-server-plugin-base';
import GraphQLRequestContext from 'apollo-server-types';
import GraphQLRequestListener from 'apollo-server-plugin-base/src/index'
import performance from 'perf_hooks'
export interface MyApolloContext
models: MyModels
export const myDebugLoggerPlugin: ApolloServerPlugin =
async requestDidStart<MyApolloContext >(
requestContext: GraphQLRequestContext<MyApolloContext >,
): Promise<GraphQLRequestListener<MyApolloContext >>
const start = performance.now()
let operation: string | null
return
// Apollo server lifetime methods that you can use. https://www.apollographql.com/docs/apollo-server/integrations/plugins/#responding-to-request-lifecycle-events
async didResolveOperation(context)
operation = context.operationName
,
async willSendResponse(context)
const elapsed = Math.round(performance.now() - start)
const size = JSON.stringify(context.response).length * 2
console.log(
`ApolloServer log: operataion=$operation duration=$elapsedms bytes=$size`,
)
,
async didEncounterErrors(context)
console.log('Did encounter error: ', context)
,
,
async serverWillStart(_context) ,
【讨论】:
以上是关于如何编写 Apollo Server 插件来记录请求及其持续时间的主要内容,如果未能解决你的问题,请参考以下文章
如何在 apollo graphql 服务器中编写解析 graphql 过滤器
使用 Apollo Server 时如何生成 schema.graphql 文件?
如何从 Apollo Server 端点获得完整的 GraphQL 架构?
记录 apollo-server GraphQL 查询和每个请求的变量