Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(instrumenation-document-load): Add custom attributes to document load #1414

Merged
4 changes: 2 additions & 2 deletions metapackages/auto-instrumentations-web/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

import { diag } from '@opentelemetry/api';
import { Instrumentation } from '@opentelemetry/instrumentation';
import { Instrumentation, InstrumentationConfig } from '@opentelemetry/instrumentation';
import { DocumentLoadInstrumentation } from '@opentelemetry/instrumentation-document-load';
import { FetchInstrumentation } from '@opentelemetry/instrumentation-fetch';
import { UserInteractionInstrumentation } from '@opentelemetry/instrumentation-user-interaction';
Expand Down Expand Up @@ -55,7 +55,7 @@ export function getWebAutoInstrumentations(
>) {
const Instance = InstrumentationMap[name];
// Defaults are defined by the instrumentation itself
const userConfig = inputConfigs[name] ?? {};
const userConfig : InstrumentationConfig = inputConfigs[name] ?? {};

if (userConfig.enabled === false) {
diag.debug(`Disabling instrumentation for ${name}`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,30 @@ Because the browser does not send a trace context header for the initial page na
</body>
```


## Optional : Add custom attributes to document load span if needed

If it is needed to add custom attributes to the document load span,and/or document fetch span and/or resource fetch spans, respective functions to do so needs to be provided
as a config to the DocumentLoad Instrumentation as shown below. The attributes will be added to the respective spans
before the individual are spans are ended. If the function throws an error , no attributes will be added to the span and
the rest of the process continues.

```js
const addCustomAttributesToSpan = (span: Span) => {
span.setAttribute('<custom.attribute.key>','<custom-attribute-value>');
}
registerInstrumentations({
instrumentations: [
new DocumentLoadInstrumentation({
applyAttributes: {
documentLoad: addCustomAttributesToSpan
}
})
]
})


```
See [examples/tracer-web](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/tracer-web) for a short example.

## Useful links
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@

export * from './instrumentation';
export * from './enums/AttributeNames';
export * from './types';
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,12 @@ import {
} from '@opentelemetry/sdk-trace-web';
import {
InstrumentationBase,
InstrumentationConfig,
safeExecuteInTheMiddle,
} from '@opentelemetry/instrumentation';
import {
DocumentLoadCustomAttributeFunction,
DocumentLoadInstrumentationConfig,
} from './types';
import { AttributeNames } from './enums/AttributeNames';
import { VERSION } from './version';
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
Expand All @@ -53,7 +57,7 @@ export class DocumentLoadInstrumentation extends InstrumentationBase<unknown> {
*
* @param config
*/
constructor(config: InstrumentationConfig = {}) {
constructor(config: DocumentLoadInstrumentationConfig = {}) {
super('@opentelemetry/instrumentation-document-load', VERSION, config);
}

Expand Down Expand Up @@ -113,6 +117,10 @@ export class DocumentLoadInstrumentation extends InstrumentationBase<unknown> {
fetchSpan.setAttribute(SemanticAttributes.HTTP_URL, location.href);
context.with(trace.setSpan(context.active(), fetchSpan), () => {
addSpanNetworkEvents(fetchSpan, entries);
this._addCustomAttributesOnSpan(
fetchSpan,
this._getConfig().applyAttributes?.documentFetch
);
this._endSpan(fetchSpan, PTN.RESPONSE_END, entries);
});
}
Expand Down Expand Up @@ -141,7 +149,10 @@ export class DocumentLoadInstrumentation extends InstrumentationBase<unknown> {
addSpanNetworkEvent(rootSpan, PTN.LOAD_EVENT_END, entries);

addSpanPerformancePaintEvents(rootSpan);

this._addCustomAttributesOnSpan(
rootSpan,
this._getConfig().applyAttributes?.documentLoad
);
this._endSpan(rootSpan, PTN.LOAD_EVENT_END, entries);
});
}
Expand Down Expand Up @@ -186,6 +197,10 @@ export class DocumentLoadInstrumentation extends InstrumentationBase<unknown> {
if (span) {
span.setAttribute(SemanticAttributes.HTTP_URL, resource.name);
addSpanNetworkEvents(span, resource);
this._addCustomAttributesOnSpan(
span,
this._getConfig().applyAttributes?.resourceFetch
);
this._endSpan(span, PTN.RESPONSE_END, resource);
}
}
Expand Down Expand Up @@ -231,6 +246,31 @@ export class DocumentLoadInstrumentation extends InstrumentationBase<unknown> {
}
}

private _getConfig(): DocumentLoadInstrumentationConfig {
return this._config;
}
/**
* adds custom attributes to root span if configured
*/
private _addCustomAttributesOnSpan(
span: Span,
applyCustomAttributesOnSpan: DocumentLoadCustomAttributeFunction | undefined
) {
if (applyCustomAttributesOnSpan) {
safeExecuteInTheMiddle(
() => applyCustomAttributesOnSpan(span),
error => {
if (!error) {
return;
}

this._diag.error('addCustomAttributesOnSpan', error);
},
true
);
}
}

/**
* implements enable function
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Span } from '@opentelemetry/api';
import { InstrumentationConfig } from '@opentelemetry/instrumentation';

export interface DocumentLoadCustomAttributeFunction {
(span: Span): void;
}

/**
* DocumentLoadInstrumentationPlugin Config
*/
export interface DocumentLoadInstrumentationConfig
extends InstrumentationConfig {
/** Function for adding custom attributes on the document load, document fetch and or resource fetch spans */
applyAttributes?: {
documentLoad?: DocumentLoadCustomAttributeFunction;
documentFetch?: DocumentLoadCustomAttributeFunction;
resourceFetch?: DocumentLoadCustomAttributeFunction;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,96 @@ describe('DocumentLoad Instrumentation', () => {
});
shouldExportCorrectSpan();
});

describe('add custom attributes to spans', () => {
let spyEntries: any;
beforeEach(() => {
spyEntries = sandbox.stub(window.performance, 'getEntriesByType');
spyEntries.withArgs('navigation').returns([entries]);
spyEntries.withArgs('resource').returns(resources);
spyEntries.withArgs('paint').returns([]);
});
afterEach(() => {
spyEntries.restore();
});

it('should add attribute to document load span', done => {
plugin = new DocumentLoadInstrumentation({
enabled: false,
applyAttributes: {
documentLoad: span => {
span.setAttribute('custom-key', 'custom-val');
},
},
});
plugin.enable();
setTimeout(() => {
const rootSpan = exporter.getFinishedSpans()[3] as ReadableSpan;
assert.strictEqual(rootSpan.attributes['custom-key'], 'custom-val');
assert.strictEqual(exporter.getFinishedSpans().length, 4);
done();
});
});

it('should add attribute to document fetch span', done => {
plugin = new DocumentLoadInstrumentation({
enabled: false,
applyAttributes: {
documentFetch: span => {
span.setAttribute('custom-key', 'custom-val');
},
},
});
plugin.enable();
setTimeout(() => {
const fetchSpan = exporter.getFinishedSpans()[0] as ReadableSpan;
assert.strictEqual(fetchSpan.attributes['custom-key'], 'custom-val');
assert.strictEqual(exporter.getFinishedSpans().length, 4);
done();
});
});

it('should add attribute to resource fetch spans', done => {
plugin = new DocumentLoadInstrumentation({
enabled: false,
applyAttributes: {
resourceFetch: span => {
span.setAttribute('custom-key', 'custom-val');
},
},
});
plugin.enable();
setTimeout(() => {
const resourceSpan1 = exporter.getFinishedSpans()[1] as ReadableSpan;
const resourceSpan2 = exporter.getFinishedSpans()[2] as ReadableSpan;
assert.strictEqual(
resourceSpan1.attributes['custom-key'],
'custom-val'
);
assert.strictEqual(
resourceSpan2.attributes['custom-key'],
'custom-val'
);
assert.strictEqual(exporter.getFinishedSpans().length, 4);
done();
});
});
it('should still create the spans if the function throws error', done => {
plugin = new DocumentLoadInstrumentation({
enabled: false,
applyAttributes: {
documentLoad: span => {
throw new Error('test error');
},
},
});
plugin.enable();
setTimeout(() => {
assert.strictEqual(exporter.getFinishedSpans().length, 4);
done();
});
});
});
});

/**
Expand Down