import { ApolloServerPlugin , GraphQLRequestListener , GraphQLRequestContext } from 'apollo-server-plugin-base' ; import chalk from 'chalk' ; import stringify from 'fast-safe-stringify' ; import loglevel from 'loglevelnext' ; import { customAlphabet } from 'nanoid' ; export type LogMutateData = Record < string , string > & { context : GraphQLRequestContext ; } ; export interface LogOptions { events : { [ name ] : boolean ; } ; mutate : ( data : LogMutateData ) => LogMutateData ; prefix : string ; timestamp : boolean ; } const defaults = { events : { didEncounterErrors : true , didResolveOperation : false , executionDidStart : false , parsingDidStart : false , responseForOperation : false , validationDidStart : false , willSendResponse : true } , mutate : ( data ) => data , prefix : 'apollo' , timestamp : false } ; const nanoid = customAlphabet ( '1234567890abcdef' , 6 ) ; const ignoredOps = [ 'IntrospectionQuery' ] ; const getLog = ( options ) => { const template = `${ options . timestamp ? '[{{time}}] ' : '' }{{level}} ` ; const prefix = { level : ( { level } ) => chalk [ level === 'info' ? 'blue' : 'red' ] ( chalk `{inverse ${ options . prefix }}` ) , template , time : ( ) => new Date ( ) . toTimeString ( ) . split ( ' ' ) [ 0 ] } ; const log = loglevel . create ( { level : 'info' , name : 'apollo-log' , prefix } ) ; return ( id , data ) => { const mutated = options . mutate ?. ( data as LogMutateData ) ; log [ ( data as any ) . errors ? 'error' : 'info' ] ( chalk `{dim ${ id }}` , stringify ( mutated ) ) ; } ; } ; export const ApolloLogPlugin = ( opts ? ) : ApolloServerPlugin => { const options = Object . assign ( { } , defaults , opts ) ; const log = getLog ( options ) ; return { requestDidStart ( context ) { const operationId = nanoid ( ) ; const ignore = ignoredOps . includes ( context . operationName || '' ) ; if ( ! ignore ) { const query = context . request . query ?. replace ( /\n/g , '' ) ; const variables = Object . keys ( context . request . variables || { } ) ; log ( operationId , { event : 'request' , operationName : context . operationName , query , variables , context } ) ; } const { events } = options ; const handlers = { didEncounterErrors ( { errors } ) { events . didEncounterErrors && log ( operationId , { event : 'errors' , errors , context } ) ; } , didResolveOperation ( { metrics , operationName } ) { events . didResolveOperation && log ( operationId , { event : 'didResolveOperation' , metrics , operationName , context } ) ; } , executionDidStart ( { metrics } ) { events . executionDidStart && log ( operationId , { event : 'executionDidStart' , metrics , context } ) ; } , parsingDidStart ( { metrics } ) { events . parsingDidStart && log ( operationId , { event : 'parsingDidStart' , metrics , context } ) ; } , responseForOperation ( { metrics , operationName } ) { events . responseForOperation && log ( operationId , { event : 'responseForOperation' , metrics , operationName , context } ) ; return null ; } , validationDidStart ( { metrics } ) { events . validationDidStart && log ( operationId , { event : '' , metrics , context } ) ; } , willSendResponse ( { metrics } ) { options . events . willSendResponse && log ( operationId , { event : 'response' , metrics , context } ) ; } } ; return handlers ; } } ; } ;