Packages
appsignal
2.8.3
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())
unless ignored?(pid) do
namespace
|> Span.create_root(pid, options[:start_time])
|> register()
|> on_create_span()
end
end
def create_span(_namespace, parent, options) do
pid = Keyword.get(options, :pid, self())
unless ignored?(pid) do
parent
|> Span.create_child(pid, options[:start_time])
|> register()
|> on_create_span()
end
end
@doc """
Finds the span in the registry table.
"""
@spec lookup(pid()) :: list() | []
def lookup(pid) do
try do
:ets.lookup(@table, pid)
rescue
ArgumentError -> []
end
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
span
|> Span.close()
|> deregister()
: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
span
|> Span.close(end_time)
|> deregister()
:ok
end
def close_span(nil, _options), do: nil
@doc """
Ignores the given process.
"""
@spec ignore(pid()) :: :ok
def ignore(pid) do
delete(pid)
insert({pid, :ignore}) && @monitor.add()
:ok
end
@doc """
Ignores the current process.
"""
@spec ignore() :: :ok
def ignore do
self() |> ignore()
end
@doc """
Removes the process' spans from the registry.
"""
@spec delete(pid()) :: :ok
def delete(pid) do
try do
:ets.delete(@table, pid)
rescue
ArgumentError -> :ok
end
:ok
end
@doc false
def register_current(span) do
# Registers a span as the current span for this process.
#
# This is necessary when you want to instrument asynchronous work.
#
# parent = Appsignal.Tracer.current_span()
#
# list
# |> Task.async_stream(fn item ->
# Appsignal.Tracer.register_current(parent)
# # ...
# end)
# |> Stream.run()
register(%{span | pid: self()})
end
defp register(%Span{pid: pid} = span) do
if insert({pid, span}) do
@monitor.add()
span
end
end
defp register(nil), do: nil
defp deregister(%Span{pid: pid} = span) do
try do
:ets.delete_object(@table, {pid, span})
rescue
ArgumentError -> false
end
end
defp ignored?(pid) when is_pid(pid) do
pid
|> lookup()
|> ignored?()
end
defp ignored?([{_pid, :ignore}]), do: true
defp ignored?(_), do: false
defp insert(span) do
try do
:ets.insert(@table, span)
rescue
ArgumentError -> nil
end
end
@spec on_create_span(Span.t() | nil) :: Span.t() | nil
defp on_create_span(span) do
custom_on_create_fun =
Application.get_env(:appsignal, :custom_on_create_fun, &__MODULE__.custom_on_create_fun/1)
custom_on_create_fun.(span)
span
end
@doc """
This function can be defined by the user and will be executed on the
creation of the span after create_span/3 is executed. It can be used to add
custom_data to the span.
Example in your own application:
```ex
defmodule MyApp.Appsignal do
def custom_on_create_fun(span) do
Appsignal.Span.set_sample_data(span, "custom_data", %{"foo": "bar"})
end
end
```
This can be added to the config with:
```ex
config :appsignal, custom_on_create_fun: &MyApp.Appsignal.custom_on_create_fun/1
```
"""
@spec custom_on_create_fun(Span.t() | nil) :: any()
def custom_on_create_fun(_span) do
nil
end
end