Current section

Files

Jump to
new_relic_agent lib new_relic telemetry phoenix.ex
Raw

lib/new_relic/telemetry/phoenix.ex

defmodule NewRelic.Telemetry.Phoenix do
use GenServer
alias NewRelic.Tracer
@moduledoc """
Provides `Phoenix` instrumentation via `telemetry`.
This instrumentation adds extra Phoenix specific instrumentation
on top of the base `NewRelic.Telemetry.Plug` instrumentation.
You can opt-out of this instrumentation with `:phoenix_instrumentation_enabled` via configuration.
See `NewRelic.Config` for details.
"""
def start_link(_) do
config = %{
enabled?: NewRelic.Config.feature?(:phoenix_instrumentation),
handler_id: {:new_relic, :phoenix}
}
GenServer.start_link(__MODULE__, config, name: __MODULE__)
end
@phoenix_router_start [:phoenix, :router_dispatch, :start]
@phoenix_controller_render_start [:phoenix, :controller, :render, :start]
@phoenix_controller_render_stop [:phoenix, :controller, :render, :stop]
@phoenix_error [:phoenix, :error_rendered]
@phoenix_events [
@phoenix_router_start,
@phoenix_controller_render_start,
@phoenix_controller_render_stop,
@phoenix_error
]
@doc false
def init(%{enabled?: false}), do: :ignore
def init(%{enabled?: true} = config) do
:telemetry.attach_many(
config.handler_id,
@phoenix_events,
&__MODULE__.handle_event/4,
config
)
{:ok, config}
end
@doc false
def terminate(_reason, %{handler_id: handler_id}) do
:telemetry.detach(handler_id)
end
def handle_event(
@phoenix_router_start,
_measurements,
%{conn: conn} = meta,
_config
) do
[
phoenix_name: phoenix_name(meta),
"phoenix.endpoint": conn.private[:phoenix_endpoint] |> inspect(),
"phoenix.router": conn.private[:phoenix_router] |> inspect(),
"phoenix.controller": meta.plug |> inspect(),
"phoenix.action":
case meta.plug_opts do
action when is_atom(action) -> to_string(action)
action -> inspect(action)
end,
"phoenix.format": conn.private[:phoenix_format],
"phoenix.template": conn.private[:phoenix_template],
"phoenix.view": conn.private[:phoenix_view] && inspect(conn.private[:phoenix_view])
]
|> NewRelic.add_attributes()
end
def handle_event(
@phoenix_error,
_measurements,
%{conn: conn, status: 404} = meta,
_config
) do
[
phoenix_name: phoenix_name(meta),
"phoenix.endpoint": conn.private[:phoenix_endpoint] |> inspect(),
"phoenix.router": conn.private[:phoenix_router] |> inspect()
]
|> NewRelic.add_attributes()
end
def handle_event(
@phoenix_controller_render_start,
meas,
%{view: view, template: template, format: format} = meta,
_config
) do
Tracer.Direct.start_span(
meta.telemetry_span_context,
"#{inspect(view)}.show",
system_time: meas.system_time,
attributes: [
"phoenix.view": inspect(view),
"phoenix.template": template,
"phoenix.format": format
]
)
end
def handle_event(
@phoenix_controller_render_stop,
meas,
meta,
_config
) do
Tracer.Direct.stop_span(meta.telemetry_span_context, duration: meas.duration)
end
def handle_event(_event, _measurements, _meta, _config) do
:ignore
end
defp phoenix_name(%{phoenix_live_view: {module, action, _, _}}) when is_atom(action) do
"/Phoenix/#{inspect(module)}/#{action}"
end
defp phoenix_name(%{plug: controller, plug_opts: action}) when is_atom(action) do
"/Phoenix/#{inspect(controller)}/#{action}"
end
defp phoenix_name(%{conn: %{private: %{phoenix_endpoint: phoenix_endpoint}} = conn}) do
"/Phoenix/#{inspect(phoenix_endpoint)}/#{conn.method}"
end
defp phoenix_name(%{conn: conn}) do
"/Phoenix/#{conn.method}"
end
end