Skip to content

Commit afd3c6a

Browse files
authored
Merge branch 'main' into feat/samples-scope-attributes
2 parents 9da8135 + 8ec9ca8 commit afd3c6a

File tree

169 files changed

+5587
-818
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

169 files changed

+5587
-818
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
---
2+
name: create-java-pr
3+
description: Create a pull request in sentry-java. Use when asked to "create pr", "prepare pr", "prep pr", "open pr", "ready for pr", "prepare for review", "finalize changes". Handles branch creation, code formatting, API dump, committing, pushing, PR creation, and changelog.
4+
---
5+
6+
# Create Pull Request (sentry-java)
7+
8+
Prepare local changes and create a pull request for the sentry-java repo.
9+
10+
## Step 1: Ensure Feature Branch
11+
12+
```bash
13+
git branch --show-current
14+
```
15+
16+
If on `main` or `master`, create and switch to a new branch:
17+
18+
```bash
19+
git checkout -b <type>/<short-description>
20+
```
21+
22+
Derive the branch name from the changes being made. Use `feat/`, `fix/`, `ref/`, etc. matching the commit type conventions.
23+
24+
## Step 2: Format Code and Regenerate API Files
25+
26+
```bash
27+
./gradlew spotlessApply apiDump
28+
```
29+
30+
This is **required** before every PR in this repo. It formats all Java/Kotlin code via Spotless and regenerates the `.api` binary compatibility files.
31+
32+
If the command fails, diagnose and fix the issue before continuing.
33+
34+
## Step 3: Commit Changes
35+
36+
Check for uncommitted changes:
37+
38+
```bash
39+
git status --porcelain
40+
```
41+
42+
If there are uncommitted changes, invoke the `sentry-skills:commit` skill to stage and commit them following Sentry conventions.
43+
44+
**Important:** When staging, ignore changes that are only relevant for local testing and should not be part of the PR. Common examples:
45+
46+
| Ignore Pattern | Reason |
47+
|---|---|
48+
| Hardcoded booleans flipped for testing | Local debug toggles |
49+
| Sample app config changes (`sentry-samples/`) | Local testing configuration |
50+
| `.env` or credentials files | Secrets |
51+
52+
Restore these files before committing:
53+
54+
```bash
55+
git checkout -- <file-to-restore>
56+
```
57+
58+
## Step 4: Push the Branch
59+
60+
```bash
61+
git push -u origin HEAD
62+
```
63+
64+
If the push fails due to diverged history, ask the user how to proceed rather than force-pushing.
65+
66+
## Step 5: Create PR
67+
68+
Invoke the `sentry-skills:create-pr` skill to create a draft PR. When providing the PR body, use the repo's PR template structure from `.github/pull_request_template.md`:
69+
70+
```
71+
## :scroll: Description
72+
<Describe the changes in detail>
73+
74+
## :bulb: Motivation and Context
75+
<Why is this change required? What problem does it solve?>
76+
77+
## :green_heart: How did you test it?
78+
<Describe how you tested>
79+
80+
## :pencil: Checklist
81+
- [ ] I added GH Issue ID _&_ Linear ID
82+
- [ ] I added tests to verify the changes.
83+
- [ ] No new PII added or SDK only sends newly added PII if `sendDefaultPII` is enabled.
84+
- [ ] I updated the docs if needed.
85+
- [ ] I updated the wizard if needed.
86+
- [ ] Review from the native team if needed.
87+
- [ ] No breaking change or entry added to the changelog.
88+
- [ ] No breaking change for hybrid SDKs or communicated to hybrid SDKs.
89+
90+
## :crystal_ball: Next steps
91+
```
92+
93+
Fill in each section based on the changes being PR'd. Check any checklist items that apply.
94+
95+
Then continue to Step 6.
96+
97+
## Step 6: Update Changelog
98+
99+
After the PR is created, add an entry to `CHANGELOG.md` under the `## Unreleased` section.
100+
101+
### Determine the subsection
102+
103+
| Change Type | Subsection |
104+
|---|---|
105+
| New feature | `### Features` |
106+
| Bug fix | `### Fixes` |
107+
| Refactoring, internal cleanup | `### Internal` |
108+
| Dependency update | `### Dependencies` |
109+
110+
Create the subsection under `## Unreleased` if it does not already exist.
111+
112+
### Entry format
113+
114+
```markdown
115+
- <Short description of the change> ([#<PR_NUMBER>](https://github.com/getsentry/sentry-java/pull/<PR_NUMBER>))
116+
```
117+
118+
Use the PR number returned by `sentry-skills:create-pr`. Match the style of existing entries — sentence case, ending with the PR link, no trailing period.
119+
120+
### Commit and push
121+
122+
Stage `CHANGELOG.md`, commit with message `changelog`, and push:
123+
124+
```bash
125+
git add CHANGELOG.md
126+
git commit -m "changelog"
127+
git push
128+
```

