Skip to content

Commit d5065e6

Browse files
authored
Use vscode-extension-telemetry for our exceptions & error telemetry (#11524)
* Add pipenv discovery telemetry (update when getInterpreters signature change gets merged) * Update package.json * Use firstParty optional arg * News file * Sanitize paths using telemetry package * Remove outdated warnings, typos * More descriptive test names * Only send one event, fix unit tests * Don't make unnecessary changes * Forgot some * Fix build warnings
1 parent 87c4584 commit d5065e6

File tree

7 files changed

+157
-226
lines changed

7 files changed

+157
-226
lines changed

gulpfile.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,9 @@ function getAllowedWarningsForWebPack(buildConfig) {
287287
'WARNING in ./node_modules/@jupyterlab/services/node_modules/ws/lib/validation.js',
288288
'WARNING in ./node_modules/any-promise/register.js',
289289
'WARNING in ./node_modules/log4js/lib/appenders/index.js',
290-
'WARNING in ./node_modules/log4js/lib/clustering.js'
290+
'WARNING in ./node_modules/log4js/lib/clustering.js',
291+
'WARNING in ./node_modules/diagnostic-channel-publishers/dist/src/azure-coretracing.pub.js',
292+
'WARNING in ./node_modules/applicationinsights/out/AutoCollection/NativePerformance.js'
291293
];
292294
case 'extension':
293295
return [
@@ -300,10 +302,16 @@ function getAllowedWarningsForWebPack(buildConfig) {
300302
301303
'WARNING in ./node_modules/@jupyterlab/services/node_modules/ws/lib/buffer-util.js',
302304
'WARNING in ./node_modules/@jupyterlab/services/node_modules/ws/lib/validation.js',
303-
'WARNING in ./node_modules/@jupyterlab/services/node_modules/ws/lib/Validation.js'
305+
'WARNING in ./node_modules/@jupyterlab/services/node_modules/ws/lib/Validation.js',
306+
'WARNING in ./node_modules/diagnostic-channel-publishers/dist/src/azure-coretracing.pub.js',
307+
'WARNING in ./node_modules/applicationinsights/out/AutoCollection/NativePerformance.js'
304308
];
305309
case 'debugAdapter':
306-
return ['WARNING in ./node_modules/vscode-uri/lib/index.js'];
310+
return [
311+
'WARNING in ./node_modules/vscode-uri/lib/index.js',
312+
'WARNING in ./node_modules/diagnostic-channel-publishers/dist/src/azure-coretracing.pub.js',
313+
'WARNING in ./node_modules/applicationinsights/out/AutoCollection/NativePerformance.js'
314+
];
307315
default:
308316
throw new Error('Unknown WebPack Configuration');
309317
}

news/3 Code Health/11436.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Update telemetry on errors and exceptions to use [vscode-extension-telemetry](https://www.npmjs.com/package/vscode-extension-telemetry).

package-lock.json

Lines changed: 67 additions & 17 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2978,7 +2978,7 @@
29782978
"untildify": "^3.0.2",
29792979
"vscode-debugadapter": "^1.28.0",
29802980
"vscode-debugprotocol": "^1.28.0",
2981-
"vscode-extension-telemetry": "0.1.0",
2981+
"vscode-extension-telemetry": "0.1.4",
29822982
"vscode-jsonrpc": "^5.0.1",
29832983
"vscode-languageclient": "^6.2.0-next.2",
29842984
"vscode-languageserver": "^6.2.0-next.2",

src/client/telemetry/index.ts

Lines changed: 44 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
3-
// tslint:disable:no-reference no-any import-name no-any function-name
4-
/// <reference path="./vscode-extension-telemetry.d.ts" />
3+
54
import type { JSONObject } from '@phosphor/coreutils';
6-
import { basename as pathBasename, sep as pathSep } from 'path';
75
import * as stackTrace from 'stack-trace';
8-
import TelemetryReporter from 'vscode-extension-telemetry';
6+
// tslint:disable-next-line: import-name
7+
import TelemetryReporter from 'vscode-extension-telemetry/lib/telemetryReporter';
98

109
import { DiagnosticCodes } from '../application/diagnostics/constants';
1110
import { IWorkspaceService } from '../common/application/types';
12-
import { AppinsightsKey, EXTENSION_ROOT_DIR, isTestExecution, PVSC_EXTENSION_ID } from '../common/constants';
11+
import { AppinsightsKey, isTestExecution, PVSC_EXTENSION_ID } from '../common/constants';
1312
import { traceError, traceInfo } from '../common/logger';
1413
import { TerminalShellType } from '../common/terminal/types';
1514
import { StopWatch } from '../common/utils/stopWatch';
@@ -28,10 +27,12 @@ import { TestProvider } from '../testing/common/types';
2827
import { EventName, PlatformErrors } from './constants';
2928
import { LinterTrigger, TestTool } from './types';
3029

30+
// tslint:disable: no-any
31+
3132
/**
3233
* Checks whether telemetry is supported.
3334
* Its possible this function gets called within Debug Adapter, vscode isn't available in there.
34-
* Withiin DA, there's a completely different way to send telemetry.
35+
* Within DA, there's a completely different way to send telemetry.
3536
* @returns {boolean}
3637
*/
3738
function isTelemetrySupported(): boolean {
@@ -63,14 +64,12 @@ function getTelemetryReporter() {
6364
const extensionId = PVSC_EXTENSION_ID;
6465
// tslint:disable-next-line:no-require-imports
6566
const extensions = (require('vscode') as typeof import('vscode')).extensions;
66-
// tslint:disable-next-line:no-non-null-assertion
6767
const extension = extensions.getExtension(extensionId)!;
68-
// tslint:disable-next-line:no-unsafe-any
6968
const extensionVersion = extension.packageJSON.version;
7069

7170
// tslint:disable-next-line:no-require-imports
7271
const reporter = require('vscode-extension-telemetry').default as typeof TelemetryReporter;
73-
return (telemetryReporter = new reporter(extensionId, extensionVersion, AppinsightsKey));
72+
return (telemetryReporter = new reporter(extensionId, extensionVersion, AppinsightsKey, true));
7473
}
7574

7675
export function clearTelemetryReporter() {
@@ -88,45 +87,46 @@ export function sendTelemetryEvent<P extends IEventNamePropertyMapping, E extend
8887
}
8988
const reporter = getTelemetryReporter();
9089
const measures = typeof durationMs === 'number' ? { duration: durationMs } : durationMs ? durationMs : undefined;
90+
let customProperties: Record<string, string> = {};
91+
let eventNameSent = eventName as string;
9192

92-
if (ex && (eventName as any) !== 'ERROR') {
93-
// When sending `ERROR` telemetry event no need to send custom properties.
93+
if (ex) {
94+
// When sending telemetry events for exceptions no need to send custom properties.
9495
// Else we have to review all properties every time as part of GDPR.
9596
// Assume we have 10 events all with their own properties.
9697
// As we have errors for each event, those properties are treated as new data items.
9798
// Hence they need to be classified as part of the GDPR process, and thats unnecessary and onerous.
98-
const props: Record<string, string> = {};
99-
props.stackTrace = getStackTrace(ex);
100-
props.originalEventName = (eventName as any) as string;
101-
reporter.sendTelemetryEvent('ERROR', props, measures);
102-
}
103-
const customProperties: Record<string, string> = {};
104-
if (properties) {
105-
// tslint:disable-next-line:prefer-type-cast no-any
106-
const data = properties as any;
107-
Object.getOwnPropertyNames(data).forEach((prop) => {
108-
if (data[prop] === undefined || data[prop] === null) {
109-
return;
110-
}
111-
try {
112-
// If there are any errors in serializing one property, ignore that and move on.
113-
// Else nothign will be sent.
114-
// tslint:disable-next-line:prefer-type-cast no-any no-unsafe-any
115-
(customProperties as any)[prop] =
116-
typeof data[prop] === 'string'
117-
? data[prop]
118-
: typeof data[prop] === 'object'
119-
? 'object'
120-
: data[prop].toString();
121-
} catch (ex) {
122-
traceError(`Failed to serialize ${prop} for ${eventName}`, ex);
123-
}
124-
});
99+
eventNameSent = 'ERROR';
100+
customProperties = { originalEventName: eventName as string, stackTrace: serializeStackTrace(ex) };
101+
reporter.sendTelemetryErrorEvent(eventNameSent, customProperties, measures, []);
102+
} else {
103+
if (properties) {
104+
const data = properties as any;
105+
Object.getOwnPropertyNames(data).forEach((prop) => {
106+
if (data[prop] === undefined || data[prop] === null) {
107+
return;
108+
}
109+
try {
110+
// If there are any errors in serializing one property, ignore that and move on.
111+
// Else nothing will be sent.
112+
customProperties[prop] =
113+
typeof data[prop] === 'string'
114+
? data[prop]
115+
: typeof data[prop] === 'object'
116+
? 'object'
117+
: data[prop].toString();
118+
} catch (ex) {
119+
traceError(`Failed to serialize ${prop} for ${eventName}`, ex);
120+
}
121+
});
122+
}
123+
124+
reporter.sendTelemetryEvent(eventNameSent, customProperties, measures);
125125
}
126-
reporter.sendTelemetryEvent((eventName as any) as string, customProperties, measures);
126+
127127
if (process.env && process.env.VSC_PYTHON_LOG_TELEMETRY) {
128128
traceInfo(
129-
`Telemetry Event : ${eventName} Measures: ${JSON.stringify(measures)} Props: ${JSON.stringify(
129+
`Telemetry Event : ${eventNameSent} Measures: ${JSON.stringify(measures)} Props: ${JSON.stringify(
130130
customProperties
131131
)} `
132132
);
@@ -246,32 +246,12 @@ export function sendTelemetryWhenDone<P extends IEventNamePropertyMapping, E ext
246246
}
247247
}
248248

249-
function sanitizeFilename(filename: string): string {
250-
if (filename.startsWith(EXTENSION_ROOT_DIR)) {
251-
filename = `<pvsc>${filename.substring(EXTENSION_ROOT_DIR.length)}`;
252-
} else {
253-
// We don't really care about files outside our extension.
254-
filename = `<hidden>${pathSep}${pathBasename(filename)}`;
255-
}
256-
return filename;
257-
}
258-
259-
function sanitizeName(name: string): string {
260-
if (name.indexOf('/') === -1 && name.indexOf('\\') === -1) {
261-
return name;
262-
} else {
263-
return '<hidden>';
264-
}
265-
}
266-
267-
function getStackTrace(ex: Error): string {
268-
// We aren't showing the error message (ex.message) since it might
269-
// contain PII.
249+
function serializeStackTrace(ex: Error): string {
250+
// We aren't showing the error message (ex.message) since it might contain PII.
270251
let trace = '';
271252
for (const frame of stackTrace.parse(ex)) {
272-
let filename = frame.getFileName();
253+
const filename = frame.getFileName();
273254
if (filename) {
274-
filename = sanitizeFilename(filename);
275255
const lineno = frame.getLineNumber();
276256
const colno = frame.getColumnNumber();
277257
trace += `\n\tat ${getCallsite(frame)} ${filename}:${lineno}:${colno}`;
@@ -297,7 +277,7 @@ function getCallsite(frame: stackTrace.StackFrame) {
297277
parts.push(frame.getFunctionName());
298278
}
299279
}
300-
return parts.map(sanitizeName).join('.');
280+
return parts.join('.');
301281
}
302282

303283
// Map all events to their properties

0 commit comments

Comments
 (0)