Skip to content
Merged
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
- Log an actionable error message when Relay returns HTTP 413 (Content Too Large) ([#5115](https://github.com/getsentry/sentry-java/pull/5115))
- Also switch the client report discard reason for all HTTP 4xx/5xx errors (except 429) from `network_error` to `send_error`
- Trim DSN string before parsing to avoid `URISyntaxException` caused by trailing whitespace ([#5113](https://github.com/getsentry/sentry-java/pull/5113))
- Reduce allocations and bytecode instructions during `Sentry.init` ([#5135](https://github.com/getsentry/sentry-java/pull/5135))

### Dependencies

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ static void loadDefaultAndMetadataOptions(

readDefaultOptionValues(options, finalContext, buildInfoProvider);
AppState.getInstance().registerLifecycleObserver(options);
options.activate();
}

@TestOnly
Expand Down Expand Up @@ -200,7 +201,7 @@ static void initializeIntegrationsAndProcessors(
final @NotNull AppStartMetrics appStartMetrics = AppStartMetrics.getInstance();

if (options.getModulesLoader() instanceof NoOpModulesLoader) {
options.setModulesLoader(new AssetsModulesLoader(context, options.getLogger()));
options.setModulesLoader(new AssetsModulesLoader(context, options));
}
if (options.getDebugMetaLoader() instanceof NoOpDebugMetaLoader) {
options.setDebugMetaLoader(new AssetsDebugMetaLoader(context, options.getLogger()));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package io.sentry.android.core.internal.modules;

import android.content.Context;
import io.sentry.ILogger;
import io.sentry.SentryLevel;
import io.sentry.SentryOptions;
import io.sentry.android.core.ContextUtils;
import io.sentry.internal.modules.ModulesLoader;
import java.io.FileNotFoundException;
Expand All @@ -18,13 +18,21 @@ public final class AssetsModulesLoader extends ModulesLoader {

private final @NotNull Context context;

public AssetsModulesLoader(final @NotNull Context context, final @NotNull ILogger logger) {
super(logger);
public AssetsModulesLoader(final @NotNull Context context, final @NotNull SentryOptions options) {
super(options.getLogger());
this.context = ContextUtils.getApplicationContext(context);

// pre-load modules on a bg thread to avoid doing so on the main thread in case of a crash/error
//noinspection Convert2MethodRef
new Thread(() -> getOrLoadModules()).start();
try {
options
.getExecutorService()
.submit(
() -> {
getOrLoadModules();
});
} catch (Throwable e) {
options.getLogger().log(SentryLevel.ERROR, "AssetsModulesLoader submit failed", e);
}
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import io.sentry.DateUtils
import io.sentry.Hint
import io.sentry.ILogger
import io.sentry.ISentryClient
import io.sentry.NoOpSentryExecutorService
import io.sentry.Sentry
import io.sentry.Sentry.OptionsConfiguration
import io.sentry.SentryEnvelope
Expand Down Expand Up @@ -528,6 +529,19 @@ class SentryAndroidTest {
assertEquals(99, AppStartMetrics.getInstance().appStartTimeSpan.startUptimeMs)
}

@Test
fun `executor service is not NoOp when AndroidConnectionStatusProvider is initialized`() {
var executorServiceIsNoOp = true
fixture.initSut(context = context) { options ->
options.dsn = "https://key@sentry.io/123"
// the config callback runs before initializeIntegrationsAndProcessors, which creates
// AndroidConnectionStatusProvider - so if the executor is already real here,
// it's guaranteed to be real when the provider calls submitSafe()
executorServiceIsNoOp = options.executorService is NoOpSentryExecutorService
}
assertFalse(executorServiceIsNoOp)
}

@Test
fun `if the config options block throws still intializes android event processors`() {
lateinit var optionsRef: SentryOptions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package io.sentry.android.core.internal.modules

import android.content.Context
import android.content.res.AssetManager
import io.sentry.ILogger
import io.sentry.SentryOptions
import io.sentry.test.ImmediateExecutorService
import java.io.FileNotFoundException
import java.nio.charset.Charset
import kotlin.test.Test
Expand All @@ -16,7 +17,7 @@ class AssetsModulesLoaderTest {
class Fixture {
val context = mock<Context>()
val assets = mock<AssetManager>()
val logger = mock<ILogger>()
val options = SentryOptions().apply { executorService = ImmediateExecutorService() }

fun getSut(
fileName: String = "sentry-external-modules.txt",
Expand All @@ -31,7 +32,7 @@ class AssetsModulesLoaderTest {
whenever(assets.open(fileName)).thenThrow(FileNotFoundException())
}
whenever(context.assets).thenReturn(assets)
return AssetsModulesLoader(context, logger)
return AssetsModulesLoader(context, options)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package io.sentry;

import io.sentry.util.Objects;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
Expand All @@ -16,7 +15,7 @@ public final class DuplicateEventDetectionEventProcessor implements EventProcess
private final @NotNull SentryOptions options;

public DuplicateEventDetectionEventProcessor(final @NotNull SentryOptions options) {
this.options = Objects.requireNonNull(options, "options are required");
this.options = options;
}

@Override
Expand Down
14 changes: 1 addition & 13 deletions sentry/src/main/java/io/sentry/MainEventProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import io.sentry.protocol.SentryTransaction;
import io.sentry.protocol.User;
import io.sentry.util.HintUtils;
import io.sentry.util.Objects;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
Expand All @@ -29,7 +28,7 @@ public final class MainEventProcessor implements EventProcessor, Closeable {
private volatile @Nullable HostnameCache hostnameCache = null;

public MainEventProcessor(final @NotNull SentryOptions options) {
this.options = Objects.requireNonNull(options, "The SentryOptions is required.");
this.options = options;

final SentryStackTraceFactory sentryStackTraceFactory =
new SentryStackTraceFactory(this.options);
Expand All @@ -38,17 +37,6 @@ public MainEventProcessor(final @NotNull SentryOptions options) {
sentryThreadFactory = new SentryThreadFactory(sentryStackTraceFactory);
}

MainEventProcessor(
final @NotNull SentryOptions options,
final @NotNull SentryThreadFactory sentryThreadFactory,
final @NotNull SentryExceptionFactory sentryExceptionFactory) {
this.options = Objects.requireNonNull(options, "The SentryOptions is required.");
this.sentryThreadFactory =
Objects.requireNonNull(sentryThreadFactory, "The SentryThreadFactory is required.");
this.sentryExceptionFactory =
Objects.requireNonNull(sentryExceptionFactory, "The SentryExceptionFactory is required.");
}

@Override
public @NotNull SentryEvent process(final @NotNull SentryEvent event, final @NotNull Hint hint) {
setCommons(event);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import io.sentry.protocol.SentryStackFrame;
import io.sentry.protocol.SentryStackTrace;
import io.sentry.protocol.SentryThread;
import io.sentry.util.Objects;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
Expand All @@ -31,8 +30,7 @@ public final class SentryExceptionFactory {
* @param sentryStackTraceFactory the sentryStackTraceFactory
*/
public SentryExceptionFactory(final @NotNull SentryStackTraceFactory sentryStackTraceFactory) {
this.sentryStackTraceFactory =
Objects.requireNonNull(sentryStackTraceFactory, "The SentryStackTraceFactory is required.");
this.sentryStackTraceFactory = sentryStackTraceFactory;
}

@NotNull
Expand Down
29 changes: 19 additions & 10 deletions sentry/src/main/java/io/sentry/SentryOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.net.ssl.SSLSocketFactory;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
Expand Down Expand Up @@ -318,6 +319,12 @@ public class SentryOptions {
/** Sentry Executor Service that sends cached events and envelopes on App. start. */
private @NotNull ISentryExecutorService executorService = NoOpSentryExecutorService.getInstance();

/**
* Whether SpotlightIntegration has already been loaded via reflection. This prevents re-adding it
* if the user removed it in their configuration callback and activate() is called again.
*/
private final @NotNull AtomicBoolean spotlightIntegrationLoaded = new AtomicBoolean(false);

/** connection timeout in milliseconds. */
private int connectionTimeoutMillis = 30_000;

Expand Down Expand Up @@ -655,6 +662,18 @@ public void activate() {
executorService = new SentryExecutorService(this);
executorService.prewarm();
}

// SpotlightIntegration is loaded via reflection to allow the sentry-spotlight module
// to be excluded from release builds, preventing insecure HTTP URLs from appearing in APKs.
// Only attempt once to avoid re-adding after user removal in their configuration callback.
if (spotlightIntegrationLoaded.compareAndSet(false, true)) {
try {
final Class<?> clazz = Class.forName("io.sentry.spotlight.SpotlightIntegration");
integrations.add((Integration) clazz.getConstructor().newInstance());
} catch (Throwable ignored) {
// SpotlightIntegration not available
}
}
}

/**
Expand Down Expand Up @@ -3340,16 +3359,6 @@ private SentryOptions(final boolean empty) {

integrations.add(new ShutdownHookIntegration());

// SpotlightIntegration is loaded via reflection to allow the sentry-spotlight module
// to be excluded from release builds, preventing insecure HTTP URLs from appearing in APKs
try {
final Class<?> clazz = Class.forName("io.sentry.spotlight.SpotlightIntegration");
final Integration spotlight = (Integration) clazz.getConstructor().newInstance();
integrations.add(spotlight);
} catch (Throwable ignored) {
// SpotlightIntegration not available
}

eventProcessors.add(new MainEventProcessor(this));
eventProcessors.add(new DuplicateEventDetectionEventProcessor(this));

Expand Down
4 changes: 1 addition & 3 deletions sentry/src/main/java/io/sentry/SentryThreadFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import io.sentry.protocol.SentryStackFrame;
import io.sentry.protocol.SentryStackTrace;
import io.sentry.protocol.SentryThread;
import io.sentry.util.Objects;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
Expand All @@ -26,8 +25,7 @@ public final class SentryThreadFactory {
* @param sentryStackTraceFactory the SentryStackTraceFactory
*/
public SentryThreadFactory(final @NotNull SentryStackTraceFactory sentryStackTraceFactory) {
this.sentryStackTraceFactory =
Objects.requireNonNull(sentryStackTraceFactory, "The SentryStackTraceFactory is required.");
this.sentryStackTraceFactory = sentryStackTraceFactory;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public UncaughtExceptionHandlerIntegration() {
}

UncaughtExceptionHandlerIntegration(final @NotNull UncaughtExceptionHandler threadAdapter) {
this.threadAdapter = Objects.requireNonNull(threadAdapter, "threadAdapter is required.");
this.threadAdapter = threadAdapter;
}

@Override
Expand Down
Loading