# Sentry

If you already use Sentry, you can send Packrat a copy of the same data with a few lines of
configuration. Sentry keeps receiving everything, and Packrat gets a copy. This is called
dual-shipping.

You can dual-ship from two places:

- [Your Rails server](#on-your-rails-server), through `sentry-ruby`.
- [Your pages](#in-the-browser), through Sentry's browser SDK, `@sentry/browser`.

For a server in another language, use [OpenTelemetry](https://packrat.sh/docs/opentelemetry)
instead.

## Before you begin

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
   ```

## On your Rails server

If your app runs `sentry-ruby` and `sentry-rails`, add a transport to your Sentry initializer. The
transport sends each event to Sentry, then sends a copy to Packrat. Packrat receives your
exceptions and your request and job traces.

If your app doesn't use Sentry yet, don't install it for Packrat. Follow the
[OpenTelemetry guide for Ruby on Rails](https://packrat.sh/docs/opentelemetry?framework=rails)
instead.

### Dual-ship from sentry-ruby

1. Set `PACKRAT_SENTRY_DSN` where your app runs. It's made from your ingest key and your app's name
   in Packrat:

   ```sh
   PACKRAT_SENTRY_DSN=https://<your ingest key>@ingest.packrat.sh/<your app>
   ```

2. Add the transport to `config/initializers/sentry.rb`, and set it in `Sentry.init`:

   ```ruby
   # config/initializers/sentry.rb

   # Sends Packrat a copy of each envelope.
   module PackratSentry
     def self.transport
       @transport ||= begin
         config = Sentry.configuration.dup
         config.dsn = ENV["PACKRAT_SENTRY_DSN"]
         Sentry::HTTPTransport.new(config)
       end
     end
   end

   class PackratDualTransport < Sentry::HTTPTransport
     def send_data(data)
       super
     ensure
       begin
         PackratSentry.transport.send_data(data)
       rescue => e
         Sentry.sdk_logger.warn("[packrat] copy failed: #{e.class}: #{e.message}")
       end
     end
   end

   Sentry.init do |config|
     config.dsn = ENV["SENTRY_DSN"]
     config.release = ENV["GIT_COMMIT"] # the commit you deployed
     config.environment = Rails.env
     config.transport.transport_class = PackratDualTransport if ENV["PACKRAT_SENTRY_DSN"].present?
   end
   ```

3. Deploy, or restart your app.

Sentry still receives each event first. If Packrat is unreachable, the copy fails and Sentry logs a
warning.

### Send to Packrat only

If you don't use Sentry, set Packrat's DSN as the only DSN in `config/initializers/sentry.rb`:

```ruby
# config/initializers/sentry.rb
Sentry.init do |config|
  config.dsn = ENV["PACKRAT_SENTRY_DSN"]
  config.release = ENV["GIT_COMMIT"] # the commit you deployed
  config.environment = Rails.env
  config.traces_sample_rate = 1.0
end
```

### Verify your server

Make a request to your app and wait about a minute. Then list the server's latest spans:

```sql
SELECT name, kind, json_extract_string(span_attributes, '$."sentry.op"') AS op, duration_ms
FROM packrat_spans(since := now()::TIMESTAMP - INTERVAL 10 MINUTE)
WHERE scope_name LIKE 'sentry.ruby%'
ORDER BY start_time DESC LIMIT 20
```

A request's first span has a `kind` of 2, and a job's has 5. Once the hour closes, `packrat_traces`
lists the requests and jobs themselves.

## In the browser

Sentry's browser SDK can send Packrat your page loads, Web Vitals, JavaScript errors and session
replays. Packrat stores them in the same app as your server's data.

If your pages don't run the SDK yet, install it with
[Sentry's JavaScript guide](https://docs.sentry.io/platforms/javascript/), then follow
[Send to Packrat only](#send-to-packrat-only-1).

### Create a browser ingest key

The key goes in your page's source, where anyone can read it. On your org's **Keys** page, create
an ingest key for the browser only. You can then revoke it without affecting your servers.

### Dual-ship from the browser SDK

Give `Sentry.init` a transport that sends each envelope to Sentry and a copy to Packrat. Replace
`<your app>` with the name your server's app has in Packrat, so the browser's data lands in the same
app, and set `environment` to the app's environment, such as `production` or `staging`.

```js
const PACKRAT =
  "https://ingest.packrat.sh/api/<your app>/envelope/?sentry_version=7&sentry_key=<browser ingest key>&environment=production";

Sentry.init({
  dsn: "<your Sentry DSN>",
  release: "<your deploy's commit SHA>",
  integrations: [Sentry.browserTracingIntegration()],
  tracesSampleRate: 1.0,
  transport: (options) => {
    const sentry = Sentry.makeFetchTransport(options);
    const packrat = Sentry.makeFetchTransport({ ...options, url: PACKRAT });
    return {
      send: (envelope) => {
        packrat.send(envelope);
        return sentry.send(envelope);
      },
      flush: (timeout) =>
        Promise.all([sentry.flush(timeout), packrat.flush(timeout)]).then(
          (done) => done.every(Boolean),
        ),
    };
  },
});
```

### Send to Packrat only

If you don't use Sentry, or no longer want the browser to report to it, use Packrat's DSN and send
every envelope to Packrat:

```js
const PACKRAT =
  "https://ingest.packrat.sh/api/<your app>/envelope/?sentry_version=7&sentry_key=<browser ingest key>&environment=production";

Sentry.init({
  dsn: "https://<browser ingest key>@ingest.packrat.sh/<your app>",
  release: "<your deploy's commit SHA>",
  integrations: [Sentry.browserTracingIntegration()],
  tracesSampleRate: 1.0,
  transport: (options) =>
    Sentry.makeFetchTransport({ ...options, url: PACKRAT }),
});
```

### Turn on session replays

To record sessions, add `Sentry.replayIntegration()` and set how many sessions to record. With
either transport above, Packrat receives the replays too:

```js
Sentry.init({
  // ...as above
  integrations: [Sentry.browserTracingIntegration(), Sentry.replayIntegration()],
  replaysSessionSampleRate: 0.1, // record 10% of sessions
  replaysOnErrorSampleRate: 1.0, // and every session with an error
});
```

Each app's **Replays** page lists the sessions, and you can query them with `packrat_replays` and
`packrat_replay_events`. The [API reference](https://packrat.sh/docs/api) lists the columns.

### Verify the browser SDK

Load a page, then check what Packrat receives. The browser shows up as the source `Sentry browser`
under traces, and under replays and exceptions once it sends them.

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

After about a minute, you can query the browser's spans:

```sql
SELECT name, json_extract_string(span_attributes, '$."sentry.op"') AS op, duration_ms
FROM packrat_spans(since := now()::TIMESTAMP - INTERVAL 10 MINUTE)
WHERE scope_name LIKE 'sentry.javascript.%'
ORDER BY start_time DESC LIMIT 20
```

---

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