Skip to content


Switch branches/tags

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time


Plug integration for the Tapper Zipkin client.

Hex pm Inline docs Build Status


Add plugs to your pipeline to add Tapper tracing to each request, e.g. in your Phoenix Endpoint:

plug Tapper.Plug.Filter, prefixes: ["__gtg", "foo/bar"]  ## (1) ignored URL prefixes

plug Tapper.Plug.Trace  ## (2) intercept incoming B3 headers, start trace

# other plugs
plug Plug.RequestId

plug Myapp.Web.Router  # standard Phoenix router etc.
  1. you can exclude certain URLs for the purposes of tracing using the optional Tapper.Plug.Filter.
  2. install the Tapper.Plug.Trace plug as soon as possible in the plug list, for timing accuracy. This plug reads any incoming B3 headers, and either joins the incoming trace, or starts a new one (dependent on result of sampling), adding a 'server receive' annotation, and various other binary annotations with incoming request details.

See also

The API documentation can be found at

Obtaining the Trace Id in Request Handlers

You can retrieve the Tapper Id from the %Plug.Conn{} using the Tapper.Plug.fetch/1 function, and then use it to start child spans etc.:

id = Tapper.Plug.fetch(conn) # get top-level span id

id = Tapper.start_span(id, name: "remote api call") # use it

id = Tapper.finish_span(id)

It is the application's responsibility to maintain the Tapper Id locally through its child-spans.

Contextual API

You can enable use of contextual API using contextual: true option in Tapper.Plug.Trace.

  plug Tapper.Plug.Trace, contextual: true

This will allow you to access the trace id in the application without explicitly passing the trace id.

  id = Tapper.Ctx.context()

Filtering with Tapper.Plug.Filter

This filter takes a list of URL path prefixes to be excluded from sampling, even if a sampled or debug B3 header is sent.

The prefixes can be in specified as a list of segments, or path strings:

plug Tapper.Plug.Filter, prefixes: ["__gtg", "foo/bar"]

# is equivalent to
plug Tapper.Plug.Filter, prefixes: [["__gtg"], ["foo", "bar"]]

For matching paths, the filter sets the Tapper id to :ignore, which is matched to a no-op in the Tapper API.


The default sampler is Tapper.Plug.Sampler.Simple, which samples a percentage of requests, where the percentage is specified with a percent option (default 10%):

plug Tapper.Plug.Trace, percent: 25 # sample 25% of requests

Tapper.Plug.Trace also takes a sampler option, specifying a module with a sample?/2 function, or a fun/2, to call with the Plug.Conn and the plug's configuration; this function should return true if a trace is to be sampled:

# silly example shows conn and config is passed to sampler
plug Tapper.Plug.Trace, x: "/foo", sampler: fn
    (conn, config) -> String.starts_with?(conn.request_path, config[:x])

The sampler is only called if:

  • the trace is not already sampled due to an incoming header, and
  • the debug option on the Trace plug is not set to true.

Note that you cannot turn sampling on for a trace after Tapper.Plug.Trace has determined that sampling should not take place; this is because this causes operations to become no-ops for performance reasons. A work-around for this, to allow traces to be sampled after some interesting event has occurred, may be included in future versions, but for now, you could hard-code the debug flag to true, and take care of determining whether to report a trace in an implementation of Tapper's reporter.

Propagating a Trace Downstream

Tapper.Plug.HeaderPropagation.encode/1 will encode a Tapper Id into B3 headers (as a keyword list) suitable for passing to HTTPoison etc. for propagation to down-stream servers:

id = Tapper.start_span(id, name: "call-out")

b3_headers = Tapper.Plug.HeaderPropagation.encode(id)

response = HTTPoison.get("", b3_headers)

For non-HTTP propagation, you could translate the encoded output to whatever structure you need to populate, or use Tapper.Id.destructure/1 to obtain the underlying information from a Tapper.Id and encode it yourself.

See also

The module Tapper.Plug.HeaderPropagation.B3Single can be used to encode to the B3 Single format instead of the original B3 multiple-header format; note that Tapper.Plug automatically supports either format when decoding incoming trace headers.


For the latest pre-release (and unstable) code, add the github repo to your mix dependencies:

def deps do
  [{:tapper_plug, github: "Financial-Times/tapper_plug"}]

For release versions, the package can be installed by adding tapper_plug to your list of dependencies in mix.exs:

def deps do
  [{:tapper_plug, "~> 0.5"}]