UNPKG

vscode-chrome-debug-core

Version:

A library for building VS Code debug adapters for targets that support the Chrome Remote Debug Protocol

200 lines (198 loc) 7.37 kB
"use strict"; /*--------------------------------------------------------- * Copyright (C) Microsoft Corporation. All rights reserved. *--------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); const vscode_debugadapter_1 = require("vscode-debugadapter"); const utils_1 = require("./utils"); // telemetry keys to prefix with `!` over DAP for classification in VS Code. const prefixTelemetryKeys = ['exceptionStack', 'exceptionMessage', 'exceptionId']; class TelemetryReporter { constructor() { this._globalTelemetryProperties = {}; } reportEvent(name, data) { if (!this._sendEvent) { return; } const combinedData = Object.assign({}, this._globalTelemetryProperties, data); for (const key of prefixTelemetryKeys) { if (combinedData.hasOwnProperty(key)) { combinedData[`!${key}`] = combinedData[key]; delete combinedData[key]; } } const event = new vscode_debugadapter_1.OutputEvent(name, 'telemetry', combinedData); this._sendEvent(event); } setupEventHandler(_sendEvent) { this._sendEvent = _sendEvent; } addCustomGlobalProperty(additionalGlobalTelemetryProperties) { Object.assign(this._globalTelemetryProperties, additionalGlobalTelemetryProperties); } } exports.TelemetryReporter = TelemetryReporter; // If you add an async global property, all events after that will include it class AsyncGlobalPropertiesTelemetryReporter { constructor(_telemetryReporter) { this._telemetryReporter = _telemetryReporter; this._actionsQueue = Promise.resolve(); // We just store the parameter } reportEvent(name, data) { /* * TODO: Put this code back after VS stops dropping telemetry events that happen after fatal errors, and disconnecting... * VS has a bug where it drops telemetry events that happen after a fatal error, or after the DA starts disconnecting. Our * temporary workaround is to make telemetry sync, so it'll likely be sent before we send the fatal errors, etc... this._actionsQueue = this._actionsQueue.then(() => // We block the report event until all the addCustomGlobalProperty have finished this._telemetryReporter.reportEvent(name, data)); */ this._telemetryReporter.reportEvent(name, data); } setupEventHandler(_sendEvent) { this._telemetryReporter.setupEventHandler(_sendEvent); } addCustomGlobalProperty(additionalGlobalPropertiesPromise) { const reportedPropertyP = Promise.resolve(additionalGlobalPropertiesPromise).then(property => this._telemetryReporter.addCustomGlobalProperty(property), rejection => this.reportErrorWhileWaitingForProperty(rejection)); this._actionsQueue = Promise.all([this._actionsQueue, reportedPropertyP]); } reportErrorWhileWaitingForProperty(rejection) { let properties = {}; properties.successful = 'false'; properties.exceptionType = 'firstChance'; utils_1.fillErrorDetails(properties, rejection); /* __GDPR__ "error-while-adding-custom-global-property" : { "${include}": [ "${IExecutionResultTelemetryProperties}" ] } */ this._telemetryReporter.reportEvent('error-while-adding-custom-global-property', properties); } } exports.AsyncGlobalPropertiesTelemetryReporter = AsyncGlobalPropertiesTelemetryReporter; class NullTelemetryReporter { reportEvent(name, data) { // no-op } setupEventHandler(_sendEvent) { // no-op } } exports.NullTelemetryReporter = NullTelemetryReporter; exports.DefaultTelemetryIntervalInMilliseconds = 10000; class BatchTelemetryReporter { constructor(_telemetryReporter, _cadenceInMilliseconds = exports.DefaultTelemetryIntervalInMilliseconds) { this._telemetryReporter = _telemetryReporter; this._cadenceInMilliseconds = _cadenceInMilliseconds; this.reset(); this.setup(); } reportEvent(name, data) { if (!this._eventBuckets[name]) { this._eventBuckets[name] = []; } this._eventBuckets[name].push(data); } finalize() { this.send(); clearInterval(this._timer); } setup() { this._timer = setInterval(() => this.send(), this._cadenceInMilliseconds); } reset() { this._eventBuckets = {}; } send() { for (const eventName in this._eventBuckets) { const bucket = this._eventBuckets[eventName]; let properties = BatchTelemetryReporter.transfromBucketData(bucket); this._telemetryReporter.reportEvent(eventName, properties); } this.reset(); } /** * Transfrom the bucket of events data from the form: * [{ * p1: v1, * p2: v2 * }, * { * p1: w1, * p2: w2 * p3: w3 * }] * * to * { * p1: [v1, w1], * p2: [v2, w2], * p3: [null, w3] * } * * * The later form is easier for downstream telemetry analysis. */ static transfromBucketData(bucketForEventType) { const allPropertyNamesInTheBucket = BatchTelemetryReporter.collectPropertyNamesFromAllEvents(bucketForEventType); let properties = {}; // Create a holder for all potential property names. for (const key of allPropertyNamesInTheBucket) { properties[`aggregated.${key}`] = []; } // Run through all the events in the bucket, collect the values for each property name. for (const event of bucketForEventType) { for (const propertyName of allPropertyNamesInTheBucket) { properties[`aggregated.${propertyName}`].push(event[propertyName] === undefined ? null : event[propertyName]); } } // Serialize each array as the final aggregated property value. for (const propertyName of allPropertyNamesInTheBucket) { properties[`aggregated.${propertyName}`] = JSON.stringify(properties[`aggregated.${propertyName}`]); } return properties; } /** * Get the property keys from all the entries of a event bucket: * * So * [{ * p1: v1, * p2: v2 * }, * { * p1: w1, * p2: w2 * p3: w3 * }] * * will return ['p1', 'p2', 'p3'] */ static collectPropertyNamesFromAllEvents(bucket) { let propertyNamesSet = {}; for (const entry of bucket) { for (const key of Object.keys(entry)) { propertyNamesSet[key] = true; } } return Object.keys(propertyNamesSet); } } exports.BatchTelemetryReporter = BatchTelemetryReporter; class TelemetryPropertyCollector { constructor() { this._properties = {}; } getProperties() { return this._properties; } addTelemetryProperty(propertyName, value) { this._properties[propertyName] = value; } } exports.TelemetryPropertyCollector = TelemetryPropertyCollector; exports.telemetry = new AsyncGlobalPropertiesTelemetryReporter(new TelemetryReporter()); //# sourceMappingURL=telemetry.js.map