StatetraceElixir

Elixir integration for https://www.statetrace.com

Installation

If available in Hex, the package can be installed by adding statetrace_elixir to your list of dependencies in mix.exs:

def deps do
  [
    {:statetrace_elixir, "~> 0.1.0"}
  ]
end

After the packages are installed you must create a database migration to add the statetrace_annotations table to your database:

mix ecto.gen.migration add_statetrace_annotations_table

Open the generated migration in your editor and call the up and down functions on StatetraceElixir.Migrations:

defmodule MyApp.Repo.Migrations.AddStatetraceAnnotationsTable do
  use Ecto.Migration

  def up do
    StatetraceElixir.Migrations.up()
  end

  # We specify `version: 1` in `down`, ensuring that we'll roll all the way back down if
  # necessary, regardless of which version we've migrated `up` to.
  def down do
    StatetraceElixir.Migrations.down(version: 1)
  end
end

New versions may require additional migrations, however, migrations will never change between versions and they are always idempotent.

Now, run the migration to create the table:

mix ecto.migrate

Next we need to annotate your http requests' transactions. Wrap &action/2 in your controllers in a transaction and annotate it.

Its easiest to add this to <YourProject>Web

defmodule <YourProject>Web.StatetraceConfig do
  @behaviour StatetraceElixir.Config

  import Plug.Conn
  import Phoenix.Controller
  alias StatetraceElixir.Annotations.ActionAnnotation
  alias StatetraceElixir.Annotations.SessionAnnotation

  @app_id "<your_project>"

  @impl
  def repo do
    <YourProject>.Repo
  end

  @impl
  def make_session_annotation(conn) do
    current_user = conn.assigns.current_user

    case current_user do
      nil ->
        %SessionAnnotation{}

      %{id: id, full_name: full_name, avatar: avatar} ->
        %SessionAnnotation{
          actor_id: id,
          actor_full_name: full_name,
          actor_avatar: avatar,
          client_user_agent: List.first(get_req_header(conn, "user-agent")),
          application_id: @app_id
        }
    end
  end

  @impl
  def make_action_annotation(conn) do
    %ActionAnnotation{
      method: conn.method,
      url: current_url(conn),
      version: get_version()
    }
  end

  defp get_version do
    {:ok, version} = :application.get_key(:<your_project>, :vsn)
    "#{version}"
  end
end

...

defmodule <YourProject>Web do
  def controller do
    quote do
      use Phoenix.Controller, namespace: <YourProject>Web

      import Plug.Conn
      import <YourProject>Web.Gettext
      alias <YourProject>Web.Router.Helpers, as: Routes
      alias StatetraceElixir.Annotations

      def action(conn, _) do
        args = [conn, conn.params]

        with {_, response} <-
               <YourProject>.Repo.transaction(fn ->
                 Annotations.process_conn(conn,
                   <YourProject>Web.StatetraceConfig
                 )

                 apply(__MODULE__, action_name(conn), args)
               end) do
          response
        end
      end
    end
  end
end

Documentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/statetrace_elixir.