Skip to content

Commit

Permalink
feat(opentelemetry-instrumentation-http): support adding custom attri…
Browse files Browse the repository at this point in the history
…butes before a span is started
  • Loading branch information
echoontheway committed Jul 8, 2021
1 parent 3a327dd commit 62c7099
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 7 deletions.
29 changes: 29 additions & 0 deletions packages/opentelemetry-instrumentation-http/src/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
SpanStatus,
SpanStatusCode,
trace,
SpanAttributes,
} from '@opentelemetry/api';
import { suppressTracing } from '@opentelemetry/core';
import type * as http from 'http';
Expand Down Expand Up @@ -396,11 +397,17 @@ export class HttpInstrumentation extends InstrumentationBase<Http> {

const headers = request.headers;

let hookAttributes: SpanAttributes | undefined;
if (instrumentation._getConfig().startIncomingSpanHook) {
hookAttributes = instrumentation._callStartIncomingSpanHook(request);
}

const spanOptions: SpanOptions = {
kind: SpanKind.SERVER,
attributes: utils.getIncomingRequestAttributes(request, {
component: component,
serverName: instrumentation._getConfig().serverName,
hookAttributes: hookAttributes,
}),
};

Expand Down Expand Up @@ -534,9 +541,15 @@ export class HttpInstrumentation extends InstrumentationBase<Http> {
return original.apply(this, [optionsParsed, ...args]);
}

let hookAttributes: SpanAttributes | undefined;
if (instrumentation._getConfig().startOutgoingSpanHook) {
hookAttributes = instrumentation._callStartOutgoingSpanHook(optionsParsed);
}

const operationName = `${component.toUpperCase()} ${method}`;
const spanOptions: SpanOptions = {
kind: SpanKind.CLIENT,
attributes: hookAttributes,
};
const span = instrumentation._startHttpSpan(operationName, spanOptions);

Expand Down Expand Up @@ -639,4 +652,20 @@ export class HttpInstrumentation extends InstrumentationBase<Http> {
true
);
}

private _callStartIncomingSpanHook(request: http.IncomingMessage): SpanAttributes {
return safeExecuteInTheMiddle(
() => this._getConfig().startIncomingSpanHook!(request),
() => { },
true
);
}

