Skip to content

Commit

Permalink
feat(instrumentation-documentLoad): custom attributes to document loa…
Browse files Browse the repository at this point in the history
…d spans
  • Loading branch information
Abinet18 committed Feb 28, 2023
1 parent 40461ce commit 31fa293
Show file tree
Hide file tree
Showing 6 changed files with 194 additions and 5 deletions.
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
24 changes: 24 additions & 0 deletions plugins/web/opentelemetry-instrumentation-document-load/README.md
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

0 comments on commit 31fa293

Please sign in to comment.