본문 바로가기
WIKI 기술 지식 베이스

OpenTracing으로 Android 애플리케이션 트레이싱 (레거시) (Tracing Android Applications with OpenTracing (legacy))

원문 보기 위키 갱신

OpenTracing(레거시) 방식으로 Android 애플리케이션에 커스텀 스팬을 만들어 트레이스를 Datadog으로 보내는 방법이에요. OpenTracing 지원은 버전 3에서 중단되었으므로 OpenTelemetry 사용을 권장해요.

출처: 문서

본문

OpenTracing 지원은 더 이상 사용되지 않는 사양을 기반으로 해요. 공개 사양으로 코드를 계측하고 싶다면 대신 OpenTelemetry를 사용하세요. Datadog SDK에서 OpenTelemetry 계측 데이터 처리를 시도해 보세요.

Datadog은 OpenTracing API와 통합돼요.

설정 (Setup)

Android SDK용 OpenTracing 지원은 버전 3에서 중단됐어요.

  1. build.gradle 파일에 라이브러리를 의존성으로 선언해 Gradle 의존성을 추가하세요.

    dependencies {
        implementation "com.datadoghq:dd-sdk-android-trace:2.x.x"
    }
    
  2. 애플리케이션 컨텍스트, 추적 동의, Datadog 클라이언트 토큰으로 Datadog SDK를 초기화하세요. 보안상의 이유로 클라이언트 토큰을 사용해야 해요. Datadog API 키는 Android 애플리케이션 APK 바이트 코드에 클라이언트 측으로 노출되므로 Datadog SDK 구성에 사용할 수 없어요. 클라이언트 토큰 설정에 대한 자세한 내용은 클라이언트 토큰 문서를 참고하세요.

app.datadoghq.com 사이트 사용자의 경우:

  • Kotlin:
class SampleApplication : Application() {
   override fun onCreate() {
       super.onCreate()
       val configuration = Configuration.Builder(
            clientToken = "<CLIENT_TOKEN>",
            env = "<ENV_NAME>",
            variant = "<APP_VARIANT_NAME>"
       ).build()

       Datadog.initialize(this, configuration, trackingConsent)
   }
}
  • Java:
public class SampleApplication extends Application {
   @Override
   public void onCreate() {
       super.onCreate();
       Configuration configuration = new Configuration.Builder("<CLIENT_TOKEN>", "<ENV_NAME>", "<APP_VARIANT_NAME>")
            .build();

       Datadog.initialize(this, configuration, trackingConsent);
   }
}

다른 사이트 사용자는 초기화 시 Configuration.Builder에 해당 사이트를 지정하면 됩니다. app.datadoghq.eu 사이트는 .useSite(DatadogSite.EU1), us3.datadoghq.com은 .useSite(DatadogSite.US3), us5.datadoghq.com은 .useSite(DatadogSite.US5), app.ddog-gov.com은 .useSite(DatadogSite.US1_FED), us2.ddog-gov.com은 .useSite(DatadogSite.US2_FED), ap1.datadoghq.com은 .useSite(DatadogSite.AP1), ap2.datadoghq.com은 .useSite(DatadogSite.AP2), uk1.datadoghq.com은 .useSite(DatadogSite.UK1)로 지정하면 돼요. Java에서는 각각 DatadogSite 열거형 값을 useSite()에 전달하면 됩니다.

GDPR 규정을 준수하기 위해 SDK는 초기화 시 추적 동의(tracking consent) 값을 요구해요. 값은 다음 중 하나일 수 있어요.

  • TrackingConsent.PENDING: SDK가 데이터 수집·배치를 시작하지만 데이터 수집 엔드포인트로 보내지는 않아요. SDK는 배치된 데이터를 어떻게 처리할지 결정하기 위해 새 추적 동의 값을 기다립니다.
  • TrackingConsent.GRANTED: SDK가 데이터 수집을 시작하고 데이터 수집 엔드포인트로 보내요.
  • TrackingConsent.NOT_GRANTED: SDK가 어떤 데이터도 수집하지 않아요. 로그·트레이스·RUM 이벤트를 수동으로 보낼 수 없어요.

SDK 초기화 후 추적 동의를 업데이트하려면 Datadog.setTrackingConsent(<NEW CONSENT>)를 호출하세요. SDK는 새 동의에 따라 동작을 바꿔요. 예를 들어 현재 추적 동의가 TrackingConsent.PENDING이고 다음으로 업데이트하면:

  • TrackingConsent.GRANTED: SDK가 현재·향후 배치 데이터를 데이터 수집 엔드포인트로 바로 보내요.
  • TrackingConsent.NOT_GRANTED: SDK가 모든 배치 데이터를 지우고 향후 데이터를 수집하지 않아요.

