this repo has no description

Initial commit

+5
.gitignore
···
+
/_build
+
/deps
+
erl_crash.dump
+
*.ez
+
/doc
+5
.travis.yml
···
+
language: elixir
+
elixir:
+
- 1.5.1
+
otp_release:
+
- 20.0
+21
LICENSE.txt
···
+
MIT License
+
+
Copyright (c) 2017 Nick Mohoric
+
+
Permission is hereby granted, free of charge, to any person obtaining a copy
+
of this software and associated documentation files (the "Software"), to deal
+
in the Software without restriction, including without limitation the rights
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+
copies of the Software, and to permit persons to whom the Software is
+
furnished to do so, subject to the following conditions:
+
+
The above copyright notice and this permission notice shall be included in all
+
copies or substantial portions of the Software.
+
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+
SOFTWARE.
+41
README.md
···
+
# PlugOpenTracing
+
+
[![Build Status](https://travis-ci.org/nmohoric/plug_opentracing.svg?branch=master)](https://travis-ci.org/nmohoric/plug_opentracing)
+
+
An Elixir Plug for adding OpenTracing instrumentation.
+
+
## Usage
+
+
Update your `mix.exs` file and run `mix deps.get`.
+
```elixir
+
defp deps do
+
[{:plug_opentracing, "~> 0.1"}]
+
end
+
```
+
+
Add the plug to a pipeline. In this case we will look for the
+
`uber-trace-id` header to exist. If so, it is split on `:` and
+
the first and second values are used as the trace_id and parent_id
+
when creating the span.
+
+
```elixir
+
defmodule MyPhoenixApp.MyController do
+
use MyPhoenixApp.Web, :controller
+
alias Plug.Conn.Status
+
+
plug PlugOpenTracing, trace_id: 'uber-trace-id'
+
plug :action
+
+
def index(conn, _params) do
+
conn
+
|> put_status(Status.code :ok)
+
end
+
end
+
+
```
+
The created span can be accessed using `conn.assigns[:trace_span]`. This is useful
+
when you want to use this span as the parent of another span in your request, or
+
if you want to add tags/logs to the span before it is finished.
+
+
The request span will be finished at the end of your request using a callback,
+
which will send it to the Jaeger or Zipkin endpoint you've set up in your config.
+24
config/config.exs
···
+
# This file is responsible for configuring your application
+
# and its dependencies with the aid of the Mix.Config module.
+
use Mix.Config
+
+
# This configuration is loaded before any dependency and is restricted
+
# to this project. If another project depends on this project, this
+
# file won't be loaded nor affect the parent project. For this reason,
+
# if you want to provide default values for your application for third-
+
# party users, it should be done in your mix.exs file.
+
+
# Sample configuration:
+
#
+
# config :logger, :console,
+
# level: :info,
+
# format: "$date $time [$level] $metadata$message\n",
+
# metadata: [:user_id]
+
+
# It is also possible to import configuration files, relative to this
+
# directory. For example, you can emulate configuration per environment
+
# by uncommenting the line below and defining dev.exs, test.exs and such.
+
# Configuration from the imported file will override the ones defined
+
# here (which is why it is important to import them last).
+
#
+
# import_config "#{Mix.env}.exs"
+84
lib/plug_opentracing.ex
···
+
defmodule PlugOpenTracing do
+
@moduledoc """
+
A plug for adding OpenTracing instrumentation to requests.
+
+
If a request already has a trace-id, the parts will be split
+
out and added as trace-id and parent-id in the new span. Otherwise,
+
a new span will be created.
+
+
The default header is "uber-trace-id" but this can be set at compile time.
+
+
At any point in the request you can access the span using conn.assigns[:trace].
+
This can then be used to either add tags onto it or use the span as a parent span
+
for other requests.
+
+
To use it, just plug it into your module:
+
+
plug PlugOpenTracing
+
+
## Options
+
+
* `:trace_id` - An incoming trace-id. This should be hex and in the
+
form `trace-id:span-id` plus any optional bits that will be removed.
+
+
plug PlugOpenTracing, trace_id: "my-trace-header"
+
"""
+
+
alias Plug.Conn
+
alias :otter, as: Otter
+
@behaviour Plug
+
+
def init(opts) do
+
Keyword.get(opts, :trace_header, "uber-trace-id")
+
end
+
+
def call(conn, trace_id_header) do
+
conn
+
|> get_trace_id(trace_id_header)
+
|> start_span()
+
|> tag_span()
+
|> register_span()
+
end
+
+
defp get_trace_id(conn, header) do
+
case Conn.get_req_header(conn, header) do
+
[] -> {conn, nil}
+
[val|_] -> {conn, extract_id(String.split(val, ":"))}
+
end
+
end
+
+
defp start_span({conn, [nil, _]}), do: start_span({conn, nil})
+
defp start_span({conn, [_, nil]}), do: start_span({conn, nil})
+
defp start_span({conn, [trace_id, span_id]}) do
+
{conn, Otter.start(conn.method, trace_id, span_id)}
+
end
+
defp start_span({conn, _}), do: {conn, Otter.start(conn.method)}
+
+
defp tag_span({conn, span}) do
+
span = span
+
|> Otter.tag("path", Enum.join(Enum.map(conn.path_info, &URI.decode/1), "/"))
+
|> Otter.tag("method", conn.method)
+
conn |> Conn.assign(:trace_span, span)
+
end
+
+
defp register_span(conn) do
+
Conn.register_before_send(conn, fn c ->
+
c.assigns[:trace_span]
+
|> Otter.finish()
+
c
+
end)
+
end
+
+
defp extract_id(vals) when length(vals) >= 2 do
+
vals
+
|> Enum.take(2)
+
|> Enum.map(fn(s) -> s
+
|> Integer.parse(16)
+
|> case do
+
:error -> nil
+
i -> elem(i,0)
+
end
+
end)
+
end
+
defp extract_id(_), do: nil
+
end
+43
mix.exs
···
+
defmodule PlugOpenTracing.Mixfile do
+
use Mix.Project
+
+
def project do
+
[
+
app: :plug_opentracing,
+
version: "0.0.1",
+
name: "PlugOpenTracing",
+
source_url: "https://github.com/nmohoric/plug_opentracing",
+
elixir: "~> 1.0",
+
deps: deps(),
+
description: description(),
+
package: package()
+
]
+
end
+
+
defp description do
+
"""
+
An Elixir Plug for adding opentracing instrumentation.
+
"""
+
end
+
+
defp package do
+
[
+
maintainers: ["Nick Mohoric"],
+
files: ["lib", "mix.exs", "README*", "LICENSE*"],
+
licenses: ["MIT"],
+
links: %{"GitHub" => "https://github.com/nmohoric/plug_opentracing"}
+
]
+
end
+
+
def application do
+
[applications: [:plug]]
+
end
+
+
defp deps do
+
[
+
{:plug, "~> 1.3"},
+
{:ibrowse, "~> 4.4"},
+
{:otter, "~> 0.2.0"},
+
]
+
end
+
end
+4
mix.lock
···
+
%{"ibrowse": {:hex, :ibrowse, "4.4.0", "2d923325efe0d2cb09b9c6a047b2835a5eda69d8a47ed6ff8bc03628b764e991", [], [], "hexpm"},
+
"mime": {:hex, :mime, "1.1.0", "01c1d6f4083d8aa5c7b8c246ade95139620ef8effb009edde934e0ec3b28090a", [], [], "hexpm"},
+
"otter": {:hex, :otter, "0.2.0", "0ca08e94db43c9ef6bf22a9b803a776880841e9f7fbf7b9207ade0cdeb0c6251", [], [], "hexpm"},
+
"plug": {:hex, :plug, "1.4.3", "236d77ce7bf3e3a2668dc0d32a9b6f1f9b1f05361019946aae49874904be4aed", [], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}}
+53
test/plug_opentracing_test.exs
···
+
defmodule PlugOpenTracingTest do
+
use ExUnit.Case, async: true
+
use Plug.Test
+
alias Plug.Conn.Status
+
+
test "missing trace header results in new span" do
+
connection = conn(:get, "/test-path")
+
response = TestApp.call(connection, [])
+
+
assert response.status == Status.code(:ok)
+
assert response.resp_body != ""
+
end
+
+
test "bad trace header results in new span" do
+
connection = conn(:get, "/test-path") |> put_req_header("uber-trace-id", "1234")
+
response = TestApp.call(connection, [])
+
+
assert response.status == Status.code(:ok)
+
assert response.resp_body != ""
+
end
+
+
test "valid trace header results in span with those ids" do
+
connection = conn(:get, "/test-path") |> put_req_header("uber-trace-id", "6a5c63925e01051b:150b1b1adcde6f4:6a5c63925e01051b:1")
+
response = TestApp.call(connection, [])
+
+
assert response.status == Status.code(:ok)
+
assert response.resp_body == "7664110146171241755"
+
end
+
+
test "invalid hex trace header results in usable span" do
+
connection = conn(:get, "/test-path") |> put_req_header("uber-trace-id", "6a5x63925t01051b:150b1p1adcdq6f4:6a5c63925e01051b:1")
+
response = TestApp.call(connection, [])
+
+
assert response.status == Status.code(:ok)
+
assert response.resp_body == "1701"
+
end
+
+
test "empty trace header results in new span" do
+
connection = conn(:get, "/test-path") |> put_req_header("uber-trace-id", "")
+
response = TestApp.call(connection, [])
+
+
assert response.status == Status.code(:ok)
+
assert response.resp_body != ""
+
end
+
+
test "just some colons results in new span" do
+
connection = conn(:get, "/test-path") |> put_req_header("uber-trace-id", "::::")
+
response = TestApp.call(connection, [])
+
+
assert response.status == Status.code(:ok)
+
assert response.resp_body != ""
+
end
+
end
+28
test/test_helper.exs
···
+
ExUnit.start()
+
+
defmodule AppMaker do
+
defmacro __using__(options) do
+
quote do
+
use Plug.Router
+
alias Plug.Conn.Status
+
+
plug PlugOpenTracing, unquote(options)
+
plug :match
+
plug :dispatch
+
end
+
end
+
end
+
+
defmodule TestApp do
+
alias :otter, as: Otter
+
use AppMaker, trace_header: "uber-trace-id"
+
+
get "/test-path" do
+
send_resp(conn, Status.code(:ok), ids_to_string(conn))
+
end
+
+
defp ids_to_string(conn) do
+
{trace_id, _} = Otter.ids(conn.assigns[:trace_span])
+
"#{Integer.to_string(trace_id)}"
+
end
+
end