|
| 1 | +--- |
| 2 | +alwaysApply: false |
| 3 | +description: Adding and modifying SDK options |
| 4 | +--- |
| 5 | +# Adding Options to the SDK |
| 6 | + |
| 7 | +New features must be **opt-in by default**. Options control whether a feature is enabled and how it behaves. |
| 8 | + |
| 9 | +## Namespaced Options |
| 10 | + |
| 11 | +Newer features use namespaced option classes nested inside `SentryOptions`, e.g.: |
| 12 | +- `SentryOptions.getLogs()` → `SentryOptions.Logs` |
| 13 | +- `SentryOptions.getMetrics()` → `SentryOptions.Metrics` |
| 14 | + |
| 15 | +Each namespaced options class is a `public static final class` inside `SentryOptions` with its own fields, getters/setters, and callbacks (e.g. `BeforeSendLogCallback`, `BeforeSendMetricCallback`). |
| 16 | + |
| 17 | +A typical namespaced options class contains: |
| 18 | +- `enabled` boolean (default `false` for opt-in) |
| 19 | +- `sampleRate` double (if the feature supports sampling) |
| 20 | +- `beforeSend` callback interface (nested inside the options class) |
| 21 | + |
| 22 | +To add a new namespaced options class: |
| 23 | +1. Create the `public static final class` inside `SentryOptions` with fields, getters/setters, and any callback interfaces |
| 24 | +2. Add a private field on `SentryOptions` initialized with `new SentryOptions.MyFeature()` |
| 25 | +3. Add getter/setter on `SentryOptions` annotated with `@ApiStatus.Experimental` |
| 26 | + |
| 27 | +## Direct (Non-Namespaced) Options |
| 28 | + |
| 29 | +Options that apply globally across the SDK (e.g. `dsn`, `environment`, `release`, `sampleRate`, `maxBreadcrumbs`) live as direct fields on `SentryOptions` with getter/setter pairs. Use this pattern for options that aren't tied to a specific feature namespace. |
| 30 | + |
| 31 | +## Configuration Layers |
| 32 | + |
| 33 | +Options can be set through multiple layers. When adding a new option, consider which layers apply: |
| 34 | + |
| 35 | +### 1. SentryOptions (always required) |
| 36 | + |
| 37 | +The core options class. Add the field (or nested class) with getter/setter here. |
| 38 | + |
| 39 | +**File:** `sentry/src/main/java/io/sentry/SentryOptions.java` |
| 40 | + |
| 41 | +**Tests:** `sentry/src/test/java/io/sentry/SentryOptionsTest.kt` |
| 42 | +- Test the default value |
| 43 | +- Test merge behavior (see layer 2) |
| 44 | + |
| 45 | +### 2. ExternalOptions (sentry.properties / environment variables) |
| 46 | + |
| 47 | +Allows setting options via `sentry.properties` file or system properties. Fields use nullable wrapper types (`@Nullable Boolean`, `@Nullable Double`) since unset means "don't override the default." |
| 48 | + |
| 49 | +**File:** `sentry/src/main/java/io/sentry/ExternalOptions.java` |
| 50 | +- Add `@Nullable` fields with getter/setter for each externally configurable option (e.g. `enableMetrics`, `logsSampleRate`) |
| 51 | +- Wire them in the static `from(PropertiesProvider)` method: |
| 52 | + - Boolean: `propertiesProvider.getBooleanProperty("metrics.enabled")` |
| 53 | + - Double: `propertiesProvider.getDoubleProperty("logs.sample-rate")` |
| 54 | + |
| 55 | +**File:** `sentry/src/main/java/io/sentry/SentryOptions.java` — `merge()` method |
| 56 | +- Add null-check blocks to apply each external option onto the namespaced options class: |
| 57 | + ```java |
| 58 | + if (options.isEnableMetrics() != null) { |
| 59 | + getMetrics().setEnabled(options.isEnableMetrics()); |
| 60 | + } |
| 61 | + if (options.getLogsSampleRate() != null) { |
| 62 | + getLogs().setSampleRate(options.getLogsSampleRate()); |
| 63 | + } |
| 64 | + ``` |
| 65 | + |
| 66 | +**Tests:** |
| 67 | +- `sentry/src/test/java/io/sentry/ExternalOptionsTest.kt` — test true/false/null for booleans, valid values and null for doubles |
| 68 | +- `sentry/src/test/java/io/sentry/SentryOptionsTest.kt` — test merge applies values and test merge preserves defaults when unset |
| 69 | + |
| 70 | +### 3. Android Manifest Metadata (Android only) |
| 71 | + |
| 72 | +Allows setting options via `AndroidManifest.xml` `<meta-data>` tags. |
| 73 | + |
| 74 | +**File:** `sentry-android-core/src/main/java/io/sentry/android/core/ManifestMetadataReader.java` |
| 75 | +- Add a `static final String` constant for the key (e.g. `"io.sentry.metrics.enabled"`) |
| 76 | +- Read it in `applyMetadata()` using `readBool(metadata, logger, CONSTANT, defaultValue)` |
| 77 | +- Apply to the namespaced options, e.g. `options.getMetrics().setEnabled(...)` |
| 78 | + |
| 79 | +**Tests:** `sentry-android-core/src/test/java/io/sentry/android/core/ManifestMetadataReaderTest.kt` |
| 80 | +- Test default value preserved when not in manifest |
| 81 | +- Test explicit true |
| 82 | +- Test explicit false |
| 83 | + |
| 84 | +### 4. Spring Boot Properties (Spring Boot only) |
| 85 | + |
| 86 | +`SentryProperties` extends `SentryOptions`, so namespaced options (nested classes) are automatically available as Spring Boot properties without extra code. For example, `SentryOptions.Logs` is automatically mapped to `sentry.logs.enabled` in `application.properties`. |
| 87 | + |
| 88 | +No additional code is needed for namespaced options — Spring Boot auto-configuration handles this via property binding on the `SentryOptions` class hierarchy. |
| 89 | + |
| 90 | +**Tests:** `sentry-spring-boot*/src/test/kotlin/.../SentryAutoConfigurationTest.kt` |
| 91 | +- Add the property (e.g. `"sentry.logs.enabled=true"`) to the existing `resolves all properties` test |
| 92 | +- Assert the value is set on the resolved `SentryProperties` bean |
| 93 | +- There are three Spring Boot modules with separate test files: `sentry-spring-boot`, `sentry-spring-boot-jakarta`, `sentry-spring-boot-4` |
| 94 | + |
| 95 | +### 5. Reading Options at Runtime |
| 96 | + |
| 97 | +Features check their options at usage time. For namespaced features the check typically happens in the feature's API class (e.g. `LoggerApi`, `MetricsApi`): |
| 98 | +- Check `options.getLogs().isEnabled()` early and return if disabled |
| 99 | +- Apply sampling via `options.getLogs().getSampleRate()` if applicable |
| 100 | +- Apply `beforeSend` callback in `SentryClient` before sending |
| 101 | + |
| 102 | +When a feature has its own capture path (e.g. `captureLog`), the relevant classes are: |
| 103 | +- `ISentryClient` — add the capture method signature |
| 104 | +- `SentryClient` — implement capture, including `beforeSend` callback execution |
| 105 | +- `NoOpSentryClient` — add no-op stub |
| 106 | + |
| 107 | +## Checklist for Adding a New Namespaced Option |
| 108 | + |
| 109 | +1. `SentryOptions.java` — nested options class + getter/setter on `SentryOptions` |
| 110 | +2. `ExternalOptions.java` — `@Nullable` fields + wiring in `from()` |
| 111 | +3. `SentryOptions.java` `merge()` — apply external options to namespaced class |
| 112 | +4. `ManifestMetadataReader.java` — Android manifest support (if Android-relevant) |
| 113 | +5. `SentryAutoConfigurationTest.kt` — Spring Boot property binding tests (all three Spring Boot modules) |
| 114 | +6. Tests for all of the above (`SentryOptionsTest`, `ExternalOptionsTest`, `ManifestMetadataReaderTest`) |
| 115 | +7. Run `./gradlew apiDump` — the nested class and its methods appear in `sentry.api` |
0 commit comments