提交 dc4c7631 编写于 作者: S Sandeep Somavarapu

Use backup channels when spdlog fails and log telemetry

上级 03a8c0c0
......@@ -5,27 +5,13 @@
import { createRotatingLogger } from 'vs/platform/log/node/spdlogService';
import { RotatingLogger } from 'spdlog';
interface Appender {
critical(content: string);
flush();
}
export class OutputAppender {
private appender: Appender;
private appender: RotatingLogger;
constructor(name: string, file: string) {
// Do not crash if logger cannot be loaded
try {
this.appender = createRotatingLogger(name, file, 1024 * 1024 * 30, 1);
(<RotatingLogger>this.appender).clearFormatters();
} catch (error) {
console.error(error);
this.appender = {
critical() { },
flush() { },
};
}
this.appender = createRotatingLogger(name, file, 1024 * 1024 * 30, 1);
this.appender.clearFormatters();
}
append(content: string): void {
......
......@@ -111,7 +111,17 @@ export class ExtHostOutputService {
if (!name) {
throw new Error('illegal argument `name`. must not be falsy');
} else {
return push ? new ExtHostPushOutputChannel(name, this._proxy) : new ExtHostFileOutputChannel(name, this._outputDir, this._proxy);
if (push) {
return new ExtHostPushOutputChannel(name, this._proxy);
} else {
// Do not crash if logger cannot be created
try {
return new ExtHostFileOutputChannel(name, this._outputDir, this._proxy);
} catch (error) {
console.log(error);
return new ExtHostPushOutputChannel(name, this._proxy);
}
}
}
}
}
......@@ -5,6 +5,7 @@
import * as nls from 'vs/nls';
import * as paths from 'vs/base/common/paths';
import * as strings from 'vs/base/common/strings';
import * as extfs from 'vs/base/node/extfs';
import { TPromise } from 'vs/base/common/winjs.base';
import { Event, Emitter } from 'vs/base/common/event';
......@@ -15,7 +16,7 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { Registry } from 'vs/platform/registry/common/platform';
import { EditorOptions } from 'vs/workbench/common/editor';
import { IOutputChannelDescriptor, IOutputChannel, IOutputService, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, OUTPUT_SCHEME, OUTPUT_MIME, LOG_SCHEME, LOG_MIME, CONTEXT_ACTIVE_LOG_OUTPUT } from 'vs/workbench/parts/output/common/output';
import { IOutputChannelDescriptor, IOutputChannel, IOutputService, Extensions, OUTPUT_PANEL_ID, IOutputChannelRegistry, OUTPUT_SCHEME, OUTPUT_MIME, LOG_SCHEME, LOG_MIME, CONTEXT_ACTIVE_LOG_OUTPUT, MAX_OUTPUT_LENGTH } from 'vs/workbench/parts/output/common/output';
import { OutputPanel } from 'vs/workbench/parts/output/browser/outputPanel';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IModelService } from 'vs/editor/common/services/modelService';
......@@ -34,10 +35,12 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
import { toLocalISOString } from 'vs/base/common/date';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { ILogService } from 'vs/platform/log/common/log';
import { binarySearch } from 'vs/base/common/arrays';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { CancellationToken } from 'vs/base/common/cancellation';
import { OutputAppender } from 'vs/platform/output/node/outputAppender';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel';
......@@ -408,6 +411,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo
@IEnvironmentService environmentService: IEnvironmentService,
@IWindowService windowService: IWindowService,
@ILogService private logService: ILogService,
@ITelemetryService private telemetryService: ITelemetryService,
@ILifecycleService private lifecycleService: ILifecycleService,
@IContextKeyService private contextKeyService: IContextKeyService,
) {
......@@ -551,7 +555,17 @@ export class OutputService extends Disposable implements IOutputService, ITextMo
if (channelData && channelData.file) {
return this.instantiationService.createInstance(FileOutputChannel, channelData, uri);
}
return this.instantiationService.createInstance(OutputChannelBackedByFile, { id, label: channelData ? channelData.label : '' }, this.outputDir, uri);
try {
return this.instantiationService.createInstance(OutputChannelBackedByFile, { id, label: channelData ? channelData.label : '' }, this.outputDir, uri);
} catch (e) {
// Do not crash if spdlog rotating logger cannot be loaded (workaround for https://github.com/Microsoft/vscode/issues/47883)
this.logService.error(e);
/* __GDPR__
"output.channel.creation.error" : {}
*/
this.telemetryService.publicLog('output.channel.creation.error');
return this.instantiationService.createInstance(BufferredOutputChannel, { id, label: channelData ? channelData.label : '' });
}
}
private doShowChannel(channel: IOutputChannel, preserveFocus: boolean): Thenable<void> {
......@@ -625,4 +639,144 @@ export class LogContentProvider {
}
return channel;
}
}
\ No newline at end of file
}
// Remove this channel when https://github.com/Microsoft/vscode/issues/47883 is fixed
class BufferredOutputChannel extends Disposable implements OutputChannel {
readonly id: string;
readonly label: string;
readonly file: URI = null;
scrollLock: boolean = false;
protected _onDidAppendedContent: Emitter<void> = new Emitter<void>();
readonly onDidAppendedContent: Event<void> = this._onDidAppendedContent.event;
private readonly _onDispose: Emitter<void> = new Emitter<void>();
readonly onDispose: Event<void> = this._onDispose.event;
private modelUpdater: RunOnceScheduler;
private model: ITextModel;
private readonly bufferredContent: BufferedContent;
private lastReadId: number = void 0;
constructor(
protected readonly outputChannelIdentifier: IOutputChannelDescriptor,
@IModelService private modelService: IModelService,
@IModeService private modeService: IModeService
) {
super();
this.id = outputChannelIdentifier.id;
this.label = outputChannelIdentifier.label;
this.modelUpdater = new RunOnceScheduler(() => this.updateModel(), 300);
this._register(toDisposable(() => this.modelUpdater.cancel()));
this.bufferredContent = new BufferedContent();
this._register(toDisposable(() => this.bufferredContent.clear()));
}
append(output: string) {
this.bufferredContent.append(output);
if (!this.modelUpdater.isScheduled()) {
this.modelUpdater.schedule();
}
}
clear(): void {
if (this.modelUpdater.isScheduled()) {
this.modelUpdater.cancel();
}
if (this.model) {
this.model.setValue('');
}
this.bufferredContent.clear();
this.lastReadId = void 0;
}
loadModel(): TPromise<ITextModel> {
const { value, id } = this.bufferredContent.getDelta(this.lastReadId);
if (this.model) {
this.model.setValue(value);
} else {
this.model = this.createModel(value);
}
this.lastReadId = id;
return TPromise.as(this.model);
}
private createModel(content: string): ITextModel {
const model = this.modelService.createModel(content, this.modeService.getOrCreateMode(OUTPUT_MIME), URI.from({ scheme: OUTPUT_SCHEME, path: this.id }));
const disposables: IDisposable[] = [];
disposables.push(model.onWillDispose(() => {
this.model = null;
dispose(disposables);
}));
return model;
}
private updateModel(): void {
if (this.model) {
const { value, id } = this.bufferredContent.getDelta(this.lastReadId);
this.lastReadId = id;
const lastLine = this.model.getLineCount();
const lastLineMaxColumn = this.model.getLineMaxColumn(lastLine);
this.model.applyEdits([EditOperation.insert(new Position(lastLine, lastLineMaxColumn), value)]);
this._onDidAppendedContent.fire();
}
}
dispose(): void {
this._onDispose.fire();
super.dispose();
}
}
class BufferedContent {
private data: string[] = [];
private dataIds: number[] = [];
private idPool = 0;
private length = 0;
public append(content: string): void {
this.data.push(content);
this.dataIds.push(++this.idPool);
this.length += content.length;
this.trim();
}
public clear(): void {
this.data.length = 0;
this.dataIds.length = 0;
this.length = 0;
}
private trim(): void {
if (this.length < MAX_OUTPUT_LENGTH * 1.2) {
return;
}
while (this.length > MAX_OUTPUT_LENGTH) {
this.dataIds.shift();
const removed = this.data.shift();
this.length -= removed.length;
}
}
public getDelta(previousId?: number): { value: string, id: number } {
let idx = -1;
if (previousId !== void 0) {
idx = binarySearch(this.dataIds, previousId, (a, b) => a - b);
}
const id = this.idPool;
if (idx >= 0) {
const value = strings.removeAnsiEscapeCodes(this.data.slice(idx + 1).join(''));
return { value, id };
} else {
const value = strings.removeAnsiEscapeCodes(this.data.join(''));
return { value, id };
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册