Skip to content

Commit a37fdf0

Browse files
samikshya-dbclaude
andcommitted
Include system_configuration, driver_connection_params, and auth_type in all telemetry logs
- Cache driver config in MetricsAggregator when connection event is processed - Include cached driver config in all statement and error metrics - Export system_configuration, driver_connection_params, and auth_type for every log - Each telemetry log is now self-contained with full context This ensures every telemetry event (connection, statement, error) includes the driver configuration context, making logs independently analyzable. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 53189a8 commit a37fdf0

File tree

4 files changed

+142
-12
lines changed

4 files changed

+142
-12
lines changed

lib/telemetry/DatabricksTelemetryExporter.ts

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -288,9 +288,8 @@ export default class DatabricksTelemetryExporter {
288288
},
289289
};
290290

291-
// Add metric-specific fields based on proto definition
292-
if (metric.metricType === 'connection' && metric.driverConfig) {
293-
// Map driverConfig to system_configuration (snake_case as per proto)
291+
// Include system_configuration, driver_connection_params, and auth_type for ALL metrics (if available)
292+
if (metric.driverConfig) {
294293
log.entry.sql_driver_log.system_configuration = {
295294
driver_version: metric.driverConfig.driverVersion,
296295
driver_name: metric.driverConfig.driverName,
@@ -304,10 +303,7 @@ export default class DatabricksTelemetryExporter {
304303
char_set_encoding: metric.driverConfig.charSetEncoding,
305304
process_name: metric.driverConfig.processName,
306305
};
307-
// Include connection open latency
308-
if (metric.latencyMs !== undefined) {
309-
log.entry.sql_driver_log.operation_latency_ms = metric.latencyMs;
310-
}
306+
311307
// Include driver connection params (only if we have fields to include)
312308
if (
313309
metric.driverConfig.httpPath ||
@@ -326,8 +322,17 @@ export default class DatabricksTelemetryExporter {
326322
}),
327323
};
328324
}
325+
329326
// Include auth type at top level
330327
log.entry.sql_driver_log.auth_type = metric.driverConfig.authType;
328+
}
329+
330+
// Add metric-specific fields based on proto definition
331+
if (metric.metricType === 'connection') {
332+
// Include connection open latency
333+
if (metric.latencyMs !== undefined) {
334+
log.entry.sql_driver_log.operation_latency_ms = metric.latencyMs;
335+
}
331336
} else if (metric.metricType === 'statement') {
332337
log.entry.sql_driver_log.operation_latency_ms = metric.latencyMs;
333338

lib/telemetry/MetricsAggregator.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@
1616

1717
import IClientContext from '../contracts/IClientContext';
1818
import { LogLevel } from '../contracts/IDBSQLLogger';
19-
import { TelemetryEvent, TelemetryEventType, TelemetryMetric, DEFAULT_TELEMETRY_CONFIG } from './types';
19+
import {
20+
TelemetryEvent,
21+
TelemetryEventType,
22+
TelemetryMetric,
23+
DriverConfiguration,
24+
DEFAULT_TELEMETRY_CONFIG,
25+
} from './types';
2026
import DatabricksTelemetryExporter from './DatabricksTelemetryExporter';
2127
import ExceptionClassifier from './ExceptionClassifier';
2228

@@ -64,6 +70,8 @@ export default class MetricsAggregator {
6470

6571
private flushIntervalMs: number;
6672

73+
private driverConfig?: DriverConfiguration;
74+
6775
constructor(private context: IClientContext, private exporter: DatabricksTelemetryExporter) {
6876
try {
6977
const config = context.getConfig();
@@ -118,6 +126,11 @@ export default class MetricsAggregator {
118126
* Process connection event (emit immediately)
119127
*/
120128
private processConnectionEvent(event: TelemetryEvent): void {
129+
// Cache driver config for use in all subsequent metrics
130+
if (event.driverConfig) {
131+
this.driverConfig = event.driverConfig;
132+
}
133+
121134
const metric: TelemetryMetric = {
122135
metricType: 'connection',
123136
timestamp: event.timestamp,
@@ -153,13 +166,14 @@ export default class MetricsAggregator {
153166
details.errors.push(event);
154167
this.completeStatement(event.statementId);
155168
} else {
156-
// Standalone error - emit immediately
169+
// Standalone error - emit immediately (include cached driver config for context)
157170
const metric: TelemetryMetric = {
158171
metricType: 'error',
159172
timestamp: event.timestamp,
160173
sessionId: event.sessionId,
161174
statementId: event.statementId,
162175
workspaceId: event.workspaceId,
176+
driverConfig: this.driverConfig,
163177
errorName: event.errorName,
164178
errorMessage: event.errorMessage,
165179
};
@@ -245,13 +259,14 @@ export default class MetricsAggregator {
245259
return;
246260
}
247261

248-
// Create statement metric
262+
// Create statement metric (include cached driver config for context)
249263
const metric: TelemetryMetric = {
250264
metricType: 'statement',
251265
timestamp: details.startTime,
252266
sessionId: details.sessionId,
253267
statementId: details.statementId,
254268
workspaceId: details.workspaceId,
269+
driverConfig: this.driverConfig,
255270
operationType: details.operationType,
256271
latencyMs: details.executionLatencyMs,
257272
resultFormat: details.resultFormat,
@@ -263,14 +278,15 @@ export default class MetricsAggregator {
263278

264279
this.addPendingMetric(metric);
265280

266-
// Add buffered error metrics
281+
// Add buffered error metrics (include cached driver config for context)
267282
for (const errorEvent of details.errors) {
268283
const errorMetric: TelemetryMetric = {
269284
metricType: 'error',
270285
timestamp: errorEvent.timestamp,
271286
sessionId: details.sessionId,
272287
statementId: details.statementId,
273288
workspaceId: details.workspaceId,
289+
driverConfig: this.driverConfig,
274290
errorName: errorEvent.errorName,
275291
errorMessage: errorEvent.errorMessage,
276292
};

lib/telemetry/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ export interface TelemetryMetric {
151151
/** Workspace ID */
152152
workspaceId?: string;
153153

154-
/** Driver configuration (for connection metrics) */
154+
/** Driver configuration (included in all metrics for context) */
155155
driverConfig?: DriverConfiguration;
156156

157157
/** Execution latency in milliseconds */

tests/e2e/telemetry-local.test.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
/**
2+
* LOCAL TELEMETRY TEST - NOT FOR COMMIT
3+
*
4+
* This test verifies telemetry requests are properly sent.
5+
* Run locally with valid credentials to check telemetry payload structure.
6+
*
7+
* Set environment variables:
8+
* - DATABRICKS_SERVER_HOSTNAME
9+
* - DATABRICKS_HTTP_PATH
10+
* - DATABRICKS_TOKEN
11+
*/
12+
13+
import { DBSQLClient, LogLevel } from '../../lib';
14+
import IDBSQLLogger from '../../lib/contracts/IDBSQLLogger';
15+
16+
// Custom logger to capture telemetry debug logs
17+
class DebugLogger implements IDBSQLLogger {
18+
async log(level: LogLevel, message: string): Promise<void> {
19+
const timestamp = new Date().toISOString();
20+
const levelStr = LogLevel[level].padEnd(5);
21+
22+
// Highlight telemetry-related logs
23+
if (message.includes('telemetry') || message.includes('Telemetry')) {
24+
console.log(`\x1b[36m[${timestamp}] [${levelStr}] ${message}\x1b[0m`);
25+
} else {
26+
console.log(`[${timestamp}] [${levelStr}] ${message}`);
27+
}
28+
}
29+
}
30+
31+
describe('Telemetry E2E Test (Local Only)', () => {
32+
it('should send telemetry for SELECT 1 query', async function () {
33+
this.timeout(30000);
34+
35+
// Check for required environment variables
36+
const host = process.env.DATABRICKS_SERVER_HOSTNAME;
37+
const path = process.env.DATABRICKS_HTTP_PATH;
38+
const token = process.env.DATABRICKS_TOKEN;
39+
40+
if (!host || !path || !token) {
41+
console.log('\n❌ Skipping test: Missing environment variables');
42+
console.log('Set the following variables to run this test:');
43+
console.log(' - DATABRICKS_SERVER_HOSTNAME');
44+
console.log(' - DATABRICKS_HTTP_PATH');
45+
console.log(' - DATABRICKS_TOKEN\n');
46+
this.skip();
47+
return;
48+
}
49+
50+
console.log('\n' + '='.repeat(60));
51+
console.log('TELEMETRY E2E TEST');
52+
console.log('='.repeat(60));
53+
54+
const client = new DBSQLClient({
55+
logger: new DebugLogger(),
56+
});
57+
58+
console.log('\n📡 Connecting with telemetry enabled...\n');
59+
60+
const connection = await client.connect({
61+
host,
62+
path,
63+
token,
64+
telemetryEnabled: true,
65+
telemetryBatchSize: 1, // Flush immediately for testing
66+
});
67+
68+
console.log('\n' + '='.repeat(60));
69+
console.log('EXECUTING SELECT 1');
70+
console.log('='.repeat(60) + '\n');
71+
72+
const session = await connection.openSession();
73+
const queryOperation = await session.executeStatement('SELECT 1', {
74+
runAsync: false,
75+
});
76+
77+
const result = await queryOperation.fetchAll();
78+
console.log('\n✅ Query Result:', JSON.stringify(result, null, 2));
79+
80+
await queryOperation.close();
81+
console.log('\n📝 Statement closed - waiting for telemetry flush...\n');
82+
83+
// Wait for telemetry to flush
84+
await new Promise<void>((resolve) => {
85+
setTimeout(resolve, 3000);
86+
});
87+
88+
console.log('\n' + '='.repeat(60));
89+
console.log('CLEANING UP');
90+
console.log('='.repeat(60) + '\n');
91+
92+
await session.close();
93+
await connection.close();
94+
95+
// Wait for final flush
96+
await new Promise<void>((resolve) => {
97+
setTimeout(resolve, 2000);
98+
});
99+
100+
console.log('\n' + '='.repeat(60));
101+
console.log('TEST COMPLETE');
102+
console.log('='.repeat(60));
103+
console.log('\nCheck the logs above for telemetry-related messages (shown in cyan)');
104+
console.log('Look for:');
105+
console.log(' - "Exporting N telemetry metrics"');
106+
console.log(' - "Successfully exported N telemetry metrics"');
107+
console.log(' - "Feature flag enabled: true"\n');
108+
});
109+
});

0 commit comments

Comments
 (0)