aurora apps are plumber2 apps, and plumber2 ships OpenTelemetry instrumentation that follows the HTTP semantic conventions 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.
The instrumentation lives in the layers under plumber2
(reqres, fiery, routr), so it
works the moment OpenTelemetry is enabled in the environment:
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.routr.route and
routr.path.param.<name> attributes.http.server.request.duration,
http.server.active_requests,
http.server.request.body.size,
http.server.response.body.size.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):
# 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.
aurora does not own exporter configuration — that
belongs to the otel
and otelsdk packages, driven by standard
OTEL_* environment variables. The essentials:
otel (instrumentation API) and
otelsdk (the collector/exporter).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.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.
Inside a route handler you can add your own spans; they automatically
pick up the active routr subspan as their parent:
#* @get /api/report/<id>
#* @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:
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.vignette("otel", package = "plumber2") — the underlying
instrumentation in full detail.