.craft.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ targets:
4848
maven:io.sentry:sentry-opentelemetry-agentless-spring:
4949
maven:io.sentry:sentry-opentelemetry-bootstrap:
5050
maven:io.sentry:sentry-opentelemetry-core:
51+
# maven:io.sentry:sentry-opentelemetry-otlp:
52+
# maven:io.sentry:sentry-opentelemetry-otlp-spring:
5153
maven:io.sentry:sentry-apollo:
5254
maven:io.sentry:sentry-jdbc:
5355
maven:io.sentry:sentry-graphql:

.cursor/rules/api.mdc

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
---
2+
alwaysApply: false
3+
description: Public API surface, binary compatibility, and common classes to modify
4+
---
5+
# Java SDK Public API
6+
7+
## API Compatibility
8+
9+
Public API is tracked via `.api` files generated by the [Binary Compatibility Validator](https://github.com/Kotlin/binary-compatibility-validator) Gradle plugin. Each module has its own file at `<module>/api/<module>.api`.
10+
11+
- **Never edit `.api` files manually.** Run `./gradlew apiDump` to regenerate them.
12+
- `./gradlew check` validates current code against `.api` files and fails on unintended changes.
13+
- `@ApiStatus.Internal` marks classes/methods as internal — they still appear in `.api` files but are not part of the public contract.
14+
- `@ApiStatus.Experimental` marks API that may change in future versions.
15+
16+
## Key Public API Classes
17+
18+
### Entry Point
19+
20+
`Sentry` (`sentry` module) is the static entry point. Most public API methods on `Sentry` delegate to `getCurrentScopes()`. When adding a new method to `Sentry`, it typically calls through to `IScopes`.
21+
22+
### Interfaces
23+
24+
| Interface | Description |
25+
|-----------|-------------|
26+
| `IScope` | Single scope — holds data (tags, extras, breadcrumbs, attributes, user, contexts, etc.) |
27+
| `IScopes` | Multi-scope container — manages global, isolation, and current scope; delegates capture calls to `SentryClient` |
28+
| `ISpan` | Performance span — timing, tags, data, measurements |
29+
| `ITransaction` | Top-level transaction — extends `ISpan` |
30+
31+
### Configuration
32+
33+
`SentryOptions` is the base configuration class. Platform-specific subclasses:
34+
- `SentryAndroidOptions` — Android-specific options
35+
- Integration modules may add their own (e.g. `SentrySpringProperties`)
36+
37+
New features must be **opt-in by default** — add a getter/setter pair to the appropriate options class.
38+
39+
### Internal Classes (Not Public API)
40+
41+
| Class | Description |
42+
|-------|-------------|
43+
| `SentryClient` | Sends events/envelopes to Sentry — receives captured data from `Scopes` |
44+
| `SentryEnvelope` / `SentryEnvelopeItem` | Low-level envelope serialization |
45+
| `Scope` | Concrete implementation of `IScope` |
46+
| `Scopes` | Concrete implementation of `IScopes` |
47+
48+
## Adding New Public API
49+
50+
When adding a new method that users can call (e.g. a new scope operation), these classes typically need changes:
51+
52+
### Interfaces and Static API
53+
1. `IScope` — add the method signature
54+
2. `IScopes` — add the method signature (usually delegates to a scope)
55+
3. `Sentry` — add static method that calls `getCurrentScopes()`
56+
57+
### Implementations
58+
4. `Scope` — actual implementation with data storage
59+
5. `Scopes` — delegates to the appropriate scope (global, isolation, or current based on `defaultScopeType`)
60+
6. `CombinedScopeView` — defines how the three scope types combine for reads (merge, first-wins, or specific scope)
61+
62+
### No-Op and Adapter Classes
63+
7. `NoOpScope` — no-op stub for `IScope`
64+
8. `NoOpScopes` — no-op stub for `IScopes`
65+
9. `ScopesAdapter` — delegates to `Sentry` static API
66+
10. `HubAdapter` — deprecated bridge from old `IHub` API
67+
11. `HubScopesWrapper` — wraps `IScopes` as `IHub`
68+
69+
### Serialization (if the data is sent to Sentry)
70+
12. Add serialization/deserialization in the relevant data class or create a new one implementing `JsonSerializable` and `JsonDeserializer`
71+
72+
### Tests
73+
13. Write tests for all implementations, especially `Scope`, `Scopes`, `SentryTest`, and any new data classes
74+
14. No-op classes typically don't need separate tests unless they have non-trivial logic
75+
76+
## Protocol / Data Model Classes
77+
78+
Classes in the `io.sentry.protocol` package represent the Sentry event protocol. They implement `JsonSerializable` for serialization and have a companion `Deserializer` class implementing `JsonDeserializer`. When adding new fields to protocol classes, update both serialization and deserialization.
79+
80+
## Namespaced APIs
81+
82+
Newer features are namespaced under `Sentry.<feature>()` rather than added directly to `Sentry`. Each namespaced API has an interface, implementation, and no-op. Examples:
83+
84+
- `Sentry.logger()` → `ILoggerApi` / `LoggerApi` / `NoOpLoggerApi` (structured logging, `io.sentry.logger` package)
85+
- `Sentry.metrics()` → `IMetricsApi` / `MetricsApi` / `NoOpMetricsApi` (metrics)
86+
87+
Options for namespaced features are similarly nested under `SentryOptions`, e.g. `SentryOptions.getMetrics()`, `SentryOptions.getLogs()`.
88+
89+
These APIs may share infrastructure like the type system (`SentryAttributeType.inferFrom()`) — changes to shared components (e.g. attribute types) may require updates across multiple namespaced APIs.

.cursor/rules/opentelemetry.mdc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ The Sentry Java SDK provides comprehensive OpenTelemetry integration through mul
1414
- `sentry-opentelemetry-agentless-spring`: Spring-specific agentless integration
1515
- `sentry-opentelemetry-bootstrap`: Classes that go into the bootstrap classloader when the agent is used. For agentless they are simply used in the applications classloader.
1616
- `sentry-opentelemetry-agentcustomization`: Classes that help wire up Sentry in OpenTelemetry. These land in the agent classloader when the agent is used. For agentless they are simply used in the application classloader.
17+
- `sentry-opentelemetry-otlp`: Classes for using OpenTelemetry to send spans to Sentry using the OTLP endpoint and have Sentry use OpenTelemetry trace and span id.
18+
- `sentry-opentelemetry-otlp-spring`: Spring Boot convenience module that includes `sentry-opentelemetry-otlp` and the OpenTelemetry Spring Boot starter as transitive dependencies.
1719

1820
## Advantages over using Sentry without OpenTelemetry
1921

@@ -86,3 +88,10 @@ After creating the transaction with child spans `SentrySpanExporter` uses Sentry
8688
## Troubleshooting
8789

8890
To debug forking of `Scopes`, we added a reference to `parent` `Scopes` and a `creator` String to store the reason why `Scopes` were created or forked.
91+
92+
# OTLP
93+
When using `sentry-opentelemetry-otlp`, Sentry only loads trace ID and span ID from OpenTelemetry `Context` (via `OpenTelemetryOtlpEventProcessor`). Sentry does not rely on OpenTelemetry `Context` for scope storage and propagation, instead relying on its `DefaultScopesStorage`.
94+
It is common to keep Performance in Sentry SDK disabled since that part is taken over by OpenTelemetry.
95+
The `sentry-opentelemetry-otlp` module is not connected to the other `sentry-opentelemetry-*` modules but instead intended only when the goal is to run OpenTelemetry for creating spans and Sentry for other products like errors, logs, metrics etc.
96+
The `sentry-opentelemetry-otlp-spring` module wraps `sentry-opentelemetry-otlp` and includes the OpenTelemetry Spring Boot starter for easier setup in Spring Boot applications.
97+
The OTLP module does not easily work with the OpenTelemetry agent as it would require customizing the agent.JAR in order to get the propagator loaded.

.cursor/rules/options.mdc

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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

Comments
 (0)