Android app in Kotlin instrumentation

SigNoz Cloud - This page applies to SigNoz Cloud editions.
Self-Host - This page applies to self-hosted SigNoz editions.

Overview

This document contains instructions on how to set up OpenTelemetry instrumentation in your Android applications built using Kotlin and view your application traces in SigNoz.

Prerequisites

  • Kotlin support in your Android app project.
  • Kotlin toolchain installed; see the Kotlin documentation.
  • Access to a SigNoz Cloud account or self-hosted SigNoz instance.

Send traces to SigNoz

Test the sample app for Kotlin.

Step 1: Add OpenTelemetry dependencies

Add these to your module build.gradle:

app/build.gradle
implementation platform("io.opentelemetry:opentelemetry-bom:1.25.0")
implementation("io.opentelemetry:opentelemetry-api")
implementation("io.opentelemetry:opentelemetry-context")
implementation("io.opentelemetry:opentelemetry-exporter-otlp")
implementation("io.opentelemetry:opentelemetry-exporter-logging")
implementation("io.opentelemetry:opentelemetry-extension-kotlin")
implementation("io.opentelemetry:opentelemetry-sdk")
implementation("io.opentelemetry:opentelemetry-semconv")

Step 2: Configure network settings

app/src/main/res/xml/network_security_config.xml:

app/src/main/res/xml/network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config cleartextTrafficPermitted="true">
        <domain includeSubdomains="true">ingest.<region>.signoz.cloud</domain>
    </domain-config>
</network-security-config>

app/src/main/AndroidManifest.xml:

app/src/main/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest ...>
  <uses-permission android:name="android.permission.INTERNET" />

  <application
    ...
    android:networkSecurityConfig="@xml/network_security_config">
    ...
  </application>

</manifest>

For self-hosted, replace the domain with your collector host (emulator often 10.0.2.2). Allow cleartext only when you use http://.

Step 3: Initialize OpenTelemetry

Create OpentelemetryUtil.kt:

app/src/main/java/com/example/androidkotlindemo/OpenTelemetryUtil.kt
package com.example.androidkotlindemo

import io.opentelemetry.api.OpenTelemetry
import io.opentelemetry.api.common.Attributes
import io.opentelemetry.api.trace.Tracer
import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator
import io.opentelemetry.context.propagation.ContextPropagators
import io.opentelemetry.exporter.logging.LoggingSpanExporter
import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter
import io.opentelemetry.sdk.OpenTelemetrySdk
import io.opentelemetry.sdk.resources.Resource
import io.opentelemetry.sdk.trace.SdkTracerProvider
import io.opentelemetry.sdk.trace.export.BatchSpanProcessor
import io.opentelemetry.sdk.trace.export.SimpleSpanProcessor
import io.opentelemetry.semconv.resource.attributes.ResourceAttributes

class OpenTelemetryUtil {
    companion object {
        @JvmStatic
        fun init() {
            val otelResource = Resource.getDefault().merge(
                Resource.create(
                    Attributes.of(
                        ResourceAttributes.SERVICE_NAME, "<service_name>",
                        ResourceAttributes.SERVICE_VERSION, "<service_version>",
                        ResourceAttributes.HOST_NAME, "<service_name>"
                    )
                )
            )

            val sdkTracerProvider = SdkTracerProvider.builder()
                .addSpanProcessor(SimpleSpanProcessor.create(LoggingSpanExporter.create()))
                .addSpanProcessor(
                    BatchSpanProcessor.builder(
                        OtlpGrpcSpanExporter.builder()
                            .setEndpoint("ingest.<region>.signoz.cloud:443/v1/traces")
                            .addHeader("signoz-ingestion-key", "<your-ingestion-key>")
                            .build()
                    ).build()
                )
                .setResource(otelResource)
                .build()

            val openTelemetry: OpenTelemetry = OpenTelemetrySdk.builder()
                .setTracerProvider(sdkTracerProvider)
                .setPropagators(ContextPropagators.create(W3CTraceContextPropagator.getInstance()))
                .buildAndRegisterGlobal()

            tracer = openTelemetry.getTracer("android-tracer", "1.0.0")
        }

        private var tracer: Tracer? = null

        @JvmStatic
        fun getTracer(): Tracer? {
            return tracer
        }
    }
}

Verify these values:

  • <service_name>: logical name of your mobile service; this is what you will see in SigNoz.
  • <service_version> (optional): Your release version, image tag, or git SHA (e.g., 1.4.2, a01dbef8).
  • <region>: Your SigNoz Cloud region
  • <your-ingestion-key>: Your SigNoz ingestion key

Set service.version to a per-build value, not a static string. SigNoz detects a deployment each time this value changes. Common sources:

  • Bash / shell: service.version=$(git rev-parse --short HEAD)
  • GitHub Actions: service.version=${{ github.sha }}
  • GitLab CI: service.version=$CI_COMMIT_SHORT_SHA
  • Kubernetes: inject from your Helm chart image tag or CI variable

Using self-hosted SigNoz? Most steps are identical. Update the endpoint and remove the ingestion key header as shown in Cloud → Self-Hosted.

Initialize OpenTelemetry in MainActivity.kt:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        OpenTelemetryUtil.init()
        ...
    }
}

Step 4: Send telemetry data

Add the tracer imports where you create spans:

app/src/main/java/com/example/androidkotlindemo/MainActivity.kt
import io.opentelemetry.api.trace.Tracer
import io.opentelemetry.api.trace.Span
import io.opentelemetry.context.Scope

Create spans:

app/src/main/java/com/example/androidkotlindemo/MainActivity.kt
fun parentSpan() {
    val tracer: Tracer = OpenTelemetryUtil.getTracer()!!
    val span = tracer.spanBuilder("Parent Span").startSpan()
    try {
        span.makeCurrent().use {
            childSpan()
        }
    } finally {
        span.end()
    }
}

fun childSpan() {
    val tracer: Tracer = OpenTelemetryUtil.getTracer()!!
    val span = tracer.spanBuilder("Child Span").startSpan()
    try {
        span.makeCurrent().use {
            // add attributes/events as needed
        }
    } finally {
        span.end()
    }
}

Step 5: Run the app

Run your application from Android Studio.

Validate

  • In SigNoz, go to Traces Explorer and confirm that the traces for your service (for example <service_name>) are present.
  • Open a recent trace and verify that spans from your Kotlin Android app are present with the attributes you set.

Troubleshooting

  • If spans are missing, verify that the ingest.<region>.signoz.cloud or your self-hosted collector endpoint is reachable from the emulator/device and that the ingestion key (for Cloud) is correct.

Next steps

Last updated: May 27, 2026

Edit on GitHub

Was this page helpful?

Your response helps us improve this page.