From 81b0bab6be05fb6ba0debf1cdf2f9534c0fa01fd Mon Sep 17 00:00:00 2001 From: Millicent Achieng Date: Mon, 24 May 2021 11:45:11 +0300 Subject: [PATCH] Enable autocollection of unhandled exceptions (#963) --- src/telemetry/error-types.ts | 1 + src/telemetry/filters.ts | 67 ++++++++++++++++++++++++++---------- src/telemetry/telemetry.ts | 4 ++- 3 files changed, 53 insertions(+), 19 deletions(-) diff --git a/src/telemetry/error-types.ts b/src/telemetry/error-types.ts index 561083a09..c66a64b27 100644 --- a/src/telemetry/error-types.ts +++ b/src/telemetry/error-types.ts @@ -4,3 +4,4 @@ export const NETWORK_ERROR = 'NETWORK_ERROR'; export const LINK_ERROR = 'LINK_ERROR'; export const COMPONENT_ERROR = 'COMPONENT_ERROR'; export const OPERATIONAL_ERROR = 'OPERATIONAL_ERROR'; +export const UNHANDLED_ERROR = 'UNHANDLED_ERROR'; diff --git a/src/telemetry/filters.ts b/src/telemetry/filters.ts index c1ce84db5..7f13d9335 100644 --- a/src/telemetry/filters.ts +++ b/src/telemetry/filters.ts @@ -1,4 +1,6 @@ import { ITelemetryItem } from '@microsoft/applicationinsights-web'; +import { exception } from 'console'; +import { errorTypes } from '.'; import { DEVX_API_URL, GRAPH_API_SANDBOX_URL, @@ -23,26 +25,25 @@ export function filterTelemetryTypes(envelope: ITelemetryItem) { } export function filterRemoteDependencyData(envelope: ITelemetryItem): boolean { - const baseData = envelope.baseData || {}; - if (envelope.baseType !== 'RemoteDependencyData') { - return true; - } + if (envelope.baseType === 'RemoteDependencyData') { + const baseData = envelope.baseData || {}; + const urlObject = new URL(baseData.target || ''); - const targetsToInclude = [GRAPH_URL, DEVX_API_URL, GRAPH_API_SANDBOX_URL]; - const urlObject = new URL(baseData.target || ''); - if (!targetsToInclude.includes(urlObject.origin)) { - return false; - } + const targetsToInclude = [GRAPH_URL, DEVX_API_URL, GRAPH_API_SANDBOX_URL]; + if (!targetsToInclude.includes(urlObject.origin)) { + return false; + } - const target = baseData.target || ''; - switch (urlObject.origin) { - case GRAPH_URL: - baseData.name = sanitizeQueryUrl(target); - break; - case GRAPH_API_SANDBOX_URL: - baseData.name = sanitizeGraphAPISandboxUrl(target); - default: - break; + const target = baseData.target || ''; + switch (urlObject.origin) { + case GRAPH_URL: + baseData.name = sanitizeQueryUrl(target); + break; + case GRAPH_API_SANDBOX_URL: + baseData.name = sanitizeGraphAPISandboxUrl(target); + default: + break; + } } return true; } @@ -73,3 +74,33 @@ export function sanitizeTelemetryItemUriProperty(envelope: ITelemetryItem) { } return true; } + +export function sanitizeStackTrace(envelope: ITelemetryItem) { + if (envelope.baseType === 'ExceptionData') { + const telemetryItem = envelope.baseData || {}; + telemetryItem.properties = telemetryItem.properties || {}; + + if (telemetryItem.exceptions && telemetryItem.exceptions.length > 0) { + const exception = telemetryItem.exceptions[0]; + const parsedStack = exception.parsedStack[0]; + + // Only capture errors coming from our source code and not dependencies, to reduce noise + if (!parsedStack.fileName.startsWith('webpack-internal')) { + return false; + } + + // Add properties for unhandled exceptions only + if (!telemetryItem.properties.ComponentName) { + telemetryItem.properties.ComponentName = parsedStack.assembly; + telemetryItem.properties.Message = exception.stack.split('\n')[0]; // Read first line only + exception.message = errorTypes.UNHANDLED_ERROR; + } + + exception.hasFullStack = false; + exception.stack = null; + exception.parsedStack = [parsedStack]; + telemetryItem.exceptions = [exception]; + } + } + return true; +} diff --git a/src/telemetry/telemetry.ts b/src/telemetry/telemetry.ts index 9b978c1a7..20f3f12a6 100644 --- a/src/telemetry/telemetry.ts +++ b/src/telemetry/telemetry.ts @@ -18,6 +18,7 @@ import { addCommonTelemetryItemProperties, filterRemoteDependencyData, filterTelemetryTypes, + sanitizeStackTrace, sanitizeTelemetryItemUriProperty, } from './filters'; import ITelemetry from './ITelemetry'; @@ -32,7 +33,7 @@ class Telemetry implements ITelemetry { this.config = { instrumentationKey: this.getInstrumentationKey(), - disableExceptionTracking: true, + disableExceptionTracking: false, // Enables autocollection of uncaught exceptions. Used with `sanitizeStackTrace` telemetry initializer to remove any data that might be PII. disableAjaxTracking: true, disableFetchTracking: false, // Enables capturing of telemetry data for outgoing requests. Used with `filterRemoteDependencyData` telemetry initializer to sanitize captured data to prevent inadvertent capture of PII. disableTelemetry: this.getInstrumentationKey() ? false : true, @@ -49,6 +50,7 @@ class Telemetry implements ITelemetry { this.appInsights.trackPageView(); this.appInsights.addTelemetryInitializer(filterTelemetryTypes); this.appInsights.addTelemetryInitializer(filterRemoteDependencyData); + this.appInsights.addTelemetryInitializer(sanitizeStackTrace); this.appInsights.addTelemetryInitializer(sanitizeTelemetryItemUriProperty); this.appInsights.addTelemetryInitializer(addCommonTelemetryItemProperties); this.appInsights.context.application.ver = version;