Packages
appsignal
2.13.2
2.17.4
2.17.3
2.17.2
2.17.1
2.17.0
2.16.0
2.15.11
2.15.10
2.15.9
2.15.8
2.15.7
2.15.6
2.15.5
2.15.4
2.15.3
2.15.2
2.15.1
2.15.0
2.14.1
2.14.0
2.13.4
2.13.3
2.13.2
2.13.1
2.13.0
2.12.3
2.12.2
2.12.1
2.12.0
2.11.0
2.10.2
2.10.1
2.10.0
2.9.2
2.9.1
2.9.0
2.8.3
2.8.2
2.8.1
2.8.0
2.7.13
2.7.12
2.7.11
2.7.10
2.7.9
2.7.8
2.7.7
2.7.6
2.7.5
2.7.4
2.7.3
2.7.2
2.7.1
2.7.0
2.7.0-beta.1
2.6.1
2.6.0
2.5.3
2.5.2
2.5.1
2.5.0
2.4.3
2.4.2
2.4.1
2.4.0
2.3.1
2.3.0
2.2.19
2.2.18
2.2.17
2.2.16
2.2.15
2.2.14
2.2.13
2.2.12
retired
2.2.11
2.2.10
2.2.9
2.2.8
2.2.7
2.2.6
2.2.5
2.2.4
2.2.3
2.2.2
2.2.1
2.2.0
2.1.15
2.1.14
2.1.13
2.1.12
2.1.11
2.1.10
2.1.9
2.1.8
2.1.7
2.1.7-beta.1
2.1.6
2.1.5
2.1.4
2.1.3
2.1.2
2.1.1
2.1.0
2.0.8
2.0.7
2.0.6
2.0.6-beta.1
2.0.5
retired
2.0.4
retired
2.0.3
retired
2.0.2
retired
2.0.1
retired
2.0.0
retired
2.0.0-beta.11
2.0.0-beta.10
2.0.0-beta.9
2.0.0-beta.8
2.0.0-beta.7
2.0.0-beta.6
2.0.0-beta.5
2.0.0-beta.4
2.0.0-beta.3
2.0.0-beta.2
2.0.0-beta.1
2.0.0-alpha.7
2.0.0-alpha.6
2.0.0-alpha.5
2.0.0-alpha.4
2.0.0-alpha.3
2.0.0-alpha.2
2.0.0-alpha.1
1.13.5
1.13.4
1.13.3
1.13.2
1.13.1
1.13.0
1.13.0-beta.2
1.13.0-beta.1
1.12.1
1.12.0
1.12.0-beta.3
1.12.0-beta.2
1.12.0-beta.1
1.11.8
1.11.7
1.11.6
1.11.5
1.11.4
1.11.3
1.11.2
1.11.1
1.11.0
1.11.0-beta.1
1.10.13
1.10.12
1.10.11
1.10.10
1.10.9
1.10.8
1.10.7
1.10.6
1.10.5
1.10.4
1.10.3
1.10.2
1.10.1
1.10.0
1.9.4
1.9.3
1.9.2
1.9.1
1.9.1-beta.1
1.9.0
1.9.0-beta.1
1.9.0-alpha.1
1.8.2
1.8.1
1.8.1-beta.2
1.8.1-beta.1
1.8.0
1.7.2
1.7.1
1.7.0
1.7.0-beta.2
1.7.0-beta.1
1.7.0-alpha.4
1.7.0-alpha.3
1.7.0-alpha.2
1.7.0-alpha.1
1.6.7
1.6.6
1.6.6-beta.2
1.6.6-beta.1
1.6.5
1.6.4
1.6.3
1.6.2
1.6.1
1.6.0
1.6.0-beta.1
1.6.0-alpha.1
1.5.1
1.5.0
1.5.0-beta.2
1.5.0-beta.1
1.4.11-beta.1
1.4.10
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.4.0-alpha.1
1.3.6
1.3.6-beta.1
1.3.5
1.3.4
1.3.4-beta.1
1.3.3
1.3.2
1.3.1
1.3.0
1.3.0-beta.3
1.3.0-beta.2
1.3.0-beta.1
1.2.3
1.2.2
1.2.1
1.2.0
1.1.1
1.1.0
1.0.4
1.0.3
1.0.2
1.0.1
1.0.0
1.0.0-rc.1
0.13.0
0.12.3
0.12.2
0.12.1
0.12.0
0.11.6
0.11.5
0.11.4
0.11.3
0.11.2
0.11.1
0.11.0
0.10.0
0.9.2
0.9.1
0.9.0
0.8.0
0.7.1
0.7.0
0.6.2
0.6.1
0.6.0
0.5.0
0.4.0
0.3.0
0.2.0
0.1.0
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.4
0.0.3
0.0.2
0.0.1
Collects error and performance data from your Elixir applications and sends it to AppSignal
Current section
Files
Jump to
Current section
Files
lib/appsignal/ecto.ex
defmodule Appsignal.Ecto do
require Logger
@tracer Application.compile_env(:appsignal, :appsignal_tracer, Appsignal.Tracer)
@span Application.compile_env(:appsignal, :appsignal_span, Appsignal.Span)
import Appsignal.Utils, only: [module_name: 1]
@doc """
Attaches `Appsignal.Ecto` to the Ecto telemetry channel configured in the
application's configuration.
"""
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
@doc """
Attaches `Appsignal.Ecto` to the Ecto telemetry channel based on the passed
`otp_app` and `repo`.
"""
def attach(otp_app, repo) do
event = telemetry_prefix(otp_app, repo) ++ [:query]
case :telemetry.attach({__MODULE__, event}, event, &__MODULE__.handle_event/4, :ok) do
:ok ->
Appsignal.IntegrationLogger.debug("Appsignal.Ecto attached to #{inspect(event)}")
:ok
{:error, _} = error ->
Logger.warning("Appsignal.Ecto not attached to #{inspect(event)}: #{inspect(error)}")
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
@doc false
def handle_event(
_event,
%{total_time: total_time},
%{repo: repo, query: query} = metadata,
_config
) do
do_handle_event(current_span(metadata), total_time, repo, query)
end
def handle_event(_event, _measurements, _metadata, _config), do: :ok
defp current_span(metadata) do
# If a current span is already set in this process, use that instead
# of the current span passed as part of the Ecto metadata.
# This fixes an issue where, when a transaction takes place in a
# parallel preload process, `handle_commit/3` and `handle_rollback/3`
# would not close the span created by `handle_begin/3`, but the span's
# parent.
@tracer.current_span() || metadata[:options][:_appsignal_current_span]
end
defp do_handle_event(nil, _total_time, _repo, _query), do: nil
defp do_handle_event(current_span, total_time, repo, query) do
case query do
"begin" -> handle_begin(current_span, total_time, repo)
"commit" -> handle_commit(current_span, total_time, repo)
"rollback" -> handle_rollback(current_span, total_time, repo)
_ -> handle_query(current_span, total_time, repo, query)
end
end
defp handle_begin(current_span, total_time, repo) do
time = :os.system_time()
# Intentionally leave span open to be closed
# by `handle_commit/3` or `handle_rollback/3`
"http_request"
|> @tracer.create_span(current_span, start_time: time - total_time)
|> @span.set_name("Transaction #{module_name(repo)}")
|> @span.set_attribute("appsignal:category", "transaction.ecto")
end
defp handle_commit(current_span, total_time, repo) do
time = :os.system_time()
"http_request"
|> @tracer.create_span(current_span, start_time: time - total_time)
|> @span.set_name("Commit #{module_name(repo)}")
|> @span.set_attribute("appsignal:category", "commit.ecto")
|> @tracer.close_span(end_time: time)
# Close span created by `handle_begin/3`
@tracer.close_span(current_span, end_time: time)
end
defp handle_rollback(current_span, total_time, repo) do
time = :os.system_time()
"http_request"
|> @tracer.create_span(current_span, start_time: time - total_time)
|> @span.set_name("Rollback #{module_name(repo)}")
|> @span.set_attribute("appsignal:category", "rollback.ecto")
|> @tracer.close_span(end_time: time)
# Close span created by `handle_begin/3`
@tracer.close_span(current_span, end_time: time)
end
defp handle_query(current_span, total_time, repo, query) do
time = :os.system_time()
"http_request"
|> @tracer.create_span(current_span, start_time: time - total_time)
|> @span.set_name("Query #{module_name(repo)}")
|> @span.set_attribute("appsignal:category", "query.ecto")
|> @span.set_sql(query)
|> @tracer.close_span(end_time: time)
end
end