Skip to content

Commit

Permalink
Add LiveviewTelemetry
Browse files Browse the repository at this point in the history
The liveview option in OpentelemetryPhoenix is not tracking enough,
causing no span to be present in some liveviews. It needs a custom
Telemetry module.
  • Loading branch information
MvanDiemen committed Jun 28, 2024
1 parent 631ca41 commit 15bdd9e
Show file tree
Hide file tree
Showing 2 changed files with 102 additions and 5 deletions.
8 changes: 3 additions & 5 deletions lib/tracing.ex
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,9 @@ defmodule Tracing do
Enum.each(elements, &(:ok = setup_element(&1)))
end

def setup_element(:phoenix) do
OpentelemetryPhoenix.setup()
end

def setup_element(:oban), do: Tracing.ObanTelemetry.setup()
def setup_element(:aws), do: Tracing.AWSTelemetry.setup()
def setup_element(:chromic_pdf), do: Tracing.ChromicPDFTelemetry.setup()
def setup_element(:liveview), do: Tracing.LiveviewTelemetry.setup()
def setup_element(:oban), do: Tracing.ObanTelemetry.setup()
def setup_element(:phoenix), do: OpentelemetryPhoenix.setup()
end
99 changes: 99 additions & 0 deletions lib/tracing/liveview_telemetry.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
defmodule Tracing.LiveviewTelemetry do
require OpenTelemetry.Tracer
alias __MODULE__
alias OpenTelemetry.Span

@tracer_id LiveviewTelemetry

@event_names [
[:phoenix, :live_view, :mount, :start],
[:phoenix, :live_view, :mount, :stop],
[:phoenix, :live_view, :mount, :exception],
[:phoenix, :live_view, :handle_params, :start],
[:phoenix, :live_view, :handle_params, :stop],
[:phoenix, :live_view, :handle_params, :exception],
[:phoenix, :live_view, :handle_event, :start],
[:phoenix, :live_view, :handle_event, :stop],
[:phoenix, :live_view, :handle_event, :exception],
[:phoenix, :live_component, :handle_event, :start],
[:phoenix, :live_component, :handle_event, :stop],
[:phoenix, :live_component, :handle_event, :exception]
]
def setup do
:telemetry.attach_many(
LiveviewTelemetry,
@event_names,
&LiveviewTelemetry.handle_event/4,
%{}
)
end

def handle_event([:phoenix, source, function, :start], _measurements, meta, _config)
when source in [:live_view, :live_component] do
%{socket: %{view: live_view}} = meta

OpentelemetryTelemetry.start_telemetry_span(
@tracer_id,
"#{inspect(live_view)}.#{function}",
meta,
%{kind: :internal}
)
|> Span.set_attributes(meta_based_attributes(meta, source, function))
end

def handle_event([:phoenix, source, _function, :stop], _measurements, meta, _config)
when source in [:live_view, :live_component] do
OpentelemetryTelemetry.end_telemetry_span(@tracer_id, meta)
end

def handle_event(
[:phoenix, source, _kind, :exception],
_measurements,
%{kind: kind, reason: reason, stacktrace: stacktrace} = meta,
_config
)
when source in [:live_view, :live_component] do
ctx = OpentelemetryTelemetry.set_current_telemetry_span(@tracer_id, meta)

exception = Exception.normalize(kind, reason, stacktrace)

Span.record_exception(ctx, exception, stacktrace, [])
Span.set_status(ctx, OpenTelemetry.status(:error, Exception.message(exception)))

OpentelemetryTelemetry.end_telemetry_span(@tracer_id, meta)
end

defp meta_based_attributes(meta, source, function) do
module =
case {source, meta} do
{:live_view, _} -> module_to_string(meta.socket.view)
{:live_component, %{component: component}} -> module_to_string(component)
end

attributes = [
"liveview.module": module,
"liveview.callback": Atom.to_string(function)
]

Enum.reduce(meta, attributes, fn
{:uri, uri}, acc ->
Keyword.put(acc, :"liveview.uri", uri)

{:component, component}, acc ->
Keyword.put(acc, :"liveview.module", module_to_string(component))

{:event, event}, acc ->
Keyword.put(acc, :"liveview.event", event)

_, acc ->
acc
end)
end

defp module_to_string(module) when is_atom(module) do
case to_string(module) do
"Elixir." <> name -> name
erlang_module -> ":#{erlang_module}"
end
end
end

0 comments on commit 15bdd9e

Please sign in to comment.