Path: blob/main/src/vs/sessions/common/sessionsTelemetry.ts
13389 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { ITelemetryService } from '../../platform/telemetry/common/telemetry.js';67// --- Titlebar button interactions ---89export type SessionsInteractionButton =10| 'newSession'11| 'runPrimaryTask'12| 'addTask'13| 'generateNewTask'14| 'openTerminal'15| 'openInVSCode';1617export type SessionsInteractionSource = 'menu' | 'actionWidget';1819type SessionsInteractionEvent = {20button: string;21source?: string;22};2324type SessionsInteractionClassification = {25owner: 'osortega';26comment: 'Tracks user interactions with buttons in the Agents window';27button: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The identifier of the button that was clicked' };28source?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The UI surface that triggered the interaction (menu or actionWidget)' };29};3031/**32* Log a titlebar button interaction in the Agents window.33*/34export function logSessionsInteraction(telemetryService: ITelemetryService, button: SessionsInteractionButton, source?: SessionsInteractionSource): void {35telemetryService.publicLog2<SessionsInteractionEvent, SessionsInteractionClassification>('vscodeAgents.interaction', source ? { button, source } : { button });36}3738// --- Changes panel interactions ---3940type ChangesViewTogglePanelEvent = {41visible: boolean;42};4344type ChangesViewTogglePanelClassification = {45owner: 'osortega';46comment: 'Tracks when the user toggles the Changes panel open or closed.';47visible: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the Changes panel is now visible.' };48};4950export function logChangesViewToggle(telemetryService: ITelemetryService, visible: boolean): void {51telemetryService.publicLog2<ChangesViewTogglePanelEvent, ChangesViewTogglePanelClassification>('vscodeAgents.changesView/togglePanel', { visible });52}5354type ChangesViewVersionModeChangeEvent = {55mode: string;56};5758type ChangesViewVersionModeChangeClassification = {59owner: 'osortega';60comment: 'Tracks when the user switches the version mode in the Changes panel (Branch Changes, All Changes, Last Turn).';61mode: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The version mode selected by the user.' };62};6364export function logChangesViewVersionModeChange(telemetryService: ITelemetryService, mode: string): void {65telemetryService.publicLog2<ChangesViewVersionModeChangeEvent, ChangesViewVersionModeChangeClassification>('vscodeAgents.changesView/versionModeChange', { mode });66}6768type ChangesViewFileSelectEvent = {69changeType: string;70};7172type ChangesViewFileSelectClassification = {73owner: 'osortega';74comment: 'Tracks when the user selects a changed file in the Changes panel.';75changeType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The type of change (added, modified, deleted).' };76};7778export function logChangesViewFileSelect(telemetryService: ITelemetryService, changeType: string): void {79telemetryService.publicLog2<ChangesViewFileSelectEvent, ChangesViewFileSelectClassification>('vscodeAgents.changesView/fileSelect', { changeType });80}8182type ChangesViewViewModeChangeEvent = {83mode: string;84};8586type ChangesViewViewModeChangeClassification = {87owner: 'osortega';88comment: 'Tracks when the user switches between list and tree view modes in the Changes panel.';89mode: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The view mode selected by the user (list or tree).' };90};9192export function logChangesViewViewModeChange(telemetryService: ITelemetryService, mode: string): void {93telemetryService.publicLog2<ChangesViewViewModeChangeEvent, ChangesViewViewModeChangeClassification>('vscodeAgents.changesView/viewModeChange', { mode });94}9596type ChangesViewReviewCommentAddedEvent = {97hasExistingFeedback: boolean;98hasSuggestion: boolean;99isFromPRReview: boolean;100};101102type ChangesViewReviewCommentAddedClassification = {103owner: 'osortega';104comment: 'Tracks when a user adds a review comment (feedback) to a file in the Changes panel.';105hasExistingFeedback: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether there was already feedback on this file.' };106hasSuggestion: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the feedback includes a code suggestion.' };107isFromPRReview: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the feedback was converted from a PR review comment.' };108};109110export function logChangesViewReviewCommentAdded(telemetryService: ITelemetryService, data: { hasExistingFeedback: boolean; hasSuggestion: boolean; isFromPRReview: boolean }): void {111telemetryService.publicLog2<ChangesViewReviewCommentAddedEvent, ChangesViewReviewCommentAddedClassification>('vscodeAgents.changesView/reviewCommentAdded', data);112}113114// --- Tunnel agent host connect ---115116export type TunnelConnectErrorCategory =117| 'relayConnectionFailed'118| 'auth'119| 'authExpired'120| 'network'121| 'other';122123export type TunnelConnectFailureReason =124| 'hostOffline'125| 'maxAttemptsReached'126| 'auth'127| 'authExpired';128129type TunnelConnectAttemptEvent = {130isReconnect: boolean;131attempt: number;132durationMs: number;133success: boolean;134errorCategory: string;135};136137type TunnelConnectAttemptClassification = {138owner: 'osortega';139comment: 'Tracks individual agent-host tunnel connect attempts for performance and reliability.';140isReconnect: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether this attempt was part of a reconnect cycle (true) or an initial connect (false).' };141attempt: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Attempt number within the current connect session (1-based).' };142durationMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Duration of this individual attempt in milliseconds.' };143success: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether this individual attempt succeeded.' };144errorCategory: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Category of error when the attempt failed (relayConnectionFailed, auth, authExpired, network, other); empty on success.' };145};146147export function logTunnelConnectAttempt(telemetryService: ITelemetryService, data: { isReconnect: boolean; attempt: number; durationMs: number; success: boolean; errorCategory?: TunnelConnectErrorCategory }): void {148telemetryService.publicLog2<TunnelConnectAttemptEvent, TunnelConnectAttemptClassification>('vscodeAgents.tunnelConnect/attempt', {149isReconnect: data.isReconnect,150attempt: data.attempt,151durationMs: data.durationMs,152success: data.success,153errorCategory: data.errorCategory ?? '',154});155}156157type TunnelConnectResolvedEvent = {158isReconnect: boolean;159totalAttempts: number;160totalDurationMs: number;161success: boolean;162failureReason: string;163};164165type TunnelConnectResolvedClassification = {166owner: 'osortega';167comment: 'Tracks overall agent-host tunnel connect session outcomes for reliability.';168isReconnect: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the resolved session was a reconnect cycle (true) or an initial connect (false).' };169totalAttempts: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Total number of attempts made before resolution.' };170totalDurationMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Total elapsed time from session start to resolution in milliseconds.' };171success: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether the connect session ultimately succeeded.' };172failureReason: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Reason the session terminated without connecting (hostOffline, maxAttemptsReached, auth, authExpired); empty on success.' };173};174175export function logTunnelConnectResolved(telemetryService: ITelemetryService, data: { isReconnect: boolean; totalAttempts: number; totalDurationMs: number; success: boolean; failureReason?: TunnelConnectFailureReason }): void {176telemetryService.publicLog2<TunnelConnectResolvedEvent, TunnelConnectResolvedClassification>('vscodeAgents.tunnelConnect/resolved', {177isReconnect: data.isReconnect,178totalAttempts: data.totalAttempts,179totalDurationMs: data.totalDurationMs,180success: data.success,181failureReason: data.failureReason ?? '',182});183}184185// --- Socket lifecycle telemetry ---186187export type SocketCloseTrigger =188| 'server'189| 'sendOnDeadSocket'190| 'visibility'191| 'offline'192| 'malformedFrames'193| 'disposed'194| 'error';195196type SocketCloseEvent = {197closeCode: number;198wasClean: boolean;199lifetimeMs: number;200messagesSent: number;201messagesReceived: number;202messagesDropped: number;203trigger: string;204};205206type SocketCloseClassification = {207owner: 'osortega';208comment: 'Tracks WebSocket close events for agent host connections to measure connection reliability.';209closeCode: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'WebSocket close code.' };210wasClean: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether the close was clean.' };211lifetimeMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'How long the socket was alive in milliseconds.' };212messagesSent: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Total messages sent.' };213messagesReceived: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Total messages received.' };214messagesDropped: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Total messages dropped due to non-OPEN socket.' };215trigger: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'What triggered the close (server, sendOnDeadSocket, visibility, offline, malformedFrames, disposed, error).' };216};217218export function logSocketClose(telemetryService: ITelemetryService, data: { closeCode: number; wasClean: boolean; lifetimeMs: number; messagesSent: number; messagesReceived: number; messagesDropped: number; trigger: SocketCloseTrigger }): void {219telemetryService.publicLog2<SocketCloseEvent, SocketCloseClassification>('vscodeAgents.socket/close', data);220}221222// --- Send dropped telemetry ---223224type SendDroppedEvent = {225readyState: number;226timeSinceLastReceiveMs: number;227timeSinceLastSendMs: number;228};229230type SendDroppedClassification = {231owner: 'osortega';232comment: 'Tracks when a message is silently dropped due to a non-OPEN WebSocket, indicating a zombie socket.';233readyState: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'WebSocket readyState at drop time (0=CONNECTING, 1=OPEN, 2=CLOSING, 3=CLOSED).' };234timeSinceLastReceiveMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Milliseconds since last received message.' };235timeSinceLastSendMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Milliseconds since last sent message.' };236};237238export function logSendDropped(telemetryService: ITelemetryService, data: { readyState: number; timeSinceLastReceiveMs: number; timeSinceLastSendMs: number }): void {239telemetryService.publicLog2<SendDroppedEvent, SendDroppedClassification>('vscodeAgents.socket/sendDropped', data);240}241242// --- Visibility resumed telemetry ---243244type VisibilityResumedEvent = {245hiddenDurationMs: number;246socketAlive: boolean;247forceClosed: boolean;248};249250type VisibilityResumedClassification = {251owner: 'osortega';252comment: 'Tracks tab visibility resume events to measure zombie socket detection effectiveness.';253hiddenDurationMs: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'How long the tab was hidden in milliseconds.' };254socketAlive: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether the socket was alive after zombie detection check.' };255forceClosed: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether the socket was force-closed on resume.' };256};257258export function logVisibilityResumed(telemetryService: ITelemetryService, data: { hiddenDurationMs: number; socketAlive: boolean; forceClosed: boolean }): void {259telemetryService.publicLog2<VisibilityResumedEvent, VisibilityResumedClassification>('vscodeAgents.socket/visibilityResumed', data);260}261262// --- Terminal recovery telemetry ---263264type TerminalRecoveryEvent = {265recoveredCount: number;266totalCount: number;267};268269type TerminalRecoveryClassification = {270owner: 'osortega';271comment: 'Tracks terminal reconnection outcomes after agent host disconnect.';272recoveredCount: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Number of terminals successfully reconnected.' };273totalCount: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Total number of active terminals at reconnect time.' };274};275276export function logTerminalRecovery(telemetryService: ITelemetryService, data: { recoveredCount: number; totalCount: number }): void {277telemetryService.publicLog2<TerminalRecoveryEvent, TerminalRecoveryClassification>('vscodeAgents.terminal/recovery', data);278}279280281