Skip to content

Commit 642b213

Browse files
authored
Merge branch 'main' into feat/poc-continuous-profiling
2 parents 8a02f7b + c8125f3 commit 642b213

File tree

474 files changed

+24074
-1298
lines changed

Some content is hidden

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

474 files changed

+24074
-1298
lines changed

.craft.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@ targets:
1919
maven:io.sentry:sentry:
2020
maven:io.sentry:sentry-spring:
2121
maven:io.sentry:sentry-spring-jakarta:
22+
# maven:io.sentry:sentry-spring-7:
2223
maven:io.sentry:sentry-spring-boot:
2324
maven:io.sentry:sentry-spring-boot-jakarta:
2425
maven:io.sentry:sentry-spring-boot-starter:
2526
maven:io.sentry:sentry-spring-boot-starter-jakarta:
27+
# maven:io.sentry:sentry-spring-boot-4:
28+
# maven:io.sentry:sentry-spring-boot-4-starter:
2629
maven:io.sentry:sentry-servlet:
2730
maven:io.sentry:sentry-servlet-jakarta:
2831
maven:io.sentry:sentry-logback:
@@ -59,3 +62,4 @@ targets:
5962
maven:io.sentry:sentry-android-replay:
6063
maven:io.sentry:sentry-apollo-4:
6164
maven:io.sentry:sentry-reactor:
65+
maven:io.sentry:sentry-ktor-client:

.cursor/rules/deduplication.mdc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
alwaysApply: false
3+
description: Java SDK Event deduplication
4+
---
5+
6+
# Java SDK Event deduplication
7+
8+
To avoid sending the same error multiple times, there is deduplication logic in place in the SDK.
9+
Duplicate captures can happen due to multiple integrations capturing the exception as well as additional manual calls to `Sentry.captureException`.
10+
11+
Deduplication is performed in `DuplicateEventDetectionEventProcessor` which returns `null` when it detects a duplicate event causing it to be dropped.
12+
13+
The `enableDeduplication` option can be used to opt out of deduplication. It is enabled by default.

.cursor/rules/e2e_tests.mdc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
alwaysApply: false
3+
description: Java SDK End to End Tests
4+
---
5+
6+
# Java SDK End to End Tests (System Tests)
7+
8+
The samples in the `sentry-samples` directory are used to run end to end tests against them.
9+
10+
There is a python script (`system-test-runner.py`) that can be used to run one (using `--module SAMPLE_NAME`) or all (using `--all`) system tests.
11+
12+
The script has an interactive mode (`-i`) which allows selection of test setups to execute, whether to run the tests or just prepare infrastructure for testing from IDE.
13+
14+
The tests run a mock Sentry server via `system-test-sentry-server.py`. Any system under test will then have a DSN set that reflects this local mock server like `http://502f25099c204a2fbf4cb16edc5975d1@localhost:8000/0`.
15+
By using this local DSN, the system under test sends events to the local mock server.
16+
The tests can then use `TestHelper` to assert envelopes that were received by the mock server.
17+
`TestHelper` uses HTTP requests to retrieve the JSON payload of the received events and deserialize them back to objects for easier assertion.
18+
Tests can then assert events, transactions, logs etc. similar to how they would appear in `beforeSend` and similar callbacks.
19+
20+
`TestHelper` has a lot of helper methods for asserting, e.g. by span name, log body etc.
21+
22+
The end to end tests either expect the system under test to either be running on a server or call `java -jar` to execute a CLI system under test.
23+
24+
For Spring Boot, we spin up the Spring Boot server. The tests then send requests to that server and assert what is sent to Sentry.
25+
26+
End to end tests are also executed on CI using a matrix build, as defined in `.github/workflows/system-tests-backend.yml`.
27+
28+
Some of the samples are tested in multiple ways, e.g. with OpenTelemetry Agent auto init turned on and off.

.cursor/rules/offline.mdc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
alwaysApply: true
2+
alwaysApply: false
33
description: Java SDK Offline behaviour
44
---
55
# Java SDK Offline behaviour

