feat: Add LS startup telemetry

Description

Follow-up to !3187 (merged) which introduced LanguageServerStartupMonitor with phase tracking.

This MR adds Snowplow telemetry to track Language Server startup outcomes - both successes and failures - with timing breakdowns and environment context. The data will help us understand where startup time is actually spent and correlate slow/failed startups with specific OS and remote environments (SSH, WSL, etc.) and adjust the timeouts.

What is tracked

A language_server / startup_completed struct event is fired on every startup attempt with the following data in the gitlab_standard context extra field:

Field Description
status success or failed
failed_phase idle, spawning, or handshake (null on success)
spawn_duration_ms Time from State.Starting to handshake begin
handshake_duration_ms Time from handshake begin to State.Running
total_duration_ms Total startup time
restart_attempts Number of spawn attempts
os darwin, win32, linux etc.
remote_name ssh-remote, wsl, dev-container or null for local

The event is sent with a 10s delay to ensure Snowplow is initialized before the send (Snowplow initialization happens concurrently with LS startup).

Bug fix included

The restart grace period failure path was not calling reject() on the startup promise, causing initAndStart() to hang forever when the LS crashed on first startup. This blocked the entire extension activation. Fixed as part of this MR.

Closes #2269 (closed)

How has this been tested?

Prerequisites:

  • Enable telemetry in VS Code: File > Preferences > Settings > Telemetry > Telemetry Level set to all or error
  • Set up a local Snowplow collector using snowplow-micro and temporarily replace the endpoint in src/common/snowplow/snowplow_options.ts:
endpoint: 'http://localhost:9090', // snowplow-micro default
  • Run npm run build:desktop, then run the extension in the development host

Scenario 1 - Happy path (fast startup)

Run the extension normally. After ~10s, check the local Snowplow collector - you should see a language_server / startup_completed event with status: success and timing values populated.

Scenario 2 - Happy path (slow startup)

In the LS, delay the initialize handler by more than 2 seconds:

await new Promise((resolve) => setTimeout(resolve, 10000));

Link the updated LS running bun run watch -- --editor=vscode. The event should show a large handshake_duration_ms value.

Scenario 3 - Spawn failure

Remove the LS bundle at dist-desktop/assets/language-server/main-bundle-node.js and run the extension without rebuilding.

The Snowplow event should show status: failed, failed_phase: spawning.

Scenario 4 - Handshake timeout

In the LS, delay the initialize handler by more than 30 seconds:

await new Promise((resolve) => setTimeout(resolve, 35000));

After 30s the event should show status: failed, failed_phase: handshake.

  • If src/browser or src/common has been modified, please consider interoperability with the Web IDE. See Running the Extension in WebIDE.
  • Consider an end-to-end test for significant new features that aren't covered by integration tests.

Screenshots (if appropriate)

What CHANGELOG entry will this MR create?

  • fix: Bug fix fixes - a user-facing issue in production - included in changelog
  • feature: New feature - a user-facing change which adds functionality - included in changelog
  • BREAKING CHANGE: (fix or feature that would cause existing functionality to change) - should bump major version, mentioned in the changelog
  • None - other non-user-facing changes

Closes #2269 (closed)

Edited by Olena Horal-Koretska

Merge request reports

Loading
Loading