Skip to content

Commit

Permalink
feat(plugin-http): add plugin hooks before processing req and res (#963)
Browse files Browse the repository at this point in the history
  • Loading branch information
blumamir authored May 13, 2020
1 parent 1827870 commit 6b2a9b7
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 1 deletion.
2 changes: 2 additions & 0 deletions packages/opentelemetry-plugin-http/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ Http plugin has few options available to choose from. You can set the following:
| Options | Type | Description |
| ------- | ---- | ----------- |
| [`applyCustomAttributesOnSpan`](https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-plugin-http/src/types.ts#L52) | `HttpCustomAttributeFunction` | Function for adding custom attributes |
| [`requestHook`](https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-plugin-http/src/types.ts#L60) | `HttpRequestCustomAttributeFunction` | Function for adding custom attributes before request is handled |
| [`responseHook`](https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-plugin-http/src/types.ts#L67) | `HttpResponseCustomAttributeFunction` | Function for adding custom attributes before response is handled |
| [`ignoreIncomingPaths`](https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-plugin-http/src/types.ts#L28) | `IgnoreMatcher[]` | Http plugin will not trace all incoming requests that match paths |
| [`ignoreOutgoingUrls`](https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-plugin-http/src/types.ts#L28) | `IgnoreMatcher[]` | Http plugin will not trace all outgoing requests that match urls |
| [`serverName`](https://github.com/open-telemetry/opentelemetry-js/blob/master/packages/opentelemetry-plugin-http/src/types.ts#L28) | `string` | The primary server name of the matched virtual host. |
Expand Down
35 changes: 35 additions & 0 deletions packages/opentelemetry-plugin-http/src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,9 @@ export class HttpPlugin extends BasePlugin<Http> {
hostname,
});
span.setAttributes(attributes);
if (this._config.requestHook) {
this._callRequestHook(span, request);
}

request.on(
'response',
Expand All @@ -212,6 +215,9 @@ export class HttpPlugin extends BasePlugin<Http> {
{ hostname }
);
span.setAttributes(attributes);
if (this._config.responseHook) {
this._callResponseHook(span, response);
}

this._tracer.bind(response);
this._logger.debug('outgoingRequest on response()');
Expand Down Expand Up @@ -316,6 +322,13 @@ export class HttpPlugin extends BasePlugin<Http> {
context.bind(request);
context.bind(response);

if (plugin._config.requestHook) {
plugin._callRequestHook(span, request);
}
if (plugin._config.responseHook) {
plugin._callResponseHook(span, response);
}

// Wraps end (inspired by:
// https://github.com/GoogleCloudPlatform/cloud-trace-nodejs/blob/master/src/plugins/plugin-connect.ts#L75)
const originalEnd = response.end;
Expand Down Expand Up @@ -465,6 +478,28 @@ export class HttpPlugin extends BasePlugin<Http> {
this._spanNotEnded.delete(span);
}

private _callResponseHook(
span: Span,
response: IncomingMessage | ServerResponse
) {
this._safeExecute(
span,
() => this._config.responseHook!(span, response),
false
);
}

private _callRequestHook(
span: Span,
request: ClientRequest | IncomingMessage
) {
this._safeExecute(
span,
() => this._config.requestHook!(span, request),
false
);
}

private _safeExecute<
T extends (...args: unknown[]) => ReturnType<T>,
K extends boolean
Expand Down
14 changes: 13 additions & 1 deletion packages/opentelemetry-plugin-http/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ export interface HttpCustomAttributeFunction {
): void;
}

export interface HttpRequestCustomAttributeFunction {
(span: Span, request: ClientRequest | IncomingMessage): void;
}

export interface HttpResponseCustomAttributeFunction {
(span: Span, response: IncomingMessage | ServerResponse): void;
}

/**
* Options available for the HTTP Plugin (see [documentation](https://github.com/open-telemetry/opentelemetry-js/tree/master/packages/opentelemetry-plugin-http#http-plugin-options))
*/
Expand All @@ -65,8 +73,12 @@ export interface HttpPluginConfig extends PluginConfig {
ignoreIncomingPaths?: IgnoreMatcher[];
/** Not trace all outgoing requests that match urls */
ignoreOutgoingUrls?: IgnoreMatcher[];
/** Function for adding custom attributes */
/** Function for adding custom attributes after response is handled */
applyCustomAttributesOnSpan?: HttpCustomAttributeFunction;
/** Function for adding custom attributes before request is handled */
requestHook?: HttpRequestCustomAttributeFunction;
/** Function for adding custom attributes before response is handled */
responseHook?: HttpResponseCustomAttributeFunction;
/** The primary server name of the matched virtual host. */
serverName?: string;
/** Require parent to create span for outgoing requests */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import { DummyPropagation } from '../utils/DummyPropagation';
import { httpRequest } from '../utils/httpRequest';
import { ContextManager } from '@opentelemetry/context-base';
import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks';
import { ClientRequest, IncomingMessage, ServerResponse } from 'http';

const applyCustomAttributesOnSpanErrorMessage =
'bad applyCustomAttributesOnSpan function';
Expand Down Expand Up @@ -76,6 +77,20 @@ export const customAttributeFunction = (span: ISpan): void => {
span.setAttribute('span kind', SpanKind.CLIENT);
};

export const requestHookFunction = (
span: ISpan,
request: ClientRequest | IncomingMessage
): void => {
span.setAttribute('custom request hook attribute', 'request');
};

export const responseHookFunction = (
span: ISpan,
response: IncomingMessage | ServerResponse
): void => {
span.setAttribute('custom response hook attribute', 'response');
};

describe('HttpPlugin', () => {
let contextManager: ContextManager;

Expand Down Expand Up @@ -207,6 +222,8 @@ describe('HttpPlugin', () => {
(url: string) => url.endsWith(`/ignored/function`),
],
applyCustomAttributesOnSpan: customAttributeFunction,
requestHook: requestHookFunction,
responseHook: responseHookFunction,
serverName,
};
plugin.enable(http, provider, provider.logger, config);
Expand Down Expand Up @@ -703,6 +720,40 @@ describe('HttpPlugin', () => {
});
req.end();
});

it('custom attributes should show up on client and server spans', async () => {
await httpRequest.get(
`${protocol}://${hostname}:${serverPort}${pathname}`
);
const spans = memoryExporter.getFinishedSpans();
const [incomingSpan, outgoingSpan] = spans;

assert.strictEqual(
incomingSpan.attributes['custom request hook attribute'],
'request'
);
assert.strictEqual(
incomingSpan.attributes['custom response hook attribute'],
'response'
);
assert.strictEqual(
incomingSpan.attributes['span kind'],
SpanKind.CLIENT
);

assert.strictEqual(
outgoingSpan.attributes['custom request hook attribute'],
'request'
);
assert.strictEqual(
outgoingSpan.attributes['custom response hook attribute'],
'response'
);
assert.strictEqual(
outgoingSpan.attributes['span kind'],
SpanKind.CLIENT
);
});
});

describe('with require parent span', () => {
Expand Down

0 comments on commit 6b2a9b7

Please sign in to comment.