feat(plugin-http): add plugin hooks before processing req and res (#963)
This commit is contained in:
		
							parent
							
								
									18278703e3
								
							
						
					
					
						commit
						6b2a9b731f
					
				| 
						 | 
				
			
			@ -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. |
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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',
 | 
			
		||||
| 
						 | 
				
			
			@ -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()');
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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))
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -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 */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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';
 | 
			
		||||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			@ -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', () => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue