# Tapper.Plug

[Plug]( integration for the [Tapper]( Zipkin client.

[![Hex pm](]( [![Inline docs](]( [![Build Status](](

## Synopsis

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]( style headers, and either joins the incoming span, 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

* [Tapper.Plug.Absinthe]( - propagate Tapper Id to [Absinthe]( resolvers.

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.

## 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.

## Sampling

The default sampler is [`Tapper.Plug.Sampler.Simple`](lib/sampler.ex), 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")

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

response = HTTPoison.get("", headers)

For non-HTTP propagation, you could translate the headers to whatever structure you need to populate, or use `Tapper.Id.destructure/1` to
obtain the underlying information.

## Installation

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.2"}]

The `:tapper` application will be started automatically by Elixir 1.4+, as it is a non-optional dependency
of this project.