Skip to content

Commit

Permalink
Add telemetry events to Controller.render_with_layouts/4 (#5122)
Browse files Browse the repository at this point in the history
Adding :telemetry events to the render_with_layouts/4 function in allows
APMs to record template rendering times, which was previously possible
through extending an app's view module, but not anymore from Phoenix 1.7
onward.
  • Loading branch information
jeffkreeftmeijer committed Nov 22, 2022
1 parent e878034 commit f095d1f
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 3 deletions.
22 changes: 19 additions & 3 deletions lib/phoenix/controller.ex
Expand Up @@ -880,15 +880,31 @@ defmodule Phoenix.Controller do
case root_layout(conn, format) do
{layout_mod, layout_tpl} ->
{layout_base, _} = split_template(layout_tpl)
inner = Phoenix.Template.render(view, template, format, render_assigns)
inner = template_render(view, template, format, render_assigns)
root_assigns = render_assigns |> Map.put(:inner_content, inner) |> Map.delete(:layout)
Phoenix.Template.render_to_iodata(layout_mod, layout_base, format, root_assigns)
template_render_to_iodata(layout_mod, layout_base, format, root_assigns)

false ->
Phoenix.Template.render_to_iodata(view, template, format, render_assigns)
template_render_to_iodata(view, template, format, render_assigns)
end
end

defp template_render(view, template, format, assigns) do
metadata = %{view: view, template: template, format: format}

:telemetry.span([:phoenix, :controller, :render], metadata, fn ->
{Phoenix.Template.render(view, template, format, assigns), metadata}
end)
end

defp template_render_to_iodata(view, template, format, assigns) do
metadata = %{view: view, template: template, format: format}

:telemetry.span([:phoenix, :controller, :render], metadata, fn ->
{Phoenix.Template.render_to_iodata(view, template, format, assigns), metadata}
end)
end

defp prepare_assigns(conn, assigns, template, format) do
assigns = to_map(assigns)

Expand Down
63 changes: 63 additions & 0 deletions test/phoenix/controller/render_test.exs
Expand Up @@ -162,4 +162,67 @@ defmodule Phoenix.Controller.RenderTest do
render(conn() |> put_view(nil), "index.html")
end
end

describe "telemetry" do
@render_start_event [:phoenix, :controller, :render, :start]
@render_stop_event [:phoenix, :controller, :render, :stop]
@render_exception_event [:phoenix, :controller, :render, :exception]

@render_events [
@render_start_event,
@render_stop_event,
@render_exception_event
]

setup context do
test_pid = self()
test_name = context.test

:telemetry.attach_many(
test_name,
@render_events,
fn event, measures, metadata, config ->
send(test_pid, {:telemetry_event, event, {measures, metadata, config}})
end,
nil
)
end

test "phoenix.controller.render.start and .stop are emitted on success" do
render(conn(), "index.html", title: "Hello")

assert_received {:telemetry_event, [:phoenix, :controller, :render, :start],
{_, %{format: "html", template: "index", view: MyApp.UserView}, _}}

assert_received {:telemetry_event, [:phoenix, :controller, :render, :stop],
{_, %{format: "html", template: "index", view: MyApp.UserView}, _}}

refute_received {:telemetry_event, [:phoenix, :controller, :render, :exception], _}
end

test "phoenix.controller.render.exception is emitted on failure" do
:ok =
try do
render(conn(), "index.html")
rescue
ArgumentError ->
:ok
end

assert_received {:telemetry_event, [:phoenix, :controller, :render, :start],
{_, %{format: "html", template: "index", view: MyApp.UserView}, _}}

refute_received {:telemetry_event, [:phoenix, :controller, :render, :stop], _}

assert_received {:telemetry_event, [:phoenix, :controller, :render, :exception],
{_,
%{
format: "html",
template: "index",
view: MyApp.UserView,
kind: :error,
reason: %ArgumentError{}
}, _}}
end
end
end

0 comments on commit f095d1f

Please sign in to comment.