@@ -12,19 +12,37 @@ import { ITelemetryService } from './services/telemetryService';
12
12
import { SSHConnectionParams } from './remote' ;
13
13
import { ISessionService } from './services/sessionService' ;
14
14
import { ILogService } from './services/logService' ;
15
+ import { RawTelemetryEventProperties } from './common/telemetry' ;
16
+
17
+ const IDEHeartbeatTelemetryEvent = 'ide_heartbeat' ;
18
+ interface IDEHeartbeatTelemetryData extends RawTelemetryEventProperties {
19
+ clientKind : 'vscode-desktop' ; // 'ssh' | 'jetbrains' | 'vscode-desktop' | 'supervisor-frontend';
20
+ totalCount : number ;
21
+ successfulCount : number ;
22
+ workspaceId : string ;
23
+ instanceId : string ;
24
+ gitpodHost : string ;
25
+ debugWorkspace : 'true' | 'false' ;
26
+ delta ?: { [ key : string ] : number ; } ;
27
+ }
15
28
16
29
export class HeartbeatManager extends Disposable {
17
30
18
31
static HEARTBEAT_INTERVAL = 30000 ;
19
- static EVENT_COUNTER_INTERVAL = 3600000 ;
32
+ static IDE_HEARTBEAT_INTERVAL = 900000 ; // 15 minutes
20
33
21
34
private lastActivity = new Date ( ) . getTime ( ) ;
22
35
private lastActivityEvent : string = 'init' ;
23
36
private isWorkspaceRunning = true ;
24
37
private heartBeatHandle : NodeJS . Timer | undefined ;
25
38
26
39
private eventCounterMap = new Map < string , number > ( ) ;
27
- private eventCounterHandle : NodeJS . Timer | undefined ;
40
+
41
+ private ideHeartbeatTelemetryHandle : NodeJS . Timer | undefined ;
42
+ private ideHeartbeatData : Pick < IDEHeartbeatTelemetryData , "successfulCount" | "totalCount" > = {
43
+ successfulCount : 0 ,
44
+ totalCount : 0 ,
45
+ }
28
46
29
47
constructor (
30
48
private readonly connectionInfo : SSHConnectionParams ,
@@ -104,7 +122,7 @@ export class HeartbeatManager extends Disposable {
104
122
this . sendHeartBeat ( ) ;
105
123
} , HeartbeatManager . HEARTBEAT_INTERVAL ) ;
106
124
107
- this . eventCounterHandle = setInterval ( ( ) => this . sendEventData ( ) , HeartbeatManager . EVENT_COUNTER_INTERVAL ) ;
125
+ this . ideHeartbeatTelemetryHandle = setInterval ( ( ) => this . sendIDEHeartbeatTelemetry ( ) , HeartbeatManager . IDE_HEARTBEAT_INTERVAL ) ;
108
126
}
109
127
110
128
private updateLastActivity ( event : string , document ?: vscode . TextDocument ) {
@@ -118,6 +136,7 @@ export class HeartbeatManager extends Disposable {
118
136
}
119
137
120
138
private async sendHeartBeat ( wasClosed ?: true ) {
139
+ let heartbeatSucceed = false
121
140
try {
122
141
await withServerApi ( this . sessionService . getGitpodToken ( ) , this . connectionInfo . gitpodHost , async service => {
123
142
const workspaceInfo = this . usePublicApi
@@ -136,9 +155,11 @@ export class HeartbeatManager extends Disposable {
136
155
} else {
137
156
this . logService . trace ( `Send heartbeat, triggered by ${ this . lastActivityEvent } event` ) ;
138
157
}
158
+ heartbeatSucceed = true ;
139
159
} else {
140
160
this . logService . trace ( 'Stopping heartbeat as workspace is not running' ) ;
141
161
this . stopHeartbeat ( ) ;
162
+ this . stopIDEHeartbeatTelemetry ( ) ;
142
163
}
143
164
} , this . logService ) ;
144
165
} catch ( e ) {
@@ -148,6 +169,11 @@ export class HeartbeatManager extends Disposable {
148
169
this . logService . error ( e ) ;
149
170
e . message = `Failed to send ${ suffix } : ${ originMsg } ` ;
150
171
this . telemetryService . sendTelemetryException ( this . connectionInfo . gitpodHost , e , { workspaceId : this . connectionInfo . workspaceId , instanceId : this . connectionInfo . instanceId } ) ;
172
+ } finally {
173
+ if ( heartbeatSucceed ) {
174
+ this . ideHeartbeatData . successfulCount ++ ;
175
+ }
176
+ this . ideHeartbeatData . totalCount ++ ;
151
177
}
152
178
}
153
179
@@ -158,22 +184,33 @@ export class HeartbeatManager extends Disposable {
158
184
}
159
185
}
160
186
161
- private sendEventData ( ) {
162
- this . telemetryService . sendRawTelemetryEvent ( this . connectionInfo . gitpodHost , 'vscode_desktop_heartbeat_delta' , { events : Object . fromEntries ( this . eventCounterMap ) , workspaceId : this . connectionInfo . workspaceId , instanceId : this . connectionInfo . instanceId , gitpodHost : this . connectionInfo . gitpodHost , clientKind : 'vscode' } ) ;
187
+ private sendIDEHeartbeatTelemetry ( ) {
188
+ this . telemetryService . sendRawTelemetryEvent ( this . connectionInfo . gitpodHost , IDEHeartbeatTelemetryEvent , {
189
+ ...this . ideHeartbeatData ,
190
+ workspaceId : this . connectionInfo . workspaceId ,
191
+ instanceId : this . connectionInfo . instanceId ,
192
+ gitpodHost : this . connectionInfo . gitpodHost ,
193
+ clientKind : 'vscode-desktop' ,
194
+ debugWorkspace : String ( ! ! this . connectionInfo . debugWorkspace ) ,
195
+ delta : Object . fromEntries ( this . eventCounterMap ) ,
196
+ } as IDEHeartbeatTelemetryData ) ;
197
+
163
198
this . eventCounterMap . clear ( ) ;
199
+ this . ideHeartbeatData . successfulCount = 0 ;
200
+ this . ideHeartbeatData . totalCount = 0 ;
164
201
}
165
202
166
- private stopEventCounter ( ) {
167
- if ( this . eventCounterHandle ) {
168
- clearInterval ( this . eventCounterHandle ) ;
169
- this . eventCounterHandle = undefined ;
203
+ private stopIDEHeartbeatTelemetry ( ) {
204
+ if ( this . ideHeartbeatTelemetryHandle ) {
205
+ clearInterval ( this . ideHeartbeatTelemetryHandle ) ;
206
+ this . ideHeartbeatTelemetryHandle = undefined ;
207
+ this . sendIDEHeartbeatTelemetry ( )
170
208
}
171
209
}
172
210
173
211
public override async dispose ( ) : Promise < void > {
174
212
super . dispose ( ) ;
175
- this . stopEventCounter ( ) ;
176
- this . sendEventData ( ) ;
213
+ this . stopIDEHeartbeatTelemetry ( ) ;
177
214
this . stopHeartbeat ( ) ;
178
215
if ( this . isWorkspaceRunning ) {
179
216
await this . sendHeartBeat ( true ) ;
0 commit comments