Set up otel4s in a JVM application

Use this page when you want to start a JVM application with otel4s-oteljava and export telemetry with OTLP.

1. Add the dependencies

Add settings to build.sbt:

libraryDependencies ++= Seq(
  "org.typelevel" %% "otel4s-oteljava" % "1.1.0", // <1>
  "io.opentelemetry" % "opentelemetry-exporter-otlp" % "1.66.0" % Runtime, // <2>
  "io.opentelemetry" % "opentelemetry-sdk-extension-autoconfigure" % "1.66.0" % Runtime // <3>
)
javaOptions += "-Dotel.java.global-autoconfigure.enabled=true" // <4>

Add directives to the *.scala file:

//> using dep "org.typelevel::otel4s-oteljava:1.1.0" // <1>
//> using dep "io.opentelemetry:opentelemetry-exporter-otlp:1.66.0" // <2>
//> using dep "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.66.0" // <3>
//> using javaOpt "-Dotel.java.global-autoconfigure.enabled=true" // <4>
  1. Add otel4s-oteljava
  2. Add an OTLP exporter
  3. Add the OpenTelemetry Java autoconfigure extension
  4. Enable SDK autoconfiguration

2. Configure the SDK

Set the service name and the OTLP endpoint with environment variables or JVM properties.

Example environment variables:

export OTEL_SERVICE_NAME=auth-service
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4317

Example JVM properties:

-Dotel.service.name=auth-service
-Dotel.exporter.otlp.endpoint=http://localhost:4317

See the full list of supported options in the OpenTelemetry Java configuration guide.

3. Create OtelJava and get the providers

Use OtelJava.autoConfigured when your application is responsible for creating its own OpenTelemetry SDK instance.

import cats.effect.{IO, IOApp}
import org.typelevel.otel4s.metrics.MeterProvider
import org.typelevel.otel4s.oteljava.OtelJava
import org.typelevel.otel4s.trace.TracerProvider

object Main extends IOApp.Simple {
  def run: IO[Unit] =
    OtelJava.autoConfigured[IO]().use { otel4s =>
      program(otel4s.meterProvider, otel4s.tracerProvider)
    }

  def program(
      meterProvider: MeterProvider[IO],
      tracerProvider: TracerProvider[IO]
  ): IO[Unit] =
    for {
      meter <- meterProvider.get("auth-service")
      tracer <- tracerProvider.get("auth-service")

      counter <- meter.counter[Long]("service.requests").create
      _ <- counter.inc()

      _ <- tracer.span("startup").surround(IO.unit)
    } yield ()
}

OtelJava.autoConfigured creates an isolated, non-global SDK instance. If you need to reuse the process-wide OpenTelemetry instance, use Use the global OpenTelemetry instance.

4. Run the application

Start your application with the same environment variables or JVM properties you used for configuration.

At that point, the application can create meters and tracers and export telemetry through OTLP.

5. Verify the setup

If you want a local target for traces, use Jaeger and Docker. If you want a local stack for traces and metrics, use Grafana.

What's next