# 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.
