1 | import { responsePathAsArray, GraphQLResolveInfo } from "graphql";
|
2 |
|
3 | type HighResolutionTime = [number, number];
|
4 |
|
5 | const traceHRStartTime = Symbol("HR Start Time");
|
6 | const traceWallStartTime = Symbol("Wall Start Time");
|
7 | export const apolloTracingContext = Symbol("Apollo Tracing Context");
|
8 |
|
9 | export interface ApolloTracingContext {
|
10 | [traceHRStartTime]: HighResolutionTime;
|
11 | [traceWallStartTime]: Date;
|
12 | version: 1;
|
13 | startTime: string;
|
14 | endTime: string;
|
15 | duration: number;
|
16 | execution: {
|
17 | parsing?: {
|
18 | startOffset: number;
|
19 | duration: number;
|
20 | };
|
21 | validation?: {
|
22 | startOffset: number;
|
23 | duration: number;
|
24 | };
|
25 | resolvers: ApolloTracingResolverStats[];
|
26 | };
|
27 | }
|
28 |
|
29 | export interface ApolloTracingResolverStats {
|
30 | path: (string | number)[];
|
31 | parentType: string;
|
32 | fieldName: string;
|
33 | returnType: string;
|
34 | startOffset: number;
|
35 | duration: number;
|
36 | }
|
37 |
|
38 | function durationHrTimeToNanos(hrtime: HighResolutionTime): number {
|
39 | return hrtime[0] * 1e9 + hrtime[1];
|
40 | }
|
41 |
|
42 | export async function apolloTracingGraphQLMiddleware(
|
43 | resolve: Function,
|
44 | parent: any,
|
45 | args: any,
|
46 | context: { [apolloTracingContext]: ApolloTracingContext },
|
47 | info: GraphQLResolveInfo
|
48 | ): Promise<any> {
|
49 | const startOffset = durationHrTimeToNanos(
|
50 | process.hrtime(context[apolloTracingContext][traceHRStartTime])
|
51 | );
|
52 | try {
|
53 | return await resolve(parent, args, context, info);
|
54 | } finally {
|
55 | context[apolloTracingContext].execution.resolvers.push({
|
56 | path: [...responsePathAsArray(info.path)],
|
57 | parentType: info.parentType.toString(),
|
58 | fieldName: info.fieldName,
|
59 | returnType: info.returnType.toString(),
|
60 | startOffset,
|
61 | duration:
|
62 | durationHrTimeToNanos(
|
63 | process.hrtime(context[apolloTracingContext][traceHRStartTime])
|
64 | ) - startOffset
|
65 | });
|
66 | }
|
67 | }
|
68 |
|
69 | export function startTracingContext(): ApolloTracingContext {
|
70 | const wallStartTime = new Date();
|
71 | return {
|
72 | [traceHRStartTime]: process.hrtime(),
|
73 | [traceWallStartTime]: wallStartTime,
|
74 | version: 1,
|
75 | startTime: wallStartTime.toISOString(),
|
76 | endTime: "",
|
77 | duration: 0,
|
78 | execution: {
|
79 | resolvers: []
|
80 | }
|
81 | };
|
82 | }
|
83 |
|
84 | export function endTracingContext(context: ApolloTracingContext): void {
|
85 | context.endTime = new Date().toISOString();
|
86 | context.duration = durationHrTimeToNanos(
|
87 | process.hrtime(context[traceHRStartTime])
|
88 | );
|
89 | }
|