@sussudio/platform
Version:
Internal APIs for VS Code's service injection the base services.
93 lines (92 loc) • 3.14 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { binarySearch } from '@sussudio/base/common/arrays.mjs';
import { errorHandler, ErrorNoTelemetry } from '@sussudio/base/common/errors.mjs';
import { DisposableStore, toDisposable } from '@sussudio/base/common/lifecycle.mjs';
import { safeStringify } from '@sussudio/base/common/objects.mjs';
import { FileOperationError } from '../../files/common/files.mjs';
export var ErrorEvent;
(function (ErrorEvent) {
function compare(a, b) {
if (a.callstack < b.callstack) {
return -1;
} else if (a.callstack > b.callstack) {
return 1;
}
return 0;
}
ErrorEvent.compare = compare;
})(ErrorEvent || (ErrorEvent = {}));
export default class BaseErrorTelemetry {
static ERROR_FLUSH_TIMEOUT = 5 * 1000;
_telemetryService;
_flushDelay;
_flushHandle = -1;
_buffer = [];
_disposables = new DisposableStore();
constructor(telemetryService, flushDelay = BaseErrorTelemetry.ERROR_FLUSH_TIMEOUT) {
this._telemetryService = telemetryService;
this._flushDelay = flushDelay;
// (1) check for unexpected but handled errors
const unbind = errorHandler.addListener((err) => this._onErrorEvent(err));
this._disposables.add(toDisposable(unbind));
// (2) install implementation-specific error listeners
this.installErrorListeners();
}
dispose() {
clearTimeout(this._flushHandle);
this._flushBuffer();
this._disposables.dispose();
}
installErrorListeners() {
// to override
}
_onErrorEvent(err) {
if (!err) {
return;
}
// unwrap nested errors from loader
if (err.detail && err.detail.stack) {
err = err.detail;
}
// If it's the no telemetry error it doesn't get logged
// TOOD @lramos15 hacking in FileOperation error because it's too messy to adopt ErrorNoTelemetry. A better solution should be found
if (ErrorNoTelemetry.isErrorNoTelemetry(err) || err instanceof FileOperationError) {
return;
}
// work around behavior in workerServer.ts that breaks up Error.stack
const callstack = Array.isArray(err.stack) ? err.stack.join('\n') : err.stack;
const msg = err.message ? err.message : safeStringify(err);
// errors without a stack are not useful telemetry
if (!callstack) {
return;
}
this._enqueue({ msg, callstack });
}
_enqueue(e) {
const idx = binarySearch(this._buffer, e, ErrorEvent.compare);
if (idx < 0) {
e.count = 1;
this._buffer.splice(~idx, 0, e);
} else {
if (!this._buffer[idx].count) {
this._buffer[idx].count = 0;
}
this._buffer[idx].count += 1;
}
if (this._flushHandle === -1) {
this._flushHandle = setTimeout(() => {
this._flushBuffer();
this._flushHandle = -1;
}, this._flushDelay);
}
}
_flushBuffer() {
for (const error of this._buffer) {
this._telemetryService.publicLogError2('UnhandledError', error);
}
this._buffer.length = 0;
}
}