--- title: "Telemetry with OpenTelemetry" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Telemetry with OpenTelemetry} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set(eval = FALSE) ``` aurora apps are plumber2 apps, and plumber2 ships [OpenTelemetry](https://opentelemetry.io) instrumentation that follows the HTTP [semantic conventions](https://opentelemetry.io/docs/specs/semconv/http/http-spans/) out of the box. aurora's only job is to make turning it on a one-liner and to stay out of the way. Nothing about telemetry is reimplemented in aurora. ## What you get for free The instrumentation lives in the layers under plumber2 (`reqres`, `fiery`, `routr`), so it works the moment OpenTelemetry is enabled in the environment: - **A span per request**, attached to the request object (`request$otel`) and closed automatically when the response is sent. It carries the standard HTTP attributes plus `server.id`, `server.framework.name` (`"plumber2"`), and `server.framework.version`. - **A subspan per route** the request passes through, with `routr.route` and `routr.path.param.` attributes. - **The standard server metrics**: `http.server.request.duration`, `http.server.active_requests`, `http.server.request.body.size`, `http.server.response.body.size`. ## Turning it on in aurora aurora wires `plumber2::api_logger(plumber2::logger_otel())` so that your logs join the spans and metrics. Enable it any of three ways (highest precedence first): ```{r} # 1. Explicitly at run time aurora_run("meu_app", otel = TRUE) # 2. In the optional manifest — _aurora.yml # otel: true # 3. Via environment (handy for containers; the generated api.R reads it) Sys.setenv(AURORA_OTEL = "true") aurora_run("meu_app") ``` The flag resolves as: explicit `otel =` argument > `_aurora.yml` `otel:` > `AURORA_OTEL` env var > `FALSE` (off). Leaving it on is safe: wiring the otel logger is a **no-op until OpenTelemetry is actually enabled** in the environment (see below). So you can bake `otel: true` into a production image and control collection purely through env vars. ## Actually exporting data aurora does **not** own exporter configuration — that belongs to the [`otel`](https://otel.r-lib.org) and `otelsdk` packages, driven by standard `OTEL_*` environment variables. The essentials: - Install `otel` (instrumentation API) and `otelsdk` (the collector/exporter). - Select exporters with `OTEL_TRACES_EXPORTER`, `OTEL_LOGS_EXPORTER`, `OTEL_METRICS_EXPORTER` (or their `OTEL_R_*` variants). **If none are set, nothing is emitted** — this is exactly what makes the aurora flag a safe no-op. - Use `OTEL_ENV=dev` while developing so telemetry code fails loudly instead of swallowing errors. Consult the `otel`/`otelsdk` package docs for the full list and for pointing the exporter at your collector (e.g. an OTLP endpoint). aurora intentionally does not wrap these. ## Custom spans and logs in your handlers Inside a route handler you can add your own spans; they automatically pick up the active `routr` subspan as their parent: ```{r} #* @get /api/report/ #* @serializer json function(id, server) { otel::start_local_active_span("build report") # closes at end of scope server$log("message", paste("building report", id)) # joins the request span build_report(id) } ``` Two aurora conventions worth following: - Log through `server$log(...)` (the reserved `server` handler argument) rather than `cat()`/`print()`, so logs are picked up by the otel logger and associated with the request span. - Keep custom instrumentation sparse — the default instrumentation is already detailed. Add spans only for the operations you specifically care about. ## See also - `vignette("otel", package = "plumber2")` — the underlying instrumentation in full detail. - The [otel R package](https://otel.r-lib.org).