|
12 | 12 | // See the License for the specific language governing permissions and |
13 | 13 | // limitations under the License. |
14 | 14 |
|
| 15 | +import os from 'os'; |
15 | 16 | import { ConnectionOptions } from '../contracts/IDBSQLClient'; |
| 17 | +import { ClientConfig } from '../contracts/IClientContext'; |
16 | 18 | import { InternalConnectionOptions } from '../contracts/InternalConnectionOptions'; |
17 | 19 | import AuthenticationError from '../errors/AuthenticationError'; |
18 | 20 | import HiveDriverError from '../errors/HiveDriverError'; |
19 | 21 | import { buildUserAgentString, normalizePemBytes } from '../utils'; |
| 22 | +import driverVersion from '../version'; |
| 23 | +import { DRIVER_NAME } from '../telemetry/types'; |
| 24 | +import { sanitizeProcessName } from '../telemetry/telemetryUtils'; |
20 | 25 |
|
21 | 26 | /** |
22 | 27 | * Default local listener port for the U2M authorization-code callback. |
@@ -131,6 +136,32 @@ export interface KernelSessionDefaults { |
131 | 136 | retryOverallTimeoutSecs?: number; |
132 | 137 | } |
133 | 138 |
|
| 139 | +export interface KernelTelemetryOptions { |
| 140 | + /** Driver/runtime identity forwarded to kernel-owned telemetry. */ |
| 141 | + driverName?: string; |
| 142 | + driverVersion?: string; |
| 143 | + runtimeName?: string; |
| 144 | + runtimeVersion?: string; |
| 145 | + runtimeVendor?: string; |
| 146 | + osName?: string; |
| 147 | + osVersion?: string; |
| 148 | + osArch?: string; |
| 149 | + clientAppName?: string; |
| 150 | + localeName?: string; |
| 151 | + charSetEncoding?: string; |
| 152 | + processName?: string; |
| 153 | + /** Kernel-owned telemetry switch and batching. */ |
| 154 | + telemetryEnabled?: boolean; |
| 155 | + telemetryBatchSize?: number; |
| 156 | + telemetryFlushIntervalMs?: number; |
| 157 | + telemetryMaxRetries?: number; |
| 158 | + telemetryRetryDelayMs?: number; |
| 159 | + telemetryCloseFlushTimeoutMs?: number; |
| 160 | + telemetryCircuitBreakerEnabled?: boolean; |
| 161 | + telemetryCircuitBreakerThreshold?: number; |
| 162 | + telemetryCircuitBreakerTimeoutMs?: number; |
| 163 | +} |
| 164 | + |
134 | 165 | /** |
135 | 166 | * TLS options shared across all auth-mode variants. Mirror the napi |
136 | 167 | * binding's `ConnectionOptions.checkServerCertificate` / `.customCaCert` |
@@ -227,6 +258,7 @@ export interface KernelFederationOptions { |
227 | 258 | export type KernelNativeConnectionOptions = KernelSessionDefaults & |
228 | 259 | KernelTlsOptions & |
229 | 260 | KernelHttpOptions & |
| 261 | + KernelTelemetryOptions & |
230 | 262 | KernelProxyOptions & |
231 | 263 | KernelFederationOptions & |
232 | 264 | ( |
@@ -588,6 +620,91 @@ export function buildKernelRetryOptions(config: { |
588 | 620 | return out; |
589 | 621 | } |
590 | 622 |
|
| 623 | +function getLocaleName(env: NodeJS.ProcessEnv = process.env): string { |
| 624 | + try { |
| 625 | + const lang = env.LANG || env.LC_ALL || env.LC_MESSAGES || ''; |
| 626 | + const match = lang.match(/^([a-z]{2}_[A-Z]{2})/); |
| 627 | + return match?.[1] ?? 'en_US'; |
| 628 | + } catch { |
| 629 | + return 'en_US'; |
| 630 | + } |
| 631 | +} |
| 632 | + |
| 633 | +function getProcessName(): string { |
| 634 | + try { |
| 635 | + if (process.title && process.title !== 'node') { |
| 636 | + return sanitizeProcessName(process.title) || 'node'; |
| 637 | + } |
| 638 | + const scriptPath = process.argv?.[1]; |
| 639 | + if (scriptPath) { |
| 640 | + return sanitizeProcessName(scriptPath).replace(/\.[^.]*$/, '') || 'node'; |
| 641 | + } |
| 642 | + return 'node'; |
| 643 | + } catch { |
| 644 | + return 'node'; |
| 645 | + } |
| 646 | +} |
| 647 | + |
| 648 | +export function isTelemetryDisabledByEnv(env: NodeJS.ProcessEnv = process.env): boolean { |
| 649 | + const raw = env.DATABRICKS_TELEMETRY_DISABLED; |
| 650 | + const trimmed = typeof raw === 'string' ? raw.trim() : ''; |
| 651 | + return trimmed.length > 0 && /^(1|true|yes|on)$/i.test(trimmed); |
| 652 | +} |
| 653 | + |
| 654 | +export function buildKernelTelemetryOptions( |
| 655 | + config: Pick< |
| 656 | + ClientConfig, |
| 657 | + | 'telemetryEnabled' |
| 658 | + | 'telemetryBatchSize' |
| 659 | + | 'telemetryFlushIntervalMs' |
| 660 | + | 'telemetryMaxRetries' |
| 661 | + | 'telemetryBackoffBaseMs' |
| 662 | + | 'telemetryCloseTimeoutMs' |
| 663 | + | 'telemetryCircuitBreakerThreshold' |
| 664 | + | 'telemetryCircuitBreakerTimeout' |
| 665 | + >, |
| 666 | +) { |
| 667 | + const telemetry: KernelTelemetryOptions = { |
| 668 | + driverName: DRIVER_NAME, |
| 669 | + driverVersion, |
| 670 | + runtimeName: 'Node.js', |
| 671 | + runtimeVersion: process.version, |
| 672 | + runtimeVendor: 'Node.js Foundation', |
| 673 | + osName: process.platform, |
| 674 | + osVersion: os.release(), |
| 675 | + osArch: os.arch(), |
| 676 | + clientAppName: undefined, |
| 677 | + localeName: getLocaleName(), |
| 678 | + charSetEncoding: 'UTF-8', |
| 679 | + processName: getProcessName(), |
| 680 | + telemetryEnabled: (config.telemetryEnabled ?? true) && !isTelemetryDisabledByEnv(), |
| 681 | + }; |
| 682 | + |
| 683 | + if (Number.isFinite(config.telemetryBatchSize)) { |
| 684 | + telemetry.telemetryBatchSize = config.telemetryBatchSize; |
| 685 | + } |
| 686 | + if (Number.isFinite(config.telemetryFlushIntervalMs)) { |
| 687 | + telemetry.telemetryFlushIntervalMs = config.telemetryFlushIntervalMs; |
| 688 | + } |
| 689 | + if (Number.isFinite(config.telemetryMaxRetries)) { |
| 690 | + telemetry.telemetryMaxRetries = config.telemetryMaxRetries; |
| 691 | + } |
| 692 | + if (Number.isFinite(config.telemetryBackoffBaseMs)) { |
| 693 | + telemetry.telemetryRetryDelayMs = config.telemetryBackoffBaseMs; |
| 694 | + } |
| 695 | + if (Number.isFinite(config.telemetryCloseTimeoutMs)) { |
| 696 | + telemetry.telemetryCloseFlushTimeoutMs = config.telemetryCloseTimeoutMs; |
| 697 | + } |
| 698 | + if (Number.isFinite(config.telemetryCircuitBreakerThreshold)) { |
| 699 | + telemetry.telemetryCircuitBreakerThreshold = config.telemetryCircuitBreakerThreshold; |
| 700 | + } |
| 701 | + if (Number.isFinite(config.telemetryCircuitBreakerTimeout)) { |
| 702 | + telemetry.telemetryCircuitBreakerTimeoutMs = config.telemetryCircuitBreakerTimeout; |
| 703 | + } |
| 704 | + |
| 705 | + return telemetry; |
| 706 | +} |
| 707 | + |
591 | 708 | /** |
592 | 709 | * Map the public `ConnectionOptions.proxy` (`{protocol, host, port, auth}` — |
593 | 710 | * the same shape the Thrift backend accepts) onto the kernel's structured napi |
|
0 commit comments