Times every matched Phoenix route, kind: "controller". Call once at startup, e.g.
application.ex's own start/2, before the rest of the supervision tree starts:
ForgeOpsTracker.Integrations.Phoenix.attach()Attaches to [:phoenix, :router_dispatch, :stop] (confirmed directly against the installed
phoenix source, not assumed from docs: Phoenix.Router.__call__/5 emits this around every
matched route's own plug pipeline, controller action included), a global event name, not
per-endpoint-namespaced, so this one call covers every Phoenix endpoint in the host app. An
unmatched route (a 404) never fires this event at all, the same "only a real match gets timed"
behavior every other language's own controller/action instrumentation in this repo already has.
transaction_name is "<HTTP method> <route pattern>" (e.g. "GET /users/:id"), not the
literal path: metadata.route is Phoenix's own matched route pattern, which keeps a distinct
user id from exploding into its own separate transaction the way the literal path would.
Also records the same automatic "controller" breadcrumb every other client's own request/
controller-lifecycle instrumentation already does (message "METHOD route", level "error" on
a 5xx response and "info" otherwise, data holding the status and path), gated independently on
track_breadcrumbs the same way ForgeOpsTracker.record_performance/3 is already independently
gated on track_performance: this one telemetry event is where several genuinely independent
mechanisms happen to be recorded from, not one mechanism triggering another.
Also opens a trace at [:phoenix, :router_dispatch, :start] and finishes it at :stop (or
:exception), the root span named like the transaction above, so a slow request's own breakdown
reaches /spans (see ForgeOpsTracker.span/4). ForgeOpsTracker.Integrations.Ecto adds a
"database" span per query into it. Outbound HTTP is not instrumented (each HTTP client library
has its own telemetry events): wrap a call in ForgeOpsTracker.http_span/4, which also hands it
the traceparent header to send.
The trace continues the caller's when the request arrived with a usable W3C traceparent header
(see ForgeOpsTracker.TraceParent), and its trace id exists even with track_tracing off, since
every error reported during the request carries it. :start fires once the router has matched
the route, before the pipeline and controller run, so the request is named there
(transaction_name and endpoint, both "GET /users/:id"): an error captured from inside the
controller already carries both.
A request that raises emits :exception before its process crashes, and the crash is only
reported afterward, by ForgeOpsTracker.LoggerHandler, once this has already finished (and
cleared) the request's trace. So :exception first remembers the request's context for that
exception (ForgeOpsTracker.Tracing.snapshot_escaped/1), which the crash report then picks up,
and marks the request errored, so its trace is sent however fast it was.