Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Identify whether the script is being consumed via the CDN or NPM package #1249

Merged
merged 1 commit into from Apr 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
63 changes: 63 additions & 0 deletions AISKU/src/Initialization.ts
Expand Up @@ -10,6 +10,8 @@ import * as Common from "@microsoft/applicationinsights-common"

"use strict";

let _internalSdkSrc: string;

/**
*
* @export
Expand All @@ -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;
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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) &&
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried quite a few work-arounds to get this information with 2.5.3 (our current TS version), but nothing worked -- I could have done some sort of post processing (like the es3-poly), but that seems like overkill considering we should be upgrading to a later version of TS sooner rather than later.

// 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) {
Copy link
Collaborator Author

@MSNev MSNev Apr 15, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only populating if this is from the standard CDN -- ignore non MS defined endpoints, and only include an anonymous name "cdn1" rather than the full domain/url

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the script is consumed from CDN via the CName will we have to update this to match those domain names?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, once we have enabled additional common names this will need to be updated and I'm planning on enumerating the known names (which is why I called the existing one cdn1)

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) {
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When caught, does that mean NPM? Is it possible to know NPM vs CDN vs unknown?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This catch is unexpected and added purely to ensure that we don't crash, in which case this will mean that we don't know the outcome (same as what would happen with all IE and really old browsers).

So it's not possible to "always" determine npm, cdn or unknown. This is also why I added the snippet property as it should always be defined in which case we could assume that snippet users are using the primary CDN (vs their own).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok so for now it will report primary CDN or (NPM + custom CDN + unknown)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will report primary CDN or nothing.
And when nothing we can assume that its NPM if also there is no snippet.

}
})();
Expand Up @@ -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.
*/
Expand Down
139 changes: 79 additions & 60 deletions extensions/applicationinsights-properties-js/src/TelemetryContext.ts
Expand Up @@ -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';
Expand All @@ -29,140 +29,159 @@ 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) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Limited to the Internal messages and initial page view

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;
}
}

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;
}
}
}
Expand Down
Expand Up @@ -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";
Expand Down Expand Up @@ -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";
}
}