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.
Related Issues
Closes #2269 (closed)
How has this been tested?
Prerequisites:
- Enable telemetry in VS Code:
File > Preferences > Settings > Telemetry > Telemetry Levelset toallorerror - 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/browserorsrc/commonhas 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)