Tracer - Elixir Tracing Framework

Build Status

Tracer is a tracing framework for elixir which features an easy to use high level interface, extensibility and safety for using in production.


If you need to integrate Tracer to your project, then you can install it from Hex, by adding tracer to your list of dependencies in mix.exs:

def deps do
  [{:tracer, "~> 0.1.1"}]

To use Tracer from the cli, then download it directly from GitHub.

When firing iex you might want to specify the node name so that you can trace other nodes remotely. Then enter the use Tracer command to be able to use its functions as commands without the Tracer prefix.

$ git clone
$ cd tracer

$ mix deps.get
$ iex --name tracer@ -S mix
Erlang/OTP 19 [erts-8.0] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Interactive Elixir (1.5.1) - press Ctrl+C to exit (type h() ENTER for help)
iex(tracer@> use Tracer
iex(tracer@> run Count, node: :"phoenix@", ...


Tools are tracing components that focus on a specific tracing aspect. They are implemented as Elixir modules so you can create your own tools.

Tracer currently provides the following tools:

  • The Count tool counts events.
  • The Duration tool measures how long it takes to execute a function.
  • The CallSeq - ‘Call Sequence’ tool displays function call sequences.
  • The FlameGraph tool which aggregates stack frames over a flame graph.
  • The Display tool displays standard tracing events.

Count Tool Example

iex(2)> run Count, process: self(), match: global String.split(string, pattern)
started tracing
iex(4)> String.split("Hello World", " ")
["Hello", "World"]
iex(5)> String.split("Hello World", " ")
["Hello", "World"]
iex(6)> String.split("Hello World", "o")
["Hell", " W", "rld"]
iex(7)> String.split("Hello", "o")
["Hell", ""]
iex(8)> done tracing: tracing_timeout 30000
        1              [string:"Hello World", pattern:"o"]
        1              [string:"Hello"      , pattern:"o" ]
        2              [string:"Hello World", pattern:" "]

Duration Tool Example

iex(1)> run Duration, match: global
started tracing
iex(2)>{a: 1})                          
        4                    '#PID<0.151.0>' [param: %{a: 1}]
%{a: 1}
iex(3)>{b: 2})                          
        3                    '#PID<0.151.0>' [param: %{b: 2}]
%{b: 2}
iex(4)>{c: [1, 2,3]})                   
        6                    '#PID<0.151.0>' [param: %{c: [1, 2, 3]}]
%{c: [1, 2, 3]}
iex(5)> stop
done tracing: :stop_command

Use aggregation option to collect all the duration samples and return you a combined result. aggregation: option can be one of :sum, :avg, :min, :max, :dist

Call Sequence Tool Example

iex(1)> run CallSeq, show_args: true, show_return: true, start_match: &Map.drop/2,
                      max_message_count: 10000, max_queue_size: 10000
started tracing
iex(2)> Map.drop(%{a: 1, b: 2, c: 3}, [:a, :b])
%{c: 3}                                
iex(3)> stop
done tracing: :stop_command

-> Map.drop/2             [[%{a: 1, b: 2, c: 3}, [:a, :b]]]
 -> Enum.to_list/1        [[[:a, :b]]]
 <- Enum.to_list/1        [:a, :b]
 -> Map.drop_list/2       [[[:a, :b], %{a: 1, b: 2, c: 3}]]
  -> :maps.remove/2       [[:a, %{a: 1, b: 2, c: 3}]]
  <- :maps.remove/2       %{b: 2, c: 3}
  -> Map.drop_list/2      [[[:b], %{b: 2, c: 3}]]
   -> :maps.remove/2      [[:b, %{b: 2, c: 3}]]
   <- :maps.remove/2      %{c: 3}
   -> Map.drop_list/2     [[[], %{c: 3}]]
   <- Map.drop_list/2     %{c: 3}
  <- Map.drop/2           %{c: 3}
  -> :erl_eval.ret_expr/3 [[%{c: 3}, [], :none]]
  <- :erl_eval.ret_expr/3 {:value, %{c: 3}, []}
 <- :erl_eval.do_apply/6  {:value, %{c: 3}, []}
<- :erl_eval.expr/5       {:value, %{c: 3}, []}

Flame Graph Tool Example

iex(17)> run FlameGraph, node: :"phoenix@", process: SampleApp.Endpoint,
        max_message_count: 10000, max_queue_size: 10000, file_name: "phoenix.svg",
        ignore: "sleep", resolution: 10, max_depth: 100
started tracing
iex(18)> stop
done tracing: :stop_command

Click here (not image) for interactive SVG Flame Graph


Building your own Tool

Tools have a similar structure like GenServers.

defmodule MyTool do
  alias __MODULE__
  alias Tracer.Probe
  use Tracer.Tool

  # store your tool's state
  defstruct []

  def init(opts) do
    # init_tool initializes the tool
    init_state = init_tool(%MyTool{}, opts, [:match])

    case Keyword.get(opts, :match) do
      nil -> init_state
      matcher ->
        type = Keyword.get(opts, :type, :call)
        probe = type,
                          process: get_process,
                          match: matcher)
        set_probes(init_state, [probe])

  # Called when the tool run starts
  def handle_start(event, state) do

  # Called when a trace event triggers
  def handle_event(event, state) do
    # report event will call to_string(event) to format
    # your event, so you can create your own events
    report_event(state, event)

  # Called when the tool run completes
  def handle_end(event, state) do