@envelop/apollo-tracing
Version:
This plugin tracks execution and resolvers and reports it using [`apollo-tracing`](https://github.com/apollographql/apollo-server/tree/main/packages/apollo-tracing) format (based on GraphQL `extensions`).
80 lines (79 loc) • 3.36 kB
JavaScript
import { responsePathAsArray } from 'graphql';
import { handleStreamOrSingleExecutionResult } from '@envelop/core';
import { useOnResolve } from '@envelop/on-resolve';
const HR_TO_NS = 1e9;
const NS_TO_MS = 1e6;
function durationHrTimeToNanos(hrtime) {
return hrtime[0] * HR_TO_NS + hrtime[1];
}
const deltaFrom = (hrtime) => {
const delta = process.hrtime(hrtime);
const ns = delta[0] * HR_TO_NS + delta[1];
return {
ns,
get ms() {
return ns / NS_TO_MS;
},
};
};
const apolloTracingSymbol = Symbol('apolloTracing');
export const useApolloTracing = () => {
return {
onPluginInit({ addPlugin }) {
addPlugin(useOnResolve(({ info, context }) => {
const ctx = context[apolloTracingSymbol];
// Taken from https://github.com/apollographql/apollo-server/blob/main/packages/apollo-tracing/src/index.ts
const resolverCall = {
path: info.path,
fieldName: info.fieldName,
parentType: info.parentType,
returnType: info.returnType,
startOffset: process.hrtime(ctx.hrtime),
};
return () => {
resolverCall.endOffset = process.hrtime(ctx.hrtime);
ctx.resolversTiming.push(resolverCall);
};
}));
},
onExecute(onExecuteContext) {
const ctx = {
startTime: new Date(),
resolversTiming: [],
hrtime: process.hrtime(),
};
onExecuteContext.extendContext({ [apolloTracingSymbol]: ctx });
return {
onExecuteDone(payload) {
const endTime = new Date();
const tracing = {
version: 1,
startTime: ctx.startTime.toISOString(),
endTime: endTime.toISOString(),
duration: deltaFrom(ctx.hrtime).ns,
execution: {
resolvers: ctx.resolversTiming.map(resolverCall => {
const startOffset = durationHrTimeToNanos(resolverCall.startOffset);
const duration = resolverCall.endOffset
? durationHrTimeToNanos(resolverCall.endOffset) - startOffset
: 0;
return {
path: [...responsePathAsArray(resolverCall.path)],
parentType: resolverCall.parentType.toString(),
fieldName: resolverCall.fieldName,
returnType: resolverCall.returnType.toString(),
startOffset,
duration,
};
}),
},
};
return handleStreamOrSingleExecutionResult(payload, ({ result }) => {
result.extensions = result.extensions || {};
result.extensions.tracing = tracing;
});
},
};
},
};
};