.cursor/rules/opentelemetry.mdc

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
---
2+
alwaysApply: false
3+
description: Java SDK OpenTelemetry Integration
4+
---
5+
# Java SDK OpenTelemetry Integration
6+
7+
## Overview
8+
9+
The Sentry Java SDK provides comprehensive OpenTelemetry integration through multiple modules:
10+
11+
- `sentry-opentelemetry-core`: Core OpenTelemetry integration functionality
12+
- `sentry-opentelemetry-agent`: Java Agent-based integration for automatic instrumentation
13+
- `sentry-opentelemetry-agentless`: Manual instrumentation without Java agent
14+
- `sentry-opentelemetry-agentless-spring`: Spring-specific agentless integration
15+
- `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.
16+
- `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+
18+
## Advantages over using Sentry without OpenTelemetry
19+
20+
- Support for more libraries and frameworks
21+
- See https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation for a list of supported libraries and frameworks
22+
- More automated Performance instrumentation (spans) created
23+
- Using `sentry-opentelemetry-agent` offers most support
24+
- Using `sentry-opentelemetry-agentless-spring` for Spring Boot also has a lot of supported libraries, altough fewer than the agent does
25+
- Note that `sentry-opentelemetry-agentless` will not have any OpenTelemetry auto instrumentation
26+
- Sentry also relies on OpenTelemetry `Context` propagation to propagate Sentry `Scopes`, ensuring e.g. that execution flow for a request shares data and does not leak data into other requests.
27+
- OpenTelemetry also offers better support for distributed tracing since more libraries are supported for attaching tracing information to outgoing requests and picking up incoming tracing information.
28+
29+
## Key Components
30+
31+
### Agent vs Agentless
32+
33+
**Java Agent-based integration**:
34+
- Automatic instrumentation via Java agent
35+
- Can be added to any JAR when starting, no extra dependencies or code changes required. Just add the agent when running the application, e.g. `SENTRY_PROPERTIES_FILE=sentry.properties JAVA_TOOL_OPTIONS="-javaagent:sentry-opentelemetry-agent.jar" java -jar your-application.jar`.
36+
- Uses OpenTelemetry Java agent with Sentry extensions
37+
- Uses bytecode manipulation
38+
39+
**Agentless-Spring integration**:
40+
- Automatic instrumentation setup via Spring Boot
41+
- Dependency needs to be added to the project.
42+
43+
**Agentless integration**:
44+
- Manual instrumentation setup
45+
- Dependency needs to be added to the project.
46+
47+
**Manual Integration**:
48+
While it's possible to manually wire up all the required classes to make Sentry and OpenTelemetry work together, we do not recommend this.
49+
It is instead preferrable to use `SentryAutoConfigurationCustomizerProvider` so the Sentry SDK has a place to manage required classes and update it when changes are needed.
50+
This way customers receive the updated config automatically as oppposed to having to update manually, wire in new classes, remove old ones etc.
51+
52+
### Integration Architecture
53+
54+
Sentry will try to locate certain classes that come with the Sentry OpenTelemetry integration to:
55+
- Determine whether any Sentry OpenTelemetry integration is present
56+
- Determine which mode to use and in turn which Sentry auto instrumentation to suppress
57+
58+
Reflection is used to search for `io.sentry.opentelemetry.OtelContextScopesStorage` and use it instead of `DefaultScopesStorage` when a Sentry OpenTelemetry integration is present at runtime. `IScopesStorage` is used to store Sentry `Scopes` instances. `DefaultScopesStorage` will use a thread local variable to store the current threads' `Scopes` whereas `OtelContextScopesStorage` makes use of OpenTelemetry SDKs `Context`. Sentry OpenTelemetry integrations configure OpenTelemetry to use `SentryOtelThreadLocalStorage` to customize restoring of the previous `Context`.
59+
60+
OpenTelemetry SDK makes use of `io.opentelemetry.context.Scope` in `try-with-resources` statements that call `close` when a code block is finished. Without customization, it would refuse to restore the previous `Context` onto the `ThreadLocal` if the current state of the `ThreadLocal` isn't the same as the one this scope was created for. Sentry changes this behaviour in `SentryScopeImpl` to restore the previous `Context` onto the `ThreadLocal` even if an inner `io.opentelemetry.context.Scope` wasn't properly cleaned up. Our thinking here is to prefer returning to a clean state as opposed to propagating the problem. The unclean state could happen, if `io.opentelemetry.context.Scope` isn't closed, e.g. when forgetting to put it in a `try-with-resources` statement and not calling `close` (e.g. not putting it in a `finally` block in that case).
61+
62+
`SentryContextStorageProvider` looks for any other `ContextStorageProvider` and forwards to that to not override any customized `ContextStorage`. If no other provider is found, `SentryOtelThreadLocalStorage` is used.
63+
64+
`SpanFactoryFactory` is used to configure Sentry to use `io.sentry.opentelemetry.OtelSpanFactory` if the class is present at runtime. Reflection is used to search for it. If the class is not available, we fall back to `DefaultSpanFactory`.
65+
66+
`DefaultSpanFactory` creates a `SentryTracer` instance when creating a transaction and spans are then created directly on the transaction via `startChild`.
67+
`OtelSpanFactory` instead creates an OpenTelemetry span and wraps it using `OtelTransactionSpanForwarder` to simulate a transaction. The `startChild` invocations on `OtelTransactionSpanForwarder` go through `OtelSpanFactory` again to create the child span.
68+
69+
## Configuration
70+
71+
We use `SentryAutoConfigurationCustomizerProvider` to configure OpenTelemetry for use with Sentry and register required classes, hooks etc.
72+
73+
## Span Processing
74+
75+
Both Sentry and OpenTelemetry API can be used to create spans. When using Sentry API, `OtelSpanFactory` is used to indirectly create a OpenTelemetry span.
76+
Regardless of API used, when an OpenTelemetry span is created, it goes through `SentrySampler` for sampling and `OtelSentrySpanProcessor` for `Scopes` forking and ensuring the trace is continued.
77+
When Sentry API is used, sampling is performed in `Scopes.createTransaction` before forwarding the call to `OtelSpanFactory`. The sampling decision and other sampling details are forwarded to `SentrySampler` and `OtelSentrySpanProcessor`.
78+
79+
When a span is finished, regardless of whether Sentry or OpenTelemetry API is used, it goes through `OtelSentrySpanProcessor` to set the end date and then through `BatchSpanProcessor` which will batch spans and then forward them to `SentrySpanExporter`.
80+
81+
`SentrySpanExporter` collects spans, then structures them to create a transaction for the local root span and attaches child spans to form a span tree.
82+
Some OpenTelemetry attributes are transformed into their corresponding Sentry data structure or format.
83+
84+
After creating the transaction with child spans `SentrySpanExporter` uses Sentry API to send the transaction to Sentry. This API call however forces the use of `DefaultSpanFactory` in order to create the required Sentry classes for sending and also to not create an infinite loop where any span created will cause a new span to be created recursively.
85+
86+
## Troubleshooting
87+
88+
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.

