Skip to content
16 changes: 16 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,22 @@
- Prevent concurrent PixelCopy access during Session Replay masking and bitmap cleanup ([#5808](https://github.com/getsentry/sentry-java/pull/5808))
- Release `MediaMuxer` when the replay video encoder fails to start to avoid a resource leak ([#5607](https://github.com/getsentry/sentry-java/pull/5607))

### Features

- Added `Environment` to the `Scope`, allowing you to overwrite the `Environment` set on the options ([#5772](https://github.com/getsentry/sentry-java/pull/5772))
### Features

- Added `Environment` to the `Scope`, allowing you to overwrite the `Environment` set on the options ([#5772](https://github.com/getsentry/sentry-java/pull/5772))

### Improvements

- Skip building Android manifest metadata debug log messages when debug logging is disabled, reducing allocations during SDK init ([#5790](https://github.com/getsentry/sentry-java/pull/5790))

### Fixes

- Prevent concurrent PixelCopy access during Session Replay masking and bitmap cleanup ([#5808](https://github.com/getsentry/sentry-java/pull/5808))
- Release `MediaMuxer` when the replay video encoder fails to start to avoid a resource leak ([#5607](https://github.com/getsentry/sentry-java/pull/5607))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Duplicated changelog sections

Low Severity

The Unreleased notes now repeat ### Features, ### Improvements, and ### Fixes with the same entries. The feature line appears twice, and the existing improvements/fixes are duplicated instead of Features being moved above them.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 3163e45. Configure here.


### Performance

- Reduce the number of SDK threads: `LifecycleWatcher` now schedules the session-end task on the shared timer executor instead of creating a dedicated `java.util.Timer` thread ([#5819](https://github.com/getsentry/sentry-java/pull/5819))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ private void backfillScope(
setRequest(event);
setUser(event);
setScopeTags(event);
setScopeEnvironment(event);
setBreadcrumbs(event);
setExtras(event);
setContexts(event);
Comment thread
bitsandfoxes marked this conversation as resolved.
Expand Down Expand Up @@ -487,6 +488,16 @@ private void setEnvironment(
}
}

private void setScopeEnvironment(final @NotNull SentryBaseEvent event) {
if (event.getEnvironment() == null) {
final @Nullable String scopeEnvironment =
readFromDisk(options, PersistingScopeObserver.ENVIRONMENT_FILENAME, String.class);
if (scopeEnvironment != null) {
event.setEnvironment(scopeEnvironment);
}
}
}

private void setDebugMeta(
final @NotNull SentryBaseEvent event, final @NotNull OptionsSource optionsSource) {
DebugMeta debugMeta = event.getDebugMeta();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,18 @@ class ApplicationExitInfoEventProcessorTest {
assertEquals("release", processed.environment)
}

@Test
fun `if scope environment is persisted, it takes precedence over the options environment`() {
val hint = HintUtils.createWithTypeCheckHint(BackfillableHint())

val processor = fixture.getSut(tmpDir, populateOptionsCache = true)
fixture.persistScope(PersistingScopeObserver.ENVIRONMENT_FILENAME, "staging")

val processed = processor.process(SentryEvent(), hint)

assertEquals("staging", processed!!.environment)
}

@Test
fun `if release is not persisted and app was not updated, uses release from options`() {
val hint = HintUtils.createWithTypeCheckHint(AbnormalExitHint(timestamp = 2_000))
Expand Down Expand Up @@ -482,6 +494,7 @@ class ApplicationExitInfoEventProcessorTest {
fixture.options.release = "io.sentry.samples@1.2.0+232"
fixture.options.environment = "production"
fixture.options.dist = "custom-dist"
fixture.persistScope(PersistingScopeObserver.ENVIRONMENT_FILENAME, "staging")
setLastUpdateTime(1_000)

val processed = processor.process(SentryEvent(), hint)!!
Expand Down
12 changes: 12 additions & 0 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ public final class io/sentry/CombinedScopeView : io/sentry/IScope {
public fun getBreadcrumbs ()Ljava/util/Queue;
public fun getClient ()Lio/sentry/ISentryClient;
public fun getContexts ()Lio/sentry/protocol/Contexts;
public fun getEnvironment ()Ljava/lang/String;
public fun getEventProcessors ()Ljava/util/List;
public fun getEventProcessorsWithOrder ()Ljava/util/List;
public fun getExtras ()Ljava/util/Map;
Expand Down Expand Up @@ -321,6 +322,7 @@ public final class io/sentry/CombinedScopeView : io/sentry/IScope {
public fun setContexts (Ljava/lang/String;Ljava/lang/String;)V
public fun setContexts (Ljava/lang/String;Ljava/util/Collection;)V
public fun setContexts (Ljava/lang/String;[Ljava/lang/Object;)V
public fun setEnvironment (Ljava/lang/String;)V
public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V
public fun setFingerprint (Ljava/util/List;)V
public fun setLastEventId (Lio/sentry/protocol/SentryId;)V
Expand Down Expand Up @@ -922,6 +924,7 @@ public abstract interface class io/sentry/IScope {
public abstract fun getBreadcrumbs ()Ljava/util/Queue;
public abstract fun getClient ()Lio/sentry/ISentryClient;
public abstract fun getContexts ()Lio/sentry/protocol/Contexts;
public abstract fun getEnvironment ()Ljava/lang/String;
public abstract fun getEventProcessors ()Ljava/util/List;
public abstract fun getEventProcessorsWithOrder ()Ljava/util/List;
public abstract fun getExtras ()Ljava/util/Map;
Expand Down Expand Up @@ -957,6 +960,7 @@ public abstract interface class io/sentry/IScope {
public abstract fun setContexts (Ljava/lang/String;Ljava/lang/String;)V
public abstract fun setContexts (Ljava/lang/String;Ljava/util/Collection;)V
public abstract fun setContexts (Ljava/lang/String;[Ljava/lang/Object;)V
public abstract fun setEnvironment (Ljava/lang/String;)V
public abstract fun setExtra (Ljava/lang/String;Ljava/lang/String;)V
public abstract fun setFingerprint (Ljava/util/List;)V
public abstract fun setLastEventId (Lio/sentry/protocol/SentryId;)V
Expand Down Expand Up @@ -984,6 +988,7 @@ public abstract interface class io/sentry/IScopeObserver {
public abstract fun removeTag (Ljava/lang/String;)V
public abstract fun setBreadcrumbs (Ljava/util/Collection;)V
public abstract fun setContexts (Lio/sentry/protocol/Contexts;)V
public abstract fun setEnvironment (Ljava/lang/String;)V
public abstract fun setExtra (Ljava/lang/String;Ljava/lang/String;)V
public abstract fun setExtras (Ljava/util/Map;)V
public abstract fun setFingerprint (Ljava/util/Collection;)V
Expand Down Expand Up @@ -1745,6 +1750,7 @@ public final class io/sentry/NoOpScope : io/sentry/IScope {
public fun getBreadcrumbs ()Ljava/util/Queue;
public fun getClient ()Lio/sentry/ISentryClient;
public fun getContexts ()Lio/sentry/protocol/Contexts;
public fun getEnvironment ()Ljava/lang/String;
public fun getEventProcessors ()Ljava/util/List;
public fun getEventProcessorsWithOrder ()Ljava/util/List;
public fun getExtras ()Ljava/util/Map;
Expand Down Expand Up @@ -1781,6 +1787,7 @@ public final class io/sentry/NoOpScope : io/sentry/IScope {
public fun setContexts (Ljava/lang/String;Ljava/lang/String;)V
public fun setContexts (Ljava/lang/String;Ljava/util/Collection;)V
public fun setContexts (Ljava/lang/String;[Ljava/lang/Object;)V
public fun setEnvironment (Ljava/lang/String;)V
public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V
public fun setFingerprint (Ljava/util/List;)V
public fun setLastEventId (Lio/sentry/protocol/SentryId;)V
Expand Down Expand Up @@ -2434,6 +2441,7 @@ public final class io/sentry/Scope : io/sentry/IScope {
public fun getBreadcrumbs ()Ljava/util/Queue;
public fun getClient ()Lio/sentry/ISentryClient;
public fun getContexts ()Lio/sentry/protocol/Contexts;
public fun getEnvironment ()Ljava/lang/String;
public fun getEventProcessors ()Ljava/util/List;
public fun getEventProcessorsWithOrder ()Ljava/util/List;
public fun getExtras ()Ljava/util/Map;
Expand Down Expand Up @@ -2469,6 +2477,7 @@ public final class io/sentry/Scope : io/sentry/IScope {
public fun setContexts (Ljava/lang/String;Ljava/lang/String;)V
public fun setContexts (Ljava/lang/String;Ljava/util/Collection;)V
public fun setContexts (Ljava/lang/String;[Ljava/lang/Object;)V
public fun setEnvironment (Ljava/lang/String;)V
public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V
public fun setFingerprint (Ljava/util/List;)V
public fun setLastEventId (Lio/sentry/protocol/SentryId;)V
Expand Down Expand Up @@ -2517,6 +2526,7 @@ public abstract class io/sentry/ScopeObserverAdapter : io/sentry/IScopeObserver
public fun removeTag (Ljava/lang/String;)V
public fun setBreadcrumbs (Ljava/util/Collection;)V
public fun setContexts (Lio/sentry/protocol/Contexts;)V
public fun setEnvironment (Ljava/lang/String;)V
public fun setExtra (Ljava/lang/String;Ljava/lang/String;)V
public fun setExtras (Ljava/util/Map;)V
public fun setFingerprint (Ljava/util/Collection;)V
Expand Down Expand Up @@ -4880,6 +4890,7 @@ public final class io/sentry/cache/PersistingOptionsObserver : io/sentry/IOption
public final class io/sentry/cache/PersistingScopeObserver : io/sentry/ScopeObserverAdapter {
public static final field BREADCRUMBS_FILENAME Ljava/lang/String;
public static final field CONTEXTS_FILENAME Ljava/lang/String;
public static final field ENVIRONMENT_FILENAME Ljava/lang/String;
public static final field EXTRAS_FILENAME Ljava/lang/String;
public static final field FINGERPRINT_FILENAME Ljava/lang/String;
public static final field LEVEL_FILENAME Ljava/lang/String;
Expand All @@ -4896,6 +4907,7 @@ public final class io/sentry/cache/PersistingScopeObserver : io/sentry/ScopeObse
public fun resetCache ()V
public fun setBreadcrumbs (Ljava/util/Collection;)V
public fun setContexts (Lio/sentry/protocol/Contexts;)V
public fun setEnvironment (Ljava/lang/String;)V
public fun setExtras (Ljava/util/Map;)V
public fun setFingerprint (Ljava/util/Collection;)V
public fun setLevel (Lio/sentry/SentryLevel;)V
Expand Down
24 changes: 23 additions & 1 deletion sentry/src/main/java/io/sentry/CombinedScopeView.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,24 @@ public void setLevel(@Nullable SentryLevel level) {
getDefaultWriteScope().setLevel(level);
}

@Override
public @Nullable String getEnvironment() {
final @Nullable String current = scope.getEnvironment();
if (current != null) {
return current;
}
final @Nullable String isolation = isolationScope.getEnvironment();
if (isolation != null) {
return isolation;
}
return globalScope.getEnvironment();
}

@Override
public void setEnvironment(@Nullable String environment) {
getDefaultWriteScope().setEnvironment(environment);
}

@Override
public @Nullable String getTransactionName() {
final @Nullable String current = scope.getTransactionName();
Expand Down Expand Up @@ -540,7 +558,11 @@ public void addEventProcessor(@NotNull EventProcessor eventProcessor) {

@Override
public @Nullable Scope.SessionPair startSession() {
return getDefaultWriteScope().startSession();
final IScope defaultScope = getDefaultWriteScope();
if (defaultScope instanceof Scope) {
return ((Scope) defaultScope).startSession(getEnvironment());
}
return defaultScope.startSession();
}

@Override
Expand Down
15 changes: 15 additions & 0 deletions sentry/src/main/java/io/sentry/IScope.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,21 @@ public interface IScope {
*/
void setLevel(final @Nullable SentryLevel level);

/**
* Returns the Scope's environment.
*
* @return the environment or {@code null} if not set on the scope
*/
@Nullable
String getEnvironment();

/**
* Sets the Scope's environment. Takes precedence over {@link SentryOptions#getEnvironment()}.
*
* @param environment the environment
*/
void setEnvironment(final @Nullable String environment);

/**
* Returns the Scope's transaction name.
*
Expand Down
2 changes: 2 additions & 0 deletions sentry/src/main/java/io/sentry/IScopeObserver.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ public interface IScopeObserver {

void setLevel(@Nullable SentryLevel level);

void setEnvironment(@Nullable String environment);

void setContexts(@NotNull Contexts contexts);

void setTransaction(@Nullable String transaction);
Expand Down
8 changes: 8 additions & 0 deletions sentry/src/main/java/io/sentry/NoOpScope.java
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ public static NoOpScope getInstance() {
@Override
public void setLevel(@Nullable SentryLevel level) {}

@Override
public @Nullable String getEnvironment() {
return null;
}

@Override
public void setEnvironment(@Nullable String environment) {}

@Override
public @Nullable String getTransactionName() {
return null;
Expand Down
41 changes: 39 additions & 2 deletions sentry/src/main/java/io/sentry/Scope.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ public final class Scope implements IScope {
/** Scope's screen */
private @Nullable String screen;

/** Scope's environment */
private @Nullable String environment;

/** Scope's request */
private @Nullable Request request;

Expand Down Expand Up @@ -138,6 +141,7 @@ private Scope(final @NotNull Scope scope) {
final User userRef = scope.user;
this.user = userRef != null ? new User(userRef) : null;
this.screen = scope.screen;
this.environment = scope.environment;
this.replayId = scope.replayId;

final Request requestRef = scope.request;
Expand Down Expand Up @@ -225,6 +229,30 @@ public void setLevel(final @Nullable SentryLevel level) {
}
}

/**
* Returns the Scope's environment.
*
* @return the environment or {@code null} if not set on the scope
*/
@Override
public @Nullable String getEnvironment() {
return environment;
}

/**
* Sets the Scope's environment. Takes precedence over {@link SentryOptions#getEnvironment()}.
*
* @param environment the environment
*/
@Override
public void setEnvironment(final @Nullable String environment) {
this.environment = environment;

for (final IScopeObserver observer : options.getScopeObservers()) {
observer.setEnvironment(environment);
}
}

Comment thread
cursor[bot] marked this conversation as resolved.
/**
* Returns the Scope's transaction name.
*
Comment thread
sentry[bot] marked this conversation as resolved.
Expand Down Expand Up @@ -566,7 +594,8 @@ public ITransaction getTransaction() {
/** Resets the Scope to its default state */
@Override
public void clear() {
level = null;
setLevel(null);
setEnvironment(null);
user = null;
request = null;
screen = null;
Comment thread
sentry[bot] marked this conversation as resolved.
Expand Down Expand Up @@ -1032,6 +1061,11 @@ interface IWithSession {
@Nullable
@Override
public SessionPair startSession() {
return startSession(environment);
}

@Nullable
SessionPair startSession(final @Nullable String sessionEnvironment) {
Session previousSession;
SessionPair pair = null;
try (final @NotNull ISentryLifecycleToken ignored = sessionLock.acquire()) {
Expand All @@ -1046,7 +1080,10 @@ public SessionPair startSession() {
if (options.getRelease() != null) {
session =
new Session(
options.getDistinctId(), user, options.getEnvironment(), options.getRelease());
options.getDistinctId(),
user,
sessionEnvironment != null ? sessionEnvironment : options.getEnvironment(),
options.getRelease());

final Session previousClone = previousSession != null ? previousSession.clone() : null;
pair = new SessionPair(session.clone(), previousClone);
Expand Down
3 changes: 3 additions & 0 deletions sentry/src/main/java/io/sentry/ScopeObserverAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ public void setFingerprint(@NotNull Collection<String> fingerprint) {}
@Override
public void setLevel(@Nullable SentryLevel level) {}

@Override
public void setEnvironment(@Nullable String environment) {}

@Override
public void setContexts(@NotNull Contexts contexts) {}

Expand Down
4 changes: 4 additions & 0 deletions sentry/src/main/java/io/sentry/Sentry.java
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,10 @@ private static void notifyOptionsObservers(final @NotNull SentryOptions options)
options.findPersistingScopeObserver();
if (scopeCache != null) {
scopeCache.resetCache();
final @Nullable String environment = globalScope.getEnvironment();
if (environment != null) {
scopeCache.setEnvironment(environment);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

l: can we maybe add a comment describing why we do this for environment now? Something like

// scope environment is configuration, not telemetry state: re-persist it after the reset like release/dist above

}
Comment thread
bitsandfoxes marked this conversation as resolved.
}
});
} catch (Throwable e) {
Expand Down
Loading
Loading