Skip to content

Commit

Permalink
Introduce optional logger property for apollo-engine-reporting.
Browse files Browse the repository at this point in the history
This is a more granular part of a larger project to introduce the concept of
a `logger` to various parts of the Apollo Server stack - including
`ApolloGateway` and `ApolloServer`.

Since this extension is most always initiated by `ApolloServer` itself, this
will generally be a property that is inherited by the configuration that
`ApolloServer` provides.

The intention is: if someone provides a `logger` at the `ApolloServer`
level, we will propagate it down to various sub-components, including the
`apollo-engine-reporting` extension.
  • Loading branch information
abernix committed Mar 16, 2020
1 parent ff20305 commit a9f1d67
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 10 deletions.
29 changes: 22 additions & 7 deletions packages/apollo-engine-reporting/src/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { fetch, RequestAgent, Response } from 'apollo-server-env';
import retry from 'async-retry';

import { EngineReportingExtension } from './extension';
import { GraphQLRequestContext } from 'apollo-server-types';
import { GraphQLRequestContext, Logger } from 'apollo-server-types';
import { InMemoryLRUCache } from 'apollo-server-caching';
import { defaultEngineReportingSignature } from 'apollo-graphql';

Expand Down Expand Up @@ -185,6 +185,13 @@ export interface EngineReportingOptions<TContext> {
* Creates the client information for operation traces.
*/
generateClientInfo?: GenerateClientInfo<TContext>;

/**
* A logger interface to be used for output and errors. When not provided
* it will default to the server's own `logger` implementation and use
* `console` when that is not available.
*/
logger?: Logger;
}

export interface AddTraceArgs {
Expand All @@ -209,6 +216,7 @@ const serviceHeaderDefaults = {
// to the Engine server.
export class EngineReportingAgent<TContext = any> {
private options: EngineReportingOptions<TContext>;
private logger: Logger = console;
private apiKey: string;
private reports: { [schemaHash: string]: FullTracesReport } = Object.create(
null,
Expand All @@ -226,6 +234,7 @@ export class EngineReportingAgent<TContext = any> {

public constructor(options: EngineReportingOptions<TContext> = {}) {
this.options = options;
if (options.logger) this.logger = options.logger;
this.apiKey = options.apiKey || process.env.ENGINE_API_KEY || '';
if (!this.apiKey) {
throw new Error(
Expand All @@ -236,7 +245,7 @@ export class EngineReportingAgent<TContext = any> {
// Since calculating the signature for Engine reporting is potentially an
// expensive operation, we'll cache the signatures we generate and re-use
// them based on repeated traces for the same `queryHash`.
this.signatureCache = createSignatureCache();
this.signatureCache = createSignatureCache({ logger: this.logger });

this.sendReportsImmediately = options.sendReportsImmediately;
if (!this.sendReportsImmediately) {
Expand Down Expand Up @@ -353,7 +362,9 @@ export class EngineReportingAgent<TContext = any> {
await Promise.resolve();

if (this.options.debugPrintReports) {
console.log(`Engine sending report: ${JSON.stringify(report.toJSON())}`);
this.logger.debug(
`Engine sending report: ${JSON.stringify(report.toJSON())}`,
);
}

const protobufError = FullTracesReport.verify(report);
Expand Down Expand Up @@ -430,7 +441,7 @@ export class EngineReportingAgent<TContext = any> {
);
}
if (this.options.debugPrintReports) {
console.log(`Engine report: status ${response.status}`);
this.logger.debug(`Engine report: status ${response.status}`);
}
}

Expand Down Expand Up @@ -516,7 +527,7 @@ export class EngineReportingAgent<TContext = any> {
if (this.options.reportErrorFunction) {
this.options.reportErrorFunction(err);
} else {
console.error(err.message);
this.logger.error(err.message);
}
});
}
Expand All @@ -529,7 +540,11 @@ export class EngineReportingAgent<TContext = any> {
}
}

function createSignatureCache(): InMemoryLRUCache<string> {
function createSignatureCache({
logger,
}: {
logger: Logger;
}): InMemoryLRUCache<string> {
let lastSignatureCacheWarn: Date;
let lastSignatureCacheDisposals: number = 0;
return new InMemoryLRUCache<string>({
Expand Down Expand Up @@ -558,7 +573,7 @@ function createSignatureCache(): InMemoryLRUCache<string> {
) {
// Log the time that we last displayed the message.
lastSignatureCacheWarn = new Date();
console.warn(
logger.warn(
[
'This server is processing a high number of unique operations. ',
`A total of ${lastSignatureCacheDisposals} records have been `,
Expand Down
5 changes: 4 additions & 1 deletion packages/apollo-engine-reporting/src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GraphQLRequestContext, WithRequired } from 'apollo-server-types';
import { GraphQLRequestContext, WithRequired, Logger } from 'apollo-server-types';
import { Request, Headers } from 'apollo-server-env';
import {
GraphQLResolveInfo,
Expand Down Expand Up @@ -30,6 +30,7 @@ const clientVersionHeaderKey = 'apollographql-client-version';
// Its public methods all implement the GraphQLExtension interface.
export class EngineReportingExtension<TContext = any>
implements GraphQLExtension<TContext> {
private logger: Logger = console;
private treeBuilder: EngineReportingTreeBuilder;
private explicitOperationName?: string | null;
private queryString?: string;
Expand All @@ -46,12 +47,14 @@ export class EngineReportingExtension<TContext = any>
this.options = {
...options,
};
if (options.logger) this.logger = options.logger;
this.addTrace = addTrace;
this.generateClientInfo =
options.generateClientInfo || defaultGenerateClientInfo;

this.treeBuilder = new EngineReportingTreeBuilder({
rewriteError: options.rewriteError,
logger: this.logger,
});
}

Expand Down
7 changes: 5 additions & 2 deletions packages/apollo-engine-reporting/src/treeBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import {
PersistedQueryNotFoundError,
PersistedQueryNotSupportedError,
} from 'apollo-server-errors';
import { InvalidGraphQLRequestError } from 'apollo-server-types';
import { InvalidGraphQLRequestError, Logger } from 'apollo-server-types';

function internalError(message: string) {
return new Error(`[internal apollo-server error] ${message}`);
}

export class EngineReportingTreeBuilder {
private rootNode = new Trace.Node();
private logger: Logger = console;
public trace = new Trace({ root: this.rootNode });
public startHrTime?: [number, number];
private stopped = false;
Expand All @@ -26,9 +27,11 @@ export class EngineReportingTreeBuilder {
private rewriteError?: (err: GraphQLError) => GraphQLError | null;

public constructor(options: {
logger?: Logger;
rewriteError?: (err: GraphQLError) => GraphQLError | null;
}) {
this.rewriteError = options.rewriteError;
if (options.logger) this.logger = options.logger;
}

public startTiming() {
Expand Down Expand Up @@ -137,7 +140,7 @@ export class EngineReportingTreeBuilder {
if (specificNode) {
node = specificNode;
} else {
console.warn(
this.logger.warn(
`Could not find node with path ${path.join(
'.',
)}; defaulting to put errors on root node.`,
Expand Down

0 comments on commit a9f1d67

Please sign in to comment.