.cursor/rules/overview_dev.mdc

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
---
2+
alwaysApply: true
3+
description: Sentry Java SDK - Development Rules Overview
4+
---
5+
6+
# Sentry Java SDK Development Rules
7+
8+
## Always Applied Rules
9+
10+
These rules are automatically included in every conversation:
11+
- **coding.mdc**: General contributing guidelines, build commands, and workflow rules
12+
13+
## Domain-Specific Rules (Fetch Only When Needed)
14+
15+
Use the `fetch_rules` tool to include these rules when working on specific areas:
16+
17+
### Core SDK Functionality
18+
- **`scopes`**: Use when working with:
19+
- Hub/Scope management, forking, or lifecycle
20+
- `Sentry.getCurrentScopes()`, `pushScope()`, `withScope()`
21+
- `ScopeType` (GLOBAL, ISOLATION, CURRENT)
22+
- Thread-local storage, scope bleeding issues
23+
- Migration from Hub API (v7 → v8)
24+
25+
- **`deduplication`**: Use when working with:
26+
- Duplicate event detection/prevention
27+
- `DuplicateEventDetectionEventProcessor`
28+
- `enableDeduplication` option
29+
30+
- **`offline`**: Use when working with:
31+
- Caching, envelope storage/retrieval
32+
- Network failure handling, retry logic
33+
- `AsyncHttpTransport`, `EnvelopeCache`
34+
- Rate limiting, cache rotation
35+
- Android vs JVM caching differences
36+
37+
### Integration & Infrastructure
38+
- **`opentelemetry`**: Use when working with:
39+
- OpenTelemetry modules (`sentry-opentelemetry-*`)
40+
- Agent vs agentless configurations
41+
- Span processing, sampling, context propagation
42+
- `OtelSpanFactory`, `SentrySpanExporter`
43+
- Tracing, distributed tracing
44+
45+
### Testing
46+
- **`e2e_tests`**: Use when working with:
47+
- System tests, sample applications
48+
- `system-test-runner.py`, mock Sentry server
49+
- End-to-end test infrastructure
50+
- CI system test workflows
51+
52+
## Usage Guidelines
53+
54+
1. **Start minimal**: Only include `coding.mdc` (auto-applied) for general tasks
55+
2. **Fetch on-demand**: Use `fetch_rules ["rule_name"]` when you identify specific domain work
56+
3. **Multiple rules**: Fetch multiple rules if task spans domains (e.g., `["scopes", "opentelemetry"]` for tracing scope issues)
57+
4. **Context clues**: Look for these keywords in requests to determine relevant rules:
58+
- Scope/Hub/forking → `scopes`
59+
- Duplicate/dedup → `deduplication`
60+
- OpenTelemetry/tracing/spans → `opentelemetry`
61+
- Cache/offline/network → `offline`
62+
- System test/e2e/sample → `e2e_tests`

