Skip to content

Commit 1fe4f7e

Browse files
committed
Prefix log messages with session ID
1 parent dc06be7 commit 1fe4f7e

File tree

2 files changed

+159
-10
lines changed

2 files changed

+159
-10
lines changed

extensions/positron-supervisor/src/KallichoreSession.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import { CommBackendRequest, CommRpcMessage, CommImpl } from './Comm';
4747
import { channel, Sender } from './Channel';
4848
import { DapComm } from './DapComm';
4949
import { JupyterKernelStatus } from './jupyter/JupyterKernelStatus.js';
50+
import { OutputChannelFormatted, LogOutputChannelFormatted } from './OutputChannelFormatted';
5051

5152
/**
5253
* The reason for a disconnection event.
@@ -135,12 +136,12 @@ export class KallichoreSession implements JupyterLanguageRuntimeSession {
135136
/**
136137
* The channel to which output for this specific kernel is logged, if any
137138
*/
138-
private readonly _kernelChannel: vscode.OutputChannel;
139+
private readonly _kernelChannel: OutputChannelFormatted;
139140

140141
/**
141142
* The channel to which output for this specific console is logged
142143
*/
143-
private readonly _consoleChannel: vscode.LogOutputChannel;
144+
private readonly _consoleChannel: LogOutputChannelFormatted;
144145

145146
/**
146147
* The channel to which profile output for this specific kernel is logged, if any
@@ -213,14 +214,18 @@ export class KallichoreSession implements JupyterLanguageRuntimeSession {
213214
this.onDidEndSession = this._exit.event;
214215

215216
// Establish log channels for the console and kernel we're connecting to
216-
this._consoleChannel = vscode.window.createOutputChannel(
217-
metadata.notebookUri ?
218-
`${runtimeMetadata.runtimeName}: Notebook: (${path.basename(metadata.notebookUri.path)})` :
219-
`${runtimeMetadata.runtimeName}: Console`,
220-
{ log: true });
221-
222-
this._kernelChannel = positron.window.createRawLogOutputChannel(
223-
`${runtimeMetadata.runtimeName}: Kernel`);
217+
this._consoleChannel = new LogOutputChannelFormatted(
218+
vscode.window.createOutputChannel(
219+
`${runtimeMetadata.languageName} Supervisor`,
220+
{ log: true }
221+
),
222+
(msg) => `${metadata.sessionId} ${msg}`
223+
);
224+
225+
this._kernelChannel = new OutputChannelFormatted(
226+
positron.window.createRawLogOutputChannel(`${runtimeMetadata.languageName} Kernel`),
227+
(msg) => `${metadata.sessionId} ${msg}`
228+
);
224229
this._kernelChannel.appendLine(`** Begin kernel log for session ${dynState.sessionName} (${metadata.sessionId}) at ${new Date().toLocaleString()} **`);
225230

226231
// Open the established barrier immediately if we're restoring an
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*---------------------------------------------------------------------------------------------
2+
* Copyright (C) 2025 Posit Software, PBC. All rights reserved.
3+
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
4+
*--------------------------------------------------------------------------------------------*/
5+
6+
import * as vscode from 'vscode';
7+
8+
/**
9+
* Function type for formatting messages before they are appended to the channel.
10+
*/
11+
export type MessageFormatter = (message: string) => string;
12+
13+
/**
14+
* OutputChannel with formatting applied to `appendLine()`.
15+
*/
16+
export class OutputChannelFormatted implements vscode.OutputChannel {
17+
constructor(
18+
private readonly channel: vscode.OutputChannel,
19+
private readonly formatter: MessageFormatter
20+
) { }
21+
22+
get name(): string {
23+
return this.channel.name;
24+
}
25+
26+
append(value: string): void {
27+
this.channel.append(value);
28+
}
29+
30+
appendLine(value: string): void {
31+
this.channel.appendLine(this.formatter(value));
32+
}
33+
34+
replace(value: string): void {
35+
this.channel.replace(value);
36+
}
37+
38+
clear(): void {
39+
this.channel.clear();
40+
}
41+
42+
show(preserveFocus?: boolean): void;
43+
show(column?: vscode.ViewColumn, preserveFocus?: boolean): void;
44+
show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
45+
if (typeof columnOrPreserveFocus === 'boolean' || columnOrPreserveFocus === undefined) {
46+
this.channel.show(columnOrPreserveFocus);
47+
} else {
48+
this.channel.show(preserveFocus);
49+
}
50+
}
51+
52+
hide(): void {
53+
this.channel.hide();
54+
}
55+
56+
dispose(): void {
57+
this.channel.dispose();
58+
}
59+
}
60+
61+
/**
62+
* A wrapper around LogOutputChannel that allows custom message formatting for all log methods.
63+
*/
64+
export class LogOutputChannelFormatted implements vscode.LogOutputChannel {
65+
constructor(
66+
private readonly channel: vscode.LogOutputChannel,
67+
private readonly formatter: MessageFormatter
68+
) { }
69+
70+
get logLevel(): vscode.LogLevel {
71+
return this.channel.logLevel;
72+
}
73+
74+
get onDidChangeLogLevel(): vscode.Event<vscode.LogLevel> {
75+
return this.channel.onDidChangeLogLevel;
76+
}
77+
78+
get name(): string {
79+
return this.channel.name;
80+
}
81+
82+
append(value: string): void {
83+
this.channel.append(value);
84+
}
85+
86+
appendLine(value: string): void {
87+
this.channel.appendLine(this.formatter(value));
88+
}
89+
90+
replace(value: string): void {
91+
this.channel.replace(value);
92+
}
93+
94+
clear(): void {
95+
this.channel.clear();
96+
}
97+
98+
show(preserveFocus?: boolean): void;
99+
show(column?: vscode.ViewColumn, preserveFocus?: boolean): void;
100+
show(columnOrPreserveFocus?: vscode.ViewColumn | boolean, preserveFocus?: boolean): void {
101+
if (typeof columnOrPreserveFocus === 'boolean' || columnOrPreserveFocus === undefined) {
102+
this.channel.show(columnOrPreserveFocus);
103+
} else {
104+
this.channel.show(preserveFocus);
105+
}
106+
}
107+
108+
hide(): void {
109+
this.channel.hide();
110+
}
111+
112+
dispose(): void {
113+
this.channel.dispose();
114+
}
115+
116+
trace(message: string, ...args: any[]): void {
117+
this.channel.trace(this.formatter(message), ...args);
118+
}
119+
120+
debug(message: string, ...args: any[]): void {
121+
this.channel.debug(this.formatter(message), ...args);
122+
}
123+
124+
info(message: string, ...args: any[]): void {
125+
this.channel.info(this.formatter(message), ...args);
126+
}
127+
128+
warn(message: string, ...args: any[]): void {
129+
this.channel.warn(this.formatter(message), ...args);
130+
}
131+
132+
error(message: string | Error, ...args: any[]): void {
133+
if (typeof message === 'string') {
134+
this.channel.error(this.formatter(message), ...args);
135+
} else {
136+
// Format the error message and include stack trace if available
137+
const formatted = this.formatter(message.message);
138+
const fullMessage = message.stack
139+
? `${formatted}\n${message.stack}`
140+
: formatted;
141+
this.channel.error(fullMessage, ...args);
142+
}
143+
}
144+
}

0 commit comments

Comments
 (0)