Support log output channels for extensions (#161249)

* introduce log api in extension context

* separate registering output vs log channel

* Separate extension log channels in show logs command

* add logging error to embedder logger

* show extension log in the extension editor

* configure log level per extension

* change the order of log entries

* introduce logger

* align with output chanel

* revert changes

* fixes
This commit is contained in:
Sandeep Somavarapu 2022-09-19 18:03:41 +02:00 committed by GitHub
parent 82431003f3
commit 35c7ee9d02
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 333 additions and 157 deletions

View file

@ -48,6 +48,9 @@
}
}
},
"enabledApiProposals": [
"extensionLog"
],
"aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255",
"main": "./out/extension.js",
"browser": "./dist/browser/extension.js",

View file

@ -6,53 +6,24 @@
import * as vscode from 'vscode';
import { AuthProviderType } from '../github';
type LogLevel = 'Trace' | 'Info' | 'Error';
export class Log {
private output: vscode.OutputChannel;
private output: vscode.LogOutputChannel;
constructor(private readonly type: AuthProviderType) {
const friendlyName = this.type === AuthProviderType.github ? 'GitHub' : 'GitHub Enterprise';
this.output = vscode.window.createOutputChannel(`${friendlyName} Authentication`);
this.output = vscode.window.createOutputChannel(`${friendlyName} Authentication`, { log: true });
}
private data2String(data: any): string {
if (data instanceof Error) {
return data.stack || data.message;
}
if (data.success === false && data.message) {
return data.message;
}
return data.toString();
public trace(message: string): void {
this.output.trace(message);
}
public trace(message: string, data?: any): void {
this.logLevel('Trace', message, data);
public info(message: string): void {
this.output.info(message);
}
public info(message: string, data?: any): void {
this.logLevel('Info', message, data);
public error(message: string): void {
this.output.error(message);
}
public error(message: string, data?: any): void {
this.logLevel('Error', message, data);
}
public logLevel(level: LogLevel, message: string, data?: any): void {
this.output.appendLine(`[${level} - ${this.now()}] ${message}`);
if (data) {
this.output.appendLine(this.data2String(data));
}
}
private now(): string {
const now = new Date();
return padLeft(now.getUTCHours() + '', 2, '0')
+ ':' + padLeft(now.getMinutes() + '', 2, '0')
+ ':' + padLeft(now.getUTCSeconds() + '', 2, '0') + '.' + now.getMilliseconds();
}
}
function padLeft(s: string, n: number, pad = ' ') {
return pad.repeat(Math.max(0, n - s.length)) + s;
}

View file

@ -12,6 +12,7 @@
},
"include": [
"src/**/*",
"../../src/vscode-dts/vscode.d.ts"
"../../src/vscode-dts/vscode.d.ts",
"../../src/vscode-dts/vscode.proposed.extensionLog.d.ts"
]
}

View file

@ -16,7 +16,8 @@
"onAuthenticationRequest:microsoft"
],
"enabledApiProposals": [
"idToken"
"idToken",
"extensionLog"
],
"capabilities": {
"virtualWorkspaces": true,

View file

@ -5,55 +5,5 @@
import * as vscode from 'vscode';
type LogLevel = 'Trace' | 'Info' | 'Error';
class Log {
private output: vscode.OutputChannel;
constructor() {
this.output = vscode.window.createOutputChannel('Microsoft Authentication');
}
private data2String(data: any): string {
if (data instanceof Error) {
return data.stack || data.message;
}
if (data.success === false && data.message) {
return data.message;
}
return data.toString();
}
public trace(message: string, data?: any): void {
this.logLevel('Trace', message, data);
}
public info(message: string, data?: any): void {
this.logLevel('Info', message, data);
}
public error(message: string, data?: any): void {
this.logLevel('Error', message, data);
}
public logLevel(level: LogLevel, message: string, data?: any): void {
this.output.appendLine(`[${level} - ${this.now()}] ${message}`);
if (data) {
this.output.appendLine(this.data2String(data));
}
}
private now(): string {
const now = new Date();
return padLeft(now.getUTCHours() + '', 2, '0')
+ ':' + padLeft(now.getMinutes() + '', 2, '0')
+ ':' + padLeft(now.getUTCSeconds() + '', 2, '0') + '.' + now.getMilliseconds();
}
}
function padLeft(s: string, n: number, pad = ' ') {
return pad.repeat(Math.max(0, n - s.length)) + s;
}
const Logger = new Log();
const Logger = vscode.window.createOutputChannel('Microsoft Authentication', { log: true });
export default Logger;

View file

@ -22,6 +22,7 @@
"include": [
"src/**/*",
"../../src/vscode-dts/vscode.d.ts",
"../../src/vscode-dts/vscode.proposed.idToken.d.ts"
"../../src/vscode-dts/vscode.proposed.idToken.d.ts",
"../../src/vscode-dts/vscode.proposed.extensionLog.d.ts"
]
}

