README.md

# Tapper.Plug

[Plug](https://github.com/elixir-lang/plug) integration for the [Tapper](https://github.com/Financial-Times/tapper) Zipkin client.

[![Hex pm](http://img.shields.io/hexpm/v/tapper_plug.svg?style=flat)](https://hex.pm/packages/tapper_plug) [![Inline docs](http://inch-ci.org/github/Financial-Times/tapper_plug.svg)](http://inch-ci.org/github/Financial-Times/tapper_plug) [![Build Status](https://travis-ci.org/Financial-Times/tapper_plug.svg?branch=master)](https://travis-ci.org/Financial-Times/tapper_plug)

## Synopsis

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

```elixir
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](https://github.com/openzipkin/b3-propagation) 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](https://github.com/Financial-Times/tapper_absinthe_plug) - propagate Tapper Id to [Absinthe](http://absinthe-graphql.org/) resolvers.

The API documentation can be found at [https://hexdocs.pm/tapper_plug](https://hexdocs.pm/tapper_plug).

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

```elixir
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:

```elixir
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%):

```elixir
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:

```elixir
# 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])
  end
```

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](https://github.com/openzipkin/b3-propagation) headers (as a keyword list) suitable for
passing to HTTPoison etc. for propagation to down-stream servers:

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

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

response = HTTPoison.get("http://some.service.com/some/api", 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:

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

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

```elixir
def deps do
  [{:tapper_plug, "~> 0.2"}]
end
```

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