@paychex/instrumentation-document-load
Version:
OpenTelemetry document-load automatic instrumentation package.
226 lines • 10.5 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.
*/
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
if (typeof b !== "function" && b !== null)
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
import { context, propagation, trace,
//ROOT_CONTEXT,
} from '@opentelemetry/api';
import { otperformance, TRACE_PARENT_HEADER } from '@opentelemetry/core';
import { addSpanNetworkEvent, addSpanNetworkEvents, hasKey, PerformanceTimingNames as PTN, } from '@opentelemetry/sdk-trace-web';
import { InstrumentationBase, safeExecuteInTheMiddle, } from '@opentelemetry/instrumentation';
import { AttributeNames } from './enums/AttributeNames';
import { VERSION } from './version';
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
import { addSpanPerformancePaintEvents, getPerformanceNavigationEntries, } from './utils';
/**
* This class represents a document load plugin
*/
var DocumentLoadInstrumentation = /** @class */ (function (_super) {
__extends(DocumentLoadInstrumentation, _super);
/**
*
* @param config
*/
function DocumentLoadInstrumentation(config) {
if (config === void 0) { config = {}; }
var _this = _super.call(this, '@opentelemetry/instrumentation-document-load', VERSION, config) || this;
_this.component = 'document-load';
_this.version = '1';
_this.moduleName = _this.component;
return _this;
}
DocumentLoadInstrumentation.prototype.init = function () { };
/**
* callback to be executed when page is loaded
*/
DocumentLoadInstrumentation.prototype._onDocumentLoaded = function () {
var _this = this;
// 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(function () {
_this._collectPerformance();
});
};
/**
* Adds spans for all resources
* @param rootSpan
*/
DocumentLoadInstrumentation.prototype._addResourcesSpans = function (rootSpan) {
var _this = this;
var _a, _b;
var resources = (_b = (_a = otperformance).getEntriesByType) === null || _b === void 0 ? void 0 : _b.call(_a, 'resource');
if (resources) {
resources.forEach(function (resource) {
_this._initResourceSpan(resource, rootSpan);
});
}
};
/**
* Collects information about performance and creates appropriate spans
*/
DocumentLoadInstrumentation.prototype._collectPerformance = function () {
var _this = this;
var metaElement = Array.from(document.getElementsByTagName('meta')).find(function (e) { return e.getAttribute('name') === TRACE_PARENT_HEADER; });
var baggageElement = Array.from(document.getElementsByTagName('meta')).find(function (e) { return e.getAttribute('name') === 'baggage'; });
var entries = getPerformanceNavigationEntries();
var traceparent = (metaElement && metaElement.content) || '';
var baggage = (baggageElement && baggageElement.content) || '';
context.with(propagation.extract(context.active(), { traceparent: traceparent, baggage: baggage }), function () {
var _a;
var rootSpan = _this._startSpan(AttributeNames.DOCUMENT_LOAD, PTN.FETCH_START, entries);
if (!rootSpan) {
return;
}
context.with(trace.setSpan(context.active(), rootSpan), function () {
var fetchSpan = _this._startSpan(AttributeNames.DOCUMENT_FETCH, PTN.FETCH_START, entries);
if (fetchSpan) {
fetchSpan.setAttribute(SemanticAttributes.HTTP_URL, location.href);
context.with(trace.setSpan(context.active(), fetchSpan), function () {
var _a;
addSpanNetworkEvents(fetchSpan, entries);
_this._addCustomAttributesOnSpan(fetchSpan, (_a = _this._getConfig().applyCustomAttributesOnSpan) === null || _a === void 0 ? void 0 : _a.documentFetch);
_this._endSpan(fetchSpan, PTN.RESPONSE_END, entries);
});
}
});
rootSpan.setAttribute(SemanticAttributes.HTTP_URL, location.href);
rootSpan.setAttribute(SemanticAttributes.HTTP_USER_AGENT, navigator.userAgent);
_this._addResourcesSpans(rootSpan);
addSpanNetworkEvent(rootSpan, PTN.FETCH_START, entries);
addSpanNetworkEvent(rootSpan, PTN.UNLOAD_EVENT_START, entries);
addSpanNetworkEvent(rootSpan, PTN.UNLOAD_EVENT_END, entries);
addSpanNetworkEvent(rootSpan, PTN.DOM_INTERACTIVE, entries);
addSpanNetworkEvent(rootSpan, PTN.DOM_CONTENT_LOADED_EVENT_START, entries);
addSpanNetworkEvent(rootSpan, PTN.DOM_CONTENT_LOADED_EVENT_END, entries);
addSpanNetworkEvent(rootSpan, PTN.DOM_COMPLETE, entries);
addSpanNetworkEvent(rootSpan, PTN.LOAD_EVENT_START, entries);
addSpanNetworkEvent(rootSpan, PTN.LOAD_EVENT_END, entries);
addSpanPerformancePaintEvents(rootSpan);
_this._addCustomAttributesOnSpan(rootSpan, (_a = _this._getConfig().applyCustomAttributesOnSpan) === null || _a === void 0 ? void 0 : _a.documentLoad);
_this._endSpan(rootSpan, PTN.LOAD_EVENT_END, entries);
});
};
/**
* Helper function for ending span
* @param span
* @param performanceName name of performance entry for time end
* @param entries
*/
DocumentLoadInstrumentation.prototype._endSpan = function (span, performanceName, entries) {
// span can be undefined when entries are missing the certain performance - the span will not be created
if (span) {
if (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
*/
DocumentLoadInstrumentation.prototype._initResourceSpan = function (resource, parentSpan) {
var _a;
var span = this._startSpan(AttributeNames.RESOURCE_FETCH, PTN.FETCH_START, resource, parentSpan);
if (span) {
span.setAttribute(SemanticAttributes.HTTP_URL, resource.name);
addSpanNetworkEvents(span, resource);
this._addCustomAttributesOnSpan(span, (_a = this._getConfig().applyCustomAttributesOnSpan) === null || _a === void 0 ? void 0 : _a.resourceFetch);
this._endSpan(span, PTN.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
*/
DocumentLoadInstrumentation.prototype._startSpan = function (spanName, performanceName, entries, parentSpan) {
if (hasKey(entries, performanceName) &&
typeof entries[performanceName] === 'number') {
var span = this.tracer.startSpan(spanName, {
startTime: entries[performanceName],
}, parentSpan ? trace.setSpan(context.active(), parentSpan) : undefined);
return span;
}
return undefined;
};
/**
* executes callback {_onDocumentLoaded} when the page is loaded
*/
DocumentLoadInstrumentation.prototype._waitForPageLoad = function () {
if (window.document.readyState === 'complete') {
this._onDocumentLoaded();
}
else {
this._onDocumentLoaded = this._onDocumentLoaded.bind(this);
window.addEventListener('load', this._onDocumentLoaded);
}
};
DocumentLoadInstrumentation.prototype._getConfig = function () {
return this._config;
};
/**
* adds custom attributes to root span if configured
*/
DocumentLoadInstrumentation.prototype._addCustomAttributesOnSpan = function (span, applyCustomAttributesOnSpan) {
var _this = this;
if (applyCustomAttributesOnSpan) {
safeExecuteInTheMiddle(function () { return applyCustomAttributesOnSpan(span); }, function (error) {
if (!error) {
return;
}
_this._diag.error('addCustomAttributesOnSpan', error);
}, true);
}
};
/**
* implements enable function
*/
DocumentLoadInstrumentation.prototype.enable = function () {
// 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
*/
DocumentLoadInstrumentation.prototype.disable = function () {
window.removeEventListener('load', this._onDocumentLoaded);
};
return DocumentLoadInstrumentation;
}(InstrumentationBase));
export { DocumentLoadInstrumentation };
//# sourceMappingURL=instrumentation.js.map