From b2df774b1f96f282a67b8e80e1ee745a635494c7 Mon Sep 17 00:00:00 2001 From: Nev Wylie Date: Tue, 14 Apr 2020 17:21:45 -0700 Subject: [PATCH] Task 7017105: Identify whether the script is being consumed via the CDN or NPM package --- AISKU/src/Initialization.ts | 63 ++++++++ .../src/Context/Internal.ts | 15 ++ .../src/TelemetryContext.ts | 139 ++++++++++-------- .../Contracts/Generated/ContextTagKeys.ts | 12 ++ 4 files changed, 169 insertions(+), 60 deletions(-) diff --git a/AISKU/src/Initialization.ts b/AISKU/src/Initialization.ts index c91881b33..41e518b54 100644 --- a/AISKU/src/Initialization.ts +++ b/AISKU/src/Initialization.ts @@ -10,6 +10,8 @@ import * as Common from "@microsoft/applicationinsights-common" "use strict"; +let _internalSdkSrc: string; + /** * * @export @@ -18,6 +20,7 @@ import * as Common from "@microsoft/applicationinsights-common" export interface Snippet { config: IConfiguration & Common.IConfig; queue?: Array<() => void>; + sv?: string; version?: number; } @@ -46,10 +49,12 @@ export class Initialization implements IApplicationInsights { private dependencies: DependenciesPlugin; private properties: PropertiesPlugin; + private _snippetVersion: string; constructor(snippet: Snippet) { let _this = this; // initialize the queue and config in case they are undefined + _this._snippetVersion = "" + (snippet.sv || snippet.version || ""); snippet.queue = snippet.queue || []; snippet.version = snippet.version || 2.0; // Default to new version let config: IConfiguration & Common.IConfig = snippet.config || ({} as any); @@ -269,6 +274,15 @@ export class Initialization implements IApplicationInsights { function _updateSnippetProperties(snippet: Snippet) { if (snippet) { + let snippetVer = ""; + if (!CoreUtils.isNullOrUndefined(_this._snippetVersion)) { + snippetVer += _this._snippetVersion; + } + if (legacyMode) { + snippetVer += ".lg"; + } + _this.properties.context.internal.snippetVer = snippetVer || "-"; + // apply updated properties to the global instance (snippet) for (const field in _this) { if (CoreUtils.isString(field) && @@ -296,6 +310,9 @@ export class Initialization implements IApplicationInsights { // initialize core _this.core.initialize(_this.config, extensions); _this.context = _this.properties.context; + if (_internalSdkSrc) { + _this.context.internal.sdkSrc = _internalSdkSrc; + } _updateSnippetProperties(_this.snippet); // Empty queue of all api calls logged prior to sdk download @@ -411,3 +428,49 @@ export class Initialization implements IApplicationInsights { _this.config.diagnosticLogInterval && _this.config.diagnosticLogInterval > 0 ? _this.config.diagnosticLogInterval : 10000; } } + +// tslint:disable-next-line +(function () { + let sdkSrc = null; + let isModule = false; + + try { + // Try and determine whether the sdk is being loaded from the CDN + // currentScript is only valid during initial processing + let scrpt = (document ||{} as any).currentScript; + if (scrpt) { + sdkSrc = scrpt.src; + // } else { + // // We need to update to at least typescript 2.9 for this to work :-( + // // Leaving as a stub for now so after we upgrade this breadcrumb is available + // let meta = import.meta; + // sdkSrc = (meta || {}).url; + // isModule = true; + } + } catch (e) { + } + + if (sdkSrc) { + try { + let url = sdkSrc.toLowerCase(); + if (url) { + let src = ""; + if (url.indexOf("://az416426.vo.msecnd.net/") !== -1) { + src = "cdn1"; + if (url.indexOf("/scripts/") === -1) { + if (url.indexOf("/next/") !== -1) { + src += "-next"; + } else if (url.indexOf("/beta/") !== -1) { + src += "-beta"; + } + } + } + + if (src) { + _internalSdkSrc = src + (isModule ? ".mod" : ""); + } + } + } catch (e) { + } + } +})(); diff --git a/extensions/applicationinsights-properties-js/src/Context/Internal.ts b/extensions/applicationinsights-properties-js/src/Context/Internal.ts index b037c0ba3..e4303b397 100644 --- a/extensions/applicationinsights-properties-js/src/Context/Internal.ts +++ b/extensions/applicationinsights-properties-js/src/Context/Internal.ts @@ -18,6 +18,21 @@ export class Internal implements IInternal { */ public agentVersion: string; + /** + * The Snippet version used to initialize the sdk instance, this will contain either + * undefined/null - Snippet not used + * '-' - Version and legacy mode not determined + * # - Version # of the snippet + * #.l - Version # in legacy mode + * .l - No defined version, but used legacy mode initialization + */ + public snippetVer: string; + + /** + * Identifies the source of the sdk script + */ + public sdkSrc: string; + /** * Constructs a new instance of the internal telemetry data class. */ diff --git a/extensions/applicationinsights-properties-js/src/TelemetryContext.ts b/extensions/applicationinsights-properties-js/src/TelemetryContext.ts index babf39b94..3710f8d00 100644 --- a/extensions/applicationinsights-properties-js/src/TelemetryContext.ts +++ b/extensions/applicationinsights-properties-js/src/TelemetryContext.ts @@ -3,9 +3,9 @@ * @copyright Microsoft 2018 */ -import { ITelemetryItem, IProcessTelemetryContext, IDiagnosticLogger, CoreUtils, hasWindow } from '@microsoft/applicationinsights-core-js'; +import { ITelemetryItem, IProcessTelemetryContext, IDiagnosticLogger, CoreUtils, hasWindow, _InternalLogMessage } from '@microsoft/applicationinsights-core-js'; import { Session, _SessionManager } from './Context/Session'; -import { Extensions, ITelemetryContext, IOperatingSystem, ITelemetryTrace, IWeb, SampleRate, CtxTagKeys } from '@microsoft/applicationinsights-common'; +import { Extensions, ITelemetryContext, IOperatingSystem, ITelemetryTrace, IWeb, SampleRate, CtxTagKeys, PageView } from '@microsoft/applicationinsights-common'; import { Application } from './Context/Application'; import { Device } from './Context/Device'; import { Internal } from './Context/Internal'; @@ -29,108 +29,126 @@ export class TelemetryContext implements ITelemetryContext { public appId: () => string; constructor(logger: IDiagnosticLogger, defaultConfig: ITelemetryConfig) { + let _self = this; if (hasWindow()) { - this.sessionManager = new _SessionManager(defaultConfig, logger); - this.application = new Application(); - this.device = new Device(); - this.internal = new Internal(defaultConfig); - this.location = new Location(); - this.user = new User(defaultConfig, logger); - this.telemetryTrace = new TelemetryTrace(undefined, undefined, undefined, logger); - this.session = new Session(); - } - this.appId = () => null; + _self.sessionManager = new _SessionManager(defaultConfig, logger); + _self.application = new Application(); + _self.device = new Device(); + _self.internal = new Internal(defaultConfig); + _self.location = new Location(); + _self.user = new User(defaultConfig, logger); + _self.telemetryTrace = new TelemetryTrace(undefined, undefined, undefined, logger); + _self.session = new Session(); + } + _self.appId = () => null; } public applySessionContext(event: ITelemetryItem, itemCtx?: IProcessTelemetryContext) { - const sessionContext = this.session || (this.sessionManager && this.sessionManager.automaticSession); + let session = this.session; + let sessionManager = this.sessionManager; + const sessionContext = session || (sessionManager && sessionManager.automaticSession); if (sessionContext) { - if (typeof sessionContext.id === "string") { + if (CoreUtils.isString(sessionContext.id)) { event.ext.app.sesId = sessionContext.id; } } - if (this.session) { + if (session) { // If customer set session info, apply his context; otherwise apply context automatically generated - if (typeof this.session.id === "string") { - event.ext.app.sesId = this.session.id; + if (CoreUtils.isString(session.id)) { + event.ext.app.sesId = session.id; } else { - event.ext.app.sesId = this.sessionManager.automaticSession.id; + event.ext.app.sesId = sessionManager.automaticSession.id; } } } public applyOperatingSystemContxt(event: ITelemetryItem, itemCtx?: IProcessTelemetryContext) { - if (this.os && this.os.name) { - event.ext.os = this.os; + let os = this.os; + if (os && os.name) { + event.ext.os = os; } } public applyApplicationContext(event: ITelemetryItem, itemCtx?: IProcessTelemetryContext) { - if (this.application) { + let application = this.application; + if (application) { - if (typeof this.application.ver === "string") { - event.tags[CtxTagKeys.applicationVersion] = this.application.ver; + if (CoreUtils.isString(application.ver)) { + event.tags[CtxTagKeys.applicationVersion] = application.ver; } - if (typeof this.application.build === "string") { - event.tags[CtxTagKeys.applicationBuild] = this.application.build; + if (CoreUtils.isString(application.build)) { + event.tags[CtxTagKeys.applicationBuild] = application.build; } } } public applyDeviceContext(event: ITelemetryItem, itemCtx?: IProcessTelemetryContext) { - - if (this.device) { - if (typeof this.device.id === "string") { - event.ext.device.localId = this.device.id; + let device = this.device; + if (device) { + if (CoreUtils.isString(device.id)) { + event.ext.device.localId = device.id; } - if (typeof this.device.ip === "string") { - event.ext.device.ip = this.device.ip; + if (CoreUtils.isString(device.ip)) { + event.ext.device.ip = device.ip; } - if (typeof this.device.model === "string") { - event.ext.device.model = this.device.model; + if (CoreUtils.isString(device.model)) { + event.ext.device.model = device.model; } - if (typeof this.device.deviceClass === "string") { - event.ext.device.deviceClass = this.device.deviceClass; + if (CoreUtils.isString(device.deviceClass)) { + event.ext.device.deviceClass = device.deviceClass; } } } public applyInternalContext(event: ITelemetryItem, itemCtx?: IProcessTelemetryContext) { - if (this.internal) { - if (typeof this.internal.agentVersion === "string") { - event.tags[CtxTagKeys.internalAgentVersion] = this.internal.agentVersion; // not mapped in CS 4.0 + let internal = this.internal; + if (internal) { + if (CoreUtils.isString(internal.agentVersion)) { + event.tags[CtxTagKeys.internalAgentVersion] = internal.agentVersion; // not mapped in CS 4.0 + } + if (CoreUtils.isString(internal.sdkVersion)) { + event.tags[CtxTagKeys.internalSdkVersion] = internal.sdkVersion; } - if (typeof this.internal.sdkVersion === "string") { - event.tags[CtxTagKeys.internalSdkVersion] = this.internal.sdkVersion; + + if (event.baseType === _InternalLogMessage.dataType || event.baseType === PageView.dataType) { + if (CoreUtils.isString(internal.snippetVer)) { + event.tags[CtxTagKeys.internalSnippet] = internal.snippetVer; + } + + if (CoreUtils.isString(internal.sdkSrc)) { + event.tags[CtxTagKeys.internalSdkSrc] = internal.sdkSrc; + } } } } public applyLocationContext(event: ITelemetryItem, itemCtx?: IProcessTelemetryContext) { - if (this.location) { - if (typeof this.location.ip === "string") { - event.tags[CtxTagKeys.locationIp] = this.location.ip; + let location = this.location; + if (location) { + if (CoreUtils.isString(location.ip)) { + event.tags[CtxTagKeys.locationIp] = location.ip; } } } public applyOperationContext(event: ITelemetryItem, itemCtx?: IProcessTelemetryContext) { - if (this.telemetryTrace) { + let telemetryTrace = this.telemetryTrace; + if (telemetryTrace) { const trace = event.ext.trace || ({traceID: undefined, parentID: undefined} as ITelemetryTrace); - if (typeof this.telemetryTrace.traceID === "string") { - trace.traceID = this.telemetryTrace.traceID; + if (CoreUtils.isString(telemetryTrace.traceID)) { + trace.traceID = telemetryTrace.traceID; } - if (typeof this.telemetryTrace.name === "string") { - trace.name = this.telemetryTrace.name; + if (CoreUtils.isString(telemetryTrace.name)) { + trace.name = telemetryTrace.name; } - if (typeof this.telemetryTrace.parentID === "string") { - trace.parentID = this.telemetryTrace.parentID; + if (CoreUtils.isString(telemetryTrace.parentID)) { + trace.parentID = telemetryTrace.parentID; } event.ext.trace = trace; @@ -138,31 +156,32 @@ export class TelemetryContext implements ITelemetryContext { } public applyWebContext(event: ITelemetryItem, itemCtx?: IProcessTelemetryContext) { - if (this.web) { + let web = this.web; + if (web) { event.ext.web = event.ext.web || {}; - event.ext.web = this.web; + event.ext.web = web; } } public applyUserContext(event: ITelemetryItem, itemCtx?: IProcessTelemetryContext) { - if (this.user) { + let user = this.user; + if (user) { if (!event.tags) { event.tags = []; } // stays in tags - if (typeof this.user.accountId === "string") { - const item = {}; - event.tags[CtxTagKeys.userAccountId] = this.user.accountId; + if (CoreUtils.isString(user.accountId)) { + event.tags[CtxTagKeys.userAccountId] = user.accountId; } // CS 4.0 - if (typeof this.user.id === "string") { - event.ext.user.id = this.user.id; + if (CoreUtils.isString( user.id)) { + event.ext.user.id = user.id; } - if (typeof this.user.authenticatedId === "string") { - event.ext.user.authId = this.user.authenticatedId; + if (CoreUtils.isString(user.authenticatedId)) { + event.ext.user.authId = user.authenticatedId; } } } diff --git a/shared/AppInsightsCommon/src/Interfaces/Contracts/Generated/ContextTagKeys.ts b/shared/AppInsightsCommon/src/Interfaces/Contracts/Generated/ContextTagKeys.ts index 844496c2f..3aee58e39 100644 --- a/shared/AppInsightsCommon/src/Interfaces/Contracts/Generated/ContextTagKeys.ts +++ b/shared/AppInsightsCommon/src/Interfaces/Contracts/Generated/ContextTagKeys.ts @@ -199,6 +199,16 @@ export class ContextTagKeys { */ public internalNodeName: string; + /** + * This identifies the version of the snippet that was used to initialize the SDK + */ + public internalSnippet: string; + + /** + * This identifies the source of the Sdk script (used to identify whether the SDK was loaded via the CDN) + */ + public internalSdkSrc: string; + constructor() { this.applicationVersion = "ai.application.ver"; this.applicationBuild = "ai.application.build"; @@ -257,5 +267,7 @@ export class ContextTagKeys { this.internalNodeName = "ai.internal.nodeName"; this.internalSdkVersion = "ai.internal.sdkVersion"; this.internalAgentVersion = "ai.internal.agentVersion"; + this.internalSnippet = "ai.internal.snippet"; + this.internalSdkSrc = "ai.internal.sdkSrc"; } }