.cursor/rules/scopes.mdc

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
---
2+
alwaysApply: false
3+
description: Java SDK Hubs and Scopes
4+
---
5+
# Java SDK Hubs and Scopes
6+
7+
## `Scopes`
8+
9+
`Scopes` implements `IScopes` and manages three `Scope` instances, `global`, `isolation` and `current` scope.
10+
For some data, all three `Scope` instances are combined, for others, a certain one is used exclusively and for some we look at each scope in a certain order and use the data of the first scope that has the data set. This logic is contained in `CombinedScopeView`.
11+
Data itself is stored on `Scope` instances.
12+
`Scopes` also has a `parent` field, linking the `Scopes` it was forked off of and a `creator` String, explaining why it was forked.
13+
14+
## `Hub`
15+
16+
Up until major version 7 of the Java SDK the `IHub` interface was a central part of the SDK.
17+
In major version 8 we replaced the `IHub` interface with `IScopes`. `IHub` has been deprecated.
18+
While there is some bridging code in place to allow for easier migration, we are planning to remove it in an upcoming major.
19+
20+
## Scope Types
21+
22+
We have introduced some new Scope types in the SDK, allowing for better control over what data is attached where.
23+
Previously there was a stack of scopes that was pushed and popped.
24+
Instead we now fork scopes for a given lifecycle and then restore the previous scopes.
25+
Since Hub is gone, it is also never cloned anymore.
26+
Separation of data now happens through the different scope types while making it easier to manipulate exactly what you need without having to attach data at the right time to have it apply where wanted.
27+
28+
### Global Scope
29+
30+
Global scope is attached to all events created by the SDK.
31+
It can also be modified before Sentry.init has been called.
32+
It can be manipulated using `Sentry.configureScope(ScopeType.GLOBAL, (scope) -> { ... })`.
33+
34+
Global scope can be retrieved from `Scopes` via `getGlobalScope`. It can also be retrieved directly via `Sentry.getGlobalScope`.
35+
36+
### Isolation Scope
37+
38+
Isolation scope can be used e.g. to attach data to all events that come up while handling an incoming request.
39+
It can also be used for other isolation purposes.
40+
It can be manipulated using `Sentry.configureScope(ScopeType.ISOLATION, (scope) -> { ... })`.
41+
The SDK automatically forks isolation scope in certain cases like incoming requests, CRON jobs, Spring `@Async` and more.
42+
43+
Isolation scope can be retrieved from `Scopes` via `getIsolationScope`.
44+
45+
### Current scope
46+
47+
Current scope is forked often and data added to it is only added to events that are created while this scope is active.
48+
Data is also passed on to newly forked child scopes but not to parents.
49+
50+
Current scope can be retrieved from `Scopes` via `getScope`.
51+
52+
## Storage of `Scopes`
53+
54+
`Scopes` are stored in a `ThreadLocal` by default (NOTE: this is different for OpenTelemetry, see opentelemetry.mdc).
55+
This happens through `Sentry.scopesStorage` and `DefaultScopesStorage`.
56+
57+
The lifetime of `Scopes` in the thread local is managed by `ISentryLifecycleToken`.
58+
When the scopes are forked, they are stored into the `ThreadLocal` and a `ISentryLifecycleToken` is returned.
59+
When the `Scopes` are no longer needed, e.g. because a request is finished, `ISentryLifecycleToken.close` can be called to restore the previous state of the `ThreadLocal`.
60+
61+
## Old versions of the Java SDK
62+
63+
There were several implementations of the `IHub` interface:
64+
- `Hub` managed a stack of `Scope` instances, which were pushed and popped.
65+
- A `Hub` could be cloned, meaning there could be multiple stacks of scopes active, e.g. for two separate requests being handled in a server application.
66+
67+
### Migrating to major version 8 of the SDK
68+
69+
`IHub` has been replaced by `IScopes`
70+
`HubAdapter` has been replaced by `ScopesAdapter`
71+
`Hub.clone` should be replaced by using `pushScope` or `pushIsolationScope`
72+
`Sentry.getCurrentHub` has been replaced by `Sentry.getCurrentScopes`
73+
`Sentry.popScope` has been deprecated. Instead `close` should be called on the `ISentryLifecycleToken` returned e.g. by `pushScope`. This can also be done in a `try-with-resource` block.
74+
75+
## `globalHubMode`
76+
The SDK has a `globalHubMode` option which affects forking behaviour of the SDK.
77+
78+
Android has `globalHubMode` enabled by default.
79+
For JVM Desktop applications, `globalHubMode` can be used.
80+
For JVM Backend applications (servers) we discourage enabling `globalHubMode` since it will cause scopes to bleed into each other. This can e.g. mean that state from request A leaks into request B and events sent to Sentry contain a mix of both request A and B potentially rendering the data useless.
81+
82+
### Enabled
83+
84+
If `globalHubMode` is enabled, the SDK avoids forking scopes.
85+
86+
This means, retrieving current scopes on a thread where specific scopes do not exist yet for the thread, the root scopes are not forked but returned directly.
87+
The SDK also doesn't fork scopes when `Sentry.pushScope`, `Sentry.pushIsolation`, `Sentry.withScope` or `Sentry.withIsolationScope` are executed.
88+
89+
The suppression of forking via `globalHubMode` only applies when using `Sentry` static API or `ScopesAdapter`.
90+
In case the `Scopes` instance is accessed directly, forking will happen as if `globalHubMode` is disabled.
91+
However, while it's possible to use `Sentry.setCurrentScopes` it does not have any effect due to `Sentry.getCurrentScopes` directly returning `rootScopes` if `globalHubMode` is enabled.
92+
This means the forked scopes have to be managed manually, e.g. by keeping a reference and accessing Sentry API via the reference instead of using static API.
93+
94+
`ScopesAdapter` makes use of the static `Sentry` API internally. It allows us to access the correct scopes for the current context without passing it along explicitly. It also makes testing easier.
95+
96+
### Disabled
97+
98+
If `globalHubMode` is disabled, the SDK forks scopes freely, e.g. when:
99+
- `Sentry.getCurrentScopes()` is executed on a Thread where no specific scopes for that thread have been stored yet. In this case the SDK will fork `rootScopes` (stored in a `Sentry` static property).
100+
- `withScope` or `withIsolationScope` are executed
101+
- `pushScope` or `pushIsolationScope` are executed
102+
103+
## `defaultScopeType`
104+
105+
The `defaultScopeType` controls which `Scope` instance is being used for writing to and reading from as a default value.
106+
When using API like `Sentry.setTag` the SDK adds that tag to the default `Scope`.
107+
108+
This also ensures, customers who migrate to the latest SDK version and already have `Sentry.configureScope` invocations in place, will now write to the default `Scope` instance that was chosen.
109+
110+
The default value for `defaultScopeType` is `ISOLATION` scope for JVM and `CURRENT` scope for Android.
111+
112+
Which fields are written/read from/to `defaultScopeType` is controlled in `CombinedScopeView`.

.fossa.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
version: 3
2+
targets:
3+
exclude:
4+
- type: setuptools

0 commit comments

Comments
 (0)