Skip to content

Commit 239e555

Browse files
samikshya-dbclaude
andcommitted
Add connection close telemetry event
Implement CONNECTION_CLOSE telemetry event to track session lifecycle: - Add CONNECTION_CLOSE event type to TelemetryEventType enum - Add emitConnectionClose() method to TelemetryEventEmitter - Add processConnectionCloseEvent() handler in MetricsAggregator - Track session open time in DBSQLSession and emit close event with latency - Remove unused TOperationType import from DBSQLOperation This provides complete session telemetry: connection open, statement execution, and connection close with latencies for each operation. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent a37fdf0 commit 239e555

15 files changed

Lines changed: 468 additions & 179 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,15 @@ To enable or disable telemetry explicitly:
8484

8585
```javascript
8686
const client = new DBSQLClient({
87-
telemetryEnabled: true, // Enable telemetry (default: false)
87+
telemetryEnabled: true, // Enable telemetry (default: false)
8888
});
8989

9090
// Or override per connection:
9191
await client.connect({
9292
host: '********.databricks.com',
9393
path: '/sql/2.0/warehouses/****************',
9494
token: 'dapi********************************',
95-
telemetryEnabled: false, // Disable for this connection
95+
telemetryEnabled: false, // Disable for this connection
9696
});
9797
```
9898

docs/TELEMETRY.md

Lines changed: 65 additions & 30 deletions
Large diffs are not rendered by default.

lib/DBSQLOperation.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import IOperation, {
1313
import {
1414
TGetOperationStatusResp,
1515
TOperationHandle,
16-
TOperationType,
1716
TTableSchema,
1817
TSparkDirectResults,
1918
TGetResultSetMetadataResp,
@@ -509,7 +508,7 @@ export default class DBSQLOperation implements IOperation {
509508
*/
510509
private emitStatementStart(): void {
511510
try {
512-
const {telemetryEmitter} = (this.context as any);
511+
const { telemetryEmitter } = this.context as any;
513512
if (!telemetryEmitter) {
514513
return;
515514
}
@@ -530,8 +529,8 @@ export default class DBSQLOperation implements IOperation {
530529
*/
531530
private async emitStatementComplete(): Promise<void> {
532531
try {
533-
const {telemetryEmitter} = (this.context as any);
534-
const {telemetryAggregator} = (this.context as any);
532+
const { telemetryEmitter } = this.context as any;
533+
const { telemetryAggregator } = this.context as any;
535534
if (!telemetryEmitter || !telemetryAggregator) {
536535
return;
537536
}
@@ -571,7 +570,7 @@ export default class DBSQLOperation implements IOperation {
571570
*/
572571
private emitErrorEvent(error: Error): void {
573572
try {
574-
const {telemetryEmitter} = (this.context as any);
573+
const { telemetryEmitter } = this.context as any;
575574
if (!telemetryEmitter) {
576575
return;
577576
}

lib/DBSQLSession.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,8 @@ export default class DBSQLSession implements IDBSQLSession {
151151

152152
private isOpen = true;
153153

154+
private openTime: number;
155+
154156
private serverProtocolVersion?: TProtocolVersion;
155157

156158
public onClose?: () => void;
@@ -169,6 +171,7 @@ export default class DBSQLSession implements IDBSQLSession {
169171
constructor({ handle, context, serverProtocolVersion }: DBSQLSessionConstructorOptions) {
170172
this.sessionHandle = handle;
171173
this.context = context;
174+
this.openTime = Date.now();
172175
// Get the server protocol version from the provided parameter (from TOpenSessionResp)
173176
this.serverProtocolVersion = serverProtocolVersion;
174177
this.context.getLogger().log(LogLevel.debug, `Session created with id: ${this.id}`);
@@ -594,6 +597,16 @@ export default class DBSQLSession implements IDBSQLSession {
594597
this.onClose?.();
595598
this.isOpen = false;
596599

600+
// Emit connection close telemetry
601+
const closeLatency = Date.now() - this.openTime;
602+
const { telemetryEmitter } = this.context as any;
603+
if (telemetryEmitter) {
604+
telemetryEmitter.emitConnectionClose({
605+
sessionId: this.id,
606+
latencyMs: closeLatency,
607+
});
608+
}
609+
597610
this.context.getLogger().log(LogLevel.debug, `Session closed with id: ${this.id}`);
598611
return new Status(response.status);
599612
}

lib/result/CloudFetchResultHandler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ export default class CloudFetchResultHandler implements IResultsProvider<ArrowBa
145145
return;
146146
}
147147

148-
const {telemetryEmitter} = (this.context as any);
148+
const { telemetryEmitter } = this.context as any;
149149
if (!telemetryEmitter) {
150150
return;
151151
}

lib/telemetry/MetricsAggregator.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,11 @@ export default class MetricsAggregator {
106106
return;
107107
}
108108

109+
if (event.eventType === TelemetryEventType.CONNECTION_CLOSE) {
110+
this.processConnectionCloseEvent(event);
111+
return;
112+
}
113+
109114
// Error events - check if terminal or retryable
110115
if (event.eventType === TelemetryEventType.ERROR) {
111116
this.processErrorEvent(event);
@@ -143,6 +148,21 @@ export default class MetricsAggregator {
143148
this.addPendingMetric(metric);
144149
}
145150

151+
/**
152+
* Process connection close event (emit immediately)
153+
*/
154+
private processConnectionCloseEvent(event: TelemetryEvent): void {
155+
const metric: TelemetryMetric = {
156+
metricType: 'connection',
157+
timestamp: event.timestamp,
158+
sessionId: event.sessionId,
159+
driverConfig: this.driverConfig,
160+
latencyMs: event.latencyMs,
161+
};
162+
163+
this.addPendingMetric(metric);
164+
}
165+
146166
/**
147167
* Process error event (terminal errors flushed immediately, retryable buffered)
148168
*/

lib/telemetry/TelemetryEventEmitter.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,29 @@ export default class TelemetryEventEmitter extends EventEmitter {
7070
}
7171
}
7272

73+
/**
74+
* Emit a connection close event.
75+
*
76+
* @param data Connection close event data including sessionId and latencyMs
77+
*/
78+
emitConnectionClose(data: { sessionId: string; latencyMs: number }): void {
79+
if (!this.enabled) return;
80+
81+
const logger = this.context.getLogger();
82+
try {
83+
const event: TelemetryEvent = {
84+
eventType: TelemetryEventType.CONNECTION_CLOSE,
85+
timestamp: Date.now(),
86+
sessionId: data.sessionId,
87+
latencyMs: data.latencyMs,
88+
};
89+
this.emit(TelemetryEventType.CONNECTION_CLOSE, event);
90+
} catch (error: any) {
91+
// Swallow all exceptions - log at debug level only
92+
logger.log(LogLevel.debug, `Error emitting connection close event: ${error.message}`);
93+
}
94+
}
95+
7396
/**
7497
* Emit a statement start event.
7598
*

lib/telemetry/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const DRIVER_NAME = 'nodejs-sql-driver';
2424
*/
2525
export enum TelemetryEventType {
2626
CONNECTION_OPEN = 'connection.open',
27+
CONNECTION_CLOSE = 'connection.close',
2728
STATEMENT_START = 'statement.start',
2829
STATEMENT_COMPLETE = 'statement.complete',
2930
CLOUDFETCH_CHUNK = 'cloudfetch.chunk',

0 commit comments

Comments
 (0)