Skip to main content
The Honeycomb OpenTelemetry Android SDK is Honeycomb’s OpenTelemetry Android distribution. It simplifies adding instrumentation to your Android applications and sending telemetry to Honeycomb. This page briefly covers usage of the SDK. If you just want to see some code, check out the examples on GitHub.

Before You Begin

Before you can add instrumentation to your Android application, you will need to do a few things.

Get Your Honeycomb API Key

To send data to Honeycomb, you need to:
  1. Sign up for a Honeycomb account. To sign up, decide whether you would like Honeycomb to store your data in a US-based or EU-based location, then create a Honeycomb account in the US or create a Honeycomb account in the EU.
  2. Create a Honeycomb Ingest API Key. To get started, you can create a key that you expect to swap out when you deploy to production. Name it something helpful, perhaps noting that it’s a Getting Started key. Make note of your API key; for security reasons, you will not be able to see the key again, and you will need it later!
For setup, make sure you select the “Can create datasets” checkbox so that your data will show up in Honeycomb. Later, when you replace this key with a permanent one, you can uncheck that box.

Install the Honeycomb Android SDK

Add the Honeycomb and OpenTelemetry Android SDKs to your application’s build.gradle.kts. When adding OpenTelemetry dependencies, make sure the library is compatible with the Honeycomb Android SDK.
dependencies {
    implementation("io.opentelemetry.android:android-agent:0.11.0-alpha")
    implementation("io.honeycomb.android:honeycomb-opentelemetry-android:0.0.20")
}
If your application’s minSDK is lower than 26, enable corelib desugaring:
android {
    // ...
    compileOptions {
        isCoreLibraryDesugaringEnabled = true
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
}


dependencies {
    coreLibraryDesugaring(libs.desugar.jdk.libs)
}
If your application’s minSdk is lower than 24, running instrumentation tests or debug application builds requires that you:

Configuration

OptionDescription
apiKeyString
Your Honeycomb Ingest API Key.
Required if sending telemetry directly to Honeycomb.
tracesApiKeyString
Ingest API Key to use when sending traces. Overrides apiKey for traces.
metricsApiKeyString
Ingest API Key to use when sending metrics. Overrides apiKey for metrics.
logsApiKeyString
Ingest API Key to use when sending logs. Overrides apiKey for logs.
datasetString
Name of the dataset to send telemetry data to.
Required if using Honeycomb Classic.
metricsDatasetString
Name of the dataset to send metrics to. Overrides dataset for metrics.
apiEndpointString
Telemetry is sent to this URL. For Honeycomb EU instances, set this to https://api.eu1.honeycomb.io:443. If you’re using an OpenTelemetry Collector, provide your collector URL instead.
Default: https://api.honeycomb.io:443 (US instance)
tracesEndpointString
API endpoint to send traces to.
metricsEndpointString
API endpoint to send metrics to.
logsEndpointString
API endpoint to send logs to.
spanProcessorio.opentelemetry.sdk.trace.SpanProcessor
Additional span processor to use.
sampleRateInt
Sample rate to apply. For example, a sampleRate of 40 means 1 in 40 traces will be exported.
Default: 1
debugBoolean
Whether to enable debug logging.
Default: false.
serviceNameString
The name of your application. Used as the value for service.name resource attribute.
Default: "unknown_service"
serviceVersionString
Current version of your application. Used as the value for service.version resource attribute.
resourceAttributesMap<String, String>
Attributes to attach to outgoing resources.
headersMap<String, String>
Headers to add to exported telemetry data.
tracesHeadersMap<String, String>
Headers to add to exported trace data.
metricsHeadersMap<String, String>
Headers to add to exported metrics data.
logsHeadersMap<String, String>
Headers to add to exported logs data.
timeoutDuration
Timeout used by exporter when sending data.
Default: Duration = 10.seconds
tracesTimeoutDuration
Timeout used by traces exporter. Overrides timeout for trace data.
metricsTimeoutDuration
Timeout used by metrics exporter. Overrides timeout for metrics data.
logsTimeoutDuration
Timeout used by logs exporter. Overrides timeout for logs data.
protocolOtlpProtocol
Protocol to use when sending data.
Can be one of: OtlpProtocol.GRPC, OtlpProtocol.HTTP_PROTOBUF, OtlpProtocol.HTTP_JSON.
Default: OtlpProtocol.HTTP_PROTOBUF
tracesProtocolOtlpProtocol
Overrides protocol for trace data.
metricsProtocolOtlpProtocol
Overrides protocol for metrics data.
logsProtocolOtlpProtocol
Overrides protocol for logs data.
offlineCachingEnabledBoolean
Enable offline caching for telemetry. When offline caching is enabled, telemetry is cached during network failures. The SDK will retry exporting telemetry for up to 18 hours. Offline caching also adds a minimum delay of 30 seconds to telemetry exports.
Offline caching is an alpha feature and may be unstable.
Default: false

Sending to OpenTelemetry Collector

In production, we recommend running an OpenTelemetry Collector. Your application sends telemetry to your Collector instead of directly to Honeycomb. Your Collector then forwards the telemetry data to Honeycomb, keeping your API key stored securely in the Collector’s configuration. Call setApiEndpoint() with your Collector’s URL when initializing the SDK:
import io.honeycomb.opentelemetry.android.Honeycomb
import io.honeycomb.opentelemetry.android.HoneycombOptions
import io.opentelemetry.android.OpenTelemetryRum

class ExampleApp: Application() {
    var otelRum: OpenTelemetryRum? = null

    override fun onCreate() {
        super.onCreate()

        val options = HoneycombOptions.builder(this)
            .setApiEndpoint("http(s)://YOUR-COLLECTOR-URL")
            .setServiceName("YOUR-SERVICE-NAME")
            .setServiceVersion("0.0.1")
            .build()

        otelRum = Honeycomb.configure(this, options)
    }
}

Sending to Honeycomb

To send telemetry data directly to Honeycomb, call setApiKey() with your Ingest API Key value.
import io.honeycomb.opentelemetry.android.Honeycomb
import io.honeycomb.opentelemetry.android.HoneycombOptions
import io.opentelemetry.android.OpenTelemetryRum

class ExampleApp: Application() {
    var otelRum: OpenTelemetryRum? = null

    override fun onCreate() {
        super.onCreate()

        val options = HoneycombOptions.builder(this)
            // Uncomment the line below to send to EU instance. Defaults to US.
            // .setApiEndpoint("https://api.eu1.honeycomb.io:443")
            .setApiKey("YOUR-API-KEY")
            .setServiceName("YOUR-SERVICE-NAME")
            .setServiceVersion("0.0.1")
            .build()

        otelRum = Honeycomb.configure(this, options)
    }
}

Add Resource Attributes

Resource attributes are available on every span your instrumentation emits. Adding custom, application-specific attributes makes it easier to correlate your data to important business information. You can add extra resource attributes during SDK configuration with the .setResourceAttributes() method.
import android.app.Application
import io.honeycomb.opentelemetry.android.Honeycomb
import io.honeycomb.opentelemetry.android.HoneycombOptions
import io.opentelemetry.android.OpenTelemetryRum

class ExampleApp: Application() {
    var otelRum: OpenTelemetryRum? = null

    override fun onCreate() {
        super.onCreate()

        val options = HoneycombOptions.builder(this)
            .setApiKey("YOUR-API-KEY")
            .setServiceName("YOUR-SERVICE-NAME")
            .setServiceVersion("0.0.1")
            .setResourceAttributes(mapOf("app.ab_test" to "test c"))
            .setDebug(true)
            .build()

        otelRum = Honeycomb.configure(this, options)
    }
}

Enable Sampling

The Honeycomb Android SDK includes optional deterministic head sampling. To enable sampling, call .setSampleRate() with your desired sample rate as an Int value. The sample rate is 1 by default, meaning every trace is exported. The example below sets a sampleRate of 40, meaning 1 in 40 traces will be exported.
// ...
val options = HoneycombOptions.Builder(this)
        .setApiKey("YOUR-API-KEY")
        .setServiceName("YOUR-SERVICE-NAME")
        .setServiceVersion("0.0.1")
        .setSampleRate(40)
        .setDebug(true)
        .build()
// ...

Add Automatic Instrumentation

Enable all OpenTelemetry auto-instrumentations by including the OpenTelemetry android-agent:
dependencies {
    implementation("io.opentelemetry.android:android-agent:0.11.0-alpha")
    implementation("io.honeycomb.android:honeycomb-opentelemetry-android:0.0.20")
}
If you don’t need all of them, you can instead add dependencies for each instrumentation you want to include:

Custom Instrumentation

Automatic instrumentation is a fast way to instrument your code, but you get more insight into your application by adding custom, or manual, instrumentation. To add your own custom instrumentation, import the OpenTelemetry API in to your application.
import io.opentelemetry.api.OpenTelemetry

Add Attributes to an Active Span

You can retrieve the currently active span in a trace and add attributes to it. This lets you add more context to traces and gives you more ways to group or filter traces in your queries:
import io.opentelemetry.api.OpenTelemetry
import io.opentelemetry.api.trace.Span
import io.opentelemetry.api.common.Attributes

fun applyDiscountCode(discountCode: String) {
  val currentSpan = Span.current()

  currentSpan.setAttribute("app.cart.discount_code", discountCode)
}
In the above example, we add an app.cart.discount_code attribute to the current span. This lets us use the app.cart.discount_code field in WHERE or GROUP BY clauses in the Honeycomb query builder.

Acquire a Tracer

To create custom spans, you need to acquire a tracer:
import io.opentelemetry.api.OpenTelemetry
import io.opentelemetry.android.OpenTelemetryRum

// ...

val otelRum = app.otelRum as OpenTelemetryRum
val tracer = otelRum.tracerProvider.tracerBuilder("my-application-tracer").build()

Create Spans

Create custom spans to get a clear view of the critical parts in your application.
import io.opentelemetry.api.OpenTelemetry
import io.opentelemetry.api.common.Attributes
import io.opentelemetry.android.OpenTelemetryRum

// ...

val otelRum = app.otelRum as OpenTelemetryRum
val tracer = otelRum.tracerProvider.tracerBuilder("my-application-tracer").build()

fun generateNewLevel() {
  val span = tracer.spanBuilder("newLevel").startSpan()
  // do some work
  span.end()
}

Custom Span Processing

Span processors provide hooks for when a span starts and when it ends. This lets you mutate spans after they have been created by automatic or manual instrumentation. Here’s a basic example of a span processor that adds an attribute to spans when they start:
import io.opentelemetry.context.Context
import io.opentelemetry.sdk.trace.ReadWriteSpan
import io.opentelemetry.sdk.trace.ReadableSpan
import io.opentelemetry.sdk.trace.SpanProcessor

class BasicSpanProcessor : SpanProcessor {
    override fun onStart(
        parentContext: Context,
        span: ReadWriteSpan,
    ) {
        span.setAttribute("app.metadata", "extra metadata")
    }

    override fun isStartRequired(): Boolean {
        return true
    }

    override fun onEnd(span: ReadableSpan) {}

    override fun isEndRequired(): Boolean {
        return false
    }
}
Add the span processor as part of your SDK configuration to use it:
// ...
val options = HoneycombOptions.builder(this)
    .setApiKey("YOUR-API-KEY")
    .setServiceName("YOUR-SERVICE-NAME")
    .setSpanProcessor(BasicSpanProcessor())
    .setServiceVersion("0.0.1")
    .setDebug(true)
    .build()
// ...

Manual Context Propagation

Kotlin Coroutines may operate across multiple threads, and do not automatically inherit the correct OpenTelemetry context. Instead, context must be propagated manually with the OpenTelemetry Kotlin Extensions.
dependencies {
  implementation("io.opentelemetry:opentelemetry-extension-kotlin:1.47.0")
}
Once these are installed, replace any launch calls with
launch(Span.current().asContextElement()) {
  // ...
}

Troubleshooting

To explore common issues when sending data, visit Common Issues with Sending Data in Honeycomb.