Skip to content

Instantly share code, notes, and snippets.

@cblavier
Last active April 14, 2022 01:27
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save cblavier/faaee0e3969de708737ed0e6c623d3dd to your computer and use it in GitHub Desktop.
Save cblavier/faaee0e3969de708737ed0e6c623d3dd to your computer and use it in GitHub Desktop.
A Phoenix Telemetry agent to monitor all LiveView events & errors
defmodule AppSignalTelemetry do
use GenServer
require Appsignal.Utils
import Appsignal.Utils, only: [module_name: 1]
@tracer Appsignal.Utils.compile_env(:appsignal, :appsignal_tracer, Appsignal.Tracer)
@span Appsignal.Utils.compile_env(:appsignal, :appsignal_span, Appsignal.Span)
@appsignal_namespace "live_view"
def start_link do
GenServer.start_link(__MODULE__, [], name: __MODULE__)
end
def init(_opts) do
events = [
{"live_view.mount", [:phoenix, :live_view, :mount]},
{"live_view.handle_params", [:phoenix, :live_view, :handle_params]},
{"live_view.handle_event", [:phoenix, :live_view, :handle_event]},
{"live_component.handle_event", [:phoenix, :live_component, :handle_event]}
]
for {name, event} <- events do
:telemetry.attach("#{name}.start", event ++ [:start], &handle_event_start/4, name)
:telemetry.attach("#{name}.stop", event ++ [:stop], &handle_event_stop/4, name)
:telemetry.attach("#{name}.exception", event ++ [:exception], &handle_event_exception/4, name)
end
{:ok, nil}
end
defp handle_event_start(_event, _params, metadata, event_name) do
@appsignal_namespace
|> @tracer.create_span(@tracer.current_span(),
start_time: :os.system_time(),
pid: metadata.socket.root_pid
)
|> @span.set_name(module_name(metadata.socket.view))
|> @span.set_attribute("type", event_type(event_name))
|> @span.set_attribute("appsignal:category", event_name)
|> maybe_set_sample_data(metadata[:params], :params)
|> maybe_set_sample_data(metadata[:session], :session_data)
|> maybe_set_attribute(metadata[:uri], :uri)
|> maybe_set_attribute(metadata[:event], :event)
end
defp handle_event_stop(_event, _params, metadata, _event_name) do
metadata.socket.root_pid
|> @tracer.current_span()
|> @tracer.close_span(end_time: :os.system_time())
end
defp handle_event_exception(_event, _params, metadata, _event_name) do
metadata.socket.root_pid
|> @tracer.current_span()
|> @span.add_error(metadata.kind, metadata.reason, metadata.stacktrace)
|> @tracer.close_span(end_time: :os.system_time())
@tracer.ignore(metadata.socket.root_pid)
end
defp event_type(event_name), do: event_name |> String.split(".") |> Enum.at(-1)
defp maybe_set_sample_data(span, val, key) when is_map(val) do
case @span.set_sample_data(span, to_string(key), val) do
nil -> span
span -> span
end
end
defp maybe_set_sample_data(span, _val, _key), do: span
defp maybe_set_attribute(span, val, key) when is_binary(val) do
case @span.set_attribute(span, to_string(key), val) do
nil -> span
span -> span
end
end
defp maybe_set_attribute(span, _val, _key), do: span
end
@terrcin
Copy link

terrcin commented Apr 4, 2022

Thanks for making this available, it saved me a HEAP of time. I made a few minor changes here: https://gist.github.com/terrcin/76ec781a586f159fc348b29756b3a449

Most notable was resolving the warnings around how the event handlers were being specified:

https://hexdocs.pm/telemetry/telemetry.html#attach/4
[info] The function passed as a handler with ID "live_component.handle_event.start" is a local function.
This means that it is either an anonymous function or a capture of a function without a module specified. That may cause a performance penalty when calling that handler. For more details see the note in `telemetry:attach/4` documentation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment