Enriching AppSignal Error Reports in LiveView

Adding essential context like current path and user info.

AppSignal automatically captures rich context across different parts of your application. For regular Phoenix controllers, Oban jobs, and other background tasks, you get the full context including session data, request data, and job parameters.

However, LiveView's real-time nature presents a unique challenge. While AppSignal provides automatic error tracking for LiveView, the default information is more limited compared to other parts of your application. By default, AppSignal captures for LiveView:

  • The error message and stack trace
  • The LiveView module name
  • The event type (mount, handle_params, handle_event, etc.)
  • Event parameters (for handle_event, handle_params, etc.)

However, it misses important contextual information that would automatically be available elsewhere:

  • The current user's information (session data)
  • The path the user was on
  • Other LiveView state (assigns)

In this post, we'll explore how to enhance AppSignal errors in LiveView. We'll attach a telemetry handler much like AppSignal does to enrich the span data.

Setting Up the Extension

First, let's create a module that will attach telemetry handlers to LiveView events. This mirrors the Appsignal.Phoenix.LiveView module:

defmodule YourApp.Appsignal.LiveView do
  @tracer Application.compile_env(:appsignal, :appsignal_tracer, Appsignal.Tracer)
  @span Application.compile_env(:appsignal, :appsignal_span, Appsignal.Span)

  def attach do
    Enum.each(
      [
        [:phoenix, :live_view, :mount],
        [:phoenix, :live_view, :handle_params],
        [:phoenix, :live_view, :handle_event],
        [:phoenix, :live_view, :render],
        [:phoenix, :live_component, :handle_event],
        [:phoenix, :live_component, :update]
      ],
      fn event ->
        name = Enum.join(event, ".")

        :telemetry.attach(
          {__MODULE__, event ++ [:start]},
          event ++ [:start],
          &__MODULE__.handle_event_start/4,
          name
        )
      end
    )
  end
end

Adding Custom Data to Spans

We'll create a handler that adds user and path information to the AppSignal span:

def handle_event_start([:phoenix, _type, _name, :start], _, metadata, _event_name) do
  assigns = if metadata[:socket], do: metadata.socket.assigns

  @tracer.current_span()
  |> @span.set_sample_data("environment", %{path_info: assigns[:active_path]})
  |> @span.set_sample_data("custom_data", %{
    user_id: assigns[:user] && assigns.user.id,
    tenant_id: assigns[:tenant] && assigns.tenant.id
  })
end

Creating a LiveView Path Tracker Hook

We can use LiveView's attach_hook to track the current path in handle_params. Create a LiveView hook module:

defmodule YourAppWeb.Hooks.Assigns do
  import Phoenix.Component
  import Phoenix.LiveView

  def on_mount(:default, _params, _session, socket) do
    socket =
      socket
      |> attach_hook(:active_path, :handle_params, fn _params, uri, socket ->
        {:cont, assign(socket, :active_path, URI.parse(uri).path)}
      end)

    {:cont, socket}
  end
end

Then use it in your router:

live_session :default do
  on_mount YourAppWeb.Hooks.Assigns

  # your live routes...
end

Preparing the LiveView Data

In your LiveView, assign user information during mount:

defmodule YourApp.SomeLiveView do
  use Phoenix.LiveView

  def mount(_params, session, socket) do
    socket =
      socket
      |> assign(:user, get_user_from_session(session))
      |> assign(:tenant, get_tenant_from_session(session))

    {:ok, socket}
  end
end

Initializing the Extension

Finally, start the telemetry handlers in your application:

# In your application.ex
def start(_type, _args) do
  Appsignal.Phoenix.LiveView.attach()
  YourApp.Appsignal.LiveView.attach()
  Extensions.Appsignal.LiveView.attach()
  # ...

What Does This Give Us?

Now, when errors occur in your LiveView application, AppSignal errors will include:

  • The current user's ID
  • The active tenant ID (if applicable)
  • The current path (updated automatically during navigation)
  • The event parameters (included by default by AppSignal)

This additional context makes it much easier to debug issues and understand the state of your application when errors occur.

Remember that you can add any other relevant data to the spans by modifying the handle_event_start/4 function. Common additions might include:

  • Current locale
  • User roles
  • Feature flags
  • Application state

Conclusion

By enriching AppSignal spans with additional context and implementing automatic path tracking, we've made our error tracking more powerful and useful. This approach helps us quickly identify and fix issues in our LiveView applications by providing the context we need right where we need it.

When you encounter an error in AppSignal, you'll now be able to see not just what went wrong, but also:

  • Who experienced the error (user context)
  • Where exactly they were in the application (current path)
  • What context they were working in (tenant)
  • What parameters were passed to the event (default AppSignal behavior)

Remember to only include non-sensitive information in your error tracking, and be mindful of data privacy considerations when deciding what information to track.

The combination of telemetry handlers and LiveView hooks provides a robust solution for comprehensive error tracking that will make debugging and issue resolution much more efficient.