UNPKG

@microsoft/applicationinsights-core-js

Version:

Microsoft Application Insights Core Javascript SDK

213 lines • 9.43 kB
/* * Application Insights JavaScript SDK - Core, 3.3.6 * Copyright (c) Microsoft and contributors. All rights reserved. */ import { arrForEach, isArray, isString, strLeft, strTrim } from "@nevware21/ts-utils"; import { _DYN_GET_ATTRIBUTE, _DYN_LENGTH, _DYN_PUSH, _DYN_SPLIT, _DYN_TO_LOWER_CASE, _DYN_TRACE_FLAGS, _DYN_VERSION } from "../__DynamicConstants"; import { generateW3CId } from "./CoreUtils"; import { findMetaTag, findNamedServerTiming } from "./EnvUtils"; import { STR_EMPTY } from "./InternalConstants"; // using {0,16} for leading and trailing whitespace just to constrain the possible runtime of a random string var TRACE_PARENT_REGEX = /^([\da-f]{2})-([\da-f]{32})-([\da-f]{16})-([\da-f]{2})(-[^\s]{1,64})?$/i; var DEFAULT_VERSION = "00"; var INVALID_VERSION = "ff"; var INVALID_TRACE_ID = "00000000000000000000000000000000"; var INVALID_SPAN_ID = "0000000000000000"; var SAMPLED_FLAG = 0x01; function _isValid(value, len, invalidValue) { if (value && value[_DYN_LENGTH /* @min:%2elength */] === len && value !== invalidValue) { return !!value.match(/^[\da-f]*$/i); } return false; } function _formatValue(value, len, defValue) { if (_isValid(value, len)) { return value; } return defValue; } function _formatFlags(value) { if (isNaN(value) || value < 0 || value > 255) { value = 0x01; } var result = value.toString(16); while (result[_DYN_LENGTH /* @min:%2elength */] < 2) { result = "0" + result; } return result; } /** * Create a new ITraceParent instance using the provided values. * @param traceId - The traceId to use, when invalid a new random W3C id will be generated. * @param spanId - The parent/span id to use, a new random value will be generated if it is invalid. * @param flags - The traceFlags to use, defaults to zero (0) if not supplied or invalid * @param version - The version to used, defaults to version "01" if not supplied or invalid. * @returns */ export function createTraceParent(traceId, spanId, flags, version) { return { version: _isValid(version, 2, INVALID_VERSION) ? version : DEFAULT_VERSION, traceId: isValidTraceId(traceId) ? traceId : generateW3CId(), spanId: isValidSpanId(spanId) ? spanId : strLeft(generateW3CId(), 16), traceFlags: flags >= 0 && flags <= 0xFF ? flags : 1 }; } /** * Attempt to parse the provided string as a W3C TraceParent header value (https://www.w3.org/TR/trace-context/#traceparent-header) * * @param value - The value to be parsed * @param selectIdx - If the found value is comma separated which is the preferred entry to select, defaults to the first * @returns */ export function parseTraceParent(value, selectIdx) { if (!value) { // Don't pass a null/undefined or empty string return null; } if (isArray(value)) { // The value may have been encoded on the page into an array so handle this automatically value = value[0] || ""; } if (!value || !isString(value) || value[_DYN_LENGTH /* @min:%2elength */] > 8192) { // limit potential processing based on total length return null; } if (value.indexOf(",") !== -1) { var values = value[_DYN_SPLIT /* @min:%2esplit */](","); value = values[selectIdx > 0 && values[_DYN_LENGTH /* @min:%2elength */] > selectIdx ? selectIdx : 0]; } // See https://www.w3.org/TR/trace-context/#versioning-of-traceparent var match = TRACE_PARENT_REGEX.exec(strTrim(value)); if (!match || // No match match[1] === INVALID_VERSION || // version ff is forbidden match[2] === INVALID_TRACE_ID || // All zeros is considered to be invalid match[3] === INVALID_SPAN_ID) { // All zeros is considered to be invalid return null; } return { version: (match[1] || STR_EMPTY)[_DYN_TO_LOWER_CASE /* @min:%2etoLowerCase */](), traceId: (match[2] || STR_EMPTY)[_DYN_TO_LOWER_CASE /* @min:%2etoLowerCase */](), spanId: (match[3] || STR_EMPTY)[_DYN_TO_LOWER_CASE /* @min:%2etoLowerCase */](), traceFlags: parseInt(match[4], 16) }; } /** * Is the provided W3c Trace Id a valid string representation, it must be a 32-character string * of lowercase hexadecimal characters for example, 4bf92f3577b34da6a3ce929d0e0e4736. * If all characters as zero (00000000000000000000000000000000) it will be considered an invalid value. * @param value - The W3c trace Id to be validated * @returns true if valid otherwise false */ export function isValidTraceId(value) { return _isValid(value, 32, INVALID_TRACE_ID); } /** * Is the provided W3c span id (aka. parent id) a valid string representation, it must be a 16-character * string of lowercase hexadecimal characters, for example, 00f067aa0ba902b7. * If all characters are zero (0000000000000000) this is considered an invalid value. * @param value - The W3c span id to be validated * @returns true if valid otherwise false */ export function isValidSpanId(value) { return _isValid(value, 16, INVALID_SPAN_ID); } /** * Validates that the provided ITraceParent instance conforms to the currently supported specifications * @param value - The parsed traceParent value * @returns */ export function isValidTraceParent(value) { if (!value || !_isValid(value[_DYN_VERSION /* @min:%2eversion */], 2, INVALID_VERSION) || !_isValid(value.traceId, 32, INVALID_TRACE_ID) || !_isValid(value.spanId, 16, INVALID_SPAN_ID) || !_isValid(_formatFlags(value[_DYN_TRACE_FLAGS /* @min:%2etraceFlags */]), 2)) { // Each known field must contain a valid value return false; } return true; } /** * Is the parsed traceParent indicating that the trace is currently sampled. * @param value - The parsed traceParent value * @returns */ export function isSampledFlag(value) { if (isValidTraceParent(value)) { return (value[_DYN_TRACE_FLAGS /* @min:%2etraceFlags */] & SAMPLED_FLAG) === SAMPLED_FLAG; } return false; } /** * Format the ITraceParent value as a string using the supported and know version formats. * So even if the passed traceParent is a later version the string value returned from this * function will convert it to only the known version formats. * This currently only supports version "00" and invalid "ff" * @param value - The parsed traceParent value * @returns */ export function formatTraceParent(value) { if (value) { // Special Note: This only supports formatting as version 00, future versions should encode any known supported version // So parsing a future version will populate the correct version value but reformatting will reduce it to version 00. var flags = _formatFlags(value[_DYN_TRACE_FLAGS /* @min:%2etraceFlags */]); if (!_isValid(flags, 2)) { flags = "01"; } var version = value[_DYN_VERSION /* @min:%2eversion */] || DEFAULT_VERSION; if (version !== "00" && version !== "ff") { // Reduce version to "00" version = DEFAULT_VERSION; } // Format as version 00 return "".concat(version.toLowerCase(), "-").concat(_formatValue(value.traceId, 32, INVALID_TRACE_ID).toLowerCase(), "-").concat(_formatValue(value.spanId, 16, INVALID_SPAN_ID).toLowerCase(), "-").concat(flags.toLowerCase()); } return ""; } /** * Helper function to fetch the passed traceparent from the page, looking for it as a meta-tag or a Server-Timing header. * @param selectIdx - If the found value is comma separated which is the preferred entry to select, defaults to the first * @returns */ export function findW3cTraceParent(selectIdx) { var name = "traceparent"; var traceParent = parseTraceParent(findMetaTag(name), selectIdx); if (!traceParent) { traceParent = parseTraceParent(findNamedServerTiming(name), selectIdx); } return traceParent; } /** * Find all script tags in the provided document and return the information about them. * @param doc - The document to search for script tags * @returns */ export function findAllScripts(doc) { var scripts = doc.getElementsByTagName("script"); var result = []; arrForEach(scripts, function (script) { var src = script[_DYN_GET_ATTRIBUTE /* @min:%2egetAttribute */]("src"); if (src) { var crossOrigin = script[_DYN_GET_ATTRIBUTE /* @min:%2egetAttribute */]("crossorigin"); var async = script.hasAttribute("async") === true; var defer = script.hasAttribute("defer") === true; var referrerPolicy = script[_DYN_GET_ATTRIBUTE /* @min:%2egetAttribute */]("referrerpolicy"); var info = { url: src }; if (crossOrigin) { info.crossOrigin = crossOrigin; } if (async) { info.async = async; } if (defer) { info.defer = defer; } if (referrerPolicy) { info.referrerPolicy = referrerPolicy; } result[_DYN_PUSH /* @min:%2epush */](info); } }); return result; } //# sourceMappingURL=W3cTraceParent.js.map