Skip to content

Route jsonrpc connection logging to the output channel instead of console - #1853

Merged
Dirk Bäumer (dbaeumer) merged 5 commits into
microsoft:mainfrom
pujitha24:auto/issue-1613
Sep 22, 2026
Merged

Dirk Bäumer (dbaeumer) merged 5 commits into
microsoft:mainfrom
pujitha24:auto/issue-1613

Conversation

@pujitha24

Copy link
Copy Markdown
Contributor

Motivation:
BaseLanguageClient's internal jsonrpc Connection was created with a
ConsoleLogger, so protocol-level diagnostics such as "Received response
message without id" (jsonrpc/src/common/connection.ts) only showed up in
VS Code's Developer Tools console. Extensions typically point users at
their own Output channel for troubleshooting, so these messages were
effectively invisible to end users and to extension authors reproducing
bug reports.

Approach:
Add an OutputChannelLogger that implements the jsonrpc Logger interface
(error/warn/info/log) by forwarding to a LogOutputChannel, mapping the
generic log() to outputChannel.info() since LogOutputChannel has no
generic log method. The module-level createConnection() now accepts an
optional lazy accessor (() => LogOutputChannel) and uses
OutputChannelLogger when provided, falling back to the previous
ConsoleLogger otherwise. BaseLanguageClient.createConnection() passes
"() => this.outputChannel" rather than a resolved channel so the output
channel keeps its existing lazy-creation semantics: a channel is still
only created the first time something is actually logged, matching the
behavior of the pre-existing this.error/warn/info/debug client methods
and shouldLogToOutputChannel().

Validation:

  • npm run compile:client (tsc -b ./client/tsconfig.json) passes with no
    errors, type-checked against the real @types/vscode LogOutputChannel
    shape.
  • cd client && npm run lint (eslint) passes clean.
  • Traced call sites in jsonrpc/src/common/connection.ts to confirm
    logger.error/warn/log are only invoked from message-processing paths,
    never from connection setup, so the lazy output-channel accessor is
    never forced eagerly and a session that never logs still creates no
    output channel, preserving prior behavior for that case.
  • Could not run this repo's client-node-tests integration suite (real
    VS Code Insiders launched via @vscode/test-electron, as used in
    build/azure-pipelines/darwin/build.yml): the sandbox this change was
    developed in has no free disk space to download the ~300MB VS Code
    build (ENOSPC), an environment constraint unrelated to this change.
    There is no lighter-weight unit-test harness for client.ts in this
    repo since everything under client-node-tests only runs inside a real
    VS Code Electron process.

Report: #1613
Signed-off-by: Pujitha Paladugu 10557236+pujitha24@users.noreply.github.com
Assisted-by: claude-sonnet-5 (via Claude Code)

Fixes #1613

…sole

Motivation:
BaseLanguageClient's internal jsonrpc Connection was created with a
ConsoleLogger, so protocol-level diagnostics such as "Received response
message without id" (jsonrpc/src/common/connection.ts) only showed up in
VS Code's Developer Tools console. Extensions typically point users at
their own Output channel for troubleshooting, so these messages were
effectively invisible to end users and to extension authors reproducing
bug reports.

Approach:
Add an OutputChannelLogger that implements the jsonrpc Logger interface
(error/warn/info/log) by forwarding to a LogOutputChannel, mapping the
generic log() to outputChannel.info() since LogOutputChannel has no
generic log method. The module-level createConnection() now accepts an
optional lazy accessor (() => LogOutputChannel) and uses
OutputChannelLogger when provided, falling back to the previous
ConsoleLogger otherwise. BaseLanguageClient.createConnection() passes
"() => this.outputChannel" rather than a resolved channel so the output
channel keeps its existing lazy-creation semantics: a channel is still
only created the first time something is actually logged, matching the
behavior of the pre-existing this.error/warn/info/debug client methods
and shouldLogToOutputChannel().

Validation:
- npm run compile:client (tsc -b ./client/tsconfig.json) passes with no
  errors, type-checked against the real @types/vscode LogOutputChannel
  shape.
- cd client && npm run lint (eslint) passes clean.
- Traced call sites in jsonrpc/src/common/connection.ts to confirm
  logger.error/warn/log are only invoked from message-processing paths,
  never from connection setup, so the lazy output-channel accessor is
  never forced eagerly and a session that never logs still creates no
  output channel, preserving prior behavior for that case.
- Could not run this repo's client-node-tests integration suite (real
  VS Code Insiders launched via @vscode/test-electron, as used in
  build/azure-pipelines/darwin/build.yml): the sandbox this change was
  developed in has no free disk space to download the ~300MB VS Code
  build (ENOSPC), an environment constraint unrelated to this change.
  There is no lighter-weight unit-test harness for client.ts in this
  repo since everything under client-node-tests only runs inside a real
  VS Code Electron process.

