apollo-tracing
Advanced tools
Comparing version 0.10.1-alpha.0 to 0.11.0-alpha.0
@@ -1,3 +0,2 @@ | ||
import { GraphQLResolveInfo } from 'graphql'; | ||
import { GraphQLExtension } from 'graphql-extensions'; | ||
import { ApolloServerPlugin } from "apollo-server-plugin-base"; | ||
export interface TracingFormat { | ||
@@ -19,15 +18,3 @@ version: 1; | ||
} | ||
export declare class TracingExtension<TContext = any> implements GraphQLExtension<TContext> { | ||
private startWallTime?; | ||
private endWallTime?; | ||
private startHrTime?; | ||
private duration?; | ||
private resolverCalls; | ||
requestDidStart(): void; | ||
executionDidStart(): () => void; | ||
willResolveField(_source: any, _args: { | ||
[argName: string]: any; | ||
}, _context: TContext, info: GraphQLResolveInfo): () => void; | ||
format(): [string, TracingFormat] | undefined; | ||
} | ||
export declare const plugin: (_futureOptions?: {}) => () => ApolloServerPlugin<Record<string, any>>; | ||
//# sourceMappingURL=index.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const graphql_1 = require("graphql"); | ||
class TracingExtension { | ||
constructor() { | ||
this.resolverCalls = []; | ||
} | ||
const { PACKAGE_NAME } = require("../package.json").name; | ||
exports.plugin = (_futureOptions = {}) => () => ({ | ||
requestDidStart() { | ||
this.startWallTime = new Date(); | ||
this.startHrTime = process.hrtime(); | ||
} | ||
executionDidStart() { | ||
return () => { | ||
this.duration = process.hrtime(this.startHrTime); | ||
this.endWallTime = new Date(); | ||
}; | ||
} | ||
willResolveField(_source, _args, _context, info) { | ||
const resolverCall = { | ||
path: info.path, | ||
fieldName: info.fieldName, | ||
parentType: info.parentType, | ||
returnType: info.returnType, | ||
startOffset: process.hrtime(this.startHrTime), | ||
}; | ||
this.resolverCalls.push(resolverCall); | ||
return () => { | ||
resolverCall.endOffset = process.hrtime(this.startHrTime); | ||
}; | ||
} | ||
format() { | ||
if (typeof this.startWallTime === 'undefined' || | ||
typeof this.endWallTime === 'undefined' || | ||
typeof this.duration === 'undefined') { | ||
return; | ||
} | ||
return [ | ||
'tracing', | ||
{ | ||
version: 1, | ||
startTime: this.startWallTime.toISOString(), | ||
endTime: this.endWallTime.toISOString(), | ||
duration: durationHrTimeToNanos(this.duration), | ||
execution: { | ||
resolvers: this.resolverCalls.map(resolverCall => { | ||
const startOffset = durationHrTimeToNanos(resolverCall.startOffset); | ||
const duration = resolverCall.endOffset | ||
? durationHrTimeToNanos(resolverCall.endOffset) - startOffset | ||
: 0; | ||
return { | ||
path: [...graphql_1.responsePathAsArray(resolverCall.path)], | ||
parentType: resolverCall.parentType.toString(), | ||
fieldName: resolverCall.fieldName, | ||
returnType: resolverCall.returnType.toString(), | ||
startOffset, | ||
duration, | ||
}; | ||
}), | ||
let startWallTime; | ||
let endWallTime; | ||
let startHrTime; | ||
let duration; | ||
const resolverCalls = []; | ||
startWallTime = new Date(); | ||
startHrTime = process.hrtime(); | ||
return { | ||
executionDidStart: () => ({ | ||
executionDidEnd: () => { | ||
duration = process.hrtime(startHrTime); | ||
endWallTime = new Date(); | ||
}, | ||
willResolveField({ info }) { | ||
const resolverCall = { | ||
path: info.path, | ||
fieldName: info.fieldName, | ||
parentType: info.parentType, | ||
returnType: info.returnType, | ||
startOffset: process.hrtime(startHrTime), | ||
}; | ||
resolverCalls.push(resolverCall); | ||
return () => { | ||
resolverCall.endOffset = process.hrtime(startHrTime); | ||
}; | ||
}, | ||
}), | ||
willSendResponse({ response }) { | ||
if (typeof startWallTime === 'undefined' || | ||
typeof endWallTime === 'undefined' || | ||
typeof duration === 'undefined') { | ||
return; | ||
} | ||
const extensions = response.extensions || (response.extensions = Object.create(null)); | ||
if (typeof extensions.tracing !== 'undefined') { | ||
throw new Error(PACKAGE_NAME + ": Could not add `tracing` to " + | ||
"`extensions` since `tracing` was unexpectedly already present."); | ||
} | ||
extensions.tracing = { | ||
version: 1, | ||
startTime: startWallTime.toISOString(), | ||
endTime: endWallTime.toISOString(), | ||
duration: durationHrTimeToNanos(duration), | ||
execution: { | ||
resolvers: resolverCalls.map(resolverCall => { | ||
const startOffset = durationHrTimeToNanos(resolverCall.startOffset); | ||
const duration = resolverCall.endOffset | ||
? durationHrTimeToNanos(resolverCall.endOffset) - startOffset | ||
: 0; | ||
return { | ||
path: [...graphql_1.responsePathAsArray(resolverCall.path)], | ||
parentType: resolverCall.parentType.toString(), | ||
fieldName: resolverCall.fieldName, | ||
returnType: resolverCall.returnType.toString(), | ||
startOffset, | ||
duration, | ||
}; | ||
}), | ||
}, | ||
}; | ||
}, | ||
]; | ||
} | ||
} | ||
exports.TracingExtension = TracingExtension; | ||
}; | ||
}, | ||
}); | ||
function durationHrTimeToNanos(hrtime) { | ||
@@ -66,0 +72,0 @@ return hrtime[0] * 1e9 + hrtime[1]; |
{ | ||
"name": "apollo-tracing", | ||
"version": "0.10.1-alpha.0", | ||
"version": "0.11.0-alpha.0", | ||
"description": "Collect and expose trace data for GraphQL requests", | ||
@@ -15,3 +15,3 @@ "main": "./dist/index.js", | ||
"apollo-server-env": "^2.4.4-alpha.0", | ||
"graphql-extensions": "^0.12.1-alpha.0" | ||
"apollo-server-plugin-base": "^0.9.0-alpha.0" | ||
}, | ||
@@ -21,3 +21,3 @@ "peerDependencies": { | ||
}, | ||
"gitHead": "626c4b6505ed98d47855712a568fc4eba9d8ff18" | ||
"gitHead": "bfef89b8cc44d4bd94f9d5fcd3df8941de526360" | ||
} |
@@ -15,12 +15,11 @@ # Apollo Tracing (for Node.js) | ||
The only code change required is to add `tracing: true` to the options passed to the Apollo Server middleware function for your framework of choice. For example, for Express: | ||
The only code change required is to add `tracing: true` to the options passed to the `ApolloServer` constructor options for your integration of choice. For example, for [`apollo-server-express`](https://npm.im/apollo-server-express): | ||
```javascript | ||
app.use('/graphql', bodyParser.json(), graphqlExpress({ | ||
const { ApolloServer } = require('apollo-server-express'); | ||
const server = new ApolloServer({ | ||
schema, | ||
context: {}, | ||
tracing: true, | ||
})); | ||
}); | ||
``` | ||
> If you are using `express-graphql`, we recommend you switch to Apollo Server. Both `express-graphql` and Apollo Server are based on the [`graphql-js`](https://github.com/graphql/graphql-js) reference implementation, and switching should only require changing a few lines of code. |
172
src/index.ts
import { | ||
ResponsePath, | ||
responsePathAsArray, | ||
GraphQLResolveInfo, | ||
GraphQLType, | ||
} from 'graphql'; | ||
import { ApolloServerPlugin } from "apollo-server-plugin-base"; | ||
import { GraphQLExtension } from 'graphql-extensions'; | ||
const { PACKAGE_NAME } = require("../package.json").name; | ||
@@ -36,90 +36,100 @@ export interface TracingFormat { | ||
export class TracingExtension<TContext = any> | ||
implements GraphQLExtension<TContext> { | ||
private startWallTime?: Date; | ||
private endWallTime?: Date; | ||
private startHrTime?: HighResolutionTime; | ||
private duration?: HighResolutionTime; | ||
export const plugin = (_futureOptions = {}) => (): ApolloServerPlugin => ({ | ||
requestDidStart() { | ||
let startWallTime: Date | undefined; | ||
let endWallTime: Date | undefined; | ||
let startHrTime: HighResolutionTime | undefined; | ||
let duration: HighResolutionTime | undefined; | ||
const resolverCalls: ResolverCall[] = []; | ||
private resolverCalls: ResolverCall[] = []; | ||
startWallTime = new Date(); | ||
startHrTime = process.hrtime(); | ||
public requestDidStart() { | ||
this.startWallTime = new Date(); | ||
this.startHrTime = process.hrtime(); | ||
} | ||
return { | ||
executionDidStart: () => ({ | ||
// It's a little odd that we record the end time after execution rather | ||
// than at the end of the whole request, but because we need to include | ||
// our formatted trace in the request itself, we have to record it | ||
// before the request is over! | ||
public executionDidStart() { | ||
// It's a little odd that we record the end time after execution rather than | ||
// at the end of the whole request, but because we need to include our | ||
// formatted trace in the request itself, we have to record it before the | ||
// request is over! It's also odd that we don't do traces for parse or | ||
// validation errors, but runQuery doesn't currently support that, as | ||
// format() is only invoked after execution. | ||
return () => { | ||
this.duration = process.hrtime(this.startHrTime); | ||
this.endWallTime = new Date(); | ||
}; | ||
} | ||
// Historically speaking: It's WAS odd that we don't do traces for parse | ||
// or validation errors. Reason being: at the time that this was written | ||
// (now a plugin but originally an extension)). That was the case | ||
// because runQuery DIDN'T (again, at the time, when it was an | ||
// extension) support that since format() was only invoked after | ||
// execution. | ||
executionDidEnd: () => { | ||
duration = process.hrtime(startHrTime); | ||
endWallTime = new Date(); | ||
}, | ||
public willResolveField( | ||
_source: any, | ||
_args: { [argName: string]: any }, | ||
_context: TContext, | ||
info: GraphQLResolveInfo, | ||
) { | ||
const resolverCall: ResolverCall = { | ||
path: info.path, | ||
fieldName: info.fieldName, | ||
parentType: info.parentType, | ||
returnType: info.returnType, | ||
startOffset: process.hrtime(this.startHrTime), | ||
}; | ||
willResolveField({ info }) { | ||
const resolverCall: ResolverCall = { | ||
path: info.path, | ||
fieldName: info.fieldName, | ||
parentType: info.parentType, | ||
returnType: info.returnType, | ||
startOffset: process.hrtime(startHrTime), | ||
}; | ||
this.resolverCalls.push(resolverCall); | ||
resolverCalls.push(resolverCall); | ||
return () => { | ||
resolverCall.endOffset = process.hrtime(this.startHrTime); | ||
}; | ||
} | ||
return () => { | ||
resolverCall.endOffset = process.hrtime(startHrTime); | ||
}; | ||
}, | ||
}), | ||
public format(): [string, TracingFormat] | undefined { | ||
// In the event that we are called prior to the initialization of critical | ||
// date metrics, we'll return undefined to signal that the extension did not | ||
// format properly. Any undefined extension results are simply purged by | ||
// the graphql-extensions module. | ||
if ( | ||
typeof this.startWallTime === 'undefined' || | ||
typeof this.endWallTime === 'undefined' || | ||
typeof this.duration === 'undefined' | ||
) { | ||
return; | ||
} | ||
willSendResponse({ response }) { | ||
// In the event that we are called prior to the initialization of | ||
// critical date metrics, we'll return undefined to signal that the | ||
// extension did not format properly. Any undefined extension | ||
// results are simply purged by the graphql-extensions module. | ||
if ( | ||
typeof startWallTime === 'undefined' || | ||
typeof endWallTime === 'undefined' || | ||
typeof duration === 'undefined' | ||
) { | ||
return; | ||
} | ||
return [ | ||
'tracing', | ||
{ | ||
version: 1, | ||
startTime: this.startWallTime.toISOString(), | ||
endTime: this.endWallTime.toISOString(), | ||
duration: durationHrTimeToNanos(this.duration), | ||
execution: { | ||
resolvers: this.resolverCalls.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, | ||
}; | ||
}), | ||
}, | ||
const extensions = | ||
response.extensions || (response.extensions = Object.create(null)); | ||
// Be defensive and make sure nothing else (other plugin, etc.) has | ||
// already used the `tracing` property on `extensions`. | ||
if (typeof extensions.tracing !== 'undefined') { | ||
throw new Error(PACKAGE_NAME + ": Could not add `tracing` to " + | ||
"`extensions` since `tracing` was unexpectedly already present."); | ||
} | ||
// Set the extensions. | ||
extensions.tracing = { | ||
version: 1, | ||
startTime: startWallTime.toISOString(), | ||
endTime: endWallTime.toISOString(), | ||
duration: durationHrTimeToNanos(duration), | ||
execution: { | ||
resolvers: resolverCalls.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, | ||
}; | ||
}), | ||
}, | ||
}; | ||
}, | ||
]; | ||
} | ||
} | ||
}; | ||
}, | ||
}) | ||
@@ -126,0 +136,0 @@ type HighResolutionTime = [number, number]; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
14592
225
25
+ Addedapollo-engine-reporting-protobuf@0.5.2(transitive)
+ Addedapollo-server-plugin-base@0.9.1(transitive)
+ Addedapollo-server-types@0.5.1(transitive)
- Removedgraphql-extensions@^0.12.1-alpha.0
- Removed@apollographql/apollo-tools@0.4.14(transitive)
- Removed@types/node@22.7.5(transitive)
- Removed@types/node-fetch@2.5.10(transitive)
- Removedapollo-env@0.9.2(transitive)
- Removedapollo-reporting-protobuf@0.6.2(transitive)
- Removedapollo-server-env@3.2.0(transitive)
- Removedapollo-server-types@0.6.3(transitive)
- Removedasynckit@0.4.0(transitive)
- Removedcombined-stream@1.0.8(transitive)
- Removedcore-js@3.38.1(transitive)
- Removeddelayed-stream@1.0.0(transitive)
- Removedform-data@3.0.1(transitive)
- Removedgraphql-extensions@0.12.8(transitive)
- Removedinherits@2.0.4(transitive)
- Removedmime-db@1.52.0(transitive)
- Removedmime-types@2.1.35(transitive)
- Removedsafe-buffer@5.2.1(transitive)
- Removedsha.js@2.4.11(transitive)
- Removedundici-types@6.19.8(transitive)