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
echoqlwang committed Jul 7, 2021
1 parent 3a327dd commit e78c15d
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 9 deletions.
2 changes: 1 addition & 1 deletion packages/opentelemetry-instrumentation-http/package.json
@@ -1,6 +1,6 @@
{
"name": "@opentelemetry/instrumentation-http",
"version": "0.23.0",
"version": "0.23.1",
"description": "OpenTelemetry http/https automatic instrumentation package.",
"main": "build/src/index.js",
"types": "build/src/index.d.ts",
Expand Down
21 changes: 18 additions & 3 deletions packages/opentelemetry-instrumentation-http/src/http.ts
Expand Up @@ -408,7 +408,8 @@ export class HttpInstrumentation extends InstrumentationBase<Http> {
const span = instrumentation._startHttpSpan(
`${component.toLocaleUpperCase()} ${method}`,
spanOptions,
ctx
ctx,
request
);
const rpcMetadata: RPCMetadata = {
type: RPCType.HTTP,
Expand Down Expand Up @@ -538,7 +539,7 @@ export class HttpInstrumentation extends InstrumentationBase<Http> {
const spanOptions: SpanOptions = {
kind: SpanKind.CLIENT,
};
const span = instrumentation._startHttpSpan(operationName, spanOptions);
const span = instrumentation._startHttpSpan(operationName, spanOptions, undefined, optionsParsed);

const parentContext = context.active();
const requestContext = trace.setSpan(parentContext, span);
Expand Down Expand Up @@ -584,7 +585,8 @@ export class HttpInstrumentation extends InstrumentationBase<Http> {
private _startHttpSpan(
name: string,
options: SpanOptions,
ctx = context.active()
ctx = context.active(),
request: http.IncomingMessage | http.RequestOptions = {}
) {
/*
* If a parent is required but not present, we use a `NoopSpan` to still
Expand All @@ -603,6 +605,9 @@ export class HttpInstrumentation extends InstrumentationBase<Http> {
} else if (requireParent === true && currentSpan?.spanContext().isRemote) {
span = currentSpan;
} else {
if (this._getConfig().startSpanHook) {
this._callStartSpanHook(options, request);
}
span = this.tracer.startSpan(name, options, ctx);
}
this._spanNotEnded.add(span);
Expand Down Expand Up @@ -639,4 +644,14 @@ export class HttpInstrumentation extends InstrumentationBase<Http> {
true
);
}
private _callStartSpanHook(
options: SpanOptions,
request: http.RequestOptions | http.IncomingMessage
) {
safeExecuteInTheMiddle(
() => this._getConfig().startSpanHook!(options, request),
() => {},
true
);
}
}
12 changes: 11 additions & 1 deletion packages/opentelemetry-instrumentation-http/src/types.ts
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,
SpanOptions,
} 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,10 @@ export interface HttpResponseCustomAttributeFunction {
(span: Span, response: IncomingMessage | ServerResponse): void;
}

export interface StartSpanCustomAttributeFunction {
(options: SpanOptions, request: IncomingMessage | RequestOptions): void;
}

/**
* 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 +89,8 @@ 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 */
startSpanHook?: StartSpanCustomAttributeFunction;
/** The primary server name of the matched virtual host. */
serverName?: string;
/** Require parent to create span for outgoing requests */
Expand Down
2 changes: 1 addition & 1 deletion packages/opentelemetry-instrumentation-http/src/version.ts
Expand Up @@ -15,4 +15,4 @@
*/

// this is autogenerated file, see scripts/version-update.js
export const VERSION = '0.23.0';
export const VERSION = '0.23.1';
Expand Up @@ -18,7 +18,9 @@ import {
context,
propagation,
Span as ISpan,
SpanKind, trace,
SpanKind,
trace,
SpanOptions,
} 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,13 @@ export const responseHookFunction = (
span.setAttribute('custom response hook attribute', 'response');
};

export const startSpanHookFunction = (
options: SpanOptions,
request: IncomingMessage | RequestOptions
): void => {
options.attributes = Object.assign({},options.attributes,{guid: request.headers?.guid});
};

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

Expand Down Expand Up @@ -201,6 +210,7 @@ describe('HttpInstrumentation', () => {
applyCustomAttributesOnSpan: customAttributeFunction,
requestHook: requestHookFunction,
responseHook: responseHookFunction,
startSpanHook: startSpanHookFunction,
serverName,
});
instrumentation.enable();
Expand Down Expand Up @@ -672,7 +682,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 +696,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 +717,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 e78c15d

Please sign in to comment.