Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 26 additions & 12 deletions docs/platforms/elixir/configuration/options.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,24 @@ The level the SDK logs at when it fails to send data to Sentry, such as an API f

These options can be used to hook the SDK in various ways to customize the reporting of events.

If a hook raises, throws, or exits, the SDK catches the failure and logs it at the `:error` level instead of letting it reach your code. Each option below describes what happens to the event in that case. See <PlatformLink to="/configuration/sdk-diagnostics/">SDK Diagnostics</PlatformLink> for how these messages are logged.

<SdkOption name="before_send" type='function'>

This function is called with an SDK-specific message or error event object, and can return a modified event object, or `null` to skip reporting the event. This can be used, for instance, for manual PII stripping before sending.
This function is called with an SDK-specific message or error event object, and can return a modified event object, or `nil` or `false` to skip reporting the event. This can be used, for instance, for manual PII stripping before sending.

By the time <PlatformIdentifier name="before_send" /> is executed, all scope data has already been applied to the event. Further modification of the scope won't have any effect.

If the function crashes, the event isn't sent, as if it had returned `false`.

</SdkOption>

<SdkOption name="after_send_event" type='function'>

This function is called with an event and the result of sending that event. This is useful to log send results, instrument Sentry calls, and so on.

With the default buffered delivery, this function runs after the event is queued and doesn't wait for delivery to complete. Its return value is ignored, so a crash doesn't change the send result.

</SdkOption>

## Transport Options
Expand Down Expand Up @@ -151,26 +157,34 @@ config :sentry,

If both `:traces_sampler` and `:traces_sample_rate` are configured, `:traces_sampler` takes precedence.

</SdkOption>
If the function crashes, sampling falls back to `:traces_sample_rate`. If `:traces_sample_rate` isn't set either, the trace is dropped. If the function returns something other than a boolean or a float between `0.0` and `1.0`, the SDK logs a warning and drops the trace.

## Telemetry Processor Options
</SdkOption>

The Telemetry Processor is a buffered, priority-based event delivery mechanism. It batches events and sends them to Sentry using a weighted scheduler that prioritizes critical events (like errors) over lower-priority ones (like logs). Logs are routed through the Telemetry Processor by default. You can opt in to route other event types through the processor as well. In a future major release, the Telemetry Processor will become the default delivery mechanism for all event types.
<SdkOption name="traces_ignore_http_status_codes" type='list' defaultValue='[404]' availableSince='14.0.0'>

<SdkOption name="telemetry_processor_categories" type='list' defaultValue='[:log]'>
HTTP response statuses to keep out of tracing. An incoming request answered with one of these statuses isn't reported as a transaction. Accepts status codes and ranges. Outgoing requests aren't affected.

A list of event categories to route through the Telemetry Processor. By default, only `:log` events use the processor. You can opt in to route additional event types through it.
The default is `[404]`, so _404 Not Found_ requests aren't traced. Set it to `[]` to trace them again:

Valid categories:
```elixir
config :sentry,
traces_ignore_http_status_codes: []
```

- `:error` — error events (critical priority)
- `:check_in` — cron check-in events (high priority)
- `:transaction` — transaction events (medium priority)
- `:log` — log events (low priority)
To ignore more statuses, list them as codes or ranges:

```elixir
config :sentry,
telemetry_processor_categories: [:error, :check_in, :transaction, :log]
traces_ignore_http_status_codes: [404, 500..599]
```

The SDK drops the transaction only once the response status is known, after the request has been handled. For sampled requests, the trace has already been propagated as sampled. Downstream services can still report spans, which appear in Sentry without their root transaction.

</SdkOption>

## Telemetry Processor Options

The Telemetry Processor batches events and sends them to Sentry, prioritizing errors over lower-priority events such as logs. It buffers logs and metrics, as well as errors, check-ins, and transactions by default.

When your application shuts down gracefully, the SDK flushes buffered telemetry before it stops, waiting up to five seconds. To flush earlier or with a different timeout, call `Sentry.flush/1`.
102 changes: 69 additions & 33 deletions docs/platforms/elixir/data-management/data-collected.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,85 +8,115 @@ Sentry takes data privacy very seriously and has default settings in place that

