Export telemetry with OTLP
OTLP (OpenTelemetry Protocol) is the default way to export telemetry
from OpenTelemetry SDKs.
In otel4s, OTLP exporters can send traces, metrics, and logs to OTLP-compatible backends such
as OpenTelemetry Collector, Grafana LGTM,
Grafana Tempo, Jaeger.
Use OTLP when you want one transport/protocol family for all telemetry signals, consistent configuration, and broad backend compatibility.
otel4s-sdk OTLP implementation supports grpc/protobuf, http/protobuf, and http/json.
By default, OTLP exporter uses http/protobuf and sends telemetry to http://localhost:4318/.
Getting Started
Add settings to build.sbt:
libraryDependencies ++= Seq(
"org.typelevel" %%% "otel4s-sdk" % "0.19.3", // <1>
"org.typelevel" %%% "otel4s-sdk-exporter" % "0.19.3" // <2>
)
Add directives to the *.scala file:
//> using dep "org.typelevel::otel4s-sdk::0.19.3" // <1>
//> using dep "org.typelevel::otel4s-sdk-exporter::0.19.3" // <2>
- Add the
otel4s-sdklibrary. - Add the
otel4s-sdk-exporterlibrary (includes OTLP exporters autoconfiguration).
Configuration
OpenTelemetrySdk.autoConfigured(...) reads standard OpenTelemetry environment variables and system properties.
See SDK configuration settings for the full list of options.
Add settings to build.sbt:
javaOptions ++= Seq(
"-Dotel.service.name=orders-api",
"-Dotel.exporter.otlp.endpoint=http://localhost:4318",
"-Dotel.exporter.otlp.protocol=http/protobuf"
)
Add directives to the *.scala file:
//> using javaOpt -Dotel.service.name=orders-api
//> using javaOpt -Dotel.exporter.otlp.endpoint=http://localhost:4318
//> using javaOpt -Dotel.exporter.otlp.protocol=http/protobuf
$ export OTEL_SERVICE_NAME=orders-api
$ export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
$ export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
You can configure OTLP globally (otel.exporter.otlp.*) or per signal (otel.exporter.otlp.traces.*,
otel.exporter.otlp.metrics.*, otel.exporter.otlp.logs.*).
Per-signal settings take precedence over global settings.
You can also mix exporters by signal. For example, use OTLP for traces and Prometheus for metrics
(otel.traces.exporter=otlp, otel.metrics.exporter=prometheus).
If you mix them, also register the Prometheus configurer in code, for example:
_.addMetricExporterConfigurer(PrometheusMetricExporterAutoConfigure[IO]).
See the Prometheus guide for more details.
Autoconfigured OTLP Exporters
import cats.effect.{IO, IOApp}
import org.typelevel.otel4s.sdk.OpenTelemetrySdk
import org.typelevel.otel4s.sdk.exporter.otlp.autoconfigure.OtlpExportersAutoConfigure
object TelemetryApp extends IOApp.Simple {
def run: IO[Unit] =
OpenTelemetrySdk
.autoConfigured[IO](
// register OTLP exporters for traces, metrics, and logs
_.addExportersConfigurer(OtlpExportersAutoConfigure[IO])
)
.use { configured =>
val sdk = configured.sdk
for {
tracer <- sdk.tracerProvider.get("orders-api")
meter <- sdk.meterProvider.meter("orders-api").get
counter <- meter.counter[Long]("orders.requests").create
_ <- tracer.span("orders.request").surround(counter.inc())
} yield ()
}
}
Run the app
Verify
- Check your backend for telemetry from
orders-api. - Confirm at least one span
orders.requestand one metricorders.requests. - If export fails, verify protocol and port match:
4318for HTTP (http/protobuf,http/json),4317forgrpc.
Troubleshooting
- No data in backend: ensure
OTEL_SERVICE_NAMEis set and backend endpoint is reachable. - HTTP protocol but gRPC port: switch endpoint to
:4318or protocol togrpc. - Unexpected paths: when using global
otel.exporter.otlp.endpoint, provide a base URL (the SDK appends/v1/{signal}for HTTP exporters). - Different endpoints per signal: set
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,OTEL_EXPORTER_OTLP_LOGS_ENDPOINT.