Opentracing for elixir applications.
Plug into your app via just a few lines of code, orchestrate span trees and send them to Jaeger, Zipkin, NewRelic, or any other tracing system.
- Prerequisites
- Installation
- Quick start
- Plug
- Module overview and functions
- Contributing
- Code of conduct (CoC)
- About SumUp
- You will need a general understanding of opentracing's concepts. You can find a good starting point here
- Install Elixir v1.9 or higher
- This project depends on tesla. Check out their documentation to learn more about Tesla and how middlewares work there.
- Throughout the examples of this readme we have used excerpts of a sample Phoenix web app, it is a good idea to get familiar with it first.
Add this line to your mix.exs
:
defp deps do
[{:ot, "~> 3.0"}]
end
Then run mix deps.get
and you are good to go!
# See "Configuration" section
Ot.start_link(config)
# See "Module overview and functions" section
Ot.start_span("my-test-span", nil) # <- span is initialized
Ot.tag("my-tag", "tag-value") #
Ot.log("log message") #
Ot.end_span() # <- span will be sent to jaeger
In config/dev.exs
:
# See "Configuration" section
config :my_app, :ot_config,
service_name: "my-app",
collectors: [
jaeger: [
url: "http://127.0.0.1:9411/api/v2/spans",
adapter: Tesla.Adapter.Hackney,
stringify_tags: true
]
]
In application.ex
:
defmodule MyApp.Application do
def start(_type, _args) do
children = [
{Ot, Application.fetch_env!(:my_app, :ot_config)}
# ... other children
In endpoint.ex
:
defmodule MyAppWeb.Endpoint do
use Phoenix.Endpoint, otp_app: :my_app
# See "Plug" section
plug Ot.Plug
# ... other plugs
Example with all supported configuration options:
# Tesla adapter to use in collectors (can be different for each collector)
# If you use this one, you must add :hackney to your deps
adapter = {Tesla.Adapter.Hackney, connect_timeout: 5000, recv_timeout: 5000}
ot_config = [
service_name: "my-app", # (required)
ignored_exceptions: [ # (optional) don't set "error=true" for these
Phoenix.Router.NoRouteError
],
plug: [ # (optional) config for Ot.Plug
paths: ["/v0.1/*"], # (optional) paths to work with; default: ["*"]
response_body_tag: "http.response_body", # (optional) tag resp body as "http.resp_body"; default: nil (no tag)
conn_private_tags: [ # (optional) list of conn-private fields to tag; default: []
req_body: "http.request_body", # tag conn.private.req_body as "http.request_body"
req_id: "http.request_id" # tag conn.private.req_id as "http.request_id"
]
],
collectors: [
jaeger: [
url: "...", # (required) span endpoint (Zipkin JSON v2 format)
adapter: tesla_adapter, # (required) tesla adapter to use
flush_interval: 500, # (optional) buffer flush interval in ms; default: 1000
flush_retries: 1, # (optional) flush retry attempts; default: 5 *
stringify_tags: true, # (optional) convert tag values to strings (required for jaeger)
middlewares: [] # (optional) Tesla middlewares; default: []
],
newrelic: [
url: "https://trace-api.newrelic.com/trace/v1",
adapter: tesla_adapter,
middlewares: [
Tesla.Middleware.Logger,
{Tesla.Middleware.Headers, [
{"api-key", "..."},
{"data-format", "zipkin"},
{"data-format-version", "2"}
]}
]
]
]
]
* tracing data is stored in a local buffer and sent to the tracing backends (jaeger, zipkin, etc.) in a batch, every X ms. On failure, the buffer is preserved and continues to accumulate spans till the next flush (retry). If the retry limit is exceeded, the buffer is discarded to prevent memory leaks.
Web applications can use Ot.Plug
to automatically have a span created for each incoming request.
Let's take a simple ping-pong web app and try this simple request:
curl 'http://localhost:4000/v0.1/ping?player=just_me'
The controller:
def MyAppWeb.MyController do
def ping(conn, params) do
Ot.tag("opponent", params["player"])
send_resp(conn, 200, "pong")
end
end
will produce this span, sent to the collector(s):
{
"annotations": [],
"duration": 1375,
"id": "d98f95a2d087a665",
"kind": "SERVER",
"localEndpoint":
{
"ipv4": "127.0.0.1",
"port": 0,
"serviceName": "my-app"
},
"name": "request",
"parentId": null,
"tags":
{
"component": "my-app",
"author": "just_me",
"http.method": "GET",
"http.path": "/v0.1/ping",
"http.query_string": "player=just_me",
"http.status_code": "200"
},
"timestamp": 1628150108323753,
"traceId": "0a922fbb7df193916b283610bcc516ff"
}
Note that the values of ipv4
and port
are hard-coded (could be made configurable in a later release).
Check out the Module overview docs.
Check out CONTRIBUTING.md
We want to foster an inclusive and friendly community around our Open Source efforts. Like all SumUp Open Source projects, this project follows the Contributor Covenant Code of Conduct. Please, read it and follow it.
If you feel another member of the community violated our CoC or you are experiencing problems participating in our community because of another individual's behavior, please get in touch with our maintainers. We will enforce the CoC.
It is our mission to make easy and fast card payments a reality across the entire world. You can pay with SumUp in more than 30 countries, already. Our engineers work in Berlin, Cologne, Sofia and Sāo Paulo. They write code in JavaScript, Swift, Ruby, Go, Java, Erlang, Elixir and more. Want to come work with us? Head to our careers page to find out more.