LightCDP emits telemetry events for all page operations and CDP commands. By default, nothing listens — zero overhead. Opt in per environment.
Option 1: Logger (no extra deps)
Add one line to your Application.start/2:
LightCDP.Telemetry.attach_default_logger(level: :debug)Output:
[debug] navigate https://example.com
[debug] CDP Page.navigate
[debug] CDP Page.navigate in 657.2ms
[debug] navigate completed in 822.3ms
[debug] fill #email
[debug] · focus
[debug] · clear
[debug] · insert (value_length=16)
[debug] fill completed in 3.1msGood for dev. No waterfall, no span nesting, but immediate visibility.
Option 2: OpenTelemetry + Jaeger
Full distributed tracing with nested spans and a visual waterfall.
Dependencies
# mix.exs
{:opentelemetry, "~> 1.4"},
{:opentelemetry_api, "~> 1.3"},
{:opentelemetry_exporter, "~> 1.7"}Configuration
# config/runtime.exs
config :opentelemetry_exporter,
otlp_protocol: :http_protobuf,
otlp_endpoint: "http://localhost:4318"Setup
# In Application.start/2
LightCDP.Telemetry.OtelBridge.setup()Root span
Wrap your workflow in a root span so all operations appear as one trace:
require OpenTelemetry.Tracer
OpenTelemetry.Tracer.with_span "my_scraper" do
{:ok, session} = LightCDP.start()
{:ok, page} = LightCDP.new_page(session)
LightCDP.Page.navigate(page, "https://example.com")
# ...
LightCDP.stop(session)
endWhat you see in Jaeger
my_scraper (1.8s)
├── connection.command Target.createTarget (30ms)
├── connection.command Target.attachToTarget (0.4ms)
├── connection.command Page.enable (0.4ms)
├── connection.command DOM.enable (0.2ms)
├── page.navigate (833ms)
│ └── connection.command Page.navigate (651ms)
├── page.fill (5ms)
│ ├── connection.command DOM.getDocument (1ms)
│ ├── connection.command DOM.querySelector (1ms)
│ ├── connection.command DOM.resolveNode (1ms)
│ ├── connection.command Runtime.callFunctionOn (1ms) ← focus
│ ├── connection.command Runtime.callFunctionOn (1ms) ← clear
│ └── connection.command Input.insertText (0.1ms)
├── page.wait_for_navigation (708ms)
│ └── page.evaluate → connection.command Runtime.evaluate
└── page.evaluate (1ms)
└── connection.command Runtime.evaluateMulti-step operations (fill, click) include span events visible as logs within the parent span — showing which step (focus, clear, insert) each CDP command belongs to.
Running Jaeger locally
# Native binary
jaeger
# Or Docker
docker run -d -p 16686:16686 -p 4318:4318 jaegertracing/jaeger:latest
UI at http://localhost:16686.
Telemetry events reference
See LightCDP.Telemetry for the full list of event names, metadata per operation, and the distinction between span events and step annotations.
For a runnable example, see examples/sample_traced.exs.