참고: 초기화에 필요한 자격 증명에는 애플리케이션 변형(variant) 이름도 필요하며, BuildConfig.FLAVOR 값(변형이 없으면 빈 문자열)을 사용해야 해요. 이것은 빌드 시 올바른 ProGuard mapping.txt 파일이 자동으로 업로드되어 난독화 해제된 RUM 오류 스택 트레이스를 볼 수 있게 하므로 중요해요. 자세한 내용은 Android 소스 매핑 파일 업로드 가이드를 참고하세요.

isInitialized 유틸리티 메서드를 사용해 SDK가 올바르게 초기화되었는지 확인하세요.

if (Datadog.isInitialized()) {
    // your code here
}

애플리케이션을 작성할 때 setVerbosity 메서드를 호출해 개발 로그를 활성화할 수 있어요. 제공된 레벨 이상의 우선순위를 가진 라이브러리의 모든 내부 메시지가 Android의 Logcat에 기록돼요.

Datadog.setVerbosity(Log.INFO)

Trace 기능을 구성·활성화하세요.

  • Kotlin:
val traceConfig = TraceConfiguration.Builder().build()
Trace.enable(traceConfig)
  • Java:
TraceConfiguration traceConfig = new TraceConfiguration.Builder().build();
Trace.enable(traceConfig);

Android Tracer를 구성·등록하세요. 보통 애플리케이션의 onCreate() 메서드에서 한 번만 하면 돼요.

  • Kotlin:
import io.opentracing.util.GlobalTracer

val tracer = AndroidTracer.Builder().build()
GlobalTracer.registerIfAbsent(tracer)
  • Java:
import io.opentracing.util.GlobalTracer;

AndroidTracer tracer = new AndroidTracer.Builder().build();
GlobalTracer.registerIfAbsent(tracer);

(선택) 애플리케이션이 생성하는 스팬 수에 따라 SDK 작업량을 최적화하도록 부분 플러시(partial flush) 임계값을 설정하세요. 라이브러리는 완료된 스팬 수가 임계값을 초과할 때까지 기다렸다가 디스크에 씁니다. 이 값을 1로 설정하면 각 스팬이 완료되는 즉시 써요.

  • Kotlin:
val tracer = AndroidTracer.Builder()
        .setPartialFlushThreshold(10)
        .build()
  • Java:
AndroidTracer tracer = new AndroidTracer.Builder()
        .setPartialFlushThreshold(10)
        .build();

다음 메서드로 커스텀 스팬을 시작하세요.

  • Kotlin:
val tracer = GlobalTracer.get()
val span = tracer.buildSpan("<SPAN_NAME>").start()
// Do something ...
// ...
// Then when the span should be closed
span.finish()
  • Java:
GlobalTracer tracer = GlobalTracer.get();
Span span = tracer.buildSpan("<SPAN_NAME>").start();
// Do something ...
// ...
// Then when the span should be closed
span.finish();

동기 호출에서 scope를 사용하려면:

  • Kotlin:
val span = tracer.buildSpan("<SPAN_NAME1>").start()
try {
    val scope = tracer.activateSpan(span)
    scope.use {
        // Do something ...
        // ...
        // Start a new Scope
        val childSpan = tracer.buildSpan("<SPAN_NAME2>").start()
        try {
            tracer.activateSpan(childSpan).use {
                // Do something ...
            }
        } catch (e: Error) {
            childSpan.error(e)
        } finally {
            childSpan.finish()
        }
    }
} catch (e: Throwable) {
    AndroidTracer.logThrowable(span, e)
} finally {
    span.finish()
}
  • Java:
Span = tracer.buildSpan("<SPAN_NAME1>").start();
try {
    Scope scope = tracer.activateSpan(span);
    try {
        // Do something ...
        // ...
        // Start a new Scope
        Span childSpan = tracer.buildSpan("<SPAN_NAME2>").start();
        try {
            Scope innerScope = tracer.activateSpan(childSpan);
            try {
                // Do something ...
            } finally {
                innerScope.close();
            }
        } catch (Throwable e) {
            AndroidTracer.logThrowable(childSpan, e);
        } finally {
            childSpan.finish();
        }
    } finally {
        scope.close();
    }
} catch (Throwable e) {
    AndroidTracer.logThrowable(span, e);
} finally {
    span.finish();
}

