Skip to content

Commit 23d0fbd

Browse files
authored
Merge pull request #703 from usethesource/feature/587-log-output-channel
Rich VS Code logs
2 parents 4f8b64c + 109284d commit 23d0fbd

File tree

16 files changed

+376
-60
lines changed

16 files changed

+376
-60
lines changed

rascal-lsp/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,11 @@
9898
<artifactId>log4j-jul</artifactId>
9999
<version>${log4j2.version}</version>
100100
</dependency>
101+
<dependency>
102+
<groupId>org.apache.logging.log4j</groupId>
103+
<artifactId>log4j-layout-template-json</artifactId>
104+
<version>${log4j2.version}</version>
105+
</dependency>
101106
<dependency>
102107
<groupId>org.eclipse.lsp4j</groupId>
103108
<artifactId>org.eclipse.lsp4j.debug</artifactId>

rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/BaseLanguageServer.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
import org.rascalmpl.uri.URIResolverRegistry;
6767
import org.rascalmpl.uri.URIUtil;
6868
import org.rascalmpl.values.IRascalValueFactory;
69+
import org.rascalmpl.vscode.lsp.log.LogRedirectConfiguration;
6970
import org.rascalmpl.vscode.lsp.terminal.ITerminalIDEServer.LanguageParameter;
7071
import org.rascalmpl.vscode.lsp.uri.jsonrpc.impl.VSCodeVFSClient;
7172
import org.rascalmpl.vscode.lsp.uri.jsonrpc.messages.PathConfigParameter;
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Copyright (c) 2018-2025, NWO-I CWI and Swat.engineering
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
*
8+
* 1. Redistributions of source code must retain the above copyright notice,
9+
* this list of conditions and the following disclaimer.
10+
*
11+
* 2. Redistributions in binary form must reproduce the above copyright notice,
12+
* this list of conditions and the following disclaimer in the documentation
13+
* and/or other materials provided with the distribution.
14+
*
15+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16+
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17+
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18+
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
19+
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20+
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21+
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22+
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23+
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24+
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25+
* POSSIBILITY OF SUCH DAMAGE.
26+
*/
27+
package org.rascalmpl.vscode.lsp.log;
28+
29+
import java.net.URI;
30+
31+
import org.apache.logging.log4j.Level;
32+
import org.apache.logging.log4j.core.LoggerContext;
33+
import org.apache.logging.log4j.core.appender.ConsoleAppender;
34+
import org.apache.logging.log4j.core.config.Configuration;
35+
import org.apache.logging.log4j.core.config.ConfigurationFactory;
36+
import org.apache.logging.log4j.core.config.ConfigurationSource;
37+
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilder;
38+
import org.apache.logging.log4j.core.config.builder.api.ConfigurationBuilderFactory;
39+
import org.apache.logging.log4j.core.config.builder.impl.BuiltConfiguration;
40+
41+
public class LogJsonConfiguration extends ConfigurationFactory {
42+
43+
@Override
44+
protected String[] getSupportedTypes() {
45+
return new String[] {"*"};
46+
}
47+
48+
49+
50+
@Override
51+
public Configuration getConfiguration(LoggerContext loggerContext, String name, URI configLocation) {
52+
return buildConfiguration();
53+
}
54+
55+
@Override
56+
public Configuration getConfiguration(LoggerContext loggerContext, String name, URI configLocation,
57+
ClassLoader loader) {
58+
return buildConfiguration();
59+
}
60+
61+
@Override
62+
public Configuration getConfiguration(LoggerContext loggerContext, ConfigurationSource source) {
63+
return buildConfiguration();
64+
}
65+
66+
private Configuration buildConfiguration() {
67+
Level targetLevel = Level.getLevel(System.getProperty("log4j2.level", "INFO"));
68+
if (targetLevel == null) {
69+
targetLevel = Level.INFO;
70+
}
71+
72+
ConfigurationBuilder<BuiltConfiguration> builder = ConfigurationBuilderFactory.newConfigurationBuilder();
73+
builder.setConfigurationName("JsonLogger");
74+
builder.setStatusLevel(targetLevel);
75+
76+
builder.add(builder
77+
.newAppender("Console", ConsoleAppender.PLUGIN_NAME)
78+
.addAttribute("target", ConsoleAppender.Target.SYSTEM_ERR)
79+
.add(builder.newLayout("JsonTemplateLayout")
80+
/* The JSON template has a max length (buffer size) of 8192 by default:
81+
https://logging.apache.org/log4j/2.x/manual/systemproperties.html#log4j2.encoderByteBufferSize
82+
If JSON documents are longer, the buffer will be flushed in between,
83+
and the JSON arrives truncated on the client side. To prevent his, we cap the max message length. */
84+
.addAttribute("maxStringLength", 6000)
85+
.addAttribute("eventTemplateUri", "classpath:JsonLogTemplate.json")
86+
)
87+
);
88+
89+
builder.add(builder
90+
.newRootLogger(targetLevel)
91+
.add(builder.newAppenderRef("Console"))
92+
);
93+
94+
return builder.build();
95+
}
96+
97+
}

rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/LogRedirectConfiguration.java renamed to rascal-lsp/src/main/java/org/rascalmpl/vscode/lsp/log/LogRedirectConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2525
* POSSIBILITY OF SUCH DAMAGE.
2626
*/
27-
package org.rascalmpl.vscode.lsp;
27+
package org.rascalmpl.vscode.lsp.log;
2828

2929
import java.net.URI;
3030

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"timestamp": {
3+
"$resolver": "timestamp"
4+
},
5+
"level": {
6+
"$resolver": "level",
7+
"field": "name"
8+
},
9+
"loggerName": {
10+
"$resolver": "logger",
11+
"field": "name"
12+
},
13+
"threadName": {
14+
"$resolver": "thread",
15+
"field": "name"
16+
},
17+
"message": {
18+
"$resolver": "message"
19+
}
20+
}

rascal-vscode-extension/.eslintrc.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
"eqeqeq": "warn",
2929
"no-throw-literal": "warn",
3030
"semi": "off",
31-
"no-unused-vars": "off"
31+
"no-unused-vars": "off",
32+
"no-console": "error"
3233
}
3334
}

rascal-vscode-extension/src/RascalExtension.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,10 @@ export class RascalExtension implements vscode.Disposable {
4343
private readonly dsls:ParameterizedLanguageServer;
4444
private readonly rascal: RascalLanguageServer;
4545

46+
private readonly log: vscode.LogOutputChannel = vscode.window.createOutputChannel("Rascal Extension", {log: true});
4647

4748
constructor(private readonly context: vscode.ExtensionContext, private readonly jarRootPath: string, private readonly icon: vscode.Uri, private readonly isDeploy = true) {
48-
this.vfsServer = new VSCodeUriResolverServer(!isDeploy);
49+
this.vfsServer = new VSCodeUriResolverServer(!isDeploy, this.log);
4950

5051
this.dsls = new ParameterizedLanguageServer(context, this.vfsServer, jarRootPath, isDeploy);
5152
this.rascal = new RascalLanguageServer(context, this.vfsServer, jarRootPath, this.dsls, isDeploy);
@@ -56,15 +57,20 @@ export class RascalExtension implements vscode.Disposable {
5657
this.registerCopySourceLocationCommand();
5758
checkForJVMUpdate();
5859

59-
vscode.window.registerTreeDataProvider('rascalmpl-configuration-view', new RascalLibraryProvider(this.rascal.rascalClient));
60+
vscode.window.registerTreeDataProvider('rascalmpl-configuration-view', new RascalLibraryProvider(this.rascal.rascalClient, this.log));
6061
vscode.window.registerTreeDataProvider('rascalmpl-debugger-view', new RascalDebugViewProvider(this.rascal.rascalDebugClient, context));
6162
vscode.window.registerTerminalLinkProvider(new RascalTerminalLinkProvider(this.rascal.rascalClient));
6263
}
6364

65+
logger(): vscode.LogOutputChannel {
66+
return this.log;
67+
}
68+
6469
dispose() {
6570
this.vfsServer.dispose();
6671
this.dsls.dispose();
6772
this.rascal.dispose();
73+
this.log.dispose();
6874
}
6975

7076
externalLanguageRegistry() {
@@ -124,7 +130,7 @@ export class RascalExtension implements vscode.Disposable {
124130
}, async (progress) => {
125131
progress.report({message: "Starting rascal-lsp"});
126132
const rascal = await this.rascal.rascalClient;
127-
console.log(`Starting Rascal REPL: on ${uri} and with command: ${command}`);
133+
this.log.debug(`Starting Rascal REPL: on ${uri} and with command: ${command}`);
128134
if (uri && !uri.path.endsWith(".rsc")) {
129135
// do not try to figure out a rascal project path when the focus is not a rascal file
130136
uri = undefined;
@@ -145,7 +151,7 @@ export class RascalExtension implements vscode.Disposable {
145151
progress.report({increment: 25, message: "Creating terminal"});
146152
const terminal = vscode.window.createTerminal({
147153
iconPath: this.icon,
148-
shellPath: await getJavaExecutable(),
154+
shellPath: await getJavaExecutable(this.log),
149155
shellArgs: this.buildShellArgs(compilationPath, serverConfig),
150156
isTransient: false, // right now we don't support transient terminals yet
151157
name: `Rascal terminal (${this.getTerminalOrigin(uri, command??"")})`,
@@ -192,7 +198,7 @@ export class RascalExtension implements vscode.Disposable {
192198
return "no module";
193199
}
194200
default:
195-
console.log(`Unknown origin format: ${originFormat}`);
201+
this.log.warn(`Unknown origin format: ${originFormat}`);
196202
}
197203
}
198204
return 'no project or module';

rascal-vscode-extension/src/auto-jvm/JavaLookup.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ let lookupCompleted: Thenable<string> | undefined;
4646

4747
const pexec = promisify(cp.exec);
4848

49-
export async function getJavaExecutable(): Promise<string> {
49+
export async function getJavaExecutable(logger: vscode.LogOutputChannel): Promise<string> {
5050
if (lookupCompleted) {
5151
return lookupCompleted;
5252
}
@@ -72,7 +72,7 @@ export async function getJavaExecutable(): Promise<string> {
7272
// ignore success, this callback is only for failures
7373

7474
}, e => {
75-
console.log("Automatic download failed: ", e);
75+
logger.error("Automatic download failed: ", e);
7676
lookupCompleted = undefined;
7777
});
7878
return lookupCompleted;

rascal-vscode-extension/src/extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export function activate(context: vscode.ExtensionContext) {
4141
const extension = new RascalExtension(context, jars, icon, (deployMode || testDeployMode));
4242
context.subscriptions.push(extension);
4343
context.subscriptions.push(new RascalMFValidator());
44-
context.subscriptions.push(new RascalProjectValidator());
44+
context.subscriptions.push(new RascalProjectValidator(extension.logger()));
4545
return extension.externalLanguageRegistry();
4646
}
4747

rascal-vscode-extension/src/fs/RascalFileSystemProviders.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export class RascalFileSystemProvider implements vscode.FileSystemProvider {
3939
*
4040
* @param client to use as a server for the file system provider methods
4141
*/
42-
constructor (client:BaseLanguageClient) {
42+
constructor (client:BaseLanguageClient, private readonly logger: vscode.LogOutputChannel) {
4343
this.client = client;
4444

4545
client.onNotification("rascal/filesystem/onDidChangeFile", (event:vscode.FileChangeEvent) => {
@@ -53,7 +53,7 @@ export class RascalFileSystemProvider implements vscode.FileSystemProvider {
5353
return this.client.sendRequest<R>(method, param ?? { uri: uri.toString()} )
5454
.catch((r: ResponseError) => {
5555
if (r !== undefined) {
56-
this.client.debug("Got response error from the file system: ", r);
56+
this.logger.debug("Got response error from the file system: ", r);
5757
switch (r.code) {
5858
case -1: throw vscode.FileSystemError.FileExists(uri);
5959
case -2: throw vscode.FileSystemError.FileIsADirectory(uri);
@@ -83,13 +83,13 @@ export class RascalFileSystemProvider implements vscode.FileSystemProvider {
8383
.forEach(s => {
8484
try {
8585
vscode.workspace.registerFileSystemProvider(s, this);
86-
this.client.debug(`Rascal VFS registered scheme: ${s}`);
86+
this.logger.debug(`Rascal VFS registered scheme: ${s}`);
8787
} catch (error) {
8888
if (isUnknownFileSystem(s)) {
89-
this.client.error(`Unable to register scheme: ${s}\n${error}`);
89+
this.logger.error(`Unable to register scheme: ${s}\n${error}`);
9090
}
9191
else {
92-
this.client.debug(`Rascal VFS lost the race to register scheme: ${s}, which in most cases is fine`);
92+
this.logger.debug(`Rascal VFS lost the race to register scheme: ${s}, which in most cases is fine`);
9393
}
9494
}
9595
});

0 commit comments

Comments
 (0)