UNPKG

@openui5/sap.ui.core

Version:

OpenUI5 Core Library sap.ui.core

363 lines (325 loc) 12.4 kB
/*! * OpenUI5 * (c) Copyright 2009-2021 SAP SE or an SAP affiliate company. * Licensed under the Apache License, Version 2.0 - see LICENSE.txt. */ /*global WeakMap */ sap.ui.define([ 'sap/ui/thirdparty/URI', 'sap/ui/Device', 'sap/ui/performance/trace/Passport', 'sap/ui/performance/trace/Interaction', 'sap/ui/performance/XHRInterceptor', 'sap/ui/performance/BeaconRequest', 'sap/base/util/Version' ], function (URI, Device, Passport, Interaction, XHRInterceptor, BeaconRequest, Version) { "use strict"; // activation by meta tag or url parameter as fallback var bFesrActive = false, sBeaconURL, oBeaconRequest, iBeaconTimeoutID, ROOT_ID = Passport.getRootId(), // static per session HOST = window.location.host, // static per session CLIENT_OS = Device.os.name + "_" + Device.os.version, CLIENT_MODEL = Device.browser.name + "_" + Device.browser.version, CLIENT_DEVICE = setClientDevice(), sAppVersion = "", // shortened app version with fesr delimiter e.g. "@1.7.1" sAppVersionFull = "", // full app version e.g. 1.7.1-SNAPSHOT sFESRTransactionId, // first transaction id of an interaction step, serves as identifier for the fesr-header iStepCounter = 0, // counts FESR interaction steps sPassportComponentInfo = "undetermined", sPassportAction = "undetermined_startup_0", sFESR, // current header string sFESRopt, // current header string wmPassportHeader = new WeakMap(); //a WeakMap to access passport header with a given XHR as key. function setClientDevice() { var iClientId = 0; if (Device.system.combi) { iClientId = 1; } else if (Device.system.desktop) { iClientId = 2; } else if (Device.system.tablet) { iClientId = 4; } else if (Device.system.phone) { iClientId = 3; } return iClientId; } function formatInteractionStartTimestamp(iTimeStamp) { var oDate = new Date(iTimeStamp); return oDate.toISOString().replace(/[^\d]/g, ''); } function isCORSRequest(sUrl) { var sHost = new URI(sUrl).host(); // url is relative or with same host return sHost && sHost !== HOST; } function passportHeaderOverride() { // only use Passport for non CORS requests if (!isCORSRequest(arguments[1])) { // use the first request of an interaction as FESR TransactionID if (!sFESRTransactionId) { sFESRTransactionId = Passport.getTransactionId(); } var sPassportHeader = Passport.header( Passport.traceFlags(), ROOT_ID, Passport.getTransactionId(), sPassportComponentInfo, sPassportAction ); // set passport with Root Context ID, Transaction ID, Component Info, Action this.setRequestHeader("SAP-PASSPORT", sPassportHeader); wmPassportHeader.set(this, sPassportHeader); } } /** * Sends the FESR header when using the piggyback aproach * @private */ function fesrHeaderOverride() { // only use FESR for non CORS requests if (!isCORSRequest(arguments[1])) { if (sFESR && sFESRopt) { this.setRequestHeader("SAP-Perf-FESRec", sFESR); this.setRequestHeader("SAP-Perf-FESRec-opt", sFESRopt); sFESR = null; sFESRopt = null; sFESRTransactionId = Passport.getTransactionId(); } } } // creates mandatory FESR header string function createFESR(oInteraction, oFESRHandle) { return [ format(ROOT_ID, 32), // root_context_id format(sFESRTransactionId, 32), // transaction_id formatInt(oInteraction.navigation, 4), // client_navigation_time formatInt(oInteraction.roundtrip, 4), // client_round_trip_time formatInt(oFESRHandle.timeToInteractive, 4), // end_to_end_time formatInt(oInteraction.completeRoundtrips, 2), // completed network_round_trips format(sPassportAction, 40, true), // passport_action formatInt(oInteraction.networkTime, 4), // network_time formatInt(oInteraction.requestTime, 4), // request_time format(CLIENT_OS, 10), // client_os "SAP_UI5" // client_type ].join(","); } // creates optional FESR header string function createFESRopt(oInteraction, oFESRHandle) { return [ format(oFESRHandle.appNameShort, 20, true), // application_name format(oFESRHandle.stepName, 20, true), // step_name "", // not assigned format(CLIENT_MODEL, 20), // client_model formatInt(oInteraction.bytesSent, 4), // client_data_sent formatInt(oInteraction.bytesReceived, 4), // client_data_received "", // network_protocol "", // network_provider formatInt(oInteraction.processing, 4), // client_processing_time oInteraction.requestCompression ? "X" : "", // compressed - empty if not compressed "", // not assigned "", // persistency_accesses "", // persistency_time "", // persistency_data_transferred formatInt(oInteraction.busyDuration, 4), // extension_1 - busy duration formatInt(oFESRHandle.interactionType || 0, 4), // extension_2 - type of interaction: 0 = not implemented, 1 = app start, 2 = follow up step, 3 = unknown format(CLIENT_DEVICE, 1), // extension_3 - client device "", // extension_4 format(formatInteractionStartTimestamp(oInteraction.start), 20), // extension_5 - interaction start time format(oFESRHandle.appNameLong, 70, true) // application_name with 70 characters, trimmed from left ].join(","); } // format string to fesr compliant string function format(vField, iLength, bCutFromFront) { if (!vField) { vField = vField === 0 ? "0" : ""; } else if (typeof vField === "number") { var iField = vField; vField = Math.round(vField).toString(); // Calculation of figures may be erroneous because incomplete performance entries lead to negative // numbers. In that case we set a -1, so the "dirty" record can be identified as such. if (vField.length > iLength || iField < 0) { vField = "-1"; } } else { vField = bCutFromFront ? vField.substr(-iLength, iLength) : vField.substr(0, iLength); } return vField; } /* Format a int number to fesr compliant specs * If given number is negative or not compliant we format to -1. * Supports int1/2/4. */ function formatInt(number, bytes) { if (typeof number !== "number") { number = ""; } else { var max = Math.pow(256, bytes) / 2 - 1; number = Math.round(number); number = number >= 0 && number <= max ? number.toString() : "-1"; } return number; } function formatVersion(sVersion) { var oVersion = new Version(sVersion); return "@" + oVersion.getMajor() + "." + oVersion.getMinor() + "." + oVersion.getPatch(); } function createHeader(oFinishedInteraction, oFESRHandle) { // create FESR from completed interaction sFESR = createFESR(oFinishedInteraction, oFESRHandle); sFESRopt = createFESRopt(oFinishedInteraction, oFESRHandle); } function onInteractionStarted(oInteraction) { // increase the step count for Passport and FESR (initial loading starts with 0) iStepCounter++; // update Passport relevant fields sPassportComponentInfo = oInteraction ? oInteraction.component + sAppVersion : undefined; sPassportAction = oInteraction ? oInteraction.trigger + "_" + oInteraction.event + "_" + iStepCounter : undefined; return sPassportAction; } function onInteractionFinished(oFinishedInteraction) { var sStepName = oFinishedInteraction.trigger + "_" + oFinishedInteraction.event; var oFESRHandle = FESR.onBeforeCreated({ stepName: sStepName, appNameLong: oFinishedInteraction.stepComponent || oFinishedInteraction.component, appNameShort: oFinishedInteraction.stepComponent || oFinishedInteraction.component, timeToInteractive: oFinishedInteraction.duration, interactionType: determineInteractionType(sStepName) }, oFinishedInteraction); // do not send UI-only FESR with piggyback approach if (oBeaconRequest || oFinishedInteraction.requests.length > 0) { createHeader(oFinishedInteraction, oFESRHandle); if (oBeaconRequest) { // reset the transactionId for Beacon approach sFESRTransactionId = null; } } // use the sendBeacon API instead of the piggyback approach if (oBeaconRequest && sFESR && sFESRopt) { oBeaconRequest.append("SAP-Perf-FESRec", sFESR + "SAP-Perf-FESRec-opt" + sFESRopt); sendBeaconRequest(); } if (sAppVersionFull != oFinishedInteraction.appVersion) { sAppVersionFull = oFinishedInteraction.appVersion; sAppVersion = sAppVersionFull ? formatVersion(sAppVersionFull) : ""; } sPassportAction = "undefined"; } function sendBeaconRequest() { if (!iBeaconTimeoutID) { iBeaconTimeoutID = setTimeout(function() { oBeaconRequest.send(); clearTimeout(iBeaconTimeoutID); iBeaconTimeoutID = undefined; }, 60000); } } function determineInteractionType(sStepName) { var interactionType = 2; if (sStepName.indexOf("startup") !== -1) { interactionType = 1; } return interactionType; } /** * FESR API, consumed by E2eTraceLib instead of former EppLib.js. * Provides functionality for creating the headers for the frontend-subrecords which will be sent with each * first request of an interaction. The headers have a specific format, you may have a look at the createFESR * methods.<br> * There is a special order in which things are happening: * <pre> * 1. Interaction starts * 1.1. Request 1.1 sent * 1.2. Request 1.2 sent * 2. Interaction starts * 2.1 Creation of FESR for 1. interaction * 2.2 Request 2.1 sent with FESR header for 1. interaction * ... * </pre> * * @namespace * @since 1.58 * @alias module:sap/ui/performance/trace/FESR * @static * @public */ var FESR = {}; FESR.getBeaconURL = function() { return sBeaconURL; }; /** * @param {boolean} bActive State of the FESR header creation * @param {string} [sUrl] beacon url * @private * @ui5-restricted sap.ui.core */ FESR.setActive = function (bActive, sUrl) { if (bActive && !bFesrActive) { oBeaconRequest = sUrl ? BeaconRequest.isSupported() && new BeaconRequest({url: sUrl}) : null; sBeaconURL = sUrl; bFesrActive = true; Passport.setActive(true); Interaction.setActive(true); XHRInterceptor.register("PASSPORT_HEADER", "open", passportHeaderOverride); if (!oBeaconRequest) { XHRInterceptor.register("FESR", "open" , fesrHeaderOverride); } Interaction.onInteractionStarted = onInteractionStarted; Interaction.onInteractionFinished = onInteractionFinished; Interaction.passportHeader = wmPassportHeader; } else if (!bActive && bFesrActive) { bFesrActive = false; Interaction.setActive(false); XHRInterceptor.unregister("FESR", "open"); // passport stays active so far if (XHRInterceptor.isRegistered("PASSPORT_HEADER", "open")) { XHRInterceptor.register("PASSPORT_HEADER", "open", function() { // set passport with Root Context ID, Transaction ID for Trace this.setRequestHeader("SAP-PASSPORT", Passport.header(Passport.traceFlags(), ROOT_ID, Passport.getTransactionId())); }); } if (oBeaconRequest) { oBeaconRequest.send(); clearTimeout(iBeaconTimeoutID); iBeaconTimeoutID = null; oBeaconRequest = null; sBeaconURL = null; } Interaction.onInteractionFinished = null; Interaction.onInteractionStarted = null; } }; /** * @return {boolean} State of the FESR header creation * @private * @ui5-restricted sap.ui.core */ FESR.getActive = function () { return bFesrActive; }; /** * Hook function that allows to override specific FESR header information. * @param {object} oFESRHandle The header information that can be modified * @param {string} oFESRHandle.stepName The step name with <Trigger>_<Event> * @param {string} oFESRHandle.appNameLong The application name with max 70 chars * @param {string} oFESRHandle.appNameShort The application name with max 20 chars * @param {string} oFESRHandle.interactionType Type of interaction: 0 = not implemented, 1 = app start, 2 = step in open app, 3 = unknown * @param {int} oFESRHandle.timeToInteractive The Time To Interactive (TTI) with max 16 digits * @param {object} oInteraction The corresponding interaction object, read-only * @return {object} Modified header information * @private * @ui5-restricted sap.ui.core, sap.ushell */ FESR.onBeforeCreated = function(oFESRHandle, oInteraction) { return { stepName: oFESRHandle.stepName, appNameLong: oFESRHandle.appNameLong, appNameShort: oFESRHandle.appNameShort, timeToInteractive: oFESRHandle.timeToInteractive, interactionType: oFESRHandle.interactionType }; }; return FESR; });