sculptui-vscode
Version:
Visually edit your React component code from the browser
142 lines (129 loc) • 4.87 kB
text/typescript
import * as vscode from 'vscode';
import TelemetryReporter from 'vscode-extension-telemetry';
import { ExtensionId } from './const/extension';
import { userInfo } from 'os';
import { sep } from 'path';
// telemetry reporter
let reporter: TelemetryReporter | undefined = undefined;
let commonProps: { [key: string]: string } = {};
/** Activate a telemetry reporting */
export function activate(context: vscode.ExtensionContext) {
// create telemetry reporter on extension activation
const extension = vscode.extensions.getExtension(ExtensionId)!;
const extensionVersion = extension.packageJSON.version;
// following key just allows you to send events to azure insights API
// so it does not need to be protected
// but obfuscating anyways - bots scan github for keys, but if you want my key you better work for it, damnit!
const innocentKitten = Buffer.from('ODFhZGMzMGMtZDRmMC00NTg5LThlOGItMzYzNDdkZDU2ZGQ3', 'base64').toString();
reporter = new TelemetryReporter(ExtensionId, extensionVersion, innocentKitten);
// ensure it gets properly disposed
context.subscriptions.push(reporter);
}
/** Deactivate a telemetry reporting */
export function deactivate() {
// This will ensure all pending events get flushed
reporter && reporter.dispose();
}
/** Add common properties that will be always sent when calling `sendEvent()` or `sendError()`. */
export function addCommonProperties(properties: { [name: string]: string | number | boolean | undefined | null }) {
commonProps = { ...commonProps, ...convertToStrings(properties) };
}
/**
* Sends an event to telemetry reporting.
*/
export function sendEvent(
/** Event name.
*
* To allow proper grouping and useful metrics, choose a small number of separate event names. For example, don't use a separate name for each generated instance of an event.
*
* Max. lenght: 512 characters */
eventName: string,
/** Propeties to add to the common properties that will be sent for this event. */
properties?: { [name: string]: string | number | boolean | undefined | null },
/** Metrics to add to this event. */
measurements?: {
[key: string]: number;
},
) {
if (!reporter) {
console.warn('Telemetry should be activated using activate() before being called.');
return;
}
reporter.sendTelemetryEvent(eventName, { ...commonProps, ...convertToStrings(properties) }, measurements);
}
/**
* Sends an exception to telemetry reporting.
*/
export function sendError(
error: Error,
properties?: { [name: string]: string | number | boolean | undefined | null },
measurements?: {
[key: string]: number;
},
) {
if (!reporter) {
console.warn('Telemetry should be activated using activate() before being called.');
return;
}
if (error.stack) {
error.stack = anonymizePaths(error.stack);
}
reporter.sendTelemetryException(error, { ...commonProps, ...convertToStrings(properties) }, measurements);
}
/**
* Sends an exception as an event to telemetry reporting.
*
* Note: Using `sendError()` is prefered unless you want it to appear in the list of custom events.
*/
export function sendErrorEvent(
eventName: string,
properties?: { [name: string]: string | number | boolean | undefined | null },
measurements?: {
[key: string]: number;
},
error?: Error,
) {
if (!reporter) {
console.warn('Telemetry should be activated using activate() before being called.');
return;
}
if (error && error.stack) {
error.stack = anonymizePaths(error.stack);
}
reporter.sendTelemetryErrorEvent(
eventName,
{
...commonProps,
...properties,
errorStack: error?.stack || '',
errorMessage: error?.message || `${error}`,
errorName: error?.name || '',
},
measurements,
[],
);
}
/** converts non-string value properties on object to strings */
function convertToStrings(properties?: {
[name: string]: string | number | boolean | undefined | null;
}): { [name: string]: string } {
if (!properties) return {};
return Object.keys(properties).reduce((prevVal, current) => {
let propVal = properties[current];
if (propVal === undefined) {
propVal = '<undefined>';
} else if (propVal === null) {
propVal = '<null>';
} else if (typeof propVal !== 'string') {
propVal = propVal.toString();
}
prevVal[current] = propVal;
return prevVal;
}, {} as { [name: string]: string });
}
/** replace username with anon */
function anonymizePaths(input: string) {
if (input == null) return input;
return input.replace(new RegExp('\\' + sep + userInfo().username, 'g'), sep + 'anon');
}
export default { activate, deactivate, addCommonProperties, sendError, sendEvent, sendErrorEvent };