@@ -7,16 +7,25 @@ import { MACHINE_METADATA } from "./constants.js";
77import { EventCache } from "./eventCache.js" ;
88import { detectContainerEnv } from "../helpers/container.js" ;
99import type { DeviceId } from "../helpers/deviceId.js" ;
10+ import { EventEmitter } from "events" ;
1011
1112type EventResult = {
1213 success : boolean ;
1314 error ?: Error ;
1415} ;
1516
17+ export interface TelemetryEvents {
18+ "events-emitted" : [ ] ;
19+ "events-send-failed" : [ ] ;
20+ "events-skipped" : [ ] ;
21+ }
22+
1623export class Telemetry {
1724 private isBufferingEvents : boolean = true ;
1825 /** Resolves when the setup is complete or a timeout occurs */
1926 public setupPromise : Promise < [ string , boolean ] > | undefined ;
27+ public readonly events : EventEmitter < TelemetryEvents > = new EventEmitter ( ) ;
28+
2029 private eventCache : EventCache ;
2130 private deviceId : DeviceId ;
2231
@@ -57,6 +66,12 @@ export class Telemetry {
5766
5867 private async setup ( ) : Promise < void > {
5968 if ( ! this . isTelemetryEnabled ( ) ) {
69+ this . session . logger . info ( {
70+ id : LogId . telemetryEmitFailure ,
71+ context : "telemetry" ,
72+ message : "Telemetry is disabled." ,
73+ noRedaction : true ,
74+ } ) ;
6075 return ;
6176 }
6277
@@ -71,34 +86,47 @@ export class Telemetry {
7186
7287 public async close ( ) : Promise < void > {
7388 this . isBufferingEvents = false ;
74- await this . emitEvents ( this . eventCache . getEvents ( ) ) ;
89+
90+ this . session . logger . debug ( {
91+ id : LogId . telemetryClose ,
92+ message : `Closing telemetry and flushing ${ this . eventCache . size } events` ,
93+ context : "telemetry" ,
94+ } ) ;
95+
96+ // Wait up to 5 seconds for events to be sent before closing, but don't throw if it times out
97+ const flushMaxWaitTime = 5000 ;
98+ let flushTimeout : NodeJS . Timeout | undefined ;
99+ await Promise . race ( [
100+ new Promise < void > ( ( resolve ) => {
101+ flushTimeout = setTimeout ( ( ) => {
102+ this . session . logger . debug ( {
103+ id : LogId . telemetryClose ,
104+ message : `Failed to flush remaining events within ${ flushMaxWaitTime } ms timeout` ,
105+ context : "telemetry" ,
106+ } ) ;
107+ resolve ( ) ;
108+ } , flushMaxWaitTime ) ;
109+ flushTimeout . unref ( ) ;
110+ } ) ,
111+ this . emit ( [ ] ) ,
112+ ] ) ;
113+
114+ clearTimeout ( flushTimeout ) ;
75115 }
76116
77117 /**
78118 * Emits events through the telemetry pipeline
79119 * @param events - The events to emit
80120 */
81- public async emitEvents ( events : BaseEvent [ ] ) : Promise < void > {
82- try {
83- if ( ! this . isTelemetryEnabled ( ) ) {
84- this . session . logger . info ( {
85- id : LogId . telemetryEmitFailure ,
86- context : "telemetry" ,
87- message : "Telemetry is disabled." ,
88- noRedaction : true ,
89- } ) ;
90- return ;
91- }
92-
93- await this . emit ( events ) ;
94- } catch {
95- this . session . logger . debug ( {
96- id : LogId . telemetryEmitFailure ,
97- context : "telemetry" ,
98- message : "Error emitting telemetry events." ,
99- noRedaction : true ,
100- } ) ;
121+ public emitEvents ( events : BaseEvent [ ] ) : void {
122+ if ( ! this . isTelemetryEnabled ( ) ) {
123+ this . events . emit ( "events-skipped" ) ;
124+ return ;
101125 }
126+
127+ // Don't wait for events to be sent - we should not block regular server
128+ // operations on telemetry
129+ void this . emit ( events ) ;
102130 }
103131
104132 /**
@@ -144,32 +172,44 @@ export class Telemetry {
144172 return ;
145173 }
146174
147- const cachedEvents = this . eventCache . getEvents ( ) ;
148- const allEvents = [ ...cachedEvents , ...events ] ;
175+ try {
176+ const cachedEvents = this . eventCache . getEvents ( ) ;
177+ const allEvents = [ ...cachedEvents . map ( ( e ) => e . event ) , ...events ] ;
149178
150- this . session . logger . debug ( {
151- id : LogId . telemetryEmitStart ,
152- context : "telemetry" ,
153- message : `Attempting to send ${ allEvents . length } events (${ cachedEvents . length } cached)` ,
154- } ) ;
179+ this . session . logger . debug ( {
180+ id : LogId . telemetryEmitStart ,
181+ context : "telemetry" ,
182+ message : `Attempting to send ${ allEvents . length } events (${ cachedEvents . length } cached)` ,
183+ } ) ;
184+
185+ const result = await this . sendEvents ( this . session . apiClient , allEvents ) ;
186+ if ( result . success ) {
187+ this . eventCache . removeEvents ( cachedEvents . map ( ( e ) => e . id ) ) ;
188+ this . session . logger . debug ( {
189+ id : LogId . telemetryEmitSuccess ,
190+ context : "telemetry" ,
191+ message : `Sent ${ allEvents . length } events successfully: ${ JSON . stringify ( allEvents ) } ` ,
192+ } ) ;
193+ this . events . emit ( "events-emitted" ) ;
194+ return ;
195+ }
155196
156- const result = await this . sendEvents ( this . session . apiClient , allEvents ) ;
157- if ( result . success ) {
158- this . eventCache . clearEvents ( ) ;
159197 this . session . logger . debug ( {
160- id : LogId . telemetryEmitSuccess ,
198+ id : LogId . telemetryEmitFailure ,
161199 context : "telemetry" ,
162- message : `Sent ${ allEvents . length } events successfully : ${ JSON . stringify ( allEvents , null , 2 ) } ` ,
200+ message : `Error sending event to client : ${ result . error instanceof Error ? result . error . message : String ( result . error ) } ` ,
163201 } ) ;
164- return ;
202+ this . eventCache . appendEvents ( events ) ;
203+ this . events . emit ( "events-send-failed" ) ;
204+ } catch ( error ) {
205+ this . session . logger . debug ( {
206+ id : LogId . telemetryEmitFailure ,
207+ context : "telemetry" ,
208+ message : `Error emitting telemetry events: ${ error instanceof Error ? error . message : String ( error ) } ` ,
209+ noRedaction : true ,
210+ } ) ;
211+ this . events . emit ( "events-send-failed" ) ;
165212 }
166-
167- this . session . logger . debug ( {
168- id : LogId . telemetryEmitFailure ,
169- context : "telemetry" ,
170- message : `Error sending event to client: ${ result . error instanceof Error ? result . error . message : String ( result . error ) } ` ,
171- } ) ;
172- this . eventCache . appendEvents ( events ) ;
173213 }
174214
175215 /**
0 commit comments