Traces every request through a Phoenix endpoint, from one call at startup.
# lib/my_app/application.ex
def start(_type, _args) do
DDTrace.Integrations.Phoenix.setup()
children = [MyAppWeb.Endpoint, ...]
Supervisor.start_link(children, strategy: :one_for_one, name: MyApp.Supervisor)
endEach request becomes one "phoenix.request" span, in the request's own
process. Inbound x-datadog-* headers are extracted first, so the span
joins the caller's trace; the resource is the method and the route
pattern — "GET /users/:id", never the concrete path — and the standard
http.* tags are set as the request goes by. Spans your code opens
inside the request, with DDTrace.trace/2, a decorator, or
DDTrace.Task, land under it with no extra wiring.
What the endpoint has to have
plug Plug.Telemetry, event_prefix: [:phoenix, :endpoint] — the line
every generated Phoenix endpoint already contains. It is the event this
integration listens to. An endpoint without it produces no spans, and
nothing else goes wrong.
Options
:filter— a one-argument function taking theconnand returning whether to trace it. True means trace. A request it rejects produces no span at all: nothing is extracted, nothing propagates onward, nothing is buffered or sent. Health checks and metrics scrapes are what this is for. Defaults to tracing everything.:hooks— customization points, as a keyword list.:requestis the only one: a one-argument function receiving the finalconn, run with the request span current, after every standard tag including the status code. It customizes through the ambient mutators, and its return value is discarded. Because the resource is already set when the hook runs, anupdate_span(resource: ...)in it wins.:endpoint_prefix— thePlug.Telemetryevent prefix to listen to. Defaults to[:phoenix, :endpoint]. See "Plug without Phoenix".
DDTrace.Integrations.Phoenix.setup(
filter: &(&1.request_path not in ["/health", "/metrics"]),
hooks: [request: &MyApp.Tracing.customize/1]
)
def customize(conn) do
DDTrace.update_span(resource: custom_route_name(conn))
DDTrace.set_tag("account.id", conn.assigns.account_id)
endA filter or hook that raises is logged and survived: the request is unaffected and the integration keeps tracing. A raising filter traces the request rather than dropping it.
Which statuses are errors
Responses in the 500–599 range mark the span an error. Any set of statuses works instead:
DD_TRACE_HTTP_SERVER_ERROR_STATUSES="500-599,429"
config :dd_trace_ex, http_server_error_statuses: "500-599,429"Single codes and inclusive lo-hi ranges, comma-separated. An unhandled
exception does not mark the span by itself: it is remembered, and
describes the error only if the response's final status is one of these.
An exception your code rescues, or one that error handling turns into a
200, leaves the span unmarked.
Plug without Phoenix
A plain Plug pipeline gets the same request span by pointing setup/1
at its own Plug.Telemetry prefix:
plug Plug.Telemetry, event_prefix: [:my_app, :router]
DDTrace.Integrations.Phoenix.setup(endpoint_prefix: [:my_app, :router])Everything works the same, with two differences: component is
"plug", and the resource comes from the route Plug.Router matched
rather than from a Phoenix router.
Two endpoints are two setup/1 calls with two prefixes, and each keeps
its own filter: and hooks:. They may be nested — an endpoint that
forwards to a router with a Plug.Telemetry of its own — in which case
a request through both produces two spans, the inner one a child of the
outer. Calling setup/1 twice with the same prefix is a quiet :ok, so
a supervision tree that restarts never crashes on it.
The one request it cannot trace
Plug.Telemetry emits its stop event from Plug.Conn.register_before_send/2,
so a request process that dies without sending any response never fires
it, and that request's span is lost rather than finished. Phoenix's own
error rendering sends a response before re-raising, so an exception in a
controller is traced normally; this is about a process killed outright.
Summary
Functions
Attaches the handlers that trace requests. Call once, at startup.
Functions
@spec setup(keyword()) :: :ok
Attaches the handlers that trace requests. Call once, at startup.
Returns :ok, including when the same prefix is already attached and
when the options make no sense — an integration that refused to start
would take the application down with it, and the point of tracing is
that it cannot.
See the module documentation for the options.