Ruby
e-volv-logs is the SDK for Ruby 2.6 or later. Standard library only — no runtime dependencies — with rack and faraday as soft, duck-typed integrations and nothing monkeypatched. It speaks the same wire contract as the Node.js, Python, Go and Java SDKs.
Install and initialise
gem install e-volv-logs # or in a Gemfile: gem "e-volv-logs"
require "evolve_logs"
EvolveLogs.init(
key: ENV["EVOLVE_LOGS_KEY"], # project ingest key — a server key, from the environment
url: "https://api.e-volv.io/api/public/v1/logs",
service: "orders-api",
environment: ENV["RACK_ENV"],
release: ENV["GIT_SHA"]
)
EvolveLogs.info("order created", { "orderId" => "o_1", "total" => 42.5 })
EvolveLogs.error("payment failed", { "orderId" => "o_1" })
begin
charge
rescue StandardError => err
EvolveLogs.exception(err, { "orderId" => "o_1" }) # exception.type/message/stack
endIf key or url is empty, init returns a no-op client and warns once on stderr — the SDK never raises into your code. EvolveLogs::Client.new(...) builds a non-default client; the module-level helpers delegate to the client installed by EvolveLogs.init.
Traces and spans
Trace context lives in Thread.current (fiber-local): it flows into fibers under the current thread and never leaks across threads.
EvolveLogs.span("db.query", { "table" => "orders" }) do
db.query("SELECT …")
end # an exception inside ends the span as failed and is re-raisedThe span end is recorded as an event with span.name and durationMs, carrying the span’s trace and span ids. EvolveLogs.traceparent returns the W3C 00-<traceId>-<spanId>-01 header, or nil outside a trace.
Rack and Rails
# config.ru — one root span per request; an inbound traceparent is continued use EvolveLogs::RackMiddleware
# config/application.rb — every Rails.logger line also ships to Observer; # Rails.logger.error(err) carries exception.* config.logger = EvolveLogs::Logger.new($stdout)
An app that raises ends the http.request span as failed and is re-raised, so the server crashes exactly as it would without the SDK.
Queue hops: ActiveJob and Sidekiq
# producer MyJob.perform_later(args, traceparent: EvolveLogs.traceparent) # consumer EvolveLogs::ActiveJob.perform( EvolveLogs::ActiveJob.traceparent_in(arguments), self.class.name ) do # one trace with the producer end
EvolveLogs::Sidekiq.perform(traceparent, worker, attrs) { … } is the Sidekiq shape; EvolveLogs::Carriers.wrap(header, span_name, attrs){ … } covers anything else. For raw headers, EvolveLogs.run_with_traceparent(header) { … } runs the block with the next hop of that trace — an absent or malformed header starts a fresh trace.
Outbound HTTP: Net::HTTP and Faraday
Monkeypatch-free — opt in per call site:
request = Net::HTTP::Post.new(uri) EvolveLogs::HTTP.inject_traceparent(request) # never overwrites an existing header http.request(request)
conn = Faraday.new(url: "https://internal") do |builder| builder.use EvolveLogs::FaradayMiddleware builder.adapter Faraday.default_adapter end
Delivery
The shared numbers: flush at 200 events, every 2 seconds or at 512 KB, always gzipped. A 429 honours Retry-After, otherwise exponential backoff from 500 ms (doubling, capped at 10 s), up to three attempts; a 413 halves the batch and counts the excess half. Past twice the batch size the oldest events drop, visible on client.dropped. The SDK installs an at_exit hook and EvolveLogs.flush is safe to call repeatedly.
Already emit OpenTelemetry? The Ruby preset in packages/logs-otel-presets remains a valid route — point the OTel Ruby SDK at the ingest with one file. See OpenTelemetry presets.
Package reference: packages/logs-ruby. Back to SDK overview.