Non-blocking analytics, error tracking, and logging for Ruby servers.
Stdlib only. No runtime dependencies. Ruby 3.0+.
Add to your Gemfile:
gem "strawberry-sdk", git: "https://github.com/steventruong/strawberry-ruby.git", tag: "v1.0.0"Then bundle install.
require "strawberry"
Strawberry.init(
api_key: ENV.fetch("STRAWBERRY_API_KEY"),
host: "https://straw.berryagents.com",
release_version: ENV["GIT_SHA"],
environment: "production"
)
Strawberry.capture("order_placed", { amount: 42.50, currency: "USD" }, distinct_id: "user_123")
Strawberry.identify("user_123", { email: "jane@example.com", plan: "pro" })
begin
do_work
rescue => e
Strawberry.capture_error(e, { endpoint: "/checkout" })
end
Strawberry.log(:info, "cache miss", category: "cache", attrs: { key: "u_123" })
Strawberry.llm_call(
provider: "openai",
model: "gpt-4o",
prompt_tokens: 120,
completion_tokens: 80,
latency_ms: 412,
cost_usd: 0.002,
status: "success"
)
Strawberry.flush # force drain
Strawberry.shutdown # stop worker (called automatically at_exit)# config.ru
require "strawberry"
Strawberry.init(api_key: ENV.fetch("STRAWBERRY_API_KEY"), host: "https://app.getstrawberry.io")
use Strawberry.middleware
run MyApp- Per-channel bounded queues (events, errors, logs, identify, llm) with drop-oldest overflow
- Per-endpoint circuit breaker (CLOSED / OPEN / HALF_OPEN)
- Jittered decorrelated backoff on retry
- Always-on PII redactor (emails, phones, Luhn-valid cards, JWTs, API-key prefixes, field-name denylist)
- Single background worker thread,
at_exitflush - Injectable transport + clock for tests
Strawberry.diagnostics
# => {
# enabled: true,
# host: "https://straw.berryagents.com",
# queue_sizes: { events: 0, errors: 0, logs: 0, identify: 0, llm: 0 },
# dropped: { events: 0, errors: 0, logs: 0, identify: 0, llm: 0 },
# circuits: { ingest: :closed, errors: :closed, logs: :closed }
# }- Public calls never raise. Observability never breaks the caller.
- Transport is asynchronous.
capturereturns immediately after enqueue. - Overflow drops oldest items, so the newest signal always wins.
- On transport failure items are requeued (bounded by cap) and retried with jittered backoff.