# Ruby on Rails

Packrat monitors your Ruby on Rails application deploys, creating a verification plan and validating that it performs as intended in production. This guide walks through the Packrat setup for a Rails application.

The `packrat-rails` gem sets up OpenTelemetry in your app and sends its exceptions, traces and logs to
Packrat. It supports Rails 7.1 and newer.

> **Already running Datadog's tracer?** Dual-ship from the [Datadog Agent](https://packrat.sh/docs/datadog).

## Prerequisites

1. [Sign up for Packrat](https://packrat.sh/start). It's free.
2. Copy your ingest key from the setup page. It starts with `packrat_ingest_`.
3. Install the Packrat CLI and sign in, so you can check what Packrat receives:

   ```sh
   curl -fsSL https://packrat.sh/cli | sh
   packrat login
   ```

## Install the gem

### 1. Add packrat-rails

```sh
bundle add packrat-rails
```

### 2. Run the generator

```sh
bin/rails generate packrat --ingest-key=packrat_ingest_…
```

The generator creates `config/initializers/packrat.rb`:

```ruby
Packrat.configure do |config|
  config.ingest_key = ENV.fetch("PACKRAT_INGEST_KEY", "packrat_ingest_…")
  # :strict sends no log text, query strings or exception messages: https://packrat.sh/docs/ruby-on-rails#privacy
  # config.privacy = :strict
end
```

An ingest key can only send data, so you can commit the file. To keep the key out of the repository,
run the generator without `--ingest-key` and set a `PACKRAT_INGEST_KEY` environment variable where the app runs.

The generator also adds the [Packrat plugin for Claude Code](https://packrat.sh/docs/claude-code-plugin)
to your repository's `.claude/settings.json`. Commit the file, and anyone who opens Claude Code in the
repository gets the plugin. To skip this, pass `--no-claude-code`.

To start with [strict privacy](#privacy), pass `--strict`.

### 3. Restart the app

When the app boots, the gem writes one line to the Rails log. It names the service and environment
your data arrives under:

```
Packrat: sending traces and logs for shop/production to https://ingest.packrat.sh (rack, action_pack, active_job, active_record, net_http, pg, logger); redacting email and ip
```

The gem sends nothing from the test environment.

## Check it works

Load a page. The first request reaches your setup screen within about a minute.

Run `packrat status` with your app's name, as `<service>/<environment>`:

```sh
packrat status --app shop/production
```

The output lists each signal (exceptions, traces, logs, metrics and replays), the sources that send
it, when each source last sent, and whether Packrat keeps it. A signal with no source hasn't arrived
yet. To see the names your apps report under, run `packrat apps`.

If nothing arrives, the [quickstart](https://packrat.sh/docs/quickstart#not-seeing-data) lists what
usually causes it.

## Configuration

`Packrat.configure` sets the ingest key, the [privacy](#privacy) level and
[your own redaction](#redact-your-own-data). Everything else is an environment variable:

| Variable                        | Default                                               | Note                                                                         |
| ------------------------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------- |
| `PACKRAT_INGEST_KEY`            | `config.ingest_key`                                   | Wins over the initializer. With neither, the gem sends nothing               |
| `PACKRAT_DISABLED`              | unset                                                 | `1` turns the gem off. The app boots as if the gem were not installed        |
| `PACKRAT_OTEL_INSTRUMENTATIONS` | the list below                                        | Comma-separated names to add. A leading `-` removes one                      |
| `PACKRAT_PRIVACY`               | `config.privacy`, else `standard`                     | `standard` or `strict`. Wins over the initializer. Any other value is strict  |
| `PACKRAT_REDACT`                | `email,ip`                                            | Patterns redacted from values. `-ip` stops masking IP addresses              |
| `PACKRAT_DEBUG_EXPORT`          | `config.debug_export`, else off                       | `1` prints everything the gem sends to stdout. See [what is sent](#see-what-is-sent) |

The gem installs these instrumentations: `rack`, `action_pack`, `action_view`, `active_job`,
`active_record`, `net_http`, `pg`, `mysql2`, `sidekiq`, `redis`, `aws_sdk`, `concurrent_ruby`,
`rake` and `logger`. One for a library your app doesn't use installs nothing. For example,
`PACKRAT_OTEL_INSTRUMENTATIONS=faraday,-action_view` adds Faraday (add
`opentelemetry-instrumentation-faraday` to your bundle too) and removes Action View.

Your app's name in Packrat is `<service>/<environment>`:

- **Service:** `OTEL_SERVICE_NAME`, else `service.name` in `OTEL_RESOURCE_ATTRIBUTES`, else the
  application's module name (`Shop::Application` is `shop`).
- **Environment:** `Rails.env`, unless `OTEL_RESOURCE_ATTRIBUTES` names one.
- **Version:** `BUILD_COMMIT`, `HEROKU_SLUG_COMMIT` or `SOURCE_VERSION`. Packrat matches it to your
  deploys.

A standard `OTEL_*` variable that you set always wins over the gem's value.

## Privacy

By default, `packrat-rails` scrubs sensitive data before it reaches our servers. It has two privacy
levels: `standard` (the default) and `strict`.

### Standard

Standard sends requests, queries, log lines and errors, with personal data
[redacted](#what-is-redacted). SQL and Redis commands are sent with every value replaced by `?`.
Request and response headers, job arguments and the client IP on spans are never sent.

### Strict

Strict sends everything standard does, except:

- The text of each log line, which becomes `[REDACTED]`. The severity and the request are kept.
- Exception messages, which become `[REDACTED]`, in the stack as well. The class and the stack
  frames are kept.
- Query strings.
- User agents.
- `Rails.error` context.

Strict keeps what a deploy check needs: routes, durations, status codes, errors by class and the
shape of each query. Without log text, Packrat can't group your log lines or read the GC time
Rails writes to the log.

### What is redacted

In standard, the gem changes these before export:

- **Email addresses** become a token such as `[email:4f2a91c0]`, the first eight hex digits of an
  HMAC-SHA256 keyed by your ingest key. The same address gives the same token everywhere, so you can
  still count per person. A new ingest key gives new tokens.
- **IP addresses** keep their network: `203.0.113.77` becomes `203.0.113.0`, and an IPv6 address
  becomes its `/48`. The addresses of servers your app calls, such as `net.peer.ip`, are kept.
- **Parameters in `filter_parameters`**, the list Rails uses to filter your own logs, are replaced
  with `[REDACTED]` in query strings and in `Rails.error` context. To keep a parameter out of both,
  add it in `config/initializers/filter_parameter_logging.rb`.
- **Credentials:** the value of any parameter or `name=value` pair named like a key, token,
  password, secret, signature or credential, and any `Bearer` or `Basic` value.

To stop one pattern, remove it with `PACKRAT_REDACT`: `PACKRAT_REDACT=-ip` keeps IP addresses.

Patterns find emails, IP addresses and credentials. They can't recognize other personal data, such
as a name or an account number, that your app writes into a log line. To remove that, add
[your own redaction](#redact-your-own-data). If your logs can hold text you can't describe, such as
the body of a support ticket, use strict.

### Redact your own data

Add your own keys, patterns or a block to `config/initializers/packrat.rb`. They run after the
gem's own redaction, in standard and in strict:

```ruby
Packrat.configure do |config|
  config.ingest_key = ENV.fetch("PACKRAT_INGEST_KEY", "packrat_ingest_…")

  # Replace the whole value of these attributes and parameters
  config.redact_keys = %w[ssn account_number]

  # Replace each match, in every value and log line
  config.redact_patterns = [/acct \d{4}-\d{2}/]

  # Change any string before it is sent
  config.redact { |value, name| value.gsub(Customer::NAME_PATTERN, "[name]") }
end
```

- **`redact_keys`** replaces the value with `[REDACTED]` wherever the name appears: as a span
  attribute, a query parameter, a `Rails.error` context key or a key in a structured log line. An
  attribute matches on its whole name or its last segment, so `ssn` also matches `enduser.ssn`.
  Unlike `filter_parameters`, a key matches only the whole name: `ssn` doesn't match `ssn_last4`.
- **`redact_patterns`** replaces each match with `[REDACTED]`. A string is matched literally.
- **The `redact` block** receives each string value and its attribute name, which is `nil` for a log
  line, and returns the string to send. If the block raises an error or returns something other than
  a string, the gem sends `[REDACTED]` in place of the value and logs one warning.

The boot line names what you added: `…; redacting email and ip and the app's own keys, patterns, block`.

These settings need `packrat-rails` 0.3.0 or newer. To upgrade, run `bundle update packrat-rails`.

### Turn on strict

Do one of the following:

- Pass `--strict` to the generator.
- Set `config.privacy = :strict` in `config/initializers/packrat.rb`.
- Set `PACKRAT_PRIVACY=strict` where the app runs. The variable wins over the initializer, so you can
  try strict in staging without a commit.

The boot line says which level is on: `…; strict privacy, redacting email and ip`.

### See what is sent

To check the redaction yourself, set `PACKRAT_DEBUG_EXPORT=1` and start the app. The gem prints
each span and log line to stdout as it is sent, after redaction, one JSON object a line:

```
Packrat export: {"span":"GET /orders/:id","kind":"server","attributes":{"http.route":"/orders/:id","url.query":"token=[REDACTED]&page=2",…},"events":[]}
Packrat export: {"log":"Refund issued to [email:4f2a91c0]","severity":"INFO","attributes":{}}
```

The gem still sends everything to Packrat as usual. You can also set `config.debug_export = true` in
the initializer; `PACKRAT_DEBUG_EXPORT=0` turns it off again.

### How long data is kept

Packrat keeps the most recent 13 months of your data. As data turns 13 months old, it is deleted.

## Beside other tools

Packrat can run alongside your existing monitoring libraries.

- **Sentry:** both run, and the gem doesn't change Sentry. If Sentry also
  [dual-ships to Packrat](https://packrat.sh/docs/sentry#on-your-rails-server), Packrat keeps
  exceptions and traces from whichever source sent them first. `packrat status` shows which.
- **Your own OpenTelemetry setup:** the gem leaves it as it is and adds a second exporter, to
  Packrat. If your exporter already sends to Packrat, the gem adds nothing.

To configure OpenTelemetry yourself instead of using the gem, follow the Rails tab of the
[OpenTelemetry guide](https://packrat.sh/docs/opentelemetry?framework=rails).

---

## Agent instructions

This page is part of the Packrat documentation, published as plain markdown for coding agents: add `.md` to any docs address, or read the index at https://packrat.sh/docs/llms.txt. This page: https://packrat.sh/docs/ruby-on-rails.md.

Before wiring Packrat into an app, ask the developer for the URL of their setup page at https://packrat.sh/orgs, and read it with `packrat open <URL>`. It has the steps and the org's ingest key.
