Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Add tracestate HTTP header support. #1683

Merged
merged 20 commits into from
Sep 2, 2021
Merged

Conversation

maciejwalkowiak
Copy link
Contributor

@maciejwalkowiak maciejwalkowiak commented Aug 25, 2021

📜 Description

Add experimental tracestate HTTP header support defined in https://develop.sentry.dev/sdk/performance/trace-context/#client-options.

Trace context is added to each envelope containing transaction or an event.

In addition to that, SentryOkHttpInterceptor attaches a new tracestate header to all outgoing requests.

By default this feature is off and is controller by a traceSampling flag on SentryOptions. All new API classes or methods are marked as @Experimental.

💚 How did you test it?

Unit & Integration tests. Sending events to sentry-java project on in sentry-sdks and verifying if they appear.

📝 Checklist

  • I reviewed the submitted code
  • I added tests to verify the changes
  • I updated the docs if needed
  • No breaking changes

@codecov-commenter
Copy link

codecov-commenter commented Aug 25, 2021

Codecov Report

Merging #1683 (e66d857) into main (90a6821) will decrease coverage by 1.10%.
The diff coverage is 54.06%.

Impacted file tree graph

@@             Coverage Diff              @@
##               main    #1683      +/-   ##
============================================
- Coverage     75.93%   74.82%   -1.11%     
- Complexity     2025     2078      +53     
============================================
  Files           207      210       +3     
  Lines          7060     7428     +368     
  Branches        699      782      +83     
============================================
+ Hits           5361     5558     +197     
- Misses         1361     1488     +127     
- Partials        338      382      +44     
Impacted Files Coverage Δ
sentry/src/main/java/io/sentry/HubAdapter.java 5.08% <0.00%> (ø)
sentry/src/main/java/io/sentry/NoOpHub.java 47.50% <ø> (ø)
...ntry/src/main/java/io/sentry/NoOpSentryClient.java 60.00% <ø> (ø)
sentry/src/main/java/io/sentry/NoOpSpan.java 27.27% <0.00%> (-2.73%) ⬇️
...entry/src/main/java/io/sentry/NoOpTransaction.java 19.35% <0.00%> (-1.34%) ⬇️
sentry/src/main/java/io/sentry/vendor/Base64.java 31.94% <31.94%> (ø)
sentry/src/main/java/io/sentry/IHub.java 86.36% <50.00%> (-4.12%) ⬇️
sentry/src/main/java/io/sentry/ISentryClient.java 85.71% <66.66%> (-3.76%) ⬇️
...ntry/src/main/java/io/sentry/TraceStateHeader.java 80.00% <80.00%> (ø)
...in/java/io/sentry/SentryEnvelopeHeaderAdapter.java 79.60% <82.53%> (+1.82%) ⬆️
... and 12 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 90a6821...e66d857. Read the comment docs.

@marandaneto
Copy link
Contributor

ps: important to test the serialization and deserialization of an envelope without such information and guarantee that nothing is broken since users that do offline caching might experience the broken behavior, if any

@maciejwalkowiak
Copy link
Contributor Author

To ensure that the state header for a transaction does not change once its calculated once, I moved computing the header from capturing transaction/event, to the SentryTracer, where it can be computed only once.

Copy link
Member

@bruno-garcia bruno-garcia left a comment

Choose a reason for hiding this comment

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

Please make it opt-in, aligning with option named here: https://develop.sentry.dev/sdk/trace-context/

Please please add a huge javadoc to the option that this is experimental and will be removed without notice.

@bruno-garcia
Copy link
Member

@marandaneto before merging this could you help @maciejwalkowiak test the OkHttp bit? He'll setup the sentry-sdks org, sentry-java repo with some rules and we can test things out:

https://sentry.io/settings/sentry-sdks/projects/sentry-java/filters-and-sampling/

@maciejwalkowiak maciejwalkowiak marked this pull request as ready for review August 30, 2021 13:40
Copy link
Member

@bruno-garcia bruno-garcia left a comment

Choose a reason for hiding this comment

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

Couple notes but lets merge and make a release of this please.
Lets make a beta first please

sentry/src/main/java/io/sentry/ISpan.java Show resolved Hide resolved
Comment on lines 229 to 241
if (hub.getOptions().isTraceSampling()) {
if (traceState == null) {
final AtomicReference<User> userAtomicReference = new AtomicReference<>();
hub.configureScope(
scope -> {
userAtomicReference.set(scope.getUser());
});
this.traceState = new TraceState(this, userAtomicReference.get(), hub.getOptions());
}
return this.traceState;
} else {
return null;
}
Copy link
Member

Choose a reason for hiding this comment

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

If the trace gets passed around to different threads so they can create child spans, this could have concurrent access, right?

If so, what about a double check lock?

Suggested change
if (hub.getOptions().isTraceSampling()) {
if (traceState == null) {
final AtomicReference<User> userAtomicReference = new AtomicReference<>();
hub.configureScope(
scope -> {
userAtomicReference.set(scope.getUser());
});
this.traceState = new TraceState(this, userAtomicReference.get(), hub.getOptions());
}
return this.traceState;
} else {
return null;
}
if (hub.getOptions().isTraceSampling()) {
if (traceState == null) {
synchronized(this) {
if (traceState == null) {
final AtomicReference<User> userAtomicReference = new AtomicReference<>();
hub.configureScope(
scope -> {
userAtomicReference.set(scope.getUser());
});
this.traceState = new TraceState(this, userAtomicReference.get(), hub.getOptions());
}
}
}
}
return this.traceState;
} else {
return null;
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

}

@Override
public @Nullable TraceStateHeader toTraceStateHeader() {
Copy link
Member

@bruno-garcia bruno-garcia Aug 31, 2021

Choose a reason for hiding this comment

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

Since TraceState has a reference to options, could we have toTraceStateHeader be part of TraceState instead and avoid this method?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

TraceState does not have a reference to options. We can put it to a transient field, just somehow does not seem right to me as TraceState was meant to be just a data container.

Copy link
Member

Choose a reason for hiding this comment

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

That's a fair point. I'm just trying to find any way not to add these two public methods here but I'm about to lose hope (the PR is approved anyway so)

Copy link
Contributor

Choose a reason for hiding this comment

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

SentryTracer is @ApiStatus.Internal already, so it'd be fine to be there.

@bruno-garcia
Copy link
Member

@marandaneto we need to ship soon, would you like to give this a pass before we merge?

@@ -280,6 +280,9 @@
*/
private @NotNull RequestSize maxRequestBodySize = RequestSize.NONE;

/** Controls if the `tracestate` header is attached to envelopes and HTTP client integrations. */
private boolean traceSampling;
Copy link
Contributor

Choose a reason for hiding this comment

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

is it disabled by default?
if this is intended for mobile too, let's add the ability to turn it on via Android Manifest
See: ManifestMetadataReader

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

public @Nullable TraceState traceState() {
if (hub.getOptions().isTraceSampling()) {
if (traceState == null) {
final AtomicReference<User> userAtomicReference = new AtomicReference<>();
Copy link
Contributor

Choose a reason for hiding this comment

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

what's about adding a function to the Scope called withUser like we do with withSession instead of using AtomicReference? that executes a callback and returns the user atomically, it could be package private or @ApiStatus.Internal. not a big fun of AtomicReference

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree, also didn't like it, but in this PR we need to avoid introducing changes to the API, so i think AtomicReference as a temporary solution is a right trade off.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants