SDKs / Ruby / Rails

Ruby / Rails

Broadcast your Rails logger and insert one Rack middleware — which re-raises, so rescue_from and your error pages still run.

Requires Ruby 3.0+Package insight_recorderSource on GitHub →

Before you start

Where your InsightRecorder runs
The base URL every call goes to — http://localhost:8080 when you are running it locally, your own hostname otherwise.
An API key
Settings → API keys, scoped to logs:write and bug:write. The token (crk_…) is shown exactly once — it is hashed at rest — and is revocable. Do not use a login JWT: it expires within the hour and is not meant for machines.
  1. 01

    Install

    HTTP is net/http; no runtime dependencies.

    gem "insight_recorder"
  2. 02

    Create the client and shipper

    In config/initializers/insight.rb, or the project's equivalent bootstrap.

    client  = InsightRecorder::Client.new(ENV.fetch("INSIGHT_BASE_URL"), ENV.fetch("INSIGHT_API_KEY"))
    shipper = client.shipper
    at_exit { shipper.close }
  3. 03

    Ship your logs

    Broadcast so lines keep reaching their usual destination too. On Rails < 7.1 use ActiveSupport::Logger.broadcast.

    Rails.logger.broadcast_to(
      ActiveSupport::Logger.new(InsightRecorder::LogDevice.new(shipper))
    )
  4. 04

    Report unhandled exceptions

    High in the stack, so it sees the whole application.

    config.middleware.insert_after ActionDispatch::ShowExceptions,
      InsightRecorder::Rack, client: client, shipper: shipper, service: "checkout-api"
  5. 05

    Verify it is working

    Log one line through the Rails logger, then raise from a route. The broadcast means you keep your usual log destination as well.

    Rails.logger.error("insightrecorder smoke test")
    
    # config/routes.rb, temporarily:
    get "/_boom", to: proc { raise "insightrecorder smoke test" }

    Then look for the line at /app/logs and the report at /app/bugs.

    If nothing shows up

    Nothing arrived? Check shipper.dropped — a rising count means the queue is undersized. Delivery errors are silent unless you pass on_error. And the at_exit { shipper.close } has to actually run, or the last batch is lost.

Before you ship

  • A Logger device only ever receives the formatted string — call shipper.send directly where structured fields matter.
  • Under Puma, also close the shipper from on_worker_shutdown.
PROMPT Install the Ruby / Rails SDK with an AI agent
Raw .md

Paste this into a coding agent running inside your project, or point the agent at /docs/prompts/ruby directly. It is self-contained: the agent needs no prior knowledge of InsightRecorder.

# Install and wire the InsightRecorder Ruby SDK

Paste this into a coding agent (Claude Code, Cursor, …) running inside your Ruby
or Rails application's repository. It is self-contained — the agent needs no
prior knowledge of InsightRecorder.

---

You are working in a Ruby application (most likely Rails). Install and wire the
InsightRecorder SDK so it ships its logs to InsightRecorder and reports unhandled
exceptions.

**Context you need:**

- InsightRecorder collects application logs and bug reports. The `insight_recorder`
  gem is the supported way for a Ruby application to talk to it.
- Requires **Ruby 3.0+** and has **zero runtime dependencies** — HTTP is
  `net/http` — so it cannot conflict with the `faraday`/`http` versions this
  project pins.
- Authentication uses a long-lived **API key** shaped like `crk_…`, created in
  the InsightRecorder UI under *Settings → API keys* with the `logs:write` scope (add
  `bug:write` to report errors). **Do not** use a login JWT: those expire after
  an hour and are not meant for machines.

  Add the matching **read** scopes for anything the SDK reads back — `bug:read` for `list_bugs/get_bug`, `logs:read` for `list_logs`. A write-only key is the common mistake: shipping works, the first read returns 403.

- **Wire trace correlation if the application uses OpenTelemetry.** A log line
  and the request that wrote it are one investigation, and that join is the
  reason to ship logs here rather than anywhere else — see the "Trace
  correlation" section of the README for what this language needs (several do it
  automatically; the rest take one hook). Without it, every log call has to carry
  `trace_id` by hand, which nobody sustains past the first week.

**Steps:**

1. Add `gem "insight_recorder"` to the `Gemfile` (in the default group — this is
   production telemetry, not a development tool) and run `bundle install`.

2. Read configuration from the environment, never hard-code it:
   `INSIGHT_BASE_URL` and `INSIGHT_API_KEY`. If this project uses Rails
   credentials or a settings gem, follow that instead — match what is there. If
   either value is missing, the application must boot normally with shipping
   disabled: telemetry is never a boot dependency.

3. Create the client and shipper once, in
   `config/initializers/insight.rb` (or the project's equivalent bootstrap):

   ```ruby
   client = InsightRecorder::Client.new(
     ENV.fetch("INSIGHT_BASE_URL"),
     ENV.fetch("INSIGHT_API_KEY")
   )
   shipper = client.shipper

   Rails.application.config.insight_client = client
   Rails.application.config.insight_shipper = shipper
   at_exit { shipper.close }
   ```

4. Ship logs by broadcasting the application logger, so lines keep going to
   their usual destination too:

   ```ruby
   Rails.logger.broadcast_to(
     ActiveSupport::Logger.new(InsightRecorder::LogDevice.new(shipper))
   )
   ```

   On Rails older than 7.1, use `ActiveSupport::Logger.broadcast` instead —
   check which the project's Rails version supports.

   Where structured fields matter, call `shipper.send(message:, level:,
   trace_id:, attrs:)` directly: a `Logger` device only ever receives the
   formatted string.

5. Report unhandled exceptions by inserting the Rack middleware high in the
   stack, in `config/application.rb`:

   ```ruby
   config.middleware.insert_after ActionDispatch::ShowExceptions,
     InsightRecorder::Rack,
     client: Rails.application.config.insight_client,
     shipper: Rails.application.config.insight_shipper,
     service: "<app name>"
   ```

   It re-raises, so `ActionDispatch` error pages and any `rescue_from` still run.
   For a non-Rails Rack app, `use InsightRecorder::Rack, client: …` in
   `config.ru`.

6. Make sure `shipper.close` runs on shutdown — the `at_exit` above, or the
   process manager's hook if this app uses one (Puma's `on_worker_shutdown`).

**Rules — do not violate these:**

- Do **not** write a logger or subscriber that makes an HTTP call per line. The
  SDK batches on a background thread, caps each request at `batch_size`, retries
  with backoff, and respects the server's quota (429 + `Retry-After`).
- Do **not** block on `shipper.send(...)` — it is non-blocking on purpose and
  drops when its queue is full.
- Do **not** log the API key, and do not commit it.
- Do **not** capture request bodies, headers, cookies, or query strings in
  reports: they routinely carry credentials and personal data.

**Verify before you finish:**

1. `bundle exec rspec` / `rails test` (whichever this project uses) passes.
2. Boot the app with both env vars set, exercise a request, then confirm the log
   lines appear in InsightRecorder at `/app/logs`.
3. Raise a deliberate exception in a controller and confirm a bug appears at
   `/app/bugs`.
4. Confirm the app still boots with both variables **unset**.
5. Report what you changed, and state explicitly where `shipper.close` runs.

If anything is ambiguous — the Rails version, how logging is configured, where
initializers live — inspect the repository and follow what is already there. Do
not restructure the application to fit the SDK.