Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
305baf5
replace hub with scopes
adinauer Mar 27, 2024
95f5e1b
Add Scopes
adinauer Apr 2, 2024
27f2398
Introduce `IScopes` interface.
adinauer Apr 2, 2024
ce3c14f
Replace `IHub` with `IScopes` in core
adinauer Apr 2, 2024
ce615f4
Replace `IHub` with `IScopes` in android core
adinauer Apr 2, 2024
22ddc00
Replace `IHub` with `IScopes` in android integrations
adinauer Apr 2, 2024
305c217
Replace `IHub` with `IScopes` in apollo integrations
adinauer Apr 2, 2024
da927bc
Replace `IHub` with `IScopes` in okhttp integration
adinauer Apr 2, 2024
8279276
Replace `IHub` with `IScopes` in graphql integration
adinauer Apr 2, 2024
9bfc086
Replace `IHub` with `IScopes` in logging integrations
adinauer Apr 2, 2024
b998e50
Replace `IHub` with `IScopes` in more integrations
adinauer Apr 2, 2024
739827a
Replace `IHub` with `IScopes` in OTel integration
adinauer Apr 2, 2024
69f2d63
Replace `IHub` with `IScopes` in Spring 5 / Spring Boot 2 integrations
adinauer Apr 2, 2024
792d482
Replace `IHub` with `IScopes` in Spring 6 / Spring Boot 3 integrations
adinauer Apr 2, 2024
9bcbce6
Replace `IHub` with `IScopes` in samples
adinauer Apr 2, 2024
3f25a4b
Merge branch 'feat/hsm-13-replacements-in-samples' into feat/hubs-sco…
adinauer Apr 2, 2024
d6fb40a
gitscopes -> github
adinauer Apr 2, 2024
7752bcc
Replace ThreadLocal with ScopesStorage
adinauer Apr 4, 2024
1e329c5
Move client and throwable to span map to scope
adinauer Apr 4, 2024
b0d89ae
Add global scope
adinauer Apr 4, 2024
cdd414a
use global scope in Scopes
adinauer Apr 4, 2024
98da9ff
Implement pushScope popScope and withScope for Scopes
adinauer Apr 4, 2024
2d26033
Add pushIsolationScope; add fork methods to ISCope
adinauer Apr 12, 2024
bbb6700
Use separate scopes for current, isolation and global scope; rename m…
adinauer Apr 12, 2024
c714b21
Allow controlling which scope configureScope uses
adinauer Apr 12, 2024
a474402
Combine scopes
adinauer Apr 12, 2024
1a804ca
Merge branch '8.x.x' into feat/hsm-22-combine-scopes
adinauer Apr 22, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ class InternalSentrySdkTest {

@Test
fun `current scope returns obj when hub is active`() {
Sentry.setCurrentHub(
Sentry.setCurrentScopes(
Hub(
SentryOptions().apply {
dsn = "https://key@uri/1234567"
Expand All @@ -131,7 +131,7 @@ class InternalSentrySdkTest {

@Test
fun `current scope returns a copy of the scope`() {
Sentry.setCurrentHub(
Sentry.setCurrentScopes(
Hub(
SentryOptions().apply {
dsn = "https://key@uri/1234567"
Expand Down
8 changes: 7 additions & 1 deletion sentry/src/main/java/io/sentry/Breadcrumb.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import org.jetbrains.annotations.Nullable;

/** Series of application events */
public final class Breadcrumb implements JsonUnknown, JsonSerializable {
public final class Breadcrumb implements JsonUnknown, JsonSerializable, Comparable<Breadcrumb> {

/** A timestamp representing when the breadcrumb occurred. */
private final @NotNull Date timestamp;
Expand Down Expand Up @@ -660,6 +660,12 @@ public void setUnknown(@Nullable Map<String, Object> unknown) {
this.unknown = unknown;
}

@Override
@SuppressWarnings("JavaUtilDate")
public int compareTo(@NotNull Breadcrumb o) {
return timestamp.compareTo(o.timestamp);
}

public static final class JsonKeys {
public static final String TIMESTAMP = "timestamp";
public static final String MESSAGE = "message";
Expand Down
214 changes: 214 additions & 0 deletions sentry/src/main/java/io/sentry/CombinedContextsView.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
package io.sentry;

import io.sentry.protocol.App;
import io.sentry.protocol.Browser;
import io.sentry.protocol.Contexts;
import io.sentry.protocol.Device;
import io.sentry.protocol.Gpu;
import io.sentry.protocol.OperatingSystem;
import io.sentry.protocol.Response;
import io.sentry.protocol.SentryRuntime;
import io.sentry.util.HintUtils;
import java.io.IOException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public final class CombinedContextsView extends Contexts {

private static final long serialVersionUID = 3585992094653318439L;
private final @NotNull Contexts globalContexts;
private final @NotNull Contexts isolationContexts;
private final @NotNull Contexts currentContexts;

private final @NotNull ScopeType defaultScopeType;

public CombinedContextsView(
final @NotNull Contexts globalContexts,
final @NotNull Contexts isolationContexts,
final @NotNull Contexts currentContexts,
final @NotNull ScopeType defaultScopeType) {
this.globalContexts = globalContexts;
this.isolationContexts = isolationContexts;
this.currentContexts = currentContexts;
this.defaultScopeType = defaultScopeType;
}

@Override
public @Nullable SpanContext getTrace() {
final @Nullable SpanContext current = currentContexts.getTrace();
if (current != null) {
return current;
}
final @Nullable SpanContext isolation = isolationContexts.getTrace();
if (isolation != null) {
return isolation;
}
return globalContexts.getTrace();
}

@Override
public void setTrace(@Nullable SpanContext traceContext) {
getDefaultContexts().setTrace(traceContext);
}

private Contexts getDefaultContexts() {
switch (defaultScopeType) {
case CURRENT:
return currentContexts;
case ISOLATION:
return isolationContexts;
case GLOBAL:
return globalContexts;
default:
return currentContexts;
}
}

@Override
public @Nullable App getApp() {
final @Nullable App current = currentContexts.getApp();
if (current != null) {
return current;
}
final @Nullable App isolation = isolationContexts.getApp();
if (isolation != null) {
return isolation;
}
return globalContexts.getApp();
}

@Override
public void setApp(@NotNull App app) {
getDefaultContexts().setApp(app);
}

@Override
public @Nullable Browser getBrowser() {
final @Nullable Browser current = currentContexts.getBrowser();
if (current != null) {
return current;
}
final @Nullable Browser isolation = isolationContexts.getBrowser();
if (isolation != null) {
return isolation;
}
return globalContexts.getBrowser();
}

@Override
public void setBrowser(@NotNull Browser browser) {
getDefaultContexts().setBrowser(browser);
}

@Override
public @Nullable Device getDevice() {
Copy link
Member

Choose a reason for hiding this comment

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

I think we need some propagation or copy-on-write mechanism for all Scope related fields.

E.g.

val user = Sentry.getUser() // returns global scope user, as it's unset
Sentry.setUser(user) // now default scope is set to the user

user.email = "[email protected]" // both global and default scope is written to

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmm this should only happen if someone calls .setUser on global scope so an actual User instance is returned from CombinedScopeView and then pass that into .setUser that goes to current or isolation scope.

Can we ignore this edge case for now and address it, when it actually happens for a customer?

One solution would be to have something like CombinedContextsView for user and other fields.

Copy link
Member Author

Choose a reason for hiding this comment

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

Here's an example that should avoid the problem:

@Nullable User user = scope.getUser();
if (user == null) {
user = new User();
scope.setUser(user);
}
if (user.getId() == null) {
try {
user.setId(Installation.id(context));
} catch (RuntimeException e) {
logger.log(SentryLevel.ERROR, "Could not retrieve installation ID", e);
}
}

final @Nullable Device current = currentContexts.getDevice();
if (current != null) {
return current;
}
final @Nullable Device isolation = isolationContexts.getDevice();
if (isolation != null) {
return isolation;
}
return globalContexts.getDevice();
}

@Override
public void setDevice(@NotNull Device device) {
getDefaultContexts().setDevice(device);
}

@Override
public @Nullable OperatingSystem getOperatingSystem() {
final @Nullable OperatingSystem current = currentContexts.getOperatingSystem();
if (current != null) {
return current;
}
final @Nullable OperatingSystem isolation = isolationContexts.getOperatingSystem();
if (isolation != null) {
return isolation;
}
return globalContexts.getOperatingSystem();
}

@Override
public void setOperatingSystem(@NotNull OperatingSystem operatingSystem) {
getDefaultContexts().setOperatingSystem(operatingSystem);
}

@Override
public @Nullable SentryRuntime getRuntime() {
final @Nullable SentryRuntime current = currentContexts.getRuntime();
if (current != null) {
return current;
}
final @Nullable SentryRuntime isolation = isolationContexts.getRuntime();
if (isolation != null) {
return isolation;
}
return globalContexts.getRuntime();
}

@Override
public void setRuntime(@NotNull SentryRuntime runtime) {
getDefaultContexts().setRuntime(runtime);
}

@Override
public @Nullable Gpu getGpu() {
final @Nullable Gpu current = currentContexts.getGpu();
if (current != null) {
return current;
}
final @Nullable Gpu isolation = isolationContexts.getGpu();
if (isolation != null) {
return isolation;
}
return globalContexts.getGpu();
}

@Override
public void setGpu(@NotNull Gpu gpu) {
getDefaultContexts().setGpu(gpu);
}

@Override
public @Nullable Response getResponse() {
final @Nullable Response current = currentContexts.getResponse();
if (current != null) {
return current;
}
final @Nullable Response isolation = isolationContexts.getResponse();
if (isolation != null) {
return isolation;
}
return globalContexts.getResponse();
}

@Override
public void withResponse(HintUtils.SentryConsumer<Response> callback) {
if (currentContexts.getResponse() != null) {
currentContexts.withResponse(callback);
} else if (isolationContexts.getResponse() != null) {
isolationContexts.withResponse(callback);
} else if (globalContexts.getResponse() != null) {
globalContexts.withResponse(callback);
} else {
getDefaultContexts().withResponse(callback);
}
}

@Override
public void setResponse(@NotNull Response response) {
getDefaultContexts().setResponse(response);
}

@Override
public void serialize(@NotNull ObjectWriter writer, @NotNull ILogger logger) throws IOException {
final @NotNull Contexts allContexts = new Contexts();
allContexts.putAll(globalContexts);
allContexts.putAll(isolationContexts);
allContexts.putAll(currentContexts);
allContexts.serialize(writer, logger);
}
}
Loading