Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

plugin: Support LogOutputChannel #12429

Merged
merged 4 commits into from
Apr 21, 2023

Conversation

ndoschek
Copy link
Contributor

@ndoschek ndoschek commented Apr 18, 2023

What it does

  • Support LogOutputChannel
  • Support LogLevel
  • Support in namespace/env: logLevel & onDidChangeLogLevel

Resolves #12017

Contributed on behalf of STMicroelectronics.

Remark:

This PR contributes the basic VS Code API support for LogOutputChannels, to fully support all aspects of a LogOutputViewChannel as in VS Code some additional features will be needed:

  • In VSCode there is support to set the "Developer log level" in the application which also configures the log level for LogOutputviewChannels. Currently the log level for LogOutputChannels is set to the default log level, i.e. info.
  • VSCode also writes the logs to a separate file per extension and offers a command and tool in the output view to directly open the extensions's log file.

I suggest to extract those additional features into separate follow-up issues if you agree.

[Edit 2023-04-21]: Extracted to follow-up issue #12440

How to test

To test, I created a simple extension that creates a LogOutputChannel and contributes commands to send log messages in different levels:
[Edit 2023-04-21: updated VSIX test extension] vscode-extension-log-output-channel-12017-0.0.1.zip

  1. Download and install the test extension vscode-extension-log-output-channel-12017-0.0.1.vsix in your Theia test workspace (via Extensions view - top menu bar More actions... - Install from VSIX).
  2. On activation of the extension, the log output channel LogOutputChannel-Test-12017 is shown in the output view and the initial message (LogLevel set to: Info) should be shown.
  3. Execute the commands (Log a <loglevel> message via LogOutputChannel (with args) / Throw an ERROR and log msg/stack via LogOutputChannel / Test replace LogOutputChannel / Append text content (as new line) to LogOutputChannel) via the command palette and check for the messages in the output channel.
    Remark: Currently the loglevel is set to default INFO log level.

Review checklist

Reminder for reviewers

Copy link
Contributor

@kittaakos kittaakos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works great 👍 I even verified with an Error after modifying your test VSIX:

2023-04-20T07:52:54.937Z [Error] Error: Logging an ERROR message via LogOutputChannel
    at /Users/a.kitta/dev/git/theia/plugins/vscode-extension-log-output-channel-12017-0.0.1/extension/out/extension.js:40:34
    at /Users/a.kitta/dev/git/theia/packages/plugin-ext/lib/plugin/command-registry.js:61:59
    at CommandRegistryImpl.executeLocalCommand (/Users/a.kitta/dev/git/theia/packages/plugin-ext/lib/plugin/command-registry.js:108:20)
    at CommandRegistryImpl.$executeCommand (/Users/a.kitta/dev/git/theia/packages/plugin-ext/lib/plugin/command-registry.js:70:25)
    at /Users/a.kitta/dev/git/theia/packages/plugin-ext/lib/common/proxy-handler.js:91:71
    at process.processTicksAndRejections (node:internal/process/task_queues:96:5)
    at async RpcProtocol.handleRequest (/Users/a.kitta/dev/git/theia/packages/core/lib/common/message-rpc/rpc-protocol.js:167:28)

Regarding the API, when creating the LogOutputChannel, options must be defined, and log must be true to be in sync with Code API. What do you think?

packages/plugin/src/theia.d.ts Outdated Show resolved Hide resolved
packages/plugin-ext/src/plugin/output-channel-registry.ts Outdated Show resolved Hide resolved
packages/plugin-ext/src/plugin/output-channel-registry.ts Outdated Show resolved Hide resolved
Comment on lines 47 to 50
replace(value: string): void {
this.info(value);
this.proxy.$append(this.name, value, this.pluginInfo);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please explain what it does? I could not figure out how to verify it via the test VSIX. Thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that! It basically should replace the channel's content with the given text value.
With the updated implementation, we reuse the OutputChannelImpl and can reuse the logic, which clears the content first and then appends the given text value.
Also, I updated the test extension, which also offers a command now to call the replace function.

packages/plugin/src/theia.d.ts Outdated Show resolved Hide resolved
packages/plugin-ext/src/common/plugin-api-rpc.ts Outdated Show resolved Hide resolved
@tsmaeder tsmaeder self-requested a review April 20, 2023 08:58
Copy link
Contributor

@tsmaeder tsmaeder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think using the logger implementation from VS Code is a good idea in this case. Feel free to convince me, though :-)

@ndoschek
Copy link
Contributor Author

Thank you both for your reviews!
I updated my implementation and also updated the test extension in the PR description (it now offers a few more commands like replace and error throwing)

Regarding the additional log file functionality in VS Code (see my remark in the PR description), is it okay if I add a follow-up for this?

packages/plugin-ext/src/plugin/output-channel-registry.ts Outdated Show resolved Hide resolved
readonly onDidChangeLogLevel: theia.Event<theia.LogLevel> = this.onDidChangeLogLevelEmitter.event;
public logLevel: theia.LogLevel;

constructor(override readonly name: string, protected override readonly proxy: OutputChannelRegistryMain, protected override readonly pluginInfo: PluginInfo) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since name is public on the superclass, do we need the override readonly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is also readonly in the superclass, so I guess this is okay this way

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But why would you redeclare a variable that is already present in the superclass and visible just fine in the subclass? It's unnecessary for all three constructor params, IMO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes basically I agree, but as I had to write a custom constructor (to initalize the log level) I have no other choice, as Typescript requires them to be overridden due to the same variable names, see This parameter property must have an 'override' modifier because it overrides a member in base class 'OutputChannelImpl'.ts(4115)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works for me:

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

The "readonly" is a property of the implicitly created field, not the constructor parameter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks I was not aware of that, that works great! I pushed another update for this.

}

trace(message: string, ...args: any[]): void {
if (this.checkLogLevel(LogLevel.Trace)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like odd code. Why do we duplicate the level check in each method an not just move it into log(level, ...)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's true, thanks for catching that! I changed it to your suggestion.

}

protected log(level: theia.LogLevel, message: string): void {
const now = new Date();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would suggest moving the level checking here instead the methods below.

error(message: string | Error, ...args: any[]): void {
if (this.checkLogLevel(LogLevel.Error)) {
if (message instanceof Error) {
const array = Array.prototype.slice.call(arguments) as unknown[];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for this processing? And wouldn't arguments.slice() work as well?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really understand what this outputs in the case of an error object. Could you give an example?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I agree, I had another look and it is sufficient to pass the error's stack member instead of this processing. I updated the test extension, it now covers both cases with error stack and only with an error message.

}
}

private format(args: any): string {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't this just doing this?

args.map(arg => stringify the arg here).join(' ');

- 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 eclipse-theia#12017

Contributed on behalf of STMicroelectronics.

Signed-off-by: Nina Doschek <[email protected]>
- Ensure options to identify LogOutputChannel is never optional
- LogOutputChannelImpl extends OutputChannelImpl instead of vscode's AbstractMessageLogger
- Remove obsolete type converter again
- Update Changelog

Contributed on behalf of STMicroelectronics.

Signed-off-by: Nina Doschek <[email protected]>
- Ensure LogOutputview is validated before logging or appending content
- Simplify processing of error objects
- Simplify formatting of arguments in log messages
- Update Changelog

Contributed on behalf of STMicroelectronics.

Signed-off-by: Nina Doschek <[email protected]>
@tsmaeder tsmaeder self-requested a review April 21, 2023 11:39
Copy link
Contributor

@tsmaeder tsmaeder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some nitpicks left.

@tsmaeder
Copy link
Contributor

@ndoschek I just have two nitpicks left, then we're good to go. Works as advertised.

@ndoschek
Copy link
Contributor Author

Thank you @tsmaeder for your review! I left my replies in the open threads.

Contributed on behalf of STMicroelectronics.

Signed-off-by: Nina Doschek <[email protected]>
Copy link
Contributor

@tsmaeder tsmaeder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me, now.

This was referenced Nov 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

[vscode] Support LogOutputChannel
4 participants