Tapper.Plug
Plug integration for the Tapper Zipkin client.
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.
- you can exclude certain URLs for the purposes of tracing using the optional
Tapper.Plug.Filter
. - 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
- Tapper.Plug.Absinthe - propagate Tapper Id to Absinthe resolvers.
The API documentation can be found at 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.:
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.
Sampling
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])
end
The sampler is only called if:
- the trace is not already sampled due to an incoming header, and
- the
debug
option on theTrace
plug is not set totrue
.
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 thedebug
flag totrue
, 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("http://some.service.com/some/api", 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.
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"}]
end
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"}]
end