Zero-config APM SDK for Ruby — Rails, Sinatra & bare Rack. In Rails it's fully automatic: add the gem and set METRIXWIRE_KEY. Elsewhere, call MetrixWire.init once. Every request, database query, outbound HTTP call and cache op is instrumented automatically. There is no manual span API and no middleware to wire up. Non-blocking: if the MetrixWire endpoint is down, your app keeps running normally.
# Gemfile
gem 'metrixwire', require: 'metrixwire'bundle installAdd the gem, set the key, done. A Railtie auto-initializes from ENV, inserts the Rack middleware (one trace per request), and subscribes to ActiveSupport::Notifications — no code changes:
export METRIXWIRE_KEY=mw_...Just require the gem and call init once, as early as possible:
require 'metrixwire'
MetrixWire.init(api_key: ENV['METRIXWIRE_KEY'])The SDK patches Rack::Builder#to_app, so the tracing middleware is inserted for you — you never write use MetrixWire::Rack. Every HTTP request becomes a trace, and every query / HTTP call / cache op within it becomes a span.
| Framework | Traced automatically | Notes |
|---|---|---|
| Rails | ✅ | Route refined to controller#action via ActiveSupport::Notifications. 5xx & exceptions captured. |
| Sinatra | ✅ | Route pattern (GET /users/:id) detected from sinatra.route. Zero wiring. |
| bare Rack | ✅ | Middleware auto-inserted via Rack::Builder#to_app. |
The Rails path uses ActiveSupport::Notifications (the idiomatic, reliable route): sql.active_record for DB spans, process_action.action_controller for the route + errors, cache_*.active_support for cache spans, and transaction.active_record for transaction spans. Outside Rails, DB and HTTP are captured by patching the underlying drivers directly.
| Library | How |
|---|---|
| ActiveRecord (Rails) | sql.active_record notifications (db_query + rowCount) |
| pg | patches PG::Connection#exec/#exec_params/#async_exec (rowCount from the result; transaction spans from BEGIN…COMMIT) |
| mysql2 | patches Mysql2::Client#query |
| sqlite3 | patches SQLite3::Database#execute |
| Net::HTTP (and libs built on it) | patches Net::HTTP#request (http_call span + statusCode) |
| redis / redis-client | cache spans (hit/miss where known) for the slow-cache detector |
Each patch is guarded: if the library or constant isn't present, it's skipped silently. The core has zero runtime gem dependencies (stdlib only).
MetrixWire.init(
api_key: 'mw_...', # required (falls back to METRIXWIRE_KEY)
endpoint: 'http://localhost:3000/ingest', # default (a base URL is accepted; /ingest is appended)
flush_interval_ms: 5000, # how often batches are sent
enabled: true, # set to false to disable entirely (or METRIXWIRE_ENABLED=false)
timeout_ms: 3000, # send timeout (short, non-blocking)
max_batch: 20, # flush immediately once this many are queued
capture_source: true # capture the file:line a span originated from
)All options fall back to environment variables when omitted: METRIXWIRE_KEY, METRIXWIRE_ENDPOINT, METRIXWIRE_ENABLED. With no API key the SDK runs in disabled mode instead of raising.
- Traces are batched on a background daemon thread and sent off the request path with a short timeout.
- All transport errors are swallowed — instrumentation never throws into or blocks your app.
- A final flush runs on
at_exit; you can also callMetrixWire.flushbefore a short-lived process exits.
For frameworks the SDK can't hook automatically, attach an exception to the active trace — this is not a manual span API; requests/queries are still captured automatically:
begin
do_work
rescue => e
MetrixWire.capture_exception(e)
raise
end