diff --git a/AISKU/Tests/Unit/src/AISKUSize.Tests.ts b/AISKU/Tests/Unit/src/AISKUSize.Tests.ts index fd11db1a..cb7c26d7 100644 --- a/AISKU/Tests/Unit/src/AISKUSize.Tests.ts +++ b/AISKU/Tests/Unit/src/AISKUSize.Tests.ts @@ -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"; diff --git a/AISKU/samples/HelloWorld/index-snippet.html b/AISKU/samples/HelloWorld/index-snippet.html index 54726080..66be828a 100644 --- a/AISKU/samples/HelloWorld/index-snippet.html +++ b/AISKU/samples/HelloWorld/index-snippet.html @@ -6,24 +6,33 @@

Hello World!

- + \ No newline at end of file diff --git a/README.md b/README.md index 3cca36bd..d55837ed 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 diff --git a/docs/exceptionTelemetry.md b/docs/exceptionTelemetry.md index af7cb47a..e045c405 100644 --- a/docs/exceptionTelemetry.md +++ b/docs/exceptionTelemetry.md @@ -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) \ No newline at end of file diff --git a/docs/img/exceptionTelemetry.png b/docs/img/exceptionTelemetry.png new file mode 100644 index 00000000..461b72c4 Binary files /dev/null and b/docs/img/exceptionTelemetry.png differ diff --git a/extensions/applicationinsights-analytics-js/Tests/Unit/src/AnalyticsPlugin.tests.ts b/extensions/applicationinsights-analytics-js/Tests/Unit/src/AnalyticsPlugin.tests.ts index 7c457d85..2fd3473c 100644 --- a/extensions/applicationinsights-analytics-js/Tests/Unit/src/AnalyticsPlugin.tests.ts +++ b/extensions/applicationinsights-analytics-js/Tests/Unit/src/AnalyticsPlugin.tests.ts @@ -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 { diff --git a/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/AnalyticsPlugin.ts b/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/AnalyticsPlugin.ts index 2530e01b..652a6db9 100644 --- a/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/AnalyticsPlugin.ts +++ b/extensions/applicationinsights-analytics-js/src/JavaScriptSDK/AnalyticsPlugin.ts @@ -6,7 +6,7 @@ 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, @@ -14,15 +14,15 @@ import { } 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"; @@ -52,7 +52,7 @@ function _getReason(error: any) { const MinMilliSeconds = 60000; -const defaultValues: IConfigDefaults = objDeepFreeze({ +const defaultValues: IConfigDefaults = objDeepFreeze({ sessionRenewalMs: cfgDfSet(_chkConfigMilliseconds, 30 * 60 * 1000), sessionExpirationMs: cfgDfSet(_chkConfigMilliseconds, 24 * 60 * 60 * 1000), disableExceptionTracking: cfgDfBoolean(), @@ -68,7 +68,7 @@ const defaultValues: IConfigDefaults = objDeepFreeze({ enableDebug: cfgDfBoolean(), disableFlushOnBeforeUnload: cfgDfBoolean(), disableFlushOnUnload: cfgDfBoolean(false, "disableFlushOnBeforeUnload"), - expCfg: cfgDfMerge({inclScripts: false}) + expCfg: cfgDfMerge({inclScripts: false, expLog: undefined, maxLogs: 50}) }); function _chkConfigMilliseconds(value: number, defValue: number): number { @@ -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 @@ -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. @@ -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( exceptionPartB, Exception.dataType, @@ -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){ diff --git a/shared/AppInsightsCommon/src/Interfaces/IConfig.ts b/shared/AppInsightsCommon/src/Interfaces/IConfig.ts index 7c63fe95..f4f292b7 100644 --- a/shared/AppInsightsCommon/src/Interfaces/IConfig.ts +++ b/shared/AppInsightsCommon/src/Interfaces/IConfig.ts @@ -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"; @@ -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 { diff --git a/shared/AppInsightsCommon/src/Interfaces/IExceptionTelemetry.ts b/shared/AppInsightsCommon/src/Interfaces/IExceptionTelemetry.ts index 5207e515..6f0be410 100644 --- a/shared/AppInsightsCommon/src/Interfaces/IExceptionTelemetry.ts +++ b/shared/AppInsightsCommon/src/Interfaces/IExceptionTelemetry.ts @@ -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 diff --git a/shared/AppInsightsCommon/src/applicationinsights-common.ts b/shared/AppInsightsCommon/src/applicationinsights-common.ts index dcc988c0..958c027c 100644 --- a/shared/AppInsightsCommon/src/applicationinsights-common.ts +++ b/shared/AppInsightsCommon/src/applicationinsights-common.ts @@ -26,7 +26,7 @@ export { IEventTelemetry } from "./Interfaces/IEventTelemetry"; export { ITraceTelemetry } from "./Interfaces/ITraceTelemetry"; export { IMetricTelemetry } from "./Interfaces/IMetricTelemetry"; export { IDependencyTelemetry } from "./Interfaces/IDependencyTelemetry"; -export { IExceptionTelemetry, IAutoExceptionTelemetry, IExceptionInternal, IExceptionConfig } from "./Interfaces/IExceptionTelemetry"; +export { IExceptionTelemetry, IAutoExceptionTelemetry, IExceptionInternal } from "./Interfaces/IExceptionTelemetry"; export { IPageViewTelemetry, IPageViewTelemetryInternal } from "./Interfaces/IPageViewTelemetry"; export { IPageViewPerformanceTelemetry, IPageViewPerformanceTelemetryInternal } from "./Interfaces/IPageViewPerformanceTelemetry"; export { Trace } from "./Telemetry/Trace"; diff --git a/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IConfiguration.ts b/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IConfiguration.ts index e6eb408a..ac30e3bc 100644 --- a/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IConfiguration.ts +++ b/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IConfiguration.ts @@ -4,6 +4,7 @@ import { IPromise } from "@nevware21/ts-async"; import { IAppInsightsCore } from "./IAppInsightsCore"; import { IChannelControls } from "./IChannelControls"; import { ICookieMgrConfig } from "./ICookieMgr"; +import { IExceptionConfig } from "./IExceptionConfig"; import { IFeatureOptIn } from "./IFeatureOptIn"; import { INotificationManager } from "./INotificationManager"; import { IPerfManager } from "./IPerfManager"; @@ -208,4 +209,9 @@ export interface IConfiguration { */ initInMemoMaxSize?: number; + /** + * [Optional] Set additional configuration for exceptions, such as more scripts to include in the exception telemetry. + * @since 3.3.2 + */ + expCfg?: IExceptionConfig; } diff --git a/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IExceptionConfig.ts b/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IExceptionConfig.ts new file mode 100644 index 00000000..54dd9514 --- /dev/null +++ b/shared/AppInsightsCore/src/JavaScriptSDK.Interfaces/IExceptionConfig.ts @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +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; + + /** + * Callback function for collecting logs to be included in telemetry data. + * + * The length of logs to generate is controlled by the `maxLogs` parameter. + * + * This callback is called before telemetry data is sent, allowing for dynamic customization of the logs. + * + * @returns {Object} An object with the following property: + * - logs: An array of strings, where each string represents a log entry to be included in the telemetry. + * + * @property {number} maxLogs - Specifies the maximum number of logs that can be generated. If not explicitly set, it defaults to 50. + */ + expLog: () => { logs: string[] }, + maxLogs: number +} diff --git a/shared/AppInsightsCore/src/applicationinsights-core-js.ts b/shared/AppInsightsCore/src/applicationinsights-core-js.ts index 3a8551ab..78cc7151 100644 --- a/shared/AppInsightsCore/src/applicationinsights-core-js.ts +++ b/shared/AppInsightsCore/src/applicationinsights-core-js.ts @@ -4,6 +4,7 @@ export { IConfiguration } from "./JavaScriptSDK.Interfaces/IConfiguration"; export { IChannelControls, MinChannelPriorty, IInternalOfflineSupport } from "./JavaScriptSDK.Interfaces/IChannelControls"; export { IChannelControlsHost } from "./JavaScriptSDK.Interfaces/IChannelControlsHost"; export { ITelemetryPlugin, IPlugin } from "./JavaScriptSDK.Interfaces/ITelemetryPlugin"; +export { IExceptionConfig } from "./JavaScriptSDK.Interfaces/IExceptionConfig"; export { IAppInsightsCore, ILoadedPlugin } from "./JavaScriptSDK.Interfaces/IAppInsightsCore"; export { ITelemetryItem, ICustomProperties, Tags } from "./JavaScriptSDK.Interfaces/ITelemetryItem"; export { IBaseProcessingContext, IProcessTelemetryContext, IProcessTelemetryUnloadContext, IProcessTelemetryUpdateContext } from "./JavaScriptSDK.Interfaces/IProcessTelemetryContext";