비동기 호출에서 scope를 사용하려면:

  • Kotlin:
val span = tracer.buildSpan("<SPAN_NAME1>").start()
try{
    val scope = tracer.activateSpan(span)
    scope.use {
        // Do something ...
        doAsyncWork {
            // Step 2: reactivate the Span in the worker thread
            val scopeContinuation = tracer.scopeManager().activate(span)
            scopeContinuation.use {
                // Do something ...
            }
        }
    }
} catch (e: Throwable) {
    AndroidTracer.logThrowable(span, e)
} finally {
    span.finish()
}
  • Java:
Span span = tracer.buildSpan("<SPAN_NAME1>").start();
try {
    Scope scope = tracer.activateSpan(span);
    try {
        // Do something ...
        new Thread(() -> {
            // Step 2: reactivate the Span in the worker thread
            Scope scopeContinuation = tracer.scopeManager().activate(span);
            try {
                // Do something
            } finally {
                scope.close();
            }
        }).start();
    } finally {
        scope.close();
    }
} catch (Throwable e){
    AndroidTracer.logThrowable(span, e);
} finally {
    span.finish();
}

(선택) 예를 들어 프론트엔드에서 백엔드로 환경 간에 트레이스를 수동으로 분산하려면:

  1. 클라이언트 요청에 트레이서 컨텍스트를 주입하세요.

    • Kotlin:
    val tracer = GlobalTracer.get()
    val span = tracer.buildSpan("<SPAN_NAME>").start()
    val tracedRequestBuilder = Request.Builder()
    tracer.inject(span.context(), Format.Builtin.TEXT_MAP_INJECT,
               TextMapInject { key, value ->
                   tracedRequestBuilder.addHeader(key, value)
               }
    )
    val request = tracedRequestBuilder.build()
    // Dispatch the request and finish the span after.
    
    • Java:
    Tracer tracer = GlobalTracer.get();
    Span span = tracer.buildSpan("<SPAN_NAME>").start();
    Request.Builder tracedRequestBuilder = new Request.Builder();
    tracer.inject(
               span.context(),
               Format.Builtin.TEXT_MAP_INJECT,
               TextMapInject() {
                  @Override
                  public void put(String key, String value) {
                      tracedRequestBuilder.addHeader(key, value);
                  }
              }
    );
    
    Request request = tracedRequestBuilder.build();
    // Dispatch the request and finish the span after
    
  2. 서버 코드에서 헤더로부터 클라이언트 트레이서 컨텍스트를 추출하세요.

    • Kotlin:
    val tracer = GlobalTracer.get()
    val extractedContext = tracer.extract(
            Format.Builtin.TEXT_MAP_EXTRACT,
            TextMapExtract {
                request.headers().toMultimap()
                .map { it.key to it.value.joinToString(";") }
                        .toMap()
                        .entrySet()
                        .iterator()
                }
            )
    val serverSpan = tracer.buildSpan("<SERVER_SPAN_NAME>").asChildOf(extractedContext).start()
    
    • Java:
    Tracer tracer = GlobalTracer.get();
    SpanContext extractedContext = tracer.extract(
            Format.Builtin.TEXT_MAP_EXTRACT,
            new TextMapExtract() {
                @Override
                public Iterator<Map.Entry<String, String>> iterator() {
                    return request.headers().toMultimap()
                      .entrySet()
                      .stream()
                      .collect(
                              Collectors.toMap(
                                      Map.Entry::getKey,
                                      entry -> String.join(";", entry.getValue())
                              )
                      )
                      .entrySet()
                      .iterator();
                }
            });
    Span serverSpan = tracer.buildSpan("<SERVER_SPAN_NAME>").asChildOf(extractedContext).start();
    

참고: OkHttp 클라이언트를 사용하는 코드베이스에는 Datadog이 아래 구현을 제공해요.

(선택) 스팬에 추가 태그를 제공하려면:

span.setTag("http.url", url)

(선택) OpenTracing 태그를 사용해 스팬에 오류가 있다고 표시하려면 기록하세요.

span.log(mapOf(Fields.ERROR_OBJECT to throwable))
span.log(mapOf(Fields.MESSAGE to errorMessage))

AndroidTracer에서 다음 헬퍼 메서드 중 하나를 사용할 수도 있어요.