The category types and amount of data collected vary, depending on the integrations you've enabled in the Sentry SDK. Here's a list of data categories the Sentry Elixir SDK collects:

## HTTP Headers
## Request Data Scrubbing

When using the Plug or Phoenix integrations, `Sentry.PlugContext` adds request data to error reports and scrubs it first. Starting with SDK version 14.0.0, scrubbed values are replaced with `"[Filtered]"`. Earlier versions use `"*********"`.

A parameter is considered sensitive when its name contains one of the following terms, compared case-insensitively. For example, `auth` matches both `Authorization` and `user_auth_code`:

`auth`, `token`, `secret`, `password`, `passwd`, `pwd`, `key`, `jwt`, `bearer`, `sso`, `saml`, `csrf`, `xsrf`, `credentials`, `session`, `sid`, `identity`

When using the Plug or Phoenix integrations, HTTP headers from requests are included in error reports with built-in filtering for security:
Values that look like credit card numbers (13 to 16 digits, optionally separated by spaces or dashes) are also replaced with `"[Filtered]"`, regardless of their parameter name.

The `:header_scrubber`, `:cookie_scrubber`, `:remote_address_reader`, `:url_scrubber`, and `:body_scrubber` options below are passed to `Sentry.PlugContext` in your endpoint or router, not set with `config :sentry`. If one of these callbacks raises an exception, the SDK logs the failure at the `:error` level and uses its default for that field instead. This means that data only your custom scrubber was removing is sent to Sentry for as long as the scrubber keeps failing.

## HTTP Headers

**Default Behavior:**

- Most request headers are included by default
- Sensitive headers are automatically scrubbed, including:
- `authorization`
- `authentication`
- `cookie`
- Request headers are included
- The `authorization`, `authentication`, and `cookie` headers are removed
- The `referer` header is kept, but sensitive query parameters in its URL are replaced with `"[Filtered]"`

**Configuration:**

```elixir
# Custom header filtering
config :sentry,
header_scrubber: &MyApp.CustomHeaderScrubber.scrub/1
plug Sentry.PlugContext, header_scrubber: &MyApp.SentryScrubber.scrub_headers/1
```

## Cookies

**Default Behavior:**
- **All cookies are scrubbed by default** for privacy protection
- No cookie data is sent to Sentry unless explicitly configured

- **All cookies are removed by default** for privacy protection
- No cookie data is sent to Sentry unless you configure a custom cookie scrubber

**Configuration:**

```elixir
# Enable cookie collection with custom scrubbing
config :sentry,
cookie_scrubber: &MyApp.CustomCookieScrubber.scrub/1
plug Sentry.PlugContext, cookie_scrubber: &MyApp.SentryScrubber.scrub_cookies/1
```

## Users' IP Address

**Default Behavior:**

- Client IP addresses are collected from HTTP requests
- Prioritizes `x-forwarded-for` header, falls back to `conn.remote_ip`
- The SDK reads the `x-forwarded-for` header first and falls back to `conn.remote_ip`

**Configuration:**

```elixir
# Custom IP address extraction
config :sentry,
remote_address_reader: {MyModule, :get_ip_address}
plug Sentry.PlugContext, remote_address_reader: {MyApp.RemoteIp, :read}
```

## Request URL

**Default Behavior:**
- Full request URLs are always sent, including query strings
- URLs may contain PII depending on your application's routing structure

- The full request URL is sent, including the query string
- Values of sensitive query parameters are replaced with `"[Filtered]"`, for example, `/search?token=abc123&page=2` is sent as `/search?token=[Filtered]&page=2`
- All other query parameters are sent exactly as the client sent them
- The URL path isn't scrubbed, so a secret in a path segment, such as `/reset/<token>`, is sent unless you add a custom URL scrubber

**Configuration:**

```elixir
# Custom URL scrubbing
config :sentry,
url_scrubber: &MyApp.UrlScrubber.scrub/1
# Use a custom URL scrubber
plug Sentry.PlugContext, url_scrubber: &MyApp.SentryScrubber.scrub_url/1

# Or turn off URL scrubbing entirely
plug Sentry.PlugContext, url_scrubber: nil
```

## Request Body
## Request Parameters

