mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-02 09:35:40 -05:00
* Initial VS Code 1.21 file copy with patches * A few more merges * Post npm install * Fix batch of build breaks * Fix more build breaks * Fix more build errors * Fix more build breaks * Runtime fixes 1 * Get connection dialog working with some todos * Fix a few packaging issues * Copy several node_modules to package build to fix loader issues * Fix breaks from master * A few more fixes * Make tests pass * First pass of license header updates * Second pass of license header updates * Fix restore dialog issues * Remove add additional themes menu items * fix select box issues where the list doesn't show up * formatting * Fix editor dispose issue * Copy over node modules to correct location on all platforms
162 lines
4.5 KiB
TypeScript
162 lines
4.5 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
'use strict';
|
|
|
|
import { binarySearch } from 'vs/base/common/arrays';
|
|
import { globals } from 'vs/base/common/platform';
|
|
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
|
import { IDisposable, toDisposable, dispose } from 'vs/base/common/lifecycle';
|
|
import Errors = require('vs/base/common/errors');
|
|
import { safeStringify } from 'vs/base/common/objects';
|
|
|
|
interface ErrorEvent {
|
|
stack: string;
|
|
message?: string;
|
|
filename?: string;
|
|
line?: number;
|
|
column?: number;
|
|
error?: { name: string; message: string; };
|
|
|
|
count?: number;
|
|
}
|
|
|
|
namespace ErrorEvent {
|
|
export function compare(a: ErrorEvent, b: ErrorEvent) {
|
|
if (a.stack < b.stack) {
|
|
return -1;
|
|
} else if (a.stack > b.stack) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
export default class ErrorTelemetry {
|
|
|
|
public static ERROR_FLUSH_TIMEOUT: number = 5 * 1000;
|
|
|
|
private _telemetryService: ITelemetryService;
|
|
private _flushDelay: number;
|
|
private _flushHandle = -1;
|
|
private _buffer: ErrorEvent[] = [];
|
|
private _disposables: IDisposable[] = [];
|
|
|
|
constructor(telemetryService: ITelemetryService, flushDelay = ErrorTelemetry.ERROR_FLUSH_TIMEOUT) {
|
|
this._telemetryService = telemetryService;
|
|
this._flushDelay = flushDelay;
|
|
|
|
// (1) check for unexpected but handled errors
|
|
const unbind = Errors.errorHandler.addListener((err) => this._onErrorEvent(err));
|
|
this._disposables.push(toDisposable(unbind));
|
|
|
|
// (2) check for uncaught global errors
|
|
let oldOnError: Function;
|
|
let that = this;
|
|
if (typeof globals.onerror === 'function') {
|
|
oldOnError = globals.onerror;
|
|
}
|
|
globals.onerror = function (message: string, filename: string, line: number, column?: number, e?: any) {
|
|
that._onUncaughtError(message, filename, line, column, e);
|
|
if (oldOnError) {
|
|
oldOnError.apply(this, arguments);
|
|
}
|
|
};
|
|
this._disposables.push(toDisposable(function () {
|
|
if (oldOnError) {
|
|
globals.onerror = oldOnError;
|
|
}
|
|
}));
|
|
}
|
|
|
|
dispose() {
|
|
clearTimeout(this._flushHandle);
|
|
this._flushBuffer();
|
|
this._disposables = dispose(this._disposables);
|
|
}
|
|
|
|
private _onErrorEvent(err: any): void {
|
|
|
|
if (!err) {
|
|
return;
|
|
}
|
|
|
|
// unwrap nested errors from loader
|
|
if (err.detail && err.detail.stack) {
|
|
err = err.detail;
|
|
}
|
|
|
|
// work around behavior in workerServer.ts that breaks up Error.stack
|
|
let stack = Array.isArray(err.stack) ? err.stack.join('\n') : err.stack;
|
|
let message = err.message ? err.message : safeStringify(err);
|
|
|
|
// errors without a stack are not useful telemetry
|
|
if (!stack) {
|
|
return;
|
|
}
|
|
|
|
this._enqueue({ message, stack });
|
|
}
|
|
|
|
private _onUncaughtError(message: string, filename: string, line: number, column?: number, err?: any): void {
|
|
|
|
let data: ErrorEvent = {
|
|
stack: message,
|
|
message,
|
|
filename,
|
|
line,
|
|
column
|
|
};
|
|
|
|
if (err) {
|
|
let { name, message, stack } = err;
|
|
data.error = { name, message };
|
|
if (stack) {
|
|
data.stack = Array.isArray(err.stack)
|
|
? err.stack = err.stack.join('\n')
|
|
: err.stack;
|
|
}
|
|
}
|
|
|
|
this._enqueue(data);
|
|
}
|
|
|
|
private _enqueue(e: ErrorEvent): void {
|
|
|
|
const idx = binarySearch(this._buffer, e, ErrorEvent.compare);
|
|
if (idx < 0) {
|
|
e.count = 1;
|
|
this._buffer.splice(~idx, 0, e);
|
|
} else {
|
|
this._buffer[idx].count += 1;
|
|
}
|
|
|
|
if (this._flushHandle === -1) {
|
|
this._flushHandle = setTimeout(() => {
|
|
this._flushBuffer();
|
|
this._flushHandle = -1;
|
|
}, this._flushDelay);
|
|
}
|
|
}
|
|
|
|
private _flushBuffer(): void {
|
|
for (let error of this._buffer) {
|
|
/* __GDPR__
|
|
"UnhandledError" : {
|
|
"message" : { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
|
|
"name": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
|
|
"stack": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
|
|
"id": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
|
|
"line": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" },
|
|
"column": { "classification": "CallstackOrException", "purpose": "PerformanceAndHealth" }
|
|
}
|
|
*/
|
|
// __GDPR__TODO__ what's the complete set of properties?
|
|
this._telemetryService.publicLog('UnhandledError', error);
|
|
}
|
|
this._buffer.length = 0;
|
|
}
|
|
}
|