This guide walks you through installing TimelessTraces, wiring it into the OpenTelemetry SDK, and running your first trace query.
Installation
Add the dependency to your mix.exs:
def deps do
[
{:timeless_traces, "~> 0.5"},
{:opentelemetry_api, "~> 1.4"},
{:opentelemetry, "~> 1.5"}
]
endThen fetch dependencies:
mix deps.get
Configuration
Set a data directory and wire up the OTel exporter:
# config/config.exs
config :timeless_traces,
data_dir: "priv/span_stream"
config :opentelemetry,
traces_exporter: {TimelessTraces.Exporter, []}That's it. TimelessTraces starts automatically as an OTP application. The exporter receives spans directly from the OpenTelemetry Erlang SDK -- no HTTP, no protobuf, no external collectors.
Creating spans
Use the standard OpenTelemetry API. Spans are automatically exported to TimelessTraces:
require OpenTelemetry.Tracer, as: Tracer
Tracer.with_span "my_operation" do
# Your code here
Tracer.set_attribute("user.id", "abc123")
:ok
endFor Phoenix applications, add opentelemetry_phoenix and opentelemetry_ecto for automatic instrumentation:
def deps do
[
{:opentelemetry_phoenix, "~> 1.2"},
{:opentelemetry_ecto, "~> 1.2"}
]
end# In your application.ex start/2
OpentelemetryPhoenix.setup()
OpentelemetryEcto.setup([:my_app, :repo])Querying spans
Once spans are flowing, query them:
# Find recent error spans
{:ok, result} = TimelessTraces.query(status: :error)
result.entries # => [%TimelessTraces.Span{}, ...]
result.total # => 42
# Find slow spans (> 100ms)
TimelessTraces.query(min_duration: 100_000_000)
# Find spans from a specific service
TimelessTraces.query(service: "my_app", kind: :server)Trace lookup
Retrieve all spans in a single trace:
{:ok, spans} = TimelessTraces.trace("abc123def456...")
# Returns all spans sorted by start timeListing services and operations
{:ok, services} = TimelessTraces.services()
# => {:ok, ["my_app", "api_gateway", "auth_service"]}
{:ok, ops} = TimelessTraces.operations("my_app")
# => {:ok, ["GET /users", "DB query", "cache_lookup"]}Statistics
Check storage status:
{:ok, stats} = TimelessTraces.stats()
stats.total_blocks #=> 42
stats.total_entries #=> 50_000
stats.disk_size #=> 24_000_000Enabling the HTTP API
Optionally enable the HTTP interface for Jaeger-compatible queries and OTLP ingest:
config :timeless_traces, http: true # port 10428, no auth
config :timeless_traces, http: [port: 10500, bearer_token: "secret"]See the HTTP API guide for endpoint details.
Next steps
- Configuration Reference -- all options with defaults
- Architecture -- how the pipeline works
- Querying -- full filter reference and optimization tips
- HTTP API -- OTLP ingest and Jaeger-compatible query endpoints
- Storage & Compression -- block formats and compaction
- Operations -- backup, retention, and troubleshooting