apollo-server/packages/apollo-server-core/src/logging.ts
David Glasser 5c65742a39
Factor out runQuery's use of logFunction into an extension (#1128)
This requires a slightly newer graphql-extensions beta which has more arguments
to requestDidStart.

Also make it ok to not pass logFunction to formatApolloErrors, and make sure
custom fieldResolvers continue to work with extensions (by upgrading the
dependency and fixing a logic bug).

The custom fieldResolvers fix means that we now unconditionally put the
extension stack on the GraphQL context, which means that the context can no
longer be (say) a string.  I changed a test that expected string contexts to
work. You couldn't use a string for a context when using extensions before, so
this isn't that big of a change.
2018-06-01 21:16:25 -07:00

106 lines
2.7 KiB
TypeScript

import { GraphQLExtension, GraphQLResponse } from 'graphql-extensions';
import { print, DocumentNode } from 'graphql';
export enum LogAction {
request,
parse,
validation,
execute,
setup,
cleanup,
}
export enum LogStep {
start,
end,
status,
}
export interface LogMessage {
action: LogAction;
step: LogStep;
key?: string;
data?: any;
}
export interface LogFunction {
(message: LogMessage);
}
// A GraphQLExtension that implements the existing logFunction interface. Note
// that now that custom extensions are supported, you may just want to do your
// logging as a GraphQLExtension rather than write a LogFunction.
export class LogFunctionExtension<TContext = any>
implements GraphQLExtension<TContext> {
private logFunction: LogFunction;
public constructor(logFunction: LogFunction) {
this.logFunction = logFunction;
}
public requestDidStart(options: {
request: Request;
queryString?: string;
parsedQuery?: DocumentNode;
operationName?: string;
variables?: { [key: string]: any };
}) {
this.logFunction({ action: LogAction.request, step: LogStep.start });
const loggedQuery = options.queryString || print(options.parsedQuery);
this.logFunction({
action: LogAction.request,
step: LogStep.status,
key: 'query',
data: loggedQuery,
});
this.logFunction({
action: LogAction.request,
step: LogStep.status,
key: 'variables',
data: options.variables,
});
this.logFunction({
action: LogAction.request,
step: LogStep.status,
key: 'operationName',
data: options.operationName,
});
return (...errors: Array<Error>) => {
// If there are no errors, we log in willSendResponse instead.
if (errors.length) {
this.logFunction({ action: LogAction.request, step: LogStep.end });
}
};
}
public parsingDidStart() {
this.logFunction({ action: LogAction.parse, step: LogStep.start });
return () => {
this.logFunction({ action: LogAction.parse, step: LogStep.end });
};
}
public validationDidStart() {
this.logFunction({ action: LogAction.validation, step: LogStep.start });
return () => {
this.logFunction({ action: LogAction.validation, step: LogStep.end });
};
}
public executionDidStart() {
this.logFunction({ action: LogAction.execute, step: LogStep.start });
return () => {
this.logFunction({ action: LogAction.execute, step: LogStep.end });
};
}
public willSendResponse(o: { graphqlResponse: GraphQLResponse }) {
this.logFunction({
action: LogAction.request,
step: LogStep.end,
key: 'response',
data: o.graphqlResponse,
});
}
}