如何编写 Apollo 服务器插件来记录请求及其持续时间

问题描述 投票:0回答:4

我很惊讶我找不到执行以下操作的库或示例:

我想要一个对服务器的每个请求的简单服务器日志,其中将说明请求的查询或突变,以及完成请求所需的时间

我知道有插件和扩展框架。但我不确定保持两个回调之间的状态的最佳实践是什么:

requestDidStart
willSendResponse

会吐出的东西:

path="createAccountMutation" service=20ms

额外的功劳是显示有效负载的大小

path="createAccountMutation" service=20ms bytes=355

很想看到打字稿中的解决方案

注意:我找到了apollo-log——但它不执行请求持续时间

谢谢!

typescript graphql apollo apollo-server
4个回答
16
投票

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`
        )
      }
    }
  },
}

请注意,这仅适用于每个请求。如果您需要更精细的东西,例如跟踪单个字段需要多长时间才能解析,您需要使用指令中间件


6
投票

这是 Typescript 中的

import {
  ApolloServerPlugin,
} from 'apollo-server-plugin-base';

import { GraphQLRequestContext } from 'apollo-server-types';
import { GraphQLRequestListener } from 'apollo-server-plugin-base/src/index'



// https://stackoverflow.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=${elapsed}ms bytes=${size}`
        )
      }
    }
  },
}

所有功劳归丹尼尔·里尔登


3
投票

我扩展了丹尼尔·里尔登和乔纳森的答案,使其对我有用。

ApolloServerPlugin
类型需要异步调用。也许这是因为我使用的是 ApolloClient v.3?此外,操作变量必须输入为
null
,因为它被初始化为 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=${elapsed}ms bytes=${size}`,
        )
      },
      async didEncounterErrors(context) {
        console.log('Did encounter error: ', context)
      },
    }
  },
  async serverWillStart(_context) {},
}

0
投票

@Emanuel,如果您不将await与async didResolveOperation(context)、async willSendResponse(context)和async didEncounterErrors(context)一起使用,它将使这些函数的执行并发,这可能会导致异常行为,因此可能不会发生超时正如预期的那样。

© www.soinside.com 2019 - 2024. All rights reserved.