**Default Behavior:**
- Request body parameters are included with automatic scrubbing
- Sensitive parameters are filtered by default:
- `password`
- `passwd`
- `secret`
- Credit card numbers (detected via regex pattern)

- Request body and query parameters are included, and so are path parameters when `Sentry.PlugContext` runs after routing, such as in your router
- Values of sensitive parameters are replaced with `"[Filtered]"`, including in nested maps and lists

**Configuration:**

```elixir
# Use a custom body scrubber
plug Sentry.PlugContext, body_scrubber: &MyApp.SentryScrubber.scrub_params/1

# Or don't send any request parameters
plug Sentry.PlugContext, body_scrubber: nil
```

## Adding Sensitive Parameter Names

<AvailableSince version="14.0.0" />

To redact more parameters, add terms to the `:param_keys` list of the `:scrubber` option:

```elixir
# Custom body parameter filtering
config :sentry,
body_scrubber: &MyApp.BodyScrubber.scrub/1
scrubber: [param_keys: ["internal_ref"]]
```

The terms extend the default list and can't remove terms from it. They're matched the same way as the default terms, as case-insensitive substrings, so `"internal_ref"` also redacts `"INTERNAL_REF_ID"`. The additional terms apply everywhere the SDK scrubs parameters:

- Request parameters and the query string of the request URL
- The query string of the `referer` header URL
- `Sentry.LiveViewHook` breadcrumb data
- Oban job arguments
- Function arguments captured in stack traces

They don't change which headers are removed.

## Source Context

**Default Behavior:**
Expand All @@ -107,8 +137,10 @@ config :sentry,
## Local Variables In Stack Trace

**Default Behavior:**

- Local variables are not included in stack traces
- Stack traces contain function names, function variables, modules, file paths, and line numbers only
- Values of sensitive keys in captured function arguments are replaced with `"[Filtered]"`

**Note:** Unlike some other SDKs, the Elixir SDK does not currently support capturing local variables due to the nature of the Erlang VM.

Expand Down Expand Up @@ -138,7 +170,9 @@ config :logger, :sentry,
```

<Alert>

There's always risk that PII will leak into Sentry via Logger integration. It is recommended to review your log metadata and scrub any sensitive information before logging.

</Alert>

## Application Dependencies
Expand All @@ -161,11 +195,13 @@ config :sentry,
When using the Oban integration for background jobs:

**Job Information:**

- Job arguments, attempt count, queue name
- Worker class name
- Job metadata and tags
- Max attempts and current state

Values of sensitive keys in job arguments are replaced with `"[Filtered]"`.

## More information

Expand Down
1 change: 1 addition & 0 deletions docs/platforms/elixir/migration.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ The migration guides for older versions of the Elixir SDK are available over on
- [Upgrade to Sentry 8.x](https://hexdocs.pm/sentry/upgrade-8-x.html)
- [Upgrade to Sentry 9.x](https://hexdocs.pm/sentry/upgrade-9-x.html)
- [Upgrade to Sentry 10.x](https://hexdocs.pm/sentry/upgrade-10-x.html)
- [Upgrade to Sentry 14.x](https://hexdocs.pm/sentry/upgrade-14-x.html)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This link leads to a 404.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@coolguyzone this will be reachable once 14.0 is released, so it will fix itself here.

89 changes: 86 additions & 3 deletions docs/platforms/elixir/testing/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,19 @@ Add `bypass` as a test dependency in `mix.exs`:
```elixir {filename:mix.exs}
defp deps do
[
{:sentry, "~> 13.0"},
{:sentry, "~> 14.0"},
{:bypass, "~> 2.0", only: [:test]},
# ...
]
end
```

Turn on test mode in your test configuration. It starts the registry the test helpers depend on and isolates Sentry configuration per test:

```elixir {filename:config/test.exs}
config :sentry, test_mode: true
```

Call `Sentry.Test.setup_sentry/1` in your ExUnit `setup` block. It opens a local HTTP server scoped to the current test, points Sentry's DSN at it, and wires up event collection:

```elixir
Expand All @@ -50,6 +56,12 @@ setup do
end
```

`setup_sentry/1` returns a map that ExUnit merges into the test context. It contains:

- `:bypass` — the local HTTP server that receives this test's envelopes
- `:telemetry_processor` — the name of this test's telemetry processor
- `:client_report_sender` — the name of this test's client report sender, so counts of discarded events don't leak between tests (SDK 14.0.0 and later)

## Errors

Use `assert_sentry_report(:event, criteria)` to assert on captured errors and messages. It fails if any criterion doesn't match or if not exactly one event was captured.
Expand Down Expand Up @@ -272,7 +284,9 @@ end

## Check-ins

Cron check-ins are delivered directly over the network, not through the ETS collector. Use `setup_bypass_envelope_collector/2` to intercept them, then assert with `assert_sentry_report/2`. Given an Oban worker that wraps its job in a check-in:
Cron check-ins aren't stored in the per-test collector that the other helpers read from. The SDK sends them to the test's local HTTP server, the same way it sends them to Sentry.

Use `setup_bypass_envelope_collector/2` to intercept them, then assert with `assert_sentry_report/2`. Given an Oban worker that wraps its job in a check-in:

```elixir {filename:lib/my_app/workers/nightly_report_worker.ex}
defmodule MyApp.Workers.NightlyReportWorker do
Expand Down Expand Up @@ -320,7 +334,76 @@ defmodule MyApp.Workers.NightlyReportWorkerTest do
end
```

The `%{bypass: bypass}` map is returned by `setup_sentry/1` and merged into the test context automatically.
### Collecting Several Envelope Types

<AvailableSince version="14.0.0" />

The `:type` option also accepts a list. Use it when one action sends several kinds of envelopes that you want to assert on together. Given a job that reports its own failures:

```elixir {filename:lib/my_app/reports.ex}
def generate_nightly(date) do
{:ok, check_in_id} =
Sentry.capture_check_in(status: :in_progress, monitor_slug: "nightly-report")

