44 *--------------------------------------------------------------------------------------------*/
55
66import { WorkspaceInfo } from '@gitpod/gitpod-protocol' ;
7- import { Workspace , WorkspaceInstanceStatus_Phase } from '@gitpod/public-api/lib/gitpod/experimental/v1/workspaces_pb' ;
87import * as vscode from 'vscode' ;
98import { Disposable } from './common/dispose' ;
109import { withServerApi } from './internalApi' ;
@@ -13,6 +12,7 @@ import { SSHConnectionParams } from './remote';
1312import { ISessionService } from './services/sessionService' ;
1413import { ILogService } from './services/logService' ;
1514import { RawTelemetryEventProperties } from './common/telemetry' ;
15+ import { WorkspaceState } from './workspaceState' ;
1616
1717const IDEHeartbeatTelemetryEvent = 'ide_heartbeat' ;
1818interface IDEHeartbeatTelemetryData extends RawTelemetryEventProperties {
@@ -23,7 +23,7 @@ interface IDEHeartbeatTelemetryData extends RawTelemetryEventProperties {
2323 instanceId : string ;
2424 gitpodHost : string ;
2525 debugWorkspace : 'true' | 'false' ;
26- delta ?: { [ key : string ] : number ; } ;
26+ delta ?: { [ key : string ] : number } ;
2727}
2828
2929export class HeartbeatManager extends Disposable {
@@ -39,17 +39,17 @@ export class HeartbeatManager extends Disposable {
3939 private eventCounterMap = new Map < string , number > ( ) ;
4040
4141 private ideHeartbeatTelemetryHandle : NodeJS . Timer | undefined ;
42- private ideHeartbeatData : Pick < IDEHeartbeatTelemetryData , " successfulCount" | " totalCount" > = {
42+ private ideHeartbeatData : Pick < IDEHeartbeatTelemetryData , ' successfulCount' | ' totalCount' > = {
4343 successfulCount : 0 ,
4444 totalCount : 0 ,
45- }
45+ } ;
4646
4747 constructor (
4848 private readonly connectionInfo : SSHConnectionParams ,
49+ private readonly workspaceState : WorkspaceState | undefined ,
4950 private readonly sessionService : ISessionService ,
5051 private readonly logService : ILogService ,
5152 private readonly telemetryService : ITelemetryService ,
52- private readonly usePublicApi : boolean ,
5353 ) {
5454 super ( ) ;
5555 this . _register ( vscode . window . onDidChangeActiveTextEditor ( e => this . updateLastActivity ( 'onDidChangeActiveTextEditor' , e ?. document ) ) ) ;
@@ -123,6 +123,15 @@ export class HeartbeatManager extends Disposable {
123123 } , HeartbeatManager . HEARTBEAT_INTERVAL ) ;
124124
125125 this . ideHeartbeatTelemetryHandle = setInterval ( ( ) => this . sendIDEHeartbeatTelemetry ( ) , HeartbeatManager . IDE_HEARTBEAT_INTERVAL ) ;
126+
127+ if ( this . workspaceState ) {
128+ this . _register ( this . workspaceState . onWorkspaceStopped ( ( ) => {
129+ this . logService . trace ( 'Stopping heartbeat as workspace is not running' ) ;
130+ this . stopHeartbeat ( ) ;
131+ this . stopIDEHeartbeatTelemetry ( ) ;
132+ this . sendIDEHeartbeatTelemetry ( ) ;
133+ } ) ) ;
134+ }
126135 }
127136
128137 private updateLastActivity ( event : string , document ?: vscode . TextDocument ) {
@@ -136,39 +145,48 @@ export class HeartbeatManager extends Disposable {
136145 }
137146
138147 private async sendHeartBeat ( wasClosed ?: true ) {
139- let heartbeatSucceed = false
148+ let heartbeatSucceed = false ;
140149 try {
141- await withServerApi ( this . sessionService . getGitpodToken ( ) , this . connectionInfo . gitpodHost , async service => {
142- const workspaceInfo = this . usePublicApi
143- ? await this . sessionService . getAPI ( ) . getWorkspace ( this . connectionInfo . workspaceId )
144- : await service . server . getWorkspace ( this . connectionInfo . workspaceId ) ;
145- this . isWorkspaceRunning = this . usePublicApi
146- ? ( workspaceInfo as Workspace ) ?. status ?. instance ?. status ?. phase === WorkspaceInstanceStatus_Phase . RUNNING && ( workspaceInfo as Workspace ) ?. status ?. instance ?. instanceId === this . connectionInfo . instanceId
147- : ( workspaceInfo as WorkspaceInfo ) . latestInstance ?. status ?. phase === 'running' && ( workspaceInfo as WorkspaceInfo ) . latestInstance ?. id === this . connectionInfo . instanceId ;
148- if ( this . isWorkspaceRunning ) {
149- this . usePublicApi
150- ? ( ! wasClosed ? await this . sessionService . getAPI ( ) . sendHeartbeat ( this . connectionInfo . workspaceId ) : await this . sessionService . getAPI ( ) . sendDidClose ( this . connectionInfo . workspaceId ) )
151- : await service . server . sendHeartBeat ( { instanceId : this . connectionInfo . instanceId , wasClosed } ) ;
152- if ( wasClosed ) {
150+ if ( this . workspaceState ) {
151+ if ( this . workspaceState . isWorkspaceRunning ( ) ) {
152+ if ( ! wasClosed ) {
153+ await this . sessionService . getAPI ( ) . sendHeartbeat ( this . connectionInfo . workspaceId ) ;
154+ this . logService . trace ( `Send heartbeat, triggered by ${ this . lastActivityEvent } event` ) ;
155+ } else {
156+ await this . sessionService . getAPI ( ) . sendDidClose ( this . connectionInfo . workspaceId ) ;
153157 this . telemetryService . sendTelemetryEvent ( this . connectionInfo . gitpodHost , 'ide_close_signal' , { workspaceId : this . connectionInfo . workspaceId , instanceId : this . connectionInfo . instanceId , gitpodHost : this . connectionInfo . gitpodHost , clientKind : 'vscode' , debugWorkspace : String ( ! ! this . connectionInfo . debugWorkspace ) } ) ;
154158 this . logService . trace ( `Send closed heartbeat` ) ;
155- } else {
156- this . logService . trace ( `Send heartbeat, triggered by ${ this . lastActivityEvent } event` ) ;
157159 }
158160 heartbeatSucceed = true ;
159- } else {
160- this . logService . trace ( 'Stopping heartbeat as workspace is not running' ) ;
161- this . stopHeartbeat ( ) ;
162- this . stopIDEHeartbeatTelemetry ( ) ;
163161 }
164- } , this . logService ) ;
162+ } else {
163+ await withServerApi ( this . sessionService . getGitpodToken ( ) , this . connectionInfo . gitpodHost , async service => {
164+ const workspaceInfo = await service . server . getWorkspace ( this . connectionInfo . workspaceId ) ;
165+ this . isWorkspaceRunning = workspaceInfo . latestInstance ?. status ?. phase === 'running' && ( workspaceInfo as WorkspaceInfo ) . latestInstance ?. id === this . connectionInfo . instanceId ;
166+ if ( this . isWorkspaceRunning ) {
167+ await service . server . sendHeartBeat ( { instanceId : this . connectionInfo . instanceId , wasClosed } ) ;
168+ if ( wasClosed ) {
169+ this . telemetryService . sendTelemetryEvent ( this . connectionInfo . gitpodHost , 'ide_close_signal' , { workspaceId : this . connectionInfo . workspaceId , instanceId : this . connectionInfo . instanceId , gitpodHost : this . connectionInfo . gitpodHost , clientKind : 'vscode' , debugWorkspace : String ( ! ! this . connectionInfo . debugWorkspace ) } ) ;
170+ this . logService . trace ( `Send closed heartbeat` ) ;
171+ } else {
172+ this . logService . trace ( `Send heartbeat, triggered by ${ this . lastActivityEvent } event` ) ;
173+ }
174+ heartbeatSucceed = true ;
175+ } else {
176+ this . logService . trace ( 'Stopping heartbeat as workspace is not running' ) ;
177+ this . stopHeartbeat ( ) ;
178+ this . stopIDEHeartbeatTelemetry ( ) ;
179+ this . sendIDEHeartbeatTelemetry ( ) ;
180+ }
181+ } , this . logService ) ;
182+ }
165183 } catch ( e ) {
166184 const suffix = wasClosed ? 'closed heartbeat' : 'heartbeat' ;
167185 const originMsg = e . message ;
168186 e . message = `Failed to send ${ suffix } , triggered by event: ${ this . lastActivityEvent } : ${ originMsg } ` ;
169187 this . logService . error ( e ) ;
170188 e . message = `Failed to send ${ suffix } : ${ originMsg } ` ;
171- this . telemetryService . sendTelemetryException ( this . connectionInfo . gitpodHost , e , { workspaceId : this . connectionInfo . workspaceId , instanceId : this . connectionInfo . instanceId } ) ;
189+ this . telemetryService . sendTelemetryException ( this . connectionInfo . gitpodHost , e , { workspaceId : this . connectionInfo . workspaceId , instanceId : this . connectionInfo . instanceId } ) ;
172190 } finally {
173191 if ( heartbeatSucceed ) {
174192 this . ideHeartbeatData . successfulCount ++ ;
@@ -184,6 +202,13 @@ export class HeartbeatManager extends Disposable {
184202 }
185203 }
186204
205+ private stopIDEHeartbeatTelemetry ( ) {
206+ if ( this . ideHeartbeatTelemetryHandle ) {
207+ clearInterval ( this . ideHeartbeatTelemetryHandle ) ;
208+ this . ideHeartbeatTelemetryHandle = undefined ;
209+ }
210+ }
211+
187212 private sendIDEHeartbeatTelemetry ( ) {
188213 this . telemetryService . sendRawTelemetryEvent ( this . connectionInfo . gitpodHost , IDEHeartbeatTelemetryEvent , {
189214 ...this . ideHeartbeatData ,
@@ -200,19 +225,12 @@ export class HeartbeatManager extends Disposable {
200225 this . ideHeartbeatData . totalCount = 0 ;
201226 }
202227
203- private stopIDEHeartbeatTelemetry ( ) {
204- if ( this . ideHeartbeatTelemetryHandle ) {
205- clearInterval ( this . ideHeartbeatTelemetryHandle ) ;
206- this . ideHeartbeatTelemetryHandle = undefined ;
207- this . sendIDEHeartbeatTelemetry ( )
208- }
209- }
210-
211228 public override async dispose ( ) : Promise < void > {
212229 super . dispose ( ) ;
213230 this . stopIDEHeartbeatTelemetry ( ) ;
214231 this . stopHeartbeat ( ) ;
215- if ( this . isWorkspaceRunning ) {
232+ if ( this . workspaceState ?. isWorkspaceRunning ( ) ?? this . isWorkspaceRunning ) {
233+ this . sendIDEHeartbeatTelemetry ( ) ;
216234 await this . sendHeartBeat ( true ) ;
217235 }
218236 }
0 commit comments