Skip to content

Commit

Permalink
plugin: Support LogOutputChannel (#12429)
Browse files Browse the repository at this point in the history
- Support LogOutputChannel
- Support LogLevel
- Support in namespace/env: logLevel & onDidChangeLogLevel
- Remark: Needs further extension of application to fully support all aspects of a LogOutputViewChannel (i.e. developer logger service, logging to file, extension of output view UI)

Resolves #12017

Contributed on behalf of STMicroelectronics.

Signed-off-by: Nina Doschek <ndoschek@eclipsesource.com>
  • Loading branch information
ndoschek authored Apr 21, 2023
1 parent 0e0e9ee commit 17e5269
Show file tree
Hide file tree
Showing 9 changed files with 264 additions and 45 deletions.
7 changes: 6 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
## History

- [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/)
## v1.37.0 0 -

## v1.37.0 -

- [plugin] implemented the VS Code `LogOutputChannel` API [#12017](https://github.com/eclipse-theia/theia/pull/12429) - Contributed on behalf of STMicroelectronics

<a name="breaking_changes_1.37.0">[Breaking Changes:](#breaking_changes_1.37.0)</a>
- [core] Inject core preference into `DockPanelRenderer` constructor [12360](https://github.com/eclipse-theia/theia/pull/12360)
- [core] Introduced `ScrollableTabBar.updateTabs()` to fully render tabs [12360](https://github.com/eclipse-theia/theia/pull/12360)
- [plugin] `plugin/src/theia-proposed.d.ts`: removed enum `LogLevel` and namespace `env` [#12017](https://github.com/eclipse-theia/theia/pull/12429)
- [plugin-ext] `output-channel-item.ts`: changed visibility from `private` to `protected` for member `proxy` and function `validate()` [#12017](https://github.com/eclipse-theia/theia/pull/12429)

## v1.36.0 0 - 03/30/2023

Expand Down
3 changes: 2 additions & 1 deletion packages/plugin-ext/src/common/plugin-api-rpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -287,7 +287,8 @@ export interface TerminalServiceExt {
getEnvironmentVariableCollection(extensionIdentifier: string): theia.EnvironmentVariableCollection;
}
export interface OutputChannelRegistryExt {
createOutputChannel(name: string, pluginInfo: PluginInfo): theia.OutputChannel
createOutputChannel(name: string, pluginInfo: PluginInfo): theia.OutputChannel,
createOutputChannel(name: string, pluginInfo: PluginInfo, options: { log: true }): theia.LogOutputChannel
}

export interface ConnectionMain {
Expand Down
27 changes: 20 additions & 7 deletions packages/plugin-ext/src/plugin/output-channel-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// *****************************************************************************
import {
PLUGIN_RPC_CONTEXT as Ext, OutputChannelRegistryMain, PluginInfo, OutputChannelRegistryExt
} from '../common/plugin-api-rpc';
import { RPCProtocol } from '../common/rpc-protocol';

import * as theia from '@theia/plugin';
import { PLUGIN_RPC_CONTEXT as Ext, OutputChannelRegistryExt, OutputChannelRegistryMain, PluginInfo } from '../common/plugin-api-rpc';
import { RPCProtocol } from '../common/rpc-protocol';
import { isObject } from '../common/types';
import { LogOutputChannelImpl } from './output-channel/log-output-channel';
import { OutputChannelImpl } from './output-channel/output-channel-item';

export class OutputChannelRegistryExtImpl implements OutputChannelRegistryExt {
Expand All @@ -28,12 +29,24 @@ export class OutputChannelRegistryExtImpl implements OutputChannelRegistryExt {
this.proxy = rpc.getProxy(Ext.OUTPUT_CHANNEL_REGISTRY_MAIN);
}

createOutputChannel(name: string, pluginInfo: PluginInfo): theia.OutputChannel {
createOutputChannel(name: string, pluginInfo: PluginInfo): theia.OutputChannel;
createOutputChannel(name: string, pluginInfo: PluginInfo, options: { log: true; }): theia.LogOutputChannel;
createOutputChannel(name: string, pluginInfo: PluginInfo, options?: { log: true; }): theia.OutputChannel | theia.LogOutputChannel {
name = name.trim();
if (!name) {
throw new Error('illegal argument \'name\'. must not be falsy');
} else {
return new OutputChannelImpl(name, this.proxy, pluginInfo);
}
const isLogOutput = options && isObject(options);
return isLogOutput
? this.doCreateLogOutputChannel(name, pluginInfo)
: this.doCreateOutputChannel(name, pluginInfo);
}

private doCreateOutputChannel(name: string, pluginInfo: PluginInfo): OutputChannelImpl {
return new OutputChannelImpl(name, this.proxy, pluginInfo);
}

private doCreateLogOutputChannel(name: string, pluginInfo: PluginInfo): LogOutputChannelImpl {
return new LogOutputChannelImpl(name, this.proxy, pluginInfo);
}
}
108 changes: 108 additions & 0 deletions packages/plugin-ext/src/plugin/output-channel/log-output-channel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// *****************************************************************************
// Copyright (C) 2023 STMicroelectronics and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// *****************************************************************************
/* eslint-disable @typescript-eslint/no-explicit-any */

import { Emitter } from '@theia/core/shared/vscode-languageserver-protocol';
import * as theia from '@theia/plugin';

import { OutputChannelRegistryMain, PluginInfo } from '../../common/plugin-api-rpc';
import { OutputChannelImpl } from './output-channel-item';
import { LogLevel } from '../types-impl';
import { isArray, isObject } from '@theia/core';

export class LogOutputChannelImpl extends OutputChannelImpl implements theia.LogOutputChannel {

readonly onDidChangeLogLevelEmitter: Emitter<theia.LogLevel> = new Emitter<theia.LogLevel>();
readonly onDidChangeLogLevel: theia.Event<theia.LogLevel> = this.onDidChangeLogLevelEmitter.event;
public logLevel: theia.LogLevel;

constructor(name: string, proxy: OutputChannelRegistryMain, pluginInfo: PluginInfo) {
super(name, proxy, pluginInfo);
this.setLogLevel(LogLevel.Info);
}

setLogLevel(level: theia.LogLevel): void {
if (this.logLevel !== level) {
this.logLevel = level;
this.onDidChangeLogLevelEmitter.fire(this.logLevel);
}
}

getLogLevel(): theia.LogLevel {
return this.logLevel;
}

override append(value: string): void {
super.validate();
this.info(value);
}

override appendLine(value: string): void {
super.validate();
this.append(value + '\n');
}

override dispose(): void {
super.dispose();
this.onDidChangeLogLevelEmitter.dispose();
}

protected log(level: theia.LogLevel, message: string): void {
super.validate();
if (this.checkLogLevel(level)) {
const now = new Date();
const eol = message.endsWith('\n') ? '' : '\n';
const logMessage = `${now.toISOString()} [${LogLevel[level]}] ${message}${eol}`;
this.proxy.$append(this.name, logMessage, this.pluginInfo);
}
}

private checkLogLevel(level: theia.LogLevel): boolean {
return this.logLevel <= level;
}

trace(message: string, ...args: any[]): void {
this.log(LogLevel.Trace, this.format(message, args));
}

debug(message: string, ...args: any[]): void {
this.log(LogLevel.Debug, this.format(message, args));
}

info(message: string, ...args: any[]): void {
this.log(LogLevel.Info, this.format(message, args));
}

warn(message: string, ...args: any[]): void {
this.log(LogLevel.Warning, this.format(message, args));
}

error(errorMsg: string | Error, ...args: any[]): void {
if (errorMsg instanceof Error) {
this.log(LogLevel.Error, this.format(errorMsg.stack || errorMsg.message, args));
} else {
this.log(LogLevel.Error, this.format(errorMsg, args));
}
}

private format(message: string, args: any[]): string {
if (args.length > 0) {
return `${message} ${args.map((arg: any) => isObject(arg) || isArray(arg) ? JSON.stringify(arg) : arg).join(' ')}`;
}
return message;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export class OutputChannelImpl implements theia.OutputChannel {

private disposed: boolean;

constructor(readonly name: string, private proxy: OutputChannelRegistryMain, private readonly pluginInfo: PluginInfo) {
constructor(readonly name: string, protected readonly proxy: OutputChannelRegistryMain, protected readonly pluginInfo: PluginInfo) {
}

dispose(): void {
Expand Down Expand Up @@ -65,7 +65,7 @@ export class OutputChannelImpl implements theia.OutputChannel {
this.proxy.$close(this.name);
}

private validate(): void {
protected validate(): void {
if (this.disposed) {
throw new Error('Channel has been closed');
}
Expand Down
6 changes: 4 additions & 2 deletions packages/plugin-ext/src/plugin/plugin-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -483,8 +483,10 @@ export function createAPIFactory(

return statusBarMessageRegistryExt.createStatusBarItem(alignment, priority, id);
},
createOutputChannel(name: string): theia.OutputChannel {
return outputChannelRegistryExt.createOutputChannel(name, pluginToPluginInfo(plugin));
createOutputChannel(name: string, options?: { log: true }): any {
return !options
? outputChannelRegistryExt.createOutputChannel(name, pluginToPluginInfo(plugin))
: outputChannelRegistryExt.createOutputChannel(name, pluginToPluginInfo(plugin), options);
},
createWebviewPanel(viewType: string,
title: string,
Expand Down
5 changes: 2 additions & 3 deletions packages/plugin-ext/src/plugin/types-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2734,13 +2734,12 @@ export namespace DebugAdapterInlineImplementation {
export type DebugAdapterDescriptor = DebugAdapterExecutable | DebugAdapterServer | DebugAdapterNamedPipeServer | DebugAdapterInlineImplementation;

export enum LogLevel {
Off = 0,
Trace = 1,
Debug = 2,
Info = 3,
Warning = 4,
Error = 5,
Critical = 6,
Off = 7
Error = 5
}

/**
Expand Down
29 changes: 0 additions & 29 deletions packages/plugin/src/theia-proposed.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,35 +168,6 @@ export module '@theia/plugin' {
color?: ThemeColor;
}

// #region LogLevel: https://github.com/microsoft/vscode/issues/85992

/**
* The severity level of a log message
*/
export enum LogLevel {
Trace = 1,
Debug = 2,
Info = 3,
Warning = 4,
Error = 5,
Critical = 6,
Off = 7
}

export namespace env {
/**
* Current logging level.
*/
export const logLevel: LogLevel;

/**
* An [event](#Event) that fires when the log level has changed.
*/
export const onDidChangeLogLevel: Event<LogLevel>;
}

// #endregion

// #region search in workspace
/**
* The parameters of a query for text search.
Expand Down
Loading

0 comments on commit 17e5269

Please sign in to comment.