try do
build_report!(date)
Sentry.capture_check_in(status: :ok, monitor_slug: "nightly-report", check_in_id: check_in_id)
rescue
exception ->
Sentry.capture_exception(exception, stacktrace: __STACKTRACE__)
Sentry.capture_check_in(status: :error, monitor_slug: "nightly-report", check_in_id: check_in_id)
end
end
```

The test collects the error and both check-ins, then splits them by type with `extract_events/1` and `extract_check_ins/1`:

```elixir {filename:test/my_app/reports_test.exs}
test "reports the exception and an error check-in", %{bypass: bypass} do
ref = Sentry.Test.setup_bypass_envelope_collector(bypass, type: ["event", "check_in"])

Reports.generate_nightly(:invalid_date)

envelopes = Sentry.Test.collect_envelopes(ref, 3)

[event] = Sentry.Test.extract_events(envelopes)
assert_sentry_report(event, level: "error")

[_started, finished] = Sentry.Test.extract_check_ins(envelopes)
assert_sentry_report(finished, status: "error", monitor_slug: "nightly-report")
end
```

## Failed Deliveries

<AvailableSince version="14.0.0" />

By default, the collector answers every envelope with a successful response. Pass a `:response` function to `setup_bypass_envelope_collector/2` to simulate a failed response from Sentry instead.

The function receives the `Plug.Conn` and the raw envelope body, and must return the response connection. The collector still records the envelope before the function runs.

This test simulates Sentry rejecting the envelope and checks that a synchronous capture returns an error:

```elixir {filename:test/my_app/error_reporting_test.exs}
test "returns an error when Sentry rejects the event", %{bypass: bypass} do
ref =
Sentry.Test.setup_bypass_envelope_collector(bypass,
response: fn conn, _body -> Plug.Conn.resp(conn, 413, "") end
)

assert {:error, %Sentry.ClientError{reason: :envelope_too_large}} =
Sentry.capture_message("Payment failed", result: :sync)

[event] = Sentry.Test.collect_sentry_events(ref, 1)
assert_sentry_report(event, message: %{formatted: "Payment failed"})
end
```

<Alert level="warning">

**Don't simulate a `429` response.** The SDK treats it as a rate limit that applies to the whole test run, not only the current test. It stops sending data for 60 seconds, or for the `Retry-After` duration, so later tests stop receiving envelopes. The SDK retries most other error statuses, such as `503`, and waits between attempts, which makes each of those tests take about 15 seconds.

</Alert>

## Structured Assertions

Expand Down
Loading
Loading