mirror of
https://github.com/vale981/apollo-server
synced 2025-03-06 02:01:40 -05:00
Provide ability to specify client info in traces (#1631)
* Provide ability to specify client info in traces Adds the createClientInfo to apollo-engine-reporting, which enables the differentiation of clients based on the request, operation, and variables. This could be extended to include the response. However for the first release. It doesn't quite make sense. * Use extensions and context in createClientInfo * Remove support for clientAddress The frontend will not support it in the near future * create -> generate and make default generator createClientInfo -> generateClientInfo * Clarify default values
This commit is contained in:
parent
e2580a31de
commit
96af44e41a
7 changed files with 52 additions and 1 deletions
|
@ -5,6 +5,7 @@ All of the packages in the `apollo-server` repo are released with the same versi
|
||||||
### vNEXT
|
### vNEXT
|
||||||
|
|
||||||
- Update link inside Authentication Docs [PR #1682](https://github.com/apollographql/apollo-server/pull/1682)
|
- Update link inside Authentication Docs [PR #1682](https://github.com/apollographql/apollo-server/pull/1682)
|
||||||
|
- Provide ability to specify client info in traces [#1631](https://github.com/apollographql/apollo-server/pull/1631)
|
||||||
|
|
||||||
### v2.0.8
|
### v2.0.8
|
||||||
|
|
||||||
|
|
|
@ -350,3 +350,13 @@ addMockFunctionsToSchema({
|
||||||
* `maskErrorDetails`: boolean
|
* `maskErrorDetails`: boolean
|
||||||
|
|
||||||
Set to true to remove error details from the traces sent to Apollo's servers. Defaults to false.
|
Set to true to remove error details from the traces sent to Apollo's servers. Defaults to false.
|
||||||
|
|
||||||
|
* generateClientInfo?: (o: { context: any, extensions?: Record<string, any>}) => ClientInfo;
|
||||||
|
|
||||||
|
Creates the client information that is attached to the traces sent to the
|
||||||
|
Apollo backend. The context field is the execution context passed to the
|
||||||
|
resolvers and the extensions field corresponds to the same value in the POST
|
||||||
|
body or GET parameters. `ClientInfo` contains fields for `clientName` and
|
||||||
|
`clientVersion`, which are both optional. The default generation copies the
|
||||||
|
respective fields from `extensions.clientInfo`. If `clientName` or
|
||||||
|
`clientVersion` is not present, the values are set to the empty string.
|
||||||
|
|
|
@ -34,6 +34,11 @@ Traces.encode = function(message, originalWriter) {
|
||||||
return writer;
|
return writer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface ClientInfo {
|
||||||
|
clientName?: string;
|
||||||
|
clientVersion?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface EngineReportingOptions {
|
export interface EngineReportingOptions {
|
||||||
// API key for the service. Get this from
|
// API key for the service. Get this from
|
||||||
// [Engine](https://engine.apollographql.com) by logging in and creating
|
// [Engine](https://engine.apollographql.com) by logging in and creating
|
||||||
|
@ -83,6 +88,14 @@ export interface EngineReportingOptions {
|
||||||
sendReportsImmediately?: boolean;
|
sendReportsImmediately?: boolean;
|
||||||
// To remove the error message from traces, set this to true. Defaults to false
|
// To remove the error message from traces, set this to true. Defaults to false
|
||||||
maskErrorDetails?: boolean;
|
maskErrorDetails?: boolean;
|
||||||
|
// Creates the client information attached to the traces sent to the Apollo
|
||||||
|
// backend
|
||||||
|
generateClientInfo?: (
|
||||||
|
o: {
|
||||||
|
context: any;
|
||||||
|
extensions?: Record<string, any>;
|
||||||
|
},
|
||||||
|
) => ClientInfo;
|
||||||
|
|
||||||
// XXX Provide a way to set client_name, client_version, client_address,
|
// XXX Provide a way to set client_name, client_version, client_address,
|
||||||
// service, and service_version fields. They are currently not revealed in the
|
// service, and service_version fields. They are currently not revealed in the
|
||||||
|
|
|
@ -15,7 +15,7 @@ import {
|
||||||
} from 'graphql-extensions';
|
} from 'graphql-extensions';
|
||||||
import { Trace, google } from 'apollo-engine-reporting-protobuf';
|
import { Trace, google } from 'apollo-engine-reporting-protobuf';
|
||||||
|
|
||||||
import { EngineReportingOptions } from './agent';
|
import { EngineReportingOptions, ClientInfo } from './agent';
|
||||||
import { defaultSignature } from './signature';
|
import { defaultSignature } from './signature';
|
||||||
|
|
||||||
// EngineReportingExtension is the per-request GraphQLExtension which creates a
|
// EngineReportingExtension is the per-request GraphQLExtension which creates a
|
||||||
|
@ -38,6 +38,12 @@ export class EngineReportingExtension<TContext = any>
|
||||||
operationName: string,
|
operationName: string,
|
||||||
trace: Trace,
|
trace: Trace,
|
||||||
) => void;
|
) => void;
|
||||||
|
private generateClientInfo: (
|
||||||
|
o: {
|
||||||
|
context: any;
|
||||||
|
extensions?: Record<string, any>;
|
||||||
|
},
|
||||||
|
) => ClientInfo;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
options: EngineReportingOptions,
|
options: EngineReportingOptions,
|
||||||
|
@ -51,6 +57,11 @@ export class EngineReportingExtension<TContext = any>
|
||||||
const root = new Trace.Node();
|
const root = new Trace.Node();
|
||||||
this.trace.root = root;
|
this.trace.root = root;
|
||||||
this.nodes.set(responsePathAsString(undefined), root);
|
this.nodes.set(responsePathAsString(undefined), root);
|
||||||
|
this.generateClientInfo =
|
||||||
|
options.generateClientInfo ||
|
||||||
|
// Default to using the clientInfo field of the request's extensions, when
|
||||||
|
// the ClientInfo fields are undefined, we send the empty string
|
||||||
|
(({ extensions }) => (extensions && extensions.clientInfo) || {});
|
||||||
}
|
}
|
||||||
|
|
||||||
public requestDidStart(o: {
|
public requestDidStart(o: {
|
||||||
|
@ -60,6 +71,8 @@ export class EngineReportingExtension<TContext = any>
|
||||||
variables?: Record<string, any>;
|
variables?: Record<string, any>;
|
||||||
persistedQueryHit?: boolean;
|
persistedQueryHit?: boolean;
|
||||||
persistedQueryRegister?: boolean;
|
persistedQueryRegister?: boolean;
|
||||||
|
context: any;
|
||||||
|
extensions?: Record<string, any>;
|
||||||
}): EndHandler {
|
}): EndHandler {
|
||||||
this.trace.startTime = dateToTimestamp(new Date());
|
this.trace.startTime = dateToTimestamp(new Date());
|
||||||
this.startHrTime = process.hrtime();
|
this.startHrTime = process.hrtime();
|
||||||
|
@ -154,6 +167,16 @@ export class EngineReportingExtension<TContext = any>
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// While clientAddress could be a part of the protobuf, we'll ignore it for
|
||||||
|
// now, since the backend does not group by it and Engine frontend will not
|
||||||
|
// support it in the short term
|
||||||
|
const { clientName, clientVersion } = this.generateClientInfo({
|
||||||
|
context: o.context,
|
||||||
|
extensions: o.extensions,
|
||||||
|
});
|
||||||
|
this.trace.clientName = clientName || '';
|
||||||
|
this.trace.clientVersion = clientVersion || '';
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
this.trace.durationNs = durationHrTimeToNanos(
|
this.trace.durationNs = durationHrTimeToNanos(
|
||||||
process.hrtime(this.startHrTime),
|
process.hrtime(this.startHrTime),
|
||||||
|
|
|
@ -402,6 +402,7 @@ export async function runHttpQuery(
|
||||||
: false,
|
: false,
|
||||||
request: request.request,
|
request: request.request,
|
||||||
extensions: optionsObject.extensions,
|
extensions: optionsObject.extensions,
|
||||||
|
queryExtensions: extensions,
|
||||||
persistedQueryHit,
|
persistedQueryHit,
|
||||||
persistedQueryRegister,
|
persistedQueryRegister,
|
||||||
};
|
};
|
||||||
|
|
|
@ -66,6 +66,7 @@ export interface QueryOptions {
|
||||||
cacheControl?: boolean | CacheControlExtensionOptions;
|
cacheControl?: boolean | CacheControlExtensionOptions;
|
||||||
request: Pick<Request, 'url' | 'method' | 'headers'>;
|
request: Pick<Request, 'url' | 'method' | 'headers'>;
|
||||||
extensions?: Array<() => GraphQLExtension>;
|
extensions?: Array<() => GraphQLExtension>;
|
||||||
|
queryExtensions?: Record<string, any>;
|
||||||
persistedQueryHit?: boolean;
|
persistedQueryHit?: boolean;
|
||||||
persistedQueryRegister?: boolean;
|
persistedQueryRegister?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -136,6 +137,7 @@ function doRunQuery(options: QueryOptions): Promise<GraphQLResponse> {
|
||||||
persistedQueryHit: options.persistedQueryHit,
|
persistedQueryHit: options.persistedQueryHit,
|
||||||
persistedQueryRegister: options.persistedQueryRegister,
|
persistedQueryRegister: options.persistedQueryRegister,
|
||||||
context,
|
context,
|
||||||
|
extensions: options.queryExtensions,
|
||||||
});
|
});
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
.then(
|
.then(
|
||||||
|
|
|
@ -81,6 +81,7 @@ export class GraphQLExtensionStack<TContext = any> {
|
||||||
persistedQueryHit?: boolean;
|
persistedQueryHit?: boolean;
|
||||||
persistedQueryRegister?: boolean;
|
persistedQueryRegister?: boolean;
|
||||||
context: TContext;
|
context: TContext;
|
||||||
|
extensions?: Record<string, any>;
|
||||||
}): EndHandler {
|
}): EndHandler {
|
||||||
return this.handleDidStart(
|
return this.handleDidStart(
|
||||||
ext => ext.requestDidStart && ext.requestDidStart(o),
|
ext => ext.requestDidStart && ext.requestDidStart(o),
|
||||||
|
|
Loading…
Add table
Reference in a new issue