Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,24 @@

## Unreleased

### Behavioral Changes

- Collections returned by scope (e.g. `getBreadcrumbs`, `getTags`, `getAttachments`) are shared state and should not be mutated. ([#5541](https://github.com/getsentry/sentry-java/pull/5541))
- Previously, when going through `CombinedScopeView`, we were returning a copy where mutations didn't show up in the underlying scopes.
- This has now changed in order to reduce SDK overhead.

### Features

- Add experimental `SentrySQLiteDriver` to `sentry-android-sqlite` for instrumenting `androidx.sqlite.SQLiteDriver` ([#5563](https://github.com/getsentry/sentry-java/pull/5563))
- To use it, pass `SQLiteDriver` to `SentrySQLiteDriver.create(...)`
- Requires `androidx.sqlite:sqlite` (2.5.0+) on runtime classpath (typically provided by Room or SQLDelight)

### Internal

- Reduce writer buffer size from 8192 to 512 ([#5544](https://github.com/getsentry/sentry-java/pull/5544))
- Remove redundant event map copies ([#5536](https://github.com/getsentry/sentry-java/pull/5536))
- Optimize combined scope by adding an early return if only one scope has data ([#5541](https://github.com/getsentry/sentry-java/pull/5541))

## 8.44.0

### Features
Expand Down
157 changes: 140 additions & 17 deletions sentry/src/main/java/io/sentry/CombinedScopeView.java
Original file line number Diff line number Diff line change
Expand Up @@ -171,10 +171,31 @@ public void setFingerprint(@NotNull List<String> fingerprint) {

@Override
public @NotNull Queue<Breadcrumb> getBreadcrumbs() {
final @NotNull Queue<Breadcrumb> globalBreadcrumbs = globalScope.getBreadcrumbs();
final @NotNull Queue<Breadcrumb> isolationBreadcrumbs = isolationScope.getBreadcrumbs();
final @NotNull Queue<Breadcrumb> currentBreadcrumbs = scope.getBreadcrumbs();

final boolean hasGlobalBreadcrumbs = !globalBreadcrumbs.isEmpty();
final boolean hasIsolationBreadcrumbs = !isolationBreadcrumbs.isEmpty();
final boolean hasCurrentBreadcrumbs = !currentBreadcrumbs.isEmpty();

if (!hasGlobalBreadcrumbs && !hasIsolationBreadcrumbs && !hasCurrentBreadcrumbs) {
return getDefaultScopeValue(globalBreadcrumbs, isolationBreadcrumbs, currentBreadcrumbs);
}
if (!hasIsolationBreadcrumbs && !hasCurrentBreadcrumbs) {
return globalBreadcrumbs;
}
if (!hasGlobalBreadcrumbs && !hasCurrentBreadcrumbs) {
return isolationBreadcrumbs;
}
if (!hasGlobalBreadcrumbs && !hasIsolationBreadcrumbs) {
return currentBreadcrumbs;
}

final @NotNull List<Breadcrumb> allBreadcrumbs = new ArrayList<>();
allBreadcrumbs.addAll(globalScope.getBreadcrumbs());
allBreadcrumbs.addAll(isolationScope.getBreadcrumbs());
allBreadcrumbs.addAll(scope.getBreadcrumbs());
allBreadcrumbs.addAll(globalBreadcrumbs);
allBreadcrumbs.addAll(isolationBreadcrumbs);
allBreadcrumbs.addAll(currentBreadcrumbs);
Collections.sort(allBreadcrumbs);

final @NotNull Queue<Breadcrumb> breadcrumbs =
Expand Down Expand Up @@ -224,10 +245,31 @@ public void clear() {

@Override
public @NotNull Map<String, String> getTags() {
final @NotNull Map<String, String> globalTags = globalScope.getTags();
final @NotNull Map<String, String> isolationTags = isolationScope.getTags();
final @NotNull Map<String, String> currentTags = scope.getTags();

final boolean hasGlobalTags = !globalTags.isEmpty();
final boolean hasIsolationTags = !isolationTags.isEmpty();
final boolean hasCurrentTags = !currentTags.isEmpty();

if (!hasGlobalTags && !hasIsolationTags && !hasCurrentTags) {
return getDefaultScopeValue(globalTags, isolationTags, currentTags);
}
if (!hasIsolationTags && !hasCurrentTags) {
return globalTags;
}
if (!hasGlobalTags && !hasCurrentTags) {
return isolationTags;
}
if (!hasGlobalTags && !hasIsolationTags) {
return currentTags;
}

final @NotNull Map<String, String> allTags = new ConcurrentHashMap<>();
allTags.putAll(globalScope.getTags());
allTags.putAll(isolationScope.getTags());
allTags.putAll(scope.getTags());
allTags.putAll(globalTags);
allTags.putAll(isolationTags);
allTags.putAll(currentTags);
return allTags;
}

Expand All @@ -243,10 +285,32 @@ public void removeTag(@Nullable String key) {

@Override
public @NotNull Map<String, SentryAttribute> getAttributes() {
final @NotNull Map<String, SentryAttribute> globalAttributes = globalScope.getAttributes();
final @NotNull Map<String, SentryAttribute> isolationAttributes =
isolationScope.getAttributes();
final @NotNull Map<String, SentryAttribute> currentAttributes = scope.getAttributes();

final boolean hasGlobalAttributes = !globalAttributes.isEmpty();
final boolean hasIsolationAttributes = !isolationAttributes.isEmpty();
final boolean hasCurrentAttributes = !currentAttributes.isEmpty();

if (!hasGlobalAttributes && !hasIsolationAttributes && !hasCurrentAttributes) {
return getDefaultScopeValue(globalAttributes, isolationAttributes, currentAttributes);
}
if (!hasIsolationAttributes && !hasCurrentAttributes) {
return globalAttributes;
}
if (!hasGlobalAttributes && !hasCurrentAttributes) {
return isolationAttributes;
}
if (!hasGlobalAttributes && !hasIsolationAttributes) {
return currentAttributes;
}

final @NotNull Map<String, SentryAttribute> allAttributes = new ConcurrentHashMap<>();
allAttributes.putAll(globalScope.getAttributes());
allAttributes.putAll(isolationScope.getAttributes());
allAttributes.putAll(scope.getAttributes());
allAttributes.putAll(globalAttributes);
allAttributes.putAll(isolationAttributes);
allAttributes.putAll(currentAttributes);
return allAttributes;
}

Expand All @@ -272,11 +336,32 @@ public void removeAttribute(@Nullable String key) {

@Override
public @NotNull Map<String, Object> getExtras() {
final @NotNull Map<String, Object> allTags = new ConcurrentHashMap<>();
allTags.putAll(globalScope.getExtras());
allTags.putAll(isolationScope.getExtras());
allTags.putAll(scope.getExtras());
return allTags;
final @NotNull Map<String, Object> globalExtras = globalScope.getExtras();
final @NotNull Map<String, Object> isolationExtras = isolationScope.getExtras();
final @NotNull Map<String, Object> currentExtras = scope.getExtras();

final boolean hasGlobalExtras = !globalExtras.isEmpty();
final boolean hasIsolationExtras = !isolationExtras.isEmpty();
final boolean hasCurrentExtras = !currentExtras.isEmpty();

if (!hasGlobalExtras && !hasIsolationExtras && !hasCurrentExtras) {
return getDefaultScopeValue(globalExtras, isolationExtras, currentExtras);
}
if (!hasIsolationExtras && !hasCurrentExtras) {
return globalExtras;
}
if (!hasGlobalExtras && !hasCurrentExtras) {
return isolationExtras;
}
if (!hasGlobalExtras && !hasIsolationExtras) {
return currentExtras;
}

final @NotNull Map<String, Object> allExtras = new ConcurrentHashMap<>();
allExtras.putAll(globalExtras);
allExtras.putAll(isolationExtras);
allExtras.putAll(currentExtras);
return allExtras;
}

@Override
Expand Down Expand Up @@ -342,6 +427,23 @@ public void removeContexts(@Nullable String key) {
return getSpecificScope(null);
}

private <T> @NotNull T getDefaultScopeValue(
final @NotNull T globalValue,
final @NotNull T isolationValue,
final @NotNull T currentValue) {
switch (getOptions().getDefaultScopeType()) {
case CURRENT:
return currentValue;
case ISOLATION:
return isolationValue;
case GLOBAL:
return globalValue;
default:
// calm the compiler
return currentValue;
}
}

IScope getSpecificScope(final @Nullable ScopeType scopeType) {
if (scopeType != null) {
switch (scopeType) {
Expand Down Expand Up @@ -373,10 +475,31 @@ IScope getSpecificScope(final @Nullable ScopeType scopeType) {

@Override
public @NotNull List<Attachment> getAttachments() {
final @NotNull List<Attachment> globalAttachments = globalScope.getAttachments();
final @NotNull List<Attachment> isolationAttachments = isolationScope.getAttachments();
final @NotNull List<Attachment> currentAttachments = scope.getAttachments();

final boolean hasGlobalAttachments = !globalAttachments.isEmpty();
final boolean hasIsolationAttachments = !isolationAttachments.isEmpty();
final boolean hasCurrentAttachments = !currentAttachments.isEmpty();

if (!hasGlobalAttachments && !hasIsolationAttachments && !hasCurrentAttachments) {
return getDefaultScopeValue(globalAttachments, isolationAttachments, currentAttachments);
}
if (!hasIsolationAttachments && !hasCurrentAttachments) {
return globalAttachments;
}
if (!hasGlobalAttachments && !hasCurrentAttachments) {
return isolationAttachments;
}
if (!hasGlobalAttachments && !hasIsolationAttachments) {
return currentAttachments;
}

final @NotNull List<Attachment> allAttachments = new CopyOnWriteArrayList<>();
allAttachments.addAll(globalScope.getAttachments());
allAttachments.addAll(isolationScope.getAttachments());
allAttachments.addAll(scope.getAttachments());
allAttachments.addAll(globalAttachments);
allAttachments.addAll(isolationAttachments);
allAttachments.addAll(currentAttachments);
return allAttachments;
}

Expand Down
5 changes: 4 additions & 1 deletion sentry/src/main/java/io/sentry/JsonSerializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ public final class JsonSerializer implements ISerializer {
@SuppressWarnings("CharsetObjectCanBeUsed")
private static final Charset UTF_8 = Charset.forName("UTF-8");

private static final int WRITER_BUFFER_SIZE = 512;

/** the SentryOptions */
private final @NotNull SentryOptions options;

Expand Down Expand Up @@ -233,7 +235,8 @@ public void serialize(@NotNull SentryEnvelope envelope, @NotNull OutputStream ou

// we do not want to close these as we would also close the stream that was passed in
final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
final Writer writer = new BufferedWriter(new OutputStreamWriter(bufferedOutputStream, UTF_8));
final Writer writer =
new BufferedWriter(new OutputStreamWriter(bufferedOutputStream, UTF_8), WRITER_BUFFER_SIZE);

try {
envelope
Expand Down
3 changes: 1 addition & 2 deletions sentry/src/main/java/io/sentry/MainEventProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.ApiStatus;
Expand Down Expand Up @@ -191,7 +190,7 @@ private void setSdk(final @NotNull SentryBaseEvent event) {

private void setTags(final @NotNull SentryBaseEvent event) {
if (event.getTags() == null) {
event.setTags(new HashMap<>(options.getTags()));
event.setTags(options.getTags());
} else {
for (Map.Entry<String, String> item : options.getTags().entrySet()) {
if (!event.getTags().containsKey(item.getKey())) {
Expand Down
9 changes: 4 additions & 5 deletions sentry/src/main/java/io/sentry/SentryClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.ApiStatus;
Expand Down Expand Up @@ -1425,7 +1424,7 @@ public void captureBatchedMetricsEvents(final @NotNull SentryMetricsEvents metri
event.setUser(scope.getUser());
}
if (event.getTags() == null) {
event.setTags(new HashMap<>(scope.getTags()));
event.setTags(scope.getTags());
} else {
for (Map.Entry<String, String> item : scope.getTags().entrySet()) {
if (!event.getTags().containsKey(item.getKey())) {
Expand Down Expand Up @@ -1483,7 +1482,7 @@ public void captureBatchedMetricsEvents(final @NotNull SentryMetricsEvents metri
replayEvent.setUser(scope.getUser());
}
if (replayEvent.getTags() == null) {
replayEvent.setTags(new HashMap<>(scope.getTags()));
replayEvent.setTags(scope.getTags());
} else {
for (Map.Entry<String, String> item : scope.getTags().entrySet()) {
if (!replayEvent.getTags().containsKey(item.getKey())) {
Expand Down Expand Up @@ -1523,7 +1522,7 @@ public void captureBatchedMetricsEvents(final @NotNull SentryMetricsEvents metri
sentryBaseEvent.setUser(scope.getUser());
}
if (sentryBaseEvent.getTags() == null) {
sentryBaseEvent.setTags(new HashMap<>(scope.getTags()));
sentryBaseEvent.setTags(scope.getTags());
} else {
for (Map.Entry<String, String> item : scope.getTags().entrySet()) {
if (!sentryBaseEvent.getTags().containsKey(item.getKey())) {
Expand All @@ -1537,7 +1536,7 @@ public void captureBatchedMetricsEvents(final @NotNull SentryMetricsEvents metri
sortBreadcrumbsByDate(sentryBaseEvent, scope.getBreadcrumbs());
}
if (sentryBaseEvent.getExtras() == null) {
sentryBaseEvent.setExtras(new HashMap<>(scope.getExtras()));
sentryBaseEvent.setExtras(scope.getExtras());
} else {
for (Map.Entry<String, Object> item : scope.getExtras().entrySet()) {
if (!sentryBaseEvent.getExtras().containsKey(item.getKey())) {
Expand Down
Loading