Skip to content

Instantly share code, notes, and snippets.

@arnodirlam
Last active December 10, 2020 22:11
Show Gist options
  • Save arnodirlam/e00e2e8a826afb85651c0d8b9f4a44ad to your computer and use it in GitHub Desktop.
Save arnodirlam/e00e2e8a826afb85651c0d8b9f4a44ad to your computer and use it in GitHub Desktop.
AppSignal: Trace spawned Ecto processes (f.ex. preload with in_parallel: true)
defmodule MyApp.Application do
use Application
def start(_type, _args) do
MyApp.Appsignal.EctoParentSpan.attach()
children = [
# ...
]
# ...
end
end
defmodule MyApp.Appsignal.EctoParentSpan do
@moduledoc """
Augments `Ecto` metrics tracked by `Appsignal.Ecto` with the pid of the parent process
in cases where processes are spawned for concurrency, f.ex. when using `preload` with
`in_parallel: true` (which is the default).
"""
@tracer Application.get_env(:appsignal, :appsignal_tracer, Appsignal.Tracer)
@monitor Application.get_env(:appsignal, :appsignal_monitor, Appsignal.Monitor)
@table :"$appsignal_registry"
def attach do
otp_app =
:appsignal
|> Application.get_env(:config, %{})
|> Map.get(:otp_app, nil)
otp_app
|> repos()
|> Enum.each(&attach(otp_app, &1))
end
def attach(otp_app, repo) do
event = telemetry_prefix(otp_app, repo) ++ [:query]
case :telemetry.attach({__MODULE__, event}, event, &handle_event/4, :ok) do
:ok ->
Logger.debug("#{__MODULE__} attached to #{inspect(event)}")
{:error, _} = error ->
Logger.warn("#{__MODULE__} not attached to #{inspect(event)}: #{inspect(error)}")
end
end
defp repos(otp_app) do
Application.get_env(:appsignal, :config, %{})[:ecto_repos] ||
Application.get_env(otp_app, :ecto_repos) ||
[]
end
defp telemetry_prefix(otp_app, repo) do
case otp_app
|> Application.get_env(repo, [])
|> Keyword.get(:telemetry_prefix) do
prefix when is_list(prefix) ->
prefix
_ ->
repo
|> Module.split()
|> Enum.map(&(&1 |> Macro.underscore() |> String.to_atom()))
end
end
def handle_event(_event, _measurements, %{options: %{appsignal_span: parent_span}}, _config) do
unless @tracer.current_span() do
:ets.insert(@table, {self(), parent_span})
@monitor.add()
end
:ok
end
def handle_event(_event, _measurements, _metadata, _config), do: :ok
end
defmodule MyApp.Repo do
use Ecto.Repo,
otp_app: :my_app,
adapter: Ecto.Adapters.Postgres
@tracer Application.get_env(:appsignal, :appsignal_tracer, Appsignal.Tracer)
def default_options(_operation) do
[telemetry_options: %{appsignal_span: @tracer.current_span()}]
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment