Packages
appsignal
2.7.1
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/tracer.ex
defmodule Appsignal.Tracer do
alias Appsignal.Span
require Appsignal.Utils
@monitor Appsignal.Utils.compile_env(:appsignal, :appsignal_monitor, Appsignal.Monitor)
@table :"$appsignal_registry"
@type option :: {:pid, pid} | {:start_time, integer}
@type options :: [option]
@doc false
def start_link do
Agent.start_link(fn -> :ets.new(@table, [:named_table, :public, :duplicate_bag]) end,
name: __MODULE__
)
end
@doc """
Creates a new root span.
## Example
Appsignal.Tracer.create_span("http_request")
"""
@spec create_span(String.t()) :: Span.t() | nil
def create_span(namespace), do: create_span(namespace, nil, [])
@doc """
Creates a new child span.
## Example
parent = Appsignal.Tracer.current_span()
Appsignal.Tracer.create_span("http_request", parent)
"""
@spec create_span(String.t(), Span.t() | nil) :: Span.t() | nil
def create_span(namespace, parent), do: create_span(namespace, parent, [])
@doc """
Creates a new span, with an optional parent or pid.
## Example
parent = Appsignal.Tracer.current_span()
Appsignal.Tracer.create_span("http_request", parent, [start_time: :os.system_time(), pid: self()])
"""
@spec create_span(String.t(), Span.t() | nil, options) :: Span.t() | nil
def create_span(namespace, nil, options) do
pid = Keyword.get(options, :pid, self())
if running?() && !ignored?(pid) do
span =
case Keyword.get(options, :start_time) do
nil -> Span.create_root(namespace, pid)
timestamp -> Span.create_root(namespace, pid, timestamp)
end
register(span)
end
end
def create_span(_namespace, parent, options) do
pid = Keyword.get(options, :pid, self())
if running?() && !ignored?(pid) do
span =
case Keyword.get(options, :start_time) do
nil -> Span.create_child(parent, pid)
timestamp -> Span.create_child(parent, pid, timestamp)
end
register(span)
end
end
@doc """
Finds the span in the registry table.
"""
@spec lookup(pid()) :: list()
def lookup(pid) do
if running?(), do: :ets.lookup(@table, pid)
end
@doc """
Returns the current span in the current process.
"""
@spec current_span() :: Span.t() | nil
def current_span, do: current_span(self())
@doc """
Returns the current span in the passed pid's process.
"""
@spec current_span(pid()) :: Span.t() | nil
def current_span(pid) do
pid
|> lookup()
|> current()
end
@doc """
Returns the root span in the current process.
"""
@spec root_span() :: Span.t() | nil
def root_span, do: root_span(self())
@doc """
Returns the root span in the passed pid's process.
"""
@spec root_span(pid()) :: Span.t() | nil
def root_span(pid) do
pid
|> lookup()
|> root()
end
@doc false
def child_spec(_) do
%{
id: Appsignal.Tracer,
start: {Appsignal.Tracer, :start_link, []}
}
end
defp current({_pid, :ignore}), do: nil
defp current({_pid, span}), do: span
defp current(spans) when is_list(spans) do
spans
|> List.last()
|> current()
end
defp current(_), do: nil
defp root([{_pid, %Span{} = root} | _]), do: root
defp root(_), do: nil
@spec close_span(Span.t() | nil) :: :ok | nil
@doc """
Closes a span and deregisters it.
## Example
Appsignal.Tracer.current_span()
|> Appsignal.Tracer.close_span()
"""
def close_span(%Span{} = span) do
if running?() do
span
|> Span.close()
|> deregister()
end
:ok
end
def close_span(nil), do: nil
@spec close_span(Span.t() | nil, list()) :: :ok | nil
@doc """
Closes a span and deregisters it. Takes an options list, which currently only
accepts a `List` with an `:end_time` integer.
## Example
Appsignal.Tracer.current_span()
|> Appsignal.Tracer.close_span(end_time: :os.system_time())
"""
def close_span(span, options)
def close_span(%Span{} = span, end_time: end_time) do
if running?() do
span
|> Span.close(end_time)
|> deregister()
end
:ok
end
def close_span(nil, _options), do: nil
@doc """
Ignores the given process.
"""
@spec ignore(pid()) :: :ok
def ignore(pid) do
if running?() do
delete(pid)
:ets.insert(@table, {pid, :ignore})
@monitor.add()
end
:ok
end
@doc """
Ignores the current process.
"""
@spec ignore() :: :ok | nil
def ignore do
self() |> ignore()
end
@doc """
Removes the process' spans from the registry.
"""
@spec delete(pid()) :: :ok
def delete(pid) do
if running?(), do: :ets.delete(@table, pid)
:ok
end
defp register(%Span{pid: pid} = span) do
:ets.insert(@table, {pid, span})
@monitor.add()
span
end
defp register(nil), do: nil
defp deregister(%Span{pid: pid} = span) do
:ets.delete_object(@table, {pid, span})
end
defp ignored?(pid) when is_pid(pid) do
pid
|> lookup()
|> ignored?()
end
defp ignored?([{_pid, :ignore}]), do: true
defp ignored?(_), do: false
defp running? do
is_pid(Process.whereis(__MODULE__))
end
end