Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AISKU/Tests/Unit/src/AISKUSize.Tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { Snippet } from "../../../src/Snippet";
import { utlRemoveSessionStorage } from "@microsoft/applicationinsights-common";

export class AISKUSizeCheck extends AITestClass {
private readonly MAX_RAW_SIZE = 143;
private readonly MAX_BUNDLE_SIZE = 143;
private readonly MAX_RAW_SIZE = 144;
private readonly MAX_BUNDLE_SIZE = 144;
private readonly MAX_RAW_DEFLATE_SIZE = 58;
private readonly MAX_BUNDLE_DEFLATE_SIZE = 58;
private readonly rawFilePath = "../dist/es5/applicationinsights-web.min.js";
Expand Down
19 changes: 14 additions & 5 deletions AISKU/samples/HelloWorld/index-snippet.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,33 @@
<!-- import Application Insights JS SDK from CDN -->
<script type="text/javascript">!function(T,l,y){var S=T.location,k="script",D="instrumentationKey",C="ingestionendpoint",I="disableExceptionTracking",E="ai.device.",b="toLowerCase",w="crossOrigin",N="POST",e="appInsightsSDK",t=y.name||"appInsights";(y.name||T[e])&&(T[e]=t);var n=T[t]||function(d){var g=!1,f=!1,m={initialize:!0,queue:[],sv:"5",version:2,config:d};function v(e,t){var n={},a="Browser";return n[E+"id"]=a[b](),n[E+"type"]=a,n["ai.operation.name"]=S&&S.pathname||"_unknown_",n["ai.internal.sdkVersion"]="javascript:snippet_"+(m.sv||m.version),{time:function(){var e=new Date;function t(e){var t=""+e;return 1===t.length&&(t="0"+t),t}return e.getUTCFullYear()+"-"+t(1+e.getUTCMonth())+"-"+t(e.getUTCDate())+"T"+t(e.getUTCHours())+":"+t(e.getUTCMinutes())+":"+t(e.getUTCSeconds())+"."+((e.getUTCMilliseconds()/1e3).toFixed(3)+"").slice(2,5)+"Z"}(),iKey:e,name:"Microsoft.ApplicationInsights."+e.replace(/-/g,"")+"."+t,sampleRate:100,tags:n,data:{baseData:{ver:2}}}}var h=d.url||y.src;if(h){function a(e){var t,n,a,i,r,o,s,c,u,p,l;g=!0,m.queue=[],f||(f=!0,t=h,s=function(){var e={},t=d.connectionString;if(t)for(var n=t.split(";"),a=0;a<n.length;a++){var i=n[a].split("=");2===i.length&&(e[i[0][b]()]=i[1])}if(!e[C]){var r=e.endpointsuffix,o=r?e.location:null;e[C]="https://"+(o?o+".":"")+"dc."+(r||"services.visualstudio.com")}return e}(),c=s[D]||d[D]||"",u=s[C],p=u?u+"/v2/track":d.endpointUrl,(l=[]).push((n="SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details)",a=t,i=p,(o=(r=v(c,"Exception")).data).baseType="ExceptionData",o.baseData.exceptions=[{typeName:"SDKLoadFailed",message:n.replace(/\./g,"-"),hasFullStack:!1,stack:n+"\nSnippet failed to load ["+a+"] -- Telemetry is disabled\nHelp Link: https://go.microsoft.com/fwlink/?linkid=2128109\nHost: "+(S&&S.pathname||"_unknown_")+"\nEndpoint: "+i,parsedStack:[]}],r)),l.push(function(e,t,n,a){var i=v(c,"Message"),r=i.data;r.baseType="MessageData";var o=r.baseData;return o.message='AI (Internal): 99 message:"'+("SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details) ("+n+")").replace(/\"/g,"")+'"',o.properties={endpoint:a},i}(0,0,t,p)),function(e,t){if(JSON){var n=T.fetch;if(n&&!y.useXhr)n(t,{method:N,body:JSON.stringify(e),mode:"cors"});else if(XMLHttpRequest){var a=new XMLHttpRequest;a.open(N,t),a.setRequestHeader("Content-type","application/json"),a.send(JSON.stringify(e))}}}(l,p))}function i(e,t){f||setTimeout(function(){!t&&m.core||a()},500)}var e=function(){var n=l.createElement(k);n.src=h;var e=y[w];return!e&&""!==e||"undefined"==n[w]||(n[w]=e),n.onload=i,n.onerror=a,n.onreadystatechange=function(e,t){"loaded"!==n.readyState&&"complete"!==n.readyState||i(0,t)},n}();y.ld<0?l.getElementsByTagName("head")[0].appendChild(e):setTimeout(function(){l.getElementsByTagName(k)[0].parentNode.appendChild(e)},y.ld||0)}try{m.cookie=l.cookie}catch(p){}function t(e){for(;e.length;)!function(t){m[t]=function(){var e=arguments;g||m.queue.push(function(){m[t].apply(m,e)})}}(e.pop())}var n="track",r="TrackPage",o="TrackEvent";t([n+"Event",n+"PageView",n+"Exception",n+"Trace",n+"DependencyData",n+"Metric",n+"PageViewPerformance","start"+r,"stop"+r,"start"+o,"stop"+o,"addTelemetryInitializer","setAuthenticatedUserContext","clearAuthenticatedUserContext","flush"]),m.SeverityLevel={Verbose:0,Information:1,Warning:2,Error:3,Critical:4};var s=(d.extensionConfig||{}).ApplicationInsightsAnalytics||{};if(!0!==d[I]&&!0!==s[I]){var c="onerror";t(["_"+c]);var u=T[c];T[c]=function(e,t,n,a,i){var r=u&&u(e,t,n,a,i);return!0!==r&&m["_"+c]({message:e,url:t,lineNumber:n,columnNumber:a,error:i}),r},d.autoExceptionInstrumented=!0}return m}(y.cfg);function a(){y.onInit&&y.onInit(n)}(T[t]=n).queue&&0===n.queue.length?(n.queue.push(a),n.trackPageView({})):a()}(window,document,{
src: "https://js.monitor.azure.com/scripts/b/ai.2.min.js", // The SDK URL Source for the CDN
//src: "http://localhost:9001/AISKU/browser/es5/ai.2.min.js", // The URL Source for the local build
<!-- src: "http://localhost:9001/AISKU/browser/es5/ai.3.3.1.js", // The URL Source for the local build -->
crossOrigin: "anonymous", // When supplied this will add the provided value as the cross origin attribute on the script tag
cfg: { // Application Insights Configuration
instrumentationKey: "INSTRUMENTATION_KEY",
connectionString: "InstrumentationKey=YOUR_INSTRUMENTATION_KEY",
expCfg: {
inclScripts: true,
expLog : () => {
return {logs: ["log info 1", "log info 2"]};
},
maxLogs: 1
}
//disableIKeyValidation: true
}});
// getExceptionScriptsInfo: true
}
});
</script>

</head>
<body>
<h1>Hello World!</h1>
<!-- trackException manual tests
trackException manual tests
<button onclick="javascript: throw 'String Error'">Throw String Exception</button>
<button onclick="javascript: appInsights.trackException({ exception: 'String Error' })">trackException with String as the exception</button>
<button onclick="javascript: try { throw 'String Error'; } catch(e) { appInsights.trackException({exception: e})}">trackException with try / catch</button>

<button onclick="javascript: appInsights.trackException('String Error')">trackException with String</button>
<button onclick="javascript: try { throw 'String Error'; } catch(e) { appInsights.trackException(null)}">trackException with null</button>
-->

</body>
</html>
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
[![GitHub Workflow Status (main)](https://img.shields.io/github/actions/workflow/status/microsoft/ApplicationInsights-JS/ci.yml?branch=main)](https://github.com/microsoft/ApplicationInsights-JS/tree/main)
[![Build Status](https://dev.azure.com/mseng/AppInsights/_apis/build/status%2FAppInsights%20-%20DevTools%2F1DS%20JavaScript%20SDK%20web%20SKU%20(main%3B%20master)?branchName=main)](https://dev.azure.com/mseng/AppInsights/_build/latest?definitionId=8184&branchName=main)
[![npm version](https://badge.fury.io/js/%40microsoft%2Fapplicationinsights-web.svg)](https://badge.fury.io/js/%40microsoft%2Fapplicationinsights-web)
[![minified size size](https://js.monitor.azure.com/scripts/b/3.min.js.svg)](https://js.monitor.azure.com/scripts/b/ai.3.min.js)
[![minified size size](https://js.monitor.azure.com/scripts/b/ai.3.min.js.svg)](https://js.monitor.azure.com/scripts/b/ai.3.min.js)
[![gzip size](https://js.monitor.azure.com/scripts/b/ai.3.min.js.gzip.svg)](https://js.monitor.azure.com/scripts/b/ai.3.min.js)

# Menu
Expand Down Expand Up @@ -633,7 +633,7 @@ While the script downloads from the CDN, all tracking of your page is queued. On
> Summary:
>
> - ![current npm version](https://badge.fury.io/js/%40microsoft%2Fapplicationinsights-web.svg)
> - ![gzip compressed size](https://js.monitor.azure.com/scripts/b/ai.3.gbl.min.js.gzip.svg)
> - ![gzip compressed size](https://js.monitor.azure.com/scripts/b/ai.3.min.js.gzip.svg)
> - **~15 ms** overall initialization time
> - **Zero** tracking missed during life cycle of page

Expand Down
13 changes: 12 additions & 1 deletion docs/exceptionTelemetry.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,20 @@ const appInsights = new ApplicationInsights({
config: {
connectionString: 'InstrumentationKey=YOUR_INSTRUMENTATION_KEY_GOES_HERE',
expCfg: {
inclScripts: true
inclScripts: true,
expLog : () => {
return {logs: ["log info 1", "log info 2"]};
},
maxLogs : 100
}
}
});
appInsights.trackException({error: new Error(), severityLevel: SeverityLevel.Critical});
```

### Where could I find those extra information?
The extra info is added as properties into exception Telemetry.
For script information, it would be stored under properties[exceptionScripts].
For log information, it would be stored under properties[exceptionLog].
You could find the info as shown in the pic:
![alt text](./img/exceptionTelemetry.png)
Binary file added docs/img/exceptionTelemetry.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -474,8 +474,66 @@ export class AnalyticsPluginTests extends AITestClass {

}
});



this.testCase({
name: "AppInsightsTests: trackException would contain log info when config turns on",
useFakeTimers: true,
test: () => {
const appInsights = new AnalyticsPlugin();
const core = new AppInsightsCore();
const channel = new ChannelPlugin();
const properties = new PropertiesPlugin();
// Configuration
const config = {
instrumentationKey: 'ikey',
};
this.onDone(() => {
core.unload(false);
});
// Initialize Application Insights core with plugins
core.initialize(config, [appInsights, channel, properties]);

const trackStub = this.sandbox.stub(appInsights.core, "track");
appInsights.trackException({error: new Error(), severityLevel: SeverityLevel.Critical});
Assert.ok(trackStub.calledOnce, "single exception is tracked");
const baseData = (trackStub.args[0][0] as ITelemetryItem).baseData as IExceptionInternal;
const prop = baseData.properties;
Assert.equal(-1, JSON.stringify(prop).indexOf("test message"), "log info is not included");

let applelist = new Array(49).fill("apple");
// check maxLength default value
appInsights.config.expCfg.expLog = () => {
return {logs: applelist.concat(['pear', 'banana'])};
};;
this.clock.tick(1);
appInsights.trackException({error: new Error(), severityLevel: SeverityLevel.Critical});
Assert.ok(trackStub.calledTwice, "second exception is tracked");
const baseData2 = (trackStub.args[1][0] as ITelemetryItem).baseData as IExceptionInternal;
const prop2 = baseData2.properties;
Assert.deepEqual(true, prop2["exceptionLog"].includes('apple'), "log info before max length is included");
Assert.deepEqual(true, prop2["exceptionLog"].includes('pear'), "log info before max length is included");

Assert.equal(-1, prop2["exceptionLog"].indexOf("banana"), "text after max length should not be included");


// check maxLength would truncate the log info
let myLogFunction = () => {
return {logs: ['test message', 'check message', 'banana']};
};
appInsights.config.expCfg.expLog = myLogFunction;
appInsights.config.expCfg.maxLogs = 2;
this.clock.tick(1);
appInsights.trackException({error: new Error(), severityLevel: SeverityLevel.Critical});
this.clock.tick(1);
Assert.ok(trackStub.calledThrice, "third exception is tracked");
const baseData3 = (trackStub.args[2][0] as ITelemetryItem).baseData as IExceptionInternal;
const prop3 = baseData3.properties;
Assert.deepEqual(true, prop3["exceptionLog"].includes('test'), "log info is included");
Assert.equal(false, prop3["exceptionLog"].includes("banana"), "text after max length should not be included");

}
});
}

private addGenericTests(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,23 @@
import dynamicProto from "@microsoft/dynamicproto-js";
import {
AnalyticsPluginIdentifier, Event as EventTelemetry, Exception, IAppInsights, IAutoExceptionTelemetry, IConfig, IDependencyTelemetry,
IEventTelemetry, IExceptionConfig, IExceptionInternal, IExceptionTelemetry, IMetricTelemetry, IPageViewPerformanceTelemetry,
IEventTelemetry, IExceptionInternal, IExceptionTelemetry, IMetricTelemetry, IPageViewPerformanceTelemetry,
IPageViewPerformanceTelemetryInternal, IPageViewTelemetry, IPageViewTelemetryInternal, ITraceTelemetry, Metric, PageView,
PageViewPerformance, PropertiesPluginIdentifier, RemoteDependencyData, Trace, createDistributedTraceContextFromTrace, createDomEvent,
createTelemetryItem, dataSanitizeString, eSeverityLevel, isCrossOriginError, strNotSpecified, utlDisableStorage, utlEnableStorage,
utlSetStoragePrefix
} from "@microsoft/applicationinsights-common";
import {
BaseTelemetryPlugin, IAppInsightsCore, IConfigDefaults, IConfiguration, ICookieMgr, ICustomProperties, IDistributedTraceContext,
IInstrumentCallDetails, IPlugin, IProcessTelemetryContext, IProcessTelemetryUnloadContext, ITelemetryInitializerHandler, ITelemetryItem,
ITelemetryPluginChain, ITelemetryUnloadState, InstrumentEvent, TelemetryInitializerFunction, _eInternalMessageId, arrForEach,
cfgDfBoolean, cfgDfMerge, cfgDfSet, cfgDfString, cfgDfValidate, createProcessTelemetryContext, createUniqueNamespace, dumpObj,
eLoggingSeverity, eventOff, eventOn, findAllScripts, generateW3CId, getDocument, getExceptionName, getHistory, getLocation, getWindow,
hasHistory, hasWindow, isFunction, isNullOrUndefined, isString, isUndefined, mergeEvtNamespace, onConfigChange, safeGetCookieMgr,
strUndefined, throwError
IExceptionConfig, IInstrumentCallDetails, IPlugin, IProcessTelemetryContext, IProcessTelemetryUnloadContext,
ITelemetryInitializerHandler, ITelemetryItem, ITelemetryPluginChain, ITelemetryUnloadState, InstrumentEvent,
TelemetryInitializerFunction, _eInternalMessageId, arrForEach, cfgDfBoolean, cfgDfMerge, cfgDfSet, cfgDfString, cfgDfValidate,
createProcessTelemetryContext, createUniqueNamespace, dumpObj, eLoggingSeverity, eventOff, eventOn, findAllScripts, generateW3CId,
getDocument, getExceptionName, getHistory, getLocation, getWindow, hasHistory, hasWindow, isFunction, isNullOrUndefined, isString,
isUndefined, mergeEvtNamespace, onConfigChange, safeGetCookieMgr, strUndefined, throwError
} from "@microsoft/applicationinsights-core-js";
import { PropertiesPlugin } from "@microsoft/applicationinsights-properties-js";
import { isError, objDeepFreeze, objDefine, scheduleTimeout, strIndexOf } from "@nevware21/ts-utils";
import { isArray, isError, objDeepFreeze, objDefine, scheduleTimeout, strIndexOf } from "@nevware21/ts-utils";
import { IAppInsightsInternal, PageViewManager } from "./Telemetry/PageViewManager";
import { PageViewPerformanceManager } from "./Telemetry/PageViewPerformanceManager";
import { PageVisitTimeManager } from "./Telemetry/PageVisitTimeManager";
Expand Down Expand Up @@ -52,7 +52,7 @@ function _getReason(error: any) {

const MinMilliSeconds = 60000;

const defaultValues: IConfigDefaults<IConfig> = objDeepFreeze({
const defaultValues: IConfigDefaults<IConfig&IConfiguration> = objDeepFreeze({
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should the default maxLogs be 50?

sessionRenewalMs: cfgDfSet(_chkConfigMilliseconds, 30 * 60 * 1000),
sessionExpirationMs: cfgDfSet(_chkConfigMilliseconds, 24 * 60 * 60 * 1000),
disableExceptionTracking: cfgDfBoolean(),
Expand All @@ -68,7 +68,7 @@ const defaultValues: IConfigDefaults<IConfig> = objDeepFreeze({
enableDebug: cfgDfBoolean(),
disableFlushOnBeforeUnload: cfgDfBoolean(),
disableFlushOnUnload: cfgDfBoolean(false, "disableFlushOnBeforeUnload"),
expCfg: cfgDfMerge<IExceptionConfig>({inclScripts: false})
expCfg: cfgDfMerge<IExceptionConfig>({inclScripts: false, expLog: undefined, maxLogs: 50})
});

function _chkConfigMilliseconds(value: number, defValue: number): number {
Expand Down Expand Up @@ -101,7 +101,7 @@ export class AnalyticsPlugin extends BaseTelemetryPlugin implements IAppInsights

public identifier: string = AnalyticsPluginIdentifier; // do not change name or priority
public priority: number = 180; // take from reserved priority range 100- 200
public readonly config: IConfig;
public readonly config: IConfig & IConfiguration;
public queue: Array<() => void>;
public autoRoutePVDelay = 500; // ms; Time to wait after a route change before triggering a pageview to allow DOM changes to take place

Expand All @@ -121,8 +121,9 @@ export class AnalyticsPlugin extends BaseTelemetryPlugin implements IAppInsights
let _autoExceptionInstrumented: boolean;
let _enableUnhandledPromiseRejectionTracking: boolean;
let _autoUnhandledPromiseInstrumented: boolean;
let _extConfig: IConfig;
let _extConfig: IConfig & IConfiguration;
let _autoTrackPageVisitTime: boolean;
let _expCfg: IExceptionConfig;

// Counts number of trackAjax invocations.
// By default we only monitor X ajax call per view to avoid too much load.
Expand Down Expand Up @@ -425,10 +426,16 @@ export class AnalyticsPlugin extends BaseTelemetryPlugin implements IAppInsights
exception.id
).toInterface();
var doc = getDocument();
if (doc && _self.config.expCfg?.inclScripts) {
if (doc && _expCfg?.inclScripts) {
var scriptsInfo = findAllScripts(doc);
exceptionPartB.properties["exceptionScripts"] = JSON.stringify(scriptsInfo);
}
if (_expCfg?.expLog) {
let logs = _expCfg.expLog();
if (logs && logs.logs && isArray(logs.logs)) {
exceptionPartB.properties["exceptionLog"] = logs.logs.slice(0, _expCfg.maxLogs).join("\n");
}
}
let telemetryItem: ITelemetryItem = createTelemetryItem<IExceptionInternal>(
exceptionPartB,
Exception.dataType,
Expand Down Expand Up @@ -625,6 +632,7 @@ export class AnalyticsPlugin extends BaseTelemetryPlugin implements IAppInsights
let ctx = createProcessTelemetryContext(null, config, core);
_extConfig = ctx.getExtCfg(identifier, defaultValues);

_expCfg = _extConfig.expCfg;
_autoTrackPageVisitTime = _extConfig.autoTrackPageVisitTime;

if (config.storagePrefix){
Expand Down
5 changes: 0 additions & 5 deletions shared/AppInsightsCommon/src/Interfaces/IConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Licensed under the MIT License.
import { IConfiguration, ICustomProperties, isNullOrUndefined } from "@microsoft/applicationinsights-core-js";
import { DistributedTracingModes } from "../Enums";
import { IExceptionConfig } from "./IExceptionTelemetry";
import { IRequestContext } from "./IRequestContext";
import { IStorageBuffer } from "./IStorageBuffer";
import { IThrottleMgrConfig } from "./IThrottleMgr";
Expand Down Expand Up @@ -391,10 +390,6 @@ export interface IConfig {
*/
userOverrideEndpointUrl?: string;

/**
* [Optional] Set additional configuration for exceptions, such as more scripts to include in the exception telemetry.
*/
expCfg?: IExceptionConfig;
}

export class ConfigurationManager {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,6 @@
import { SeverityLevel } from "./Contracts/SeverityLevel";
import { IPartC } from "./IPartC";

export interface IExceptionConfig{
/**
* If set to true, when exception is sent out, the SDK will also send out all scripts basic info that are loaded on the page.
* Notice: This would increase the size of the exception telemetry.
*/
inclScripts?: boolean;
}

/**
* @export
* @interface IExceptionTelemetry
Expand Down
Loading