AndroidTracer.logThrowable(span, throwable)
AndroidTracer.logErrorMessage(span, message)

배치하기 전에 Span 이벤트의 일부 속성을 수정해야 한다면 Trace 기능을 활성화할 때 SpanEventMapper 구현을 제공하면 돼요.

  • Kotlin:
val traceConfig = TraceConfiguration.Builder()
        // ...
        .setEventMapper(spanEventMapper)
        .build()
  • Java:
TraceConfiguration config = new TraceConfiguration.Builder()
        // ...
        .setEventMapper(spanEventMapper)
        .build();

Kotlin 확장 (Kotlin Extensions)

Span 안에서 람다 실행하기 (Running a lambda within a Span)

주어진 람다의 성능을 모니터링하려면 withinSpan() 메서드를 사용할 수 있어요. 기본적으로 스팬에 대한 scope가 생성되지만, activate 매개변수를 false로 설정해 이 동작을 비활성화할 수 있어요.

import com.datadog.android.trace.withinSpan
import io.opentracing.Span

withinSpan("<SPAN_NAME>", parentSpan, activate) {
   // Your code here
}

스팬 확장 메서드 (Span extension methods)

다음 error() 메서드 중 하나를 사용해 스팬에 오류가 있다고 표시할 수 있어요.

import com.datadog.android.trace.setError
import io.opentracing.Tracer

val span = tracer.buildSpan("<SPAN_NAME>").start()
try {
    // …
} catch (e: IOException) {
    span.setError(e)
}
span.finish()
import com.datadog.android.trace.setError
import io.opentracing.Tracer

val span = tracer.buildSpan("<SPAN_NAME>").start()
if (invalidState) {
    span.setError("Something unexpected happened")
}
span.finish()

SQLite 트랜잭션 트레이싱 (Tracing SQLite transaction)

SQLiteDatabase를 사용해 데이터를 로컬에 영속화한다면 다음 메서드로 데이터베이스 트랜잭션을 트레이싱할 수 있어요.

import com.datadog.android.trace.transactionTraced
import android.database.sqlite.SQLiteDatabase

sqliteDatabase.transactionTraced("<SPAN_NAME>", isExclusive) { database ->
   // Your queries here
   database.insert("<TABLE_NAME>", null, contentValues)

   // Decorate the Span
   setTag("<TAG_KEY>", "<TAG_VALUE>")
}

이것은 core-ktx AndroidX 패키지에서 제공하는 SQLiteDatabase.transaction 메서드처럼 동작하며 스팬 작업 이름만 필요해요.

통합 (Integrations)

수동 트레이싱 외에도 Datadog SDK는 다음 통합을 제공해요.

OkHttp

OkHttp 요청을 트레이싱하려면 제공된 Interceptor(dd-sdk-android-okhttp 라이브러리에서 찾을 수 있음)를 다음과 같이 추가할 수 있어요.

  1. 모듈 수준 build.gradle 파일에 dd-sdk-android-okhttp 라이브러리의 Gradle 의존성을 추가하세요.

    dependencies {
      implementation "com.datadoghq:dd-sdk-android-okhttp:x.x.x"
    }
    
  2. DatadogInterceptor를 OkHttpClient에 추가하세요.

    • Kotlin:
    val tracedHosts = listOf("example.com", "example.eu")
    val okHttpClient = OkHttpClient.Builder()
            .addInterceptor(
                DatadogInterceptor.Builder(tracedHosts)
                    .setTraceSampleRate(20f)
                    .build()
            )
            .build()
    
    • Java:
    List<String> tracedHosts = Arrays.asList("example.com", "example.eu");
    OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .addInterceptor(
                new DatadogInterceptor.Builder(tracedHosts)
                        .setTraceSampleRate(20f)
                        .build()
            )
            .build();
    

이것은 제공된 호스트와 일치하는 OkHttpClient가 처리하는 각 요청 주위에 스팬을 만들고, 관련 정보(URL, 메서드, 상태 코드, 오류)를 자동으로 채우며, 트레이싱 정보를 백엔드로 전파해 Datadog 내에서 통합된 트레이스를 얻을 수 있게 해요.

네트워크 트레이스는 조절 가능한 샘플링 비율로 샘플링돼요. 기본적으로 20% 샘플링이 적용돼요.

인터셉터는 애플리케이션 수준에서 요청을 추적해요. 리다이렉션을 따를 때처럼 더 자세한 정보가 필요하면 네트워크 수준에서 TracingInterceptor를 추가할 수도 있어요.

  • Kotlin:
