WEBINAR Nov 6: End-to-end mobile observability with Embrace and Grafana Cloud. Learn how to connect Embrace mobile telemetry with Grafana Cloud data.

Sign-up

Building a Kotlin API for OpenTelemetry: Our journey so far

Android robot holding the OpenTelemetry telescope

Embrace has opened a proposal to donate a Kotlin implementation of the OpenTelemetry specification that can be used in Kotlin Multiplatform projects.

Kotlin Multiplatform (KMP) allows running Kotlin code on many different platforms, such as browser, server, and desktop environments. Traditionally, Kotlin has been most popular on Android and the Java Virtual Machine (JVM), but with the advent of KMP the number of folks using it to share code between different platforms has been steadily growing.

Embrace has opened a proposal to donate a Kotlin implementation of the OpenTelemetry specification that can be used in KMP projects. This would allow KMP and Kotlin projects to capture telemetry using one API for many different platforms. The API has been designed to remain as platform-agnostic an implementation of OpenTelemetry as possible and attempts to be as mobile-friendly as possible for the important Android/iOS use case.

What were the options for using OpenTelemetry on Android before today?

The OpenTelemetry Java SDK and OpenTelemetry Android SDK were the main options for using OpenTelemetry on the Android ecosystem. The OpenTelemetry Android SDK is built on top of  the OpenTelemetry Java SDK, adding extra instrumentation and default behaviors to enhance its use on the Android ecosystem.

What were some of the disadvantages?

First, the OpenTelemetry Java SDK was implemented in Java. This is the correct decision for the OpenTelemetry Java SDK’s primary use case of capturing telemetry on JVM backends running Java applications. However, the vast majority of Android developers use Kotlin in their applications. While Kotlin does have interoperability with Java, APIs accessed via Java interop tend not to feel like idiomatic Kotlin.

Second, mobile is a very different ecosystem from the backend, and the OpenTelemetry Java SDK was originally designed with non-mobile use cases in mind. Things like SDK startup time don’t matter as much to backend applications that run on powerful hardware, whereas on mobile this is a critical path for most applications as it can affect UI load times.

We wanted to start from scratch with a mobile-friendly API for Kotlin, so that’s what we did.

How did we design a Kotlin API for OpenTelemetry?

We had a few requirements on our shopping list when developing a Kotlin API. We wanted the API to have the following benefits:

  1. Make it idiomatic and easy to use in Kotlin, with an emphasis on using a Kotlin DSL rather than Java builders.
  2. Hide implementation details of how OpenTelemetry works by exposing interfaces rather than concrete types wherever possible.
  3. Support Kotlin Multiplatform, by avoiding the use of platform-specific types in our API.
  4. Make it mobile-friendly by using minimal resources at startup and performing lazy initializations wherever possible.
  5. Ensure it’s fully compliant with the OpenTelemetry specification. We have attempted to follow the specification as closely as possible, and separated any syntactic sugar into an “API extension” module where we had non-consensus opinions on how the SDK should function.

Note that at the time of publication of this post, our API is marked as experimental and is subject to change without warning. As we continue to get usage from folks and gain confidence in the API surface, we will promote the necessary parts of the API to stable.

How does this interact with Embrace?

Embrace’s Android SDK is now built on top of the OpenTelemetry Kotlin API, but we made a conscious decision to avoid adding Embrace-specific concepts or naming to the API. The OpenTelemetry Kotlin API follows the OpenTelemetry specification as closely as possible and remains a vendor-neutral implementation.

Recently, we opened a proposal to donate the OpenTelemetry Kotlin API to the Cloud Native Computing Foundation (CNCF), the foundation that oversees OpenTelemetry, so that the project truly becomes a community effort. When this donation proposal is accepted, CNCF will assume ultimate control over the OpenTelemetry Kotlin API, rather than Embrace.

The donation won’t change anything about Embrace’s contributions and ongoing maintenance towards the project. Hopefully though, it will increase the number of organizations and individuals contributing towards the project. We think this diversity of opinion will be helpful for improving aspects of the OpenTelemetry Kotlin API that Embrace might not be as focused on.

What does the OpenTelemetry Kotlin API API look like?

Our approach to the API has been to use Domain-Specific Languages (DSLs) and interfaces judiciously to make it feel like a more idiomatic Kotlin API. See the examples below for what this looks like in practice.

⚠️ The OpenTelemetry Kotlin API has not reached 1.0.0, so its API surface isn’t considered stable yet. At time of publication, these code snippets were correct for OpenTelemetry Kotlin API 0.6.0.

