diff --git a/CHANGELOG.md b/CHANGELOG.md index 7086207b01..8984032866 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Changelog +## 6.x.x + +* Ref: Bind external properties to a dedicated class. (#1750) + +Breaking changes: + +- `SentryOptions` can merge properties from `ExternalOptions` instead of another instance of `SentryOptions` +- Following boolean properties from `SentryOptions` that allowed `null` values are now not nullable - `debug`, `enableUncaughtExceptionHandler`, `enableDeduplication` +- `SentryOptions` cannot be created anymore using `PropertiesProvider` with `SentryOptions#from` method. Use `ExternalOptions#from` instead and merge created object with `SentryOptions#merge` + ## Unreleased * Feat: Attach Java vendor and version to events and transactions (#1703) diff --git a/sentry-log4j2/src/main/java/io/sentry/log4j2/SentryAppender.java b/sentry-log4j2/src/main/java/io/sentry/log4j2/SentryAppender.java index 1ec436dce1..f2aaaeddfe 100644 --- a/sentry-log4j2/src/main/java/io/sentry/log4j2/SentryAppender.java +++ b/sentry-log4j2/src/main/java/io/sentry/log4j2/SentryAppender.java @@ -108,7 +108,9 @@ public void start() { options -> { options.setEnableExternalConfiguration(true); options.setDsn(dsn); - options.setDebug(debug); + if (debug != null) { + options.setDebug(debug); + } options.setSentryClientName(BuildConfig.SENTRY_LOG4J2_SDK_NAME); options.setSdkVersion(createSdkVersion(options)); Optional.ofNullable(transportFactory).ifPresent(options::setTransportFactory); diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index fc20cf5d61..ffdf95af29 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -99,6 +99,45 @@ public abstract interface class io/sentry/EventProcessor { public fun process (Lio/sentry/protocol/SentryTransaction;Ljava/lang/Object;)Lio/sentry/protocol/SentryTransaction; } +public final class io/sentry/ExternalOptions { + public fun ()V + public fun addIgnoredExceptionForType (Ljava/lang/Class;)V + public fun addInAppExclude (Ljava/lang/String;)V + public fun addInAppInclude (Ljava/lang/String;)V + public fun addTracingOrigin (Ljava/lang/String;)V + public static fun from (Lio/sentry/config/PropertiesProvider;Lio/sentry/ILogger;)Lio/sentry/ExternalOptions; + public fun getDebug ()Ljava/lang/Boolean; + public fun getDist ()Ljava/lang/String; + public fun getDsn ()Ljava/lang/String; + public fun getEnableDeduplication ()Ljava/lang/Boolean; + public fun getEnableUncaughtExceptionHandler ()Ljava/lang/Boolean; + public fun getEnvironment ()Ljava/lang/String; + public fun getIgnoredExceptionsForType ()Ljava/util/Set; + public fun getInAppExcludes ()Ljava/util/List; + public fun getInAppIncludes ()Ljava/util/List; + public fun getMaxRequestBodySize ()Lio/sentry/SentryOptions$RequestSize; + public fun getProguardUuid ()Ljava/lang/String; + public fun getProxy ()Lio/sentry/SentryOptions$Proxy; + public fun getRelease ()Ljava/lang/String; + public fun getServerName ()Ljava/lang/String; + public fun getTags ()Ljava/util/Map; + public fun getTracesSampleRate ()Ljava/lang/Double; + public fun getTracingOrigins ()Ljava/util/List; + public fun setDebug (Ljava/lang/Boolean;)V + public fun setDist (Ljava/lang/String;)V + public fun setDsn (Ljava/lang/String;)V + public fun setEnableDeduplication (Ljava/lang/Boolean;)V + public fun setEnableUncaughtExceptionHandler (Ljava/lang/Boolean;)V + public fun setEnvironment (Ljava/lang/String;)V + public fun setMaxRequestBodySize (Lio/sentry/SentryOptions$RequestSize;)V + public fun setProguardUuid (Ljava/lang/String;)V + public fun setProxy (Lio/sentry/SentryOptions$Proxy;)V + public fun setRelease (Ljava/lang/String;)V + public fun setServerName (Ljava/lang/String;)V + public fun setTag (Ljava/lang/String;Ljava/lang/String;)V + public fun setTracesSampleRate (Ljava/lang/Double;)V +} + public final class io/sentry/GsonSerializer : io/sentry/ISerializer { public fun (Lio/sentry/SentryOptions;)V public fun deserialize (Ljava/io/Reader;Ljava/lang/Class;)Ljava/lang/Object; @@ -810,7 +849,6 @@ public class io/sentry/SentryOptions { public fun addIntegration (Lio/sentry/Integration;)V public fun addScopeObserver (Lio/sentry/IScopeObserver;)V public fun addTracingOrigin (Ljava/lang/String;)V - public static fun from (Lio/sentry/config/PropertiesProvider;Lio/sentry/ILogger;)Lio/sentry/SentryOptions; public fun getBeforeBreadcrumb ()Lio/sentry/SentryOptions$BeforeBreadcrumbCallback; public fun getBeforeSend ()Lio/sentry/SentryOptions$BeforeSendCallback; public fun getCacheDirPath ()Ljava/lang/String; @@ -820,7 +858,6 @@ public class io/sentry/SentryOptions { public fun getDist ()Ljava/lang/String; public fun getDistinctId ()Ljava/lang/String; public fun getDsn ()Ljava/lang/String; - public fun getEnableUncaughtExceptionHandler ()Ljava/lang/Boolean; public fun getEnvelopeDiskCache ()Lio/sentry/cache/IEnvelopeCache; public fun getEnvelopeReader ()Lio/sentry/IEnvelopeReader; public fun getEnvironment ()Ljava/lang/String; @@ -880,19 +917,19 @@ public class io/sentry/SentryOptions { public fun setCacheDirPath (Ljava/lang/String;)V public fun setCacheDirSize (I)V public fun setConnectionTimeoutMillis (I)V - public fun setDebug (Ljava/lang/Boolean;)V + public fun setDebug (Z)V public fun setDiagnosticLevel (Lio/sentry/SentryLevel;)V public fun setDist (Ljava/lang/String;)V public fun setDistinctId (Ljava/lang/String;)V public fun setDsn (Ljava/lang/String;)V public fun setEnableAutoSessionTracking (Z)V - public fun setEnableDeduplication (Ljava/lang/Boolean;)V + public fun setEnableDeduplication (Z)V public fun setEnableExternalConfiguration (Z)V public fun setEnableNdk (Z)V public fun setEnableScopeSync (Z)V public fun setEnableSessionTracking (Z)V public fun setEnableShutdownHook (Z)V - public fun setEnableUncaughtExceptionHandler (Ljava/lang/Boolean;)V + public fun setEnableUncaughtExceptionHandler (Z)V public fun setEnvelopeDiskCache (Lio/sentry/cache/IEnvelopeCache;)V public fun setEnvelopeReader (Lio/sentry/IEnvelopeReader;)V public fun setEnvironment (Ljava/lang/String;)V diff --git a/sentry/src/main/java/io/sentry/ExternalOptions.java b/sentry/src/main/java/io/sentry/ExternalOptions.java new file mode 100644 index 0000000000..4170d0824d --- /dev/null +++ b/sentry/src/main/java/io/sentry/ExternalOptions.java @@ -0,0 +1,243 @@ +package io.sentry; + +import io.sentry.config.PropertiesProvider; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CopyOnWriteArraySet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** Externally bindable properties set on {@link SentryOptions}. */ +public final class ExternalOptions { + + /** The default HTTP proxy port to use if an HTTP Proxy hostname is set but port is not. */ + private static final String PROXY_PORT_DEFAULT = "80"; + + private @Nullable String dsn; + private @Nullable String environment; + private @Nullable String release; + private @Nullable String dist; + private @Nullable String serverName; + private @Nullable Boolean enableUncaughtExceptionHandler; + private @Nullable Boolean debug; + private @Nullable Boolean enableDeduplication; + private @Nullable Double tracesSampleRate; + private @Nullable SentryOptions.RequestSize maxRequestBodySize; + private final @NotNull Map tags = new ConcurrentHashMap<>(); + private @Nullable SentryOptions.Proxy proxy; + private final @NotNull List inAppExcludes = new CopyOnWriteArrayList<>(); + private final @NotNull List inAppIncludes = new CopyOnWriteArrayList<>(); + private final @NotNull List tracingOrigins = new CopyOnWriteArrayList<>(); + private @Nullable String proguardUuid; + private final @NotNull Set> ignoredExceptionsForType = + new CopyOnWriteArraySet<>(); + + @SuppressWarnings("unchecked") + public static @NotNull ExternalOptions from( + final @NotNull PropertiesProvider propertiesProvider, final @NotNull ILogger logger) { + final ExternalOptions options = new ExternalOptions(); + options.setDsn(propertiesProvider.getProperty("dsn")); + options.setEnvironment(propertiesProvider.getProperty("environment")); + options.setRelease(propertiesProvider.getProperty("release")); + options.setDist(propertiesProvider.getProperty("dist")); + options.setServerName(propertiesProvider.getProperty("servername")); + options.setEnableUncaughtExceptionHandler( + propertiesProvider.getBooleanProperty("uncaught.handler.enabled")); + options.setTracesSampleRate(propertiesProvider.getDoubleProperty("traces-sample-rate")); + options.setDebug(propertiesProvider.getBooleanProperty("debug")); + options.setEnableDeduplication(propertiesProvider.getBooleanProperty("enable-deduplication")); + final String maxRequestBodySize = propertiesProvider.getProperty("max-request-body-size"); + if (maxRequestBodySize != null) { + options.setMaxRequestBodySize( + SentryOptions.RequestSize.valueOf(maxRequestBodySize.toUpperCase(Locale.ROOT))); + } + final Map tags = propertiesProvider.getMap("tags"); + for (final Map.Entry tag : tags.entrySet()) { + options.setTag(tag.getKey(), tag.getValue()); + } + + final String proxyHost = propertiesProvider.getProperty("proxy.host"); + final String proxyUser = propertiesProvider.getProperty("proxy.user"); + final String proxyPass = propertiesProvider.getProperty("proxy.pass"); + final String proxyPort = propertiesProvider.getProperty("proxy.port", PROXY_PORT_DEFAULT); + + if (proxyHost != null) { + options.setProxy(new SentryOptions.Proxy(proxyHost, proxyPort, proxyUser, proxyPass)); + } + + for (final String inAppInclude : propertiesProvider.getList("in-app-includes")) { + options.addInAppInclude(inAppInclude); + } + for (final String inAppExclude : propertiesProvider.getList("in-app-excludes")) { + options.addInAppExclude(inAppExclude); + } + for (final String tracingOrigin : propertiesProvider.getList("tracing-origins")) { + options.addTracingOrigin(tracingOrigin); + } + options.setProguardUuid(propertiesProvider.getProperty("proguard-uuid")); + + for (final String ignoredExceptionType : + propertiesProvider.getList("ignored-exceptions-for-type")) { + try { + Class clazz = Class.forName(ignoredExceptionType); + if (Throwable.class.isAssignableFrom(clazz)) { + options.addIgnoredExceptionForType((Class) clazz); + } else { + logger.log( + SentryLevel.WARNING, + "Skipping setting %s as ignored-exception-for-type. Reason: %s does not extend Throwable", + ignoredExceptionType, + ignoredExceptionType); + } + } catch (ClassNotFoundException e) { + logger.log( + SentryLevel.WARNING, + "Skipping setting %s as ignored-exception-for-type. Reason: %s class is not found", + ignoredExceptionType, + ignoredExceptionType); + } + } + return options; + } + + public @Nullable String getDsn() { + return dsn; + } + + public void setDsn(final @Nullable String dsn) { + this.dsn = dsn; + } + + public @Nullable String getEnvironment() { + return environment; + } + + public void setEnvironment(final @Nullable String environment) { + this.environment = environment; + } + + public @Nullable String getRelease() { + return release; + } + + public void setRelease(final @Nullable String release) { + this.release = release; + } + + public @Nullable String getDist() { + return dist; + } + + public void setDist(final @Nullable String dist) { + this.dist = dist; + } + + public @Nullable String getServerName() { + return serverName; + } + + public void setServerName(final @Nullable String serverName) { + this.serverName = serverName; + } + + public @Nullable Boolean getEnableUncaughtExceptionHandler() { + return enableUncaughtExceptionHandler; + } + + public void setEnableUncaughtExceptionHandler( + final @Nullable Boolean enableUncaughtExceptionHandler) { + this.enableUncaughtExceptionHandler = enableUncaughtExceptionHandler; + } + + public @NotNull List getTracingOrigins() { + return tracingOrigins; + } + + public @Nullable Boolean getDebug() { + return debug; + } + + public void setDebug(final @Nullable Boolean debug) { + this.debug = debug; + } + + public @Nullable Boolean getEnableDeduplication() { + return enableDeduplication; + } + + public void setEnableDeduplication(final @Nullable Boolean enableDeduplication) { + this.enableDeduplication = enableDeduplication; + } + + public @Nullable Double getTracesSampleRate() { + return tracesSampleRate; + } + + public void setTracesSampleRate(final @Nullable Double tracesSampleRate) { + this.tracesSampleRate = tracesSampleRate; + } + + public @Nullable SentryOptions.RequestSize getMaxRequestBodySize() { + return maxRequestBodySize; + } + + public void setMaxRequestBodySize(final @Nullable SentryOptions.RequestSize maxRequestBodySize) { + this.maxRequestBodySize = maxRequestBodySize; + } + + public @NotNull Map getTags() { + return tags; + } + + public @Nullable SentryOptions.Proxy getProxy() { + return proxy; + } + + public void setProxy(final @Nullable SentryOptions.Proxy proxy) { + this.proxy = proxy; + } + + public @NotNull List getInAppExcludes() { + return inAppExcludes; + } + + public @NotNull List getInAppIncludes() { + return inAppIncludes; + } + + public @Nullable String getProguardUuid() { + return proguardUuid; + } + + public void setProguardUuid(final @Nullable String proguardUuid) { + this.proguardUuid = proguardUuid; + } + + public @NotNull Set> getIgnoredExceptionsForType() { + return ignoredExceptionsForType; + } + + public void addInAppInclude(final @NotNull String include) { + inAppIncludes.add(include); + } + + public void addInAppExclude(final @NotNull String exclude) { + inAppExcludes.add(exclude); + } + + public void addTracingOrigin(final @NotNull String tracingOrigin) { + this.tracingOrigins.add(tracingOrigin); + } + + public void addIgnoredExceptionForType(final @NotNull Class exceptionType) { + this.ignoredExceptionsForType.add(exceptionType); + } + + public void setTag(final @NotNull String key, final @NotNull String value) { + this.tags.put(key, value); + } +} diff --git a/sentry/src/main/java/io/sentry/Sentry.java b/sentry/src/main/java/io/sentry/Sentry.java index 6a779f8c18..4f30502b9c 100644 --- a/sentry/src/main/java/io/sentry/Sentry.java +++ b/sentry/src/main/java/io/sentry/Sentry.java @@ -191,7 +191,7 @@ private static synchronized void init( private static boolean initConfigurations(final @NotNull SentryOptions options) { if (options.isEnableExternalConfiguration()) { - options.merge(SentryOptions.from(PropertiesProviderFactory.create(), options.getLogger())); + options.merge(ExternalOptions.from(PropertiesProviderFactory.create(), options.getLogger())); } final String dsn = options.getDsn(); diff --git a/sentry/src/main/java/io/sentry/SentryOptions.java b/sentry/src/main/java/io/sentry/SentryOptions.java index 5c83ca633a..4b25f64288 100644 --- a/sentry/src/main/java/io/sentry/SentryOptions.java +++ b/sentry/src/main/java/io/sentry/SentryOptions.java @@ -2,7 +2,6 @@ import com.jakewharton.nopen.annotation.Open; import io.sentry.cache.IEnvelopeCache; -import io.sentry.config.PropertiesProvider; import io.sentry.protocol.SdkVersion; import io.sentry.transport.ITransportGate; import io.sentry.transport.NoOpEnvelopeCache; @@ -13,7 +12,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -32,9 +30,6 @@ public class SentryOptions { /** Default Log level if not specified Default is DEBUG */ static final SentryLevel DEFAULT_DIAGNOSTIC_LEVEL = SentryLevel.DEBUG; - /** The default HTTP proxy port to use if an HTTP Proxy hostname is set but port is not. */ - private static final String PROXY_PORT_DEFAULT = "80"; - /** * Are callbacks that run for every event. They can either return a new event which in most cases * means just adding data OR return null in case the event will be dropped and not sent. @@ -75,7 +70,7 @@ public class SentryOptions { * Turns debug mode on or off. If debug is enabled SDK will attempt to print out useful debugging * information if something goes wrong. Default is disabled. */ - private @Nullable Boolean debug; + private boolean debug; /** Turns NDK on or off. Default is enabled. */ private boolean enableNdk = true; @@ -218,7 +213,7 @@ public class SentryOptions { /* When enabled, Sentry installs UncaughtExceptionHandlerIntegration. */ - private @Nullable Boolean enableUncaughtExceptionHandler = true; + private boolean enableUncaughtExceptionHandler = true; /** Sentry Executor Service that sends cached events and envelopes on App. start. */ private @NotNull ISentryExecutorService executorService = NoOpSentryExecutorService.getInstance(); @@ -267,7 +262,7 @@ public class SentryOptions { * deduplication prevents from receiving the same exception multiple times when there is more than * one framework active that captures errors, for example Logback and Spring Boot. */ - private @Nullable Boolean enableDeduplication = true; + private boolean enableDeduplication = true; /** Maximum number of spans that can be atteched to single transaction. */ private int maxSpans = 1000; @@ -292,80 +287,6 @@ public class SentryOptions { /** Proguard UUID. */ private @Nullable String proguardUuid; - /** - * Creates {@link SentryOptions} from properties provided by a {@link PropertiesProvider}. - * - * @param propertiesProvider the properties provider - * @return the sentry options - */ - @SuppressWarnings("unchecked") - public static @NotNull SentryOptions from( - final @NotNull PropertiesProvider propertiesProvider, final @NotNull ILogger logger) { - final SentryOptions options = new SentryOptions(); - options.setDsn(propertiesProvider.getProperty("dsn")); - options.setEnvironment(propertiesProvider.getProperty("environment")); - options.setRelease(propertiesProvider.getProperty("release")); - options.setDist(propertiesProvider.getProperty("dist")); - options.setServerName(propertiesProvider.getProperty("servername")); - options.setEnableUncaughtExceptionHandler( - propertiesProvider.getBooleanProperty("uncaught.handler.enabled")); - options.setTracesSampleRate(propertiesProvider.getDoubleProperty("traces-sample-rate")); - options.setDebug(propertiesProvider.getBooleanProperty("debug")); - options.setEnableDeduplication(propertiesProvider.getBooleanProperty("enable-deduplication")); - final String maxRequestBodySize = propertiesProvider.getProperty("max-request-body-size"); - if (maxRequestBodySize != null) { - options.setMaxRequestBodySize( - RequestSize.valueOf(maxRequestBodySize.toUpperCase(Locale.ROOT))); - } - final Map tags = propertiesProvider.getMap("tags"); - for (final Map.Entry tag : tags.entrySet()) { - options.setTag(tag.getKey(), tag.getValue()); - } - - final String proxyHost = propertiesProvider.getProperty("proxy.host"); - final String proxyUser = propertiesProvider.getProperty("proxy.user"); - final String proxyPass = propertiesProvider.getProperty("proxy.pass"); - final String proxyPort = propertiesProvider.getProperty("proxy.port", PROXY_PORT_DEFAULT); - - if (proxyHost != null) { - options.setProxy(new Proxy(proxyHost, proxyPort, proxyUser, proxyPass)); - } - - for (final String inAppInclude : propertiesProvider.getList("in-app-includes")) { - options.addInAppInclude(inAppInclude); - } - for (final String inAppExclude : propertiesProvider.getList("in-app-excludes")) { - options.addInAppExclude(inAppExclude); - } - for (final String tracingOrigin : propertiesProvider.getList("tracing-origins")) { - options.addTracingOrigin(tracingOrigin); - } - options.setProguardUuid(propertiesProvider.getProperty("proguard-uuid")); - - for (final String ignoredExceptionType : - propertiesProvider.getList("ignored-exceptions-for-type")) { - try { - Class clazz = Class.forName(ignoredExceptionType); - if (Throwable.class.isAssignableFrom(clazz)) { - options.addIgnoredExceptionForType((Class) clazz); - } else { - logger.log( - SentryLevel.WARNING, - "Skipping setting %s as ignored-exception-for-type. Reason: %s does not extend Throwable", - ignoredExceptionType, - ignoredExceptionType); - } - } catch (ClassNotFoundException e) { - logger.log( - SentryLevel.WARNING, - "Skipping setting %s as ignored-exception-for-type. Reason: %s class is not found", - ignoredExceptionType, - ignoredExceptionType); - } - } - return options; - } - /** * Adds an event processor * @@ -426,7 +347,7 @@ public void setDsn(@Nullable String dsn) { * @return true if ON or false otherwise */ public boolean isDebug() { - return Boolean.TRUE.equals(debug); + return debug; } /** @@ -434,19 +355,10 @@ public boolean isDebug() { * * @param debug true if ON or false otherwise */ - public void setDebug(final @Nullable Boolean debug) { + public void setDebug(final boolean debug) { this.debug = debug; } - /** - * Check if debug mode is ON, OFF or not set. - * - * @return true if ON or false otherwise - */ - private @Nullable Boolean getDebug() { - return debug; - } - /** * Returns the Logger interface * @@ -1048,15 +960,6 @@ public void setFlushTimeoutMillis(long flushTimeoutMillis) { * @return true if enabled or false otherwise. */ public boolean isEnableUncaughtExceptionHandler() { - return Boolean.TRUE.equals(enableUncaughtExceptionHandler); - } - - /** - * Checks if the default UncaughtExceptionHandlerIntegration is enabled or disabled or not set. - * - * @return true if enabled, false otherwise or null if not set. - */ - public @Nullable Boolean getEnableUncaughtExceptionHandler() { return enableUncaughtExceptionHandler; } @@ -1065,8 +968,7 @@ public boolean isEnableUncaughtExceptionHandler() { * * @param enableUncaughtExceptionHandler true if enabled or false otherwise. */ - public void setEnableUncaughtExceptionHandler( - final @Nullable Boolean enableUncaughtExceptionHandler) { + public void setEnableUncaughtExceptionHandler(final boolean enableUncaughtExceptionHandler) { this.enableUncaughtExceptionHandler = enableUncaughtExceptionHandler; } @@ -1330,15 +1232,6 @@ public void setMaxAttachmentSize(long maxAttachmentSize) { * @return if event deduplication is turned on. */ public boolean isEnableDeduplication() { - return Boolean.TRUE.equals(enableDeduplication); - } - - /** - * Returns if event deduplication is turned on or of or {@code null} if not specified. - * - * @return if event deduplication is turned on or of or {@code null} if not specified. - */ - private @Nullable Boolean getEnableDeduplication() { return enableDeduplication; } @@ -1347,7 +1240,7 @@ public boolean isEnableDeduplication() { * * @param enableDeduplication true if enabled false otherwise */ - public void setEnableDeduplication(final @Nullable Boolean enableDeduplication) { + public void setEnableDeduplication(final boolean enableDeduplication) { this.enableDeduplication = enableDeduplication; } @@ -1600,7 +1493,7 @@ private SentryOptions(final boolean empty) { * * @param options options loaded from external locations */ - void merge(final @NotNull SentryOptions options) { + void merge(final @NotNull ExternalOptions options) { if (options.getDsn() != null) { setDsn(options.getDsn()); } diff --git a/sentry/src/test/java/io/sentry/ExternalOptionsTest.kt b/sentry/src/test/java/io/sentry/ExternalOptionsTest.kt new file mode 100644 index 0000000000..0fd56c68f3 --- /dev/null +++ b/sentry/src/test/java/io/sentry/ExternalOptionsTest.kt @@ -0,0 +1,181 @@ +package io.sentry + +import com.nhaarman.mockitokotlin2.any +import com.nhaarman.mockitokotlin2.eq +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.verify +import io.sentry.config.PropertiesProviderFactory +import java.lang.RuntimeException +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue +import org.junit.rules.TemporaryFolder + +class ExternalOptionsTest { + + @Test + fun `creates options with proxy using external properties`() { + withPropertiesFile(listOf("proxy.host=proxy.example.com", "proxy.port=9090", "proxy.user=some-user", "proxy.pass=some-pass")) { + assertNotNull(it.proxy) { proxy -> + assertEquals("proxy.example.com", proxy.host) + assertEquals("9090", proxy.port) + assertEquals("some-user", proxy.user) + assertEquals("some-pass", proxy.pass) + } + } + } + + @Test + fun `when proxy port is not set default proxy port is used`() { + withPropertiesFile("proxy.host=proxy.example.com") { + assertNotNull(it.proxy) + assertEquals("proxy.example.com", it.proxy!!.host) + assertEquals("80", it.proxy!!.port) + } + } + + @Test + fun `creates options with tags using external properties`() { + withPropertiesFile(listOf("tags.tag1=value1", "tags.tag2=value2")) { + assertEquals(mapOf("tag1" to "value1", "tag2" to "value2"), it.tags) + } + } + + @Test + fun `creates options with uncaught handler set to true enabled using external properties`() { + withPropertiesFile("uncaught.handler.enabled=true") { options -> + assertNotNull(options.enableUncaughtExceptionHandler) { + assertTrue(it) + } + } + } + + @Test + fun `creates options with uncaught handler set to false enabled using external properties`() { + withPropertiesFile("uncaught.handler.enabled=false") { options -> + assertNotNull(options.enableUncaughtExceptionHandler) { + assertFalse(it) + } + } + } + + @Test + fun `creates options with uncaught handler set to null enabled using external properties`() { + withPropertiesFile { + assertNull(it.enableUncaughtExceptionHandler) + } + } + + @Test + fun `creates options with debug set to true enabled using external properties`() { + withPropertiesFile("debug=true") { + assertNotNull(it.debug) { + assertTrue(it) + } + } + } + + @Test + fun `creates options with debug set to false enabled using external properties`() { + withPropertiesFile("debug=false") { + assertNotNull(it.debug) { + assertFalse(it) + } + } + } + + @Test + fun `creates options with debug set to null enabled using external properties`() { + withPropertiesFile() { + val mergeResult = SentryOptions().apply { + setDebug(true) + } + mergeResult.merge(it) + assertTrue(mergeResult.isDebug) + } + } + + @Test + fun `creates options with inAppInclude and inAppExclude using external properties`() { + withPropertiesFile(listOf("in-app-includes=org.springframework,com.myapp", "in-app-excludes=org.jboss,com.microsoft")) { + assertEquals(listOf("org.springframework", "com.myapp"), it.inAppIncludes) + assertEquals(listOf("org.jboss", "com.microsoft"), it.inAppExcludes) + } + } + + @Test + fun `creates options with tracesSampleRate using external properties`() { + withPropertiesFile("traces-sample-rate=0.2") { + assertEquals(0.2, it.tracesSampleRate) + } + } + + @Test + fun `creates options with enableDeduplication using external properties`() { + withPropertiesFile("enable-deduplication=true") { + assertNotNull(it.enableDeduplication) { + assertTrue(it) + } + } + } + + @Test + fun `creates options with maxRequestBodySize using external properties`() { + withPropertiesFile("max-request-body-size=small") { + assertEquals(SentryOptions.RequestSize.SMALL, it.maxRequestBodySize) + } + } + + @Test + fun `creates options with tracing origins using external properties`() { + withPropertiesFile("""tracing-origins=localhost,^(http|https)://api\\..*$""") { + assertEquals(listOf("localhost", """^(http|https)://api\..*$"""), it.tracingOrigins) + } + } + + @Test + fun `creates options with proguardUuid using external properties`() { + withPropertiesFile("proguard-uuid=id") { + assertEquals("id", it.proguardUuid) + } + } + + @Test + fun `creates options with ignored exception types using external properties`() { + val logger = mock() + // Setting few types of classes: + // - RuntimeException and IllegalStateException - valid exception classes + // - NonExistingClass - class that does not exist - should not trigger a failure to create options + // - io.sentry.Sentry - class that does not extend Throwable - should not trigger a failure + withPropertiesFile("ignored-exceptions-for-type=java.lang.RuntimeException,java.lang.IllegalStateException,com.xx.NonExistingClass,io.sentry.Sentry", logger) { options -> + assertTrue(options.ignoredExceptionsForType.contains(RuntimeException::class.java)) + assertTrue(options.ignoredExceptionsForType.contains(IllegalStateException::class.java)) + verify(logger).log(eq(SentryLevel.WARNING), any(), eq("com.xx.NonExistingClass"), eq("com.xx.NonExistingClass")) + verify(logger).log(eq(SentryLevel.WARNING), any(), eq("io.sentry.Sentry"), eq("io.sentry.Sentry")) + } + } + + private fun withPropertiesFile(textLines: List = emptyList(), logger: ILogger = mock(), fn: (ExternalOptions) -> Unit) { + // create a sentry.properties file in temporary folder + val temporaryFolder = TemporaryFolder() + temporaryFolder.create() + val file = temporaryFolder.newFile("sentry.properties") + textLines.forEach { file.appendText("$it\n") } + // set location of the sentry.properties file + System.setProperty("sentry.properties.file", file.absolutePath) + + try { + val options = ExternalOptions.from(PropertiesProviderFactory.create(), logger) + fn.invoke(options) + } finally { + temporaryFolder.delete() + } + } + + private fun withPropertiesFile(text: String, logger: ILogger = mock(), fn: (ExternalOptions) -> Unit) { + withPropertiesFile(listOf(text), logger, fn) + } +} diff --git a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt index 98c312c1b1..451bf0b0a0 100644 --- a/sentry/src/test/java/io/sentry/SentryOptionsTest.kt +++ b/sentry/src/test/java/io/sentry/SentryOptionsTest.kt @@ -1,12 +1,7 @@ package io.sentry -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.eq import com.nhaarman.mockitokotlin2.mock -import com.nhaarman.mockitokotlin2.verify -import io.sentry.config.PropertiesProviderFactory import java.io.File -import java.lang.RuntimeException import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFailsWith @@ -14,7 +9,6 @@ import kotlin.test.assertFalse import kotlin.test.assertNotNull import kotlin.test.assertNull import kotlin.test.assertTrue -import org.junit.rules.TemporaryFolder class SentryOptionsTest { @Test @@ -188,7 +182,7 @@ class SentryOptionsTest { @Test fun `copies options from another SentryOptions instance`() { - val externalOptions = SentryOptions() + val externalOptions = ExternalOptions() externalOptions.dsn = "http://key@localhost/proj" externalOptions.dist = "distribution" externalOptions.environment = "environment" @@ -216,7 +210,7 @@ class SentryOptionsTest { assertEquals("example.com", options.proxy!!.host) assertEquals("8090", options.proxy!!.port) assertEquals(mapOf("tag1" to "value1", "tag2" to "value2"), options.tags) - assertFalse(options.enableUncaughtExceptionHandler!!) + assertFalse(options.isEnableUncaughtExceptionHandler) assertEquals(0.5, options.tracesSampleRate) assertEquals(listOf("com.app"), options.inAppIncludes) assertEquals(listOf("io.off"), options.inAppExcludes) @@ -225,16 +219,15 @@ class SentryOptionsTest { @Test fun `merging options when enableUncaughtExceptionHandler is not set preserves the default value`() { - val externalOptions = SentryOptions() - externalOptions.enableUncaughtExceptionHandler = null + val externalOptions = ExternalOptions() val options = SentryOptions() options.merge(externalOptions) - assertTrue(options.enableUncaughtExceptionHandler!!) + assertTrue(options.isEnableUncaughtExceptionHandler) } @Test fun `merging options merges and overwrites existing tag values`() { - val externalOptions = SentryOptions() + val externalOptions = ExternalOptions() externalOptions.setTag("tag1", "value1") externalOptions.setTag("tag2", "value2") val options = SentryOptions() @@ -246,170 +239,13 @@ class SentryOptionsTest { assertEquals(mapOf("tag1" to "value1", "tag2" to "value2", "tag3" to "value3"), options.tags) } - @Test - fun `creates options with proxy using external properties`() { - withPropertiesFile(listOf("proxy.host=proxy.example.com", "proxy.port=9090", "proxy.user=some-user", "proxy.pass=some-pass")) { - assertNotNull(it.proxy) { proxy -> - assertEquals("proxy.example.com", proxy.host) - assertEquals("9090", proxy.port) - assertEquals("some-user", proxy.user) - assertEquals("some-pass", proxy.pass) - } - } - } - - @Test - fun `when proxy port is not set default proxy port is used`() { - withPropertiesFile("proxy.host=proxy.example.com") { - assertNotNull(it.proxy) - assertEquals("proxy.example.com", it.proxy!!.host) - assertEquals("80", it.proxy!!.port) - } - } - - @Test - fun `creates options with tags using external properties`() { - withPropertiesFile(listOf("tags.tag1=value1", "tags.tag2=value2")) { - assertEquals(mapOf("tag1" to "value1", "tag2" to "value2"), it.tags) - } - } - - @Test - fun `creates options with uncaught handler set to true enabled using external properties`() { - withPropertiesFile("uncaught.handler.enabled=true") { options -> - assertNotNull(options.enableUncaughtExceptionHandler) { - assertTrue(it) - } - } - } - - @Test - fun `creates options with uncaught handler set to false enabled using external properties`() { - withPropertiesFile("uncaught.handler.enabled=false") { options -> - assertNotNull(options.enableUncaughtExceptionHandler) { - assertFalse(it) - } - } - } - - @Test - fun `creates options with uncaught handler set to null enabled using external properties`() { - withPropertiesFile { - assertNull(it.enableUncaughtExceptionHandler) - } - } - - @Test - fun `creates options with debug set to true enabled using external properties`() { - withPropertiesFile("debug=true") { - assertTrue(it.isDebug) - } - } - - @Test - fun `creates options with debug set to false enabled using external properties`() { - withPropertiesFile("debug=false") { - assertFalse(it.isDebug) - } - } - - @Test - fun `creates options with debug set to null enabled using external properties`() { - withPropertiesFile() { - val mergeResult = SentryOptions().apply { - setDebug(true) - } - mergeResult.merge(it) - assertTrue(mergeResult.isDebug) - } - } - @Test fun `when options is initialized, Gson Serializer is set by default`() { assertTrue(SentryOptions().serializer is GsonSerializer) } - @Test - fun `creates options with inAppInclude and inAppExclude using external properties`() { - withPropertiesFile(listOf("in-app-includes=org.springframework,com.myapp", "in-app-excludes=org.jboss,com.microsoft")) { - assertEquals(listOf("org.springframework", "com.myapp"), it.inAppIncludes) - assertEquals(listOf("org.jboss", "com.microsoft"), it.inAppExcludes) - } - } - - @Test - fun `creates options with tracesSampleRate using external properties`() { - withPropertiesFile("traces-sample-rate=0.2") { - assertEquals(0.2, it.tracesSampleRate) - } - } - - @Test - fun `creates options with enableDeduplication using external properties`() { - withPropertiesFile("enable-deduplication=true") { - assertTrue(it.isEnableDeduplication) - } - } - - @Test - fun `creates options with maxRequestBodySize using external properties`() { - withPropertiesFile("max-request-body-size=small") { - assertEquals(SentryOptions.RequestSize.SMALL, it.maxRequestBodySize) - } - } - - @Test - fun `creates options with tracing origins using external properties`() { - withPropertiesFile("""tracing-origins=localhost,^(http|https)://api\\..*$""") { - assertEquals(listOf("localhost", """^(http|https)://api\..*$"""), it.tracingOrigins) - } - } - - @Test - fun `creates options with proguardUuid using external properties`() { - withPropertiesFile("proguard-uuid=id") { - assertEquals("id", it.proguardUuid) - } - } - - @Test - fun `creates options with ignored exception types using external properties`() { - val logger = mock() - // Setting few types of classes: - // - RuntimeException and IllegalStateException - valid exception classes - // - NonExistingClass - class that does not exist - should not trigger a failure to create options - // - io.sentry.Sentry - class that does not extend Throwable - should not trigger a failure - withPropertiesFile("ignored-exceptions-for-type=java.lang.RuntimeException,java.lang.IllegalStateException,com.xx.NonExistingClass,io.sentry.Sentry", logger) { options -> - assertTrue(options.ignoredExceptionsForType.contains(RuntimeException::class.java)) - assertTrue(options.ignoredExceptionsForType.contains(IllegalStateException::class.java)) - verify(logger).log(eq(SentryLevel.WARNING), any(), eq("com.xx.NonExistingClass"), eq("com.xx.NonExistingClass")) - verify(logger).log(eq(SentryLevel.WARNING), any(), eq("io.sentry.Sentry"), eq("io.sentry.Sentry")) - } - } - @Test fun `when options are initialized, maxAttachmentSize is 20`() { assertEquals(20 * 1024 * 1024, SentryOptions().maxAttachmentSize) } - - private fun withPropertiesFile(textLines: List = emptyList(), logger: ILogger = mock(), fn: (SentryOptions) -> Unit) { - // create a sentry.properties file in temporary folder - val temporaryFolder = TemporaryFolder() - temporaryFolder.create() - val file = temporaryFolder.newFile("sentry.properties") - textLines.forEach { file.appendText("$it\n") } - // set location of the sentry.properties file - System.setProperty("sentry.properties.file", file.absolutePath) - - try { - val options = SentryOptions.from(PropertiesProviderFactory.create(), logger) - fn.invoke(options) - } finally { - temporaryFolder.delete() - } - } - - private fun withPropertiesFile(text: String, logger: ILogger = mock(), fn: (SentryOptions) -> Unit) { - withPropertiesFile(listOf(text), logger, fn) - } } diff --git a/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt b/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt index 2a822e1429..87e4949f5f 100644 --- a/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt +++ b/sentry/src/test/java/io/sentry/UncaughtExceptionHandlerIntegrationTest.kt @@ -105,7 +105,7 @@ class UncaughtExceptionHandlerIntegrationTest { @Test fun `When defaultUncaughtExceptionHandler is disabled, should not install Sentry UncaughtExceptionHandler`() { val options = SentryOptions() - options.enableUncaughtExceptionHandler = false + options.isEnableUncaughtExceptionHandler = false val hub = mock() val handlerMock = mock() val integration = UncaughtExceptionHandlerIntegration(handlerMock)