10
10
import android .app .Application ;
11
11
import android .util .Log ;
12
12
import androidx .annotation .NonNull ;
13
+ import androidx .annotation .Nullable ;
13
14
import io .opentelemetry .android .common .RumConstants ;
14
15
import io .opentelemetry .android .config .OtelRumConfig ;
16
+ import io .opentelemetry .android .export .BufferDelegatingLogExporter ;
17
+ import io .opentelemetry .android .export .BufferDelegatingSpanExporter ;
15
18
import io .opentelemetry .android .features .diskbuffering .DiskBufferingConfiguration ;
16
19
import io .opentelemetry .android .features .diskbuffering .SignalFromDiskExporter ;
17
20
import io .opentelemetry .android .features .diskbuffering .scheduler .DefaultExportScheduleHandler ;
63
66
import java .util .function .BiFunction ;
64
67
import java .util .function .Consumer ;
65
68
import java .util .function .Function ;
66
- import javax .annotation .Nullable ;
67
69
import kotlin .jvm .functions .Function0 ;
68
70
69
71
/**
@@ -94,7 +96,10 @@ public final class OpenTelemetryRumBuilder {
94
96
95
97
private Resource resource ;
96
98
99
+ private boolean isBuilt = false ;
100
+
97
101
@ Nullable private ServiceManager serviceManager ;
102
+
98
103
@ Nullable private ExportScheduleHandler exportScheduleHandler ;
99
104
100
105
private static TextMapPropagator buildDefaultPropagator () {
@@ -122,6 +127,7 @@ public static OpenTelemetryRumBuilder create(Application application, OtelRumCon
122
127
* @return {@code this}
123
128
*/
124
129
public OpenTelemetryRumBuilder setResource (Resource resource ) {
130
+ checkNotBuilt ();
125
131
this .resource = resource ;
126
132
return this ;
127
133
}
@@ -134,6 +140,7 @@ public OpenTelemetryRumBuilder setResource(Resource resource) {
134
140
* @return {@code this}
135
141
*/
136
142
public OpenTelemetryRumBuilder mergeResource (Resource resource ) {
143
+ checkNotBuilt ();
137
144
this .resource = this .resource .merge (resource );
138
145
return this ;
139
146
}
@@ -173,6 +180,7 @@ public OpenTelemetryRumBuilder addTracerProviderCustomizer(
173
180
*/
174
181
public OpenTelemetryRumBuilder addMeterProviderCustomizer (
175
182
BiFunction <SdkMeterProviderBuilder , Application , SdkMeterProviderBuilder > customizer ) {
183
+ checkNotBuilt ();
176
184
meterProviderCustomizers .add (customizer );
177
185
return this ;
178
186
}
@@ -193,6 +201,7 @@ public OpenTelemetryRumBuilder addMeterProviderCustomizer(
193
201
public OpenTelemetryRumBuilder addLoggerProviderCustomizer (
194
202
BiFunction <SdkLoggerProviderBuilder , Application , SdkLoggerProviderBuilder >
195
203
customizer ) {
204
+ checkNotBuilt ();
196
205
loggerProviderCustomizers .add (customizer );
197
206
return this ;
198
207
}
@@ -204,6 +213,7 @@ public OpenTelemetryRumBuilder addLoggerProviderCustomizer(
204
213
*/
205
214
public OpenTelemetryRumBuilder addInstrumentation (AndroidInstrumentation instrumentation ) {
206
215
instrumentations .add (instrumentation );
216
+ checkNotBuilt ();
207
217
return this ;
208
218
}
209
219
@@ -218,6 +228,7 @@ public OpenTelemetryRumBuilder addInstrumentation(AndroidInstrumentation instrum
218
228
public OpenTelemetryRumBuilder addPropagatorCustomizer (
219
229
Function <? super TextMapPropagator , ? extends TextMapPropagator > propagatorCustomizer ) {
220
230
requireNonNull (propagatorCustomizer , "propagatorCustomizer" );
231
+ checkNotBuilt ();
221
232
Function <? super TextMapPropagator , ? extends TextMapPropagator > existing =
222
233
this .propagatorCustomizer ;
223
234
this .propagatorCustomizer =
@@ -237,6 +248,7 @@ public OpenTelemetryRumBuilder addPropagatorCustomizer(
237
248
public OpenTelemetryRumBuilder addSpanExporterCustomizer (
238
249
Function <? super SpanExporter , ? extends SpanExporter > spanExporterCustomizer ) {
239
250
requireNonNull (spanExporterCustomizer , "spanExporterCustomizer" );
251
+ checkNotBuilt ();
240
252
Function <? super SpanExporter , ? extends SpanExporter > existing =
241
253
this .spanExporterCustomizer ;
242
254
this .spanExporterCustomizer =
@@ -256,6 +268,7 @@ public OpenTelemetryRumBuilder addSpanExporterCustomizer(
256
268
public OpenTelemetryRumBuilder addLogRecordExporterCustomizer (
257
269
Function <? super LogRecordExporter , ? extends LogRecordExporter >
258
270
logRecordExporterCustomizer ) {
271
+ checkNotBuilt ();
259
272
Function <? super LogRecordExporter , ? extends LogRecordExporter > existing =
260
273
this .logRecordExporterCustomizer ;
261
274
this .logRecordExporterCustomizer =
@@ -276,9 +289,63 @@ public OpenTelemetryRumBuilder addLogRecordExporterCustomizer(
276
289
* @return A new {@link OpenTelemetryRum} instance.
277
290
*/
278
291
public OpenTelemetryRum build () {
292
+ if (isBuilt ) {
293
+ throw new IllegalStateException ("You cannot call build multiple times" );
294
+ }
295
+ isBuilt = true ;
279
296
InitializationEvents initializationEvents = InitializationEvents .get ();
280
297
applyConfiguration (initializationEvents );
281
298
299
+ BufferDelegatingLogExporter bufferDelegatingLogExporter = new BufferDelegatingLogExporter ();
300
+
301
+ BufferDelegatingSpanExporter bufferDelegatingSpanExporter =
302
+ new BufferDelegatingSpanExporter ();
303
+
304
+ SessionManager sessionManager =
305
+ SessionManager .create (timeoutHandler , config .getSessionTimeout ().toNanos ());
306
+
307
+ OpenTelemetrySdk sdk =
308
+ OpenTelemetrySdk .builder ()
309
+ .setTracerProvider (
310
+ buildTracerProvider (
311
+ sessionManager , application , bufferDelegatingSpanExporter ))
312
+ .setLoggerProvider (
313
+ buildLoggerProvider (
314
+ sessionManager , application , bufferDelegatingLogExporter ))
315
+ .setMeterProvider (buildMeterProvider (application ))
316
+ .setPropagators (buildFinalPropagators ())
317
+ .build ();
318
+
319
+ otelSdkReadyListeners .forEach (listener -> listener .accept (sdk ));
320
+
321
+ SdkPreconfiguredRumBuilder delegate =
322
+ new SdkPreconfiguredRumBuilder (
323
+ application ,
324
+ sdk ,
325
+ timeoutHandler ,
326
+ sessionManager ,
327
+ config .shouldDiscoverInstrumentations (),
328
+ getServiceManager ());
329
+
330
+ // AsyncTask is deprecated but the thread pool is still used all over the Android SDK
331
+ // and it provides a way to get a background thread without having to create a new one.
332
+ android .os .AsyncTask .THREAD_POOL_EXECUTOR .execute (
333
+ () ->
334
+ initializeExporters (
335
+ initializationEvents ,
336
+ bufferDelegatingSpanExporter ,
337
+ bufferDelegatingLogExporter ));
338
+
339
+ instrumentations .forEach (delegate ::addInstrumentation );
340
+
341
+ return delegate .build ();
342
+ }
343
+
344
+ private void initializeExporters (
345
+ InitializationEvents initializationEvents ,
346
+ BufferDelegatingSpanExporter bufferDelegatingSpanExporter ,
347
+ BufferDelegatingLogExporter bufferedDelegatingLogExporter ) {
348
+
282
349
DiskBufferingConfiguration diskBufferingConfiguration =
283
350
config .getDiskBufferingConfiguration ();
284
351
SpanExporter spanExporter = buildSpanExporter ();
@@ -306,44 +373,25 @@ public OpenTelemetryRum build() {
306
373
}
307
374
initializationEvents .spanExporterInitialized (spanExporter );
308
375
309
- SessionManager sessionManager =
310
- SessionManager .create (timeoutHandler , config .getSessionTimeout ().toNanos ());
376
+ bufferedDelegatingLogExporter .setDelegate (logsExporter );
311
377
312
- OpenTelemetrySdk sdk =
313
- OpenTelemetrySdk .builder ()
314
- .setTracerProvider (
315
- buildTracerProvider (sessionManager , application , spanExporter ))
316
- .setLoggerProvider (
317
- buildLoggerProvider (sessionManager , application , logsExporter ))
318
- .setMeterProvider (buildMeterProvider (application ))
319
- .setPropagators (buildFinalPropagators ())
320
- .build ();
321
-
322
- otelSdkReadyListeners .forEach (listener -> listener .accept (sdk ));
378
+ bufferDelegatingSpanExporter .setDelegate (spanExporter );
323
379
324
380
scheduleDiskTelemetryReader (signalFromDiskExporter );
325
-
326
- SdkPreconfiguredRumBuilder delegate =
327
- new SdkPreconfiguredRumBuilder (
328
- application ,
329
- sdk ,
330
- timeoutHandler ,
331
- sessionManager ,
332
- config .shouldDiscoverInstrumentations (),
333
- getServiceManager ());
334
- instrumentations .forEach (delegate ::addInstrumentation );
335
- return delegate .build ();
336
381
}
337
382
338
383
@ NonNull
339
384
private ServiceManager getServiceManager () {
340
385
if (serviceManager == null ) {
341
386
serviceManager = ServiceManagerImpl .Companion .create (application );
342
387
}
343
- return serviceManager ;
388
+ // This can never be null since we never write `null` to it
389
+ return requireNonNull (serviceManager );
344
390
}
345
391
346
- public OpenTelemetryRumBuilder setServiceManager (ServiceManager serviceManager ) {
392
+ public OpenTelemetryRumBuilder setServiceManager (@ NonNull ServiceManager serviceManager ) {
393
+ requireNonNull (serviceManager , "serviceManager cannot be null" );
394
+ checkNotBuilt ();
347
395
this .serviceManager = serviceManager ;
348
396
return this ;
349
397
}
@@ -353,7 +401,9 @@ public OpenTelemetryRumBuilder setServiceManager(ServiceManager serviceManager)
353
401
* If not specified, the default schedule exporter will be used.
354
402
*/
355
403
public OpenTelemetryRumBuilder setExportScheduleHandler (
356
- ExportScheduleHandler exportScheduleHandler ) {
404
+ @ NonNull ExportScheduleHandler exportScheduleHandler ) {
405
+ requireNonNull (exportScheduleHandler , "exportScheduleHandler cannot be null" );
406
+ checkNotBuilt ();
357
407
this .exportScheduleHandler = exportScheduleHandler ;
358
408
return this ;
359
409
}
@@ -376,7 +426,6 @@ private StorageConfiguration createStorageConfiguration() throws IOException {
376
426
}
377
427
378
428
private void scheduleDiskTelemetryReader (@ Nullable SignalFromDiskExporter signalExporter ) {
379
-
380
429
if (exportScheduleHandler == null ) {
381
430
ServiceManager serviceManager = getServiceManager ();
382
431
// TODO: Is it safe to get the work service yet here? If so, we can
@@ -387,6 +436,9 @@ private void scheduleDiskTelemetryReader(@Nullable SignalFromDiskExporter signal
387
436
new DefaultExportScheduler (getWorkService ), getWorkService );
388
437
}
389
438
439
+ final ExportScheduleHandler exportScheduleHandler =
440
+ requireNonNull (this .exportScheduleHandler );
441
+
390
442
if (signalExporter == null ) {
391
443
// Disabling here allows to cancel previously scheduled exports using tools that
392
444
// can run even after the app has been terminated (such as WorkManager).
@@ -408,6 +460,7 @@ private void scheduleDiskTelemetryReader(@Nullable SignalFromDiskExporter signal
408
460
* @return this
409
461
*/
410
462
public OpenTelemetryRumBuilder addOtelSdkReadyListener (Consumer <OpenTelemetrySdk > callback ) {
463
+ checkNotBuilt ();
411
464
otelSdkReadyListeners .add (callback );
412
465
return this ;
413
466
}
@@ -521,4 +574,10 @@ private ContextPropagators buildFinalPropagators() {
521
574
TextMapPropagator defaultPropagator = buildDefaultPropagator ();
522
575
return ContextPropagators .create (propagatorCustomizer .apply (defaultPropagator ));
523
576
}
577
+
578
+ private void checkNotBuilt () {
579
+ if (isBuilt ) {
580
+ throw new IllegalStateException ("This method cannot be called after calling build" );
581
+ }
582
+ }
524
583
}
0 commit comments