dd_trace_ex

A Datadog APM tracing library for Elixir.

dd_trace_ex (top-level module DDTrace) is written to match what Datadog's official dd-trace libraries do. It sends traces to the Datadog agent in the same format they do, so features like 128-bit trace IDs, numeric metrics, tracing across services, sampling and manual keep or drop all behave the way Datadog users expect. dd-trace-js and dd-trace-go were the reference for how things should work on the wire. Tracing context lives in the process and follows work into other processes, the API is small and built around a single trace call, and Phoenix, Ecto and Req are supported through :telemetry. It is still early, but the core is in place.

Status: 0.1.0 release candidate. This is a first public release, not a mature library: it covers a documented subset of Datadog tracing (see Current limitations), and the API may still change before 1.0.

Installation

Add dd_trace_ex to your dependencies:

# mix.exs
defp deps do
[
{:dd_trace_ex, "~> 0.1.0"}
]
end

Run mix deps.get, then configure your service and agent connection:

# config/runtime.exs
config :dd_trace_ex,
service: "my_app",
agent_url: "http://localhost:8126"

The tracer starts automatically with your application. You need a Datadog Agent with APM enabled to receive the traces. See the setup guide for environment variables and integration setup.

Trace application code

Wrap work in DDTrace.trace. Tags and metrics describe the span being traced:

require DDTrace
DDTrace.trace "orders.import" do
DDTrace.set_tag("order.channel", "web")
DDTrace.set_metric("orders.count", length(orders))
MyApp.Orders.import(orders)
end

The block returns its result. Nested calls create child spans. Exceptions that escape the block are recorded on the span and re-raised.

For a function you want to trace on every call, use a decorator:

defmodule MyApp.Orders do
use DDTrace.Decorators
@decorate trace(name: "orders.import")
def import(orders) do
DDTrace.set_metric("orders.count", length(orders))
# ...
end
end

DDTrace.with_trace/3 takes a function when that fits better than a block.

Integrations

Enable Phoenix and Ecto tracing at application startup, before starting your endpoint and repo:

DDTrace.Integrations.Phoenix.setup()
DDTrace.Integrations.Ecto.setup(repo: MyApp.Repo)

Phoenix request spans include the route, HTTP status, and incoming trace context. Ecto query spans include SQL and timing information; bind parameter values are excluded.

For Req, attach tracing when building the client:

req =
Req.new(base_url: "https://inventory.example.com")
|> DDTrace.Integrations.Req.attach()
Req.get!(req, url: "/items")

Requests through that client produce spans and carry Datadog trace headers to the receiving service.

Trace work across processes

Use DDTrace.Task to carry the current trace into tasks. Create spans inside the task for the work you want to measure:

require DDTrace
DDTrace.trace "orders.import" do
orders
|> DDTrace.Task.async_stream(fn order ->
DDTrace.trace "order.process" do
MyApp.Orders.process(order)
end
end)
|> Enum.to_list()
end

For messages, GenServers, and other process boundaries, capture the context with DDTrace.current_context/0 and pass it to the receiving process. Use parent: when opening its span.

Sampling and logs

The tracer uses sampling rates supplied by the agent. You can also configure sampling rules or a fixed rate, and override the decision with DDTrace.keep_trace/0 or DDTrace.drop_trace/0. Dropped traces still reach the agent; dropping controls retention.

While a span is open, trace and span IDs are available in Logger metadata. Include these fields in your JSON logs for Datadog log correlation.

See the setup guide for configuration and logging examples.

Testing

Capture spans in ExUnit without running an agent:

defmodule MyApp.OrdersTest do
use ExUnit.Case, async: true
use DDTrace.Test
test "imports are traced" do
MyApp.Orders.import([])
assert_span "orders.import"
end
end

Current limitations

License

MIT. See LICENSE.

dd_trace_ex is an independent project. It is not affiliated with, endorsed by, or sponsored by Datadog, Inc. "Datadog" and "dd-trace" are trademarks of Datadog, Inc., used here only to describe what this library interoperates with.