Walkthrough of how to capture a trace with the OpenTelemetry Kotlin API, compared to the OpenTelemetry Java SDK

Creating a span in the OpenTelemetry Java SDK looks like this:

// create a Tracer
val otel = OpenTelemetrySdk.builder().build()
val tracer = otel.tracerProvider.get("my_tracer")

// start a span named 'my_span' with custom attributes
val span = tracer.spanBuilder("my_span")
    .setAttribute("attribute_key", "some_value")
    .startSpan()

// add a span event
span.addEvent("click_event")

// end the span
span.end()

Whereas in the OpenTelemetry Kotlin API, it looks like this:

// create a Tracer 
val otel = createOpenTelemetry()
val tracer = otel.getTracer("my_tracer")

// start a span named 'my_span' with custom attributes
val span = tracer.createSpan("my_span") { 
    setStringAttribute("attribute_key", "some_value")
}

// add a span event
span.addEvent("click_event")

// end the span
span.end()

Walkthrough of how to capture a log with the OpenTelemetry Kotlin API, compared to the OpenTelemetry Java SDK

Sending a log in the OpenTelemetry Java SDK looks like this:

// create a Logger
val otel = OpenTelemetrySdk.builder().build()
val logger = otel.logsBridge.get("my_logger")

// emit a log named with a 'Hello, World!' body
logger.logRecordBuilder() 
    .setBody("Hello, World!")
    .setAttribute("attribute_key", "some_value")
    .emit()

Whereas in the OpenTelemetry Kotlin API, it looks like this:

// create a Logger
val otel = createOpenTelemetry()
val logger = otel.getLogger("my_logger")

// emit a log named with a 'Hello, World!' body
logger.log("Hello, World!") {
    setStringAttribute("attribute_key", "some_value") 
}

If you’re interested in more examples of how the OpenTelemetry Kotlin API works, please check out the examples in the repository.

Is the OpenTelemetry Kotlin API backward compatible with the OpenTelemetry Java SDK?

The OpenTelemetry Kotlin API is capable of running in a compatibility mode where it decorates an OpenTelemetry Java SDK instance. This allows you to use the Kotlin API for capturing telemetry, but is driven by the Java implementation under the hood.

Even if you’re not running in compatibility mode, it’s possible to use OpenTelemetry Java’s API with the OpenTelemetry Kotlin API on the JVM (with some caveats around features such as context propagation). This can be beneficial if you have existing instrumentation written using OpenTelemetry Java’s API that you can’t or don’t want to migrate to the OpenTelemetry Kotlin API yet.

What do we still need to work on?

The Logging and Tracing API are practically complete and can be used today. APIs for creating Processors and Exporters exist, but no concrete implementation is in place for OTLP export that works on all platforms. At the time of publication, this is something Embrace is actively working on and hopes to implement on the JVM first, and other platforms later.

Embrace has only deployed the OpenTelemetry Kotlin API in our Android SDK. The SDK can be built for iOS/JS targets, but this has not been tested to the same level as the Android/JVM targets. At this point, other targets (e.g., iOS/JS) are less of a priority for Embrace, so we’re curious to see whether there will be community contributions for them.

There are various other OpenTelemetry APIs, such as implicit context propagation and metrics, that still require implementation. But in short, the core APIs for logging and tracing are effectively complete and can be used today.

How can you help or give feedback?

If you’re interested in using OpenTelemetry on Kotlin Multiplatform, we need your help! We are looking for contributors who are willing to maintain the codebase, participate in regular Special Interest Group (SIG) meetings, and generally help drive the SDK forward.

If you’re interested in becoming a contributor or if you know someone who might be, please comment on the donation proposal.

If you don’t want to become a contributor but do want to give feedback on the project so far or try it out, please check out the repository here and file an issue with your thoughts.

Embrace Deliver incredible mobile experiences with Embrace.

Get started today with 1 million free user sessions.

Get started free
Related Content
The Embrace and OpenTelemtry logos.

Getting started with OpenTelemetry for mobile: Key takeaways

Mobile engineers often hear that OpenTelemetry is the standard — but applying it to mobile is harder than it looks. In our latest Getting Started webinar, we walked through the pitfalls of vanilla OTel and how Embrace helps teams instrument in minutes. Here’s the recap, with clips and resources to help you try it yourself.

OpenTelemetry Metrics: Types & Examples

This article explores the core types of OpenTelemetry metrics, their data model, and practical examples to help you leverage these tools effectively.