11import type { TelemetryOptions } from "shared" ;
2- import { metrics } from "@opentelemetry/api" ;
3- import { logs } from "@opentelemetry/api-logs" ;
42import { getNodeAutoInstrumentations } from "@opentelemetry/auto-instrumentations-node" ;
5- import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-hooks" ;
63import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-proto" ;
74import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-proto" ;
85import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto" ;
@@ -11,47 +8,30 @@ import {
118 registerInstrumentations as otelRegisterInstrumentations ,
129} from "@opentelemetry/instrumentation" ;
1310import {
14- detectResourcesSync ,
11+ detectResources ,
1512 envDetector ,
1613 hostDetector ,
1714 processDetector ,
18- Resource ,
15+ type Resource ,
16+ resourceFromAttributes ,
1917} from "@opentelemetry/resources" ;
20- import {
21- BatchLogRecordProcessor ,
22- LoggerProvider ,
23- } from "@opentelemetry/sdk-logs" ;
24- import {
25- MeterProvider ,
26- PeriodicExportingMetricReader ,
27- } from "@opentelemetry/sdk-metrics" ;
28- import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base" ;
29- import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node" ;
18+ import { BatchLogRecordProcessor } from "@opentelemetry/sdk-logs" ;
19+ import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics" ;
20+ import { AlwaysOnSampler } from "@opentelemetry/sdk-trace-base" ;
3021import {
3122 ATTR_SERVICE_NAME ,
3223 ATTR_SERVICE_VERSION ,
3324} from "@opentelemetry/semantic-conventions" ;
3425import { TelemetryProvider } from "./telemetry-provider" ;
3526import type { TelemetryConfig } from "./types" ;
27+ import { NodeSDK } from "@opentelemetry/sdk-node" ;
3628
3729export class TelemetryManager {
3830 private static readonly DEFAULT_EXPORT_INTERVAL_MS = 10000 ;
31+ private static readonly DEFAULT_FALLBACK_APP_NAME = "databricks-app" ;
3932
4033 private static instance ?: TelemetryManager ;
41- private static shutdownRegistered = false ;
42- private tracerProvider ?: NodeTracerProvider ;
43- private meterProvider ?: MeterProvider ;
44- private loggerProvider ?: LoggerProvider ;
45- private isInitialized = false ;
46-
47- private constructor ( ) { }
48-
49- static getInstance ( ) : TelemetryManager {
50- if ( ! TelemetryManager . instance ) {
51- TelemetryManager . instance = new TelemetryManager ( ) ;
52- }
53- return TelemetryManager . instance ;
54- }
34+ private sdk ?: NodeSDK ;
5535
5636 /**
5737 * Create a scoped telemetry provider for a specific plugin.
@@ -68,43 +48,57 @@ export class TelemetryManager {
6848 return new TelemetryProvider ( pluginName , globalManager , telemetryConfig ) ;
6949 }
7050
51+ private constructor ( ) { }
52+
53+ static getInstance ( ) : TelemetryManager {
54+ if ( ! TelemetryManager . instance ) {
55+ TelemetryManager . instance = new TelemetryManager ( ) ;
56+ }
57+ return TelemetryManager . instance ;
58+ }
59+
7160 static initialize ( config : Partial < TelemetryConfig > = { } ) : void {
72- TelemetryManager . registerShutdown ( ) ;
7361 const instance = TelemetryManager . getInstance ( ) ;
7462 instance . _initialize ( config ) ;
7563 }
7664
7765 private _initialize ( config : Partial < TelemetryConfig > ) : void {
78- if ( this . isInitialized ) {
79- return ;
80- }
66+ if ( this . sdk ) return ;
8167
8268 if ( ! process . env . OTEL_EXPORTER_OTLP_ENDPOINT ) {
8369 console . log (
8470 "[Telemetry] OTEL_EXPORTER_OTLP_ENDPOINT not set; telemetry disabled" ,
8571 ) ;
86- this . isInitialized = true ;
8772 return ;
8873 }
8974
9075 try {
91- const resource = this . createResource ( config ) ;
92- this . setupTraces ( resource , config ) ;
93- this . setupMetrics ( resource , config ) ;
94- this . setupLogs ( resource , config ) ;
95-
96- const instrumentations =
97- config . instrumentations || this . getDefaultInstrumentations ( ) ;
98- otelRegisterInstrumentations ( {
99- tracerProvider : this . tracerProvider ,
100- meterProvider : this . meterProvider ,
101- instrumentations,
76+ this . sdk = new NodeSDK ( {
77+ resource : this . createResource ( config ) ,
78+ autoDetectResources : false ,
79+ sampler : new AlwaysOnSampler ( ) ,
80+ traceExporter : new OTLPTraceExporter ( { headers : config . headers } ) ,
81+ metricReaders : [
82+ new PeriodicExportingMetricReader ( {
83+ exporter : new OTLPMetricExporter ( { headers : config . headers } ) ,
84+ exportIntervalMillis :
85+ config . exportIntervalMs ||
86+ TelemetryManager . DEFAULT_EXPORT_INTERVAL_MS ,
87+ } ) ,
88+ ] ,
89+ logRecordProcessors : [
90+ new BatchLogRecordProcessor (
91+ new OTLPLogExporter ( { headers : config . headers } ) ,
92+ ) ,
93+ ] ,
94+ instrumentations : this . getDefaultInstrumentations ( ) ,
10295 } ) ;
10396
104- this . isInitialized = true ;
97+ this . sdk . start ( ) ;
98+ this . registerShutdown ( ) ;
99+ console . log ( "[Telemetry] Initialized successfully" ) ;
105100 } catch ( error ) {
106- console . error ( "[Telemetry] Failed to initialize telemetry:" , error ) ;
107- this . isInitialized = true ;
101+ console . error ( "[Telemetry] Failed to initialize:" , error ) ;
108102 }
109103 }
110104
@@ -115,8 +109,7 @@ export class TelemetryManager {
115109 */
116110 registerInstrumentations ( instrumentations : Instrumentation [ ] ) : void {
117111 otelRegisterInstrumentations ( {
118- tracerProvider : this . tracerProvider ,
119- meterProvider : this . meterProvider ,
112+ // global providers set by NodeSDK.start()
120113 instrumentations,
121114 } ) ;
122115 }
@@ -126,83 +119,17 @@ export class TelemetryManager {
126119 config . serviceName ||
127120 process . env . OTEL_SERVICE_NAME ||
128121 process . env . DATABRICKS_APP_NAME ||
129- "databricks-app" ;
130-
131- const initialResource = new Resource ( {
122+ TelemetryManager . DEFAULT_FALLBACK_APP_NAME ;
123+ const initialResource = resourceFromAttributes ( {
132124 [ ATTR_SERVICE_NAME ] : serviceName ,
133125 [ ATTR_SERVICE_VERSION ] : config . serviceVersion ?? undefined ,
134126 } ) ;
135- const detectedResource = detectResourcesSync ( {
127+ const detectedResource = detectResources ( {
136128 detectors : [ envDetector , hostDetector , processDetector ] ,
137129 } ) ;
138130 return initialResource . merge ( detectedResource ) ;
139131 }
140132
141- private setupTraces (
142- resource : Resource ,
143- config : Partial < TelemetryConfig > ,
144- ) : void {
145- this . tracerProvider = new NodeTracerProvider ( {
146- resource,
147- } ) ;
148-
149- const traceExporter = new OTLPTraceExporter ( {
150- // reads the endpoint automatically
151- headers : config . headers || { } ,
152- } ) ;
153-
154- const spanProcessor = new BatchSpanProcessor ( traceExporter ) ;
155- this . tracerProvider . addSpanProcessor ( spanProcessor ) ;
156-
157- const contextManager = new AsyncLocalStorageContextManager ( ) ;
158- contextManager . enable ( ) ;
159-
160- this . tracerProvider . register ( {
161- contextManager : contextManager ,
162- } ) ;
163- }
164-
165- private setupMetrics (
166- resource : Resource ,
167- config : Partial < TelemetryConfig > ,
168- ) : void {
169- this . meterProvider = new MeterProvider ( {
170- resource,
171- } ) ;
172-
173- const metricExporter = new OTLPMetricExporter ( {
174- // reads the endpoint automatically
175- headers : config . headers || { } ,
176- } ) ;
177-
178- const metricReader = new PeriodicExportingMetricReader ( {
179- exporter : metricExporter ,
180- exportIntervalMillis :
181- config . exportIntervalMs || TelemetryManager . DEFAULT_EXPORT_INTERVAL_MS ,
182- } ) ;
183-
184- this . meterProvider . addMetricReader ( metricReader ) ;
185- metrics . setGlobalMeterProvider ( this . meterProvider ) ;
186- }
187-
188- private setupLogs (
189- resource : Resource ,
190- config : Partial < TelemetryConfig > ,
191- ) : void {
192- this . loggerProvider = new LoggerProvider ( {
193- resource,
194- } ) ;
195-
196- const logExporter = new OTLPLogExporter ( {
197- // reads the endpoint automatically
198- headers : config . headers || { } ,
199- } ) ;
200-
201- const logProcessor = new BatchLogRecordProcessor ( logExporter ) ;
202- this . loggerProvider . addLogRecordProcessor ( logProcessor ) ;
203- logs . setGlobalLoggerProvider ( this . loggerProvider ) ;
204- }
205-
206133 private getDefaultInstrumentations ( ) : Instrumentation [ ] {
207134 return [
208135 ...getNodeAutoInstrumentations ( {
@@ -231,41 +158,22 @@ export class TelemetryManager {
231158 ] ;
232159 }
233160
234- private static registerShutdown ( ) {
235- if ( TelemetryManager . shutdownRegistered ) {
236- return ;
237- }
238-
161+ private registerShutdown ( ) {
239162 const shutdownFn = async ( ) => {
240163 await TelemetryManager . getInstance ( ) . shutdown ( ) ;
241164 } ;
242165 process . once ( "SIGTERM" , shutdownFn ) ;
243166 process . once ( "SIGINT" , shutdownFn ) ;
244- TelemetryManager . shutdownRegistered = true ;
245167 }
246168
247169 private async shutdown ( ) : Promise < void > {
248- if ( ! this . isInitialized ) {
170+ if ( ! this . sdk ) {
249171 return ;
250172 }
251173
252174 try {
253- if ( this . tracerProvider ) {
254- await this . tracerProvider . shutdown ( ) ;
255- this . tracerProvider = undefined ;
256- }
257-
258- if ( this . meterProvider ) {
259- await this . meterProvider . shutdown ( ) ;
260- this . meterProvider = undefined ;
261- }
262-
263- if ( this . loggerProvider ) {
264- await this . loggerProvider . shutdown ( ) ;
265- this . loggerProvider = undefined ;
266- }
267-
268- this . isInitialized = false ;
175+ await this . sdk . shutdown ( ) ;
176+ this . sdk = undefined ;
269177 } catch ( error ) {
270178 console . error ( "[Telemetry] Error shutting down:" , error ) ;
271179 }
0 commit comments