mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-02 09:35:40 -05:00
SQL Operations Studio Public Preview 1 (0.23) release source code
This commit is contained in:
150
src/vs/platform/telemetry/browser/errorTelemetry.ts
Normal file
150
src/vs/platform/telemetry/browser/errorTelemetry.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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) {
|
||||
this._telemetryService.publicLog('UnhandledError', error);
|
||||
}
|
||||
this._buffer.length = 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user