mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Clean up extension telemetry (#5596)
This commit is contained in:
@@ -8,7 +8,7 @@ import * as path from 'path';
|
|||||||
import * as azdata from 'azdata';
|
import * as azdata from 'azdata';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { Telemetry } from './telemetry';
|
import { Telemetry } from './telemetry';
|
||||||
import { doubleEscapeSingleQuotes, backEscapeDoubleQuotes } from './utils';
|
import { doubleEscapeSingleQuotes, backEscapeDoubleQuotes, getTelemetryErrorType } from './utils';
|
||||||
import { ChildProcess, exec } from 'child_process';
|
import { ChildProcess, exec } from 'child_process';
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
const ssmsMinVer = JSON.parse(JSON.stringify(require('./config.json'))).version;
|
const ssmsMinVer = JSON.parse(JSON.stringify(require('./config.json'))).version;
|
||||||
@@ -66,7 +66,6 @@ export interface LaunchSsmsDialogParams {
|
|||||||
export async function activate(context: vscode.ExtensionContext): Promise<void> {
|
export async function activate(context: vscode.ExtensionContext): Promise<void> {
|
||||||
// This is for Windows-specific support so do nothing on other platforms
|
// This is for Windows-specific support so do nothing on other platforms
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
Telemetry.sendTelemetryEvent('startup/ExtensionActivated');
|
|
||||||
exePath = path.join(context.extensionPath, 'ssmsmin', 'Windows', ssmsMinVer, 'ssmsmin.exe');
|
exePath = path.join(context.extensionPath, 'ssmsmin', 'Windows', ssmsMinVer, 'ssmsmin.exe');
|
||||||
registerCommands(context);
|
registerCommands(context);
|
||||||
}
|
}
|
||||||
@@ -76,7 +75,7 @@ export async function deactivate(): Promise<void> {
|
|||||||
// If the extension is being deactivated we want to kill all processes that are still currently
|
// If the extension is being deactivated we want to kill all processes that are still currently
|
||||||
// running otherwise they will continue to run as orphan processes. We use taskkill here in case
|
// running otherwise they will continue to run as orphan processes. We use taskkill here in case
|
||||||
// they started off child processes of their own
|
// they started off child processes of their own
|
||||||
runningProcesses.forEach(p => exec('taskkill /pid ' + p.pid + ' /T /F'));
|
runningProcesses.forEach(p => exec(`taskkill /pid ${p.pid} /T /F`));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,6 +95,7 @@ function registerCommands(context: vscode.ExtensionContext): void {
|
|||||||
*/
|
*/
|
||||||
async function handleLaunchSsmsMinPropertiesDialogCommand(connectionContext?: azdata.ObjectExplorerContext): Promise<void> {
|
async function handleLaunchSsmsMinPropertiesDialogCommand(connectionContext?: azdata.ObjectExplorerContext): Promise<void> {
|
||||||
if (!connectionContext) {
|
if (!connectionContext) {
|
||||||
|
Telemetry.sendTelemetryEventForError('NoConnectionContext', { action: 'Properties' });
|
||||||
vscode.window.showErrorMessage(localize('adminToolExtWin.noConnectionContextForProp', 'No ConnectionContext provided for handleLaunchSsmsMinPropertiesDialogCommand'));
|
vscode.window.showErrorMessage(localize('adminToolExtWin.noConnectionContextForProp', 'No ConnectionContext provided for handleLaunchSsmsMinPropertiesDialogCommand'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -106,9 +106,9 @@ async function handleLaunchSsmsMinPropertiesDialogCommand(connectionContext?: az
|
|||||||
}
|
}
|
||||||
else if (connectionContext.nodeInfo) {
|
else if (connectionContext.nodeInfo) {
|
||||||
nodeType = connectionContext.nodeInfo.nodeType;
|
nodeType = connectionContext.nodeInfo.nodeType;
|
||||||
}
|
} else {
|
||||||
else {
|
Telemetry.sendTelemetryEventForError('NoOENode', { action: 'Properties' });
|
||||||
vscode.window.showErrorMessage(localize('adminToolExtWin.noOeNode', 'Could not determine NodeType for handleLaunchSsmsMinPropertiesDialogCommand with context {0}', JSON.stringify(connectionContext)));
|
vscode.window.showErrorMessage(localize('adminToolExtWin.noOENode', 'Could not determine Object Explorer node from connectionContext : {0}', JSON.stringify(connectionContext)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,12 +122,14 @@ async function handleLaunchSsmsMinPropertiesDialogCommand(connectionContext?: az
|
|||||||
* @param connectionId The connection context from the command
|
* @param connectionId The connection context from the command
|
||||||
*/
|
*/
|
||||||
async function handleLaunchSsmsMinGswDialogCommand(connectionContext?: azdata.ObjectExplorerContext): Promise<void> {
|
async function handleLaunchSsmsMinGswDialogCommand(connectionContext?: azdata.ObjectExplorerContext): Promise<void> {
|
||||||
|
const action = 'GenerateScripts';
|
||||||
if (!connectionContext) {
|
if (!connectionContext) {
|
||||||
|
Telemetry.sendTelemetryEventForError('NoConnectionContext', { action: action });
|
||||||
vscode.window.showErrorMessage(localize('adminToolExtWin.noConnectionContextForGsw', 'No ConnectionContext provided for handleLaunchSsmsMinPropertiesDialogCommand'));
|
vscode.window.showErrorMessage(localize('adminToolExtWin.noConnectionContextForGsw', 'No ConnectionContext provided for handleLaunchSsmsMinPropertiesDialogCommand'));
|
||||||
}
|
}
|
||||||
|
|
||||||
launchSsmsDialog(
|
launchSsmsDialog(
|
||||||
'GenerateScripts',
|
action,
|
||||||
connectionContext);
|
connectionContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,6 +141,7 @@ async function handleLaunchSsmsMinGswDialogCommand(connectionContext?: azdata.Ob
|
|||||||
*/
|
*/
|
||||||
async function launchSsmsDialog(action: string, connectionContext: azdata.ObjectExplorerContext): Promise<void> {
|
async function launchSsmsDialog(action: string, connectionContext: azdata.ObjectExplorerContext): Promise<void> {
|
||||||
if (!connectionContext.connectionProfile) {
|
if (!connectionContext.connectionProfile) {
|
||||||
|
Telemetry.sendTelemetryEventForError('NoConnectionProfile', { action: action });
|
||||||
vscode.window.showErrorMessage(localize('adminToolExtWin.noConnectionProfile', 'No connectionProfile provided from connectionContext : {0}', JSON.stringify(connectionContext)));
|
vscode.window.showErrorMessage(localize('adminToolExtWin.noConnectionProfile', 'No connectionProfile provided from connectionContext : {0}', JSON.stringify(connectionContext)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -152,6 +155,7 @@ async function launchSsmsDialog(action: string, connectionContext: azdata.Object
|
|||||||
oeNode = await azdata.objectexplorer.getNode(connectionContext.connectionProfile.id, connectionContext.nodeInfo.nodePath);
|
oeNode = await azdata.objectexplorer.getNode(connectionContext.connectionProfile.id, connectionContext.nodeInfo.nodePath);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
Telemetry.sendTelemetryEventForError('NoOENode', { action: action });
|
||||||
vscode.window.showErrorMessage(localize('adminToolExtWin.noOENode', 'Could not determine Object Explorer node from connectionContext : {0}', JSON.stringify(connectionContext)));
|
vscode.window.showErrorMessage(localize('adminToolExtWin.noOENode', 'Could not determine Object Explorer node from connectionContext : {0}', JSON.stringify(connectionContext)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -175,7 +179,12 @@ async function launchSsmsDialog(action: string, connectionContext: azdata.Object
|
|||||||
|
|
||||||
const args = buildSsmsMinCommandArgs(params);
|
const args = buildSsmsMinCommandArgs(params);
|
||||||
|
|
||||||
Telemetry.sendTelemetryEvent('LaunchSsmsDialog', { 'action': action });
|
Telemetry.sendTelemetryEvent('LaunchSsmsDialog',
|
||||||
|
{
|
||||||
|
action: action,
|
||||||
|
nodeType: oeNode ? oeNode.nodeType : 'Server',
|
||||||
|
authType: connectionContext.connectionProfile.authenticationType
|
||||||
|
});
|
||||||
|
|
||||||
vscode.window.setStatusBarMessage(localize('adminToolExtWin.launchingDialogStatus', 'Launching dialog...'), 3000);
|
vscode.window.setStatusBarMessage(localize('adminToolExtWin.launchingDialogStatus', 'Launching dialog...'), 3000);
|
||||||
|
|
||||||
@@ -186,11 +195,12 @@ async function launchSsmsDialog(action: string, connectionContext: azdata.Object
|
|||||||
(execException, stdout, stderr) => {
|
(execException, stdout, stderr) => {
|
||||||
// Process has exited so remove from map of running processes
|
// Process has exited so remove from map of running processes
|
||||||
runningProcesses.delete(proc.pid);
|
runningProcesses.delete(proc.pid);
|
||||||
|
const err = stderr.toString();
|
||||||
Telemetry.sendTelemetryEvent('LaunchSsmsDialogResult', {
|
Telemetry.sendTelemetryEvent('LaunchSsmsDialogResult', {
|
||||||
'action': params.action,
|
action: params.action,
|
||||||
'returnCode': execException && execException.code ? execException.code.toString() : '0'
|
returnCode: execException && execException.code ? execException.code.toString() : '0',
|
||||||
|
errorType: getTelemetryErrorType(err)
|
||||||
});
|
});
|
||||||
let err = stderr.toString();
|
|
||||||
if (err !== '') {
|
if (err !== '') {
|
||||||
vscode.window.showErrorMessage(localize(
|
vscode.window.showErrorMessage(localize(
|
||||||
'adminToolExtWin.ssmsMinError',
|
'adminToolExtWin.ssmsMinError',
|
||||||
|
|||||||
@@ -62,27 +62,11 @@ export class Telemetry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send a telemetry event for an exception
|
* Send a telemetry event for a general error
|
||||||
|
* @param err The error to log
|
||||||
*/
|
*/
|
||||||
public static sendTelemetryEventForException(
|
public static sendTelemetryEventForError(err: string, properties?: ITelemetryEventProperties): void {
|
||||||
err: any, methodName: string, extensionConfigName: string): void {
|
this.sendTelemetryEvent('Error', { error: err, ...properties });
|
||||||
try {
|
|
||||||
let stackArray: string[];
|
|
||||||
let firstLine: string = '';
|
|
||||||
if (err !== undefined && err.stack !== undefined) {
|
|
||||||
stackArray = err.stack.split('\n');
|
|
||||||
if (stackArray !== undefined && stackArray.length >= 2) {
|
|
||||||
firstLine = stackArray[1]; // The first line is the error message and we don't want to send that telemetry event
|
|
||||||
firstLine = filterErrorPath(firstLine);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only adding the method name and the fist line of the stack trace. We don't add the error message because it might have PII
|
|
||||||
this.sendTelemetryEvent('Exception', { methodName: methodName, errorLine: firstLine });
|
|
||||||
} catch (telemetryErr) {
|
|
||||||
// If sending telemetry event fails ignore it so it won't break the extension
|
|
||||||
console.error('Failed to send telemetry event. error: ' + telemetryErr, extensionConfigName);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -106,7 +90,12 @@ export class Telemetry {
|
|||||||
properties = {};
|
properties = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.reporter.sendTelemetryEvent(eventName, properties, measures);
|
try {
|
||||||
|
this.reporter.sendTelemetryEvent(eventName, properties, measures);
|
||||||
|
} catch (telemetryErr) {
|
||||||
|
// If sending telemetry event fails ignore it so it won't break the extension
|
||||||
|
console.error('Failed to send telemetry event. error: ' + telemetryErr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,4 +36,26 @@ export function doubleEscapeSingleQuotes(value: string): string {
|
|||||||
*/
|
*/
|
||||||
export function backEscapeDoubleQuotes(value: string): string {
|
export function backEscapeDoubleQuotes(value: string): string {
|
||||||
return value.replace(/"/g, '\\"');
|
return value.replace(/"/g, '\\"');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Map an error message into a GDPR-Compliant short name for the type of error.
|
||||||
|
* @param msg The error message to map
|
||||||
|
*/
|
||||||
|
export function getTelemetryErrorType(msg: string): string {
|
||||||
|
if (msg.indexOf('is not recognized as an internal or external command') !== -1) {
|
||||||
|
return 'ExeNotFound';
|
||||||
|
}
|
||||||
|
else if (msg.indexOf('Unknown Action') !== -1) {
|
||||||
|
return 'UnknownAction';
|
||||||
|
}
|
||||||
|
else if (msg.indexOf('No Action Provided') !== -1) {
|
||||||
|
return 'NoActionProvided';
|
||||||
|
}
|
||||||
|
else if (msg.indexOf('Run exception') !== -1) {
|
||||||
|
return 'RunException';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return 'Other';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user