val tracedHosts = listOf("example.com", "example.eu")
val okHttpClient =  OkHttpClient.Builder()
        .addInterceptor(
            DatadogInterceptor.Builder(tracedHosts)
                .setTraceSampleRate(20f)
                .build()
        )
        .addNetworkInterceptor(
            TracingInterceptor.Builder(tracedHosts)
                .setTraceSampleRate(100f)
                .build()
        )
        .build()
  • Java:
List<String> tracedHosts = Arrays.asList("example.com", "example.eu");
OkHttpClient okHttpClient = new OkHttpClient.Builder()
        .addInterceptor(
            new DatadogInterceptor.Builder(tracedHosts)
                    .setTraceSampleRate(20f)
                    .build()
        )
        .addNetworkInterceptor(
            new TracingInterceptor.Builder(tracedHosts)
                    .setTraceSampleRate(20f)
                    .build()
        )
        .build();

이 경우 특정 요청에 대해 업스트림 인터셉터가 내린 트레이스 샘플링 결정은 다운스트림 인터셉터에서 존중돼요.

OkHttp Request가 실행되는 방식(Thread pool 사용) 때문에 요청 스팬은 요청을 트리거한 스팬과 자동으로 연결되지 않아요. OkHttp Request.Builder의 Request.Builder.parentSpan 확장 메서드를 사용해 수동으로 부모 스팬을 제공할 수 있어요.

  • Kotlin:
val request = Request.Builder()
        .url(requestUrl)
        .parentSpan(parentSpan)
        .build()
  • Java:
Request.Builder requestBuilder = new Request.Builder()
        .url(requestUrl);
Request request = OkHttpRequestExtKt
        .parentSpan(requestBuilder, parentSpan)
        .build();

참고:

  • 여러 Interceptor를 사용한다면 이 인터셉터가 먼저 호출되어야 해요.
  • Datadog 구성에서 커스텀 트레이싱 헤더 타입을 정의하고 GlobalTracer로 등록된 SDK를 사용한다면, 사용 중인 SDK에도 동일한 트레이싱 헤더 타입이 설정되어 있는지 확인하세요.

RxJava

RxJava 스트림 안에서 연속적인 트레이스를 제공하려면 다음 단계를 따라야 해요.

  1. RxJava용 OpenTracing 의존성을 프로젝트에 추가하고 Readme 파일의 지침을 따르세요. 예를 들어 연속 트레이스의 경우 다음을 추가해야 해요.

    TracingRxJava3Utils.enableTracing(GlobalTracer.get())
    
  2. 그런 다음 프로젝트에서 Observable이 구독될 때 scope를 열고 완료되면 닫으세요. 스트림 연산자 안에서 생성된 모든 스팬은 이 scope(부모 Span) 안에 표시돼요.

    • Kotlin:
    var spanScope: Scope? = null
    Single.fromSupplier { }
            .subscribeOn(Schedulers.io())
            .map {
                val span = GlobalTracer.get().buildSpan("<YOUR_OP_NAME>").start()
                // ...
                span.finish()
            }
            .doOnSubscribe {
                val span = GlobalTracer.get()
                        .buildSpan("<YOUR_OP_NAME>")
                        .start()
                spanScope = GlobalTracer.get().scopeManager().activate(span)
            }
            .doFinally {
                GlobalTracer.get().scopeManager().activeSpan()?.let {
                    it.finish()
                }
                spanScope?.close()
            }
    
    • Java:
    ThreadLocal<Scope> scopeStorage = new ThreadLocal<>();
    ...
    Single.fromSupplier({})
            .subscribeOn(Schedulers.io())
            .map(data -> {
                final Span span = GlobalTracer.get().buildSpan("<YOUR_OP_NAME>").start();
                // ...
                span.finish();
                // ...
             })
            .doOnSubscribe(disposable -> {
                final Span span = GlobalTracer.get().buildSpan("<YOUR_OP_NAME>").start();
                Scope spanScope = GlobalTracer.get().scopeManager().activate(span);
                scopeStorage.set(spanScope);
            })
            .doFinally(() -> {
                final Span activeSpan = GlobalTracer.get().scopeManager().activeSpan();
                if (activeSpan != null) {
                    activeSpan.finish();
                }
                Scope spanScope = scopeStorage.get();
                if (spanScope != null) {
                    spanScope.close();
                    scopeStorage.remove();
                }
            });
    

