提交 5dbd0935 编写于 作者: S Sandeep Somavarapu

#57618: ExtHost: Implement writable output channels using spdlog

上级 a030947f
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { RotatingLogger } from 'spdlog';
export class OutputAppender {
private appender: RotatingLogger;
constructor(name: string, file: string) {
this.appender = new RotatingLogger(name, file, 1024 * 1024 * 30, 1);
this.appender.clearFormatters();
}
append(content: string): void {
this.appender.critical(content);
}
flush(): void {
this.appender.flush();
}
}
\ No newline at end of file
......@@ -11,6 +11,7 @@ import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { MainThreadOutputServiceShape, MainContext, IExtHostContext } from '../node/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/electron-browser/extHostCustomers';
import { UriComponents, URI } from 'vs/base/common/uri';
@extHostNamedCustomer(MainContext.MainThreadOutputService)
export class MainThreadOutputService implements MainThreadOutputServiceShape {
......@@ -34,28 +35,33 @@ export class MainThreadOutputService implements MainThreadOutputServiceShape {
// Leave all the existing channels intact (e.g. might help with troubleshooting)
}
public $append(channelId: string, label: string, value: string): TPromise<void> {
this._getChannel(channelId, label).append(value);
return undefined;
public $register(id: string, label: string, file?: UriComponents): TPromise<void> {
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).registerChannel({ id, label, file: file ? URI.revive(file) : null, log: false });
return TPromise.as(null);
}
public $clear(channelId: string, label: string): TPromise<void> {
this._getChannel(channelId, label).clear();
public $append(channelId: string, value: string): TPromise<void> {
const channel = this._getChannel(channelId);
if (channel) {
channel.append(value);
}
return undefined;
}
public $reveal(channelId: string, label: string, preserveFocus: boolean): TPromise<void> {
const channel = this._getChannel(channelId, label);
this._outputService.showChannel(channel.id, preserveFocus);
public $clear(channelId: string): TPromise<void> {
const channel = this._getChannel(channelId);
if (channel) {
channel.clear();
}
return undefined;
}
private _getChannel(channelId: string, label: string): IOutputChannel {
if (!Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).getChannel(channelId)) {
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).registerChannel(channelId, label);
public $reveal(channelId: string, preserveFocus: boolean): TPromise<void> {
const channel = this._getChannel(channelId);
if (channel) {
this._outputService.showChannel(channel.id, preserveFocus);
}
return this._outputService.getChannel(channelId);
return undefined;
}
public $close(channelId: string): TPromise<void> {
......@@ -67,8 +73,15 @@ export class MainThreadOutputService implements MainThreadOutputServiceShape {
return undefined;
}
public $dispose(channelId: string, label: string): TPromise<void> {
this._getChannel(channelId, label).dispose();
public $dispose(channelId: string): TPromise<void> {
const channel = this._getChannel(channelId);
if (channel) {
channel.dispose();
}
return undefined;
}
private _getChannel(channelId: string): IOutputChannel {
return this._outputService.getChannel(channelId);
}
}
......@@ -61,6 +61,8 @@ import { ExtHostWebviews } from 'vs/workbench/api/node/extHostWebview';
import { ExtHostComments } from './extHostComments';
import { ExtHostSearch } from './extHostSearch';
import { ExtHostUrls } from './extHostUrls';
import { toLocalISOString } from 'vs/base/common/date';
import { posix } from 'path';
export interface IExtensionApiFactory {
(extension: IExtensionDescription): typeof vscode;
......@@ -136,7 +138,8 @@ export function createApiFactory(
const extHostMessageService = new ExtHostMessageService(rpcProtocol);
const extHostDialogs = new ExtHostDialogs(rpcProtocol);
const extHostStatusBar = new ExtHostStatusBar(rpcProtocol);
const extHostOutputService = new ExtHostOutputService(rpcProtocol);
const outputDir = posix.join(initData.logsPath, `output_logging_${initData.windowId}_${toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')}`);
const extHostOutputService = new ExtHostOutputService(outputDir, rpcProtocol);
const extHostLanguages = new ExtHostLanguages(rpcProtocol);
// Register API-ish commands
......
......@@ -302,11 +302,12 @@ export interface MainThreadMessageServiceShape extends IDisposable {
}
export interface MainThreadOutputServiceShape extends IDisposable {
$append(channelId: string, label: string, value: string): TPromise<void>;
$clear(channelId: string, label: string): TPromise<void>;
$dispose(channelId: string, label: string): TPromise<void>;
$reveal(channelId: string, label: string, preserveFocus: boolean): TPromise<void>;
$register(channelId: string, label: string, file?: UriComponents): TPromise<void>;
$append(channelId: string, value: string): TPromise<void>;
$clear(channelId: string): TPromise<void>;
$reveal(channelId: string, preserveFocus: boolean): TPromise<void>;
$close(channelId: string): TPromise<void>;
$dispose(channelId: string): TPromise<void>;
}
export interface MainThreadProgressShape extends IDisposable {
......
......@@ -6,19 +6,24 @@
import { MainContext, MainThreadOutputServiceShape, IMainContext } from './extHost.protocol';
import * as vscode from 'vscode';
import { URI } from 'vs/base/common/uri';
import { posix } from 'path';
import { OutputAppender } from 'vs/platform/output/node/outputAppender';
import { TPromise } from 'vs/base/common/winjs.base';
export class ExtHostOutputChannel implements vscode.OutputChannel {
export abstract class AbstractExtHostOutputChannel implements vscode.OutputChannel {
private static _idPool = 1;
private _proxy: MainThreadOutputServiceShape;
private _name: string;
private _id: string;
protected readonly _id: string;
private readonly _name: string;
protected readonly _proxy: MainThreadOutputServiceShape;
protected _registerationPromise: TPromise<void> = TPromise.as(null);
private _disposed: boolean;
constructor(name: string, proxy: MainThreadOutputServiceShape) {
this._id = 'extension-output-#' + (AbstractExtHostOutputChannel._idPool++);
this._name = name;
this._id = 'extension-output-#' + (ExtHostOutputChannel._idPool++);
this._proxy = proxy;
}
......@@ -26,18 +31,7 @@ export class ExtHostOutputChannel implements vscode.OutputChannel {
return this._name;
}
dispose(): void {
if (!this._disposed) {
this._proxy.$dispose(this._id, this._name).then(() => {
this._disposed = true;
});
}
}
append(value: string): void {
this.validate();
this._proxy.$append(this._id, this._name, value);
}
abstract append(value: string): void;
appendLine(value: string): void {
this.validate();
......@@ -46,44 +40,81 @@ export class ExtHostOutputChannel implements vscode.OutputChannel {
clear(): void {
this.validate();
this._proxy.$clear(this._id, this._name);
this._registerationPromise.then(() => this._proxy.$clear(this._id));
}
show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
this.validate();
if (typeof columnOrPreserveFocus === 'boolean') {
preserveFocus = columnOrPreserveFocus;
}
this._proxy.$reveal(this._id, this._name, preserveFocus);
this._registerationPromise.then(() => this._proxy.$reveal(this._id, typeof columnOrPreserveFocus === 'boolean' ? columnOrPreserveFocus : preserveFocus));
}
hide(): void {
this.validate();
this._proxy.$close(this._id);
this._registerationPromise.then(() => this._proxy.$close(this._id));
}
private validate(): void {
protected validate(): void {
if (this._disposed) {
throw new Error('Channel has been closed');
}
}
dispose(): void {
if (!this._disposed) {
this._registerationPromise
.then(() => this._proxy.$dispose(this._id))
.then(() => this._disposed = true);
}
}
}
export class ExtHostOutputChannel extends AbstractExtHostOutputChannel {
constructor(name: string, proxy: MainThreadOutputServiceShape) {
super(name, proxy);
this._registerationPromise = proxy.$register(this._id, name);
}
append(value: string): void {
this.validate();
this._registerationPromise.then(() => this._proxy.$append(this._id, value));
}
}
export class ExtHostLoggingOutputChannel extends AbstractExtHostOutputChannel {
private _appender: OutputAppender;
constructor(name: string, outputDir: string, proxy: MainThreadOutputServiceShape) {
super(name, proxy);
const file = URI.file(posix.join(outputDir, `${this._id}.log`));
this._appender = new OutputAppender(this._id, file.fsPath);
this._registerationPromise = proxy.$register(this._id, this.name, file);
}
append(value: string): void {
this.validate();
this._appender.append(value);
}
}
export class ExtHostOutputService {
private _proxy: MainThreadOutputServiceShape;
private _outputDir: string;
constructor(mainContext: IMainContext) {
constructor(outputDir: string, mainContext: IMainContext) {
this._outputDir = outputDir;
this._proxy = mainContext.getProxy(MainContext.MainThreadOutputService);
}
createOutputChannel(name: string): vscode.OutputChannel {
createOutputChannel(name: string, logging?: boolean): vscode.OutputChannel {
name = name.trim();
if (!name) {
throw new Error('illegal argument `name`. must not be falsy');
} else {
return new ExtHostOutputChannel(name, this._proxy);
return logging ? new ExtHostLoggingOutputChannel(name, this._outputDir, this._proxy) : new ExtHostOutputChannel(name, this._proxy);
}
}
}
......@@ -55,7 +55,7 @@ workbenchRegistry.registerWorkbenchContribution(KeymapExtensions, LifecyclePhase
workbenchRegistry.registerWorkbenchContribution(ExtensionsViewletViewsContribution, LifecyclePhase.Starting);
Registry.as<IOutputChannelRegistry>(OutputExtensions.OutputChannels)
.registerChannel(ExtensionsChannelId, ExtensionsLabel);
.registerChannel({ id: ExtensionsChannelId, label: ExtensionsLabel, log: false });
// Quickopen
Registry.as<IQuickOpenRegistry>(Extensions.Quickopen).registerQuickOpenHandler(
......
......@@ -29,10 +29,10 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution {
) {
super();
let outputChannelRegistry = Registry.as<IOutputChannelRegistry>(OutputExt.OutputChannels);
outputChannelRegistry.registerChannel(Constants.mainLogChannelId, nls.localize('mainLog', "Log (Main)"), URI.file(join(this.environmentService.logsPath, `main.log`)));
outputChannelRegistry.registerChannel(Constants.sharedLogChannelId, nls.localize('sharedLog', "Log (Shared)"), URI.file(join(this.environmentService.logsPath, `sharedprocess.log`)));
outputChannelRegistry.registerChannel(Constants.rendererLogChannelId, nls.localize('rendererLog', "Log (Window)"), URI.file(join(this.environmentService.logsPath, `renderer${this.windowService.getCurrentWindowId()}.log`)));
outputChannelRegistry.registerChannel(Constants.extHostLogChannelId, nls.localize('extensionsLog', "Log (Extension Host)"), URI.file(join(this.environmentService.logsPath, `exthost${this.windowService.getCurrentWindowId()}.log`)));
outputChannelRegistry.registerChannel({ id: Constants.mainLogChannelId, label: nls.localize('mainLog', "Log (Main)"), file: URI.file(join(this.environmentService.logsPath, `main.log`)), log: true });
outputChannelRegistry.registerChannel({ id: Constants.sharedLogChannelId, label: nls.localize('sharedLog', "Log (Shared)"), file: URI.file(join(this.environmentService.logsPath, `sharedprocess.log`)), log: true });
outputChannelRegistry.registerChannel({ id: Constants.rendererLogChannelId, label: nls.localize('rendererLog', "Log (Window)"), file: URI.file(join(this.environmentService.logsPath, `renderer${this.windowService.getCurrentWindowId()}.log`)), log: true });
outputChannelRegistry.registerChannel({ id: Constants.extHostLogChannelId, label: nls.localize('extensionsLog', "Log (Extension Host)"), file: URI.file(join(this.environmentService.logsPath, `exthost${this.windowService.getCurrentWindowId()}.log`)), log: true });
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions);
const devCategory = nls.localize('developer', "Developer");
......
......@@ -8,7 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base';
import * as nls from 'vs/nls';
import * as aria from 'vs/base/browser/ui/aria/aria';
import { IAction, Action } from 'vs/base/common/actions';
import { IOutputService, OUTPUT_PANEL_ID, IOutputChannelRegistry, Extensions as OutputExt, IOutputChannelIdentifier, COMMAND_OPEN_LOG_VIEWER } from 'vs/workbench/parts/output/common/output';
import { IOutputService, OUTPUT_PANEL_ID, IOutputChannelRegistry, Extensions as OutputExt, IOutputChannelDescriptor, COMMAND_OPEN_LOG_VIEWER } from 'vs/workbench/parts/output/common/output';
import { SelectActionItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { IPartService } from 'vs/workbench/services/part/common/partService';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
......@@ -20,7 +20,6 @@ import { IContextViewService } from 'vs/platform/contextview/browser/contextView
import { Registry } from 'vs/platform/registry/common/platform';
import { groupBy } from 'vs/base/common/arrays';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { URI } from 'vs/base/common/uri';
export class ToggleOutputAction extends TogglePanelAction {
......@@ -112,8 +111,8 @@ export class SwitchOutputActionItem extends SelectActionItem {
private static readonly SEPARATOR = '─────────';
private normalChannels: IOutputChannelIdentifier[];
private fileChannels: IOutputChannelIdentifier[];
private outputChannels: IOutputChannelDescriptor[];
private logChannels: IOutputChannelDescriptor[];
constructor(
action: IAction,
......@@ -133,30 +132,30 @@ export class SwitchOutputActionItem extends SelectActionItem {
}
protected getActionContext(option: string, index: number): string {
const channel = index < this.normalChannels.length ? this.normalChannels[index] : this.fileChannels[index - this.normalChannels.length - 1];
const channel = index < this.outputChannels.length ? this.outputChannels[index] : this.logChannels[index - this.outputChannels.length - 1];
return channel ? channel.id : option;
}
private updateOtions(selectedChannel: string): void {
const groups = groupBy(this.outputService.getChannels(), (c1: IOutputChannelIdentifier, c2: IOutputChannelIdentifier) => {
if (!c1.file && c2.file) {
const groups = groupBy(this.outputService.getChannelDescriptors(), (c1: IOutputChannelDescriptor, c2: IOutputChannelDescriptor) => {
if (!c1.log && c2.log) {
return -1;
}
if (c1.file && !c2.file) {
if (c1.log && !c2.log) {
return 1;
}
return 0;
});
this.normalChannels = groups[0] || [];
this.fileChannels = groups[1] || [];
const showSeparator = this.normalChannels.length && this.fileChannels.length;
const separatorIndex = showSeparator ? this.normalChannels.length : -1;
const options: string[] = [...this.normalChannels.map(c => c.label), ...(showSeparator ? [SwitchOutputActionItem.SEPARATOR] : []), ...this.fileChannels.map(c => c.label)];
this.outputChannels = groups[0] || [];
this.logChannels = groups[1] || [];
const showSeparator = this.outputChannels.length && this.logChannels.length;
const separatorIndex = showSeparator ? this.outputChannels.length : -1;
const options: string[] = [...this.outputChannels.map(c => c.label), ...(showSeparator ? [SwitchOutputActionItem.SEPARATOR] : []), ...this.logChannels.map(c => c.label)];
let selected = 0;
if (selectedChannel) {
selected = this.normalChannels.map(c => c.id).indexOf(selectedChannel);
selected = this.outputChannels.map(c => c.id).indexOf(selectedChannel);
if (selected === -1) {
selected = separatorIndex + 1 + this.fileChannels.map(c => c.id).indexOf(selectedChannel);
selected = separatorIndex + 1 + this.logChannels.map(c => c.id).indexOf(selectedChannel);
}
}
this.setOptions(options, Math.max(0, selected), separatorIndex !== -1 ? separatorIndex : void 0);
......@@ -180,17 +179,16 @@ export class OpenLogOutputFile extends Action {
}
private update(): void {
const logFile = this.getActiveLogChannelFile();
this.enabled = !!logFile;
this.enabled = this.isLogChannel();
}
public run(): TPromise<any> {
return this.commandService.executeCommand(COMMAND_OPEN_LOG_VIEWER, this.getActiveLogChannelFile());
return this.commandService.executeCommand(COMMAND_OPEN_LOG_VIEWER, this.isLogChannel());
}
private getActiveLogChannelFile(): URI {
private isLogChannel(): boolean {
const channel = this.outputService.getActiveChannel();
const identifier = channel ? this.outputService.getChannels().filter(c => c.id === channel.id)[0] : null;
return identifier ? identifier.file : null;
const descriptor = channel ? this.outputService.getChannelDescriptors().filter(c => c.id === channel.id)[0] : null;
return descriptor && descriptor.log;
}
}
......@@ -78,9 +78,9 @@ export interface IOutputService {
getChannel(id: string): IOutputChannel;
/**
* Returns an array of all known output channels as identifiers.
* Returns an array of all known output channels descriptors.
*/
getChannels(): IOutputChannelIdentifier[];
getChannelDescriptors(): IOutputChannelDescriptor[];
/**
* Returns the currently active channel.
......@@ -132,9 +132,10 @@ export interface IOutputChannel {
dispose(): void;
}
export interface IOutputChannelIdentifier {
export interface IOutputChannelDescriptor {
id: string;
label: string;
log: boolean;
file?: URI;
}
......@@ -146,17 +147,17 @@ export interface IOutputChannelRegistry {
/**
* Make an output channel known to the output world.
*/
registerChannel(id: string, name: string, file?: URI): void;
registerChannel(descriptor: IOutputChannelDescriptor): void;
/**
* Returns the list of channels known to the output world.
*/
getChannels(): IOutputChannelIdentifier[];
getChannels(): IOutputChannelDescriptor[];
/**
* Returns the channel with the passed id.
*/
getChannel(id: string): IOutputChannelIdentifier;
getChannel(id: string): IOutputChannelDescriptor;
/**
* Remove the output channel with the passed id.
......@@ -165,7 +166,7 @@ export interface IOutputChannelRegistry {
}
class OutputChannelRegistry implements IOutputChannelRegistry {
private channels = new Map<string, IOutputChannelIdentifier>();
private channels = new Map<string, IOutputChannelDescriptor>();
private readonly _onDidRegisterChannel: Emitter<string> = new Emitter<string>();
readonly onDidRegisterChannel: Event<string> = this._onDidRegisterChannel.event;
......@@ -173,20 +174,20 @@ class OutputChannelRegistry implements IOutputChannelRegistry {
private readonly _onDidRemoveChannel: Emitter<string> = new Emitter<string>();
readonly onDidRemoveChannel: Event<string> = this._onDidRemoveChannel.event;
public registerChannel(id: string, label: string, file?: URI): void {
if (!this.channels.has(id)) {
this.channels.set(id, { id, label, file });
this._onDidRegisterChannel.fire(id);
public registerChannel(descriptor: IOutputChannelDescriptor): void {
if (!this.channels.has(descriptor.id)) {
this.channels.set(descriptor.id, descriptor);
this._onDidRegisterChannel.fire(descriptor.id);
}
}
public getChannels(): IOutputChannelIdentifier[] {
const result: IOutputChannelIdentifier[] = [];
public getChannels(): IOutputChannelDescriptor[] {
const result: IOutputChannelDescriptor[] = [];
this.channels.forEach(value => result.push(value));
return result;
}
public getChannel(id: string): IOutputChannelIdentifier {
public getChannel(id: string): IOutputChannelDescriptor {
return this.channels.get(id);
}
......
......@@ -15,7 +15,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 { IOutputChannelIdentifier, 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 } 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';
......@@ -31,7 +31,6 @@ import { IFileService } from 'vs/platform/files/common/files';
import { IPanel } from 'vs/workbench/common/panel';
import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { RotatingLogger } from 'spdlog';
import { toLocalISOString } from 'vs/base/common/date';
import { IWindowService } from 'vs/platform/windows/common/windows';
import { ILogService } from 'vs/platform/log/common/log';
......@@ -39,6 +38,7 @@ import { Schemas } from 'vs/base/common/network';
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';
const OUTPUT_ACTIVE_CHANNEL_KEY = 'output.activechannel';
......@@ -83,6 +83,7 @@ abstract class AbstractFileOutputChannel extends Disposable {
protected _onDispose: Emitter<void> = new Emitter<void>();
readonly onDispose: Event<void> = this._onDispose.event;
private readonly mimeType: string;
protected modelUpdater: RunOnceScheduler;
protected model: ITextModel;
readonly file: URI;
......@@ -91,25 +92,25 @@ abstract class AbstractFileOutputChannel extends Disposable {
protected endOffset: number = 0;
constructor(
protected readonly outputChannelIdentifier: IOutputChannelIdentifier,
protected readonly outputChannelDescriptor: IOutputChannelDescriptor,
private readonly modelUri: URI,
private mimeType: string,
protected fileService: IFileService,
protected modelService: IModelService,
protected modeService: IModeService,
) {
super();
this.file = this.outputChannelIdentifier.file;
this.mimeType = outputChannelDescriptor.log ? LOG_MIME : OUTPUT_MIME;
this.file = this.outputChannelDescriptor.file;
this.modelUpdater = new RunOnceScheduler(() => this.updateModel(), 300);
this._register(toDisposable(() => this.modelUpdater.cancel()));
}
get id(): string {
return this.outputChannelIdentifier.id;
return this.outputChannelDescriptor.id;
}
get label(): string {
return this.outputChannelIdentifier.label;
return this.outputChannelDescriptor.label;
}
clear(): void {
......@@ -162,14 +163,14 @@ abstract class AbstractFileOutputChannel extends Disposable {
*/
class OutputChannelBackedByFile extends AbstractFileOutputChannel implements OutputChannel {
private outputWriter: RotatingLogger;
private appender: OutputAppender;
private appendedMessage = '';
private loadingFromFileInProgress: boolean = false;
private resettingDelayer: ThrottledDelayer<void>;
private readonly rotatingFilePath: string;
constructor(
outputChannelIdentifier: IOutputChannelIdentifier,
outputChannelDescriptor: IOutputChannelDescriptor,
outputDir: string,
modelUri: URI,
@IFileService fileService: IFileService,
......@@ -177,12 +178,11 @@ class OutputChannelBackedByFile extends AbstractFileOutputChannel implements Out
@IModeService modeService: IModeService,
@ILogService logService: ILogService
) {
super({ ...outputChannelIdentifier, file: URI.file(paths.join(outputDir, `${outputChannelIdentifier.id}.log`)) }, modelUri, OUTPUT_MIME, fileService, modelService, modeService);
super({ ...outputChannelDescriptor, file: URI.file(paths.join(outputDir, `${outputChannelDescriptor.id}.log`)) }, modelUri, fileService, modelService, modeService);
// Use one rotating file to check for main file reset
this.outputWriter = new RotatingLogger(this.id, this.file.fsPath, 1024 * 1024 * 30, 1);
this.outputWriter.clearFormatters();
this.rotatingFilePath = `${outputChannelIdentifier.id}.1.log`;
this.appender = new OutputAppender(this.id, this.file.fsPath);
this.rotatingFilePath = `${outputChannelDescriptor.id}.1.log`;
this._register(watchOutputDirectory(paths.dirname(this.file.fsPath), logService, (eventType, file) => this.onFileChangedInOutputDirector(eventType, file)));
this.resettingDelayer = new ThrottledDelayer<void>(50);
......@@ -264,11 +264,11 @@ class OutputChannelBackedByFile extends AbstractFileOutputChannel implements Out
}
private write(content: string): void {
this.outputWriter.critical(content);
this.appender.append(content);
}
private flush(): void {
this.outputWriter.flush();
this.appender.flush();
}
}
......@@ -323,14 +323,14 @@ class FileOutputChannel extends AbstractFileOutputChannel implements OutputChann
private updateInProgress: boolean = false;
constructor(
outputChannelIdentifier: IOutputChannelIdentifier,
outputChannelDescriptor: IOutputChannelDescriptor,
modelUri: URI,
@IFileService fileService: IFileService,
@IModelService modelService: IModelService,
@IModeService modeService: IModeService,
@ILogService logService: ILogService,
) {
super(outputChannelIdentifier, modelUri, LOG_MIME, fileService, modelService, modeService);
super(outputChannelDescriptor, modelUri, fileService, modelService, modeService);
this.fileHandler = this._register(new OutputFileListener(this.file, this.fileService));
this._register(this.fileHandler.onDidContentChange(() => this.onDidContentChange()));
......@@ -426,7 +426,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo
// Set active channel to first channel if not set
if (!this.activeChannel) {
const channels = this.getChannels();
const channels = this.getChannelDescriptors();
this.activeChannel = channels && channels.length > 0 ? this.getChannel(channels[0].id) : null;
}
......@@ -461,7 +461,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo
return this.channels.get(id);
}
getChannels(): IOutputChannelIdentifier[] {
getChannelDescriptors(): IOutputChannelDescriptor[] {
return Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).getChannels();
}
......@@ -521,7 +521,7 @@ export class OutputService extends Disposable implements IOutputService, ITextMo
channel.onDispose(() => {
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).removeChannel(id);
if (this.activeChannel === channel) {
const channels = this.getChannels();
const channels = this.getChannelDescriptors();
if (this.isPanelShown() && channels.length) {
this.doShowChannel(this.getChannel(channels[0].id), true);
this._onActiveOutputChannel.fire(channels[0].id);
......@@ -610,7 +610,7 @@ export class LogContentProvider {
let channel = this.channels.get(id);
if (!channel) {
const channelDisposables: IDisposable[] = [];
channel = this.instantiationService.createInstance(FileOutputChannel, { id, label: '', file: resource.with({ scheme: Schemas.file }) }, resource);
channel = this.instantiationService.createInstance(FileOutputChannel, { id, label: '', file: resource.with({ scheme: Schemas.file }), log: true }, resource);
channel.onDispose(() => dispose(channelDisposables), channelDisposables);
this.channels.set(id, channel);
}
......
......@@ -167,7 +167,7 @@ export class ViewPickerHandler extends QuickOpenHandler {
});
// Output Channels
const channels = this.outputService.getChannels();
const channels = this.outputService.getChannelDescriptors();
channels.forEach((channel, index) => {
const outputCategory = nls.localize('channels', "Output");
const entry = new ViewEntry(channel.label, outputCategory, () => this.outputService.showChannel(channel.id));
......
......@@ -2503,7 +2503,7 @@ MenuRegistry.addCommand({ id: 'workbench.action.tasks.configureDefaultTestTask',
// Tasks Output channel. Register it before using it in Task Service.
let outputChannelRegistry = Registry.as<IOutputChannelRegistry>(OutputExt.OutputChannels);
outputChannelRegistry.registerChannel(TaskService.OutputChannelId, TaskService.OutputChannelLabel);
outputChannelRegistry.registerChannel({ id: TaskService.OutputChannelId, label: TaskService.OutputChannelLabel, log: false });
// Task Service
registerSingleton(ITaskService, TaskService);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册