1
1
// Copyright (c) Microsoft Corporation. All rights reserved.
2
2
// 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
+
5
4
import type { JSONObject } from '@phosphor/coreutils' ;
6
- import { basename as pathBasename , sep as pathSep } from 'path' ;
7
5
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' ;
9
8
10
9
import { DiagnosticCodes } from '../application/diagnostics/constants' ;
11
10
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' ;
13
12
import { traceError , traceInfo } from '../common/logger' ;
14
13
import { TerminalShellType } from '../common/terminal/types' ;
15
14
import { StopWatch } from '../common/utils/stopWatch' ;
@@ -28,10 +27,12 @@ import { TestProvider } from '../testing/common/types';
28
27
import { EventName , PlatformErrors } from './constants' ;
29
28
import { LinterTrigger , TestTool } from './types' ;
30
29
30
+ // tslint:disable: no-any
31
+
31
32
/**
32
33
* Checks whether telemetry is supported.
33
34
* 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.
35
36
* @returns {boolean }
36
37
*/
37
38
function isTelemetrySupported ( ) : boolean {
@@ -63,14 +64,12 @@ function getTelemetryReporter() {
63
64
const extensionId = PVSC_EXTENSION_ID ;
64
65
// tslint:disable-next-line:no-require-imports
65
66
const extensions = ( require ( 'vscode' ) as typeof import ( 'vscode' ) ) . extensions ;
66
- // tslint:disable-next-line:no-non-null-assertion
67
67
const extension = extensions . getExtension ( extensionId ) ! ;
68
- // tslint:disable-next-line:no-unsafe-any
69
68
const extensionVersion = extension . packageJSON . version ;
70
69
71
70
// tslint:disable-next-line:no-require-imports
72
71
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 ) ) ;
74
73
}
75
74
76
75
export function clearTelemetryReporter ( ) {
@@ -88,45 +87,46 @@ export function sendTelemetryEvent<P extends IEventNamePropertyMapping, E extend
88
87
}
89
88
const reporter = getTelemetryReporter ( ) ;
90
89
const measures = typeof durationMs === 'number' ? { duration : durationMs } : durationMs ? durationMs : undefined ;
90
+ let customProperties : Record < string , string > = { } ;
91
+ let eventNameSent = eventName as string ;
91
92
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.
94
95
// Else we have to review all properties every time as part of GDPR.
95
96
// Assume we have 10 events all with their own properties.
96
97
// As we have errors for each event, those properties are treated as new data items.
97
98
// 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 ) ;
125
125
}
126
- reporter . sendTelemetryEvent ( ( eventName as any ) as string , customProperties , measures ) ;
126
+
127
127
if ( process . env && process . env . VSC_PYTHON_LOG_TELEMETRY ) {
128
128
traceInfo (
129
- `Telemetry Event : ${ eventName } Measures: ${ JSON . stringify ( measures ) } Props: ${ JSON . stringify (
129
+ `Telemetry Event : ${ eventNameSent } Measures: ${ JSON . stringify ( measures ) } Props: ${ JSON . stringify (
130
130
customProperties
131
131
) } `
132
132
) ;
@@ -246,32 +246,12 @@ export function sendTelemetryWhenDone<P extends IEventNamePropertyMapping, E ext
246
246
}
247
247
}
248
248
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.
270
251
let trace = '' ;
271
252
for ( const frame of stackTrace . parse ( ex ) ) {
272
- let filename = frame . getFileName ( ) ;
253
+ const filename = frame . getFileName ( ) ;
273
254
if ( filename ) {
274
- filename = sanitizeFilename ( filename ) ;
275
255
const lineno = frame . getLineNumber ( ) ;
276
256
const colno = frame . getColumnNumber ( ) ;
277
257
trace += `\n\tat ${ getCallsite ( frame ) } ${ filename } :${ lineno } :${ colno } ` ;
@@ -297,7 +277,7 @@ function getCallsite(frame: stackTrace.StackFrame) {
297
277
parts . push ( frame . getFunctionName ( ) ) ;
298
278
}
299
279
}
300
- return parts . map ( sanitizeName ) . join ( '.' ) ;
280
+ return parts . join ( '.' ) ;
301
281
}
302
282
303
283
// Map all events to their properties
0 commit comments