this repo has no description
1defmodule PlugOpenTracing do
2 @moduledoc """
3 A plug for adding OpenTracing instrumentation to requests.
4
5 If a request already has a trace-id, the parts will be split
6 out and added as trace-id and parent-id in the new span. Otherwise,
7 a new span will be created.
8
9 The default header is "uber-trace-id" but this can be set at compile time.
10
11 At any point in the request you can access the span using conn.assigns[:trace].
12 This can then be used to either add tags onto it or use the span as a parent span
13 for other requests.
14
15 To use it, just plug it into your module:
16
17 plug PlugOpenTracing
18
19 ## Options
20
21 * `:trace_id` - An incoming trace-id. This should be hex and in the
22 form `trace-id:span-id` plus any optional bits that will be removed.
23
24 plug PlugOpenTracing, trace_id: "my-trace-header"
25 """
26
27 alias Plug.Conn
28 alias :otter, as: Otter
29 @behaviour Plug
30
31 def init(opts) do
32 Keyword.get(opts, :trace_header, "uber-trace-id")
33 end
34
35 def call(conn, trace_id_header) do
36 conn
37 |> get_trace_id(trace_id_header)
38 |> start_span()
39 |> tag_span()
40 |> register_span()
41 end
42
43 defp get_trace_id(conn, header) do
44 case Conn.get_req_header(conn, header) do
45 [] -> {conn, nil}
46 [val|_] -> {conn, extract_id(String.split(val, ":"))}
47 end
48 end
49
50 defp start_span({conn, [nil, _]}), do: start_span({conn, nil})
51 defp start_span({conn, [_, nil]}), do: start_span({conn, nil})
52 defp start_span({conn, [trace_id, span_id]}) do
53 {conn, Otter.start(conn.method, trace_id, span_id)}
54 end
55 defp start_span({conn, _}), do: {conn, Otter.start(conn.method)}
56
57 defp tag_span({conn, span}) do
58 span = span
59 |> Otter.tag("path", Enum.join(Enum.map(conn.path_info, &URI.decode/1), "/"))
60 |> Otter.tag("method", conn.method)
61 conn |> Conn.assign(:trace_span, span)
62 end
63
64 defp register_span(conn) do
65 Conn.register_before_send(conn, fn c ->
66 c.assigns[:trace_span]
67 |> Otter.finish()
68 c
69 end)
70 end
71
72 defp extract_id(vals) when length(vals) >= 2 do
73 vals
74 |> Enum.take(2)
75 |> Enum.map(fn(s) -> s
76 |> Integer.parse(16)
77 |> case do
78 :error -> nil
79 i -> elem(i,0)
80 end
81 end)
82 end
83 defp extract_id(_), do: nil
84end