@paychex/instrumentation-document-load
Version:
OpenTelemetry document-load automatic instrumentation package.
204 lines • 9.71 kB
JavaScript
;
/*
* 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.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.DocumentLoadInstrumentation = void 0;
const api_1 = require("@opentelemetry/api");
const core_1 = require("@opentelemetry/core");
const sdk_trace_web_1 = require("@opentelemetry/sdk-trace-web");
const instrumentation_1 = require("@opentelemetry/instrumentation");
const AttributeNames_1 = require("./enums/AttributeNames");
const version_1 = require("./version");
const semantic_conventions_1 = require("@opentelemetry/semantic-conventions");
const utils_1 = require("./utils");
/**
* This class represents a document load plugin
*/
class DocumentLoadInstrumentation extends instrumentation_1.InstrumentationBase {
/**
*
* @param config
*/
constructor(config = {}) {
super('@opentelemetry/instrumentation-document-load', version_1.VERSION, config);
this.component = 'document-load';
this.version = '1';
this.moduleName = this.component;
}
init() { }
/**
* callback to be executed when page is loaded
*/
_onDocumentLoaded() {
// Timeout is needed as load event doesn't have yet the performance metrics for loadEnd.
// Support for event "loadend" is very limited and cannot be used
window.setTimeout(() => {
this._collectPerformance();
});
}
/**
* Adds spans for all resources
* @param rootSpan
*/
_addResourcesSpans(rootSpan) {
var _a, _b;
const resources = (_b = (_a = core_1.otperformance).getEntriesByType) === null || _b === void 0 ? void 0 : _b.call(_a, 'resource');
if (resources) {
resources.forEach(resource => {
this._initResourceSpan(resource, rootSpan);
});
}
}
/**
* Collects information about performance and creates appropriate spans
*/
_collectPerformance() {
const metaElement = Array.from(document.getElementsByTagName('meta')).find(e => e.getAttribute('name') === core_1.TRACE_PARENT_HEADER);
const baggageElement = Array.from(document.getElementsByTagName('meta')).find(e => e.getAttribute('name') === 'baggage');
const entries = (0, utils_1.getPerformanceNavigationEntries)();
const traceparent = (metaElement && metaElement.content) || '';
const baggage = (baggageElement && baggageElement.content) || '';
api_1.context.with(api_1.propagation.extract(api_1.context.active(), { traceparent, baggage }), () => {
var _a;
const rootSpan = this._startSpan(AttributeNames_1.AttributeNames.DOCUMENT_LOAD, sdk_trace_web_1.PerformanceTimingNames.FETCH_START, entries);
if (!rootSpan) {
return;
}
api_1.context.with(api_1.trace.setSpan(api_1.context.active(), rootSpan), () => {
const fetchSpan = this._startSpan(AttributeNames_1.AttributeNames.DOCUMENT_FETCH, sdk_trace_web_1.PerformanceTimingNames.FETCH_START, entries);
if (fetchSpan) {
fetchSpan.setAttribute(semantic_conventions_1.SemanticAttributes.HTTP_URL, location.href);
api_1.context.with(api_1.trace.setSpan(api_1.context.active(), fetchSpan), () => {
var _a;
(0, sdk_trace_web_1.addSpanNetworkEvents)(fetchSpan, entries);
this._addCustomAttributesOnSpan(fetchSpan, (_a = this._getConfig().applyCustomAttributesOnSpan) === null || _a === void 0 ? void 0 : _a.documentFetch);
this._endSpan(fetchSpan, sdk_trace_web_1.PerformanceTimingNames.RESPONSE_END, entries);
});
}
});
rootSpan.setAttribute(semantic_conventions_1.SemanticAttributes.HTTP_URL, location.href);
rootSpan.setAttribute(semantic_conventions_1.SemanticAttributes.HTTP_USER_AGENT, navigator.userAgent);
this._addResourcesSpans(rootSpan);
(0, sdk_trace_web_1.addSpanNetworkEvent)(rootSpan, sdk_trace_web_1.PerformanceTimingNames.FETCH_START, entries);
(0, sdk_trace_web_1.addSpanNetworkEvent)(rootSpan, sdk_trace_web_1.PerformanceTimingNames.UNLOAD_EVENT_START, entries);
(0, sdk_trace_web_1.addSpanNetworkEvent)(rootSpan, sdk_trace_web_1.PerformanceTimingNames.UNLOAD_EVENT_END, entries);
(0, sdk_trace_web_1.addSpanNetworkEvent)(rootSpan, sdk_trace_web_1.PerformanceTimingNames.DOM_INTERACTIVE, entries);
(0, sdk_trace_web_1.addSpanNetworkEvent)(rootSpan, sdk_trace_web_1.PerformanceTimingNames.DOM_CONTENT_LOADED_EVENT_START, entries);
(0, sdk_trace_web_1.addSpanNetworkEvent)(rootSpan, sdk_trace_web_1.PerformanceTimingNames.DOM_CONTENT_LOADED_EVENT_END, entries);
(0, sdk_trace_web_1.addSpanNetworkEvent)(rootSpan, sdk_trace_web_1.PerformanceTimingNames.DOM_COMPLETE, entries);
(0, sdk_trace_web_1.addSpanNetworkEvent)(rootSpan, sdk_trace_web_1.PerformanceTimingNames.LOAD_EVENT_START, entries);
(0, sdk_trace_web_1.addSpanNetworkEvent)(rootSpan, sdk_trace_web_1.PerformanceTimingNames.LOAD_EVENT_END, entries);
(0, utils_1.addSpanPerformancePaintEvents)(rootSpan);
this._addCustomAttributesOnSpan(rootSpan, (_a = this._getConfig().applyCustomAttributesOnSpan) === null || _a === void 0 ? void 0 : _a.documentLoad);
this._endSpan(rootSpan, sdk_trace_web_1.PerformanceTimingNames.LOAD_EVENT_END, entries);
});
}
/**
* Helper function for ending span
* @param span
* @param performanceName name of performance entry for time end
* @param entries
*/
_endSpan(span, performanceName, entries) {
// span can be undefined when entries are missing the certain performance - the span will not be created
if (span) {
if ((0, sdk_trace_web_1.hasKey)(entries, performanceName)) {
span.end(entries[performanceName]);
}
else {
// just end span
span.end();
}
}
}
/**
* Creates and ends a span with network information about resource added as timed events
* @param resource
* @param parentSpan
*/
_initResourceSpan(resource, parentSpan) {
var _a;
const span = this._startSpan(AttributeNames_1.AttributeNames.RESOURCE_FETCH, sdk_trace_web_1.PerformanceTimingNames.FETCH_START, resource, parentSpan);
if (span) {
span.setAttribute(semantic_conventions_1.SemanticAttributes.HTTP_URL, resource.name);
(0, sdk_trace_web_1.addSpanNetworkEvents)(span, resource);
this._addCustomAttributesOnSpan(span, (_a = this._getConfig().applyCustomAttributesOnSpan) === null || _a === void 0 ? void 0 : _a.resourceFetch);
this._endSpan(span, sdk_trace_web_1.PerformanceTimingNames.RESPONSE_END, resource);
}
}
/**
* Helper function for starting a span
* @param spanName name of span
* @param performanceName name of performance entry for time start
* @param entries
* @param parentSpan
*/
_startSpan(spanName, performanceName, entries, parentSpan) {
if ((0, sdk_trace_web_1.hasKey)(entries, performanceName) &&
typeof entries[performanceName] === 'number') {
const span = this.tracer.startSpan(spanName, {
startTime: entries[performanceName],
}, parentSpan ? api_1.trace.setSpan(api_1.context.active(), parentSpan) : undefined);
return span;
}
return undefined;
}
/**
* executes callback {_onDocumentLoaded} when the page is loaded
*/
_waitForPageLoad() {
if (window.document.readyState === 'complete') {
this._onDocumentLoaded();
}
else {
this._onDocumentLoaded = this._onDocumentLoaded.bind(this);
window.addEventListener('load', this._onDocumentLoaded);
}
}
_getConfig() {
return this._config;
}
/**
* adds custom attributes to root span if configured
*/
_addCustomAttributesOnSpan(span, applyCustomAttributesOnSpan) {
if (applyCustomAttributesOnSpan) {
(0, instrumentation_1.safeExecuteInTheMiddle)(() => applyCustomAttributesOnSpan(span), error => {
if (!error) {
return;
}
this._diag.error('addCustomAttributesOnSpan', error);
}, true);
}
}
/**
* implements enable function
*/
enable() {
// remove previously attached load to avoid adding the same event twice
// in case of multiple enable calling.
window.removeEventListener('load', this._onDocumentLoaded);
this._waitForPageLoad();
}
/**
* implements disable function
*/
disable() {
window.removeEventListener('load', this._onDocumentLoaded);
}
}
exports.DocumentLoadInstrumentation = DocumentLoadInstrumentation;
//# sourceMappingURL=instrumentation.js.map