Report: microsoft#1613
Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
Assisted-by: claude-sonnet-5 (via Claude Code)
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
There may be pipelines that require an authorized user to comment /azp run to run.

Comment thread client/src/common/client.ts Outdated

function createConnection(input: MessageReader, output: MessageWriter, errorHandler: ConnectionErrorHandler, closeHandler: ConnectionCloseHandler, options?: ConnectionOptions): Connection {
const logger = new ConsoleLogger();
function createConnection(input: MessageReader, output: MessageWriter, errorHandler: ConnectionErrorHandler, closeHandler: ConnectionCloseHandler, getOutputChannel: (() => LogOutputChannel) | undefined, options?: ConnectionOptions): Connection {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can we make a type / interface ChannelProvider like

interface ChannelProvider {
outputChannel: LogOutputChanneln
}

And then pass the client instead of a function instance.

…nnel closure

Per review feedback, createConnection/OutputChannelLogger now take a
ChannelProvider ({ outputChannel: LogOutputChannel }) and the client
itself is passed in, rather than a bound () => this.outputChannel
function. BaseLanguageClient's outputChannel getter already satisfies
the interface structurally, so lazy output-channel creation is
unchanged.

Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
@pujitha24

Copy link
Copy Markdown
Contributor Author

Dirk Bäumer (@dbaeumer) I've pushed changes addressing your review — the branch is now at 93a5c6e and CI is green. Could you take another look when you have a moment? Happy to keep iterating if anything is still off.

@dbaeumer

Copy link
Copy Markdown
Member

/AzurePipelines run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Changes recommended

Logging can recreate the disposed output channel after shutdown, and the behavior lacks regression coverage.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

Routes JSON-RPC diagnostics to the language client’s output channel for improved visibility.

Changes:

  • Adds an output-channel-backed JSON-RPC logger.
  • Preserves lazy channel creation.
  • Maps generic logs to info severity.
File summaries
File Description
client/src/common/client.ts Connects protocol logging to the client output channel.
Review details
  • Files reviewed: 1/1 changed files
  • Comments generated: 2
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread client/src/common/client.ts
outputChannel: LogOutputChannel;
}

class OutputChannelLogger implements Logger {
@dbaeumer

Copy link
Copy Markdown
Member

Deleted user (@ghost) see the Copilot review comments.

Per Copilot's review comment, OutputChannelLogger previously dereferenced
channelProvider.outputChannel directly, bypassing BaseLanguageClient's
shouldLogToOutputChannel() guard. An in-flight JSON-RPC handler rejection
after shutdown() could still reach the logger and recreate a disposed
output channel for a stopped client. ChannelProvider now exposes
error/warn/info (matching the client's existing public logging methods)
and OutputChannelLogger calls those with notifications disabled, so the
stopped-state guard applies to protocol-level logging too.

Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
@pujitha24

Copy link
Copy Markdown
Contributor Author

Dirk Bäumer (@dbaeumer) I've pushed changes addressing your review — the branch is now at f739ee8 and CI is green. Could you take another look when you have a moment? Happy to keep iterating if anything is still off.

…annel

Per Copilot's review comment, adds a test in the 'Server output' suite
that triggers a jsonrpc-level diagnostic (a throwing notification
handler) and asserts it reaches the client's guarded error() method
(showNotification=false) rather than bypassing it, covering the routing
fixed in f739ee8.

Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟢 Approval recommended

The implementation correctly addresses the reported logging visibility issue with focused integration coverage.

Review effort: Balanced
Findings: 1 Low severity

Open (1)
Resolved since last review (1)

Copilot's review flagged that the earlier regression test (1282da8)
never verified "no channel is created before the first diagnostic".
Investigating client.ts shows the output channel is already created
during start() as a side effect of wiring the trace log-level
listener, unrelated to diagnostics, so that literal assertion would
be false. Instead this adds a test for the actual regression
f739ee8 fixed: a diagnostic logged after stop() must not recreate
the disposed output channel.

Signed-off-by: Pujitha Paladugu <10557236+pujitha24@users.noreply.github.com>
@dbaeumer

Copy link
Copy Markdown
Member

/AzurePipelines run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@pujitha24

Copy link
Copy Markdown
Contributor Author

Dirk Bäumer (@dbaeumer) I've pushed changes addressing your review — the branch is now at dbff547 and CI is green. Could you take another look when you have a moment? Happy to keep iterating if anything is still off.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟢 Approval recommended

The focused implementation preserves lifecycle safeguards and includes appropriate integration coverage.

Review effort: Balanced
Findings: 1 Low severity

Open (1)

@pujitha24

Copy link
Copy Markdown
Contributor Author

This is approved with green CI and no outstanding comments as far as I can tell — ready whenever you have a moment. Happy to rebase first if you'd like it freshened.

@dbaeumer
Dirk Bäumer (dbaeumer) merged commit a7ca284 into microsoft:main Sep 22, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

VSCode language client should configure logger pointing to the output channel rather than console

6 participants