RxJava + Retrofit

네트워크 요청에 Retrofit을 사용하는 RxJava 스트림 안의 연속 트레이스를 위해:

  1. Datadog Interceptor를 구성하세요.

  2. Retrofit RxJava 어댑터를 사용해 네트워크 요청에 동기 Observable을 사용하세요.

    • Kotlin:
    Retrofit.Builder()
        .baseUrl("<YOUR_URL>")
        .addCallAdapterFactory(RxJava3CallAdapterFactory.createSynchronous())
        .client(okHttpClient)
        .build()
    
    • Java:
    new Retrofit.Builder()
        .baseUrl("<YOUR_URL>")
        .addCallAdapterFactory(RxJava3CallAdapterFactory.createSynchronous())
        .client(okHttpClient)
        .build();
    
  3. Rx 스트림 주위에 scope를 다음과 같이 여세요.

    • Kotlin:
    var spanScope: Scope? = null
    remoteDataSource.getData(query)
        .subscribeOn(Schedulers.io())
        .map {
            // ...
        }
        .doOnSuccess {
            localDataSource.persistData(it)
        }
        .doOnSubscribe {
            val span = GlobalTracer.get().buildSpan("<YOUR_OP_NAME>").start()
            spanScope = GlobalTracer.get().scopeManager().activate(span)
        }
        .doFinally {
            GlobalTracer.get().scopeManager().activeSpan()?.let {
                it.finish()
            }
            spanScope?.close()
        }
    
    • Java:
    ThreadLocal<Scope> scopeStorage = new ThreadLocal<>();
    ...
    remoteDataSource.getData(query)
        .subscribeOn(Schedulers.io())
        .map(data -> { /*...*/ })
        .doOnSuccess(data -> {
            localDataSource.persistData(data);
        })
        .doOnSubscribe(disposable -> {
            final Span span = GlobalTracer.get().buildSpan("<YOUR_OP_NAME>").start();
            Scope spanScope = GlobalTracer.get().scopeManager().activate(span);
            scopeStorage.set(spanScope);
        })
        .doFinally(() -> {
            final Span activeSpan = GlobalTracer.get().scopeManager().activeSpan();
            if (activeSpan != null) {
                activeSpan.finish();
            }
            Scope spanScope = scopeStorage.get();
            if (spanScope != null) {
                spanScope.close();
                scopeStorage.remove();
            }
        });
    

배치 수집 (Batch collection)

모든 스팬은 먼저 로컬 디바이스에 배치로 저장돼요. 각 배치는 수집 사양을 따릅니다. 네트워크를 사용할 수 있고 배터리가 충분히 높아 Datadog SDK가 최종 사용자 경험에 영향을 주지 않을 때 전송돼요. 애플리케이션이 포그라운드에 있는 동안 네트워크를 사용할 수 없거나 데이터 업로드가 실패하면, 성공적으로 보낼 수 있을 때까지 배치가 유지돼요.

즉 사용자가 오프라인 상태로 애플리케이션을 열어도 데이터가 손실되지 않아요.

SDK가 디스크 공간을 너무 많이 사용하지 않도록 데이터가 너무 오래되면 디스크의 데이터는 자동으로 폐기돼요.

초기화 (Initialization)

Tracer를 초기화할 때 AndroidTracer.Builder에서 다음 메서드를 사용할 수 있어요.

메서드 설명
setService(<SERVICE_NAME>) service 값을 설정해요.
setPartialFlushThreshold(<INT>) 이 임계값에 도달하면(닫힌 <INT> 개수의 스팬이 대기 중) 플러시 메커니즘이 트리거되어 보류 중인 모든 닫힌 스팬이 처리·수집 전송돼요.
addTag(<KEY>, <VALUE>) Tracer가 만든 스팬에 추가할 <KEY>:<VALUE> 태그 쌍을 설정해요.
setBundleWithRumEnabled(true) true로 설정하면 스팬이 현재 RUM View 정보로 강화돼요. 이렇게 하면 특정 View 수명 동안 생성된 모든 스팬을 RUM Explorer에서 볼 수 있어요.
setSampleRate(<FLOAT>) 수집할 트레이스의 비율을 정의하는 0-100 값을 설정해요.
setTracingHeaderTypes(Set<TracingHeaderType>) Tracer가 주입할 수 있는 트레이싱 헤더 스타일을 설정해요.

더 알아보기 (Learn more)

도움이 되는 추가 문서, 링크, 글: