Current section
Files
Jump to
Current section
Files
lib/scout_apm/instruments/ecto.ex
defmodule ScoutApm.Instruments.Ecto do
alias ScoutApm.Internal.Layer
defmacro __using__(_args) do
quote unquote: false do
contents = quote do
# A handful of things that shouldn't be instrumented, so are passed straight through.
def config(), do: unquote(__MODULE__).config()
def start_link(opts \\ []), do: unquote(__MODULE__).start_link(opts)
def stop(pid, timeout \\ 5000), do: unquote(__MODULE__).stop(pid, timeout)
def rollback(a), do: unquote(__MODULE__).rollback(a)
def in_transaction?(), do: unquote(__MODULE__).in_transaction?()
def __adapter__(), do: unquote(__MODULE__).__adapter__()
def __query_cache__(), do: unquote(__MODULE__).__query_cache__()
def __repo__(), do: unquote(__MODULE__).__repo__()
def __pool__(), do: unquote(__MODULE__).__pool__()
def log(a), do: unquote(__MODULE__).log(a)
def load(a, b), do: unquote(__MODULE__).load(a, b)
# The functions to wrap with tracing
def aggregate(a,b,c) do
__trace(fn -> unquote(__MODULE__).aggregate(a,b,c) end)
end
def aggregate(a,b,c,d) do
__trace(fn -> unquote(__MODULE__).aggregate(a,b,c,d) end)
end
def all(a) do
__trace(fn -> unquote(__MODULE__).all(a) end)
end
def all(a,b) do
__trace(fn -> unquote(__MODULE__).all(a,b) end)
end
def delete!(a) do
__trace(fn -> unquote(__MODULE__).delete!(a) end)
end
def delete!(a,b) do
__trace(fn -> unquote(__MODULE__).delete!(a, b) end)
end
def delete(a) do
__trace(fn -> unquote(__MODULE__).delete(a) end)
end
def delete(a,b) do
__trace(fn -> unquote(__MODULE__).delete(a,b) end)
end
def delete_all(a) do
__trace(fn -> unquote(__MODULE__).delete_all(a) end)
end
def delete_all(a,b) do
__trace(fn -> unquote(__MODULE__).delete_all(a,b) end)
end
def get!(a,b) do
__trace(fn -> unquote(__MODULE__).get!(a,b) end)
end
def get!(a,b,c) do
__trace(fn -> unquote(__MODULE__).get!(a,b,c) end)
end
def get(a,b) do
__trace(fn -> unquote(__MODULE__).get(a,b) end)
end
def get(a,b,c) do
__trace(fn -> unquote(__MODULE__).get(a,b,c) end)
end
def get_by!(a,b) do
__trace(fn -> unquote(__MODULE__).get_by!(a,b) end)
end
def get_by!(a,b,c) do
__trace(fn -> unquote(__MODULE__).get_by!(a,b,c) end)
end
def get_by(a,b) do
__trace(fn -> unquote(__MODULE__).get_by(a,b) end)
end
def get_by(a,b,c) do
__trace(fn -> unquote(__MODULE__).get_by(a,b,c) end)
end
def insert!(a) do
__trace(fn -> unquote(__MODULE__).insert!(a) end)
end
def insert!(a,b) do
__trace(fn -> unquote(__MODULE__).insert!(a,b) end)
end
def insert(a) do
__trace(fn -> unquote(__MODULE__).insert(a) end)
end
def insert(a,b) do
__trace(fn -> unquote(__MODULE__).insert(a,b) end)
end
def insert_all(a,b) do
__trace(fn -> unquote(__MODULE__).insert_all(a,b) end)
end
def insert_all(a,b,c) do
__trace(fn -> unquote(__MODULE__).insert_all(a,b,c) end)
end
def insert_or_update!(a) do
__trace(fn -> unquote(__MODULE__).insert_or_update!(a) end)
end
def insert_or_update!(a,b) do
__trace(fn -> unquote(__MODULE__).insert_or_update!(a,b) end)
end
def insert_or_update(a) do
__trace(fn -> unquote(__MODULE__).insert_or_update(a) end)
end
def insert_or_update(a,b) do
__trace(fn -> unquote(__MODULE__).insert_or_update(a,b) end)
end
def one!(a) do
__trace(fn -> unquote(__MODULE__).one!(a) end)
end
def one!(a,b) do
__trace(fn -> unquote(__MODULE__).one!(a,b) end)
end
def one(a) do
__trace(fn -> unquote(__MODULE__).one(a) end)
end
def one(a,b) do
__trace(fn -> unquote(__MODULE__).one(a,b) end)
end
def preload(a,b) do
__trace(fn -> unquote(__MODULE__).preload(a,b) end)
end
def preload(a,b,c) do
__trace(fn -> unquote(__MODULE__).preload(a,b,c) end)
end
def query!(a) do
__trace(fn -> unquote(__MODULE__).query!(a) end)
end
def query!(a,b) do
__trace(fn -> unquote(__MODULE__).query!(a,b) end)
end
def query!(a,b,c) do
__trace(fn -> unquote(__MODULE__).query!(a,b,c) end)
end
def query(a) do
__trace(fn -> unquote(__MODULE__).query(a) end)
end
def query(a,b) do
__trace(fn -> unquote(__MODULE__).query(a,b) end)
end
def query(a,b,c) do
__trace(fn -> unquote(__MODULE__).query(a,b,c) end)
end
def stream(a) do
__trace(fn -> unquote(__MODULE__).stream(a) end)
end
def stream(a,b) do
__trace(fn -> unquote(__MODULE__).stream(a,b) end)
end
def transaction(a) do
__trace(fn -> unquote(__MODULE__).transaction(a) end)
end
def transaction(a,b) do
__trace(fn -> unquote(__MODULE__).transaction(a,b) end)
end
def update!(a) do
__trace(fn -> unquote(__MODULE__).update!(a) end)
end
def update!(a,b) do
__trace(fn -> unquote(__MODULE__).update!(a,b) end)
end
def update(a) do
__trace(fn -> unquote(__MODULE__).update(a) end)
end
def update(a,b) do
__trace(fn -> unquote(__MODULE__).update(a,b) end)
end
def update_all(a,b) do
__trace(fn -> unquote(__MODULE__).update_all(a,b) end)
end
def update_all(a,b,c) do
__trace(fn -> unquote(__MODULE__).update_all(a,b,c) end)
end
defp __trace(f) do
ScoutApm.TrackedRequest.start_layer("Ecto", nil)
ecto_result = f.()
log_entry = Process.get(:ecto_log_entry)
ScoutApm.TrackedRequest.stop_layer(
ScoutApm.Instruments.Ecto.name_query(log_entry),
fn layer -> ScoutApm.Instruments.Ecto.annotate_layer_callback(layer, log_entry) end)
ecto_result
end
end
scout_repo_module =
__MODULE__
|> Atom.to_string
|> (Kernel.<> ".ScoutApm")
|> String.to_atom
Module.create(scout_repo_module, contents, __ENV__)
end
end
def query_time(entry) do
raw_time = entry.query_time
System.convert_time_unit(raw_time, :native, :microsecond) / 1_000_000
end
def name_query(entry) do
case entry do
nil -> "Unknown"
_ ->
command = case entry.result do
{:ok, result} -> result.command
_ -> "Unknown"
end
table = entry.source
"#{command}##{table}"
end
end
def num_rows(entry) do
case entry.result do
{:ok, result} -> result.num_rows
_ -> 0
end
end
def annotate_layer_callback(layer, ecto_log) do
backtrace = case Process.info(self(), :current_stacktrace) do
{:current_stacktrace, stack} ->
Enum.drop(stack, 4)
_ ->
nil
end
layer
|> Layer.update_desc(ecto_log.query)
|> Layer.update_backtrace(backtrace)
end
end