private _callStartOutgoingSpanHook(request: http.RequestOptions): SpanAttributes {
return safeExecuteInTheMiddle(
() => this._getConfig().startOutgoingSpanHook!(request),
() => {},
true
);
}
}
18 changes: 17 additions & 1 deletion packages/opentelemetry-instrumentation-http/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Span } from '@opentelemetry/api';
import {
Span,
SpanAttributes,
} from '@opentelemetry/api';
import type * as http from 'http';
import type * as https from 'https';
import {
Expand All @@ -22,6 +25,7 @@ import {
IncomingMessage,
request,
ServerResponse,
RequestOptions,
} from 'http';
import * as url from 'url';
import { InstrumentationConfig } from '@opentelemetry/instrumentation';
Expand Down Expand Up @@ -67,6 +71,14 @@ export interface HttpResponseCustomAttributeFunction {
(span: Span, response: IncomingMessage | ServerResponse): void;
}

export interface StartIncomingSpanCustomAttributeFunction {
(request: IncomingMessage ): SpanAttributes;
}

export interface StartOutgoingSpanCustomAttributeFunction {
(request: RequestOptions ): SpanAttributes;
}

/**
* Options available for the HTTP instrumentation (see [documentation](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-instrumentation-http#http-instrumentation-options))
*/
Expand All @@ -81,6 +93,10 @@ export interface HttpInstrumentationConfig extends InstrumentationConfig {
requestHook?: HttpRequestCustomAttributeFunction;
/** Function for adding custom attributes before response is handled */
responseHook?: HttpResponseCustomAttributeFunction;
/** Function for adding custom attributes before a span is started in incomingRequest */
startIncomingSpanHook?: StartIncomingSpanCustomAttributeFunction;
/** Function for adding custom attributes before a span is started in outgoingRequest */
startOutgoingSpanHook?: StartOutgoingSpanCustomAttributeFunction;
/** The primary server name of the matched virtual host. */
serverName?: string;
/** Require parent to create span for outgoing requests */
Expand Down
6 changes: 3 additions & 3 deletions packages/opentelemetry-instrumentation-http/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,11 +410,11 @@ export const getOutgoingRequestAttributesOnResponse = (
/**
* Returns incoming request attributes scoped to the request data
* @param {IncomingMessage} request the request object
* @param {{ component: string, serverName?: string }} options used to pass data needed to create attributes
* @param {{ component: string, serverName?: string, hookAttributes?: SpanAttributes }} options used to pass data needed to create attributes
*/
export const getIncomingRequestAttributes = (
request: IncomingMessage,
options: { component: string; serverName?: string }
options: { component: string; serverName?: string; hookAttributes?: SpanAttributes }
): SpanAttributes => {
const headers = request.headers;
const userAgent = headers['user-agent'];
Expand Down Expand Up @@ -458,7 +458,7 @@ export const getIncomingRequestAttributes = (
setRequestContentLengthAttribute(request, attributes);

const httpKindAttributes = getAttributesFromHttpKind(httpVersion);
return Object.assign(attributes, httpKindAttributes);
return Object.assign(attributes, httpKindAttributes, options.hookAttributes);
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ import {
context,
propagation,
Span as ISpan,
SpanKind, trace,
SpanKind,
trace,
SpanAttributes,
} from '@opentelemetry/api';
import { NodeTracerProvider } from '@opentelemetry/node';
import {
Expand All @@ -39,7 +41,7 @@ import { DummyPropagation } from '../utils/DummyPropagation';
import { httpRequest } from '../utils/httpRequest';
import { ContextManager } from '@opentelemetry/api';
import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks';
import type { ClientRequest, IncomingMessage, ServerResponse } from 'http';
import type { ClientRequest, IncomingMessage, ServerResponse, RequestOptions } from 'http';
import { isWrapped } from '@opentelemetry/instrumentation';
import { getRPCMetadata, RPCType } from '@opentelemetry/core';

Expand Down Expand Up @@ -95,6 +97,18 @@ export const responseHookFunction = (
span.setAttribute('custom response hook attribute', 'response');
};

export const startIncomingSpanHookFunction = (
request: IncomingMessage
): SpanAttributes => {
return {guid: request.headers?.guid}
};

export const startOutgoingSpanHookFunction = (
request: RequestOptions
): SpanAttributes => {
return {guid: request.headers?.guid}
};

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

Expand Down Expand Up @@ -201,6 +215,8 @@ describe('HttpInstrumentation', () => {
applyCustomAttributesOnSpan: customAttributeFunction,
requestHook: requestHookFunction,
responseHook: responseHookFunction,
startIncomingSpanHook: startIncomingSpanHookFunction,
startOutgoingSpanHook: startOutgoingSpanHookFunction,
serverName,
});
instrumentation.enable();
Expand Down Expand Up @@ -672,7 +688,8 @@ describe('HttpInstrumentation', () => {

it('custom attributes should show up on client and server spans', async () => {
await httpRequest.get(
`${protocol}://${hostname}:${serverPort}${pathname}`
`${protocol}://${hostname}:${serverPort}${pathname}`,
{headers: {guid: 'user_guid'}}
);
const spans = memoryExporter.getFinishedSpans();
const [incomingSpan, outgoingSpan] = spans;
Expand All @@ -685,6 +702,14 @@ describe('HttpInstrumentation', () => {
incomingSpan.attributes['custom response hook attribute'],
'response'
);
assert.strictEqual(
incomingSpan.attributes['custom response hook attribute'],
'response'
);
assert.strictEqual(
incomingSpan.attributes['guid'],
'user_guid'
);
assert.strictEqual(
incomingSpan.attributes['span kind'],
SpanKind.CLIENT
Expand All @@ -698,6 +723,10 @@ describe('HttpInstrumentation', () => {
outgoingSpan.attributes['custom response hook attribute'],
'response'
);
assert.strictEqual(
outgoingSpan.attributes['guid'],
'user_guid'
);
assert.strictEqual(
outgoingSpan.attributes['span kind'],
SpanKind.CLIENT
Expand Down

0 comments on commit 62c7099

Please sign in to comment.