Ruby / Rails
Broadcast your Rails logger and insert one Rack middleware — which re-raises, so rescue_from and your error pages still run.
Before you start
- Where your InsightRecorder runs
- The base URL every call goes to —
http://localhost:8080when you are running it locally, your own hostname otherwise. - An API key
- Settings → API keys, scoped to
logs:writeandbug: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.
- 01
Install
HTTP is net/http; no runtime dependencies.
gem "insight_recorder" - 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 } - 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)) ) - 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" - 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 upNothing 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.
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.