View file

@ -41,12 +41,12 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut
setVisibleChannel();
}
public async $register(label: string, log: boolean, file: UriComponents, languageId: string, extensionId: string): Promise<string> {
public async $register(label: string, file: UriComponents, log: boolean, languageId: string | undefined, extensionId: string): Promise<string> {
const idCounter = (MainThreadOutputService._extensionIdPool.get(extensionId) || 0) + 1;
MainThreadOutputService._extensionIdPool.set(extensionId, idCounter);
const id = `extension-output-${extensionId}-#${idCounter}-${label}`;
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).registerChannel({ id, label, file: URI.revive(file), log, languageId });
Registry.as<IOutputChannelRegistry>(Extensions.OutputChannels).registerChannel({ id, label, file: URI.revive(file), log, languageId, extensionId });
this._register(toDisposable(() => this.$dispose(id)));
return id;
}

View file

@ -703,8 +703,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
withProgress<R>(options: vscode.ProgressOptions, task: (progress: vscode.Progress<{ message?: string; worked?: number }>, token: vscode.CancellationToken) => Thenable<R>) {
return extHostProgress.withProgress(extension, options, task);
},
createOutputChannel(name: string, languageId?: string): vscode.OutputChannel {
return extHostOutputService.createOutputChannel(name, languageId, extension);
createOutputChannel(name: string, options: string | { log: true } | undefined): any {
return extHostOutputService.createOutputChannel(name, options, extension);
},
createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn; preserveFocus?: boolean }, options?: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel {
return extHostWebviewPanels.createWebviewPanel(extension, viewType, title, showOptions, options);

View file

@ -418,7 +418,7 @@ export interface MainThreadMessageServiceShape extends IDisposable {
}
export interface MainThreadOutputServiceShape extends IDisposable {
$register(label: string, log: boolean, file: UriComponents, languageId: string | undefined, extensionId: string): Promise<string>;
$register(label: string, file: UriComponents, log: boolean, languageId: string | undefined, extensionId: string): Promise<string>;
$update(channelId: string, mode: OutputChannelUpdateMode, till?: number): Promise<void>;
$reveal(channelId: string, preserveFocus: boolean): Promise<void>;
$close(channelId: string): Promise<void>;

View file

@ -6,11 +6,10 @@
import { MainContext, MainThreadOutputServiceShape, ExtHostOutputServiceShape } from './extHost.protocol';
import type * as vscode from 'vscode';
import { URI } from 'vs/base/common/uri';
import { Disposable } from 'vs/base/common/lifecycle';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { ILogger, ILoggerService } from 'vs/platform/log/common/log';
import { AbstractMessageLogger, ILogger, ILoggerService, log, LogLevel } from 'vs/platform/log/common/log';
import { OutputChannelUpdateMode } from 'vs/workbench/services/output/common/output';
import { IExtHostConsumerFileSystem } from 'vs/workbench/api/common/extHostFileSystemConsumer';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
@ -19,23 +18,63 @@ import { toLocalISOString } from 'vs/base/common/date';
import { VSBuffer } from 'vs/base/common/buffer';
import { isString } from 'vs/base/common/types';
export class ExtHostOutputChannel extends Disposable implements vscode.OutputChannel {
private offset: number = 0;
public visible: boolean = false;
export class ExtHostLogOutputChannel extends AbstractMessageLogger implements vscode.LogOutputChannel {
private _disposed: boolean = false;
get disposed(): boolean { return this._disposed; }
public visible: boolean = false;
constructor(
readonly id: string, readonly name: string,
private readonly logger: ILogger,
private readonly proxy: MainThreadOutputServiceShape
protected readonly logger: ILogger,
protected readonly proxy: MainThreadOutputServiceShape,
readonly extension: IExtensionDescription,
) {
super();
}
appendLine(value: string): void {
this.info(value);
}
show(preserveFocus?: boolean): void {
this.proxy.$reveal(this.id, !!preserveFocus);
}
hide(): void {
this.proxy.$close(this.id);
}
protected log(level: LogLevel, message: string): void {
log(this.logger, level, message);
}
override dispose(): void {
super.dispose();
if (!this._disposed) {
this.proxy.$dispose(this.id);
this._disposed = true;
}
}
}
export class ExtHostOutputChannel extends ExtHostLogOutputChannel implements vscode.OutputChannel {
private offset: number = 0;
constructor(
id: string, name: string,
logger: ILogger,
proxy: MainThreadOutputServiceShape,
extension: IExtensionDescription
) {
super(id, name, logger, proxy, extension);
}
override appendLine(value: string): void {
this.append(value + '\n');
}
@ -62,13 +101,9 @@ export class ExtHostOutputChannel extends Disposable implements vscode.OutputCha
}
}
show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
override show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
this.logger.flush();
this.proxy.$reveal(this.id, !!(typeof columnOrPreserveFocus === 'boolean' ? columnOrPreserveFocus : preserveFocus));
}
hide(): void {
this.proxy.$close(this.id);
super.show(!!(typeof columnOrPreserveFocus === 'boolean' ? columnOrPreserveFocus : preserveFocus));
}
private write(value: string): void {
@ -76,15 +111,6 @@ export class ExtHostOutputChannel extends Disposable implements vscode.OutputCha
this.logger.info(value);
}
override dispose(): void {
super.dispose();
if (!this._disposed) {
this.proxy.$dispose(this.id);
this._disposed = true;
}
}
}
export class ExtHostOutputService implements ExtHostOutputServiceShape {
@ -97,7 +123,7 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape {
private outputDirectoryPromise: Thenable<URI> | undefined;
private namePool: number = 1;
private readonly channels: Map<string, ExtHostOutputChannel> = new Map<string, ExtHostOutputChannel>();
private readonly channels = new Map<string, ExtHostLogOutputChannel | ExtHostOutputChannel>();
private visibleChannelId: string | null = null;
constructor(
@ -118,35 +144,44 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape {
}
}
createOutputChannel(name: string, languageId: string | undefined, extension: IExtensionDescription): vscode.OutputChannel {
createOutputChannel(name: string, options: string | { log: true } | undefined, extension: IExtensionDescription): vscode.OutputChannel | vscode.LogOutputChannel {
name = name.trim();
if (!name) {
throw new Error('illegal argument `name`. must not be falsy');
}
const log = typeof options === 'object' && options.log;
const languageId = isString(options) ? options : undefined;
if (isString(languageId) && !languageId.trim()) {
throw new Error('illegal argument `languageId`. must not be empty');
}
const extHostOutputChannel = this.doCreateOutputChannel(name, languageId, extension);
const extHostOutputChannel = log ? this.doCreateLogOutputChannel(name, extension) : this.doCreateOutputChannel(name, languageId, extension);
extHostOutputChannel.then(channel => {
this.channels.set(channel.id, channel);
channel.visible = channel.id === this.visibleChannelId;
});
return this.createExtHostOutputChannel(name, extHostOutputChannel);
return log ? this.createExtHostLogOutputChannel(name, <Promise<ExtHostOutputChannel>>extHostOutputChannel) : this.createExtHostOutputChannel(name, <Promise<ExtHostOutputChannel>>extHostOutputChannel);
}
private async doCreateOutputChannel(name: string, languageId: string | undefined, extension: IExtensionDescription): Promise<ExtHostOutputChannel> {
const outputDir = await this.createOutputDirectory();
const file = this.extHostFileSystemInfo.extUri.joinPath(outputDir, `${this.namePool++}-${name.replace(/[\\/:\*\?"<>\|]/g, '')}.log`);
const file = await this.createLogFile(name);
const logger = this.loggerService.createLogger(file, { always: true, donotRotate: true, donotUseFormatters: true });
const id = await this.proxy.$register(name, false, file, languageId, extension.identifier.value);
return new ExtHostOutputChannel(id, name, logger, this.proxy);
const id = await this.proxy.$register(name, file, false, languageId, extension.identifier.value);
return new ExtHostOutputChannel(id, name, logger, this.proxy, extension);
}
private createOutputDirectory(): Thenable<URI> {
private async doCreateLogOutputChannel(name: string, extension: IExtensionDescription): Promise<ExtHostLogOutputChannel> {
const file = await this.createLogFile(name);
const logger = this.loggerService.createLogger(file, { name });
const id = await this.proxy.$register(name, file, true, undefined, extension.identifier.value);
return new ExtHostLogOutputChannel(id, name, logger, this.proxy, extension);
}
private async createLogFile(name: string): Promise<URI> {
if (!this.outputDirectoryPromise) {
this.outputDirectoryPromise = this.extHostFileSystem.value.createDirectory(this.outputsLocation).then(() => this.outputsLocation);
}
return this.outputDirectoryPromise;
const outputDir = await this.outputDirectoryPromise;
return this.extHostFileSystemInfo.extUri.joinPath(outputDir, `${this.namePool++}-${name.replace(/[\\/:\*\?"<>\|]/g, '')}.log`);
}
private createExtHostOutputChannel(name: string, channelPromise: Promise<ExtHostOutputChannel>): vscode.OutputChannel {
@ -188,6 +223,54 @@ export class ExtHostOutputService implements ExtHostOutputServiceShape {
}
};
}
private createExtHostLogOutputChannel(name: string, channelPromise: Promise<ExtHostOutputChannel>): vscode.LogOutputChannel {
let disposed = false;
const validate = () => {
if (disposed) {
throw new Error('Channel has been closed');
}
};
return {
get name(): string { return name; },
appendLine(value: string): void {
validate();
channelPromise.then(channel => channel.appendLine(value));
},
trace(value: string): void {
validate();
channelPromise.then(channel => channel.info(value));
},
debug(value: string): void {
validate();
channelPromise.then(channel => channel.debug(value));
},
info(value: string): void {
validate();
channelPromise.then(channel => channel.info(value));
},
warn(value: string): void {
validate();
channelPromise.then(channel => channel.warn(value));
},
error(value: Error | string): void {
validate();
channelPromise.then(channel => channel.error(value));
},
show(preserveFocus?: boolean): void {
validate();
channelPromise.then(channel => channel.show(preserveFocus));
},
hide(): void {
validate();
channelPromise.then(channel => channel.hide());
},
dispose(): void {
disposed = true;
channelPromise.then(channel => channel.dispose());
}
};
}
}
export interface IExtHostOutputService extends ExtHostOutputService { }

View file

@ -24,7 +24,7 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { ViewContainer, IViewContainersRegistry, ViewContainerLocation, Extensions as ViewContainerExtensions, IViewsRegistry, IViewsService } from 'vs/workbench/common/views';
import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer';
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry';
import { IQuickPickItem, IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IQuickPickItem, IQuickInputService, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { assertIsDefined } from 'vs/base/common/types';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
@ -231,9 +231,26 @@ registerAction2(class extends Action2 {
async run(accessor: ServicesAccessor): Promise<void> {
const outputService = accessor.get(IOutputService);
const quickInputService = accessor.get(IQuickInputService);
const entries: { id: string; label: string }[] = outputService.getChannelDescriptors().filter(c => c.file && c.log)
.map(({ id, label }) => ({ id, label }));
const extensionLogs = [], logs = [];
for (const channel of outputService.getChannelDescriptors()) {
if (channel.log) {
if (channel.extensionId) {
extensionLogs.push(channel);
} else {
logs.push(channel);
}
}
}
const entries: ({ id: string; label: string } | IQuickPickSeparator)[] = [];
for (const { id, label } of logs) {
entries.push({ id, label });
}
if (extensionLogs.length && logs.length) {
entries.push({ type: 'separator', label: nls.localize('extensionLogs', "Extension Logs") });
}
for (const { id, label } of extensionLogs) {
entries.push({ id, label });
}
const entry = await quickInputService.pick(entries, { placeHolder: nls.localize('selectlog', "Select Log") });
if (entry) {
return outputService.showChannel(entry.id);

View file

@ -30,7 +30,6 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
import { Registry } from 'vs/platform/registry/common/platform';
import { attachSelectBoxStyler, attachStylerCallback } from 'vs/platform/theme/common/styler';
import { ISelectOptionItem } from 'vs/base/browser/ui/selectBox/selectBox';
import { groupBy } from 'vs/base/common/arrays';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
import { editorBackground, selectBorder } from 'vs/platform/theme/common/colorRegistry';
import { SelectActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems';
@ -271,12 +270,13 @@ export class OutputEditor extends AbstractTextResourceEditor {
}
}
type OutputChannelSelectionOptionItem = ISelectOptionItem & { readonly channel?: IOutputChannelDescriptor };
class SwitchOutputActionViewItem extends SelectActionViewItem {
private static readonly SEPARATOR = '\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500';
private outputChannels: IOutputChannelDescriptor[] = [];
private logChannels: IOutputChannelDescriptor[] = [];
private selectionOptionItems: OutputChannelSelectionOptionItem[] = [];
constructor(
action: IAction,
@ -304,35 +304,48 @@ class SwitchOutputActionViewItem extends SelectActionViewItem {
}
protected override getActionContext(option: string, index: number): string {
const channel = index < this.outputChannels.length ? this.outputChannels[index] : this.logChannels[index - this.outputChannels.length - 1];
return channel ? channel.id : option;
return this.selectionOptionItems[index]?.channel?.id ?? option;
}
private updateOptions(): void {
const groups = groupBy(this.outputService.getChannelDescriptors(), (c1: IOutputChannelDescriptor, c2: IOutputChannelDescriptor) => {
if (!c1.log && c2.log) {
return -1;
const outputChannels = [];
const logChannels = [];
const extensionLogChannels = [];
this.selectionOptionItems = [];
for (const descriptor of this.outputService.getChannelDescriptors()) {
if (descriptor.log) {
if (descriptor.extensionId) {
extensionLogChannels.push(descriptor);
} else {
logChannels.push(descriptor);
}
} else {
outputChannels.push(descriptor);
}
if (c1.log && !c2.log) {
return 1;
}
return 0;
});
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 ? [SwitchOutputActionViewItem.SEPARATOR] : []), ...this.logChannels.map(c => nls.localize('logChannel', "Log ({0})", c.label))];
}
for (const descriptor of outputChannels) {
this.selectionOptionItems.push({ text: descriptor.label, isDisabled: false, channel: descriptor });
}
if (outputChannels.length && logChannels.length) {
this.selectionOptionItems.push({ text: SwitchOutputActionViewItem.SEPARATOR, isDisabled: true });
}
for (const descriptor of logChannels) {
this.selectionOptionItems.push({ text: nls.localize('logChannel', "Log ({0})", descriptor.label), isDisabled: false, channel: descriptor });
}
if (logChannels.length && extensionLogChannels.length) {
this.selectionOptionItems.push({ text: SwitchOutputActionViewItem.SEPARATOR, isDisabled: true });
}
for (const descriptor of extensionLogChannels) {
this.selectionOptionItems.push({ text: nls.localize('logChannel', "Log ({0})", descriptor.label), isDisabled: false, channel: descriptor });
}
let selected = 0;
const activeChannel = this.outputService.getActiveChannel();
if (activeChannel) {
selected = this.outputChannels.map(c => c.id).indexOf(activeChannel.id);
if (selected === -1) {
const logChannelIndex = this.logChannels.map(c => c.id).indexOf(activeChannel.id);
selected = logChannelIndex !== -1 ? separatorIndex + 1 + logChannelIndex : 0;
}
selected = this.selectionOptionItems.findIndex(item => item.channel?.id === activeChannel.id);
}
this.setOptions(options.map((label, index) => <ISelectOptionItem>{ text: label, isDisabled: (index === separatorIndex ? true : false) }), Math.max(0, selected));
this.setOptions(this.selectionOptionItems, Math.max(0, selected));
}
}

View file

@ -26,6 +26,7 @@ export const allApiProposals = Object.freeze({
editSessionIdentityProvider: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.editSessionIdentityProvider.d.ts',
editorInsets: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.editorInsets.d.ts',
envShellEvent: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.envShellEvent.d.ts',
extensionLog: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.extensionLog.d.ts',
extensionRuntime: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.extensionRuntime.d.ts',
extensionsAny: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.extensionsAny.d.ts',
externalUriOpener: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.externalUriOpener.d.ts',

View file

@ -160,6 +160,7 @@ export interface IOutputChannelDescriptor {
log: boolean;
languageId?: string;
file?: URI;
extensionId?: string;
}
export interface IFileOutputChannelDescriptor extends IOutputChannelDescriptor {

View file

@ -0,0 +1,134 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
declare module 'vscode' {
export interface AbstractOutputChannel {
/**
* The human-readable name of this output channel.
*/
readonly name: string;
/**
* Append the given value and a line feed character
* to the channel.
*
* @param value A string, falsy values will be printed.
*/
appendLine(value: string): void;
/**
* Reveal this channel in the UI.
*
* @param preserveFocus When `true` the channel will not take focus.
*/
show(preserveFocus?: boolean): void;
/**
* Hide this channel from the UI.
*/
hide(): void;
/**
* Dispose and free associated resources.
*/
dispose(): void;
}
/**
* An output channel is a container for readonly textual information.
*
* To get an instance of an `OutputChannel` use
* {@link window.createOutputChannel createOutputChannel}.
*/
export interface OutputChannel extends AbstractOutputChannel {
/**
* Append the given value to the channel.
*
* @param value A string, falsy values will not be printed.
*/
append(value: string): void;
/**
* Replaces all output from the channel with the given value.
*
* @param value A string, falsy values will not be printed.
*/
replace(value: string): void;
/**
* Removes all output from the channel.
*/
clear(): void;
/**
* Reveal this channel in the UI.
*
* @deprecated Use the overload with just one parameter (`show(preserveFocus?: boolean): void`).
*
* @param column This argument is **deprecated** and will be ignored.
* @param preserveFocus When `true` the channel will not take focus.
*/
show(column?: ViewColumn, preserveFocus?: boolean): void;
}
/**
* A channel for containing log output.
*/
export interface LogOutputChannel extends AbstractOutputChannel {
/**
* Log the given trace message to the channel.
*
* Messages are only printed when the user has enabled trace logging for the extension.
*
* @param message trace message to log
*/
trace(message: string): void;
/**
* Log the given debug message to the channel.
*
* Messages are only printed when the user has enabled debug logging for the extension.
*
* @param message debug message to log
*/
debug(message: string): void;
/**
* Log the given info message to the channel.
*
* Messages are only printed when the user has enabled info logging for the extension.
*
* @param message info message to log
*/
info(message: string): void;
/**
* Log the given warning message to the channel.
*
* Messages are only printed when the user has enabled warn logging for the extension.
*
* @param message warning message to log
*/
warn(message: string): void;
/**
* Log the given error or error message to the channel.
*
* Messages are only printed when the user has enabled error logging for the extension.
*
* @param error Error or error message to log
*/
error(error: string | Error): void;
}
export namespace window {
/**
* Creates a new {@link LogOutputChannel log output channel} with the given name.
*
* @param name Human-readable string which will be used to represent the channel in the UI.
* @param options Options for the log output channel.
*/
export function createOutputChannel(name: string, options: { readonly log: true }): LogOutputChannel;
}
}